diff options
Diffstat (limited to 'src/main/java/com/google')
12 files changed, 438 insertions, 4 deletions
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 541569c20a..ac85e0741c 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 @@ -28,6 +28,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.devtools.build.lib.analysis.buildinfo.BuildInfoFactory; 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.BuildOptions; import com.google.devtools.build.lib.analysis.config.ConfigurationFragmentFactory; import com.google.devtools.build.lib.analysis.config.DefaultsPackage; @@ -50,6 +51,7 @@ import com.google.devtools.build.lib.packages.RuleClassProvider; import com.google.devtools.build.lib.packages.RuleErrorConsumer; import com.google.devtools.build.lib.packages.Target; import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.InvocationPolicy; +import com.google.devtools.build.lib.skylarkinterface.SkylarkModule; import com.google.devtools.build.lib.syntax.Environment; import com.google.devtools.build.lib.syntax.Environment.Extension; import com.google.devtools.build.lib.syntax.Environment.Phase; @@ -296,6 +298,9 @@ public class ConfiguredRuleClassProvider implements RuleClassProvider { * between option classes, factories, and fragments, such that the factory depends only on the * options class and creates the fragment. This method provides a convenient way of adding both * the options class and the factory in a single call. + * + * <p>Note that configuration fragments annotated with a Skylark name must have a unique + * name; no two different configuration fragments can share the same name. */ public Builder addConfig( Class<? extends FragmentOptions> options, ConfigurationFragmentFactory factory) { @@ -312,6 +317,12 @@ public class ConfiguredRuleClassProvider implements RuleClassProvider { return this; } + /** + * Adds a configuration fragment factory. + * + * <p>Note that configuration fragments annotated with a Skylark name must have a unique + * name; no two different configuration fragments can share the same name. + */ public Builder addConfigurationFragment(ConfigurationFragmentFactory factory) { configurationFragmentFactories.add(factory); return this; @@ -549,6 +560,8 @@ public class ConfiguredRuleClassProvider implements RuleClassProvider { private final Environment.Frame globals; + private final ImmutableMap<String, Class<?>> configurationFragmentMap; + private ConfiguredRuleClassProvider( Label preludeLabel, String runfilesPrefix, @@ -581,6 +594,7 @@ public class ConfiguredRuleClassProvider implements RuleClassProvider { this.universalFragment = universalFragment; this.prerequisiteValidator = prerequisiteValidator; this.globals = createGlobals(skylarkAccessibleJavaClasses, skylarkModules); + this.configurationFragmentMap = createFragmentMap(configurationFragmentFactories); } public PrerequisiteValidator getPrerequisiteValidator() { @@ -700,6 +714,19 @@ public class ConfiguredRuleClassProvider implements RuleClassProvider { } } + private static ImmutableMap<String, Class<?>> createFragmentMap( + Iterable<ConfigurationFragmentFactory> configurationFragmentFactories) { + ImmutableMap.Builder<String, Class<?>> mapBuilder = ImmutableMap.builder(); + for (ConfigurationFragmentFactory fragmentFactory : configurationFragmentFactories) { + Class<? extends Fragment> fragmentClass = fragmentFactory.creates(); + String fragmentName = SkylarkModule.Resolver.resolveName(fragmentClass); + if (fragmentName != null) { + mapBuilder.put(fragmentName, fragmentClass); + } + } + return mapBuilder.build(); + } + private Environment createSkylarkRuleClassEnvironment( Mutability mutability, Environment.Frame globals, @@ -717,6 +744,7 @@ public class ConfiguredRuleClassProvider implements RuleClassProvider { .setPhase(Phase.LOADING) .build(); SkylarkUtils.setToolsRepository(env, toolsRepository); + SkylarkUtils.setFragmentMap(env, configurationFragmentMap); return env; } @@ -747,6 +775,11 @@ public class ConfiguredRuleClassProvider implements RuleClassProvider { return defaultWorkspaceFileSuffix; } + @Override + public Map<String, Class<?>> getConfigurationFragmentMap() { + return configurationFragmentMap; + } + /** * Returns all registered {@link BuildConfiguration.Fragment} classes. */ diff --git a/src/main/java/com/google/devtools/build/lib/analysis/DependencyResolver.java b/src/main/java/com/google/devtools/build/lib/analysis/DependencyResolver.java index d406511d66..48113f2596 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/DependencyResolver.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/DependencyResolver.java @@ -488,7 +488,7 @@ public abstract class DependencyResolver { LateBoundDefault<FragmentT, ValueT> lateBoundDefault, Rule rule, AttributeMap attributeMap, - BuildConfiguration config) { + BuildConfiguration config) throws EvalException { Class<FragmentT> fragmentClass = lateBoundDefault.getFragmentClass(); // TODO(b/65746853): remove this when nothing uses it anymore if (BuildConfiguration.class.equals(fragmentClass)) { diff --git a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkAttr.java b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkAttr.java index ad927c4801..9883e635e3 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkAttr.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkAttr.java @@ -49,6 +49,7 @@ import com.google.devtools.build.lib.syntax.SkylarkDict; import com.google.devtools.build.lib.syntax.SkylarkList; import com.google.devtools.build.lib.syntax.SkylarkSignatureProcessor; import com.google.devtools.build.lib.syntax.SkylarkType; +import com.google.devtools.build.lib.syntax.SkylarkUtils; import com.google.devtools.build.lib.syntax.Type; import com.google.devtools.build.lib.syntax.Type.ConversionException; import com.google.devtools.build.lib.syntax.Type.LabelClass; @@ -196,6 +197,8 @@ public final class SkylarkAttr implements SkylarkValue { builder.value( new SkylarkComputedDefaultTemplate( type, callback.getParameterNames(), callback, ast.getLocation())); + } else if (defaultValue instanceof SkylarkLateBoundDefault) { + builder.value((SkylarkLateBoundDefault) defaultValue); } else { builder.defaultValue(defaultValue, env.getGlobals().getTransitiveLabel(), DEFAULT_ARG); } @@ -519,6 +522,49 @@ public final class SkylarkAttr implements SkylarkValue { }; @SkylarkSignature( + name = "configuration_field", + returnType = SkylarkLateBoundDefault.class, + // TODO(cparsons): Provide a link to documentation for available SkylarkConfigurationFields. + doc = "References a late-bound default value for an attribute of type " + + "<a href=\"attr.html#label\">label</a>. A value is 'late-bound' if it requires " + + "the configuration to be built before determining the value. Any attribute using this " + + "as a value must <a href=\"../rules.html#private-attributes\">be private</a>.", + parameters = { + @Param( + name = "fragment", + type = String.class, + doc = "The name of a configuration fragment which contains the late-bound value." + ), + @Param( + name = "name", + type = String.class, + doc = "The name of the value to obtain from the configuration fragment."), + }, + useLocation = true, + useEnvironment = true + ) + private static final BuiltinFunction configurationField = + new BuiltinFunction("configuration_field") { + public SkylarkLateBoundDefault<?> invoke( + String fragment, String name, Location loc, Environment env) + throws EvalException { + Class<?> fragmentClass = SkylarkUtils.getFragmentMap(env).get(fragment); + + if (fragmentClass == null) { + throw new EvalException( + loc, + String.format("invalid configuration fragment name '%s'", fragment)); + } + try { + return SkylarkLateBoundDefault.forConfigurationField( + fragmentClass, name, SkylarkUtils.getToolsRepository(env)); + } catch (SkylarkLateBoundDefault.InvalidConfigurationFieldException exception) { + throw new EvalException(loc, exception); + } + } + }; + + @SkylarkSignature( name = "string", doc = "Creates an attribute of type <a href=\"string.html\">string</a>.", objectType = SkylarkAttr.class, @@ -598,6 +644,7 @@ public final class SkylarkAttr implements SkylarkValue { allowedTypes = { @ParamType(type = Label.class), @ParamType(type = String.class), + @ParamType(type = SkylarkLateBoundDefault.class) }, callbackEnabled = true, noneable = true, diff --git a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkConfigurationField.java b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkConfigurationField.java new file mode 100644 index 0000000000..153751c318 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkConfigurationField.java @@ -0,0 +1,71 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.lib.analysis.skylark; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * A marker interface for Java methods of Skylark-exposed configuration fragments which denote + * Skylark "configuration fields": late-bound attribute defaults that depend on configuration. + * + * <p>Methods annotated with this annotation have a few constraints: + * <ul> + * <li>The annotated method must be on a configuration fragment exposed to skylark.</li> + * <li>The method must have return type Label.</li> + * <li>The method must be public.</li> + * <li>The method must have zero arguments.</li> + * <li>The method must not throw exceptions.</li> + * </ul> + */ +// TODO(b/68817606): Verify the above constraints using annotation processing. +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface SkylarkConfigurationField { + + /** + * Name of the configuration field, as exposed to Skylark. + */ + String name(); + + /** + * The default label associated with this field, corresponding to the value of this configuration + * field with default command line flags. + * + * <p>If the default label is under the tools repository, omit the tools repository prefix + * from this default, but set {@link #defaultInToolRepository} to true.</p> + */ + String defaultLabel(); + + /** + * Whether the default label as defined in {@link #defaultLabel} should be prefixed with + * the tools repository. + */ + boolean defaultInToolRepository() default false; + + /** + * The documentation text in Skylark. It can contain HTML tags for special formatting. + * + * <p>It is allowed to be empty only if {@link #documented()} is false. + */ + String doc() default ""; + + /** + * If true, the function will appear in the Skylark documentation. Set this to false if the + * function is experimental or an overloading and doesn't need to be documented. + */ + boolean documented() default true; +} diff --git a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkLateBoundDefault.java b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkLateBoundDefault.java new file mode 100644 index 0000000000..51f694cc4d --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkLateBoundDefault.java @@ -0,0 +1,224 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.devtools.build.lib.analysis.skylark; + +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.collect.ImmutableMap; +import com.google.devtools.build.lib.cmdline.Label; +import com.google.devtools.build.lib.packages.Attribute.LateBoundDefault; +import com.google.devtools.build.lib.packages.AttributeMap; +import com.google.devtools.build.lib.packages.Rule; +import com.google.devtools.build.lib.skylarkinterface.SkylarkModule; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import javax.annotation.concurrent.Immutable; + +/** + * An implementation of {@link LateBoundDefault} which obtains a late-bound attribute value + * (of type 'label') specifically by skylark configuration fragment name and field name, as + * registered by {@link SkylarkConfigurationField}. + * + * <p>For example, a SkylarkLateBoundDefault on "java" and "toolchain" would + * require a valid configuration fragment named "java" with a method annotated with + * {@link SkylarkConfigurationField} of name "toolchain". This {@link LateBoundDefault} would + * provide a late-bound dependency (defined by the label returned by that configuration field) + * in the current target configuration. + */ +@Immutable +public class SkylarkLateBoundDefault<FragmentT> extends LateBoundDefault<FragmentT, Label> { + + private final Method method; + private final String fragmentName; + private final String fragmentFieldName; + + @Override + public Label resolve(Rule rule, AttributeMap attributes, FragmentT config) { + Class<?> fragmentClass = config.getClass(); + try { + Object result = method.invoke(config); + return (Label) result; + } catch (IllegalAccessException | InvocationTargetException e) { + // Configuration field methods should not throw either of these exceptions. + throw new AssertionError("Method invocation failed: " + e); + } + } + + /** + * Returns the {@link SkylarkConfigurationField} annotation corresponding to this method. + */ + private static Label getDefaultLabel( + SkylarkConfigurationField annotation, String toolsRepository) { + Label defaultLabel = annotation.defaultInToolRepository() + ? Label.parseAbsoluteUnchecked(toolsRepository + annotation.defaultLabel()) + : Label.parseAbsoluteUnchecked(annotation.defaultLabel()); + return defaultLabel; + } + + private SkylarkLateBoundDefault(SkylarkConfigurationField annotation, + Class<FragmentT> fragmentClass, String fragmentName, Method method, String toolsRepository) { + super(false /* don't use host configuration */, + fragmentClass, + getDefaultLabel(annotation, toolsRepository)); + + this.method = method; + this.fragmentName = fragmentName; + this.fragmentFieldName = annotation.name(); + } + + /** + * Returns the skylark name of the configuration fragment that this late bound default requires. + */ + public String getFragmentName() { + return fragmentName; + } + + /** + * Returns the skylark name of the configuration field name, as registered by + * {@link SkylarkConfigurationField} annotation on the configuration fragment. + */ + public String getFragmentFieldName() { + return fragmentFieldName; + } + + /** + * An exception thrown if a user specifies an invalid configuration field identifier. + * + * @see SkylarkConfigurationField + **/ + public static class InvalidConfigurationFieldException extends Exception { + public InvalidConfigurationFieldException(String message) { + super(message); + } + } + + + private static class CacheKey { + private final Class<?> fragmentClass; + private final String toolsRepository; + + private CacheKey(Class<?> fragmentClass, + String toolsRepository) { + this.fragmentClass = fragmentClass; + this.toolsRepository = toolsRepository; + } + + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } else if (!(object instanceof CacheKey)) { + return false; + } else { + CacheKey cacheKey = (CacheKey) object; + return fragmentClass.equals(cacheKey.fragmentClass) + && toolsRepository.equals(cacheKey.toolsRepository); + } + } + + @Override + public int hashCode() { + int result = fragmentClass.hashCode(); + result = 31 * result + toolsRepository.hashCode(); + return result; + } + } + + /** + * A cache for efficient {@link SkylarkLateBoundDefault} loading by configuration fragment. Each + * configuration fragment class key is mapped to a {@link Map} where keys are configuration field + * skylark names, and values are the {@link SkylarkLateBoundDefault}s. Methods must be annotated + * with {@link SkylarkConfigurationField} to be considered. + */ + private static final LoadingCache<CacheKey, Map<String, SkylarkLateBoundDefault<?>>> fieldCache = + CacheBuilder.newBuilder() + .initialCapacity(10) + .maximumSize(100) + .build( + new CacheLoader<CacheKey, Map<String, SkylarkLateBoundDefault<?>>>() { + @Override + public Map<String, SkylarkLateBoundDefault<?>> load(CacheKey key) throws Exception { + ImmutableMap.Builder<String, SkylarkLateBoundDefault<?>> lateBoundDefaultMap = + new ImmutableMap.Builder<>(); + Class<?> fragmentClass = key.fragmentClass; + String fragmentName = SkylarkModule.Resolver.resolveName(fragmentClass); + for (Method method : fragmentClass.getMethods()) { + if (method.isAnnotationPresent(SkylarkConfigurationField.class)) { + // TODO(b/68817606): Use annotation processors to verify these constraints. + Preconditions.checkArgument( + method.getReturnType() == Label.class, + String.format("Method %s must have return type 'Label'", method)); + Preconditions.checkArgument( + method.getParameterTypes().length == 0, + String.format("Method %s must not accept arguments", method)); + + SkylarkConfigurationField configField = + method.getAnnotation(SkylarkConfigurationField.class); + lateBoundDefaultMap.put( + configField.name(), + new SkylarkLateBoundDefault<>( + configField, + fragmentClass, + fragmentName, + method, + key.toolsRepository)); + } + } + return lateBoundDefaultMap.build(); + } + }); + + /** + * Returns a {@link LateBoundDefault} which obtains a late-bound attribute value + * (of type 'label') specifically by skylark configuration fragment name and field name, as + * registered by {@link SkylarkConfigurationField}. + * + * @param fragmentClass the configuration fragment class, which must have a valid skylark name + * @param fragmentFieldName the configuration field name, as registered by + * {@link SkylarkConfigurationField} annotation + * @param toolsRepository the Bazel tools repository path fragment + * + * @throws InvalidConfigurationFieldException if there is no valid configuration field with the + * given fragment class and field name + */ + public static <FragmentT> SkylarkLateBoundDefault<FragmentT> forConfigurationField( + Class<FragmentT> fragmentClass, + String fragmentFieldName, + String toolsRepository) throws InvalidConfigurationFieldException { + try { + CacheKey cacheKey = new CacheKey(fragmentClass, toolsRepository); + SkylarkLateBoundDefault resolver = + fieldCache.get(cacheKey).get(fragmentFieldName); + if (resolver == null) { + String fragmentName = SkylarkModule.Resolver.resolveName(fragmentClass); + if (Strings.isNullOrEmpty(fragmentName)) { + throw new AssertionError("fragment class must have a valid skylark name"); + } + throw new InvalidConfigurationFieldException( + String.format("invalid configuration field name '%s' on fragment '%s'", + fragmentFieldName, fragmentName)); + } + return resolver; + } catch (ExecutionException e) { + throw new IllegalStateException("method invocation failed: " + e); + } + } + +} diff --git a/src/main/java/com/google/devtools/build/lib/packages/AbstractAttributeMapper.java b/src/main/java/com/google/devtools/build/lib/packages/AbstractAttributeMapper.java index 8b99d8182b..ec03962931 100644 --- a/src/main/java/com/google/devtools/build/lib/packages/AbstractAttributeMapper.java +++ b/src/main/java/com/google/devtools/build/lib/packages/AbstractAttributeMapper.java @@ -58,6 +58,8 @@ public abstract class AbstractAttributeMapper implements AttributeMap { Object value = attributes.getAttributeValue(index); if (value instanceof Attribute.ComputedDefault) { value = ((Attribute.ComputedDefault) value).getDefault(this); + } else if (value instanceof Attribute.LateBoundDefault) { + value = ((Attribute.LateBoundDefault) value).getDefault(); } try { return type.cast(value); @@ -87,6 +89,25 @@ public abstract class AbstractAttributeMapper implements AttributeMap { } } + /** + * Returns the given attribute if it's a {@link Attribute.LateBoundDefault}, null otherwise. + * + * @throws IllegalArgumentException if the given attribute doesn't exist with the specified + * type. This happens whether or not it's a late bound default. + */ + @Nullable + @SuppressWarnings("unchecked") + public <T> Attribute.LateBoundDefault<?, T> getLateBoundDefault( + String attributeName, Type<T> type) { + int index = getIndexWithTypeCheck(attributeName, type); + Object value = attributes.getAttributeValue(index); + if (value instanceof Attribute.LateBoundDefault) { + return (Attribute.LateBoundDefault<?, T>) value; + } else { + return null; + } + } + @Override public Iterable<String> getAttributeNames() { ImmutableList.Builder<String> names = ImmutableList.builder(); diff --git a/src/main/java/com/google/devtools/build/lib/packages/Attribute.java b/src/main/java/com/google/devtools/build/lib/packages/Attribute.java index 54f6255226..9b158e6a58 100644 --- a/src/main/java/com/google/devtools/build/lib/packages/Attribute.java +++ b/src/main/java/com/google/devtools/build/lib/packages/Attribute.java @@ -680,7 +680,6 @@ public final class Attribute implements Comparable<Attribute> { */ public Builder<TYPE> value(LateBoundDefault<?, ? extends TYPE> defaultValue) { Preconditions.checkState(!valueSet, "the default value is already set"); - Preconditions.checkState(name.isEmpty() || isLateBound(name)); value = defaultValue; valueSource = AttributeValueSource.LATE_BOUND; valueSet = true; @@ -1082,6 +1081,9 @@ public final class Attribute implements Comparable<Attribute> { */ public Attribute build(String name) { Preconditions.checkState(!name.isEmpty(), "name has not been set"); + if (valueSource == AttributeValueSource.LATE_BOUND) { + Preconditions.checkState(isLateBound(name)); + } // TODO(bazel-team): Set the default to be no file type, then remove this check, and also // remove all allowedFileTypes() calls without parameters. @@ -1715,7 +1717,7 @@ public final class Attribute implements Comparable<Attribute> { private static final LateBoundDefault<Void, Void> ALWAYS_NULL = new SimpleLateBoundDefault<>(false, Void.class, null, (rule, attributes, unused) -> null); - private LateBoundDefault( + protected LateBoundDefault( boolean useHostConfiguration, Class<FragmentT> fragmentClass, ValueT defaultValue) { 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 7dd8acba2d..bf99b43ade 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 @@ -1601,6 +1601,8 @@ public class RuleClass { // that depends on non-computed default attribute values, and that condition predicate is // evaluated by the call to Attribute#getDefaultValue. attrsWithComputedDefaults.add(attr); + } else if (attr.isLateBound()) { + rule.setAttributeValue(attr, attr.getLateBoundDefault(), /*explicit=*/ false); } else { Object defaultValue = getAttributeNoncomputedDefaultValue(attr, pkgBuilder); rule.setAttributeValue(attr, defaultValue, /*explicit=*/ false); diff --git a/src/main/java/com/google/devtools/build/lib/packages/RuleClassProvider.java b/src/main/java/com/google/devtools/build/lib/packages/RuleClassProvider.java index 5235523d53..302b05bde2 100644 --- a/src/main/java/com/google/devtools/build/lib/packages/RuleClassProvider.java +++ b/src/main/java/com/google/devtools/build/lib/packages/RuleClassProvider.java @@ -93,4 +93,10 @@ public interface RuleClassProvider { * Retrieves an aspect from the aspect factory map using the key provided */ NativeAspectClass getNativeAspectClass(String key); + + /** + * Retrieves a {@link Map} from skylark configuration fragment name to configuration fragment + * class. + */ + Map<String, Class<?>> getConfigurationFragmentMap(); } diff --git a/src/main/java/com/google/devtools/build/lib/rules/apple/AppleCommandLineOptions.java b/src/main/java/com/google/devtools/build/lib/rules/apple/AppleCommandLineOptions.java index 1446cd0ed3..14fb440670 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/apple/AppleCommandLineOptions.java +++ b/src/main/java/com/google/devtools/build/lib/rules/apple/AppleCommandLineOptions.java @@ -331,7 +331,8 @@ public class AppleCommandLineOptions extends FragmentOptions { * changed from the default using the {@code xcode_version_config} build flag. */ // TODO(cparsons): Update all callers to reference the actual xcode_version_config flag value. - static final String DEFAULT_XCODE_VERSION_CONFIG_LABEL = "//tools/objc:host_xcodes"; + @VisibleForTesting + public static final String DEFAULT_XCODE_VERSION_CONFIG_LABEL = "//tools/objc:host_xcodes"; /** Converter for --default_ios_provisioning_profile. */ public static class DefaultProvisioningProfileConverter extends DefaultLabelConverter { diff --git a/src/main/java/com/google/devtools/build/lib/rules/apple/AppleConfiguration.java b/src/main/java/com/google/devtools/build/lib/rules/apple/AppleConfiguration.java index cdd22ee85f..127b0ace57 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/apple/AppleConfiguration.java +++ b/src/main/java/com/google/devtools/build/lib/rules/apple/AppleConfiguration.java @@ -25,6 +25,7 @@ import com.google.devtools.build.lib.analysis.config.BuildOptions; import com.google.devtools.build.lib.analysis.config.ConfigurationEnvironment; import com.google.devtools.build.lib.analysis.config.ConfigurationFragmentFactory; import com.google.devtools.build.lib.analysis.config.FragmentOptions; +import com.google.devtools.build.lib.analysis.skylark.SkylarkConfigurationField; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; import com.google.devtools.build.lib.rules.apple.AppleCommandLineOptions.AppleBitcodeMode; @@ -416,6 +417,12 @@ public class AppleConfiguration extends BuildConfiguration.Fragment { /** * Returns the label of the xcode_config rule to use for resolving the host system xcode version. */ + @SkylarkConfigurationField( + name = "xcode_config_label", + doc = "Returns the target denoted by the value of the --xcode_version_config flag", + defaultLabel = AppleCommandLineOptions.DEFAULT_XCODE_VERSION_CONFIG_LABEL, + defaultInToolRepository = true + ) public Label getXcodeConfigLabel() { return xcodeConfigLabel; } diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkUtils.java b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkUtils.java index 970543f60a..397768afe4 100644 --- a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkUtils.java +++ b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkUtils.java @@ -14,12 +14,15 @@ package com.google.devtools.build.lib.syntax; +import com.google.common.collect.ImmutableMap; + /** This class contains Bazel-specific functions to extend or interoperate with Skylark. */ public final class SkylarkUtils { /** Bazel-specific information that we store in the Environment. */ private static class BazelInfo { String toolsRepository; + ImmutableMap<String, Class<?>> fragmentNameToClass; } private static final String BAZEL_INFO_KEY = "$bazel"; @@ -46,4 +49,21 @@ public final class SkylarkUtils { public static String getToolsRepository(Environment env) { return getInfo(env).toolsRepository; } + + /** + * Sets, on an {@link Environment}, a {@link Map} from configuration fragment name to + * configuration fragment class. + */ + public static void setFragmentMap(Environment env, + ImmutableMap<String, Class<?>> fragmentNameToClass) { + getInfo(env).fragmentNameToClass = fragmentNameToClass; + } + + /* + * Returns the {@link Map} from configuration fragment name to configuration fragment class, as + * set by {@link #setFragmentMap}. + */ + public static ImmutableMap<String, Class<?>> getFragmentMap(Environment env) { + return getInfo(env).fragmentNameToClass; + } } |