aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/google')
-rw-r--r--src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java33
-rw-r--r--src/main/java/com/google/devtools/build/lib/analysis/DependencyResolver.java2
-rw-r--r--src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkAttr.java47
-rw-r--r--src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkConfigurationField.java71
-rw-r--r--src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkLateBoundDefault.java224
-rw-r--r--src/main/java/com/google/devtools/build/lib/packages/AbstractAttributeMapper.java21
-rw-r--r--src/main/java/com/google/devtools/build/lib/packages/Attribute.java6
-rw-r--r--src/main/java/com/google/devtools/build/lib/packages/RuleClass.java2
-rw-r--r--src/main/java/com/google/devtools/build/lib/packages/RuleClassProvider.java6
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/apple/AppleCommandLineOptions.java3
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/apple/AppleConfiguration.java7
-rw-r--r--src/main/java/com/google/devtools/build/lib/syntax/SkylarkUtils.java20
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;
+ }
}