--show_config_fragments logic cleanup.
Let rule implementations manage their implementation-specific requirements.
Also add test coverage for android_binary, android_test, android_local_test
(they set feature flags with a custom attribute, therefore "require" them).
PiperOrigin-RevId: 307859878
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTargetBuilder.java b/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTargetBuilder.java
index ba3fcff..3d827cc 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTargetBuilder.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTargetBuilder.java
@@ -25,7 +25,6 @@
import com.google.devtools.build.lib.actions.Actions.GeneratingActions;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.MutableActionGraph.ActionConflictException;
-import com.google.devtools.build.lib.analysis.config.ConfigMatchingProvider;
import com.google.devtools.build.lib.analysis.config.CoreOptions;
import com.google.devtools.build.lib.analysis.config.CoreOptions.IncludeConfigFragmentsEnum;
import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget;
@@ -48,7 +47,6 @@
import com.google.devtools.build.lib.collect.nestedset.Order;
import com.google.devtools.build.lib.packages.Attribute;
import com.google.devtools.build.lib.packages.BuildSetting;
-import com.google.devtools.build.lib.packages.BuildType;
import com.google.devtools.build.lib.packages.Info;
import com.google.devtools.build.lib.packages.Provider;
import com.google.devtools.build.lib.packages.Rule;
@@ -57,11 +55,12 @@
import com.google.devtools.build.lib.packages.Type.LabelClass;
import com.google.devtools.build.lib.syntax.EvalException;
import com.google.devtools.build.lib.syntax.Location;
+import java.util.Collection;
import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
-import java.util.stream.Collectors;
import javax.annotation.Nullable;
/**
@@ -89,6 +88,8 @@
private Runfiles persistentTestRunnerRunfiles;
private Artifact executable;
private ImmutableSet<ActionAnalysisMetadata> actionsWithoutExtraAction = ImmutableSet.of();
+ private final LinkedHashSet<String> ruleImplSpecificRequiredConfigFragments =
+ new LinkedHashSet<>();
public RuleConfiguredTargetBuilder(RuleContext ruleContext) {
this.ruleContext = ruleContext;
@@ -256,11 +257,8 @@
* CoreOptions.IncludeConfigFragmentsEnum#OFF}.
*
* <p>See {@link ConfiguredTargetFactory#getRequiredConfigFragments} for a description of the
- * meaning of this provider's content. That method populates {@link
- * RuleContext#getRequiredConfigFragments}, which we read here. We add to that any additional
- * config state that is only known to be required after the rule's analysis function has finished.
- * In particular, if the current rule is a {@code config_setting}, we add as a direct requirement
- * the config state that it matches.
+ * meaning of this provider's content. That method populates the results of {@link
+ * RuleContext#getRequiredConfigFragments} and {@link #ruleImplSpecificRequiredConfigFragments}.
*/
private void maybeAddRequiredConfigFragmentsProvider() {
if (ruleContext
@@ -268,36 +266,14 @@
.getOptions()
.get(CoreOptions.class)
.includeRequiredConfigFragmentsProvider
- == IncludeConfigFragmentsEnum.OFF) {
- return;
+ != IncludeConfigFragmentsEnum.OFF) {
+ addProvider(
+ new RequiredConfigFragmentsProvider(
+ ImmutableSet.<String>builder()
+ .addAll(ruleContext.getRequiredConfigFragments())
+ .addAll(ruleImplSpecificRequiredConfigFragments)
+ .build()));
}
-
- ImmutableSet.Builder<String> requiredFragments = ImmutableSet.builder();
- requiredFragments.addAll(ruleContext.getRequiredConfigFragments());
-
- if (providersBuilder.contains(ConfigMatchingProvider.class)) {
- // config_setting discovers extra requirements through its "values = {'some_option': ...}"
- // references. Make sure those are included here.
- requiredFragments.addAll(
- providersBuilder.getProvider(ConfigMatchingProvider.class).getRequiredFragmentOptions());
- }
-
- if (ruleContext.getRule().isAttrDefined("feature_flags", BuildType.LABEL_KEYED_STRING_DICT)) {
- // Sad hack to make android_binary rules list the feature flags they set.
- // TODO(gregce): move this to AndroidBinary.java. This requires some more coordination to
- // make sure we don't add a RequiredConfigFragmentsProvider both there and here, which is
- // technically a violation of TransitiveInfoProviderMapBuilder.put.
- requiredFragments.addAll(
- ruleContext
- .attributes()
- .get("feature_flags", BuildType.LABEL_KEYED_STRING_DICT)
- .keySet()
- .stream()
- .map(label -> label.toString())
- .collect(Collectors.toList()));
- }
-
- addProvider(new RequiredConfigFragmentsProvider(requiredFragments.build()));
}
private NestedSet<Label> transitiveLabels() {
@@ -635,11 +611,11 @@
}
/**
- * Set the extra action pseudo actions.
+ * Supplements {@link #maybeAddRequiredConfigFragmentsProvider} with rule implementation-specific
+ * requirements.
*/
- public RuleConfiguredTargetBuilder setActionsWithoutExtraAction(
- ImmutableSet<ActionAnalysisMetadata> actions) {
- this.actionsWithoutExtraAction = actions;
+ public RuleConfiguredTargetBuilder addRequiredConfigFragments(Collection<String> fragments) {
+ ruleImplSpecificRequiredConfigFragments.addAll(fragments);
return this;
}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java
index d2add04..771fb98 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java
@@ -690,6 +690,11 @@
.addNativeDeclaredProvider(
AndroidFeatureFlagSetProvider.create(
AndroidFeatureFlagSetProvider.getAndValidateFlagMapFromRuleContext(ruleContext)))
+ // Report set feature flags as required "config fragments".
+ // While these aren't technically fragments, in practice they're user-defined settings with
+ // the same meaning: pieces of configuration the rule requires to work properly. So it makes
+ // sense to treat them equivalently for "requirements" reporting purposes.
+ .addRequiredConfigFragments(AndroidFeatureFlagSetProvider.getFlagNames(ruleContext))
.addOutputGroup("android_deploy_info", deployInfo);
}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidFeatureFlagSetProvider.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidFeatureFlagSetProvider.java
index 69a8f25..4323d26 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidFeatureFlagSetProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidFeatureFlagSetProvider.java
@@ -16,6 +16,7 @@
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.analysis.AliasProvider;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment;
@@ -125,6 +126,17 @@
return Optional.of(ImmutableMap.copyOf(expectedValues));
}
+ /** Returns the feature flags this rule sets as user-friendly strings. */
+ public static ImmutableSet<String> getFlagNames(RuleContext ruleContext) {
+ return ruleContext
+ .attributes()
+ .get(AndroidFeatureFlagSetProvider.FEATURE_FLAG_ATTR, BuildType.LABEL_KEYED_STRING_DICT)
+ .keySet()
+ .stream()
+ .map(label -> label.toString())
+ .collect(ImmutableSet.toImmutableSet());
+ }
+
/**
* Returns whether it is acceptable to have a dependency with flags {@code depFlags} if the target
* has flags {@code targetFlags}.
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLocalTestBase.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLocalTestBase.java
index 3a00c5d..83d4287 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLocalTestBase.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLocalTestBase.java
@@ -338,6 +338,11 @@
// Just confirming that there are no aliases being used here.
AndroidFeatureFlagSetProvider.getAndValidateFlagMapFromRuleContext(ruleContext);
+ // Report set feature flags as required "config fragments".
+ // While these aren't technically fragments, in practice they're user-defined settings with
+ // the same meaning: pieces of configuration the rule requires to work properly. So it makes
+ // sense to treat them equivalently for "requirements" reporting purposes.
+ builder.addRequiredConfigFragments(AndroidFeatureFlagSetProvider.getFlagNames(ruleContext));
if (oneVersionOutputArtifact != null) {
builder.addOutputGroup(OutputGroupInfo.HIDDEN_TOP_LEVEL, oneVersionOutputArtifact);
diff --git a/src/main/java/com/google/devtools/build/lib/rules/config/ConfigSetting.java b/src/main/java/com/google/devtools/build/lib/rules/config/ConfigSetting.java
index dd448bb..a0bfdea 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/config/ConfigSetting.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/config/ConfigSetting.java
@@ -156,6 +156,7 @@
.addProvider(FilesToRunProvider.class, FilesToRunProvider.EMPTY)
.addProvider(LicensesProviderImpl.EMPTY)
.addProvider(ConfigMatchingProvider.class, configMatcher)
+ .addRequiredConfigFragments(configMatcher.getRequiredFragmentOptions())
.build();
}
diff --git a/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBinaryTest.java b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBinaryTest.java
index 497671a..38e4202 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBinaryTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBinaryTest.java
@@ -41,6 +41,7 @@
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.cmdline.RepositoryName;
@@ -3401,6 +3402,29 @@
}
@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).getRequiredConfigFragments())
+ .contains("//java/com/foo:flag1");
+ }
+
+ @Test
public void testNocompressExtensions() throws Exception {
scratch.file(
"java/r/android/BUILD",
diff --git a/src/test/java/com/google/devtools/build/lib/rules/android/AndroidLocalTestTest.java b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidLocalTestTest.java
index 6998282..88ac994 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/android/AndroidLocalTestTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidLocalTestTest.java
@@ -22,6 +22,7 @@
import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
import com.google.devtools.build.lib.analysis.ConfiguredTarget;
import com.google.devtools.build.lib.analysis.FileProvider;
+import com.google.devtools.build.lib.analysis.RequiredConfigFragmentsProvider;
import com.google.devtools.build.lib.analysis.actions.SpawnAction;
import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
@@ -277,6 +278,41 @@
assertThat(aaptArguments).contains("ar_XB");
}
+ @Test
+ public void featureFlagsSetByAndroidLocalTestAreInRequiredFragments() throws Exception {
+ useConfiguration("--include_config_fragments_provider=direct");
+ scratch.overwriteFile(
+ "tools/whitelists/config_feature_flag/BUILD",
+ "package_group(",
+ " name = 'config_feature_flag',",
+ " packages = ['//java/com/google/android/foo'])");
+ scratch.file(
+ "java/com/google/android/foo/BUILD",
+ "load('//java/bar:foo.bzl', 'extra_deps')",
+ "config_feature_flag(",
+ " name = 'flag1',",
+ " allowed_values = ['on', 'off'],",
+ " default_value = 'off',",
+ ")",
+ "android_binary(",
+ " name = 'foo_under_test',",
+ " srcs = ['Test.java'],",
+ " manifest = 'AndroidManifest.xml',",
+ ")",
+ "android_local_test(",
+ " name = 'local_test',",
+ " srcs = ['test.java'],",
+ " deps = extra_deps,",
+ " feature_flags = {",
+ " 'flag1': 'on',",
+ " },",
+ " resource_configuration_filters = ['ar_XB'])");
+
+ ConfiguredTarget ct = getConfiguredTarget("//java/com/google/android/foo:local_test");
+ assertThat(ct.getProvider(RequiredConfigFragmentsProvider.class).getRequiredConfigFragments())
+ .contains("//java/com/google/android/foo:flag1");
+ }
+
@Override
protected String getRuleName() {
return "android_local_test";