Calculate the options fragment map in ConfiguredRuleClassProvider instead. This refactoring allows for retroactive trimming to use the option fragment map. RELNOTES: None. PiperOrigin-RevId: 236126725
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java index 56ce67b..2e8c096 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java
@@ -66,12 +66,15 @@ import com.google.devtools.build.lib.syntax.SkylarkUtils.Phase; import com.google.devtools.build.lib.syntax.StarlarkSemantics; import com.google.devtools.build.lib.syntax.Type; +import com.google.devtools.common.options.Option; import com.google.devtools.common.options.OptionDefinition; import com.google.devtools.common.options.OptionsProvider; import java.lang.reflect.Constructor; +import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -598,6 +601,13 @@ /** The set of configuration fragment factories. */ private final ImmutableList<ConfigurationFragmentFactory> configurationFragmentFactories; + /** + * 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; + /** The transition factory used to produce the transition that will trim targets. */ @Nullable private final RuleTransitionFactory trimmingTransitionFactory; @@ -659,6 +669,7 @@ this.buildInfoFactories = buildInfoFactories; this.configurationOptions = configurationOptions; this.configurationFragmentFactories = configurationFragments; + this.optionsToFragmentMap = computeOptionsToFragmentMap(configurationFragments); this.universalFragments = universalFragments; this.trimmingTransitionFactory = trimmingTransitionFactory; this.shouldInvalidateCacheForOptionDiff = shouldInvalidateCacheForOptionDiff; @@ -671,6 +682,37 @@ this.thirdPartyLicenseExistencePolicy = thirdPartyLicenseExistencePolicy; } + /** + * 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( + Iterable<ConfigurationFragmentFactory> configurationFragments) { + Map<String, Class<? extends Fragment>> result = new LinkedHashMap<>(); + Map<Class<? extends FragmentOptions>, Integer> visitedOptionsClasses = new HashMap<>(); + for (ConfigurationFragmentFactory factory : configurationFragments) { + Set<Class<? extends FragmentOptions>> requiredOpts = factory.requiredOptions(); + for (Class<? extends FragmentOptions> optionsClass : requiredOpts) { + Integer previousBest = visitedOptionsClasses.get(optionsClass); + if (previousBest != null && previousBest <= requiredOpts.size()) { + // 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. Pick one that depends + // on as few options classes as possible (not necessarily unique). + continue; + } + visitedOptionsClasses.put(optionsClass, requiredOpts.size()); + for (Field field : optionsClass.getFields()) { + if (field.isAnnotationPresent(Option.class)) { + result.put(field.getAnnotation(Option.class).name(), factory.creates()); + } + } + } + } + return result; + } + public PrerequisiteValidator getPrerequisiteValidator() { return prerequisiteValidator; } @@ -719,6 +761,11 @@ return configurationFragmentFactories; } + @Nullable + public Class<? extends Fragment> getConfigurationFragmentForOption(String requiredOption) { + return optionsToFragmentMap.get(requiredOption); + } + /** * Returns the transition factory used to produce the transition to trim targets. *
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 7d67c21..4366a0c 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
@@ -19,7 +19,6 @@ 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.collect.nestedset.NestedSet; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; @@ -42,11 +41,7 @@ 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.HashMap; -import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; @@ -61,47 +56,8 @@ 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<>(); - Map<Class<? extends FragmentOptions>, Integer> visitedOptionsClasses = new HashMap<>(); - for (ConfigurationFragmentFactory factory : ruleClassProvider.getConfigurationFragments()) { - Set<Class<? extends FragmentOptions>> requiredOpts = factory.requiredOptions(); - for (Class<? extends FragmentOptions> optionsClass : requiredOpts) { - Integer previousBest = visitedOptionsClasses.get(optionsClass); - if (previousBest != null && previousBest <= requiredOpts.size()) { - // 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. Pick one that depends - // on as few options classes as possible (not necessarily unique). - continue; - } - visitedOptionsClasses.put(optionsClass, requiredOpts.size()); - for (Field field : optionsClass.getFields()) { - if (field.isAnnotationPresent(Option.class)) { - result.put(field.getAnnotation(Option.class).name(), factory.creates()); - } - } - } - } - return result; } @Override @@ -233,7 +189,7 @@ new ImmutableSet.Builder<>(); for (String requiredOption : requiredOptions) { Class<? extends BuildConfiguration.Fragment> fragment = - optionsToFragmentMap.get(requiredOption); + ruleClassProvider.getConfigurationFragmentForOption(requiredOption); // Null values come from BuildConfiguration.Options, which is implicitly included. if (fragment != null) { optionsFragments.add(fragment);