blob: 5d38681f4d64d9b14a51ba99049364b167d8b43a [file] [log] [blame]
// Copyright 2021 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.devtools.build.lib.view.java;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.devtools.build.lib.actions.util.ActionsTestUtil.prettyArtifactNames;
import static com.google.devtools.build.lib.rules.java.JavaCompileActionTestHelper.getDirectJars;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
import com.google.devtools.build.lib.analysis.ConfiguredTarget;
import com.google.devtools.build.lib.analysis.OutputGroupInfo;
import com.google.devtools.build.lib.analysis.Runfiles;
import com.google.devtools.build.lib.analysis.actions.SpawnAction;
import com.google.devtools.build.lib.analysis.configuredtargets.OutputFileConfiguredTarget;
import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.rules.java.JavaCompilationArgsProvider;
import com.google.devtools.build.lib.rules.java.JavaCompilationInfoProvider;
import com.google.devtools.build.lib.rules.java.JavaCompileAction;
import com.google.devtools.build.lib.rules.java.JavaImport;
import com.google.devtools.build.lib.rules.java.JavaInfo;
import com.google.devtools.build.lib.rules.java.JavaRuleOutputJarsProvider;
import com.google.devtools.build.lib.rules.java.JavaSourceJarsProvider;
import com.google.devtools.build.lib.rules.java.ProguardSpecProvider;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Tests for {@link JavaImport}. */
@RunWith(JUnit4.class)
public class JavaImportConfiguredTargetTest extends BuildViewTestCase {
@Before
public final void writeBuildFile() throws Exception {
scratch.file(
"java/jarlib/BUILD",
"java_import(name = 'libraryjar',",
" jars = ['library.jar'])",
"java_import(name = 'libraryjar_with_srcjar',",
" jars = ['library.jar'],",
" srcjar = 'library.srcjar')");
scratch.overwriteFile(
"tools/allowlists/java_import_exports/BUILD",
"package_group(",
" name = 'java_import_exports',",
" packages = ['//...'],",
")");
}
@Test
public void testSimple() throws Exception {
ConfiguredTarget jarLib = getConfiguredTarget("//java/jarlib:libraryjar");
assertThat(prettyArtifactNames(getFilesToBuild(jarLib)))
.containsExactly("java/jarlib/library.jar");
}
// Regression test for b/5868388.
@Test
public void testJavaLibraryAllowsImportInDeps() throws Exception {
scratchConfiguredTarget(
"java",
"javalib",
"java_library(name = 'javalib',",
" srcs = ['Other.java'],",
" exports = ['//java/jarlib:libraryjar'])");
assertNoEvents(); // Make sure that no warnings were emitted.
}
private static void validateRuntimeClassPath(
ConfiguredTarget binary, String... expectedRuntimeClasspath) throws Exception {
assertThat(
prettyArtifactNames(
JavaInfo.getProvider(JavaCompilationInfoProvider.class, binary)
.getRuntimeClasspath()
.getSet(Artifact.class)))
.containsExactlyElementsIn(expectedRuntimeClasspath)
.inOrder();
}
private static void validateCompilationClassPath(
ConfiguredTarget binary, String... expectedCompilationClasspath) throws Exception {
assertThat(
prettyArtifactNames(
JavaInfo.getProvider(JavaCompilationInfoProvider.class, binary)
.getCompilationClasspath()
.getSet(Artifact.class)))
.containsExactlyElementsIn(expectedCompilationClasspath)
.inOrder();
}
@Test
public void testWithJavaLibrary() throws Exception {
scratch.file(
"java/somelib/BUILD",
"java_library(name = 'javalib',",
" srcs = ['Other.java'],",
" deps = ['//java/jarlib:libraryjar'])");
ConfiguredTarget javaLib = getConfiguredTarget("//java/somelib:javalib");
validateCompilationClassPath(
javaLib, "java/jarlib/_ijar/libraryjar/java/jarlib/library-ijar.jar");
validateRuntimeClassPath(javaLib, "java/somelib/libjavalib.jar", "java/jarlib/library.jar");
}
@Test
public void testDeps() throws Exception {
scratch.file(
"java/jarlib2/BUILD",
"java_library(name = 'lib',",
" srcs = ['Main.java'],",
" deps = [':import-jar'])",
"java_import(name = 'import-jar',",
" jars = ['import.jar'],",
" deps = ['//java/jarlib2:depjar'],",
" exports = ['//java/jarlib2:exportjar'],",
")",
"java_import(name = 'depjar',",
" jars = ['depjar.jar'])",
"java_import(name = 'exportjar',",
" jars = ['exportjar.jar'])");
ConfiguredTarget importJar = getConfiguredTarget("//java/jarlib2:import-jar");
assertThat(prettyArtifactNames(getFilesToBuild(importJar)))
.containsExactly("java/jarlib2/import.jar");
// JavaCompilationArgs should hold classpaths of the transitive closure.
JavaCompilationArgsProvider recursiveCompilationArgs =
JavaInfo.getProvider(JavaCompilationArgsProvider.class, importJar);
assertThat(prettyArtifactNames(recursiveCompilationArgs.getTransitiveCompileTimeJars()))
.containsExactly(
"java/jarlib2/_ijar/import-jar/java/jarlib2/import-ijar.jar",
"java/jarlib2/_ijar/exportjar/java/jarlib2/exportjar-ijar.jar",
"java/jarlib2/_ijar/depjar/java/jarlib2/depjar-ijar.jar")
.inOrder();
assertThat(prettyArtifactNames(recursiveCompilationArgs.getRuntimeJars()))
.containsExactly(
"java/jarlib2/import.jar", "java/jarlib2/exportjar.jar", "java/jarlib2/depjar.jar")
.inOrder();
// Recursive deps work the same as with java_library.
JavaCompilationArgsProvider compilationArgsProvider =
JavaInfo.getProvider(JavaCompilationArgsProvider.class, importJar);
assertThat(prettyArtifactNames(compilationArgsProvider.getDirectCompileTimeJars()))
.containsExactly(
"java/jarlib2/_ijar/import-jar/java/jarlib2/import-ijar.jar",
"java/jarlib2/_ijar/exportjar/java/jarlib2/exportjar-ijar.jar")
.inOrder();
assertThat(prettyArtifactNames(compilationArgsProvider.getRuntimeJars()))
.containsExactly(
"java/jarlib2/import.jar", "java/jarlib2/exportjar.jar", "java/jarlib2/depjar.jar")
.inOrder();
// Check that parameters propagate to Java libraries properly.
ConfiguredTarget lib = getConfiguredTarget("//java/jarlib2:lib");
validateCompilationClassPath(
lib,
"java/jarlib2/_ijar/import-jar/java/jarlib2/import-ijar.jar",
"java/jarlib2/_ijar/exportjar/java/jarlib2/exportjar-ijar.jar",
"java/jarlib2/_ijar/depjar/java/jarlib2/depjar-ijar.jar");
validateRuntimeClassPath(
lib,
"java/jarlib2/liblib.jar",
"java/jarlib2/import.jar",
"java/jarlib2/exportjar.jar",
"java/jarlib2/depjar.jar");
}
@Test
public void testSrcJars() throws Exception {
ConfiguredTarget jarLibWithSources =
getConfiguredTarget("//java/jarlib:libraryjar_with_srcjar");
assertThat(
Iterables.getOnlyElement(
JavaInfo.getProvider(JavaRuleOutputJarsProvider.class, jarLibWithSources)
.getAllSrcOutputJars())
.prettyPrint())
.isEqualTo("java/jarlib/library.srcjar");
}
@Test
public void testFromGenrule() throws Exception {
scratch.file(
"java/genrules/BUILD",
"genrule(name = 'generated_jar',",
" outs = ['generated.jar'],",
" cmd = '')",
"genrule(name = 'generated_src_jar',",
" outs = ['generated.srcjar'],",
" cmd = '')",
"java_import(name = 'library-jar',",
" jars = [':generated_jar'],",
" srcjar = ':generated_src_jar',",
" exports = ['//java/jarlib:libraryjar'])");
ConfiguredTarget jarLib = getConfiguredTarget("//java/genrules:library-jar");
JavaCompilationArgsProvider compilationArgs =
JavaInfo.getProvider(JavaCompilationArgsProvider.class, jarLib);
assertThat(prettyArtifactNames(compilationArgs.getTransitiveCompileTimeJars()))
.containsExactly(
"java/genrules/_ijar/library-jar/java/genrules/generated-ijar.jar",
"java/jarlib/_ijar/libraryjar/java/jarlib/library-ijar.jar")
.inOrder();
assertThat(prettyArtifactNames(compilationArgs.getRuntimeJars()))
.containsExactly("java/genrules/generated.jar", "java/jarlib/library.jar")
.inOrder();
Artifact jar = compilationArgs.getRuntimeJars().toList().get(0);
assertThat(getGeneratingAction(jar).prettyPrint())
.isEqualTo("action 'Executing genrule //java/genrules:generated_jar'");
}
@Test
public void testAllowsJarInSrcjars() throws Exception {
scratch.file(
"java/srcjarlib/BUILD",
"java_import(name = 'library-jar',",
" jars = ['somelib.jar'],",
" srcjar = 'somelib-src.jar')");
ConfiguredTarget jarLib = getConfiguredTarget("//java/srcjarlib:library-jar");
assertThat(
Iterables.getOnlyElement(
JavaInfo.getProvider(JavaRuleOutputJarsProvider.class, jarLib)
.getAllSrcOutputJars())
.prettyPrint())
.isEqualTo("java/srcjarlib/somelib-src.jar");
}
@Test
public void testRequiresJars() throws Exception {
checkError("pkg", "rule", "mandatory attribute 'jars'", "java_import(name = 'rule')");
}
@Test
public void testPermitsEmptyJars() throws Exception {
scratchConfiguredTarget("pkg", "rule", "java_import(name = 'rule', jars = [])");
}
@Test
public void testDisallowsFilesInExports() throws Exception {
scratch.file("pkg/bad.jar", "");
checkError(
"pkg",
"rule",
"expected no files",
"java_import(name = 'rule', jars = ['good.jar'], exports = ['bad.jar'])");
}
@Test
public void testDisallowsArbitraryFiles() throws Exception {
scratch.file("badlib/not-a-jar.txt", "foo");
checkError(
"badlib",
"library-jar",
getErrorMsgMisplacedFiles(
"jars", "java_import", "//badlib:library-jar", "//badlib:not-a-jar.txt"),
"java_import(name = 'library-jar',",
" jars = ['not-a-jar.txt'])");
}
@Test
public void testDisallowsArbitraryFilesFromGenrule() throws Exception {
checkError(
"badlib",
"library-jar",
getErrorMsgNoGoodFiles("jars", "java_import", "//badlib:library-jar", "//badlib:gen"),
"genrule(name = 'gen', outs = ['not-a-jar.txt'], cmd = '')",
"java_import(name = 'library-jar',",
" jars = [':gen'])");
}
@Test
public void testDisallowsJavaRulesInSrcs() throws Exception {
checkError(
"badlib",
"library-jar",
"//badlib:library-jar: should not refer to Java rules",
"java_library(name = 'javalib',",
" srcs = ['Javalib.java'])",
"java_import(name = 'library-jar',",
" jars = [':javalib'])");
}
@Test
public void testJavaImportExportsTransitiveProguardSpecs() throws Exception {
scratch.file(
"java/com/google/android/hello/BUILD",
"java_import(name = 'export',",
" jars = ['Export.jar'],",
" proguard_specs = ['export.pro'],",
" constraints = ['android'])",
"java_import(name = 'runtime_dep',",
" jars = ['RuntimeDep.jar'],",
" proguard_specs = ['runtime_dep.pro'],",
" constraints = ['android'])",
"java_import(name = 'lib',",
" jars = ['Lib.jar'],",
" proguard_specs = ['lib.pro'],",
" constraints = ['android'],",
" exports = [':export'],",
" runtime_deps = [':runtime_dep'])");
NestedSet<Artifact> providedSpecs =
getConfiguredTarget("//java/com/google/android/hello:lib")
.get(ProguardSpecProvider.PROVIDER)
.getTransitiveProguardSpecs();
assertThat(ActionsTestUtil.baseArtifactNames(providedSpecs))
.containsAtLeast("lib.pro_valid", "export.pro_valid", "runtime_dep.pro_valid");
}
@Test
public void testJavaImportValidatesProguardSpecs() throws Exception {
scratch.file(
"java/com/google/android/hello/BUILD",
"java_import(name = 'lib',",
" jars = ['Lib.jar'],",
" proguard_specs = ['lib.pro'],",
" constraints = ['android'])");
SpawnAction action =
(SpawnAction)
actionsTestUtil()
.getActionForArtifactEndingWith(
getOutputGroup(
getConfiguredTarget("//java/com/google/android/hello:lib"),
OutputGroupInfo.HIDDEN_TOP_LEVEL),
"lib.pro_valid");
assertWithMessage("Proguard validate action").that(action).isNotNull();
assertWithMessage("Proguard validate action input")
.that(prettyArtifactNames(action.getInputs()))
.contains("java/com/google/android/hello/lib.pro");
}
@Test
public void testJavaImportValidatesTransitiveProguardSpecs() throws Exception {
scratch.file(
"java/com/google/android/hello/BUILD",
"java_import(name = 'transitive',",
" jars = ['Transitive.jar'],",
" proguard_specs = ['transitive.pro'],",
" constraints = ['android'])",
"java_import(name = 'lib',",
" jars = ['Lib.jar'],",
" constraints = ['android'],",
" exports = [':transitive'])");
SpawnAction action =
(SpawnAction)
actionsTestUtil()
.getActionForArtifactEndingWith(
getOutputGroup(
getConfiguredTarget("//java/com/google/android/hello:lib"),
OutputGroupInfo.HIDDEN_TOP_LEVEL),
"transitive.pro_valid");
assertWithMessage("Proguard validate action").that(action).isNotNull();
assertWithMessage("Proguard validate action input")
.that(prettyArtifactNames(action.getInputs()))
.contains("java/com/google/android/hello/transitive.pro");
}
@Test
public void testNeverlinkIsPopulated() throws Exception {
scratch.file(
"java/com/google/test/BUILD",
"java_library(name = 'lib')",
"java_import(name = 'jar',",
" neverlink = 1,",
" jars = ['dummy.jar'],",
" exports = [':lib'])");
ConfiguredTarget processorTarget = getConfiguredTarget("//java/com/google/test:jar");
JavaInfo javaInfo = (JavaInfo) processorTarget.get(JavaInfo.PROVIDER.getKey());
assertThat(javaInfo.isNeverlink()).isTrue();
}
@Test
public void testTransitiveSourceJars() throws Exception {
ConfiguredTarget aTarget =
scratchConfiguredTarget(
"java/my",
"a",
"java_import(name = 'a',",
" jars = ['dummy.jar'],",
" srcjar = 'dummy-src.jar',",
" exports = [':b'])",
"java_library(name = 'b',",
" srcs = ['B.java'])");
getConfiguredTarget("//java/my:a");
Set<String> inputs =
artifactsToStrings(
JavaInfo.getProvider(JavaSourceJarsProvider.class, aTarget).getTransitiveSourceJars());
assertThat(inputs)
.isEqualTo(Sets.newHashSet("src java/my/dummy-src.jar", "bin java/my/libb-src.jar"));
}
@Test
public void testExportsRunfilesCollection() throws Exception {
scratch.file(
"java/com/google/exports/BUILD",
"java_import(name = 'other_lib',",
" data = ['foo.txt'],",
" jars = ['other.jar'])",
"java_import(name = 'lib',",
" jars = ['lib.jar'],",
" exports = [':other_lib'])",
"java_binary(name = 'tool',",
" data = [':lib'],",
" main_class = 'com.google.exports.Launcher')");
ConfiguredTarget testTarget = getConfiguredTarget("//java/com/google/exports:tool");
Runfiles runfiles = getDefaultRunfiles(testTarget);
assertThat(prettyArtifactNames(runfiles.getArtifacts()))
.containsAtLeast(
"java/com/google/exports/lib.jar",
"java/com/google/exports/other.jar",
"java/com/google/exports/foo.txt");
}
// Regression test for b/13936397: don't flatten transitive dependencies into direct deps.
@Test
public void testTransitiveDependencies() throws Exception {
scratch.file(
"java/jarlib2/BUILD",
"java_library(name = 'lib',",
" srcs = ['Lib.java'],",
" deps = ['//java/jarlib:libraryjar'])",
"java_import(name = 'library2-jar',",
" jars = ['library2.jar'],",
" exports = [':lib'])",
"java_library(name = 'javalib2',",
" srcs = ['Other.java'],",
" deps = [':library2-jar'])");
JavaCompileAction javacAction =
(JavaCompileAction) getGeneratingActionForLabel("//java/jarlib2:libjavalib2.jar");
// Direct jars should NOT include java/jarlib/libraryjar-ijar.jar
assertThat(prettyArtifactNames(getInputs(javacAction, getDirectJars(javacAction))))
.isEqualTo(
Arrays.asList(
"java/jarlib2/_ijar/library2-jar/java/jarlib2/library2-ijar.jar",
"java/jarlib2/liblib-hjar.jar"));
}
@Test
public void testRuntimeDepsAreNotOnClasspath() throws Exception {
scratch.file(
"java/com/google/runtimetest/BUILD",
"java_import(",
" name = 'import_dep',",
" jars = ['import_compile.jar'],",
" runtime_deps = ['import_runtime.jar'],",
")",
"java_library(",
" name = 'library_dep',",
" srcs = ['library_compile.java'],",
")",
"java_library(",
" name = 'depends_on_runtimedep',",
" srcs = ['dummy.java'],",
" deps = [",
" ':import_dep',",
" ':library_dep',",
" ],",
")");
OutputFileConfiguredTarget dependsOnRuntimeDep =
(OutputFileConfiguredTarget)
getFileConfiguredTarget("//java/com/google/runtimetest:libdepends_on_runtimedep.jar");
JavaCompileAction javacAction =
(JavaCompileAction) getGeneratingAction(dependsOnRuntimeDep.getArtifact());
// Direct jars should NOT include import_runtime.jar
assertThat(prettyArtifactNames(getInputs(javacAction, getDirectJars(javacAction))))
.containsExactly(
"java/com/google/runtimetest/_ijar/import_dep/java/com/google/runtimetest/import_compile-ijar.jar",
"java/com/google/runtimetest/liblibrary_dep-hjar.jar");
}
@Test
public void testDuplicateJars() throws Exception {
checkError(
"ji",
"ji-with-dupe",
// error:
"Label '//ji:a.jar' is duplicated in the 'jars' attribute of rule 'ji-with-dupe'",
// build file
"filegroup(name='jars', srcs=['a.jar'])",
"java_import(name = 'ji-with-dupe', jars = ['a.jar', 'a.jar'])");
}
@Test
public void testDuplicateJarsThroughFilegroup() throws Exception {
checkError(
"ji",
"ji-with-dupe-through-fg",
// error:
"in jars attribute of java_import rule //ji:ji-with-dupe-through-fg: a.jar is a duplicate",
// build file
"filegroup(name='jars', srcs=['a.jar'])",
"java_import(name = 'ji-with-dupe-through-fg', jars = ['a.jar', ':jars'])");
}
@Test
public void testExposesJavaProvider() throws Exception {
ConfiguredTarget jarLib = getConfiguredTarget("//java/jarlib:libraryjar");
JavaCompilationArgsProvider compilationArgsProvider =
JavaInfo.getProvider(JavaCompilationArgsProvider.class, jarLib);
assertThat(prettyArtifactNames(compilationArgsProvider.getRuntimeJars()))
.containsExactly("java/jarlib/library.jar");
}
@Test
public void testIjarCanBeDisabled() throws Exception {
useConfiguration("--nouse_ijars");
ConfiguredTarget lib =
scratchConfiguredTarget(
"java/a",
"a",
"java_library(name='a', srcs=['A.java'], deps=[':b'])",
"java_import(name='b', jars=['b.jar'])");
List<String> jars =
ActionsTestUtil.baseArtifactNames(
JavaInfo.getProvider(JavaCompilationArgsProvider.class, lib)
.getTransitiveCompileTimeJars());
assertThat(jars).doesNotContain("b-ijar.jar");
assertThat(jars).contains("b.jar");
}
@Test
public void testExports() throws Exception {
useConfiguration("--incompatible_disallow_java_import_exports");
checkError(
"ugly",
"jar",
"java_import.exports is no longer supported; use java_import.deps instead",
"java_library(name = 'dep', srcs = ['dep.java'])",
"java_import(name = 'jar',",
" jars = ['dummy.jar'],",
" exports = [':dep'])");
}
}