Add dynamic config support for config_setting rules.
Specifically, given:
config_setting(
name = 'foo',
values = {'copts': '-DABC'})
this requires a dependency on the Cpp configuration fragment.
config_setting rules are unique in that this dependency comes
from string representations of option names.
--
MOS_MIGRATED_REVID=108268831
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/TransitiveTargetFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/TransitiveTargetFunction.java
index 47a01ea..9b0eb68 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/TransitiveTargetFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/TransitiveTargetFunction.java
@@ -13,11 +13,14 @@
// limitations under the License.
package com.google.devtools.build.lib.skyframe;
+import static com.google.devtools.build.lib.analysis.config.ConfigRuleClasses.ConfigSettingRule;
+
import com.google.common.collect.ImmutableList;
import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration.Fragment;
import com.google.devtools.build.lib.analysis.config.ConfigurationFragmentFactory;
+import com.google.devtools.build.lib.analysis.config.FragmentOptions;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.cmdline.PackageIdentifier;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
@@ -39,9 +42,14 @@
import com.google.devtools.build.skyframe.SkyKey;
import com.google.devtools.build.skyframe.SkyValue;
import com.google.devtools.build.skyframe.ValueOrException2;
+import com.google.devtools.common.options.Option;
+import java.lang.reflect.Field;
import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
+import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
@@ -56,8 +64,44 @@
private final ConfiguredRuleClassProvider ruleClassProvider;
+ /**
+ * Maps build option names to matching config fragments. This is used to determine correct
+ * fragment requirements for config_setting rules, which are unique in that their dependencies
+ * are triggered by string representations of option names.
+ */
+ private final Map<String, Class<? extends Fragment>> optionsToFragmentMap;
+
TransitiveTargetFunction(RuleClassProvider ruleClassProvider) {
this.ruleClassProvider = (ConfiguredRuleClassProvider) ruleClassProvider;
+ this.optionsToFragmentMap = computeOptionsToFragmentMap(this.ruleClassProvider);
+ }
+
+ /**
+ * Computes the option name --> config fragments map. Note that this mapping is technically
+ * one-to-many: a single option may be required by multiple fragments (e.g. Java options are
+ * used by both JavaConfiguration and Jvm). In such cases, we arbitrarily choose one fragment
+ * since that's all that's needed to satisfy the config_setting.
+ */
+ private static Map<String, Class<? extends Fragment>> computeOptionsToFragmentMap(
+ ConfiguredRuleClassProvider ruleClassProvider) {
+ Map<String, Class<? extends Fragment>> result = new LinkedHashMap<>();
+ Set<Class<? extends FragmentOptions>> visitedOptionsClasses = new HashSet<>();
+ for (ConfigurationFragmentFactory factory : ruleClassProvider.getConfigurationFragments()) {
+ for (Class<? extends FragmentOptions> optionsClass : factory.requiredOptions()) {
+ if (visitedOptionsClasses.contains(optionsClass)) {
+ // Multiple config fragments may require the same options class, but we only need one of
+ // them to guarantee that class makes it into the configuration.
+ continue;
+ }
+ visitedOptionsClasses.add(optionsClass);
+ for (Field field : optionsClass.getFields()) {
+ if (field.isAnnotationPresent(Option.class)) {
+ result.put(field.getAnnotation(Option.class).name(), factory.creates());
+ }
+ }
+ }
+ }
+ return result;
}
@Override
@@ -150,6 +194,15 @@
fragment.asSubclass(BuildConfiguration.Fragment.class));
}
}
+
+ // config_setting rules have values like {"some_flag": "some_value"} that need the
+ // corresponding fragments in their configurations to properly resolve.
+ Rule rule = (Rule) target;
+ if (rule.getRuleClass().equals(ConfigSettingRule.RULE_NAME)) {
+ builder.getTransitiveConfigFragments().addAll(
+ ConfigSettingRule.requiresConfigurationFragments(rule, optionsToFragmentMap));
+ }
+
Class<? extends Fragment> universalFragment =
ruleClassProvider.getUniversalFragment().asSubclass(BuildConfiguration.Fragment.class);
if (!builder.getConfigFragmentsFromDeps().contains(universalFragment)) {