diff options
author | 2015-11-19 19:39:50 +0000 | |
---|---|---|
committer | 2015-11-20 09:17:25 +0000 | |
commit | 4f3b2b30d0834016302c48ea52d39708c7f01eab (patch) | |
tree | 6f672a5cb98a4129645afd3f4810e3527c66f937 /src/main/java/com/google/devtools/build/lib/skyframe/TransitiveTargetFunction.java | |
parent | 9d5ab0beda873a15ec6fe589a1d5cd30e7fa9e72 (diff) |
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
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/skyframe/TransitiveTargetFunction.java')
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/skyframe/TransitiveTargetFunction.java | 53 |
1 files changed, 53 insertions, 0 deletions
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 47a01ea543..9b0eb68ee5 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.lib.skyframe.TransitiveTargetFunction.Transitiv 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 @@ public class TransitiveTargetFunction 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 @@ public class TransitiveTargetFunction 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)) { |