// Copyright 2017 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.rules.android; import static com.google.common.base.Verify.verifyNotNull; import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.truth.Truth; import com.google.devtools.build.lib.actions.Action; 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.actions.FileWriteAction; import com.google.devtools.build.lib.analysis.actions.SpawnAction; import com.google.devtools.build.lib.analysis.config.BuildConfiguration; import com.google.devtools.build.lib.analysis.configuredtargets.FileConfiguredTarget; import com.google.devtools.build.lib.analysis.configuredtargets.OutputFileConfiguredTarget; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.cmdline.RepositoryName; import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.rules.android.AndroidLibraryAarProvider.Aar; 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.JavaExportsProvider; 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.JavaSemantics; import com.google.devtools.build.lib.skyframe.ConfiguredTargetAndData; import com.google.devtools.build.lib.vfs.PathFragment; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Set; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Tests for {@link AndroidLibrary}. */ @RunWith(JUnit4.class) public class AndroidLibraryTest extends AndroidBuildViewTestCase { @Test public void testSimpleLibrary() throws Exception { scratch.file("java/android/BUILD", "android_library(name = 'a',", " srcs = ['A.java'],", " )"); getConfiguredTarget("//java/android:a"); } @Test public void testBaselineCoverageArtifacts() throws Exception { useConfiguration("--collect_code_coverage"); ConfiguredTarget target = scratchConfiguredTarget("java/a", "a", "android_library(name='a', srcs=['A.java'], deps=[':b'])", "android_library(name='b', srcs=['B.java'])"); assertThat(baselineCoverageArtifactBasenames(target)).containsExactly("A.java", "B.java"); } // regression test for #3169099 @Test public void testLibrarySrcs() throws Exception { scratch.file("java/srcs/a.foo", "foo"); scratch.file("java/srcs/BUILD", "android_library(name = 'valid', srcs = ['a.java', 'b.srcjar', ':gvalid', ':gmix'])", "android_library(name = 'invalid', srcs = ['a.foo', ':ginvalid'])", "android_library(name = 'mix', srcs = ['a.java', 'a.foo'])", "genrule(name = 'gvalid', srcs = ['a.java'], outs = ['b.java'], cmd = '')", "genrule(name = 'ginvalid', srcs = ['a.java'], outs = ['b.foo'], cmd = '')", "genrule(name = 'gmix', srcs = ['a.java'], outs = ['c.java', 'c.foo'], cmd = '')" ); assertSrcsValidityForRuleType("//java/srcs", "android_library", ".java or .srcjar"); } // regression test for #3169095 @Test public void testXmbInSrcsDoesNotThrow() throws Exception { reporter.removeHandler(failFastHandler); scratchConfiguredTarget("java/xmb", "a", "android_library(name = 'a', srcs = ['a.xmb'])"); } @Test public void testSlashInIdlImportRoot() throws Exception { scratchConfiguredTarget("java/com/google/android", "avocado", "android_library(name='avocado',", " idl_parcelables=['tropical/fruit/Avocado.aidl'],", " idl_import_root='tropical/fruit')"); } @Test public void testAndroidLibraryWithIdlImportAndNoIdls() throws Exception { checkError("java/com/google/android", "lib", "Neither idl_srcs nor idl_parcelables were specified, " + "but 'idl_import_root' attribute was set", "android_library(name = 'lib',", " srcs = ['Dummy.java'],", " idl_import_root = 'src')"); } @Test public void testAndroidLibraryWithIdlImportAndIdlSrcs() throws Exception { scratchConfiguredTarget("java/com/google/android", "lib", "android_library(name = 'lib',", " idl_srcs = ['Dummy.aidl'],", " idl_import_root = 'src')"); } @Test public void testAndroidLibraryWithIdlImportAndIdlParcelables() throws Exception { scratchConfiguredTarget("java/com/google/android", "lib", "android_library(name = 'lib',", " idl_parcelables = ['src/android/DummyParcelable.aidl'],", " idl_import_root = 'src')"); } @Test public void testAndroidLibraryWithIdlImportAndBothIdlTypes() throws Exception { scratchConfiguredTarget("java/com/google/android", "lib", "android_library(name = 'lib',", " idl_srcs = ['src/android/Dummy.aidl'],", " idl_parcelables = ['src/android/DummyParcelable.aidl'],", " idl_import_root = 'src')"); } @Test public void testAndroidLibraryWithIdlImportAndEmptyLists() throws Exception { scratchConfiguredTarget("java/com/google/android", "lib", "android_library(name = 'lib',", " idl_srcs = [],", " idl_parcelables = [],", " idl_import_root = 'src')"); } @Test public void testAndroidLibraryWithIdlPreprocessed() throws Exception { scratchConfiguredTarget( "java/com/google/android", "lib", "android_library(name = 'lib',", " idl_srcs = ['src/android/Dummy.aidl'],", " idl_preprocessed = ['src/android/DummyParcelable.aidl'])"); } @Test public void testCommandLineContainsTargetLabelAndRuleKind() throws Exception { scratch.file("java/android/BUILD", "android_library(name = 'a', srcs = ['A.java'])"); JavaCompileAction javacAction = (JavaCompileAction) getGeneratingActionForLabel("//java/android:liba.jar"); String commandLine = Iterables.toString(javacAction.buildCommandLine()); assertThat(commandLine).contains("--target_label, //java/android:a"); } @Test public void testStrictAndroidDepsOff() throws Exception { useConfiguration("--strict_java_deps=OFF"); scratch.file("java/android/strict/BUILD", "android_library(name = 'b', srcs = ['B.java'])"); Artifact artifact = getFileConfiguredTarget("//java/android/strict:libb.jar").getArtifact(); JavaCompileAction compileAction = (JavaCompileAction) getGeneratingAction(artifact); assertThat(compileAction.getStrictJavaDepsMode()) .isEqualTo(BuildConfiguration.StrictDepsMode.OFF); } @Test public void testStrictAndroidDepsOn() throws Exception { scratch.file("java/android/strict/BUILD", "android_library(name = 'b', srcs = ['B.java'])"); Artifact artifact = getFileConfiguredTarget("//java/android/strict:libb.jar").getArtifact(); JavaCompileAction compileAction = (JavaCompileAction) getGeneratingAction(artifact); assertThat(compileAction.getStrictJavaDepsMode()) .isEqualTo(BuildConfiguration.StrictDepsMode.ERROR); } @Test public void testStrictAndroidDepsWarn() throws Exception { useConfiguration("--strict_android_deps=WARN"); scratch.file("java/android/strict/BUILD", "android_library(name = 'b', srcs = ['B.java'])"); Artifact artifact = getFileConfiguredTarget("//java/android/strict:libb.jar").getArtifact(); JavaCompileAction compileAction = (JavaCompileAction) getGeneratingAction(artifact); assertThat(compileAction.getStrictJavaDepsMode()) .isEqualTo(BuildConfiguration.StrictDepsMode.WARN); } @Test public void testFixDepsToolEmpty() throws Exception { scratch.file("java/android/BUILD", "android_library(name = 'b', srcs = ['B.java'])"); List commandLine = getGeneratingSpawnActionArgs( getFileConfiguredTarget("//java/android:libb.jar").getArtifact()); assertThat(commandLine).containsAllOf("--experimental_fix_deps_tool", "add_dep").inOrder(); } @Test public void testFixDepsTool() throws Exception { useConfiguration("--experimental_fix_deps_tool=auto_fixer"); scratch.file("java/android/BUILD", "android_library(name = 'b', srcs = ['B.java'])"); List commandLine = getGeneratingSpawnActionArgs( getFileConfiguredTarget("//java/android:libb.jar").getArtifact()); assertThat(commandLine).containsAllOf("--experimental_fix_deps_tool", "auto_fixer").inOrder(); } @Test public void testJavaPluginProcessorPath() throws Exception { scratch.file("java/test/BUILD", "java_library(name = 'plugin_dep',", " srcs = [ 'ProcessorDep.java'])", "java_plugin(name = 'plugin',", " srcs = ['AnnotationProcessor.java'],", " processor_class = 'com.google.process.stuff',", " deps = [ ':plugin_dep' ])", "android_library(name = 'to_be_processed',", " plugins = [':plugin'],", " srcs = ['ToBeProcessed.java'])"); ConfiguredTarget target = getConfiguredTarget("//java/test:to_be_processed"); OutputFileConfiguredTarget output = (OutputFileConfiguredTarget) getFileConfiguredTarget("//java/test:libto_be_processed.jar"); JavaCompileAction javacAction = (JavaCompileAction) getGeneratingAction(output.getArtifact()); assertThat(javacAction.getProcessorNames()).contains("com.google.process.stuff"); assertThat(javacAction.getProcessorNames()).hasSize(1); assertThat(ActionsTestUtil.baseNamesOf(javacAction.getProcessorpath())) .isEqualTo("libplugin.jar libplugin_dep.jar"); assertThat( actionsTestUtil() .predecessorClosureOf(getFilesToBuild(target), JavaSemantics.JAVA_SOURCE)) .isEqualTo("ToBeProcessed.java AnnotationProcessor.java ProcessorDep.java"); } // Same test as above, enabling the plugin through the command line. @Test public void testPluginCommandLine() throws Exception { scratch.file("java/test/BUILD", "java_library(name = 'plugin_dep',", " srcs = [ 'ProcessorDep.java'])", "java_plugin(name = 'plugin',", " srcs = ['AnnotationProcessor.java'],", " processor_class = 'com.google.process.stuff',", " deps = [ ':plugin_dep' ])", "android_library(name = 'to_be_processed',", " srcs = ['ToBeProcessed.java'])"); useConfiguration("--plugin=//java/test:plugin"); ConfiguredTarget target = getConfiguredTarget("//java/test:to_be_processed"); OutputFileConfiguredTarget output = (OutputFileConfiguredTarget) getFileConfiguredTarget("//java/test:libto_be_processed.jar"); JavaCompileAction javacAction = (JavaCompileAction) getGeneratingAction(output.getArtifact()); assertThat(javacAction.getProcessorNames()).contains("com.google.process.stuff"); assertThat(javacAction.getProcessorNames()).hasSize(1); assertThat(ActionsTestUtil.baseNamesOf(javacAction.getProcessorpath())) .isEqualTo("libplugin.jar libplugin_dep.jar"); assertThat( actionsTestUtil() .predecessorClosureOf(getFilesToBuild(target), JavaSemantics.JAVA_SOURCE)) .isEqualTo("ToBeProcessed.java AnnotationProcessor.java ProcessorDep.java"); } @Test public void testInvalidPlugin() throws Exception { checkError("java/test", "lib", // error: getErrorMsgMisplacedRules("plugins", "android_library", "//java/test:lib", "java_library", "//java/test:not_a_plugin"), // BUILD file: "java_library(name = 'not_a_plugin',", " srcs = [ 'NotAPlugin.java'])", "android_library(name = 'lib',", " plugins = [':not_a_plugin'],", " srcs = ['Lib.java'])"); } @Test public void testDisallowDepsWithoutSrcsWarning() throws Exception { useConfiguration("--experimental_allow_android_library_deps_without_srcs=true"); checkWarning("android/deps", "b", // message: "android_library will be deprecating the use of deps to export targets implicitly", // build file "android_library(name = 'a', srcs = ['a.java'])", "android_library(name = 'b', deps = [':a'])"); } @Test public void testDisallowDepsWithoutSrcsError() throws Exception { checkError("android/deps", "b", // message: "android_library will be deprecating the use of deps to export targets implicitly", // build file "android_library(name = 'a', srcs = ['a.java'])", "android_library(name = 'b', deps = [':a'])"); } @Test public void testAlwaysAllowDepsWithoutSrcsIfLocalResources() throws Exception { scratch.file("java/android/BUILD", "android_library(name = 'a', srcs = ['a.java'])", "android_library(name = 'r',", " manifest = 'AndroidManifest.xml',", " resource_files = glob(['res/**']),", " deps = [':a'])"); scratch.file("java/android/res/values/strings.xml", "Hello Android!"); useConfiguration("--experimental_allow_android_library_deps_without_srcs=false"); getConfiguredTarget("//java/android:r"); assertNoEvents(); } @Test public void testTransitiveDependencyThroughExports() throws Exception { scratch.file("java/test/BUILD", "android_library(name = 'somelib',", " srcs = ['Lib.java'],", " deps = [':somealias'])", "android_library(name = 'somealias',", " exports = [':somedep'])", "android_library(name = 'somedep',", " srcs = ['Dependency.java'],", " deps = [ ':otherdep' ])", "android_library(name = 'otherdep',", " srcs = ['OtherDependency.java'])"); ConfiguredTarget libTarget = getConfiguredTarget("//java/test:somelib"); assertThat(actionsTestUtil().predecessorClosureAsCollection(getFilesToBuild(libTarget), JavaSemantics.JAVA_SOURCE)).containsExactly( "Lib.java", "Dependency.java", "OtherDependency.java"); assertNoEvents(); } @Test public void testTransitiveStrictDeps() throws Exception { scratch.file("java/peach/BUILD", "android_library(name='a', exports=[':b'])", "android_library(name='b', srcs=['B.java'], deps=[':c'])", "android_library(name='c', srcs=['C.java'])"); useConfiguration("--strict_java_deps=ERROR"); ConfiguredTarget a = getConfiguredTarget("//java/peach:a"); Iterable compileTimeJars = ActionsTestUtil.baseArtifactNames( JavaInfo.getProvider(JavaCompilationArgsProvider.class, a).getDirectCompileTimeJars()); assertThat(compileTimeJars).contains("libb-hjar.jar"); assertThat(compileTimeJars).doesNotContain("libc-hjar.jar"); assertNoEvents(); } @Test public void testEmitOutputDeps() throws Exception { scratch.file("java/deps/BUILD", "android_library(name = 'a', exports = [':b'])", "android_library(name = 'b', srcs = ['B.java'])"); useConfiguration("--java_deps"); JavaCompileAction aAction = (JavaCompileAction) getGeneratingActionForLabel( "//java/deps:liba.jar"); List aOutputs = ActionsTestUtil.prettyArtifactNames(aAction.getOutputs()); assertThat(aOutputs).doesNotContain("java/deps/liba.jdeps"); JavaCompileAction bAction = (JavaCompileAction) getGeneratingActionForLabel( "//java/deps:libb.jar"); List bOutputs = ActionsTestUtil.prettyArtifactNames(bAction.getOutputs()); assertThat(bOutputs).contains("java/deps/libb.jdeps"); assertNoEvents(); } @Test public void testDependencyArtifactsWithExports() throws Exception { scratch.file("java/classpath/BUILD", "android_library(name = 'a', srcs = ['A.java'], deps = [':b', ':c'])", "android_library(name = 'b', exports = [':d'])", "android_library(name = 'c', srcs = ['C.java'], exports = [':e'])", "android_library(name = 'd', srcs = ['D.java'])", "android_library(name = 'e', srcs = ['E.java'])"); JavaCompileAction aAction = (JavaCompileAction) getGeneratingActionForLabel( "//java/classpath:liba.jar"); List deps = ActionsTestUtil.prettyArtifactNames(aAction.getCompileTimeDependencyArtifacts()); assertThat(deps) .containsExactly( "java/classpath/libc-hjar.jdeps", "java/classpath/libd-hjar.jdeps", "java/classpath/libe-hjar.jdeps"); assertNoEvents(); } @Test public void testSrcsLessExportsAreDisallowed() throws Exception { checkError( "java/deps", "b", "android_library will be deprecating the use of deps to export targets implicitly", "android_library(name = 'a', srcs = ['a.java'])", "android_library(name = 'b', deps = ['a'])" ); } @Test public void testExportsWithStrictJavaDepsFlag() throws Exception { scratch.file("java/exports/BUILD", "android_library(name = 'a', srcs = ['a.java'])", "android_library(name = 'b', srcs = ['b.java'], exports = ['a'])", "android_library(name = 'c', srcs = ['c.java'], deps = [':b'])"); useConfiguration("--strict_java_deps=WARN"); JavaCompileAction javacAction = (JavaCompileAction) getGeneratingActionForLabel("//java/exports:libc.jar"); assertThat(ActionsTestUtil.prettyArtifactNames(javacAction.getDirectJars())) .containsExactly("java/exports/libb-hjar.jar", "java/exports/liba-hjar.jar"); assertNoEvents(); } @Test public void testExportsRunfiles() throws Exception { scratch.file("java/exports/BUILD", "android_library(name = 'a', srcs = ['a.java'], data = ['data.txt'])", "android_library(name = 'b', srcs = ['b.java'], exports = [':a'])"); ConfiguredTarget bTarget = getConfiguredTarget("//java/exports:b"); assertThat(Arrays.asList("data.txt", "liba.jar", "libb.jar")) .isEqualTo(ActionsTestUtil.baseArtifactNames(getDefaultRunfiles(bTarget).getArtifacts())); assertNoEvents(); } @Test public void testTransitiveExports() throws Exception { scratch.file("java/com/google/exports/BUILD", "android_library(name = 'dummy',", " srcs = ['dummy.java'],", " exports = [':dummy2'])", "android_library(name = 'dummy2',", " srcs = ['dummy2.java'],", " exports = [':dummy3'])", "android_library(name = 'dummy3',", " srcs = ['dummy3.java'],", " exports = [':dummy4'])", "android_library(name = 'dummy4',", " srcs = ['dummy4.java'])"); ConfiguredTarget target = getConfiguredTarget("//java/com/google/exports:dummy"); List