Skip to content

Commit

Permalink
Make AndroidRuleClasses#hasProguardSpecs less fragile.
Browse files Browse the repository at this point in the history
Keeps the semantics the same.

RELNOTES: None
PiperOrigin-RevId: 173899927
  • Loading branch information
aj-michael authored and katre committed Oct 31, 2017
1 parent fadeb90 commit 5df4759
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -321,9 +321,10 @@ public Attribute.Transition buildTransitionFor(Rule depRule) {
public static boolean hasProguardSpecs(AttributeMap rule) {
// The below is a hack to support configurable attributes (proguard_specs seems like
// too valuable an attribute to make nonconfigurable, and we don't currently
// have the ability to know the configuration when determining implicit outputs).
// An IllegalArgumentException gets triggered if the attribute instance is configurable.
// We assume, heuristically, that means every configurable value is a non-empty list.
// have the ability to know the configuration when determining implicit outputs). So if the
// attribute is configurable, we create the proguard implicit output. At analysis time, we know
// the actual value of proguard_specs, and if it is empty we do not use the proguarded jar for
// dexing. If the user explicitly tries to build the proguard jar, it will fail.
//
// TODO(bazel-team): find a stronger approach for this. One simple approach is to somehow
// receive 'rule' as an AggregatingAttributeMapper instead of a RawAttributeMapper,
Expand All @@ -332,12 +333,8 @@ public static boolean hasProguardSpecs(AttributeMap rule) {
// to somehow determine implicit outputs after the configuration is known. A third
// approach is to refactor the Android rule logic to avoid these dependencies in the
// first place.
try {
return !rule.get("proguard_specs", LABEL_LIST).isEmpty();
} catch (IllegalArgumentException e) {
// We assume at this point the attribute instance is configurable.
return true;
}
return rule.isConfigurable("proguard_specs")
|| !rule.get("proguard_specs", LABEL_LIST).isEmpty();
}

public static final SafeImplicitOutputsFunction ANDROID_BINARY_IMPLICIT_OUTPUTS =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// 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.Iterables.getOnlyElement;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
Expand Down Expand Up @@ -40,6 +41,8 @@
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.AndroidRuleClasses.MultidexMode;
import com.google.devtools.build.lib.rules.android.deployinfo.AndroidDeployInfoOuterClass.AndroidDeployInfo;
Expand Down Expand Up @@ -2330,25 +2333,36 @@ public void testMainDexGenerationWithoutProguardMap() throws Exception {
// regression test for b/14288948
@Test
public void testEmptyListAsProguardSpec() throws Exception {
scratchConfiguredTarget("java/foo", "abin",
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 {
scratchConfiguredTarget("java/foo", "abin",
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
Expand Down Expand Up @@ -3810,4 +3824,44 @@ public void testNoInstrumentationInfoProviderIfNotInstrumenting() throws Excepti
AndroidInstrumentationInfo provider = b1.get(AndroidInstrumentationInfo.PROVIDER);
assertThat(provider).isNull();
}

/**
* '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 {
scratch.file("conditions/BUILD",
"config_setting(",
" name = 'a',",
" values = {'test_arg': 'a'})",
"config_setting(",
" name = 'b',",
" values = {'test_arg': '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(
"//java/foo:abin",
"abin_proguard.jar",
/*expectMapping=*/ false,
/*passes=*/ null,
getAndroidJarPath());
}
}

0 comments on commit 5df4759

Please sign in to comment.