// 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.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.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.OutputGroupProvider;
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.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.AndroidIdeInfoProvider.SourceDirectory;
import com.google.devtools.build.lib.rules.java.JavaCompilationArgsProvider;
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.JavaSemantics;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.Arrays;
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("--rule_kind, android_library");
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 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)
.getJavaCompilationArgs().getCompileTimeJars());
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