Skip to content

Commit

Permalink
Calculate the options fragment map in ConfiguredRuleClassProvider ins…
Browse files Browse the repository at this point in the history
…tead.

This refactoring allows for retroactive trimming to use the option fragment map.

RELNOTES: None.
PiperOrigin-RevId: 236126725
  • Loading branch information
mstaib authored and copybara-github committed Feb 28, 2019
1 parent bdbf640 commit 2d7c0ff
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -598,6 +601,13 @@ public String getToolsRepository() {
/** 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;

Expand Down Expand Up @@ -659,6 +669,7 @@ private ConfiguredRuleClassProvider(
this.buildInfoFactories = buildInfoFactories;
this.configurationOptions = configurationOptions;
this.configurationFragmentFactories = configurationFragments;
this.optionsToFragmentMap = computeOptionsToFragmentMap(configurationFragments);
this.universalFragments = universalFragments;
this.trimmingTransitionFactory = trimmingTransitionFactory;
this.shouldInvalidateCacheForOptionDiff = shouldInvalidateCacheForOptionDiff;
Expand All @@ -671,6 +682,37 @@ private ConfiguredRuleClassProvider(
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;
}
Expand Down Expand Up @@ -719,6 +761,11 @@ public ImmutableList<ConfigurationFragmentFactory> getConfigurationFragments() {
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.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -61,47 +56,8 @@ 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<>();
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
Expand Down Expand Up @@ -233,7 +189,7 @@ private Set<Class<? extends Fragment>> getFragmentsFromRequiredOptions(Rule rule
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);
Expand Down

0 comments on commit 2d7c0ff

Please sign in to comment.