diff options
Diffstat (limited to 'src/main/java')
5 files changed, 183 insertions, 105 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java index af05afd28d..b6affcacf9 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java @@ -39,6 +39,7 @@ import com.google.devtools.build.lib.analysis.buildinfo.BuildInfoFactory.BuildIn 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.ConfigMatchingProvider; +import com.google.devtools.build.lib.analysis.config.FragmentCollection; import com.google.devtools.build.lib.collect.ImmutableSortedKeyListMultimap; import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; @@ -257,26 +258,44 @@ public final class RuleContext extends TargetContext * Returns a configuration fragment for this this target. */ @Nullable - public <T extends Fragment> T getFragment(Class<T> fragment) { + public <T extends Fragment> T getFragment(Class<T> fragment, ConfigurationTransition config) { // TODO(bazel-team): The fragments can also be accessed directly through BuildConfiguration. // Can we lock that down somehow? - Preconditions.checkArgument(isLegalFragment(fragment), - "%s does not have access to %s", rule.getRuleClass(), fragment); - return getConfiguration().getFragment(fragment); + Preconditions.checkArgument(isLegalFragment(fragment, config), + "%s does not have access to '%s' in %s configuration", rule.getRuleClass(), + fragment.getSimpleName(), FragmentCollection.getConfigurationName(config)); + return getConfiguration(config).getFragment(fragment); + } + + @Nullable + public <T extends Fragment> T getFragment(Class<T> fragment) { + // NONE means target configuration. + return getFragment(fragment, ConfigurationTransition.NONE); } @Nullable - public Fragment getSkylarkFragment(String name) { - Class<? extends Fragment> fragmentClass = getConfiguration().getSkylarkFragmentByName(name); - return (fragmentClass == null) ? null : getFragment(fragmentClass); + public Fragment getSkylarkFragment(String name, ConfigurationTransition config) { + Class<? extends Fragment> fragmentClass = + getConfiguration(config).getSkylarkFragmentByName(name); + return (fragmentClass == null) ? null : getFragment(fragmentClass, config); + } + + public ImmutableCollection<String> getSkylarkFragmentNames(ConfigurationTransition config) { + return getConfiguration(config).getSkylarkFragmentNames(); } - public ImmutableCollection<String> getSkylarkFragmentNames() { - return getConfiguration().getSkylarkFragmentNames(); + public <T extends Fragment> boolean isLegalFragment( + Class<T> fragment, ConfigurationTransition config) { + return rule.getRuleClassObject().isLegalConfigurationFragment(fragment, config); } public <T extends Fragment> boolean isLegalFragment(Class<T> fragment) { - return rule.getRuleClassObject().isLegalConfigurationFragment(fragment); + // NONE means target configuration. + return isLegalFragment(fragment, ConfigurationTransition.NONE); + } + + protected BuildConfiguration getConfiguration(ConfigurationTransition config) { + return config.equals(ConfigurationTransition.HOST) ? hostConfiguration : getConfiguration(); } @Override diff --git a/src/main/java/com/google/devtools/build/lib/analysis/config/FragmentCollection.java b/src/main/java/com/google/devtools/build/lib/analysis/config/FragmentCollection.java index d649f47b51..a7d935a23c 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/config/FragmentCollection.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/config/FragmentCollection.java @@ -17,6 +17,7 @@ import com.google.common.base.Joiner; import com.google.common.collect.ImmutableCollection; import com.google.devtools.build.lib.analysis.RuleContext; import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; +import com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition; import com.google.devtools.build.lib.syntax.ClassObject; import com.google.devtools.build.lib.syntax.SkylarkModule; @@ -30,30 +31,38 @@ import javax.annotation.Nullable; @SkylarkModule(name = "fragments", documented = false, doc = "") public class FragmentCollection implements ClassObject { private final RuleContext ruleContext; + private final ConfigurationTransition config; - public FragmentCollection(RuleContext ruleContext) { + public FragmentCollection(RuleContext ruleContext, ConfigurationTransition config) { this.ruleContext = ruleContext; + this.config = config; } @Override @Nullable public Object getValue(String name) { - return ruleContext.getSkylarkFragment(name); + return ruleContext.getSkylarkFragment(name, config); } @Override public ImmutableCollection<String> getKeys() { - return ruleContext.getSkylarkFragmentNames(); + return ruleContext.getSkylarkFragmentNames(config); } @Override @Nullable public String errorMessage(String name) { - return String.format("There is no configuration fragment named '%s'. Available fragments: %s", - name, printKeys()); + return String.format( + "There is no configuration fragment named '%s' in %s configuration. " + + "Available fragments: %s", + name, getConfigurationName(config), printKeys()); } private String printKeys() { return String.format("'%s'", Joiner.on("', '").join(getKeys())); } + + public static String getConfigurationName(ConfigurationTransition config) { + return (config == ConfigurationTransition.HOST) ? "host" : "target"; + } }
\ No newline at end of file diff --git a/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java b/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java index 59fcbb595b..1202f0465c 100644 --- a/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java +++ b/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java @@ -31,6 +31,7 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Ordering; import com.google.devtools.build.lib.events.EventHandler; import com.google.devtools.build.lib.events.Location; +import com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition; import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType; import com.google.devtools.build.lib.syntax.Argument; import com.google.devtools.build.lib.syntax.BaseFunction; @@ -501,7 +502,8 @@ public final class RuleClass { private SkylarkEnvironment ruleDefinitionEnvironment = null; private Set<Class<?>> configurationFragments = new LinkedHashSet<>(); private MissingFragmentPolicy missingFragmentPolicy = MissingFragmentPolicy.FAIL_ANALYSIS; - private Set<String> requiredFragmentNames = new LinkedHashSet<>(); + private Map<ConfigurationTransition, ImmutableSet<String>> requiredFragmentNames = + new LinkedHashMap<>(); private FragmentClassNameResolver fragmentNameResolver; private boolean supportsConstraintChecking = true; @@ -590,7 +592,7 @@ public final class RuleClass { configuredTargetFactory, validityPredicate, preferredDependencyPredicate, ImmutableSet.copyOf(advertisedProviders), configuredTargetFunction, externalBindingsFunction, ruleDefinitionEnvironment, configurationFragments, - requiredFragmentNames, fragmentNameResolver, missingFragmentPolicy, + ImmutableMap.copyOf(requiredFragmentNames), fragmentNameResolver, missingFragmentPolicy, supportsConstraintChecking, attributes.values().toArray(new Attribute[0])); } @@ -615,14 +617,18 @@ public final class RuleClass { this.missingFragmentPolicy = missingFragmentPolicy; return this; } - + /** - * Does the same as {@link #requiresConfigurationFragments(Class...)}, except for taking names - * of fragments instead of classes. + * Declares the configuration fragments that are required by this rule. + * + * <p>In contrast to {@link #requiresConfigurationFragments(Class...)}, this method a) takes the + * names of fragments instead of their classes and b) distinguishes whether the fragments can be + * accessed in host (HOST) or target (NONE) configuration. */ public Builder requiresConfigurationFragments( - FragmentClassNameResolver fragmentNameResolver, String... configurationFragmentNames) { - Collections.addAll(requiredFragmentNames, configurationFragmentNames); + FragmentClassNameResolver fragmentNameResolver, + Map<ConfigurationTransition, ImmutableSet<String>> configurationFragmentNames) { + requiredFragmentNames.putAll(configurationFragmentNames); this.fragmentNameResolver = fragmentNameResolver; return this; } @@ -974,10 +980,12 @@ public final class RuleClass { private final ImmutableSet<Class<?>> requiredConfigurationFragments; /** - * Same idea as requiredConfigurationFragments, but stores fragments by name instead of by class + * A dictionary that maps configurations (NONE for target configuration, HOST for host + * configuration) to lists of names of required configuration fragments. */ - private final ImmutableSet<String> requiredConfigurationFragmentNames; - + private final ImmutableMap<ConfigurationTransition, ImmutableSet<String>> + requiredConfigurationFragmentNames; + /** * Used to resolve the names of fragments in order to compare them to values in {@link * #requiredConfigurationFragmentNames} @@ -1037,7 +1045,8 @@ public final class RuleClass { externalBindingsFunction, ruleDefinitionEnvironment, allowedConfigurationFragments, - ImmutableSet.<String>of(), null, + ImmutableMap.<ConfigurationTransition, ImmutableSet<String>>of(), + null, // FragmentClassNameResolver missingFragmentPolicy, supportsConstraintChecking, attributes); @@ -1078,7 +1087,7 @@ public final class RuleClass { Function<? super Rule, Map<String, Label>> externalBindingsFunction, @Nullable SkylarkEnvironment ruleDefinitionEnvironment, Set<Class<?>> allowedConfigurationFragments, - Set<String> allowedConfigurationFragmentNames, + ImmutableMap<ConfigurationTransition, ImmutableSet<String>> allowedConfigurationFragmentNames, @Nullable FragmentClassNameResolver fragmentNameResolver, MissingFragmentPolicy missingFragmentPolicy, boolean supportsConstraintChecking, @@ -1102,8 +1111,7 @@ public final class RuleClass { this.workspaceOnly = workspaceOnly; this.outputsDefaultExecutable = outputsDefaultExecutable; this.requiredConfigurationFragments = ImmutableSet.copyOf(allowedConfigurationFragments); - this.requiredConfigurationFragmentNames = - ImmutableSet.copyOf(allowedConfigurationFragmentNames); + this.requiredConfigurationFragmentNames = allowedConfigurationFragmentNames; this.fragmentNameResolver = fragmentNameResolver; this.missingFragmentPolicy = missingFragmentPolicy; this.supportsConstraintChecking = supportsConstraintChecking; @@ -1260,24 +1268,32 @@ public final class RuleClass { } /** - * Checks if the configuration fragment may be accessed (i.e., if it's declared). If no fragments - * are declared, this allows access to all fragments for backwards compatibility. + * Checks if the configuration fragment may be accessed (i.e., if it's declared) in the specified + * configuration (target or host). */ - public boolean isLegalConfigurationFragment(Class<?> configurationFragment) { + public boolean isLegalConfigurationFragment( + Class<?> configurationFragment, ConfigurationTransition config) { return requiredConfigurationFragments.contains(configurationFragment) - || hasLegalFragmentName(configurationFragment); + || hasLegalFragmentName(configurationFragment, config); + } + + public boolean isLegalConfigurationFragment(Class<?> configurationFragment) { + // NONE means target configuration. + return isLegalConfigurationFragment(configurationFragment, ConfigurationTransition.NONE); } /** - * Checks whether the name of the given fragment class was declared as required fragment + * Checks whether the name of the given fragment class was declared as required fragment in the + * specified configuration (target or host). */ - private boolean hasLegalFragmentName(Class<?> configurationFragment) { + private boolean hasLegalFragmentName( + Class<?> configurationFragment, ConfigurationTransition config) { if (fragmentNameResolver == null) { return false; } String name = fragmentNameResolver.resolveName(configurationFragment); - return (name != null && requiredConfigurationFragmentNames.contains(name)); + return (name != null && requiredConfigurationFragmentNames.get(config).contains(name)); } /** diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java index e2540342c7..42449c534a 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java +++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java @@ -16,6 +16,7 @@ package com.google.devtools.build.lib.rules; import static com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition.DATA; import static com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition.HOST; +import static com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition.NONE; import static com.google.devtools.build.lib.packages.Attribute.attr; import static com.google.devtools.build.lib.packages.Type.BOOLEAN; import static com.google.devtools.build.lib.packages.Type.INTEGER; @@ -33,7 +34,7 @@ import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Iterables; +import com.google.common.collect.ImmutableSet; import com.google.devtools.build.lib.analysis.BaseRuleClasses; import com.google.devtools.build.lib.analysis.config.BuildConfiguration; import com.google.devtools.build.lib.analysis.config.RunUnder; @@ -78,6 +79,7 @@ import com.google.devtools.build.lib.syntax.SkylarkSignature.Param; import com.google.devtools.build.lib.syntax.SkylarkSignatureProcessor; import com.google.devtools.build.lib.vfs.PathFragment; +import java.util.HashMap; import java.util.Map; import java.util.concurrent.ExecutionException; @@ -232,72 +234,91 @@ public class SkylarkRuleClassFunctions { @Param(name = "output_to_genfiles", type = Boolean.class, defaultValue = "False", doc = "If true, the files will be generated in the genfiles directory instead of the " + "bin directory. This is used for compatibility with existing rules."), - @Param(name = "fragments", type = SkylarkList.class, generic1 = String.class, + @Param(name = "fragments", type = SkylarkList.class, generic1 = String.class, defaultValue = "[]", - doc = "List of names of configuration fragments that the rule requires.")}, + doc = + "List of names of configuration fragments that the rule requires " + + "in target configuration."), + @Param(name = "host_fragments", type = SkylarkList.class, generic1 = String.class, + defaultValue = "[]", + doc = + "List of names of configuration fragments that the rule requires " + + "in host configuration.")}, useAst = true, useEnvironment = true) private static final BuiltinFunction rule = new BuiltinFunction("rule") { - @SuppressWarnings({"rawtypes", "unchecked"}) // castMap produces - // an Attribute.Builder instead of a Attribute.Builder<?> but it's OK. - public BaseFunction invoke(BaseFunction implementation, Boolean test, Object attrs, - Object implicitOutputs, Boolean executable, Boolean outputToGenfiles, - SkylarkList fragments, FuncallExpression ast, Environment funcallEnv) - throws EvalException, ConversionException { - - funcallEnv.checkLoadingPhase("rule", ast.getLocation()); - RuleClassType type = test ? RuleClassType.TEST : RuleClassType.NORMAL; - RuleClass parent = test ? testBaseRule : (executable ? binaryBaseRule : baseRule); - - // We'll set the name later, pass the empty string for now. - RuleClass.Builder builder = new RuleClass.Builder("", type, true, parent); - - if (attrs != Runtime.NONE) { - for (Map.Entry<String, Attribute.Builder> attr : castMap( - attrs, String.class, Attribute.Builder.class, "attrs").entrySet()) { - Attribute.Builder<?> attrBuilder = (Attribute.Builder<?>) attr.getValue(); - String attrName = attributeToNative(attr.getKey(), ast.getLocation(), - attrBuilder.hasLateBoundValue()); - builder.addOrOverrideAttribute(attrBuilder.build(attrName)); - } - } - if (executable || test) { - builder.addOrOverrideAttribute( - attr("$is_executable", BOOLEAN).value(true) - .nonconfigurable("Called from RunCommand.isExecutable, which takes a Target") - .build()); - builder.setOutputsDefaultExecutable(); + @SuppressWarnings({"rawtypes", "unchecked"}) // castMap produces + // an Attribute.Builder instead of a Attribute.Builder<?> but it's OK. + public BaseFunction invoke(BaseFunction implementation, Boolean test, Object attrs, + Object implicitOutputs, Boolean executable, Boolean outputToGenfiles, SkylarkList fragments, + SkylarkList hostFragments, FuncallExpression ast, Environment funcallEnv) + throws EvalException, ConversionException { + funcallEnv.checkLoadingPhase("rule", ast.getLocation()); + RuleClassType type = test ? RuleClassType.TEST : RuleClassType.NORMAL; + RuleClass parent = test ? testBaseRule : (executable ? binaryBaseRule : baseRule); + + // We'll set the name later, pass the empty string for now. + RuleClass.Builder builder = new RuleClass.Builder("", type, true, parent); + + if (attrs != Runtime.NONE) { + for (Map.Entry<String, Attribute.Builder> attr : + castMap(attrs, String.class, Attribute.Builder.class, "attrs").entrySet()) { + Attribute.Builder<?> attrBuilder = (Attribute.Builder<?>) attr.getValue(); + String attrName = + attributeToNative(attr.getKey(), ast.getLocation(), attrBuilder.hasLateBoundValue()); + builder.addOrOverrideAttribute(attrBuilder.build(attrName)); } + } + if (executable || test) { + builder.addOrOverrideAttribute( + attr("$is_executable", BOOLEAN) + .value(true) + .nonconfigurable("Called from RunCommand.isExecutable, which takes a Target") + .build()); + builder.setOutputsDefaultExecutable(); + } - if (implicitOutputs != Runtime.NONE) { - if (implicitOutputs instanceof BaseFunction) { - BaseFunction func = (BaseFunction) implicitOutputs; - final SkylarkCallbackFunction callback = - new SkylarkCallbackFunction(func, ast, (SkylarkEnvironment) funcallEnv); - builder.setImplicitOutputsFunction( - new SkylarkImplicitOutputsFunctionWithCallback(callback, ast.getLocation())); - } else { - builder.setImplicitOutputsFunction(new SkylarkImplicitOutputsFunctionWithMap( - ImmutableMap.copyOf(castMap(implicitOutputs, String.class, String.class, - "implicit outputs of the rule class")))); - } + if (implicitOutputs != Runtime.NONE) { + if (implicitOutputs instanceof BaseFunction) { + BaseFunction func = (BaseFunction) implicitOutputs; + final SkylarkCallbackFunction callback = + new SkylarkCallbackFunction(func, ast, (SkylarkEnvironment) funcallEnv); + builder.setImplicitOutputsFunction( + new SkylarkImplicitOutputsFunctionWithCallback(callback, ast.getLocation())); + } else { + builder.setImplicitOutputsFunction( + new SkylarkImplicitOutputsFunctionWithMap(ImmutableMap.copyOf(castMap(implicitOutputs, + String.class, String.class, "implicit outputs of the rule class")))); } + } - if (outputToGenfiles) { - builder.setOutputToGenfiles(); - } + if (outputToGenfiles) { + builder.setOutputToGenfiles(); + } - if (!fragments.isEmpty()) { - builder.requiresConfigurationFragments( - new SkylarkModuleNameResolver(), - Iterables.toArray(castList(fragments, String.class), String.class)); - } + registerRequiredFragments(fragments, hostFragments, builder); - builder.setConfiguredTargetFunction(implementation); - builder.setRuleDefinitionEnvironment( - ((SkylarkEnvironment) funcallEnv).getGlobalEnvironment()); - return new RuleFunction(builder, type); - } - }; + builder.setConfiguredTargetFunction(implementation); + builder.setRuleDefinitionEnvironment( + ((SkylarkEnvironment) funcallEnv).getGlobalEnvironment()); + return new RuleFunction(builder, type); + } + + private void registerRequiredFragments( + SkylarkList fragments, SkylarkList hostFragments, RuleClass.Builder builder) { + Map<ConfigurationTransition, ImmutableSet<String>> map = new HashMap<>(); + addFragmentsToMap(map, fragments, NONE); // NONE represents target configuration + addFragmentsToMap(map, hostFragments, HOST); + + builder.requiresConfigurationFragments(new SkylarkModuleNameResolver(), map); + } + + private void addFragmentsToMap(Map<ConfigurationTransition, ImmutableSet<String>> map, + SkylarkList fragments, ConfigurationTransition config) { + if (!fragments.isEmpty()) { + map.put(config, ImmutableSet.copyOf(castList(fragments, String.class))); + } + } + }; // This class is needed for testing static final class RuleFunction extends BaseFunction { diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleContext.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleContext.java index af962f076a..d3909fbcf8 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleContext.java +++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleContext.java @@ -36,6 +36,7 @@ import com.google.devtools.build.lib.analysis.config.BuildConfiguration; import com.google.devtools.build.lib.analysis.config.FragmentCollection; import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.packages.Attribute; +import com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition; import com.google.devtools.build.lib.packages.ImplicitOutputsFunction; import com.google.devtools.build.lib.packages.ImplicitOutputsFunction.SkylarkImplicitOutputsFunction; import com.google.devtools.build.lib.packages.OutputFile; @@ -94,9 +95,11 @@ public final class SkylarkRuleContext { }); private final RuleContext ruleContext; - + private final FragmentCollection fragments; + private final FragmentCollection hostFragments; + // TODO(bazel-team): support configurable attributes. private final SkylarkClassObject attrObject; @@ -120,7 +123,8 @@ public final class SkylarkRuleContext { */ public SkylarkRuleContext(RuleContext ruleContext) throws EvalException { this.ruleContext = Preconditions.checkNotNull(ruleContext); - fragments = new FragmentCollection(ruleContext); + fragments = new FragmentCollection(ruleContext, ConfigurationTransition.NONE); + hostFragments = new FragmentCollection(ruleContext, ConfigurationTransition.HOST); HashMap<String, Object> outputsBuilder = new HashMap<>(); if (ruleContext.getRule().getRuleClassObject().outputsDefaultExecutable()) { @@ -326,19 +330,28 @@ public final class SkylarkRuleContext { return ruleContext.getLabel(); } - @SkylarkCallable( - name = "fragments", - structField = true, - doc = - "Allows access to configuration fragments. Possible fields are <code>cpp</code>, " - + "<code>java</code> and <code>jvm</code>. " - + "However, rules have to declare their required fragments in order to access them " - + "(see <a href=\"../rules.html#fragments\">here</a>)." - ) + @SkylarkCallable(name = "fragments", structField = true, + doc = + "Allows access to configuration fragments in target configuration. " + + "Possible fields are <code>cpp</code>, " + + "<code>java</code> and <code>jvm</code>. " + + "However, rules have to declare their required fragments in order to access them " + + "(see <a href=\"../rules.html#fragments\">here</a>).") public FragmentCollection getFragments() { return fragments; } + @SkylarkCallable(name = "host_fragments", structField = true, + doc = + "Allows access to configuration fragments in host configuration. " + + "Possible fields are <code>cpp</code>, " + + "<code>java</code> and <code>jvm</code>. " + + "However, rules have to declare their required fragments in order to access them " + + "(see <a href=\"../rules.html#fragments\">here</a>).") + public FragmentCollection getHostFragments() { + return hostFragments; + } + @SkylarkCallable(name = "configuration", structField = true, doc = "Returns the default configuration. See the <a href=\"configuration.html\">" + "configuration</a> type for more details.") |