blob: e3aa1a0da16fe30361c6bb2ec1442baf9b69f6a6 [file] [log] [blame]
// 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.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
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 static com.google.devtools.build.lib.actions.util.ActionsTestUtil.getFirstArtifactEndingWith;
import static com.google.devtools.build.lib.actions.util.ActionsTestUtil.hasInput;
import static com.google.devtools.build.lib.actions.util.ActionsTestUtil.prettyArtifactNames;
import static com.google.devtools.build.lib.rules.java.JavaCompileActionTestHelper.getJavacArguments;
import static com.google.devtools.build.lib.rules.java.JavaCompileActionTestHelper.getProcessorpath;
import static org.junit.Assert.assertThrows;
import com.google.common.base.Ascii;
import com.google.common.base.Joiner;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
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.FailAction;
import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
import com.google.devtools.build.lib.analysis.AnalysisResult;
import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
import com.google.devtools.build.lib.analysis.ConfiguredTarget;
import com.google.devtools.build.lib.analysis.FileProvider;
import com.google.devtools.build.lib.analysis.FilesToRunProvider;
import com.google.devtools.build.lib.analysis.OutputGroupInfo;
import com.google.devtools.build.lib.analysis.RequiredConfigFragmentsProvider;
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.util.DummyTestFragment;
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.packages.BuildType;
import com.google.devtools.build.lib.packages.FileTarget;
import com.google.devtools.build.lib.packages.Rule;
import com.google.devtools.build.lib.packages.util.BazelMockAndroidSupport;
import com.google.devtools.build.lib.rules.android.AndroidBinaryTest.WithPlatforms;
import com.google.devtools.build.lib.rules.android.AndroidBinaryTest.WithoutPlatforms;
import com.google.devtools.build.lib.rules.android.AndroidRuleClasses.MultidexMode;
import com.google.devtools.build.lib.rules.android.deployinfo.AndroidDeployInfoOuterClass.AndroidDeployInfo;
import com.google.devtools.build.lib.rules.cpp.CppFileTypes;
import com.google.devtools.build.lib.rules.cpp.CppLinkAction;
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.JavaInfo;
import com.google.devtools.build.lib.rules.java.JavaSemantics;
import com.google.devtools.build.lib.skyframe.ConfiguredTargetAndData;
import com.google.devtools.build.lib.testutil.MoreAsserts;
import com.google.devtools.build.lib.testutil.TestConstants;
import com.google.devtools.build.lib.testutil.TestRuleClassProvider;
import com.google.devtools.build.lib.util.FileType;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
/** A test for {@link com.google.devtools.build.lib.rules.android.AndroidBinary}. */
@RunWith(Suite.class)
@SuiteClasses({WithoutPlatforms.class, WithPlatforms.class})
public abstract class AndroidBinaryTest extends AndroidBuildViewTestCase {
/** Allow use of --foo as a dummy flag */
@Override
protected ConfiguredRuleClassProvider createRuleClassProvider() {
ConfiguredRuleClassProvider.Builder builder = new ConfiguredRuleClassProvider.Builder();
TestRuleClassProvider.addStandardRules(builder);
builder.addConfigurationFragment(DummyTestFragment.class);
return builder.build();
}
/** Use legacy toolchain resolution. */
@RunWith(JUnit4.class)
public static class WithoutPlatforms extends AndroidBinaryTest {
@Test
public void testAndroidSplitTransitionWithInvalidCpu() throws Exception {
scratch.file(
"test/starlark/my_rule.bzl",
"def impl(ctx): ",
" return []",
"my_rule = rule(",
" implementation = impl,",
" attrs = {",
" 'deps': attr.label_list(cfg = android_common.multi_cpu_configuration),",
" 'dep': attr.label(cfg = android_common.multi_cpu_configuration),",
" })");
scratch.file(
"test/starlark/BUILD",
"load('//test/starlark:my_rule.bzl', 'my_rule')",
"my_rule(name = 'test', deps = [':main'], dep = ':main')",
"cc_binary(name = 'main', srcs = ['main.c'])");
BazelMockAndroidSupport.setupNdk(mockToolsConfig);
// --android_cpu with --android_crosstool_top also triggers the split transition.
useConfiguration(
"--fat_apk_cpu=doesnotexist", "--android_crosstool_top=//android/crosstool:everything");
AssertionError noToolchainError =
assertThrows(AssertionError.class, () -> getConfiguredTarget("//test/starlark:test"));
assertThat(noToolchainError)
.hasMessageThat()
.contains("does not contain a toolchain for cpu 'doesnotexist'");
}
}
/** Use platform-based toolchain resolution. */
@RunWith(JUnit4.class)
public static class WithPlatforms extends AndroidBinaryTest {
@Override
protected boolean platformBasedToolchains() {
return true;
}
@Override
protected String defaultPlatformFlag() {
return String.format(
"--android_platforms=%sandroid:armeabi-v7a", TestConstants.CONSTRAINTS_PACKAGE_ROOT);
}
@Test
public void testFatApk_androidPlatformsFlag() throws Exception {
BazelMockAndroidSupport.setupNdk(mockToolsConfig);
scratch.file(
"java/foo/BUILD",
"config_setting(",
" name = 'is_x86',",
" constraint_values = ['" + TestConstants.CONSTRAINTS_PACKAGE_ROOT + "cpu:x86_32'],",
")",
"config_setting(",
" name = 'is_arm',",
" constraint_values = ['" + TestConstants.CONSTRAINTS_PACKAGE_ROOT + "cpu:armv7'],",
")",
"android_library(",
" name = 'lib',",
" srcs = ['MyLibrary.java'])",
"cc_library(",
" name = 'arm-native-lib',",
" srcs = ['armnative.so'])",
"cc_library(",
" name = 'x86-native-lib',",
" srcs = ['x86native.so'])",
"cc_library(",
" name = 'native-lib',",
" deps = select({",
" ':is_x86': [':x86-native-lib'],",
" ':is_arm': [':arm-native-lib'],",
" '//conditions:default': [],",
" }))",
"android_binary(",
" name = 'abin',",
" srcs = ['a.java'],",
" proguard_specs = [],",
" deps = ['lib', 'native-lib'],",
" manifest = 'AndroidManifest.xml',",
")");
useConfiguration(
"--android_platforms=//java/android/platforms:x86,//java/android/platforms:armeabi-v7a",
"--dynamic_mode=off");
ConfiguredTarget apk = getConfiguredTarget("//java/foo:abin");
List<String> args = getGeneratingSpawnActionArgs(getCompressedUnsignedApk(apk));
assertContainsSublist(
args,
ImmutableList.of("--resources", "java/foo/armnative.so:lib/armeabi-v7a/armnative.so"));
assertContainsSublist(
args, ImmutableList.of("--resources", "java/foo/x86native.so:lib/x86/x86native.so"));
}
}
@Before
public void setupCcToolchain() throws Exception {
getAnalysisMock().ccSupport().setupCcToolchainConfigForCpu(mockToolsConfig, "armeabi-v7a");
}
@Before
public void setup() throws Exception {
scratch.file(
"java/android/BUILD",
"android_binary(name = 'app',",
" srcs = ['A.java'],",
" manifest = 'AndroidManifest.xml',",
" resource_files = glob(['res/**']),",
" )");
scratch.file(
"java/android/res/values/strings.xml",
"<resources><string name = 'hello'>Hello Android!</string></resources>");
scratch.file("java/android/A.java", "package android; public class A {};");
if (platformBasedToolchains()) {
scratch.file(
"java/android/platforms/BUILD",
"platform(",
" name = 'x86',",
" parents = ['" + TestConstants.CONSTRAINTS_PACKAGE_ROOT + "android:armeabi-v7a'],",
" constraint_values = ['" + TestConstants.CONSTRAINTS_PACKAGE_ROOT + "cpu:x86_32'],",
")",
"platform(",
" name = 'armeabi-v7a',",
" parents = ['" + TestConstants.CONSTRAINTS_PACKAGE_ROOT + "android:armeabi-v7a'],",
" constraint_values = ['" + TestConstants.CONSTRAINTS_PACKAGE_ROOT + "cpu:armv7'],",
")");
scratch.file(
"/workspace/platform_mappings",
"platforms:",
" //java/android/platforms:armeabi-v7a",
" --cpu=armeabi-v7a",
" --android_cpu=armeabi-v7a",
" --crosstool_top=//android/crosstool:everything",
" //java/android/platforms:x86",
" --cpu=x86",
" --android_cpu=x86",
" --crosstool_top=//android/crosstool:everything",
"flags:",
" --crosstool_top=//android/crosstool:everything",
" --cpu=armeabi-v7a",
" //java/android/platforms:armv7",
" --crosstool_top=//android/crosstool:everything",
" --cpu=x86",
" //java/android/platforms:x86");
invalidatePackages(false);
}
setBuildLanguageOptions("--experimental_google_legacy_api");
}
@Test
public void testAssetsInExternalRepository() throws Exception {
FileSystemUtils.appendIsoLatin1(
scratch.resolve("WORKSPACE"), "local_repository(name='r', path='/r')");
scratch.file("/r/WORKSPACE");
scratch.file("/r/p/BUILD", "filegroup(name='assets', srcs=['a/b'])");
scratch.file("/r/p/a/b");
invalidatePackages();
scratchConfiguredTarget(
"java/a",
"a",
"android_binary(",
" name = 'a',",
" srcs = ['A.java'],",
" manifest = 'AndroidManifest.xml',",
" assets = ['@r//p:assets'],",
" assets_dir = '')");
}
@Test
public void testMultidexModeAndMainDexProguardSpecs() throws Exception {
checkError(
"java/a",
"a",
"only allowed if 'multidex' is set to 'legacy'",
"android_binary(",
" name = 'a',",
" srcs = ['A.java'],",
" manifest = 'AndroidManifest.xml',",
" main_dex_proguard_specs = ['foo'])");
}
@Test
public void testAndroidManifestWithCustomName() throws Exception {
scratchConfiguredTarget(
"java/a",
"a",
"android_binary(",
" name = 'a',",
" srcs = ['A.java'],",
" manifest = 'SomeOtherAndroidManifest.xml')");
assertNoEvents();
}
@Test
public void testMainDexProguardSpecs() throws Exception {
useConfiguration("--noincremental_dexing");
ConfiguredTarget ct =
scratchConfiguredTarget(
"java/a",
"a",
"android_binary(",
" name = 'a',",
" srcs = ['A.java'],",
" manifest = 'AndroidManifest.xml',",
" multidex = 'legacy',",
" main_dex_proguard_specs = ['a.spec'])");
Artifact intermediateJar =
artifactByPath(
ImmutableList.of(getCompressedUnsignedApk(ct)),
".apk",
".dex.zip",
".dex.zip",
"main_dex_list.txt",
"_intermediate.jar");
List<String> args = getGeneratingSpawnActionArgs(intermediateJar);
MoreAsserts.assertContainsSublist(args, "-include", "java/a/a.spec");
assertThat(Joiner.on(" ").join(args)).doesNotContain("mainDexClasses.rules");
}
@Test
public void testLegacyMainDexListGenerator() throws Exception {
scratch.file(
"java/a/BUILD",
"android_binary(",
" name = 'a',",
" srcs = ['A.java'],",
" manifest = 'AndroidManifest.xml',",
" multidex = 'legacy')");
scratch.file(
"tools/fake/BUILD",
"cc_binary(",
" name = 'generate_main_dex_list',",
" srcs = ['main.cc'])");
useConfiguration("--legacy_main_dex_list_generator=//tools/fake:generate_main_dex_list");
ConfiguredTarget binary = getConfiguredTarget("//java/a:a");
Artifact mainDexList =
ActionsTestUtil.getFirstArtifactEndingWith(
actionsTestUtil().artifactClosureOf(getFilesToBuild(binary)), "main_dex_list.txt");
List<String> args = getGeneratingSpawnActionArgs(mainDexList);
NestedSet<Artifact> mainDexInputs = getGeneratingAction(mainDexList).getInputs();
MoreAsserts.assertContainsSublist(args, "--lib", getAndroidJarPath());
MoreAsserts.assertContainsSublist(args, "--main-dex-rules", getMainDexClassesPath());
assertThat(ActionsTestUtil.baseArtifactNames(mainDexInputs)).contains("generate_main_dex_list");
assertThat(ActionsTestUtil.baseArtifactNames(mainDexInputs)).contains("a_deploy.jar");
assertThat(ActionsTestUtil.baseArtifactNames(mainDexInputs)).contains(getAndroidJarFilename());
assertThat(ActionsTestUtil.baseArtifactNames(mainDexInputs))
.contains(getMainDexClassesFilename());
assertThat(ActionsTestUtil.baseArtifactNames(mainDexInputs))
.contains("main_dex_a_proguard.cfg");
assertThat(getFirstArtifactEndingWith(mainDexInputs, "main_dex_list_creator")).isNull();
}
@Test
public void testMainDexListObfuscation() throws Exception {
useConfiguration("--noincremental_dexing");
scratch.file("/java/a/list.txt");
ConfiguredTarget ct =
scratchConfiguredTarget(
"java/a",
"a",
"android_binary(",
" name = 'a',",
" srcs = ['A.java'],",
" manifest = 'AndroidManifest.xml',",
" multidex = 'manual_main_dex',",
" proguard_generate_mapping = 1,",
" main_dex_list = 'list.txt')");
Artifact obfuscatedDexList =
artifactByPath(
ImmutableList.of(getCompressedUnsignedApk(ct)),
".apk",
".dex.zip",
".dex.zip",
"main_dex_list_obfuscated.txt");
List<String> args = getGeneratingSpawnActionArgs(obfuscatedDexList);
assertThat(args.get(0)).contains("dex_list_obfuscator");
MoreAsserts.assertContainsSublist(args, "--input", "java/a/list.txt");
}
@Test
public void testNonLegacyNativeDepsDoesNotPolluteDexSharding() throws Exception {
scratch.file(
"java/a/BUILD",
"android_binary(name = 'a',",
" manifest = 'AndroidManifest.xml',",
" multidex = 'native',",
" deps = [':cc'],",
" dex_shards = 2)",
"cc_library(name = 'cc',",
" srcs = ['cc.cc'])");
Artifact jarShard =
artifactByPath(
ImmutableList.of(getCompressedUnsignedApk(getConfiguredTarget("//java/a:a"))),
".apk",
"classes.dex.zip",
"shard1.dex.zip",
"shard1.jar.dex.zip");
NestedSet<Artifact> shardInputs = getGeneratingAction(jarShard).getInputs();
assertThat(getFirstArtifactEndingWith(shardInputs, ".txt")).isNull();
}
@Test
public void testCcInfoDeps() throws Exception {
scratch.file(
"java/a/cc_info.bzl",
"def _impl(ctx):",
" cc_toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]",
" feature_configuration = cc_common.configure_features(",
" ctx = ctx,",
" cc_toolchain = cc_toolchain,",
" requested_features = ctx.features,",
" unsupported_features = ctx.disabled_features,",
" )",
" library_to_link = cc_common.create_library_to_link(",
" actions=ctx.actions, feature_configuration=feature_configuration, ",
" cc_toolchain = cc_toolchain, ",
" static_library=ctx.file.static_library)",
" linker_input = cc_common.create_linker_input(",
" libraries = depset([library_to_link]),",
" user_link_flags=depset(ctx.attr.user_link_flags),",
" owner = ctx.label,",
" )",
" linking_context = cc_common.create_linking_context(",
" linker_inputs=depset([linker_input]))",
" return [CcInfo(linking_context=linking_context)]",
"cc_info = rule(",
" implementation=_impl,",
" fragments = [\"cpp\"],",
" attrs = {",
" 'srcs': attr.label_list(allow_files=True),",
" 'user_link_flags' : attr.string_list(),",
" 'static_library': attr.label(allow_single_file=True),",
" '_cc_toolchain': attr.label(default=Label('//java/a:alias'))",
" },",
");");
scratch.file(
"java/a/BUILD",
"load('//java/a:cc_info.bzl', 'cc_info')",
"cc_toolchain_alias(name='alias')",
"android_binary(",
" name = 'a',",
" manifest = 'AndroidManifest.xml',",
" multidex = 'native',",
" deps = [':cc_info'],",
")",
"cc_info(",
" name = 'cc_info',",
" user_link_flags = ['-first_flag', '-second_flag'],",
" static_library = 'cc_info.a',",
")");
ConfiguredTarget app = getConfiguredTarget("//java/a:a");
assertNoEvents();
Artifact copiedLib = getOnlyElement(getNativeLibrariesInApk(app));
Artifact linkedLib = getGeneratingAction(copiedLib).getInputs().getSingleton();
CppLinkAction action = (CppLinkAction) getGeneratingAction(linkedLib);
assertThat(action.getArguments()).containsAtLeast("-first_flag", "-second_flag");
NestedSet<Artifact> linkInputs = action.getInputs();
assertThat(ActionsTestUtil.baseArtifactNames(linkInputs)).contains("cc_info.a");
}
@Test
public void testCcInfoDepsViaAndroidLibrary() throws Exception {
scratch.file(
"java/a/cc_info.bzl",
"def _impl(ctx):",
" cc_toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]",
" feature_configuration = cc_common.configure_features(",
" ctx = ctx,",
" cc_toolchain = cc_toolchain,",
" requested_features = ctx.features,",
" unsupported_features = ctx.disabled_features,",
" )",
" library_to_link = cc_common.create_library_to_link(",
" actions=ctx.actions, feature_configuration=feature_configuration, ",
" cc_toolchain = cc_toolchain, ",
" static_library=ctx.file.static_library)",
" linker_input = cc_common.create_linker_input(",
" libraries = depset([library_to_link]),",
" user_link_flags=depset(ctx.attr.user_link_flags),",
" owner = ctx.label,",
" )",
" linking_context = cc_common.create_linking_context(",
" linker_inputs=depset([linker_input]))",
" return [CcInfo(linking_context=linking_context)]",
"cc_info = rule(",
" implementation=_impl,",
" fragments = [\"cpp\"],",
" attrs = {",
" 'srcs': attr.label_list(allow_files=True),",
" 'user_link_flags' : attr.string_list(),",
" 'static_library': attr.label(allow_single_file=True),",
" '_cc_toolchain': attr.label(default=Label('//java/a:alias'))",
" },",
");");
scratch.file(
"java/a/BUILD",
"load('//java/a:cc_info.bzl', 'cc_info')",
"cc_toolchain_alias(name='alias')",
"android_binary(",
" name = 'a',",
" manifest = 'AndroidManifest.xml',",
" multidex = 'native',",
" deps = [':liba'],",
")",
"android_library(",
" name = 'liba',",
" srcs = ['a.java'],",
" deps = [':cc_info'],",
")",
"cc_info(",
" name = 'cc_info',",
" user_link_flags = ['-first_flag', '-second_flag'],",
" static_library = 'cc_info.a',",
")");
ConfiguredTarget app = getConfiguredTarget("//java/a:a");
Artifact copiedLib = getOnlyElement(getNativeLibrariesInApk(app));
Artifact linkedLib = getGeneratingAction(copiedLib).getInputs().getSingleton();
CppLinkAction action = (CppLinkAction) getGeneratingAction(linkedLib);
assertThat(action.getArguments()).containsAtLeast("-first_flag", "-second_flag");
NestedSet<Artifact> linkInputs = action.getInputs();
assertThat(ActionsTestUtil.baseArtifactNames(linkInputs)).contains("cc_info.a");
}
@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_binary(name = 'to_be_processed',",
" manifest = 'AndroidManifest.xml',",
" plugins = [':plugin'],",
" srcs = ['ToBeProcessed.java'])");
ConfiguredTarget target = getConfiguredTarget("//java/test:to_be_processed");
JavaCompileAction javacAction =
(JavaCompileAction) getGeneratingAction(getBinArtifact("libto_be_processed.jar", target));
assertThat(getProcessorNames(javacAction)).contains("com.google.process.stuff");
assertThat(getProcessorNames(javacAction)).hasSize(1);
assertThat(
ActionsTestUtil.baseArtifactNames(
getInputs(javacAction, getProcessorpath(javacAction))))
.containsExactly("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_binary(name = 'to_be_processed',",
" manifest = 'AndroidManifest.xml',",
" srcs = ['ToBeProcessed.java'])");
useConfiguration("--plugin=//java/test:plugin");
ConfiguredTarget target = getConfiguredTarget("//java/test:to_be_processed");
JavaCompileAction javacAction =
(JavaCompileAction) getGeneratingAction(getBinArtifact("libto_be_processed.jar", target));
assertThat(getProcessorNames(javacAction)).contains("com.google.process.stuff");
assertThat(getProcessorNames(javacAction)).hasSize(1);
assertThat(
ActionsTestUtil.baseArtifactNames(
getInputs(javacAction, getProcessorpath(javacAction))))
.containsExactly("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:
getErrorMsgMandatoryProviderMissing("//java/test:not_a_plugin", "JavaPluginInfo"),
// BUILD file:
"java_library(name = 'not_a_plugin',",
" srcs = [ 'NotAPlugin.java'])",
"android_binary(name = 'lib',",
" plugins = [':not_a_plugin'],",
" manifest = 'AndroidManifest.xml',",
" srcs = ['Lib.java'])");
}
@Test
public void testBaselineCoverageArtifacts() throws Exception {
useConfiguration("--collect_code_coverage");
ConfiguredTarget target =
scratchConfiguredTarget(
"java/com/google/a",
"bin",
"android_binary(name='bin', srcs=['Main.java'], manifest='AndroidManifest.xml')");
assertThat(baselineCoverageArtifactBasenames(target)).containsExactly("Main.java");
}
@Test
public void testSameSoFromMultipleDeps() throws Exception {
scratch.file(
"java/d/BUILD",
"genrule(name='genrule', srcs=[], outs=['genrule.so'], cmd='')",
"cc_library(name='cc1', srcs=[':genrule.so'])",
"cc_library(name='cc2', srcs=[':genrule.so'])",
"android_binary(name='ab', deps=[':cc1', ':cc2'], manifest='AndroidManifest.xml')");
getConfiguredTarget("//java/d:ab");
}
@Test
public void testSimpleBinary_desugarJava8() throws Exception {
useConfiguration("--experimental_desugar_for_android");
ConfiguredTarget binary = getConfiguredTarget("//java/android:app");
SpawnAction action =
(SpawnAction)
actionsTestUtil()
.getActionForArtifactEndingWith(
actionsTestUtil().artifactClosureOf(getFilesToBuild(binary)), "_deploy.jar");
assertThat(ActionsTestUtil.baseArtifactNames(action.getInputs()))
.contains("libapp.jar_desugared.jar");
assertThat(ActionsTestUtil.baseArtifactNames(action.getInputs())).doesNotContain("libapp.jar");
}
/**
* Tests that --experimental_check_desugar_deps causes the relevant flags to be set on desugaring
* and singlejar actions, and makes sure the deploy jar is built even when just building an APK.
*/
@Test
public void testSimpleBinary_checkDesugarDepsAlwaysHappens() throws Exception {
useConfiguration("--experimental_check_desugar_deps");
ConfiguredTarget binary = getConfiguredTarget("//java/android:app");
assertNoEvents();
// 1. Find app's deploy jar and make sure checking flags are set for it and its inputs
SpawnAction singlejar =
(SpawnAction)
actionsTestUtil()
.getActionForArtifactEndingWith(getFilesToBuild(binary), "/app_deploy.jar");
assertThat(getGeneratingSpawnActionArgs(singlejar.getPrimaryOutput()))
.contains("--check_desugar_deps");
SpawnAction desugar =
(SpawnAction)
actionsTestUtil()
.getActionForArtifactEndingWith(singlejar.getInputs(), "/libapp.jar_desugared.jar");
assertThat(desugar).isNotNull();
assertThat(getGeneratingSpawnActionArgs(desugar.getPrimaryOutput()))
.contains("--emit_dependency_metadata_as_needed");
// 2. Make sure all APK outputs depend on the deploy Jar.
int found = 0;
for (Artifact built : getFilesToBuild(binary).toList()) {
if (built.getExtension().equals("apk")) {
// If this assertion breaks then APK artifacts have stopped depending on deploy jars.
// If that's desired then we'll need to make sure dependency checking is done in another
// action that APK artifacts depend on, in addition to the check that happens when building
// deploy.jars, which we assert above.
assertWithMessage("%s dependency on deploy.jar", built.getFilename())
.that(actionsTestUtil().artifactClosureOf(built))
.contains(singlejar.getPrimaryOutput());
++found;
}
}
assertThat(found).isEqualTo(2 /* signed and unsigned apks */);
}
@Test
public void testSimpleBinary_dexNoMinSdkVersion() throws Exception {
scratch.overwriteFile(
"java/android/BUILD",
"android_binary(name = 'app',",
" srcs = ['A.java'],",
" manifest = 'AndroidManifest.xml',",
" resource_files = glob(['res/**']),",
" multidex = 'legacy',",
" )");
useConfiguration("--experimental_desugar_java8_libs");
ConfiguredTarget binary = getConfiguredTarget("//java/android:app");
List<String> args =
getGeneratingSpawnActionArgs(
ActionsTestUtil.getFirstArtifactEndingWith(
actionsTestUtil().artifactClosureOf(getFilesToBuild(binary)),
"/libapp.jar.dex.zip"));
assertThat(args).doesNotContain("--min_sdk_version");
}
@Test
public void testSimpleBinary_dexMinSdkVersion() throws Exception {
scratch.overwriteFile(
"java/android/BUILD",
"android_binary(name = 'app',",
" srcs = ['A.java'],",
" manifest = 'AndroidManifest.xml',",
" resource_files = glob(['res/**']),",
" min_sdk_version = 28,",
" multidex = 'legacy',",
" )");
useConfiguration("--experimental_desugar_java8_libs");
ConfiguredTarget binary = getConfiguredTarget("//java/android:app");
List<String> args =
getGeneratingSpawnActionArgs(
ActionsTestUtil.getFirstArtifactEndingWith(
actionsTestUtil().artifactClosureOf(getFilesToBuild(binary)),
"/libapp.jar.dex.zip"));
assertThat(args).contains("--min_sdk_version");
assertThat(args).contains("28");
}
@Test
public void testSimpleBinary_desugarNoMinSdkVersion() throws Exception {
scratch.overwriteFile(
"java/android/BUILD",
"android_binary(name = 'app',",
" srcs = ['A.java'],",
" manifest = 'AndroidManifest.xml',",
" resource_files = glob(['res/**']),",
" multidex = 'legacy',",
" )");
useConfiguration("--experimental_desugar_java8_libs");
ConfiguredTarget binary = getConfiguredTarget("//java/android:app");
List<String> args =
getGeneratingSpawnActionArgs(
ActionsTestUtil.getFirstArtifactEndingWith(
actionsTestUtil().artifactClosureOf(getFilesToBuild(binary)),
"/libapp.jar_desugared.jar"));
assertThat(args).doesNotContain("--min_sdk_version");
}
@Test
public void testSimpleBinary_desugarMinSdkVersion() throws Exception {
scratch.overwriteFile(
"java/android/BUILD",
"android_binary(name = 'app',",
" srcs = ['A.java'],",
" manifest = 'AndroidManifest.xml',",
" resource_files = glob(['res/**']),",
" min_sdk_version = 28,",
" multidex = 'legacy',",
" )");
useConfiguration("--experimental_desugar_java8_libs");
ConfiguredTarget binary = getConfiguredTarget("//java/android:app");
List<String> args =
getGeneratingSpawnActionArgs(
ActionsTestUtil.getFirstArtifactEndingWith(
actionsTestUtil().artifactClosureOf(getFilesToBuild(binary)),
"/libapp.jar_desugared.jar"));
assertThat(args).contains("--min_sdk_version");
assertThat(args).contains("28");
}
@Test
public void testSimpleAndroidBinary_multipleMinSdkVersionOnSameLibrary() throws Exception {
scratch.overwriteFile(
"java/android/BUILD",
"android_binary(name = 'app1',",
" srcs = ['A.java'],",
" deps = ['lib'],",
" manifest = 'AndroidManifest.xml',",
" resource_files = glob(['res/**']),",
" )",
"android_binary(name = 'app2',",
" srcs = ['B.java'],",
" deps = ['lib'],",
" manifest = 'AndroidManifest.xml',",
" resource_files = glob(['res/**']),",
" min_sdk_version = 28,",
" )",
"android_library(name = 'lib',",
" srcs = ['C.java'])");
AnalysisResult result =
update(
ImmutableList.of("//java/android:app1", "//java/android:app2"),
/* keepGoing= */ true,
/* loadingPhaseThreads= */ 1,
/* doAnalysis= */ true,
eventBus);
ImmutableMap<String, ConfiguredTarget> targets =
result.getTargetsToBuild().stream()
.collect(toImmutableMap(ct -> ct.getLabel().toString(), Function.identity()));
ConfiguredTarget app1 = targets.get("//java/android:app1");
ConfiguredTarget app2 = targets.get("//java/android:app2");
// The "lib" target is built twice: once with the default minsdk from app1, and again with
// min_sdk_version = 28 from app2.
List<String> libDesugarNoMinSdkArgs =
getGeneratingSpawnActionArgs(
ActionsTestUtil.getFirstArtifactEndingWith(
actionsTestUtil().artifactClosureOf(getFilesToBuild(app1)),
"/liblib.jar_desugared.jar"));
assertThat(libDesugarNoMinSdkArgs).doesNotContain("--min_sdk_version");
List<String> libDexNoMinSdkArgs =
getGeneratingSpawnActionArgs(
ActionsTestUtil.getFirstArtifactEndingWith(
actionsTestUtil().artifactClosureOf(getFilesToBuild(app1)), "/liblib.jar.dex.zip"));
assertThat(libDexNoMinSdkArgs).doesNotContain("--min_sdk_version");
List<String> libDesugarWithMinSdkArgs =
getGeneratingSpawnActionArgs(
ActionsTestUtil.getFirstArtifactEndingWith(
actionsTestUtil().artifactClosureOf(getFilesToBuild(app2)),
"/liblib.jar_minsdk=28_desugared.jar"));
assertThat(libDesugarWithMinSdkArgs).containsAtLeast("--min_sdk_version", "28").inOrder();
List<String> libDexWithMinSdkArgs =
getGeneratingSpawnActionArgs(
ActionsTestUtil.getFirstArtifactEndingWith(
actionsTestUtil().artifactClosureOf(getFilesToBuild(app2)),
"liblib.jar--min_sdk_version=28.dex.zip"));
assertThat(libDexWithMinSdkArgs).containsAtLeast("--min_sdk_version", "28").inOrder();
}
// regression test for #3169099
@Test
public void testBinarySrcs() throws Exception {
scratch.file("java/srcs/a.foo", "foo");
scratch.file(
"java/srcs/BUILD",
"android_binary(name = 'valid', manifest = 'AndroidManifest.xml', "
+ "srcs = ['a.java', 'b.srcjar', ':gvalid', ':gmix'])",
"android_binary(name = 'invalid', manifest = 'AndroidManifest.xml', "
+ "srcs = ['a.foo', ':ginvalid'])",
"android_binary(name = 'mix', manifest = 'AndroidManifest.xml', "
+ "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_binary", ".java or .srcjar");
}
// regression test for #3169095
@Test
public void testXmbInSrcs_notPermittedButDoesNotThrow() throws Exception {
reporter.removeHandler(failFastHandler);
scratchConfiguredTarget(
"java/xmb",
"a",
"android_binary(name = 'a', manifest = 'AndroidManifest.xml', srcs = ['a.xmb'])");
// We expect there to be an error here because a.xmb is not a valid src,
// and more importantly, no exception to have been thrown.
assertContainsEvent(
"in srcs attribute of android_binary rule //java/xmb:a: "
+ "target '//java/xmb:a.xmb' does not exist");
}
@Test
public void testNativeLibraryBasenameCollision() throws Exception {
reporter.removeHandler(failFastHandler); // expect errors
scratch.file(
"java/android/common/BUILD",
"cc_library(name = 'libcommon_armeabi',",
" srcs = ['armeabi/native.so'],)");
scratch.file(
"java/android/app/BUILD",
"cc_library(name = 'libnative',",
" srcs = ['native.so'],)",
"android_binary(name = 'b',",
" srcs = ['A.java'],",
" deps = [':libnative', '//java/android/common:libcommon_armeabi'],",
" manifest = 'AndroidManifest.xml',",
" )");
getConfiguredTarget("//java/android/app:b");
assertContainsEvent(
"Each library in the transitive closure must have a unique basename to avoid name"
+ " collisions when packaged into an apk, but two libraries have the basename"
+ " 'native.so': java/android/common/armeabi/native.so and"
+ " java/android/app/native.so");
}
private void setupNativeLibrariesForLinking() throws Exception {
scratch.file(
"java/android/common/BUILD",
"cc_library(name = 'common_native',",
" srcs = ['common.cc'],)",
"android_library(name = 'common',",
" exports = [':common_native'],)");
scratch.file(
"java/android/app/BUILD",
"cc_library(name = 'native',",
" srcs = ['native.cc'],)",
"android_binary(name = 'auto',",
" srcs = ['A.java'],",
" deps = [':native', '//java/android/common:common'],",
" manifest = 'AndroidManifest.xml',",
" )",
"android_binary(name = 'off',",
" srcs = ['A.java'],",
" deps = [':native', '//java/android/common:common'],",
" manifest = 'AndroidManifest.xml',",
" )");
}
private void assertNativeLibraryLinked(ConfiguredTarget target, String... srcNames) {
Artifact linkedLib = getOnlyElement(getNativeLibrariesInApk(target));
assertThat(linkedLib.getFilename())
.isEqualTo("lib" + target.getLabel().toPathFragment().getBaseName() + ".so");
assertThat(linkedLib.isSourceArtifact()).isFalse();
assertWithMessage("Native libraries were not linked to produce " + linkedLib)
.that(getGeneratingLabelForArtifact(linkedLib))
.isEqualTo(target.getLabel());
assertThat(artifactsToStrings(actionsTestUtil().artifactClosureOf(linkedLib)))
.containsAtLeastElementsIn(ImmutableSet.copyOf(Arrays.asList(srcNames)));
}
@Test
public void testNativeLibrary_linksLibrariesWhenCodeIsPresent() throws Exception {
setupNativeLibrariesForLinking();
assertNativeLibraryLinked(
getConfiguredTarget("//java/android/app:auto"),
"src java/android/common/common.cc",
"src java/android/app/native.cc");
assertNativeLibraryLinked(
getConfiguredTarget("//java/android/app:off"),
"src java/android/common/common.cc",
"src java/android/app/native.cc");
}
@Test
public void testNativeLibrary_copiesLibrariesDespiteExtraLayersOfIndirection() throws Exception {
setBuildLanguageOptions(
"--experimental_builtins_injection_override=+cc_library",
"--experimental_google_legacy_api");
scratch.file(
"java/android/app/BUILD",
"cc_library(name = 'native_dep',",
" srcs = ['dep.so'])",
"cc_library(name = 'native',",
" srcs = ['native_prebuilt.so'],",
" deps = [':native_dep'])",
"cc_library(name = 'native_wrapper',",
" deps = [':native'])",
"android_binary(name = 'app',",
" srcs = ['A.java'],",
" deps = [':native_wrapper'],",
" manifest = 'AndroidManifest.xml',",
" )");
ConfiguredTarget app = getConfiguredTarget("//java/android/app:app");
assertNativeLibrariesCopiedNotLinked(
app,
getConfiguration(getDirectPrerequisite(app, "//java/android/app:native_wrapper")),
"src java/android/app/dep.so",
"src java/android/app/native_prebuilt.so");
}
@Test
public void testNativeLibrary_copiesLibrariesWrappedInCcLibraryWithSameName() throws Exception {
scratch.file(
"java/android/app/BUILD",
"cc_library(name = 'native',",
" srcs = ['libnative.so'])",
"android_binary(name = 'app',",
" srcs = ['A.java'],",
" deps = [':native'],",
" manifest = 'AndroidManifest.xml',",
" )");
ConfiguredTarget app = getConfiguredTarget("//java/android/app:app");
assertNativeLibrariesCopiedNotLinked(
app,
getConfiguration(getDirectPrerequisite(app, "//java/android/app:native")),
"src java/android/app/libnative.so");
}
@Test
public void testNativeLibrary_linksWhenPrebuiltArchiveIsSupplied() throws Exception {
scratch.file(
"java/android/app/BUILD",
"cc_library(name = 'native_dep',",
" srcs = ['dep.lo'])",
"cc_library(name = 'native',",
" srcs = ['native_prebuilt.a'],",
" deps = [':native_dep'])",
"cc_library(name = 'native_wrapper',",
" deps = [':native'])",
"android_binary(name = 'app',",
" srcs = ['A.java'],",
" deps = [':native_wrapper'],",
" manifest = 'AndroidManifest.xml',",
" )");
assertNativeLibraryLinked(
getConfiguredTarget("//java/android/app:app"), "src java/android/app/native_prebuilt.a");
}
@Test
public void testNativeLibrary_copiesFullLibrariesInIfsoMode() throws Exception {
useConfiguration("--interface_shared_objects");
scratch.file(
"java/android/app/BUILD",
"cc_library(name = 'native_dep',",
" srcs = ['dep.so'])",
"cc_library(name = 'native',",
" srcs = ['native.cc', 'native_prebuilt.so'],",
" deps = [':native_dep'])",
"android_binary(name = 'app',",
" srcs = ['A.java'],",
" deps = [':native'],",
" manifest = 'AndroidManifest.xml',",
" )");
ConfiguredTarget app = getConfiguredTarget("//java/android/app:app");
Iterable<Artifact> nativeLibraries = getNativeLibrariesInApk(app);
assertThat(artifactsToStrings(nativeLibraries))
.containsAtLeast("src java/android/app/native_prebuilt.so", "src java/android/app/dep.so");
assertThat(FileType.filter(nativeLibraries, CppFileTypes.INTERFACE_SHARED_LIBRARY)).isEmpty();
}
@Test
public void testNativeLibrary_providesLinkerScriptToLinkAction() throws Exception {
scratch.file(
"java/android/app/BUILD",
"cc_library(name = 'native',",
" srcs = ['native.cc'],",
" linkopts = ['-Wl,-version-script', '$(location jni.lds)'],",
" deps = ['jni.lds'],)",
"android_binary(name = 'app',",
" srcs = ['A.java'],",
" deps = [':native'],",
" manifest = 'AndroidManifest.xml',",
" )");
ConfiguredTarget app = getConfiguredTarget("//java/android/app:app");
Artifact copiedLib = getOnlyElement(getNativeLibrariesInApk(app));
Artifact linkedLib = getGeneratingAction(copiedLib).getInputs().getSingleton();
NestedSet<Artifact> linkInputs = getGeneratingAction(linkedLib).getInputs();
assertThat(ActionsTestUtil.baseArtifactNames(linkInputs)).contains("jni.lds");
}
/** Regression test for http://b/33173461. */
@Test
public void testIncrementalDexingUsesDexArchives_binaryDependingOnAliasTarget() throws Exception {
useConfiguration(
"--experimental_use_dex_splitter_for_incremental_dexing=false",
"--experimental_incremental_dexing_after_proguard_by_default=false",
"--experimental_incremental_dexing_after_proguard=1");
scratch.file(
"java/com/google/android/BUILD",
"android_library(",
" name = 'dep',",
" srcs = ['dep.java'],",
" manifest = 'AndroidManifest.xml',",
")",
"alias(",
" name = 'alt',",
" actual = ':dep',",
")",
"android_binary(",
" name = 'top',",
" srcs = ['foo.java', 'bar.srcjar'],",
" multidex = 'native',",
" manifest = 'AndroidManifest.xml',",
" deps = [':alt',],",
")");
ConfiguredTarget topTarget = getConfiguredTarget("//java/com/google/android:top");
assertNoEvents();
Action shardAction = getGeneratingAction(getBinArtifact("_dx/top/classes.jar", topTarget));
for (Artifact input : getNonToolInputs(shardAction)) {
String basename = input.getFilename();
// all jars are converted to dex archives
assertWithMessage(basename)
.that(!basename.contains(".jar") || basename.endsWith(".jar.dex.zip"))
.isTrue();
// all jars are desugared before being converted
if (basename.endsWith(".jar.dex.zip")) {
assertThat(getGeneratingAction(input).getPrimaryInput().getFilename())
.isEqualTo(
basename.substring(0, basename.length() - ".jar.dex.zip".length())
+ ".jar_desugared.jar");
}
}
// Make sure exactly the dex archives generated for top and dependents appear. We also *don't*
// want neverlink and unused_dep to appear, and to be safe we do so by explicitly enumerating
// *all* expected input dex archives.
assertThat(
Iterables.filter(
ActionsTestUtil.baseArtifactNames(getNonToolInputs(shardAction)),
Predicates.containsPattern("\\.jar")))
.containsExactly(
// top's dex archives
"libtop.jar.dex.zip",
"top_resources.jar.dex.zip",
// dep's dex archives
"libdep.jar.dex.zip");
}
@Test
public void testIncrementalDexingDisabledWithBlacklistedDexopts() throws Exception {
// Even if we mark a dx flag as supported, incremental dexing isn't used with disallowlisted
// dexopts (unless incremental_dexing attribute is set, which a different test covers)
useConfiguration(
"--incremental_dexing",
"--non_incremental_per_target_dexopts=--no-locals",
"--dexopts_supported_in_incremental_dexing=--no-locals");
scratch.file(
"java/com/google/android/BUILD",
"android_binary(",
" name = 'top',",
" srcs = ['foo.java', 'bar.srcjar'],",
" manifest = 'AndroidManifest.xml',",
" dexopts = ['--no-locals'],",
" dex_shards = 2,",
" multidex = 'native',",
")");
ConfiguredTarget topTarget = getConfiguredTarget("//java/com/google/android:top");
assertNoEvents();
Action shardAction = getGeneratingAction(getBinArtifact("_dx/top/shard1.jar", topTarget));
assertThat(
Iterables.filter(
ActionsTestUtil.baseArtifactNames(getNonToolInputs(shardAction)),
Predicates.containsPattern("\\.jar\\.dex\\.zip")))
.isEmpty(); // no dex archives are used
}
@Test
public void testIncrementalDexingDisabledWithProguard() throws Exception {
useConfiguration(
"--experimental_use_dex_splitter_for_incremental_dexing=false",
"--experimental_incremental_dexing_after_proguard_by_default=false",
"--experimental_incremental_dexing_after_proguard=1");
scratch.file(
"java/com/google/android/BUILD",
"android_binary(",
" name = 'top',",
" srcs = ['foo.java', 'bar.srcjar'],",
" manifest = 'AndroidManifest.xml',",
" proguard_specs = ['proguard.cfg'],",
")");
ConfiguredTarget topTarget = getConfiguredTarget("//java/com/google/android:top");
assertNoEvents();
Action dexAction =
getGeneratingAction(getBinArtifact("_dx/top/intermediate_classes.dex.zip", topTarget));
assertThat(
Iterables.filter(
ActionsTestUtil.baseArtifactNames(dexAction.getInputs()),
Predicates.containsPattern("\\.jar")))
.containsExactly("top_proguard.jar", "dx_binary.jar"); // proguard output is used directly
}
@Test
public void testIncrementalDexing_incompatibleWithProguardWhenDisabled() throws Exception {
useConfiguration(
"--experimental_incremental_dexing_after_proguard=0", // disable with Proguard
"--experimental_use_dex_splitter_for_incremental_dexing=false",
"--experimental_incremental_dexing_after_proguard_by_default=false");
checkError(
"java/com/google/android",
"top",
"target cannot be incrementally dexed",
"android_binary(",
" name = 'top',",
" srcs = ['foo.java', 'bar.srcjar'],",
" manifest = 'AndroidManifest.xml',",
" proguard_specs = ['proguard.cfg'],",
" incremental_dexing = 1,",
")");
}
@Test
public void testIncrementalDexingAfterProguard_unsharded() throws Exception {
useConfiguration("--experimental_incremental_dexing_after_proguard=1");
// Use "legacy" multidex mode so we get a main dex list file and can test that it's passed to
// the splitter action (similar to _withDexShards below), unlike without the dex splitter where
// the main dex list goes to the merging action.
scratch.file(
"java/com/google/android/BUILD",
"android_binary(",
" name = 'top',",
" srcs = ['foo.java', 'bar.srcjar'],",
" manifest = 'AndroidManifest.xml',",
" incremental_dexing = 1,",
" multidex = 'legacy',",
" dexopts = ['--minimal-main-dex', '--positions=none'],",
" proguard_specs = ['b.pro'],",
")");
ConfiguredTarget topTarget = getConfiguredTarget("//java/com/google/android:top");
assertNoEvents();
SpawnAction shardAction =
getGeneratingSpawnAction(getBinArtifact("_dx/top/classes.dex.zip", topTarget));
assertThat(shardAction.getArguments()).contains("--main-dex-list");
assertThat(shardAction.getArguments()).contains("--minimal-main-dex");
assertThat(ActionsTestUtil.baseArtifactNames(getNonToolInputs(shardAction)))
.containsExactly("classes.jar", "main_dex_list.txt");
// --positions dexopt is supported after Proguard, even though not normally otherwise
assertThat(
paramFileArgsForAction(
getGeneratingSpawnAction(getBinArtifact("_dx/top/classes.jar", topTarget))))
.contains("--positions=none");
}
@Test
public void testIncrementalDexingAfterProguard_autoShardedMultidexAutoOptIn() throws Exception {
useConfiguration(
"--experimental_incremental_dexing_after_proguard=3",
"--experimental_incremental_dexing_after_proguard_by_default");
// Use "legacy" multidex mode so we get a main dex list file and can test that it's passed to
// the splitter action (similar to _withDexShards below), unlike without the dex splitter where
// the main dex list goes to the merging action.
scratch.file(
"java/com/google/android/BUILD",
"android_binary(",
" name = 'top',",
" srcs = ['foo.java', 'bar.srcjar'],",
" manifest = 'AndroidManifest.xml',",
" multidex = 'legacy',",
" dexopts = ['--minimal-main-dex', '--positions=none'],",
" proguard_specs = ['b.pro'],",
")"); // incremental_dexing = 1 attribute not needed
ConfiguredTarget topTarget = getConfiguredTarget("//java/com/google/android:top");
assertNoEvents();
SpawnAction splitAction = getGeneratingSpawnAction(getTreeArtifact("dexsplits/top", topTarget));
assertThat(splitAction.getArguments()).contains("--main-dex-list");
assertThat(splitAction.getArguments()).contains("--minimal-main-dex");
assertThat(ActionsTestUtil.baseArtifactNames(getNonToolInputs(splitAction)))
.containsExactly(
"shard1.jar.dex.zip", "shard2.jar.dex.zip", "shard3.jar.dex.zip", "main_dex_list.txt");
SpawnAction shuffleAction =
getGeneratingSpawnAction(getBinArtifact("_dx/top/shard1.jar", topTarget));
assertThat(shuffleAction.getArguments()).doesNotContain("--main-dex-list");
assertThat(ActionsTestUtil.baseArtifactNames(getNonToolInputs(shuffleAction)))
.containsExactly("top_proguard.jar");
// --positions dexopt is supported after Proguard, even though not normally otherwise
assertThat(
paramFileArgsForAction(
getGeneratingSpawnAction(getBinArtifact("_dx/top/shard3.jar.dex.zip", topTarget))))
.contains("--positions=none");
}
@Test
public void testIncrementalDexingAfterProguard_explicitDexShards() throws Exception {
useConfiguration("--experimental_incremental_dexing_after_proguard=2");
// Use "legacy" multidex mode so we get a main dex list file and can test that it's passed to
// the shardAction, not to the subsequent dexMerger action. Without dex_shards, main dex list
// file goes to the dexMerger instead (see _multidex test).
scratch.file(
"java/com/google/android/BUILD",
"android_binary(",
" name = 'top',",
" srcs = ['foo.java', 'bar.srcjar'],",
" manifest = 'AndroidManifest.xml',",
" dex_shards = 25,",
" incremental_dexing = 1,",
" multidex = 'legacy',",
" proguard_specs = ['b.pro'],",
")");
ConfiguredTarget topTarget = getConfiguredTarget("//java/com/google/android:top");
assertNoEvents();
SpawnAction shardAction =
getGeneratingSpawnAction(getBinArtifact("_dx/top/shard25.jar", topTarget));
assertThat(shardAction.getArguments()).contains("--main_dex_filter");
assertThat(ActionsTestUtil.baseArtifactNames(getNonToolInputs(shardAction)))
.containsExactly("top_proguard.jar", "main_dex_list.txt");
SpawnAction mergeAction =
getGeneratingSpawnAction(getBinArtifact("_dx/top/shard1.jar.dex.zip", topTarget));
assertThat(mergeAction.getArguments()).doesNotContain("--main-dex-list");
assertThat(ActionsTestUtil.baseArtifactNames(getNonToolInputs(mergeAction)))
.contains("shard1.jar");
}
@Test
public void testV1SigningMethod() throws Exception {
actualSignerToolTests("v1", "true", "false", null);
}
@Test
public void testV2SigningMethod() throws Exception {
actualSignerToolTests("v2", "false", "true", null);
}
@Test
public void testV1V2SigningMethod() throws Exception {
actualSignerToolTests("v1_v2", "true", "true", null);
}
@Test
public void testV4SigningMethod() throws Exception {
actualSignerToolTests("v4", "false", "false", "true");
}
private void actualSignerToolTests(
String apkSigningMethod, String signV1, String signV2, String signV4) throws Exception {
scratch.file(
"java/com/google/android/hello/BUILD",
"android_binary(name = 'hello',",
" srcs = ['Foo.java'],",
" manifest = 'AndroidManifest.xml',)");
useConfiguration("--apk_signing_method=" + apkSigningMethod);
ConfiguredTarget binary = getConfiguredTarget("//java/com/google/android/hello:hello");
Set<Artifact> artifacts = actionsTestUtil().artifactClosureOf(getFilesToBuild(binary));
assertThat(getFirstArtifactEndingWith(artifacts, "signed_hello.apk")).isNull();
SpawnAction unsignedApkAction =
(SpawnAction)
actionsTestUtil().getActionForArtifactEndingWith(artifacts, "/hello_unsigned.apk");
assertThat(
unsignedApkAction.getInputs().toList().stream()
.map(Artifact::getFilename)
.anyMatch(filename -> Ascii.toLowerCase(filename).contains("singlejar")))
.isTrue();
SpawnAction compressedUnsignedApkAction =
(SpawnAction)
actionsTestUtil()
.getActionForArtifactEndingWith(artifacts, "compressed_hello_unsigned.apk");
assertThat(
compressedUnsignedApkAction.getInputs().toList().stream()
.map(Artifact::getFilename)
.anyMatch(filename -> Ascii.toLowerCase(filename).contains("singlejar")))
.isTrue();
SpawnAction zipalignAction =
(SpawnAction)
actionsTestUtil().getActionForArtifactEndingWith(artifacts, "zipaligned_hello.apk");
assertThat(zipalignAction.getCommandFilename()).endsWith("zipalign");
Artifact a = ActionsTestUtil.getFirstArtifactEndingWith(artifacts, "hello.apk");
assertThat(getGeneratingSpawnAction(a).getCommandFilename()).endsWith("ApkSignerBinary");
List<String> args = getGeneratingSpawnActionArgs(a);
assertThat(flagValue("--v1-signing-enabled", args)).isEqualTo(signV1);
assertThat(flagValue("--v2-signing-enabled", args)).isEqualTo(signV2);
if (signV4 != null) {
assertThat(flagValue("--v4-signing-enabled", args)).isEqualTo(signV4);
if (signV4.equals("true")) {
assertThat(getFirstArtifactEndingWith(artifacts, "hello.apk.idsig")).isNotNull();
}
} else {
assertThat(args).doesNotContain("--v4-signing-enabled");
}
}
@Test
public void testResourcePathShortening_flagEnabledAndCOpt_optimizedApkIsInputToApkBuilderAction()
throws Exception {
useConfiguration("--experimental_android_resource_path_shortening", "-c", "opt");
ConfiguredTarget binary = getConfiguredTarget("//java/android:app");
Set<Artifact> artifacts = actionsTestUtil().artifactClosureOf(getFilesToBuild(binary));
SpawnAction optimizeAction =
getGeneratingSpawnAction(getFirstArtifactEndingWith(artifacts, "app_optimized.ap_"));
assertThat(optimizeAction.getMnemonic()).isEqualTo("Aapt2Optimize");
assertThat(getGeneratingAction(getFirstArtifactEndingWith(artifacts, "app_resource_paths.map")))
.isEqualTo(optimizeAction);
List<String> processingArgs = optimizeAction.getArguments();
assertThat(processingArgs).contains("--shorten-resource-paths");
assertThat(flagValue("--resource-path-shortening-map", processingArgs))
.endsWith("app_resource_paths.map");
// Verify that the optimized APK is an input to build the unsigned APK.
SpawnAction apkAction =
getGeneratingSpawnAction(getFirstArtifactEndingWith(artifacts, "app_unsigned.apk"));
assertThat(apkAction.getMnemonic()).isEqualTo("ApkBuilder");
assertThat(hasInput(apkAction, "app_optimized.ap_")).isTrue();
}
@Test
public void testResourcePathShortening_flagEnabledAndCDefault_optimizeArtifactsAbsent()
throws Exception {
useConfiguration("--experimental_android_resource_path_shortening");
ConfiguredTarget binary = getConfiguredTarget("//java/android:app");
Set<Artifact> artifacts = actionsTestUtil().artifactClosureOf(getFilesToBuild(binary));
Artifact resourcePathShorteningMapArtifact =
getFirstArtifactEndingWith(artifacts, "app_resource_paths.map");
assertThat(resourcePathShorteningMapArtifact).isNull();
Artifact optimizedResourceApk = getFirstArtifactEndingWith(artifacts, "app_optimized.ap_");
assertThat(optimizedResourceApk).isNull();
// Verify that the optimized APK is not an input to build the unsigned APK.
SpawnAction apkAction =
getGeneratingSpawnAction(getFirstArtifactEndingWith(artifacts, "app_unsigned.apk"));
assertThat(apkAction.getMnemonic()).isEqualTo("ApkBuilder");
assertThat(hasInput(apkAction, "app_optimized.ap_")).isFalse();
}
@Test
public void testResourcePathShortening_flagNotEnabledAndCOpt_optimizeArtifactsAbsent()
throws Exception {
useConfiguration("-c", "opt");
ConfiguredTarget binary = getConfiguredTarget("//java/android:app");
Set<Artifact> artifacts = actionsTestUtil().artifactClosureOf(getFilesToBuild(binary));
Artifact resourcePathShorteningMapArtifact =
getFirstArtifactEndingWith(artifacts, "app_resource_paths.map");
assertThat(resourcePathShorteningMapArtifact).isNull();
Artifact optimizedResourceApk = getFirstArtifactEndingWith(artifacts, "app_optimized.ap_");
assertThat(optimizedResourceApk).isNull();
// Verify that the optimized APK is not an input to build the unsigned APK.
SpawnAction apkAction =
getGeneratingSpawnAction(getFirstArtifactEndingWith(artifacts, "app_unsigned.apk"));
assertThat(apkAction.getMnemonic()).isEqualTo("ApkBuilder");
assertThat(hasInput(apkAction, "app_optimized.ap_")).isFalse();
}
@Test
public void resourceNameCollapse_flagAndProguardSpecsPresent_optimizedApkIsInputToApkBuilder()
throws Exception {
useConfiguration("--experimental_android_resource_name_obfuscation");
scratch.file(
"java/com/google/android/hello/BUILD",
"android_binary(name = 'hello',",
" srcs = ['Foo.java'],",
" manifest = 'AndroidManifest.xml',",
" shrink_resources = 1,",
" proguard_specs = ['proguard-spec.pro'],)");
ConfiguredTarget binary = getConfiguredTarget("//java/com/google/android/hello:hello");
Set<Artifact> artifacts = actionsTestUtil().artifactClosureOf(getFilesToBuild(binary));
SpawnAction shrinkerAction =
getGeneratingSpawnAction(
getFirstArtifactEndingWith(artifacts, "resource_optimization.cfg"));
assertThat(shrinkerAction.getMnemonic()).isEqualTo("ResourceShrinker");
SpawnAction optimizeAction =
getGeneratingSpawnAction(getFirstArtifactEndingWith(artifacts, "hello_optimized.ap_"));
assertThat(optimizeAction.getMnemonic()).isEqualTo("Aapt2Optimize");
List<String> processingArgs = optimizeAction.getArguments();
assertThat(processingArgs).contains("--collapse-resource-names");
assertThat(flagValue("--resources-config-path", processingArgs))
.endsWith("resource_optimization.cfg");
// Verify that the optimized APK is an input to build the unsigned APK.
SpawnAction apkAction =
getGeneratingSpawnAction(getFirstArtifactEndingWith(artifacts, "hello_unsigned.apk"));
assertThat(apkAction.getMnemonic()).isEqualTo("ApkBuilder");
assertThat(hasInput(apkAction, "hello_optimized.ap_")).isTrue();
}
@Test
public void resourceNameCollapse_featureAndProguardSpecsPresent_optimizedApkIsInputToApkBuilder()
throws Exception {
useConfiguration("--features=resource_name_obfuscation");
scratch.file(
"java/com/google/android/hello/BUILD",
"android_binary(name = 'hello',",
" srcs = ['Foo.java'],",
" manifest = 'AndroidManifest.xml',",
" shrink_resources = 1,",
" proguard_specs = ['proguard-spec.pro'],)");
ConfiguredTarget binary = getConfiguredTarget("//java/com/google/android/hello:hello");
Set<Artifact> artifacts = actionsTestUtil().artifactClosureOf(getFilesToBuild(binary));
SpawnAction shrinkerAction =
getGeneratingSpawnAction(
getFirstArtifactEndingWith(artifacts, "resource_optimization.cfg"));
assertThat(shrinkerAction.getMnemonic()).isEqualTo("ResourceShrinker");
SpawnAction optimizeAction =
getGeneratingSpawnAction(getFirstArtifactEndingWith(artifacts, "hello_optimized.ap_"));
assertThat(optimizeAction.getMnemonic()).isEqualTo("Aapt2Optimize");
List<String> processingArgs = optimizeAction.getArguments();
assertThat(processingArgs).contains("--collapse-resource-names");
assertThat(flagValue("--resources-config-path", processingArgs))
.endsWith("resource_optimization.cfg");
// Verify that the optimized APK is an input to build the unsigned APK.
SpawnAction apkAction =
getGeneratingSpawnAction(getFirstArtifactEndingWith(artifacts, "hello_unsigned.apk"));
assertThat(apkAction.getMnemonic()).isEqualTo("ApkBuilder");
assertThat(hasInput(apkAction, "hello_optimized.ap_")).isTrue();
}
@Test
public void resourceNameCollapse_flagPresentProguardSpecsAbsent_optimizeArtifactsAbsent()
throws Exception {
useConfiguration("--experimental_android_resource_name_obfuscation");
scratch.file(
"java/com/google/android/hello/BUILD",
"android_binary(name = 'hello',",
" srcs = ['Foo.java'],",
" manifest = 'AndroidManifest.xml',",
" shrink_resources = 1,)");
ConfiguredTarget binary = getConfiguredTarget("//java/com/google/android/hello:hello");
Set<Artifact> artifacts = actionsTestUtil().artifactClosureOf(getFilesToBuild(binary));
Artifact optimizedResourceApk = getFirstArtifactEndingWith(artifacts, "hello_optimized.ap_");
assertThat(optimizedResourceApk).isNull();
// Verify that the optimized APK is not an input to build the unsigned APK.
SpawnAction apkAction =
getGeneratingSpawnAction(getFirstArtifactEndingWith(artifacts, "hello_unsigned.apk"));
assertThat(apkAction.getMnemonic()).isEqualTo("ApkBuilder");
assertThat(hasInput(apkAction, "hello_optimized.ap_")).isFalse();
}
@Test
public void resourceNameCollapse_flagAbsentProguardSpecsPresent_optimizeArtifactsAbsent()
throws Exception {
scratch.file(
"java/com/google/android/hello/BUILD",
"android_binary(name = 'hello',",
" srcs = ['Foo.java'],",
" manifest = 'AndroidManifest.xml',",
" shrink_resources = 1,",
" proguard_specs = ['proguard-spec.pro'],)");
ConfiguredTarget binary = getConfiguredTarget("//java/com/google/android/hello:hello");
Set<Artifact> artifacts = actionsTestUtil().artifactClosureOf(getFilesToBuild(binary));
Artifact optimizedResourceApk = getFirstArtifactEndingWith(artifacts, "hello_optimized.ap_");
assertThat(optimizedResourceApk).isNull();
// Verify that the optimized APK is not an input to build the unsigned APK.
SpawnAction apkAction =
getGeneratingSpawnAction(getFirstArtifactEndingWith(artifacts, "hello_unsigned.apk"));
assertThat(apkAction.getMnemonic()).isEqualTo("ApkBuilder");
assertThat(hasInput(apkAction, "hello_optimized.ap_")).isFalse();
}
@Test
public void testResourceShrinking_requiresProguard() throws Exception {
scratch.file(
"java/com/google/android/hello/BUILD",
"android_binary(name = 'hello',",
" srcs = ['Foo.java'],",
" manifest = 'AndroidManifest.xml',",
" resource_files = ['res/values/strings.xml'],",
" shrink_resources = 1,)");
ConfiguredTarget binary = getConfiguredTarget("//java/com/google/android/hello:hello");
Set<Artifact> artifacts = actionsTestUtil().artifactClosureOf(getFilesToBuild(binary));
assertThat(artifacts)
.containsNoneOf(
getFirstArtifactEndingWith(artifacts, "shrunk.jar"),
getFirstArtifactEndingWith(artifacts, "shrunk.ap_"));
}
@Test
public void testProguardExtraOutputs() throws Exception {
scratch.file(
"java/com/google/android/hello/BUILD",
"android_binary(name = 'b',",
" srcs = ['HelloApp.java'],",
" manifest = 'AndroidManifest.xml',",
" proguard_specs = ['proguard-spec.pro'])");
ConfiguredTarget output = getConfiguredTarget("//java/com/google/android/hello:b");
// Checks that ProGuard is called with the appropriate options.
Artifact a = getFirstArtifactEndingWith(getFilesToBuild(output), "_proguard.jar");
SpawnAction action = getGeneratingSpawnAction(a);
List<String> args = getGeneratingSpawnActionArgs(a);
// Assert that the ProGuard executable set in the android_sdk rule appeared in the command-line
// of the SpawnAction that generated the _proguard.jar.
assertThat(args)
.containsAtLeast(
getProguardBinary().getExecPathString(),
"-injars",
execPathEndingWith(action.getInputs(), "b_deploy.jar"),
"-printseeds",
execPathEndingWith(action.getOutputs(), "b_proguard.seeds"),
"-printusage",
execPathEndingWith(action.getOutputs(), "b_proguard.usage"))
.inOrder();
// Checks that the output files are produced.
assertProguardUsed(output);
assertThat(getBinArtifact("b_proguard.usage", output)).isNotNull();
assertThat(getBinArtifact("b_proguard.seeds", output)).isNotNull();
}
@Test
public void testProGuardExecutableMatchesConfiguration() throws Exception {
scratch.file(
"java/com/google/devtools/build/jkrunchy/BUILD",
"package(default_visibility=['//visibility:public'])",
"java_binary(name = 'jkrunchy',",
" main_class = 'com.google.devtools.build.jkrunchy.JKrunchyMain')");
useConfiguration("--proguard_top=//java/com/google/devtools/build/jkrunchy:jkrunchy");
scratch.file(
"java/com/google/android/hello/BUILD",
"android_binary(name = 'b',",
" srcs = ['HelloApp.java'],",
" manifest = 'AndroidManifest.xml',",
" proguard_specs = ['proguard-spec.pro'])");
ConfiguredTarget output = getConfiguredTarget("//java/com/google/android/hello:b_proguard.jar");
assertProguardUsed(output);
SpawnAction proguardAction =
(SpawnAction)
actionsTestUtil()
.getActionForArtifactEndingWith(getFilesToBuild(output), "_proguard.jar");
Artifact jkrunchyExecutable =
getHostConfiguredTarget("//java/com/google/devtools/build/jkrunchy")
.getProvider(FilesToRunProvider.class)
.getExecutable();
assertWithMessage("ProGuard implementation was not correctly taken from the configuration")
.that(proguardAction.getCommandFilename())
.endsWith(jkrunchyExecutable.getRepositoryRelativePathString());
}
@Test
public void enforceProguardFileExtension_disabled_allowsOtherExtensions() throws Exception {
useConfiguration("--noenforce_proguard_file_extension");
scratch.file(
"java/android/hello/BUILD",
"android_binary(name = 'b',",
" srcs = ['HelloApp.java'],",
" manifest = 'AndroidManifest.xml',",
" proguard_specs = ['proguard-spec.pro'])");
ConfiguredTarget unused = getConfiguredTarget("//java/android/hello:b");
}
@Test
public void enforceProguardFileExtension_enabled_disallowsOtherExtensions() throws Exception {
useConfiguration("--enforce_proguard_file_extension");
scratch.file(
"java/android/hello/BUILD",
"android_binary(name = 'b',",
" srcs = ['HelloApp.java'],",
" manifest = 'AndroidManifest.xml',",
" proguard_specs = ['proguard-spec.pro'])");
AssertionError assertionError =
assertThrows(AssertionError.class, () -> getConfiguredTarget("//java/android/hello:b"));
assertThat(assertionError).hasMessageThat().contains("These files do not end in .pgcfg");
}
@Test
public void enforceProguardFileExtension_enabled_allowsPgcfg() throws Exception {
useConfiguration("--enforce_proguard_file_extension");
scratch.file(
"java/android/hello/BUILD",
"android_binary(name = 'b',",
" srcs = ['HelloApp.java'],",
" manifest = 'AndroidManifest.xml',",
" proguard_specs = [':proguard.pgcfg'])");
ConfiguredTarget unused = getConfiguredTarget("//java/android/hello:b");
}
@Test
public void enforceProguardFileExtension_enabled_ignoresThirdParty() throws Exception {
useConfiguration("--enforce_proguard_file_extension");
scratch.file(
"third_party/bar/BUILD", "licenses(['unencumbered'])", "exports_files(['proguard.pro'])");
scratch.file(
"java/android/hello/BUILD",
"android_binary(name = 'b',",
" srcs = ['HelloApp.java'],",
" manifest = 'AndroidManifest.xml',",
" proguard_specs = ['//third_party/bar:proguard.pro'])");
ConfiguredTarget unused = getConfiguredTarget("//java/android/hello:b");
}
@Test
public void testNeverlinkTransitivity() throws Exception {
useConfiguration("--android_fixed_resource_neverlinking");
scratch.file(
"java/com/google/android/neversayneveragain/BUILD",
"android_library(name = 'l1',",
" srcs = ['l1.java'],",
" manifest = 'AndroidManifest.xml',",
" resource_files = ['res/values/resource.xml'])",
"android_library(name = 'l2',",
" srcs = ['l2.java'],",
" deps = [':l1'],",
" neverlink = 1)",
"android_library(name = 'l3',",
" srcs = ['l3.java'],",
" deps = [':l2'])",
"android_library(name = 'l4',",
" srcs = ['l4.java'],",
" deps = [':l1'])",
"android_binary(name = 'b1',",
" srcs = ['b1.java'],",
" deps = [':l2'],",
" manifest = 'AndroidManifest.xml')",
"android_binary(name = 'b2',",
" srcs = ['b2.java'],",
" deps = [':l3'],",
" manifest = 'AndroidManifest.xml')",
"android_binary(name = 'b3',",
" srcs = ['b3.java'],",
" deps = [':l3', ':l4'],",
" manifest = 'AndroidManifest.xml')");
ConfiguredTarget b1 = getConfiguredTarget("//java/com/google/android/neversayneveragain:b1");
Action b1DeployAction =
actionsTestUtil()
.getActionForArtifactEndingWith(
actionsTestUtil().artifactClosureOf(getFilesToBuild(b1)), "b1_deploy.jar");
List<String> b1Inputs = prettyArtifactNames(b1DeployAction.getInputs());
assertThat(b1Inputs)
.containsNoneOf(
"java/com/google/android/neversayneveragain/libl1.jar_desugared.jar",
"java/com/google/android/neversayneveragain/libl2.jar_desugared.jar",
"java/com/google/android/neversayneveragain/libl3.jar_desugared.jar",
"java/com/google/android/neversayneveragain/libl4.jar_desugared.jar");
assertThat(b1Inputs)
.contains("java/com/google/android/neversayneveragain/libb1.jar_desugared.jar");
assertThat(
resourceInputPaths(
"java/com/google/android/neversayneveragain", getValidatedResources(b1)))
.doesNotContain("res/values/resource.xml");
ConfiguredTarget b2 = getConfiguredTarget("//java/com/google/android/neversayneveragain:b2");
Action b2DeployAction =
actionsTestUtil()
.getActionForArtifactEndingWith(
actionsTestUtil().artifactClosureOf(getFilesToBuild(b2)), "b2_deploy.jar");
List<String> b2Inputs = prettyArtifactNames(b2DeployAction.getInputs());
assertThat(b2Inputs)
.containsNoneOf(
"java/com/google/android/neversayneveragain/libl1.jar_desugared.jar",
"java/com/google/android/neversayneveragain/libl2.jar_desugared.jar",
"java/com/google/android/neversayneveragain/libl4.jar_desugared.jar");
assertThat(b2Inputs)
.containsAtLeast(
"java/com/google/android/neversayneveragain/_dx/l3/libl3.jar_desugared.jar",
"java/com/google/android/neversayneveragain/libb2.jar_desugared.jar");
assertThat(
resourceInputPaths(
"java/com/google/android/neversayneveragain", getValidatedResources(b2)))
.doesNotContain("res/values/resource.xml");
ConfiguredTarget b3 = getConfiguredTarget("//java/com/google/android/neversayneveragain:b3");
Action b3DeployAction =
actionsTestUtil()
.getActionForArtifactEndingWith(
actionsTestUtil().artifactClosureOf(getFilesToBuild(b3)), "b3_deploy.jar");
List<String> b3Inputs = prettyArtifactNames(b3DeployAction.getInputs());
assertThat(b3Inputs)
.containsAtLeast(
"java/com/google/android/neversayneveragain/_dx/l1/libl1.jar_desugared.jar",
"java/com/google/android/neversayneveragain/_dx/l3/libl3.jar_desugared.jar",
"java/com/google/android/neversayneveragain/_dx/l4/libl4.jar_desugared.jar",
"java/com/google/android/neversayneveragain/libb3.jar_desugared.jar");
assertThat(b3Inputs)
.doesNotContain("java/com/google/android/neversayneveragain/libl2.jar_desugared.jar");
assertThat(
resourceInputPaths(
"java/com/google/android/neversayneveragain", getValidatedResources(b3)))
.contains("res/values/resource.xml");
}
@Test
public void testDexopts() throws Exception {
useConfiguration("--noincremental_dexing");
checkDexopts("[ '--opt1', '--opt2' ]", ImmutableList.of("--opt1", "--opt2"));
}
@Test
public void testDexoptsTokenization() throws Exception {
useConfiguration("--noincremental_dexing");
checkDexopts(
"[ '--opt1', '--opt2 tokenized' ]", ImmutableList.of("--opt1", "--opt2", "tokenized"));
}
@Test
public void testDexoptsMakeVariableSubstitution() throws Exception {
useConfiguration("--noincremental_dexing");
checkDexopts("[ '--opt1', '$(COMPILATION_MODE)' ]", ImmutableList.of("--opt1", "fastbuild"));
}
private void checkDexopts(String dexopts, List<String> expectedArgs) throws Exception {
scratch.file(
"java/com/google/android/BUILD",
"android_binary(name = 'b',",
" srcs = ['dummy1.java'],",
" dexopts = " + dexopts + ",",
" manifest = 'AndroidManifest.xml')");
// Include arguments that are always included.
ImmutableList<String> fixedArgs = ImmutableList.of("--multi-dex");
expectedArgs =
new ImmutableList.Builder<String>().addAll(expectedArgs).addAll(fixedArgs).build();
// Ensure that the args that immediately follow "--dex" match the expectation.
ConfiguredTarget binary = getConfiguredTarget("//java/com/google/android:b");
List<String> args =
getGeneratingSpawnActionArgs(
ActionsTestUtil.getFirstArtifactEndingWith(
actionsTestUtil().artifactClosureOf(getFilesToBuild(binary)),
"intermediate_classes.dex.zip"));
int start = args.indexOf("--dex") + 1;
assertThat(start).isNotEqualTo(0);
int end = Math.min(args.size(), start + expectedArgs.size());
assertThat(args.subList(start, end)).isEqualTo(expectedArgs);
}
@Test
public void testDexMainListOpts() throws Exception {
checkDexMainListOpts("[ '--opt1', '--opt2' ]", "--opt1", "--opt2");
}
@Test
public void testDexMainListOptsTokenization() throws Exception {
checkDexMainListOpts("[ '--opt1', '--opt2 tokenized' ]", "--opt1", "--opt2", "tokenized");
}
@Test
public void testDexMainListOptsMakeVariableSubstitution() throws Exception {
checkDexMainListOpts("[ '--opt1', '$(COMPILATION_MODE)' ]", "--opt1", "fastbuild");
}
private void checkDexMainListOpts(String mainDexListOpts, String... expectedArgs)
throws Exception {
scratch.file(
"java/com/google/android/BUILD",
"android_binary(name = 'b',",
" srcs = ['dummy1.java'],",
" multidex = \"legacy\",",
" main_dex_list_opts = " + mainDexListOpts + ",",
" manifest = 'AndroidManifest.xml')");
// Ensure that the args that immediately follow the main class in the shell command
// match the expectation.
ConfiguredTarget binary = getConfiguredTarget("//java/com/google/android:b");
List<String> args =
getGeneratingSpawnActionArgs(
ActionsTestUtil.getFirstArtifactEndingWith(
actionsTestUtil().artifactClosureOf(getFilesToBuild(binary)), "main_dex_list.txt"));
// args: [ "bash", "-c", "java -cp dx.jar main opts other" ]
MoreAsserts.assertContainsSublist(args, expectedArgs);
}
@Test
public void omitResourcesInfoProviderFromAndroidBinary_enabled() throws Exception {
useConfiguration("--experimental_omit_resources_info_provider_from_android_binary");
ConfiguredTarget binary =
scratchConfiguredTarget(
"java/com/pkg/myapp",
"myapp",
"android_binary(",
" name = 'myapp',",
" manifest = 'AndroidManifest.xml',",
")");
assertThat(binary.get(AndroidResourcesInfo.PROVIDER)).isNull();
}
@Test
public void omitResourcesInfoProviderFromAndroidBinary_disabled() throws Exception {
useConfiguration("--noexperimental_omit_resources_info_provider_from_android_binary");
ConfiguredTarget binary =
scratchConfiguredTarget(
"java/com/pkg/myapp",
"myapp",
"android_binary(",
" name = 'myapp',",
" manifest = 'AndroidManifest.xml',",
")");
assertThat(binary.get(AndroidResourcesInfo.PROVIDER)).isNotNull();
}
@Test
public void testResourceConfigurationFilters() throws Exception {
scratch.file(
"java/com/google/android/BUILD",
"android_binary(name = 'b',",
" srcs = ['dummy1.java'],",
" manifest = 'AndroidManifest.xml',",
" resource_configuration_filters = [ 'en', 'fr'],)");
// Ensure that the args are present
ConfiguredTarget binary = getConfiguredTarget("//java/com/google/android:b");
List<String> args = resourceArguments(getValidatedResources(binary));
assertThat(flagValue("--resourceConfigs", args)).contains("en,fr");
}
/** Test that resources are not filtered in analysis under aapt2. */
@Test
public void testFilteredResourcesFilteringAapt2() throws Exception {
List<String> resources =
ImmutableList.of("res/values/foo.xml", "res/values-en/foo.xml", "res/values-fr/foo.xml");
String dir = "java/r/android";
ConfiguredTarget binary =
scratchConfiguredTarget(
dir,
"r",
"android_binary(name = 'r',",
" manifest = 'AndroidManifest.xml',",
" resource_configuration_filters = ['', 'en, es, '],",
" densities = ['hdpi, , ', 'xhdpi'],",
" resource_files = ['" + Joiner.on("', '").join(resources) + "'])");
ValidatedAndroidResources directResources =
getValidatedResources(binary, /* transitive= */ false);
// Validate that the AndroidResourceProvider for this binary contains all values.
assertThat(resourceContentsPaths(dir, directResources)).containsExactlyElementsIn(resources);
// Validate that the input to resource processing contains all values.
assertThat(resourceInputPaths(dir, directResources)).containsAtLeastElementsIn(resources);
// Validate that the filters are correctly passed to the resource processing action
// This includes trimming whitespace and ignoring empty filters.
assertThat(resourceArguments(directResources)).contains("en,es");
assertThat(resourceArguments(directResources)).contains("hdpi,xhdpi");
}
@Test
public void testFilterResourcesPseudolocalesPropagated() throws Exception {
String dir = "java/r/android";
ConfiguredTarget binary =
scratchConfiguredTarget(
dir,
"bin",
"android_binary(name = 'bin',",
" resource_configuration_filters = ['en', 'en-rXA', 'ar-rXB'],",
" manifest = 'AndroidManifest.xml')");
List<String> resourceProcessingArgs =
getGeneratingSpawnActionArgs(getValidatedResources(binary).getRTxt());
assertThat(resourceProcessingArgs).containsAtLeast("--resourceConfigs", "ar-rXB,en,en-rXA");
}
/**
* Gets the paths of matching artifacts contained within a resource container
*
* @param dir the directory to look for artifacts in
* @param resource the container that contains eligible artifacts
* @return the paths to all artifacts from the input that are contained within the given
* directory, relative to that directory.
*/
private List<String> resourceContentsPaths(String dir, ValidatedAndroidResources resource) {
return pathsToArtifacts(dir, resource.getArtifacts());
}
/**
* Gets the paths of matching artifacts that are used as input to resource processing
*
* @param dir the directory to look for artifacts in
* @param resource the output from the resource processing that uses these artifacts as inputs
* @return the paths to all artifacts used as inputs to resource processing that are contained
* within the given directory, relative to that directory.
*/
private List<String> resourceInputPaths(String dir, ValidatedAndroidResources resource) {
return pathsToArtifacts(dir, resourceGeneratingAction(resource).getInputs().toList());
}
/**
* Gets the paths of matching artifacts from an iterable
*
* @param dir the directory to look for artifacts in
* @param artifacts all available artifacts
* @return the paths to all artifacts from the input that are contained within the given
* directory, relative to that directory.
*/
private List<String> pathsToArtifacts(String dir, Iterable<Artifact> artifacts) {
List<String> paths = new ArrayList<>();
Path containingDir = rootDirectory;
for (String part : dir.split("/")) {
containingDir = containingDir.getChild(part);
}
for (Artifact a : artifacts) {
if (a.getPath().startsWith(containingDir)) {
paths.add(a.getPath().relativeTo(containingDir).toString());
}
}
return paths;
}
@Test
public void testInheritedRNotInRuntimeJars() throws Exception {
String dir = "java/r/android/";
scratch.file(
dir + "BUILD",
"android_library(name = 'sublib',",
" manifest = 'AndroidManifest.xml',",
" srcs =['sublib.java'],",
" )",
"android_library(name = 'lib',",
" manifest = 'AndroidManifest.xml',",
" deps = [':sublib'],",
" srcs =['lib.java'],",
" )",
"android_binary(name = 'bin',",
" manifest = 'AndroidManifest.xml',",
" deps = [':lib'],",
" srcs =['bin.java'],",
" )");
Action deployJarAction =
getGeneratingAction(
getFileConfiguredTarget("//java/r/android:bin_deploy.jar").getArtifact());
List<String> inputs = ActionsTestUtil.baseArtifactNames(deployJarAction.getInputs());
assertThat(inputs)
.containsAtLeast(
"libsublib.jar_desugared.jar",
"liblib.jar_desugared.jar",
"libbin.jar_desugared.jar",
"bin_resources.jar_desugared.jar");
assertThat(inputs)
.containsNoneOf("lib_resources.jar_desugared.jar", "sublib_resources.jar_desugared.jar");
}
@Test
public void testLocalResourcesUseRClassGenerator() throws Exception {
scratch.file(
"java/r/android/BUILD",
"android_library(name = 'lib',",
" manifest = 'AndroidManifest.xml',",
" resource_files = glob(['res2/**']),",
" )",
"android_binary(name = 'r',",
" manifest = 'AndroidManifest.xml',",
" resource_files = glob(['res/**']),",
" deps = [':lib'],",
" )");
scratch.file(
"java/r/android/res2/values/strings.xml",
"<resources><string name = 'lib_string'>Libs!</string></resources>");
scratch.file(
"java/r/android/res/values/strings.xml",
"<resources><string name = 'hello'>Hello Android!</string></resources>");
Artifact jar = getResourceClassJar(getConfiguredTargetAndData("//java/r/android:r"));
assertThat(getGeneratingAction(jar).getMnemonic()).isEqualTo("RClassGenerator");
assertThat(getGeneratingSpawnActionArgs(jar))
.containsAtLeast("--primaryRTxt", "--primaryManifest", "--library", "--classJarOutput");
}
@Test
public void testLocalResourcesUseRClassGeneratorNoLibraries() throws Exception {
scratch.file(
"java/r/android/BUILD",
"android_binary(name = 'r',",
" manifest = 'AndroidManifest.xml',",
" resource_files = glob(['res/**']),",
" )");
scratch.file(
"java/r/android/res/values/strings.xml",
"<resources><string name = 'hello'>Hello Android!</string></resources>");
Artifact jar = getResourceClassJar(getConfiguredTargetAndData("//java/r/android:r"));
assertThat(getGeneratingAction(jar).getMnemonic()).isEqualTo("RClassGenerator");
List<String> args = getGeneratingSpawnActionArgs(jar);
assertThat(args).containsAtLeast("--primaryRTxt", "--primaryManifest", "--classJarOutput");
assertThat(args).doesNotContain("--libraries");
}
@Test
public void testUseRClassGeneratorCustomPackage() throws Exception {
scratch.file(
"java/r/android/BUILD",
"android_library(name = 'lib',",
" manifest = 'AndroidManifest.xml',",
" resource_files = glob(['res2/**']),",
" custom_package = 'com.lib.custom',",
" )",
"android_binary(name = 'r',",
" manifest = 'AndroidManifest.xml',",
" resource_files = glob(['res/**']),",
" custom_package = 'com.binary.custom',",
" deps = [':lib'],",
" )");
scratch.file(
"java/r/android/res2/values/strings.xml",
"<resources><string name = 'lib_string'>Libs!</string></resources>");
scratch.file(
"java/r/android/res/values/strings.xml",
"<resources><string name = 'hello'>Hello Android!</string></resources>");
ConfiguredTargetAndData binary = getConfiguredTargetAndData("//java/r/android:r");
Artifact jar = getResourceClassJar(binary);
assertThat(getGeneratingAction(jar).getMnemonic()).isEqualTo("RClassGenerator");
List<String> args = getGeneratingSpawnActionArgs(jar);
assertThat(args)
.containsAtLeast(
"--primaryRTxt",
"--primaryManifest",
"--library",
"--classJarOutput",
"--packageForR",
"com.binary.custom");
}
@Test
public void testUseRClassGeneratorMultipleDeps() throws Exception {
scratch.file(
"java/r/android/BUILD",
"android_library(name = 'lib1',",
" manifest = 'AndroidManifest.xml',",
" )",
"android_library(name = 'lib2',",
" manifest = 'AndroidManifest.xml',",
" )",
"android_binary(name = 'r',",
" manifest = 'AndroidManifest.xml',",
" deps = [':lib1', ':lib2'],",
" )");
ConfiguredTargetAndData binary = getConfiguredTargetAndData("//java/r/android:r");
Artifact jar = getResourceClassJar(binary);
assertThat(getGeneratingAction(jar).getMnemonic()).isEqualTo("RClassGenerator");
List<String> args = getGeneratingSpawnActionArgs(jar);
AndroidResourcesInfo resourcesInfo =
binary.getConfiguredTarget().get(AndroidResourcesInfo.PROVIDER);
assertThat(resourcesInfo.getTransitiveAndroidResources().toList()).hasSize(2);
ValidatedAndroidResources firstDep =
resourcesInfo.getTransitiveAndroidResources().toList().get(0);
ValidatedAndroidResources secondDep =
resourcesInfo.getTransitiveAndroidResources().toList().get(1);
assertThat(args)
.containsAtLeast(
"--primaryRTxt",
"--primaryManifest",
"--library",
firstDep.getAapt2RTxt().getExecPathString()
+ ","
+ firstDep.getManifest().getExecPathString(),
"--library",
secondDep.getAapt2RTxt().getExecPathString()
+ ","
+ secondDep.getManifest().getExecPathString(),
"--classJarOutput")
.inOrder();
}
@Test
public void useRTxtFromMergedResourcesForFinalRClasses() throws Exception {
useConfiguration("--experimental_use_rtxt_from_merged_resources");
scratch.file(
"java/pkg/BUILD",
"android_library(",
" name = 'lib',",
" srcs = ['B.java'],",
" manifest = 'AndroidManifest.xml',",
" resource_files = [ 'res/values/values.xml' ], ",
")",
"android_binary(",
" name = 'bin',",
" manifest = 'AndroidManifest.xml',",
" deps = [ ':lib' ],",
")");
ConfiguredTargetAndData bin = getConfiguredTargetAndData("//java/pkg:bin");
ConfiguredTarget lib = getDirectPrerequisite(bin.getConfiguredTarget(), "//java/pkg:lib");
ValidatedAndroidResources libResources =
lib.get(AndroidResourcesInfo.PROVIDER).getDirectAndroidResources().toList().get(0);
SpawnAction topLevelResourceClassAction = getGeneratingSpawnAction(getResourceClassJar(bin));
// verify that the R.txt from creating the library-level resources.jar is also used for creating
// the top-level resources.jar
assertThat(getGeneratingSpawnAction(libResources.getClassJar()).getOutputs())
.contains(libResources.getAapt2RTxt());
MoreAsserts.assertContainsSublist(
topLevelResourceClassAction.getArguments(),
"--library",
libResources.getAapt2RTxt().getExecPathString()
+ ","
+ libResources.getManifest().getExecPathString());
// the "validation artifact" shouldn't be used for creating the top-level resources.jar,
// but it's still fed as an pseudo-input to trigger validation.
MoreAsserts.assertDoesNotContainSublist(
topLevelResourceClassAction.getArguments(),
"--library",
libResources.getAapt2ValidationArtifact().getExecPathString()
+ ","
+ libResources.getManifest().getExecPathString());
assertThat(topLevelResourceClassAction.getInputs().toList())
.contains(libResources.getAapt2ValidationArtifact());
}
// (test for undesired legacy behavior)
@Test
public void doNotUseRTxtFromMergedResourcesForFinalRClasses() throws Exception {
scratch.file(
"java/pkg/BUILD",
"android_library(",
" name = 'lib',",
" srcs = ['B.java'],",
" manifest = 'AndroidManifest.xml',",
" resource_files = [ 'res/values/values.xml' ], ",
")",
"android_binary(",
" name = 'bin',",
" manifest = 'AndroidManifest.xml',",
" deps = [ ':lib' ],",
")");
ConfiguredTargetAndData bin = getConfiguredTargetAndData("//java/pkg:bin");
ConfiguredTarget lib = getDirectPrerequisite(bin.getConfiguredTarget(), "//java/pkg:lib");
ValidatedAndroidResources libResources =
lib.get(AndroidResourcesInfo.PROVIDER).getDirectAndroidResources().toList().get(0);
// use the "validation artifact" for creating the top-level resources.jar
MoreAsserts.assertContainsSublist(
getGeneratingSpawnActionArgs(getResourceClassJar(bin)),
"--library",
libResources.getAapt2ValidationArtifact().getExecPathString()
+ ","
+ libResources.getManifest().getExecPathString());
}
@Test
public void testNoCrunchBinaryOnly() throws Exception {
scratch.file(
"java/r/android/BUILD",
"android_binary(name = 'r',",
" srcs = ['Foo.java'],",
" manifest = 'AndroidManifest.xml',",
" resource_files = ['res/drawable-hdpi-v4/foo.png',",
" 'res/drawable-hdpi-v4/bar.9.png'],",
" crunch_png = 0,",
" )");
ConfiguredTarget binary = getConfiguredTarget("//java/r/android:r");
List<String> args = getGeneratingSpawnActionArgs(getResourceApk(binary));
assertThat(args).contains("--useAaptCruncher=no");
}
@Test
public void testDoCrunch() throws Exception {
scratch.file(
"java/r/android/BUILD",
"android_binary(name = 'r',",
" srcs = ['Foo.java'],",
" manifest = 'AndroidManifest.xml',",
" resource_files = ['res/drawable-hdpi-v4/foo.png',",
" 'res/drawable-hdpi-v4/bar.9.png'],",
" crunch_png = 1,",
" )");
ConfiguredTarget binary = getConfiguredTarget("//java/r/android:r");
List<String> args = getGeneratingSpawnActionArgs(getResourceApk(binary));
assertThat(args).doesNotContain("--useAaptCruncher=no");
}
@Test
public void testDoCrunchDefault() throws Exception {
scratch.file(
"java/r/android/BUILD",
"android_binary(name = 'r',",
" srcs = ['Foo.java'],",
" manifest = 'AndroidManifest.xml',",
" resource_files = ['res/drawable-hdpi-v4/foo.png',",
" 'res/drawable-hdpi-v4/bar.9.png'],",
" )");
ConfiguredTarget binary = getConfiguredTarget("//java/r/android:r");
List<String> args = getGeneratingSpawnActionArgs(getResourceApk(binary));
assertThat(args).doesNotContain("--useAaptCruncher=no");
}
@Test
public void testNoCrunchWithAndroidLibraryNoBinaryResources() throws Exception {
scratch.file(
"java/r/android/BUILD",
"android_library(name = 'resources',",
" manifest = 'AndroidManifest.xml',",
" resource_files = ['res/values/strings.xml',",
" 'res/drawable-hdpi-v4/foo.png',",
" 'res/drawable-hdpi-v4/bar.9.png'],",
" )",
"android_binary(name = 'r',",
" srcs = ['Foo.java'],",
" manifest = 'AndroidManifest.xml',",
" deps = [':resources'],",
" crunch_png = 0,",
" )");
ConfiguredTarget binary = getConfiguredTarget("//java/r/android:r");
List<String> args = getGeneratingSpawnActionArgs(getResourceApk(binary));
assertThat(args).contains("--useAaptCruncher=no");
}
@Test
public void testNoCrunchWithMultidexNative() throws Exception {
scratch.file(
"java/r/android/BUILD",
"android_library(name = 'resources',",
" manifest = 'AndroidManifest.xml',",
" resource_files = ['res/values/strings.xml',",
" 'res/drawable-hdpi-v4/foo.png',",
" 'res/drawable-hdpi-v4/bar.9.png'],",
" )",
"android_binary(name = 'r',",
" srcs = ['Foo.java'],",
" manifest = 'AndroidManifest.xml',",
" deps = [':resources'],",
" multidex = 'native',",
" crunch_png = 0,",
" )");
ConfiguredTarget binary = getConfiguredTarget("//java/r/android:r");
List<String> args = getGeneratingSpawnActionArgs(getResourceApk(binary));
assertThat(args).contains("--useAaptCruncher=no");
}
@Test
public void testZipaligned() throws Exception {
ConfiguredTarget binary = getConfiguredTarget("//java/android:app");
Artifact a =
ActionsTestUtil.getFirstArtifactEndingWith(
actionsTestUtil().artifactClosureOf(getFilesToBuild(binary)), "zipaligned_app.apk");
SpawnAction action = getGeneratingSpawnAction(a);
assertThat(action.getMnemonic()).isEqualTo("AndroidZipAlign");
List<String> arguments = getGeneratingSpawnActionArgs(a);
assertThat(arguments).contains("-p");
assertThat(arguments).contains("4");
Artifact zipAlignTool = getFirstArtifactEndingWith(action.getInputs(), "/zipalign");
assertThat(arguments).contains(zipAlignTool.getExecPathString());
Artifact unsignedApk = getFirstArtifactEndingWith(action.getInputs(), "/app_unsigned.apk");
assertThat(arguments).contains(unsignedApk.getExecPathString());
Artifact zipalignedApk = getFirstArtifactEndingWith(action.getOutputs(), "/zipaligned_app.apk");
assertThat(arguments).contains(zipalignedApk.getExecPathString());
}
@Test
public void testDeployInfo() throws Exception {
ConfiguredTarget binary = getConfiguredTarget("//java/android:app");
NestedSet<Artifact> outputGroup = getOutputGroup(binary, "android_deploy_info");
Artifact deployInfoArtifact =
ActionsTestUtil.getFirstArtifactEndingWith(outputGroup, "/deploy_info.deployinfo.pb");
assertThat(deployInfoArtifact).isNotNull();
AndroidDeployInfo deployInfo = getAndroidDeployInfo(deployInfoArtifact);
assertThat(deployInfo).isNotNull();
assertThat(deployInfo.getMergedManifest().getExecRootPath()).endsWith("/AndroidManifest.xml");
assertThat(deployInfo.getAdditionalMergedManifestsList()).isEmpty();
assertThat(deployInfo.getApksToDeploy(0).getExecRootPath()).endsWith("/app.apk");
Artifact mergedManifest =
ActionsTestUtil.getFirstArtifactEndingWith(outputGroup, "/AndroidManifest.xml");
assertThat(mergedManifest).isNotNull();
}
/**
* Internal helper method: checks that dex sharding input and output is correct for different
* combinations of multidex mode and build with and without proguard.
*/
private void internalTestDexShardStructure(
MultidexMode multidexMode, boolean proguard, String nonProguardSuffix) throws Exception {
ConfiguredTarget target = getConfiguredTarget("//java/a:a");
assertNoEvents();
Action shardAction = getGeneratingAction(getBinArtifact("_dx/a/shard1.jar", target));
// Verify command line arguments
List<String> arguments = ((SpawnAction) shardAction).getRemainingArguments();
List<String> expectedArguments = new ArrayList<>();
Set<Artifact> artifacts = actionsTestUtil().artifactClosureOf(getFilesToBuild(target));
Artifact shard1 = getFirstArtifactEndingWith(artifacts, "shard1.jar");
Artifact shard2 = getFirstArtifactEndingWith(artifacts, "shard2.jar");
Artifact resourceJar = getFirstArtifactEndingWith(artifacts, "/java_resources.jar");
expectedArguments.add("--output_jar");
expectedArguments.add(shard1.getExecPathString());
expectedArguments.add("--output_jar");
expectedArguments.add(shard2.getExecPathString());
expectedArguments.add("--output_resources");
expectedArguments.add(resourceJar.getExecPathString());
if (multidexMode == MultidexMode.LEGACY) {
Artifact mainDexList = getFirstArtifactEndingWith(artifacts, "main_dex_list.txt");
expectedArguments.add("--main_dex_filter");
expectedArguments.add(mainDexList.getExecPathString());
}
if (!proguard) {
expectedArguments.add("--input_jar");
expectedArguments.add(
getFirstArtifactEndingWith(artifacts, "a_resources.jar" + nonProguardSuffix)
.getExecPathString());
}
Artifact inputJar;
if (proguard) {
inputJar = getFirstArtifactEndingWith(artifacts, "a_proguard.jar");
} else {
inputJar = getFirstArtifactEndingWith(artifacts, "liba.jar" + nonProguardSuffix);
}
expectedArguments.add("--input_jar");
expectedArguments.add(inputJar.getExecPathString());
assertThat(arguments).containsExactlyElementsIn(expectedArguments).inOrder();
// Verify input and output artifacts
List<String> shardOutputs = ActionsTestUtil.baseArtifactNames(shardAction.getOutputs());
List<String> shardInputs = ActionsTestUtil.baseArtifactNames(shardAction.getInputs());
assertThat(shardOutputs).containsExactly("shard1.jar", "shard2.jar", "java_resources.jar");
if (multidexMode == MultidexMode.LEGACY) {
assertThat(shardInputs).contains("main_dex_list.txt");
} else {
assertThat(shardInputs).doesNotContain("main_dex_list.txt");
}
if (proguard) {
assertThat(shardInputs).contains("a_proguard.jar");
assertThat(shardInputs).doesNotContain("liba.jar" + nonProguardSuffix);
} else {
assertThat(shardInputs).contains("liba.jar" + nonProguardSuffix);
assertThat(shardInputs).doesNotContain("a_proguard.jar");
}
assertThat(shardInputs).doesNotContain("a_deploy.jar");
// Verify that dex compilation is followed by the correct merge operation
Action apkAction =
getGeneratingAction(
getFirstArtifactEndingWith(
actionsTestUtil().artifactClosureOf(getFilesToBuild(target)),
"compressed_a_unsigned.apk"));
Action mergeAction =
getGeneratingAction(getFirstArtifactEndingWith(apkAction.getInputs(), "classes.dex.zip"));
Iterable<Artifact> dexShards =
Iterables.filter(
mergeAction.getInputs().toList(), ActionsTestUtil.getArtifactSuffixMatcher(".dex.zip"));
assertThat(ActionsTestUtil.baseArtifactNames(dexShards))
.containsExactly("shard1.dex.zip", "shard2.dex.zip");
}
@Test
public void testDexShardingDoesNotWorkWithManualMultidex() throws Exception {
scratch.file(
"java/a/BUILD",
"android_binary(",
" name='a',",
" srcs=['A.java'],",
" dex_shards=2,",
" multidex='manual_main_dex',",
" main_dex_list='main_dex_list.txt',",
" manifest='AndroidManifest.xml')");
reporter.removeHandler(failFastHandler);
getConfiguredTarget("//java/a:a");
assertContainsEvent(".dex sharding is not available in manual multidex mode");
}
@Test
public void testDexShardingLegacyStructure() throws Exception {
useConfiguration("--noincremental_dexing");
scratch.file(
"java/a/BUILD",
"android_binary(",
" name='a',",
" srcs=['A.java'],",
" dex_shards=2,",
" multidex='legacy',",
" manifest='AndroidManifest.xml')");
internalTestDexShardStructure(MultidexMode.LEGACY, false, "_desugared.jar");
}
@Test
public void testDexShardingNativeStructure_withNoDesugaring() throws Exception {
useConfiguration("--noexperimental_desugar_for_android", "--noincremental_dexing");
scratch.file(
"java/a/BUILD",
"android_binary(",
" name='a',",
" srcs=['A.java'],",
" dex_shards=2,",
" multidex='native',",
" manifest='AndroidManifest.xml')");
internalTestDexShardStructure(MultidexMode.NATIVE, false, "");
}
@Test
public void testDexShardingNativeStructure() throws Exception {
useConfiguration("--noincremental_dexing");
scratch.file(
"java/a/BUILD",
"android_binary(",
" name='a',",
" srcs=['A.java'],",
" dex_shards=2,",
" multidex='native',",
" manifest='AndroidManifest.xml')");
internalTestDexShardStructure(MultidexMode.NATIVE, false, "_desugared.jar");
}
@Test
public void testDexShardingLegacyAndProguardStructure_withNoDesugaring() throws Exception {
useConfiguration(
"--noexperimental_desugar_for_android",
"--experimental_use_dex_splitter_for_incremental_dexing=false",
"--experimental_incremental_dexing_after_proguard_by_default=false",
"--experimental_incremental_dexing_after_proguard=1");
scratch.file(
"java/a/BUILD",
"android_binary(",
" name='a',",
" srcs=['A.java'],",
" dex_shards=2,",
" multidex='legacy',",
" manifest='AndroidManifest.xml',",
" proguard_specs=['proguard.cfg'])");
internalTestDexShardStructure(MultidexMode.LEGACY, true, "");
}
@Test
public void testDexShardingLegacyAndProguardStructure() throws Exception {
useConfiguration(
"--experimental_use_dex_splitter_for_incremental_dexing=false",
"--experimental_incremental_dexing_after_proguard_by_default=false",
"--experimental_incremental_dexing_after_proguard=1");
scratch.file(
"java/a/BUILD",
"android_binary(",
" name='a',",
" srcs=['A.java'],",
" dex_shards=2,",
" multidex='legacy',",
" manifest='AndroidManifest.xml',",
" proguard_specs=['proguard.cfg'])");
internalTestDexShardStructure(MultidexMode.LEGACY, true, "_desugared.jar");
}
@Test
public void testDexShardingNativeAndProguardStructure() throws Exception {
useConfiguration(
"--experimental_use_dex_splitter_for_incremental_dexing=false",
"--experimental_incremental_dexing_after_proguard_by_default=false",
"--experimental_incremental_dexing_after_proguard=1");
scratch.file(
"java/a/BUILD",
"android_binary(",
" name='a',",
" srcs=['A.java'],",
" dex_shards=2,",
" multidex='native',",
" manifest='AndroidManifest.xml',",
" proguard_specs=['proguard.cfg'])");
internalTestDexShardStructure(MultidexMode.NATIVE, true, "");
}
@Test
public void testIncrementalApkAndProguardBuildStructure() throws Exception {
scratch.file(
"java/a/BUILD",
"android_binary(",
" name='a',",
" srcs=['A.java'],",
" dex_shards=2,",
" multidex='native',",
" manifest='AndroidManifest.xml',",
" proguard_specs=['proguard.cfg'])");
ConfiguredTarget target = getConfiguredTarget("//java/a:a");
Action shardAction = getGeneratingAction(getBinArtifact("_dx/a/shard1.jar", target));
List<String> shardOutputs = ActionsTestUtil.baseArtifactNames(shardAction.getOutputs());
assertThat(shardOutputs).contains("java_resources.jar");
assertThat(shardOutputs).doesNotContain("a_deploy.jar");
}
@Test
public void testManualMainDexBuildStructure() throws Exception {
checkError(
"java/foo",
"maindex_nomultidex",
"Both \"main_dex_list\" and \"multidex='manual_main_dex'\" must be specified",
"android_binary(",
" name = 'maindex_nomultidex',",
" srcs = ['a.java'],",
" manifest = 'AndroidManifest.xml',",
" multidex = 'manual_main_dex')");
}
@Test
public void testMainDexListLegacyMultidex() throws Exception {
checkError(
"java/foo",
"maindex_nomultidex",
"Both \"main_dex_list\" and \"multidex='manual_main_dex'\" must be specified",
"android_binary(",
" name = 'maindex_nomultidex',",
" srcs = ['a.java'],",
" manifest = 'AndroidManifest.xml',",
" multidex = 'legacy',",
" main_dex_list = 'main_dex_list.txt')");
}
@Test
public void testMainDexListNativeMultidex() throws Exception {
checkError(
"java/foo",
"maindex_nomultidex",
"Both \"main_dex_list\" and \"multidex='manual_main_dex'\" must be specified",
"android_binary(",
" name = 'maindex_nomultidex',",
" srcs = ['a.java'],",
" manifest = 'AndroidManifest.xml',",
" multidex = 'native',",
" main_dex_list = 'main_dex_list.txt')");
}
@Test
public void testMainDexListNoMultidex() throws Exception {
checkError(
"java/foo",
"maindex_nomultidex",
"Both \"main_dex_list\" and \"multidex='manual_main_dex'\" must be specified",
"android_binary(",
" name = 'maindex_nomultidex',",
" srcs = ['a.java'],",
" manifest = 'AndroidManifest.xml',",
" main_dex_list = 'main_dex_list.txt')");
}
@Test
public void testMainDexListWithAndroidSdk() throws Exception {
scratch.file(
"sdk/BUILD",
"android_sdk(",
" name = 'sdk',",
" aapt = 'aapt',",
" aapt2 = 'aapt2',",
" adb = 'adb',",
" aidl = 'aidl',",
" android_jar = 'android.jar',",
" apksigner = 'apksigner',",
" dx = 'dx',",
" framework_aidl = 'framework_aidl',",
" main_dex_classes = 'main_dex_classes',",
" main_dex_list_creator = 'main_dex_list_creator',",
" proguard = 'proguard',",
" shrinked_android_jar = 'shrinked_android_jar',",
" zipalign = 'zipalign',",
" tags = ['__ANDROID_RULES_MIGRATION__'])");
scratch.file(
"java/a/BUILD",
"android_binary(",
" name = 'a',",
" srcs = ['A.java'],",
" manifest = 'AndroidManifest.xml',",
" multidex = 'legacy',",
" main_dex_list_opts = ['--hello', '--world'])");
useConfiguration("--android_sdk=//sdk:sdk");
ConfiguredTarget a = getConfiguredTarget("//java/a:a");
Artifact mainDexList =
ActionsTestUtil.getFirstArtifactEndingWith(
actionsTestUtil().artifactClosureOf(getFilesToBuild(a)), "main_dex_list.txt");
List<String> args = getGeneratingSpawnActionArgs(mainDexList);
assertThat(args).containsAtLeast("--hello", "--world");
}
@Test
public void testLegacyMainDexListWithAndroidSdk() throws Exception {
scratch.file(
"tools/fake/BUILD",
"cc_binary(",
" name = 'generate_main_dex_list',",
" srcs = ['main.cc'])");
scratch.file(
"sdk/BUILD",
"android_sdk(",
" name = 'sdk',",
" aapt = 'aapt',",
" aapt2 = 'aapt2',",
" adb = 'adb',",
" aidl = 'aidl',",
" android_jar = 'android.jar',",
" apksigner = 'apksigner',",
" dx = 'dx',",
" framework_aidl = 'framework_aidl',",
" main_dex_classes = 'main_dex_classes',",
" main_dex_list_creator = 'main_dex_list_creator',",
" proguard = 'proguard',",
" shrinked_android_jar = 'shrinked_android_jar',",
" zipalign = 'zipalign',",
" legacy_main_dex_list_generator = '//tools/fake:generate_main_dex_list',",
" tags = ['__ANDROID_RULES_MIGRATION__'])");
scratch.file(
"java/a/BUILD",
"android_binary(",
" name = 'a',",
" srcs = ['A.java'],",
" manifest = 'AndroidManifest.xml',",
" multidex = 'legacy')");
useConfiguration("--android_sdk=//sdk:sdk");
ConfiguredTarget a = getConfiguredTarget("//java/a:a");
Artifact mainDexList =
ActionsTestUtil.getFirstArtifactEndingWith(
actionsTestUtil().artifactClosureOf(getFilesToBuild(a)), "main_dex_list.txt");
List<String> args = getGeneratingSpawnActionArgs(mainDexList);
NestedSet<Artifact> mainDexInputs = getGeneratingAction(mainDexList).getInputs();
MoreAsserts.assertContainsSublist(args, "--lib", "sdk/android.jar");
assertThat(ActionsTestUtil.baseArtifactNames(mainDexInputs)).contains("generate_main_dex_list");
assertThat(ActionsTestUtil.baseArtifactNames(mainDexInputs)).contains("a_deploy.jar");
assertThat(getFirstArtifactEndingWith(mainDexInputs, "main_dex_list_creator")).isNull();
}
@Test
public void testMainDexAaptGenerationSupported() throws Exception {
useConfiguration("--android_sdk=//sdk:sdk", "--noincremental_dexing");
scratch.file(
"sdk/BUILD",
"android_sdk(",
" name = 'sdk',",
" build_tools_version = '24.0.0',",
" aapt = 'aapt',",
" aapt2 = 'aapt2',",
" adb = 'adb',",
" aidl = 'aidl',",
" android_jar = 'android.jar',",
" apksigner = 'apksigner',",
" dx = 'dx',",
" framework_aidl = 'framework_aidl',",
" main_dex_classes = 'main_dex_classes',",
" main_dex_list_creator = 'main_dex_list_creator',",
" proguard = 'proguard',",
" shrinked_android_jar = 'shrinked_android_jar',",
" zipalign = 'zipalign',",
" tags = ['__ANDROID_RULES_MIGRATION__'])");
scratch.file(
"java/a/BUILD",
"android_binary(",
" name = 'a',",
" srcs = ['A.java'],",
" manifest = 'AndroidManifest.xml',",
" multidex = 'legacy')");
ConfiguredTargetAndData a = getConfiguredTargetAndData("//java/a:a");
Artifact intermediateJar =
artifactByPath(
ImmutableList.of(getCompressedUnsignedApk(a.getConfiguredTarget())),
".apk",
".dex.zip",
".dex.zip",
"main_dex_list.txt",
"_intermediate.jar");
List<String> args = getGeneratingSpawnActionArgs(intermediateJar);
assertContainsSublist(
args,
ImmutableList.of(
"-include",
a.getConfiguration().getBinFragment(RepositoryName.MAIN)
+ "/java/a/proguard/a/main_dex_a_proguard.cfg"));
}
@Test
public void testMainDexGenerationWithoutProguardMap() throws Exception {
useConfiguration("--noincremental_dexing");
scratchConfiguredTarget(
"java/foo",
"abin",
"android_binary(",
" name = 'abin',",
" srcs = ['a.java'],",
" proguard_specs = [],",
" manifest = 'AndroidManifest.xml',",
" multidex = 'legacy',)");
ConfiguredTarget a = getConfiguredTarget("//java/foo:abin");
Artifact intermediateJar =
artifactByPath(
ImmutableList.of(getCompressedUnsignedApk(a)),
".apk",
".dex.zip",
".dex.zip",
"main_dex_list.txt",
"_intermediate.jar");
List<String> args = getGeneratingSpawnActionArgs(intermediateJar);
MoreAsserts.assertDoesNotContainSublist(args, "-previousobfuscationmap");
}
// regression test for b/14288948
@Test
public void testEmptyListAsProguardSpec() throws Exception {
scratch.file(
"java/foo/BUILD",
"android_binary(",
" name = 'abin',",
" srcs = ['a.java'],",
" proguard_specs = [],",
" manifest = 'AndroidManifest.xml')");
Rule rule = getTarget("//java/foo:abin").getAssociatedRule();
assertNoEvents();
ImmutableList<String> implicitOutputFilenames =
rule.getOutputFiles().stream().map(FileTarget::getName).collect(toImmutableList());
assertThat(implicitOutputFilenames).doesNotContain("abin_proguard.jar");
}
@Test
public void testConfigurableProguardSpecsEmptyList() throws Exception {
scratch.file(
"java/foo/BUILD",
"android_binary(",
" name = 'abin',",
" srcs = ['a.java'],",
" proguard_specs = select({",
" '" + BuildType.Selector.DEFAULT_CONDITION_KEY + "': [],",
" }),",
" manifest = 'AndroidManifest.xml')");
Rule rule = getTarget("//java/foo:abin").getAssociatedRule();
assertNoEvents();
ImmutableList<String> implicitOutputFilenames =
rule.getOutputFiles().stream().map(FileTarget::getName).collect(toImmutableList());
assertThat(implicitOutputFilenames).contains("abin_proguard.jar");
}
@Test
public void testConfigurableProguardSpecsEmptyListWithMapping() throws Exception {
scratchConfiguredTarget(
"java/foo",
"abin",
"android_binary(",
" name = 'abin',",
" srcs = ['a.java'],",
" proguard_specs = select({",
" '" + BuildType.Selector.DEFAULT_CONDITION_KEY + "': [],",
" }),",
" proguard_generate_mapping = 1,",
" manifest = 'AndroidManifest.xml')");
assertNoEvents();
}
@Test
public void testResourcesWithConfigurationQualifier_localResources() throws Exception {
scratch.file(
"java/android/resources/BUILD",
"android_binary(name = 'r',",
" manifest = 'AndroidManifest.xml',",
" resource_files = glob(['res/**']),",
" )");
scratch.file(
"java/android/resources/res/values-en/strings.xml",
"<resources><string name = 'hello'>Hello Android!</string></resources>");
scratch.file(
"java/android/resources/res/values/strings.xml",
"<resources><string name = 'hello'>Hello Android!</string></resources>");
ConfiguredTarget resource = getConfiguredTarget("//java/android/resources:r");
List<String> args = getGeneratingSpawnActionArgs(getResourceApk(resource));
assertPrimaryResourceDirs(ImmutableList.of("java/android/resources/res"), args);
}
@Test
public void testResourcesInOtherPackage_exported_localResources() throws Exception {
scratch.file(
"java/android/resources/BUILD",
"android_binary(name = 'r',",
" manifest = 'AndroidManifest.xml',",
" resource_files = ['//java/resources/other:res/values/strings.xml'],",
" )");
scratch.file("java/resources/other/BUILD", "exports_files(['res/values/strings.xml'])");
ConfiguredTarget resource = getConfiguredTarget("//java/android/resources:r");
List<String> args = getGeneratingSpawnActionArgs(getResourceApk(resource));
assertPrimaryResourceDirs(ImmutableList.of("java/resources/other/res"), args);
assertNoEvents();
}
@Test
public void testResourcesInOtherPackage_filegroup_localResources() throws Exception {
scratch.file(
"java/android/resources/BUILD",
"android_binary(name = 'r',",
" manifest = 'AndroidManifest.xml',",
" resource_files = ['//java/other/resources:fg'],",
" )");
scratch.file(
"java/other/resources/BUILD",
"filegroup(name = 'fg',",
" srcs = ['res/values/strings.xml'],",
")");
ConfiguredTarget resource = getConfiguredTarget("//java/android/resources:r");
List<String> args = getGeneratingSpawnActionArgs(getResourceApk(resource));
assertPrimaryResourceDirs(ImmutableList.of("java/other/resources/res"), args);
assertNoEvents();
}
@Test
public void testResourcesInOtherPackage_filegroupWithExternalSources_localResources()
throws Exception {
scratch.file(
"java/android/resources/BUILD",
"android_binary(name = 'r',",
" manifest = 'AndroidManifest.xml',",
" resource_files = [':fg'],",
" )",
"filegroup(name = 'fg',",
" srcs = ['//java/other/resources:res/values/strings.xml'])");
scratch.file("java/other/resources/BUILD", "exports_files(['res/values/strings.xml'])");
ConfiguredTarget resource = getConfiguredTarget("//java/android/resources:r");
List<String> args = getGeneratingSpawnActionArgs(getResourceApk(resource));
assertPrimaryResourceDirs(ImmutableList.of("java/other/resources/res"), args);
assertNoEvents();
}
@Test
public void testMultipleDependentResourceDirectories_localResources() throws Exception {
scratch.file(
"java/android/resources/d1/BUILD",
"android_library(name = 'd1',",
" manifest = 'AndroidManifest.xml',",
" resource_files = ['d1-res/values/strings.xml'],",
" )");
scratch.file(
"java/android/resources/d2/BUILD",
"android_library(name = 'd2',",
" manifest = 'AndroidManifest.xml',",
" resource_files = ['d2-res/values/strings.xml'],",
" )");
scratch.file(
"java/android/resources/BUILD",
"android_binary(name = 'r',",
" manifest = 'AndroidManifest.xml',",
" resource_files = ['bin-res/values/strings.xml'],",
" deps = [",
" '//java/android/resources/d1:d1','//java/android/resources/d2:d2'",
" ])");
ConfiguredTarget resource = getConfiguredTarget("//java/android/resources:r");
List<String> args = getGeneratingSpawnActionArgs(getResourceApk(resource));
assertPrimaryResourceDirs(ImmutableList.of("java/android/resources/bin-res"), args);
assertThat(getDirectDependentResourceDirs(args))
.containsAtLeast("java/android/resources/d1/d1-res", "java/android/resources/d2/d2-res");
assertNoEvents();
}
// Regression test for b/11924769
@Test
public void testResourcesInOtherPackage_doubleFilegroup_localResources() throws Exception {
scratch.file(
"java/android/resources/BUILD",
"android_binary(name = 'r',",
" manifest = 'AndroidManifest.xml',",
" resource_files = [':fg'],",
" )",
"filegroup(name = 'fg',",
" srcs = ['//java/other/resources:fg'])");
scratch.file(
"java/other/resources/BUILD",
"filegroup(name = 'fg',",
" srcs = ['res/values/strings.xml'],",
")");
ConfiguredTarget resource = getConfiguredTarget("//java/android/resources:r");
List<String> args = getGeneratingSpawnActionArgs(getResourceApk(resource));
assertPrimaryResourceDirs(ImmutableList.of("java/other/resources/res"), args);
assertNoEvents();
}
@Test
public void testResourcesDoesNotMatchDirectoryLayout_badFile_localResources() throws Exception {
checkError(
"java/android/resources",
"r",
"'java/android/resources/res/somefile.xml' is not in the expected resource directory "
+ "structure of <resource directory>/{"
+ Joiner.on(',').join(AndroidResources.RESOURCE_DIRECTORY_TYPES)
+ "}",
"android_binary(name = 'r',",
" manifest = 'AndroidManifest.xml',",
" resource_files = ['res/somefile.xml', 'r/t/f/m/raw/fold']",
" )");
}
@Test
public void testResourcesDoesNotMatchDirectoryLayout_badDirectory_localResources()
throws Exception {
checkError(
"java/android/resources",
"r",
"'java/android/resources/res/other/somefile.xml' is not in the expected resource directory "
+ "structure of <resource directory>/{"
+ Joiner.on(',').join(AndroidResources.RESOURCE_DIRECTORY_TYPES)
+ "}",
"android_binary(name = 'r',",
" manifest = 'AndroidManifest.xml',",
" resource_files = ['res/other/somefile.xml', 'r/t/f/m/raw/fold']",
" )");
}
@Test
public void testResourcesNotUnderCommonDirectoryFails_localResources() throws Exception {
checkError(
"java/android/resources",
"r",
"'java/android/resources/r/t/f/m/raw/fold' (generated by '//java/android/resources:r/t/f/m/"
+ "raw/fold') is not in the same directory 'res' "
+ "(derived from java/android/resources/res/raw/speed). "
+ "All resources must share a common directory",
"android_binary(name = 'r',",
" manifest = 'AndroidManifest.xml',",
" resource_files = ['res/raw/speed', 'r/t/f/m/raw/fold']",
" )");
}
@Test
public void testAssetsAndNoAssetsDirFails_localResources() throws Exception {
scratch.file(
"java/android/resources/assets/values/strings.xml",
"<resources><string name = 'hello'>Hello Android!</string></resources>");
checkError(
"java/android/resources",
"r",
"'assets' and 'assets_dir' should be either both empty or both non-empty",
"android_binary(name = 'r',",
" manifest = 'AndroidManifest.xml',",
" assets = glob(['assets/**']),",
" )");
}
@Test
public void testAssetsDirAndNoAssetsFails_localResources() throws Exception {
checkError(
"java/cpp/android",
"r",
"'assets' and 'assets_dir' should be either both empty or both non-empty",
"android_binary(name = 'r',",
" manifest = 'AndroidManifest.xml',",
" assets_dir = 'assets',",
" )");
}
@Test
public void testAssetsNotUnderAssetsDirFails_localResources() throws Exception {
checkError(
"java/android/resources",
"r",
"'java/android/resources/r/t/f/m' (generated by '//java/android/resources:r/t/f/m') "
+ "is not beneath 'assets'",
"android_binary(name = 'r',",
" manifest = 'AndroidManifest.xml',",
" assets_dir = 'assets',",
" assets = ['assets/valuable', 'r/t/f/m']",
" )");
}
@Test
public void testFileLocation_localResources() throws Exception {
scratch.file(
"java/android/resources/BUILD",
"android_binary(name = 'r',",
" srcs = ['Foo.java'],",
" manifest = 'AndroidManifest.xml',",
" resource_files = ['res/values/strings.xml'],",
" )");
ConfiguredTargetAndData r = getConfiguredTargetAndData("//java/android/resources:r");
assertThat(
getFirstArtifactEndingWith(getFilesToBuild(r.getConfiguredTarget()), ".apk").getRoot())
.isEqualTo(r.getConfiguration().getBinDirectory(RepositoryName.MAIN));
}
@Test
public void testCustomPackage_localResources() throws Exception {
scratch.file(
"a/r/BUILD",
"android_binary(name = 'r',",
" srcs = ['Foo.java'],",
" custom_package = 'com.google.android.bar',",
" manifest = 'AndroidManifest.xml',",
" resource_files = ['res/values/strings.xml'],",
" )");
ConfiguredTarget r = getConfiguredTarget("//a/r:r");
assertNoEvents();
List<String> args = getGeneratingSpawnActionArgs(getResourceApk(r));
assertContainsSublist(args, ImmutableList.of("--packageForR", "com.google.android.bar"));
}
@Test
public void testCustomJavacopts() throws Exception {
scratch.file("java/foo/A.java", "foo");
scratch.file(
"java/foo/BUILD",
"android_binary(name = 'a', manifest = 'AndroidManifest.xml', ",
" srcs = ['A.java'], javacopts = ['-g:lines,source'])");
Artifact deployJar = getFileConfiguredTarget("//java/foo:a_deploy.jar").getArtifact();
Action deployAction = getGeneratingAction(deployJar);
JavaCompileAction javacAction =
(JavaCompileAction)
actionsTestUtil()
.getActionForArtifactEndingWith(
actionsTestUtil().artifactClosureOf(deployAction.getInputs()), "liba.jar");
assertThat(getJavacArguments(javacAction)).contains("-g:lines,source");
}
@Test
public void testFixDepsToolFlag() throws Exception {
useConfiguration("--experimental_fix_deps_tool=autofixer");
scratch.file("java/foo/A.java", "foo");
scratch.file(
"java/foo/BUILD",
"android_binary(name = 'a', manifest = 'AndroidManifest.xml', ",
" srcs = ['A.java'])");
Iterable<String> commandLine =
getJavacArguments(
((JavaCompileAction)
actionsTestUtil()
.getActionForArtifactEndingWith(
actionsTestUtil()
.artifactClosureOf(
getGeneratingAction(
getFileConfiguredTarget("//java/foo:a_deploy.jar")
.getArtifact())
.getInputs()),
"liba.jar")));
assertThat(commandLine).containsAtLeast("--experimental_fix_deps_tool", "autofixer").inOrder();
}
@Test
public void testFixDepsToolFlagEmpty() throws Exception {
scratch.file("java/foo/A.java", "foo");
scratch.file(
"java/foo/BUILD",
"android_binary(name = 'a', manifest = 'AndroidManifest.xml', ",
" srcs = ['A.java'])");
Iterable<String> commandLine =
getJavacArguments(
((JavaCompileAction)
actionsTestUtil()
.getActionForArtifactEndingWith(
actionsTestUtil()
.artifactClosureOf(
getGeneratingAction(
getFileConfiguredTarget("//java/foo:a_deploy.jar")
.getArtifact())
.getInputs()),
"liba.jar")));
assertThat(commandLine).containsAtLeast("--experimental_fix_deps_tool", "add_dep").inOrder();
}
@Test
public void testAndroidBinaryExportsJavaCompilationArgsProvider() throws Exception {
scratch.file("java/foo/A.java", "foo");
scratch.file(
"java/foo/BUILD",
"android_binary(name = 'a', manifest = 'AndroidManifest.xml', ",
" srcs = ['A.java'], javacopts = ['-g:lines,source'])");
final JavaCompilationArgsProvider provider =
JavaInfo.getProvider(
JavaCompilationArgsProvider.class, getConfiguredTarget("//java/foo:a"));
assertThat(provider).isNotNull();
}
@Test
public void testNoApplicationId_localResources() throws Exception {
scratch.file(
"java/a/r/BUILD",
"android_binary(name = 'r',",
" srcs = ['Foo.java'],",
" manifest = 'AndroidManifest.xml',",
" resource_files = ['res/values/strings.xml'],",
" )");
ConfiguredTarget r = getConfiguredTarget("//java/a/r:r");
assertNoEvents();
List<String> args = getGeneratingSpawnActionArgs(getResourceApk(r));
Truth.assertThat(args).doesNotContain("--applicationId");
}
@Test
public void testDisallowPrecompiledJars() throws Exception {
checkError(
"java/precompiled",
"binary",
// messages:
"does not produce any android_binary srcs files (expected .java or .srcjar)",
// build file:
"android_binary(name = 'binary',",
" manifest='AndroidManifest.xml',",
" srcs = [':jar'])",
"filegroup(name = 'jar',",
" srcs = ['lib.jar'])");
}
@Test
public void testDesugarJava8Libs_noProguard() throws Exception {
useConfiguration("--experimental_desugar_java8_libs");
scratch.file(
"java/com/google/android/BUILD",
"android_binary(",
" name = 'foo',",
" srcs = ['foo.java'],",
" manifest = 'AndroidManifest.xml',",
" multidex = 'native',",
")");
ConfiguredTarget top = getConfiguredTarget("//java/com/google/android:foo");
Artifact artifact = getBinArtifact("_dx/foo/_final_classes.dex.zip", top);
assertWithMessage("_final_classes.dex.zip").that(artifact).isNotNull();
Action generatingAction = getGeneratingAction(artifact);
assertThat(ActionsTestUtil.baseArtifactNames(generatingAction.getInputs()))
.containsAtLeast("classes.dex.zip", /*canned*/ "java8_legacy.dex.zip");
}
@Test
public void testDesugarJava8Libs_withProguard() throws Exception {
useConfiguration("--experimental_desugar_java8_libs");
scratch.file(
"java/com/google/android/BUILD",
"android_binary(",
" name = 'foo',",
" srcs = ['foo.java'],",
" manifest = 'AndroidManifest.xml',",
" multidex = 'native',",
" proguard_specs = ['foo.cfg'],",
")");
ConfiguredTarget top = getConfiguredTarget("//java/com/google/android:foo");
Artifact artifact = getBinArtifact("_dx/foo/_final_classes.dex.zip", top);
assertWithMessage("_final_classes.dex.zip").that(artifact).isNotNull();
Action generatingAction = getGeneratingAction(artifact);
assertThat(ActionsTestUtil.baseArtifactNames(generatingAction.getInputs()))
.containsAtLeast("classes.dex.zip", /*built*/ "_java8_legacy.dex.zip");
}
private List<String> generatingActionArgs(Artifact artifact) throws Exception {
Action action = getGeneratingAction(artifact);
return ((SpawnAction) action).getArguments();
}
@Test
public void testDesugarJava8Libs_withProguardWithMap() throws Exception {
useConfiguration("--experimental_desugar_java8_libs");
scratch.file(
"java/com/google/android/BUILD",
"android_binary(",
" name = 'foo',",
" srcs = ['foo.java'],",
" manifest = 'AndroidManifest.xml',",
" multidex = 'native',",
" proguard_specs = ['foo.cfg'],",
" proguard_generate_mapping = 1,",
")");
ConfiguredTarget top = getConfiguredTarget("//java/com/google/android:foo");
Artifact desugaredLibraryDex = getBinArtifact("_dx/foo/_java8_legacy.dex.zip", top);
List<String> desugaredLibraryActionArgs = generatingActionArgs(desugaredLibraryDex);
assertThat(desugaredLibraryActionArgs.get(0)).contains("build_java8_legacy_dex");
Artifact desugaredLibraryDexProguardMap = getBinArtifact("_dx/foo/_java8_legacy.dex.map", top);
MoreAsserts.assertContainsSublist(
desugaredLibraryActionArgs,
"--rules",
getBinArtifact("_dx/foo/_java8_legacy.dex.pgcfg", top).getExecPathString());
MoreAsserts.assertContainsSublist(
desugaredLibraryActionArgs, "--output", desugaredLibraryDex.getExecPathString());
MoreAsserts.assertContainsSublist(
desugaredLibraryActionArgs,
"--output_map",
desugaredLibraryDexProguardMap.getExecPathString());
Artifact proguardMap = getBinArtifact("foo_proguard.map", top);
List<String> mergeProguardMapsActionArgs =
((SpawnAction) getGeneratingAction(proguardMap)).getArguments();
assertThat(mergeProguardMapsActionArgs.get(0)).contains("merge_proguard_maps");
assertThat(flagValues("--pg-map", mergeProguardMapsActionArgs))
.isEqualTo(
ImmutableSet.of(
desugaredLibraryDexProguardMap.getExecPathString(),
getBinArtifact("_dx/foo/_proguard_output_for_desugared_library.map", top)
.getExecPathString()));
assertThat(flagValue("--pg-map-output", mergeProguardMapsActionArgs))
.isEqualTo(proguardMap.getExecPathString());
Artifact artifact = getBinArtifact("_dx/foo/_final_classes.dex.zip", top);
assertWithMessage("_final_classes.dex.zip").that(artifact).isNotNull();
Action generatingAction = getGeneratingAction(artifact);
assertThat(ActionsTestUtil.baseArtifactNames(generatingAction.getInputs()))
.containsAtLeast("classes.dex.zip", /*built*/ "_java8_legacy.dex.zip");
}
@Test
public void testDesugarJava8Libs_withProguardWithoutSpecsWithMap() throws Exception {
useConfiguration("--experimental_desugar_java8_libs");
scratch.file(
"java/com/google/android/BUILD",
"android_binary(",
" name = 'foo',",
" srcs = ['foo.java'],",
" manifest = 'AndroidManifest.xml',",
" multidex = 'native',",
" proguard_generate_mapping = 1,",
")");
ConfiguredTarget top = getConfiguredTarget("//java/com/google/android:foo");
Artifact desugaredLibraryDex = getBinArtifact("_dx/foo/_java8_legacy.dex.zip", top);
assertThat(getGeneratingAction(desugaredLibraryDex)).isNull();
Artifact proguardMap = getBinArtifact("foo_proguard.map", top);
assertThat(getGeneratingAction(proguardMap)).isInstanceOf(FailAction.class);
Artifact artifact = getBinArtifact("_dx/foo/_final_classes.dex.zip", top);
assertWithMessage("_final_classes.dex.zip").that(artifact).isNotNull();
Action generatingAction = getGeneratingAction(artifact);
assertThat(ActionsTestUtil.baseArtifactNames(generatingAction.getInputs()))
.containsAtLeast("classes.dex.zip", /*canned*/ "java8_legacy.dex.zip");
}
@Test
public void testApplyProguardMapping() throws Exception {
scratch.file(
"java/com/google/android/BUILD",
"android_binary(",
" name = 'foo',",
" srcs = ['foo.java'],",
" proguard_apply_mapping = 'proguard.map',",
" proguard_specs = ['foo.pro'],",
" manifest = 'AndroidManifest.xml',",
")");
ConfiguredTarget ct = getConfiguredTarget("//java/com/google/android:foo");
Artifact artifact = artifactByPath(getFilesToBuild(ct), "_proguard.jar");
Action generatingAction = getGeneratingAction(artifact);
assertThat(Artifact.asExecPaths(generatingAction.getInputs()))
.contains("java/com/google/android/proguard.map");
// Cannot use assertThat().containsAllOf().inOrder() as that does not assert that the elements
// are consecutive.
MoreAsserts.assertContainsSublist(
getGeneratingSpawnActionArgs(artifact),
"-applymapping",
"java/com/google/android/proguard.map");
}
@Test
public void testApplyProguardMappingWithNoSpec() throws Exception {
checkError(
"java/com/google/android",
"foo",
// messages:
"'proguard_apply_mapping' can only be used when 'proguard_specs' is also set",
// build file:
"android_binary(",
" name = 'foo',",
" srcs = ['foo.java'],",
" proguard_apply_mapping = 'proguard.map',",
" manifest = 'AndroidManifest.xml',",
")");
}
@Test
public void testApplyProguardDictionary() throws Exception {
scratch.file(
"java/com/google/android/BUILD",
"android_binary(",
" name = 'foo',",
" srcs = ['foo.java'],",
" proguard_apply_dictionary = 'dictionary.txt',",
" proguard_specs = ['foo.pro'],",
" manifest = 'AndroidManifest.xml',",
")");
ConfiguredTarget ct = getConfiguredTarget("//java/com/google/android:foo");
Artifact artifact = artifactByPath(getFilesToBuild(ct), "_proguard.jar");
Action generatingAction = getGeneratingAction(artifact);
assertThat(Artifact.asExecPaths(generatingAction.getInputs()))
.contains("java/com/google/android/dictionary.txt");
// Cannot use assertThat().containsAllOf().inOrder() as that does not assert that the elements
// are consecutive.
MoreAsserts.assertContainsSublist(
getGeneratingSpawnActionArgs(artifact),
"-obfuscationdictionary",
"java/com/google/android/dictionary.txt");
MoreAsserts.assertContainsSublist(
getGeneratingSpawnActionArgs(artifact),
"-classobfuscationdictionary",
"java/com/google/android/dictionary.txt");
MoreAsserts.assertContainsSublist(
getGeneratingSpawnActionArgs(artifact),
"-packageobfuscationdictionary",
"java/com/google/android/dictionary.txt");
}
@Test
public void testApplyProguardDictionaryWithNoSpec() throws Exception {
checkError(
"java/com/google/android",
"foo",
// messages:
"'proguard_apply_dictionary' can only be used when 'proguard_specs' is also set",
// build file:
"android_binary(",
" name = 'foo',",
" srcs = ['foo.java'],",
" proguard_apply_dictionary = 'dictionary.txt',",
" manifest = 'AndroidManifest.xml',",
")");
}
@Test
public void testFeatureFlagsAttributeSetsSelectInDependency() throws Exception {
useConfiguration("--enforce_transitive_configs_for_config_feature_flag");
scratch.file(
"java/com/foo/BUILD",
"config_feature_flag(",
" name = 'flag1',",
" allowed_values = ['on', 'off'],",
" default_value = 'off',",
")",
"config_setting(",
" name = 'flag1@on',",
" flag_values = {':flag1': 'on'},",
" transitive_configs = [':flag1'],",
")",
"config_feature_flag(",
" name = 'flag2',",
" allowed_values = ['on', 'off'],",
" default_value = 'off',",
")",
"config_setting(",
" name = 'flag2@on',",
" flag_values = {':flag2': 'on'},",
" transitive_configs = [':flag2'],",
")",
"android_library(",
" name = 'lib',",
" srcs = select({",
" ':flag1@on': ['Flag1On.java'],",
" '//conditions:default': ['Flag1Off.java'],",
" }) + select({",
" ':flag2@on': ['Flag2On.java'],",
" '//conditions:default': ['Flag2Off.java'],",
" }),",
" transitive_configs = [':flag1', ':flag2'],",
")",
"android_binary(",
" name = 'foo',",
" manifest = 'AndroidManifest.xml',",
" deps = [':lib'],",
" feature_flags = {",
" 'flag1': 'on',",
" },",
" transitive_configs = [':flag1', ':flag2'],",
")");
ConfiguredTarget binary = getConfiguredTarget("//java/com/foo");
List<String> inputs =
prettyArtifactNames(actionsTestUtil().artifactClosureOf(getFinalUnsignedApk(binary)));
assertThat(inputs).containsAtLeast("java/com/foo/Flag1On.java", "java/com/foo/Flag2Off.java");
assertThat(inputs).containsNoneOf("java/com/foo/Flag1Off.java", "java/com/foo/Flag2On.java");
}
@Test
public void testFeatureFlagsAttributeSetsSelectInBinary() throws Exception {
useConfiguration("--enforce_transitive_configs_for_config_feature_flag");
scratch.file(
"java/com/foo/BUILD",
"config_feature_flag(",
" name = 'flag1',",
" allowed_values = ['on', 'off'],",
" default_value = 'off',",
")",
"config_setting(",
" name = 'flag1@on',",
" flag_values = {':flag1': 'on'},",
" transitive_configs = [':flag1'],",
")",
"config_feature_flag(",
" name = 'flag2',",
" allowed_values = ['on', 'off'],",
" default_value = 'off',",
")",
"config_setting(",
" name = 'flag2@on',",
" flag_values = {':flag2': 'on'},",
" transitive_configs = [':flag2'],",
")",
"android_binary(",
" name = 'foo',",
" manifest = 'AndroidManifest.xml',",
" srcs = select({",
" ':flag1@on': ['Flag1On.java'],",
" '//conditions:default': ['Flag1Off.java'],",
" }) + select({",
" ':flag2@on': ['Flag2On.java'],",
" '//conditions:default': ['Flag2Off.java'],",
" }),",
" feature_flags = {",
" 'flag1': 'on',",
" },",
" transitive_configs = [':flag1', ':flag2'],",
")");
ConfiguredTarget binary = getConfiguredTarget("//java/com/foo");
List<String> inputs =
prettyArtifactNames(actionsTestUtil().artifactClosureOf(getFinalUnsignedApk(binary)));
assertThat(inputs).containsAtLeast("java/com/foo/Flag1On.java", "java/com/foo/Flag2Off.java");
assertThat(inputs).containsNoneOf("java/com/foo/Flag1Off.java", "java/com/foo/Flag2On.java");
}
@Test
public void testFeatureFlagsAttributeSetsSelectInBinaryAlias() throws Exception {
useConfiguration("--enforce_transitive_configs_for_config_feature_flag");
scratch.file(
"java/com/foo/BUILD",
"config_feature_flag(",
" name = 'flag1',",
" allowed_values = ['on', 'off'],",
" default_value = 'off',",
")",
"config_setting(",
" name = 'flag1@on',",
" flag_values = {':flag1': 'on'},",
" transitive_configs = [':flag1'],",
")",
"android_binary(",
" name = 'foo',",
" manifest = 'AndroidManifest.xml',",
" srcs = select({",
" ':flag1@on': ['Flag1On.java'],",
" '//conditions:default': ['Flag1Off.java'],",
" }),",
" feature_flags = {",
" 'flag1': 'on',",
" },",
" transitive_configs = [':flag1'],",
")",
"alias(",
" name = 'alias',",
" actual = ':foo',",
")");
ConfiguredTarget binary = getConfiguredTarget("//java/com/foo:alias");
List<String> inputs =
prettyArtifactNames(actionsTestUtil().artifactClosureOf(getFinalUnsignedApk(binary)));
assertThat(inputs).contains("java/com/foo/Flag1On.java");
assertThat(inputs).doesNotContain("java/com/foo/Flag1Off.java");
}
@Test
public void testFeatureFlagsAttributeFailsAnalysisIfFlagValueIsInvalid() throws Exception {
reporter.removeHandler(failFastHandler);
useConfiguration("--enforce_transitive_configs_for_config_feature_flag");
scratch.file(
"java/com/foo/BUILD",
"config_feature_flag(",
" name = 'flag1',",
" allowed_values = ['on', 'off'],",
" default_value = 'off',",
")",
"config_setting(",
" name = 'flag1@on',",
" flag_values = {':flag1': 'on'},",
" transitive_configs = [':flag1'],",
")",
"android_library(",
" name = 'lib',",
" srcs = select({",
" ':flag1@on': ['Flag1On.java'],",
" '//conditions:default': ['Flag1Off.java'],",
" }),",
" transitive_configs = [':flag1'],",
")",
"android_binary(",
" name = 'foo',",
" manifest = 'AndroidManifest.xml',",
" deps = [':lib'],",
" feature_flags = {",
" 'flag1': 'invalid',",
" },",
" transitive_configs = [':flag1'],",
")");
assertThat(getConfiguredTarget("//java/com/foo")).isNull();
assertContainsEvent(
"in config_feature_flag rule //java/com/foo:flag1: "
+ "value must be one of [\"off\", \"on\"], but was \"invalid\"");
}
@Test
public void testFeatureFlagsAttributeFailsAnalysisIfFlagValueIsInvalidEvenIfNotUsed()
throws Exception {
reporter.removeHandler(failFastHandler);
useConfiguration("--enforce_transitive_configs_for_config_feature_flag");
scratch.file(
"java/com/foo/BUILD",
"config_feature_flag(",
" name = 'flag1',",
" allowed_values = ['on', 'off'],",
" default_value = 'off',",
")",
"config_setting(",
" name = 'flag1@on',",
" flag_values = {':flag1': 'on'},",
" transitive_configs = [':flag1'],",
")",
"android_binary(",
" name = 'foo',",
" manifest = 'AndroidManifest.xml',",
" feature_flags = {",
" 'flag1': 'invalid',",
" },",
" transitive_configs = [':flag1'],",
")");
assertThat(getConfiguredTarget("//java/com/foo")).isNull();
assertContainsEvent(
"in config_feature_flag rule //java/com/foo:flag1: "
+ "value must be one of [\"off\", \"on\"], but was \"invalid\"");
}
@Test
public void testFeatureFlagsAttributeFailsAnalysisIfFlagIsAliased() throws Exception {
reporter.removeHandler(failFastHandler);
useConfiguration("--enforce_transitive_configs_for_config_feature_flag");
scratch.file(
"java/com/foo/BUILD",
"config_feature_flag(",
" name = 'flag1',",
" allowed_values = ['on', 'off'],",
" default_value = 'off',",
")",
"alias(",
" name = 'alias',",
" actual = 'flag1',",
" transitive_configs = [':flag1'],",
")",
"android_binary(",
" name = 'foo',",
" manifest = 'AndroidManifest.xml',",
" feature_flags = {",
" 'alias': 'on',",
" },",
" transitive_configs = [':flag1'],",
")");
assertThat(getConfiguredTarget("//java/com/foo")).isNull();
assertContainsEvent(
"in feature_flags attribute of android_binary rule //java/com/foo:foo: "
+ "Feature flags must be named directly, not through aliases; "
+ "use '//java/com/foo:flag1', not '//java/com/foo:alias'");
}
@Test
public void testFeatureFlagsAttributeSetsFeatureFlagProviderValues() throws Exception {
useConfiguration("--enforce_transitive_configs_for_config_feature_flag");
scratch.file(
"java/com/foo/reader.bzl",
"def _impl(ctx):",
" ctx.actions.write(",
" ctx.outputs.java,",
" '\\n'.join([",
" str(target.label) + ': ' + target[config_common.FeatureFlagInfo].value",
" for target in ctx.attr.flags]))",
" return [DefaultInfo(files=depset([ctx.outputs.java]))]",
"flag_reader = rule(",
" implementation=_impl,",
" attrs={'flags': attr.label_list(providers=[config_common.FeatureFlagInfo])},",
" outputs={'java': '%{name}.java'},",
")");
scratch.file(
"java/com/foo/BUILD",
"load('//java/com/foo:reader.bzl', 'flag_reader')",
"config_feature_flag(",
" name = 'flag1',",
" allowed_values = ['on', 'off'],",
" default_value = 'off',",
")",
"config_feature_flag(",
" name = 'flag2',",
" allowed_values = ['on', 'off'],",
" default_value = 'off',",
")",
"flag_reader(",
" name = 'FooFlags',",
" flags = [':flag1', ':flag2'],",
" transitive_configs = [':flag1', ':flag2'],",
")",
"android_binary(",
" name = 'foo',",
" manifest = 'AndroidManifest.xml',",
" srcs = [':FooFlags.java'],",
" feature_flags = {",
" 'flag1': 'on',",
" },",
" transitive_configs = [':flag1', ':flag2'],",
")");
Artifact flagList =
getFirstArtifactEndingWith(
actionsTestUtil()
.artifactClosureOf(getFinalUnsignedApk(getConfiguredTarget("//java/com/foo"))),
"/FooFlags.java");
FileWriteAction action = (FileWriteAction) getGeneratingAction(flagList);
assertThat(action.getFileContents())
.isEqualTo("@//java/com/foo:flag1: on\n@//java/com/foo:flag2: off");
}
@Test
public void featureFlagsSetByAndroidBinaryAreInRequiredFragments() throws Exception {
useConfiguration("--include_config_fragments_provider=direct");
scratch.file(
"java/com/foo/BUILD",
"config_feature_flag(",
" name = 'flag1',",
" allowed_values = ['on', 'off'],",
" default_value = 'off',",
")",
"android_binary(",
" name = 'foo',",
" manifest = 'AndroidManifest.xml',",
" srcs = [':FooFlags.java'],",
" feature_flags = {",
" 'flag1': 'on',",
" },",
")");
ConfiguredTarget ct = getConfiguredTarget("//java/com/foo:foo");
assertThat(ct.getProvider(RequiredConfigFragmentsProvider.class).getStarlarkOptions())
.containsExactly(Label.parseAbsoluteUnchecked("//java/com/foo:flag1"));
}
@Test
public void testNocompressExtensions() throws Exception {
scratch.file(
"java/r/android/BUILD",
"android_binary(",
" name = 'r',",
" srcs = ['Foo.java'],",
" manifest = 'AndroidManifest.xml',",
" resource_files = ['res/raw/foo.apk'],",
" nocompress_extensions = ['.apk', '.so'],",
")");
ConfiguredTarget binary = getConfiguredTarget("//java/r/android:r");
ValidatedAndroidResources resource = getValidatedResources(binary);
List<String> args = resourceArguments(resource);
Artifact inputManifest =
getFirstArtifactEndingWith(
getGeneratingSpawnAction(resource.getManifest()).getInputs(), "AndroidManifest.xml");
assertContainsSublist(
args,
ImmutableList.of(
"--primaryData", "java/r/android/res::" + inputManifest.getExecPathString()));
assertThat(args).contains("--uncompressedExtensions");
assertThat(args.get(args.indexOf("--uncompressedExtensions") + 1)).isEqualTo(".apk,.so");
assertThat(getGeneratingSpawnActionArgs(getCompressedUnsignedApk(binary)))
.containsAtLeast("--nocompress_suffixes", ".apk", ".so")
.inOrder();
assertThat(getGeneratingSpawnActionArgs(getFinalUnsignedApk(binary)))
.containsAtLeast("--nocompress_suffixes", ".apk", ".so")
.inOrder();
}
@Test
public void testFeatureFlagPolicyMustContainRuleToUseFeatureFlags() throws Exception {
reporter.removeHandler(failFastHandler); // expecting an error
useConfiguration("--enforce_transitive_configs_for_config_feature_flag");
scratch.overwriteFile(
"tools/allowlists/config_feature_flag/BUILD",
"package_group(",
" name = 'config_feature_flag',",
" packages = ['//flag'])");
scratch.file(
"flag/BUILD",
"config_feature_flag(",
" name = 'flag',",
" allowed_values = ['right', 'wrong'],",
" default_value = 'right',",
" visibility = ['//java/com/google/android/foo:__pkg__'],",
")");
scratch.file(
"java/com/google/android/foo/BUILD",
"android_binary(",
" name = 'foo',",
" manifest = 'AndroidManifest.xml',",
" srcs = [':FooFlags.java'],",
" feature_flags = {",
" '//flag:flag': 'right',",
" },",
" transitive_configs = ['//flag:flag'],",
")");
assertThat(getConfiguredTarget("//java/com/google/android/foo:foo")).isNull();
assertContainsEvent(
"in android_binary rule //java/com/google/android/foo:foo: "
+ "the attribute feature_flags is not available in this package");
}
@Test
public void testFeatureFlagPolicyDoesNotBlockRuleIfInPolicy() throws Exception {
useConfiguration("--enforce_transitive_configs_for_config_feature_flag");
scratch.overwriteFile(
"tools/allowlists/config_feature_flag/BUILD",
"package_group(",
" name = 'config_feature_flag',",
" packages = ['//flag', '//java/com/google/android/foo'])");
scratch.file(
"flag/BUILD",
"config_feature_flag(",
" name = 'flag',",
" allowed_values = ['right', 'wrong'],",
" default_value = 'right',",
" visibility = ['//java/com/google/android/foo:__pkg__'],",
")");
scratch.file(
"java/com/google/android/foo/BUILD",
"android_binary(",
" name = 'foo',",
" manifest = 'AndroidManifest.xml',",
" srcs = [':FooFlags.java'],",
" feature_flags = {",
" '//flag:flag': 'right',",
" },",
" transitive_configs = ['//flag:flag'],",
")");
assertThat(getConfiguredTarget("//java/com/google/android/foo:foo")).isNotNull();
assertNoEvents();
}
@Test
public void testFeatureFlagPolicyIsNotUsedIfFlagValuesNotUsed() throws Exception {
scratch.overwriteFile(
"tools/allowlists/config_feature_flag/BUILD",
"package_group(",
" name = 'config_feature_flag',",
" packages = ['*super* busted package group'])");
scratch.file(
"java/com/google/android/foo/BUILD",
"android_binary(",
" name = 'foo',",
" manifest = 'AndroidManifest.xml',",
" srcs = [':FooFlags.java'],",
")");
assertThat(getConfiguredTarget("//java/com/google/android/foo:foo")).isNotNull();
// the package_group is busted, so we would have failed to get this far if we depended on it
assertNoEvents();
// Check time: does this test actually test what we're testing for?
reporter.removeHandler(failFastHandler);
assertThat(getConfiguredTarget("//tools/allowlists/config_feature_flag:config_feature_flag"))
.isNull();
assertContainsEvent("*super* busted package group");
}
@Test
public void testAapt2WithAndroidSdk() throws Exception {
scratch.file(
"java/a/BUILD",
"android_binary(",
" name = 'a',",
" srcs = ['A.java'],",
" manifest = 'AndroidManifest.xml',",
" resource_files = [ 'res/values/values.xml' ], ",
")");
ConfiguredTarget a = getConfiguredTarget("//java/a:a");
Artifact apk = getImplicitOutputArtifact(a, AndroidRuleClasses.ANDROID_RESOURCES_APK);
assertThat(getGeneratingSpawnActionArgs(apk))
.containsAtLeast(
"--aapt2",
// The path to aapt2 is different between Blaze and Bazel, so we omit it here.
// It's safe to do so as we've already checked for the `--aapt2` flag.
"--tool",
"AAPT2_PACKAGE");
}
@Test
public void testAapt2WithAndroidSdkAndDependencies() throws Exception {
scratch.file(
"java/b/BUILD",
"android_library(",
" name = 'b',",
" srcs = ['B.java'],",
" manifest = 'AndroidManifest.xml',",
" resource_files = [ 'res/values/values.xml' ], ",
")");
scratch.file(
"java/a/BUILD",
"android_binary(",
" name = 'a',",
" srcs = ['A.java'],",
" manifest = 'AndroidManifest.xml',",
" deps = [ '//java/b:b' ],",
" resource_files = [ 'res/values/values.xml' ], ",
")");
ConfiguredTarget a = getConfiguredTarget("//java/a:a");
ConfiguredTarget b = getDirectPrerequisite(a, "//java/b:b");
Artifact classJar =
getImplicitOutputArtifact(a, AndroidRuleClasses.ANDROID_RESOURCES_CLASS_JAR);
Artifact apk = getImplicitOutputArtifact(a, AndroidRuleClasses.ANDROID_RESOURCES_APK);
SpawnAction apkAction = getGeneratingSpawnAction(apk);
assertThat(getGeneratingSpawnActionArgs(apk))
.containsAtLeast(
"--aapt2",
// The path to aapt2 is different between Blaze and Bazel, so we omit it here.
// It's safe to do so as we've already checked for the `--aapt2` flag.
"--tool",
"AAPT2_PACKAGE");
assertThat(apkAction.getInputs().toList())
.contains(getImplicitOutputArtifact(b, AndroidRuleClasses.ANDROID_COMPILED_SYMBOLS));
SpawnAction classAction = getGeneratingSpawnAction(classJar);
assertThat(classAction.getInputs().toList())
.containsAtLeast(
getImplicitOutputArtifact(a, AndroidRuleClasses.ANDROID_R_TXT),
getImplicitOutputArtifact(b, AndroidRuleClasses.ANDROID_R_TXT));
}
@Test
public void testAapt2ResourceShrinkingAction() throws Exception {
scratch.file(
"java/com/google/android/hello/BUILD",
"android_binary(name = 'hello',",
" srcs = ['Foo.java'],",
" manifest = 'AndroidManifest.xml',",
" resource_files = ['res/values/strings.xml'],",
" shrink_resources = 1,",
" proguard_specs = ['proguard-spec.pro'],)");
ConfiguredTargetAndData targetAndData =
getConfiguredTargetAndData("//java/com/google/android/hello:hello");
ConfiguredTarget binary = targetAndData.getConfiguredTarget();
Artifact jar = getResourceClassJar(targetAndData);
assertThat(getGeneratingAction(jar).getMnemonic()).isEqualTo("RClassGenerator");
assertThat(getGeneratingSpawnActionArgs(jar)).contains("--finalFields");
Set<Artifact> artifacts = actionsTestUtil().artifactClosureOf(getFilesToBuild(binary));
assertThat(artifacts)
.containsAtLeast(
getFirstArtifactEndingWith(artifacts, "resource_files.zip"),
getFirstArtifactEndingWith(artifacts, "proguard.jar"),
getFirstArtifactEndingWith(artifacts, "shrunk.ap_"));
List<String> processingArgs =
getGeneratingSpawnActionArgs(getFirstArtifactEndingWith(artifacts, "resource_files.zip"));
assertThat(flagValue("--resourcesOutput", processingArgs))
.endsWith("hello_files/resource_files.zip");
List<String> proguardArgs =
getGeneratingSpawnActionArgs(getFirstArtifactEndingWith(artifacts, "proguard.jar"));
assertThat(flagValue("-outjars", proguardArgs)).endsWith("hello_proguard.jar");
List<String> shrinkingArgs =
getGeneratingSpawnActionArgs(getFirstArtifactEndingWith(artifacts, "shrunk.ap_"));
assertThat(flagValue("--tool", shrinkingArgs)).isEqualTo("SHRINK_AAPT2");
assertThat(flagValue("--aapt2", shrinkingArgs)).isEqualTo(flagValue("--aapt2", processingArgs));
assertThat(flagValue("--resources", shrinkingArgs))
.isEqualTo(flagValue("--resourcesOutput", processingArgs));
assertThat(flagValue("--shrunkJar", shrinkingArgs))
.isEqualTo(flagValue("-outjars", proguardArgs));
assertThat(flagValue("--proguardMapping", shrinkingArgs))
.isEqualTo(flagValue("-printmapping", proguardArgs));
assertThat(flagValue("--rTxt", shrinkingArgs))
.isEqualTo(flagValue("--rOutput", processingArgs));
assertThat(flagValue("--resourcesConfigOutput", shrinkingArgs))
.endsWith("resource_optimization.cfg");
List<String> packageArgs =
getGeneratingSpawnActionArgs(getFirstArtifactEndingWith(artifacts, "_hello_proguard.cfg"));
assertThat(flagValue("--tool", packageArgs)).isEqualTo("AAPT2_PACKAGE");
assertThat(packageArgs).doesNotContain("--conditionalKeepRules");
}
@Test
public void testAapt2ResourceShrinking_proguardSpecsAbsent_noShrunkApk() throws Exception {
scratch.file(
"java/com/google/android/hello/BUILD",
"android_binary(name = 'hello',",
" srcs = ['Foo.java'],",
" manifest = 'AndroidManifest.xml',",
" resource_files = ['res/values/strings.xml'],",
" shrink_resources = 1,)");
ConfiguredTargetAndData targetAndData =
getConfiguredTargetAndData("//java/com/google/android/hello:hello");
ConfiguredTarget binary = targetAndData.getConfiguredTarget();
Set<Artifact> artifacts = actionsTestUtil().artifactClosureOf(getFilesToBuild(binary));
assertThat(getFirstArtifactEndingWith(artifacts, "shrunk.ap_")).isNull();
}
@Test
public void testAapt2ResourceCycleShrinking() throws Exception {
useConfiguration("--experimental_android_resource_cycle_shrinking=true");
scratch.file(
"java/com/google/android/hello/BUILD",
"android_binary(name = 'hello',",
" srcs = ['Foo.java'],",
" manifest = 'AndroidManifest.xml',",
" resource_files = ['res/values/strings.xml'],",
" shrink_resources = 1,",
" proguard_specs = ['proguard-spec.pro'],)");
ConfiguredTargetAndData targetAndData =
getConfiguredTargetAndData("//java/com/google/android/hello:hello");
ConfiguredTarget binary = targetAndData.getConfiguredTarget();
Artifact jar = getResourceClassJar(targetAndData);
assertThat(getGeneratingAction(jar).getMnemonic()).isEqualTo("RClassGenerator");
assertThat(getGeneratingSpawnActionArgs(jar)).contains("--nofinalFields");
Set<Artifact> artifacts = actionsTestUtil().artifactClosureOf(getFilesToBuild(binary));
List<String> packageArgs =
getGeneratingSpawnActionArgs(getFirstArtifactEndingWith(artifacts, "_hello_proguard.cfg"));
assertThat(flagValue("--tool", packageArgs)).isEqualTo("AAPT2_PACKAGE");
assertThat(packageArgs).contains("--conditionalKeepRules");
}
@Test
public void testAapt2ResourceCycleShinkingWithoutResourceShrinking() throws Exception {
useConfiguration("--experimental_android_resource_cycle_shrinking=true");
scratch.file(
"java/com/google/android/hello/BUILD",
"android_binary(",
" name = 'a',",
" srcs = ['A.java'],",
" manifest = 'AndroidManifest.xml',",
" resource_files = [ 'res/values/values.xml' ], ",
" shrink_resources = 0,",
" proguard_specs = ['proguard-spec.pro'],",
")");
ConfiguredTargetAndData targetAndData =
getConfiguredTargetAndData("//java/com/google/android/hello:a");
ConfiguredTarget binary = targetAndData.getConfiguredTarget();
Artifact jar = getResourceClassJar(targetAndData);
assertThat(getGeneratingAction(jar).getMnemonic()).isEqualTo("RClassGenerator");
assertThat(getGeneratingSpawnActionArgs(jar)).doesNotContain("--nofinalFields");
Set<Artifact> artifacts = actionsTestUtil().artifactClosureOf(getFilesToBuild(binary));
List<String> packageArgs =
getGeneratingSpawnActionArgs(getFirstArtifactEndingWith(artifacts, "_a_proguard.cfg"));
assertThat(flagValue("--tool", packageArgs)).isEqualTo("AAPT2_PACKAGE");
assertThat(packageArgs).doesNotContain("--conditionalKeepRules");
}
@Test
public void testAapt2ResourceCycleShrinkingDisabledNoProguardSpecs() throws Exception {
useConfiguration("--experimental_android_resource_cycle_shrinking=true");
scratch.file(
"java/com/google/android/hello/BUILD",
"android_binary(name = 'hello',",
" srcs = ['Foo.java'],",
" manifest = 'AndroidManifest.xml',",
" resource_files = ['res/values/strings.xml'],",
" shrink_resources = 1)");
ConfiguredTargetAndData targetAndData =
getConfiguredTargetAndData("//java/com/google/android/hello:hello");
Artifact jar = getResourceClassJar(targetAndData);
assertThat(getGeneratingAction(jar).getMnemonic()).isEqualTo("RClassGenerator");
// Final fields should still be generated for non-ProGuarded builds.
assertThat(getGeneratingSpawnActionArgs(jar)).contains("--finalFields");
}
@Test
public void testAapt2ResourceCycleShrinkingDisabledNoProguardSpecsApplicationResources()
throws Exception {
useConfiguration("--experimental_android_resource_cycle_shrinking=true");
scratch.file(
"java/com/google/android/hello/BUILD",
"android_binary(name = 'hello',",
" srcs = ['Foo.java'],",
" manifest = 'AndroidManifest.xml',",
" shrink_resources = 1)");
ConfiguredTargetAndData targetAndData =
getConfiguredTargetAndData("//java/com/google/android/hello:hello");
Artifact jar = getResourceClassJar(targetAndData);
assertThat(getGeneratingAction(jar).getMnemonic()).isEqualTo("RClassGenerator");
// Final fields should still be generated for non-ProGuarded builds.
assertThat(getGeneratingSpawnActionArgs(jar)).contains("--finalFields");
}
@Test
public void testOnlyProguardSpecs() throws Exception {
useConfiguration(
"--experimental_use_dex_splitter_for_incremental_dexing=false",
"--experimental_incremental_dexing_after_proguard_by_default=false",
"--experimental_incremental_dexing_after_proguard=1");
scratch.file(
"java/com/google/android/hello/BUILD",
"android_library(name = 'l2',",
" srcs = ['MoreMaps.java'],",
" neverlink = 1)",
"android_library(name = 'l3',",
" idl_srcs = ['A.aidl'],",
" deps = [':l2'])",
"android_library(name = 'l4',",
" srcs = ['SubMoreMaps.java'],",
" neverlink = 1)",
"android_binary(name = 'b',",
" srcs = ['HelloApp.java'],",
" deps = [':l3', ':l4'],",
" manifest = 'AndroidManifest.xml',",
" proguard_specs = ['proguard-spec.pro', 'proguard-spec1.pro',",
" 'proguard-spec2.pro'])");
ConfiguredTargetAndData binary =
getConfiguredTargetAndData("//java/com/google/android/hello:b");
checkProguardUse(
binary.getConfiguredTarget(),
"b_proguard.jar",
false,
/*passes=*/ null,
// no-op since passes is null.
/*bytecodeOptimizationPassActions=*/ 0,
binary.getConfiguration().getBinFragment(RepositoryName.MAIN)
+ "/java/com/google/android/hello/proguard/b/legacy_b_combined_library_jars.jar");
}
@Test
public void testOnlyProguardSpecsProguardJar() throws Exception {
scratch.file(
"java/com/google/android/hello/BUILD",
"android_library(name = 'l2',",
" srcs = ['MoreMaps.java'],",
" neverlink = 1)",
"android_library(name = 'l3',",
" idl_srcs = ['A.aidl'],",
" deps = [':l2'])",
"android_library(name = 'l4',",
" srcs = ['SubMoreMaps.java'],",
" neverlink = 1)",
"android_binary(name = 'b',",
" srcs = ['HelloApp.java'],",
" deps = [':l3', ':l4'],",
" manifest = 'AndroidManifest.xml',",
" proguard_generate_mapping = 1,",
" proguard_specs = ['proguard-spec.pro', 'proguard-spec1.pro',",
" 'proguard-spec2.pro'])");
ConfiguredTarget output = getConfiguredTarget("//java/com/google/android/hello:b_proguard.jar");
assertProguardUsed(output);
output = getConfiguredTarget("//java/com/google/android/hello:b_proguard.map");
assertWithMessage("proguard.map is not in the rule output")
.that(
actionsTestUtil()
.getActionForArtifactEndingWith(getFilesToBuild(output), "_proguard.map"))
.isNotNull();
}
@Test
public void testCommandLineForMultipleProguardSpecs() throws Exception {
scratch.file(
"java/com/google/android/hello/BUILD",
"android_library(name = 'l1',",
" srcs = ['Maps.java'],",
" neverlink = 1)",
"android_binary(name = 'b',",
" srcs = ['HelloApp.java'],",
" deps = [':l1'],",
" manifest = 'AndroidManifest.xml',",
" proguard_specs = ['proguard-spec.pro', 'proguard-spec1.pro',",
" 'proguard-spec2.pro'])");
ConfiguredTarget binary = getConfiguredTarget("//java/com/google/android/hello:b");
SpawnAction action =
(SpawnAction)
actionsTestUtil()
.getActionForArtifactEndingWith(getFilesToBuild(binary), "_proguard.jar");
assertWithMessage("Proguard action does not contain expected inputs.")
.that(prettyArtifactNames(action.getInputs()))
.containsAtLeast(
"java/com/google/android/hello/proguard-spec.pro",
"java/com/google/android/hello/proguard-spec1.pro",
"java/com/google/android/hello/proguard-spec2.pro");
assertThat(action.getArguments())
.containsExactly(
getProguardBinary().getExecPathString(),
"-forceprocessing",
"-injars",
execPathEndingWith(action.getInputs(), "b_deploy.jar"),
"-outjars",
execPathEndingWith(action.getOutputs(), "b_proguard.jar"),
// Only one combined library jar
"-libraryjars",
execPathEndingWith(action.getInputs(), "legacy_b_combined_library_jars.jar"),
"@" + execPathEndingWith(action.getInputs(), "b_proguard.cfg"),
"@java/com/google/android/hello/proguard-spec.pro",
"@java/com/google/android/hello/proguard-spec1.pro",
"@java/com/google/android/hello/proguard-spec2.pro",
"-printseeds",
execPathEndingWith(action.getOutputs(), "_proguard.seeds"),
"-printusage",
execPathEndingWith(action.getOutputs(), "_proguard.usage"),
"-printconfiguration",
execPathEndingWith(action.getOutputs(), "_proguard.config"))
.inOrder();
}
/** Regression test for b/17790639 */
@Test
public void testNoDuplicatesInProguardCommand() throws Exception {
scratch.file(
"java/com/google/android/hello/BUILD",
"android_library(name = 'l1',",
" srcs = ['Maps.java'],",
" neverlink = 1)",
"android_binary(name = 'b',",
" srcs = ['HelloApp.java'],",
" deps = [':l1'],",
" manifest = 'AndroidManifest.xml',",
" proguard_specs = ['proguard-spec.pro', 'proguard-spec1.pro',",
" 'proguard-spec2.pro'])");
ConfiguredTarget binary = getConfiguredTarget("//java/com/google/android/hello:b");
SpawnAction action =
(SpawnAction)
actionsTestUtil()
.getActionForArtifactEndingWith(getFilesToBuild(binary), "_proguard.jar");
assertThat(action.getArguments())
.containsExactly(
getProguardBinary().getExecPathString(),
"-forceprocessing",
"-injars",
execPathEndingWith(action.getInputs(), "b_deploy.jar"),
"-outjars",
execPathEndingWith(action.getOutputs(), "b_proguard.jar"),
// Only one combined library jar
"-libraryjars",
execPathEndingWith(action.getInputs(), "legacy_b_combined_library_jars.jar"),
"@" + execPathEndingWith(action.getInputs(), "b_proguard.cfg"),
"@java/com/google/android/hello/proguard-spec.pro",
"@java/com/google/android/hello/proguard-spec1.pro",
"@java/com/google/android/hello/proguard-spec2.pro",
"-printseeds",
execPathEndingWith(action.getOutputs(), "_proguard.seeds"),
"-printusage",
execPathEndingWith(action.getOutputs(), "_proguard.usage"),
"-printconfiguration",
execPathEndingWith(action.getOutputs(), "_proguard.config"))
.inOrder();
}
@Test
public void testProguardMapping() throws Exception {
useConfiguration(
"--experimental_use_dex_splitter_for_incremental_dexing=false",
"--experimental_incremental_dexing_after_proguard_by_default=false",
"--experimental_incremental_dexing_after_proguard=1");
scratch.file(
"java/com/google/android/hello/BUILD",
"android_binary(name = 'b',",
" srcs = ['HelloApp.java'],",
" manifest = 'AndroidManifest.xml',",
" proguard_specs = ['proguard-spec.pro'],",
" proguard_generate_mapping = 1)");
checkProguardUse(
getConfiguredTarget("//java/com/google/android/hello:b"),
"b_proguard.jar",
true,
/*passes=*/ null,
// no-op since passes is null.
/*bytecodeOptimizationPassActions=*/ 0,
getAndroidJarPath());
}
@Test
public void testProguardMappingProvider() throws Exception {
scratch.file(
"java/com/google/android/hello/BUILD",
"android_library(name = 'l2',",
" srcs = ['MoreMaps.java'],",
" neverlink = 1)",
"android_library(name = 'l3',",
" idl_srcs = ['A.aidl'],",
" deps = [':l2'])",
"android_library(name = 'l4',",
" srcs = ['SubMoreMaps.java'],",
" neverlink = 1)",
"android_binary(name = 'b1',",
" srcs = ['HelloApp.java'],",
" deps = [':l3', ':l4'],",
" manifest = 'AndroidManifest.xml',",
" proguard_generate_mapping = 1,",
" proguard_specs = ['proguard-spec.pro', 'proguard-spec1.pro',",
" 'proguard-spec2.pro'])",
"android_binary(name = 'b2',",
" srcs = ['HelloApp.java'],",
" deps = [':l3', ':l4'],",
" manifest = 'AndroidManifest.xml',",
" proguard_specs = ['proguard-spec.pro', 'proguard-spec1.pro',",
" 'proguard-spec2.pro'])");
ConfiguredTarget output = getConfiguredTarget("//java/com/google/android/hello:b1");
assertProguardUsed(output);
Artifact mappingArtifact = getBinArtifact("b1_proguard.map", output);
ProguardMappingProvider mappingProvider = output.get(ProguardMappingProvider.PROVIDER);
assertThat(mappingProvider.getProguardMapping()).isEqualTo(mappingArtifact);
output = getConfiguredTarget("//java/com/google/android/hello:b2");
assertProguardUsed(output);
assertThat(output.get(ProguardMappingProvider.PROVIDER)).isNull();
}
@Test
public void testProguardSpecFromLibraryUsedInBinary() throws Exception {
scratch.file(
"java/com/google/android/hello/BUILD",
"android_library(name = 'l2',",
" srcs = ['MoreMaps.java'],",
" proguard_specs = ['library_spec.cfg'])",
"android_library(name = 'l3',",
" idl_srcs = ['A.aidl'],",
" proguard_specs = ['library_spec.cfg'],",
" deps = [':l2'])",
"android_library(name = 'l4',",
" srcs = ['SubMoreMaps.java'],",
" neverlink = 1)",
"android_binary(name = 'b',",
" srcs = ['HelloApp.java'],",
" deps = [':l3', ':l4'],",
" proguard_specs = ['proguard-spec.pro'],",
" manifest = 'AndroidManifest.xml',)");
assertProguardUsed(getConfiguredTarget("//java/com/google/android/hello:b"));
assertProguardGenerated(getConfiguredTarget("//java/com/google/android/hello:b"));
SpawnAction action =
(SpawnAction)
actionsTestUtil()
.getActionForArtifactEndingWith(
getFilesToBuild(getConfiguredTarget("//java/com/google/android/hello:b")),
"_proguard.jar");
assertThat(prettyArtifactNames(action.getInputs()))
.contains("java/com/google/android/hello/proguard-spec.pro");
assertThat(prettyArtifactNames(action.getInputs()))
.contains(
"java/com/google/android/hello/validated_proguard/l2/java/com/google/android/hello/library_spec.cfg_valid");
assertThat(prettyArtifactNames(action.getInputs())).containsNoDuplicates();
}
@Test
public void testResourcesUsedInProguardGenerate() throws Exception {
scratch.file(
"java/com/google/android/hello/BUILD",
"android_binary(name = 'b',",
" srcs = ['HelloApp.java'],",
" manifest = 'AndroidManifest.xml',",
" resource_files = ['res/values/strings.xml'],",
" proguard_specs = ['proguard-spec.pro', 'proguard-spec1.pro',",
" 'proguard-spec2.pro'])");
scratch.file(
"java/com/google/android/hello/res/values/strings.xml",
"<resources><string name = 'hello'>Hello Android!</string></resources>");
ConfiguredTarget binary = getConfiguredTarget("//java/com/google/android/hello:b");
SpawnAction action =
(SpawnAction)
actionsTestUtil()
.getActionForArtifactEndingWith(
actionsTestUtil().artifactClosureOf(getFilesToBuild(binary)), "_proguard.cfg");
assertProguardGenerated(binary);
assertWithMessage("Generate proguard action does not contain expected input.")
.that(prettyArtifactNames(action.getInputs()))
.contains("java/com/google/android/hello/res/values/strings.xml");
}
@Test
public void testUseSingleJarForLibraryJars() throws Exception {
scratch.file(
"java/com/google/android/hello/BUILD",
"android_library(name = 'l1',",
" srcs = ['Maps.java'],",
" neverlink = 1)",
"android_binary(name = 'b',",
" srcs = ['HelloApp.java'],",
" deps = [':l1'],",
" manifest = 'AndroidManifest.xml',",
" proguard_specs = ['proguard-spec.pro', 'proguard-spec1.pro',",
" 'proguard-spec2.pro'])");
ConfiguredTargetAndData binary =
getConfiguredTargetAndData("//java/com/google/android/hello:b");
SpawnAction action =
(SpawnAction)
actionsTestUtil()
.getActionForArtifactEndingWith(
getFilesToBuild(binary.getConfiguredTarget()), "_proguard.jar");
checkProguardLibJars(
action,
binary.getConfiguration().getBinFragment(RepositoryName.MAIN)
+ "/java/com/google/android/hello/proguard/b/legacy_b_combined_library_jars.jar");
}
@Test
public void testUseSingleJarForFilteredLibraryJars() throws Exception {
useConfiguration("--experimental_filter_library_jar_with_program_jar=true");
scratch.file(
"java/com/google/android/hello/BUILD",
"android_library(name = 'l1',",
" srcs = ['Maps.java'],",
" neverlink = 1)",
"android_binary(name = 'b',",
" srcs = ['HelloApp.java'],",
" deps = [':l1'],",
" manifest = 'AndroidManifest.xml',",
" proguard_specs = ['proguard-spec.pro', 'proguard-spec1.pro',",
" 'proguard-spec2.pro'])");
ConfiguredTargetAndData binary =
getConfiguredTargetAndData("//java/com/google/android/hello:b");
SpawnAction action =
(SpawnAction)
actionsTestUtil()
.getActionForArtifactEndingWith(
getFilesToBuild(binary.getConfiguredTarget()), "_proguard.jar");
checkProguardLibJars(
action,
binary.getConfiguration().getBinFragment(RepositoryName.MAIN)
+ "/java/com/google/android/hello/proguard/b/legacy_b_combined_library_jars_filtered.jar");
}
@Test
public void testOnlyOneLibraryJar() throws Exception {
scratch.file(
"java/com/google/android/hello/BUILD",
"android_binary(name = 'b',",
" srcs = ['HelloApp.java'],",
" manifest = 'AndroidManifest.xml',",
" proguard_specs = ['proguard-spec.pro'],",
" proguard_generate_mapping = 1)");
ConfiguredTarget binary = getConfiguredTarget("//java/com/google/android/hello:b");
SpawnAction action =
(SpawnAction)
actionsTestUtil()
.getActionForArtifactEndingWith(getFilesToBuild(binary), "_proguard.jar");
checkProguardLibJars(action, getAndroidJarPath());
}
@Test
public void testApkInfoAccessibleFromStarlark() throws Exception {
scratch.file(
"java/com/google/android/BUILD",
"load(':postprocess.bzl', 'postprocess')",
"android_binary(name = 'b1',",
" srcs = ['b1.java'],",
" manifest = 'AndroidManifest.xml')",
"postprocess(name = 'postprocess', dep = ':b1')");
scratch.file(
"java/com/google/android/postprocess.bzl",
"def _impl(ctx):",
" return [DefaultInfo(files=depset(",
" [ctx.attr.dep[ApkInfo].signed_apk, ctx.attr.dep[ApkInfo].deploy_jar]))]",
"postprocess = rule(implementation=_impl,",
" attrs={'dep': attr.label(providers=[ApkInfo])})");
ConfiguredTarget postprocess = getConfiguredTarget("//java/com/google/android:postprocess");
assertThat(postprocess).isNotNull();
assertThat(
prettyArtifactNames(postprocess.getProvider(FilesToRunProvider.class).getFilesToRun()))
.containsExactly("java/com/google/android/b1.apk", "java/com/google/android/b1_deploy.jar");
}
@Test
public void testInstrumentationInfoAccessibleFromStarlark() throws Exception {
scratch.file(
"java/com/google/android/instr/BUILD",
"load(':instr.bzl', 'instr')",
"android_binary(name = 'b1',",
" srcs = ['b1.java'],",
" instruments = ':b2',",
" manifest = 'AndroidManifest.xml')",
"android_binary(name = 'b2',",
" srcs = ['b2.java'],",
" manifest = 'AndroidManifest.xml')",
"instr(name = 'instr', dep = ':b1')");
scratch.file(
"java/com/google/android/instr/instr.bzl",
"def _impl(ctx):",
" target = ctx.attr.dep[AndroidInstrumentationInfo].target.signed_apk",
" return [DefaultInfo(files=depset([target]))]",
"instr = rule(implementation=_impl,",
" attrs={'dep': attr.label(providers=[AndroidInstrumentationInfo])})");
ConfiguredTarget instr = getConfiguredTarget("//java/com/google/android/instr");
assertThat(instr).isNotNull();
assertThat(prettyArtifactNames(instr.getProvider(FilesToRunProvider.class).getFilesToRun()))
.containsExactly("java/com/google/android/instr/b2.apk");
}
@Test
public void testInstrumentationInfoCreatableFromStarlark() throws Exception {
scratch.file(
"java/com/google/android/instr/BUILD",
"load(':instr.bzl', 'instr')",
"android_binary(name = 'b1',",
" srcs = ['b1.java'],",
" instruments = ':b2',",
" manifest = 'AndroidManifest.xml')",
"android_binary(name = 'b2',",
" srcs = ['b2.java'],",
" manifest = 'AndroidManifest.xml')",
"instr(name = 'instr', dep = ':b1')");
scratch.file(
"java/com/google/android/instr/instr.bzl",
"def _impl(ctx):",
" target = ctx.attr.dep[AndroidInstrumentationInfo].target",
" return [AndroidInstrumentationInfo(target=target)]",
"instr = rule(implementation=_impl,",
" attrs={'dep': attr.label(providers=[AndroidInstrumentationInfo])})");
ConfiguredTarget instr = getConfiguredTarget("//java/com/google/android/instr");
assertThat(instr).isNotNull();
assertThat(instr.get(AndroidInstrumentationInfo.PROVIDER).getTarget().getApk().prettyPrint())
.isEqualTo("java/com/google/android/instr/b2.apk");
}
@Test
public void testInstrumentationInfoProviderHasApks() throws Exception {
scratch.file(
"java/com/google/android/instr/BUILD",
"android_binary(name = 'b1',",
" srcs = ['b1.java'],",
" instruments = ':b2',",
" manifest = 'AndroidManifest.xml')",
"android_binary(name = 'b2',",
" srcs = ['b2.java'],",
" manifest = 'AndroidManifest.xml')");
ConfiguredTarget b1 = getConfiguredTarget("//java/com/google/android/instr:b1");
AndroidInstrumentationInfo provider = b1.get(AndroidInstrumentationInfo.PROVIDER);
assertThat(provider.getTarget()).isNotNull();
assertThat(provider.getTarget().getApk().prettyPrint())
.isEqualTo("java/com/google/android/instr/b2.apk");
}
@Test
public void testNoInstrumentationInfoProviderIfNotInstrumenting() throws Exception {
scratch.file(
"java/com/google/android/instr/BUILD",
"android_binary(name = 'b1',",
" srcs = ['b1.java'],",
" manifest = 'AndroidManifest.xml')");
ConfiguredTarget b1 = getConfiguredTarget("//java/com/google/android/instr:b1");
AndroidInstrumentationInfo provider = b1.get(AndroidInstrumentationInfo.PROVIDER);
assertThat(provider).isNull();
}
@Test
public void testFilterActionWithInstrumentedBinary() throws Exception {
scratch.file(
"java/com/google/android/instr/BUILD",
"android_binary(name = 'b1',",
" srcs = ['b1.java'],",
" instruments = ':b2',",
" manifest = 'AndroidManifest.xml')",
"android_binary(name = 'b2',",
" srcs = ['b2.java'],",
" manifest = 'AndroidManifest.xml')");
ConfiguredTarget b1 = getConfiguredTarget("//java/com/google/android/instr:b1");
SpawnAction action =
(SpawnAction)
actionsTestUtil().getActionForArtifactEndingWith(getFilesToBuild(b1), "_filtered.jar");
assertThat(action.getArguments())
.containsAtLeast(
"--inputZip",
getFirstArtifactEndingWith(action.getInputs(), "b1_deploy.jar").getExecPathString(),
"--filterZips",
getFirstArtifactEndingWith(action.getInputs(), "b2_deploy.jar").getExecPathString(),
"--outputZip",
getFirstArtifactEndingWith(action.getOutputs(), "b1_filtered.jar").getExecPathString(),
"--filterTypes",
".class",
"--checkHashMismatch",
"IGNORE",
"--explicitFilters",
"/BR\\.class$,/databinding/[^/]+Binding\\.class$,(^|/)R\\.class,(^|/)R\\$.*\\.class",
"--outputMode",
"DONT_CARE");
}
/**
* 'proguard_specs' attribute gets read by an implicit outputs function: the current heuristic is
* that if this attribute is configurable, we assume its contents are non-empty and thus create
* the mybinary_proguard.jar output. Test that here.
*/
@Test
public void testConfigurableProguardSpecs() throws Exception {
useConfiguration(
"--experimental_use_dex_splitter_for_incremental_dexing=false",
"--experimental_incremental_dexing_after_proguard_by_default=false",
"--experimental_incremental_dexing_after_proguard=1");
scratch.file(
"conditions/BUILD",
"config_setting(",
" name = 'a',",
" values = {'foo': 'a'})",
"config_setting(",
" name = 'b',",
" values = {'foo': 'b'})");
scratchConfiguredTarget(
"java/foo",
"abin",
"android_binary(",
" name = 'abin',",
" srcs = ['a.java'],",
" proguard_specs = select({",
" '//conditions:a': [':file1.pro'],",
" '//conditions:b': [],",
" '//conditions:default': [':file3.pro'],",
" }) + [",
// Add a long list here as a regression test for b/68238721
" 'file4.pro',",
" 'file5.pro',",
" 'file6.pro',",
" 'file7.pro',",
" 'file8.pro',",
" ],",
" manifest = 'AndroidManifest.xml')");
checkProguardUse(
getConfiguredTarget("//java/foo:abin"),
"abin_proguard.jar",
/*expectMapping=*/ false,
/*passes=*/ null,
// no-op since passes is null.
/*bytecodeOptimizationPassActions=*/ 0,
getAndroidJarPath());
}
@Test
public void alwaysSkipParsingActionWithAapt2() throws Exception {
scratch.file(
"java/b/BUILD",
"android_library(",
" name = 'b',",
" srcs = ['B.java'],",
" manifest = 'AndroidManifest.xml',",
" resource_files = [ 'res/values/values.xml' ], ",
")");
scratch.file(
"java/a/BUILD",
"android_binary(",
" name = 'a',",
" srcs = ['A.java'],",
" manifest = 'AndroidManifest.xml',",
" deps = [ '//java/b:b' ],",
" resource_files = [ 'res/values/values.xml' ], ",
")");
ConfiguredTarget a = getConfiguredTarget("//java/a:a");
ConfiguredTarget b = getDirectPrerequisite(a, "//java/b:b");
List<String> resourceProcessingArgs =
getGeneratingSpawnActionArgs(getValidatedResources(a).getApk());
assertThat(resourceProcessingArgs).contains("AAPT2_PACKAGE");
String directData =
resourceProcessingArgs.get(resourceProcessingArgs.indexOf("--directData") + 1);
assertThat(directData).contains("symbols.zip");
assertThat(directData).doesNotContain("merged.bin");
List<String> resourceMergingArgs =
getGeneratingSpawnActionArgs(getValidatedResources(b).getJavaClassJar());
assertThat(resourceMergingArgs).contains("MERGE_COMPILED");
}
@Test
public void starlarkJavaInfoToAndroidBinaryAttributes() throws Exception {
scratch.file(
"java/r/android/extension.bzl",
"def _impl(ctx):",
" dep_params = ctx.attr.dep[JavaInfo]",
" return [dep_params]",
"my_rule = rule(",
" _impl,",
" attrs = {",
" 'dep': attr.label(),",
" },",
")");
scratch.file(
"java/r/android/BUILD",
"load(':extension.bzl', 'my_rule')",
"android_library(",
" name = 'al_bottom_for_deps',",
" srcs = ['java/A.java'],",
")",
"my_rule(",
" name = 'mya',",
" dep = ':al_bottom_for_deps',",
")",
"android_binary(",
" name = 'foo_app',",
" srcs = ['java/B.java'],",
" deps = [':mya'],",
" manifest = 'AndroidManifest.xml',",
// TODO(b/75051107): Remove the following line when fixed.
" incremental_dexing = 0,",
")");
// Test that all bottom jars are on the runtime classpath of the app.
ConfiguredTarget target = getConfiguredTarget("//java/r/android:foo_app");
ImmutableList<Artifact> transitiveSrcJars =
OutputGroupInfo.get(target).getOutputGroup(JavaSemantics.SOURCE_JARS_OUTPUT_GROUP).toList();
assertThat(ActionsTestUtil.baseArtifactNames(transitiveSrcJars))
.containsExactly("libal_bottom_for_deps-src.jar", "libfoo_app-src.jar");
ImmutableList<Artifact> directSrcJar =
OutputGroupInfo.get(target)
.getOutputGroup(JavaSemantics.DIRECT_SOURCE_JARS_OUTPUT_GROUP)
.toList();
assertThat(ActionsTestUtil.baseArtifactNames(directSrcJar))
.containsExactly("libfoo_app-src.jar");
}
@Test
public void androidManifestMergerOrderAlphabetical_MergeesSortedByExecPath() throws Exception {
useConfiguration("--android_manifest_merger_order=alphabetical");
/*
* Dependency hierarchy:
* - //java/binary:application
* - //java/binary:library
* - //java/common:theme
* - //java/android:utility
* - //java/common:common
* - //java/android:core
*/
scratch.overwriteFile(
"java/android/BUILD",
"android_library(",
" name = 'core',",
" manifest = 'core/AndroidManifest.xml',",
" exports_manifest = 1,",
" resource_files = ['core/res/values/strings.xml'],",
")",
"android_library(",
" name = 'utility',",
" manifest = 'utility/AndroidManifest.xml',",
" exports_manifest = 1,",
" resource_files = ['utility/res/values/values.xml'],",
" deps = ['//java/common:common'],",
")");
scratch.file(
"java/binary/BUILD",
"android_binary(",
" name = 'application',",
" srcs = ['App.java'],",
" manifest = 'app/AndroidManifest.xml',",
" deps = [':library'],",
")",
"android_library(",
" name = 'library',",
" manifest = 'library/AndroidManifest.xml',",
" exports_manifest = 1,",
" deps = ['//java/common:theme', '//java/android:utility'],",
")");
scratch.file(
"java/common/BUILD",
"android_library(",
" name = 'common',",
" manifest = 'common/AndroidManifest.xml',",
" exports_manifest = 1,",
" resource_files = ['common/res/values/common.xml'],",
" deps = ['//java/android:core'],",
")",
"android_library(",
" name = 'theme',",
" manifest = 'theme/AndroidManifest.xml',",
" exports_manifest = 1,",
" resource_files = ['theme/res/values/values.xml'],",
")");
// These have to be found via the same inheritance hierarchy, because getDirectPrerequsite can
// only traverse one level.
ConfiguredTarget application = getConfiguredTarget("//java/binary:application");
ConfiguredTarget library = getDirectPrerequisite(application, "//java/binary:library");
ConfiguredTarget theme = getDirectPrerequisite(library, "//java/common:theme");
ConfiguredTarget utility = getDirectPrerequisite(library, "//java/android:utility");
ConfiguredTarget common = getDirectPrerequisite(utility, "//java/common:common");
ConfiguredTarget core = getDirectPrerequisite(common, "//java/android:core");
Artifact androidCoreManifest = getLibraryManifest(core);
Artifact androidUtilityManifest = getLibraryManifest(utility);
Artifact binaryLibraryManifest = getLibraryManifest(library);
Artifact commonManifest = getLibraryManifest(common);
Artifact commonThemeManifest = getLibraryManifest(theme);
assertThat(getBinaryMergeeManifests(application))
.containsExactlyEntriesIn(
ImmutableMap.of(
androidCoreManifest.getExecPath().toString(), "//java/android:core",
androidUtilityManifest.getExecPath().toString(), "//java/android:utility",
binaryLibraryManifest.getExecPath().toString(), "//java/binary:library",
commonManifest.getExecPath().toString(), "//java/common:common",
commonThemeManifest.getExecPath().toString(), "//java/common:theme"))
.inOrder();
}
@Test
public void androidManifestMergerOrderAlphabeticalByConfiguration_MergeesSortedByPathInBinOrGen()
throws Exception {
useConfiguration(
"--android_manifest_merger_order=alphabetical_by_configuration");
scratch.overwriteFile(
"java/android/BUILD",
"android_library(",
" name = 'core',",
" manifest = 'core/AndroidManifest.xml',",
" exports_manifest = 1,",
" resource_files = ['core/res/values/strings.xml'],",
")",
"android_library(",
" name = 'utility',",
" manifest = 'utility/AndroidManifest.xml',",
" exports_manifest = 1,",
" resource_files = ['utility/res/values/values.xml'],",
" deps = ['//java/common:common'],",
" transitive_configs = ['//flags:a', '//flags:b'],",
")");
scratch.file(
"java/binary/BUILD",
"android_binary(",
" name = 'application',",
" srcs = ['App.java'],",
" manifest = 'app/AndroidManifest.xml',",
" deps = [':library'],",
" feature_flags = {",
" '//flags:a': 'on',",
" '//flags:b': 'on',",
" '//flags:c': 'on',",
" },",
" transitive_configs = ['//flags:a', '//flags:b', '//flags:c'],",
")",
"android_library(",
" name = 'library',",
" manifest = 'library/AndroidManifest.xml',",
" exports_manifest = 1,",
" deps = ['//java/common:theme', '//java/android:utility'],",
" transitive_configs = ['//flags:a', '//flags:b', '//flags:c'],",
")");
scratch.file(
"java/common/BUILD",
"android_library(",
" name = 'common',",
" manifest = 'common/AndroidManifest.xml',",
" exports_manifest = 1,",
" resource_files = ['common/res/values/common.xml'],",
" deps = ['//java/android:core'],",
" transitive_configs = ['//flags:a'],",
")",
"android_library(",
" name = 'theme',",
" manifest = 'theme/AndroidManifest.xml',",
" exports_manifest = 1,",
" resource_files = ['theme/res/values/values.xml'],",
" transitive_configs = ['//flags:a', '//flags:b', '//flags:c'],",
")");
scratch.file(
"flags/BUILD",
"config_feature_flag(",
" name = 'a',",
" allowed_values = ['on', 'off'],",
" default_value = 'off',",
")",
"config_feature_flag(",
" name = 'b',",
" allowed_values = ['on', 'off'],",
" default_value = 'off',",
")",
"config_feature_flag(",
" name = 'c',",
" allowed_values = ['on', 'off'],",
" default_value = 'off',",
")");
assertThat(getBinaryMergeeManifests(getConfiguredTarget("//java/binary:application")).values())
.containsExactly(
"//java/android:core",
"//java/android:utility",
"//java/binary:library",
"//java/common:common",
"//java/common:theme")
.inOrder();
}
@Test
public void testAndroidStarlarkApiNativeLibs() throws Exception {
scratch.file(
"java/a/fetch_native_libs.bzl",
"def _impl(ctx):",
" libs = ctx.attr.android_binary.android.native_libs",
" return [DefaultInfo(files = libs.values()[0])]",
"fetch_native_libs = rule(implementation = _impl,",
" attrs = {",
" 'android_binary': attr.label(),",
" },",
")");
scratch.file(
"java/a/BUILD",
"load('//java/a:fetch_native_libs.bzl', 'fetch_native_libs')",
"android_binary(",
" name = 'app',",
" srcs=['Main.java'],",
" manifest='AndroidManifest.xml',",
" deps=[':cc'],",
")",
"cc_library(",
" name = 'cc',",
" srcs = ['cc.cc'],",
")",
"fetch_native_libs(",
" name = 'clibs',",
" android_binary = 'app',",
")");
ConfiguredTarget clibs = getConfiguredTarget("//java/a:clibs");
assertThat(
ActionsTestUtil.baseArtifactNames(
clibs.getProvider(FileProvider.class).getFilesToBuild()))
.containsExactly("libapp.so");
}
@Test
public void testInstrumentsManifestMergeEnabled() throws Exception {
// This is the incorrect behavior where dependency manifests are merged into the test apk.
useConfiguration("--noexperimental_disable_instrumentation_manifest_merge");
scratch.file(
"java/com/google/android/instr/BUILD",
"android_binary(",
" name = 'b1',",
" srcs = ['b1.java'],",
" instruments = ':b2',",
" deps = [':lib'],",
" manifest = 'test/AndroidManifest.xml',",
")",
"android_library(",
" name = 'lib',",
" manifest = 'lib/AndroidManifest.xml',",
" exports_manifest = 1,",
" resource_files = ['lib/res/values/strings.xml'],",
")",
"android_binary(",
" name = 'b2',",
" srcs = ['b2.java'],",
" deps = [':lib'],",
" manifest = 'bin/AndroidManifest.xml',",
")");
assertThat(
getBinaryMergeeManifests(getConfiguredTarget("//java/com/google/android/instr:b1"))
.values())
.containsExactly("//java/com/google/android/instr:lib");
}
@Test
public void testInstrumentsManifestMergeDisabled() throws Exception {
// This is the correct behavior where dependency manifests are not merged into the test apk.
useConfiguration("--experimental_disable_instrumentation_manifest_merge");
scratch.file(
"java/com/google/android/instr/BUILD",
"android_binary(",
" name = 'b1',",
" srcs = ['b1.java'],",
" instruments = ':b2',",
" deps = [':lib'],",
" manifest = 'test/AndroidManifest.xml',",
")",
"android_library(",
" name = 'lib',",
" manifest = 'lib/AndroidManifest.xml',",
" exports_manifest = 1,",
" resource_files = ['lib/res/values/strings.xml'],",
")",
"android_binary(",
" name = 'b2',",
" srcs = ['b2.java'],",
" deps = [':lib'],",
" manifest = 'bin/AndroidManifest.xml',",
")");
assertThat(
getBinaryMergeeManifests(getConfiguredTarget("//java/com/google/android/instr:b1"))
.values())
.isEmpty();
}
@Test
public void testOptimizedJavaResourcesDisabled() throws Exception {
useConfiguration("--noexperimental_get_android_java_resources_from_optimized_jar");
ConfiguredTarget ct =
scratchConfiguredTarget(
"java/a",
"a",
"android_binary(",
" name = 'a',",
" srcs = ['A.java'],",
" manifest = 'AndroidManifest.xml',",
" proguard_specs = ['proguard.cfg'],",
")");
Set<Artifact> artifacts = actionsTestUtil().artifactClosureOf(getFilesToBuild(ct));
Artifact extractedResources = getFirstArtifactEndingWith(artifacts, "extracted_a_deploy.jar");
String args = Joiner.on(" ").join(getGeneratingSpawnActionArgs(extractedResources));
assertThat(args).contains("/a_deploy.jar");
assertThat(args).doesNotContain("a_proguard.jar");
}
@Test
public void testOptimizedJavaResourcesEnabled() throws Exception {
useConfiguration("--experimental_get_android_java_resources_from_optimized_jar");
ConfiguredTarget ct =
scratchConfiguredTarget(
"java/a",
"a",
"android_binary(",
" name = 'a',",
" srcs = ['A.java'],",
" manifest = 'AndroidManifest.xml',",
" proguard_specs = ['proguard.cfg'],",
")");
Set<Artifact> artifacts = actionsTestUtil().artifactClosureOf(getFilesToBuild(ct));
Artifact extractedResources = getFirstArtifactEndingWith(artifacts, "extracted_a_proguard.jar");
String args = Joiner.on(" ").join(getGeneratingSpawnActionArgs(extractedResources));
assertThat(args).doesNotContain("a_deploy.jar");
assertThat(args).contains("/a_proguard.jar");
}
}