aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java
diff options
context:
space:
mode:
authorGravatar Dmitry Lomov <dslomov@google.com>2015-12-16 15:10:20 +0000
committerGravatar Damien Martin-Guillerez <dmarting@google.com>2015-12-16 15:31:44 +0000
commitace678e16def33a94ad1cb3bec7336d894510272 (patch)
treef83705c727e7af498cc2e2ad0e75020d866d14c0 /src/main/java
parent8853df9a19dabc72c3b723b84e6cb69b6fb2884e (diff)
Implement aspect attributes and expose them to aspect implementation function.
-- MOS_MIGRATED_REVID=110356954
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java19
-rw-r--r--src/main/java/com/google/devtools/build/lib/packages/AspectDefinition.java11
-rw-r--r--src/main/java/com/google/devtools/build/lib/packages/Attribute.java4
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java160
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleConfiguredTargetBuilder.java3
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleContext.java403
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/SkylarkAspectFactory.java3
7 files changed, 384 insertions, 219 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 b75c6b9d11..cc9951458f 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
@@ -136,7 +136,7 @@ public final class RuleContext extends TargetContext
private final Set<ConfigMatchingProvider> configConditions;
private final AttributeMap attributes;
private final ImmutableSet<String> features;
- private final Map<String, Attribute> aspectAttributes;
+ private final ImmutableMap<String, Attribute> aspectAttributes;
private final BuildConfiguration hostConfiguration;
private final ConfigurationFragmentPolicy configurationFragmentPolicy;
private final Class<? extends BuildConfiguration.Fragment> universalFragment;
@@ -147,11 +147,13 @@ public final class RuleContext extends TargetContext
/* lazily computed cache for Make variables, computed from the above. See get... method */
private transient ConfigurationMakeVariableContext configurationMakeVariableContext = null;
- private RuleContext(Builder builder, ListMultimap<String, ConfiguredTarget> targetMap,
+ private RuleContext(
+ Builder builder,
+ ListMultimap<String, ConfiguredTarget> targetMap,
ListMultimap<String, ConfiguredFilesetEntry> filesetEntryMap,
Set<ConfigMatchingProvider> configConditions,
Class<? extends BuildConfiguration.Fragment> universalFragment,
- Map<String, Attribute> aspectAttributes) {
+ ImmutableMap<String, Attribute> aspectAttributes) {
super(builder.env, builder.rule, builder.configuration, builder.prerequisiteMap.get(null),
builder.visibility);
this.rule = builder.rule;
@@ -234,6 +236,13 @@ public final class RuleContext extends TargetContext
}
/**
+ * Attributes from aspects.
+ */
+ public ImmutableMap<String, Attribute> getAspectAttributes() {
+ return aspectAttributes;
+ }
+
+ /**
* Accessor for the Rule's attribute values.
*/
public AttributeMap attributes() {
@@ -1241,7 +1250,7 @@ public final class RuleContext extends TargetContext
private ListMultimap<Attribute, ConfiguredTarget> prerequisiteMap;
private Set<ConfigMatchingProvider> configConditions;
private NestedSet<PackageSpecification> visibility;
- private Map<String, Attribute> aspectAttributes;
+ private ImmutableMap<String, Attribute> aspectAttributes;
Builder(AnalysisEnvironment env, Rule rule, BuildConfiguration configuration,
BuildConfiguration hostConfiguration,
@@ -1284,7 +1293,7 @@ public final class RuleContext extends TargetContext
* Adds attributes which are defined by an Aspect (and not by RuleClass).
*/
Builder setAspectAttributes(Map<String, Attribute> aspectAttributes) {
- this.aspectAttributes = aspectAttributes;
+ this.aspectAttributes = ImmutableMap.copyOf(aspectAttributes);
return this;
}
diff --git a/src/main/java/com/google/devtools/build/lib/packages/AspectDefinition.java b/src/main/java/com/google/devtools/build/lib/packages/AspectDefinition.java
index 15829d8216..763ae117c0 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/AspectDefinition.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/AspectDefinition.java
@@ -250,6 +250,17 @@ public final class AspectDefinition {
*/
public <TYPE> Builder add(Attribute.Builder<TYPE> attr) {
Attribute attribute = attr.build();
+ return add(attribute);
+ }
+
+ /**
+ * Adds an attribute to the aspect.
+ *
+ * <p>Since aspects do not appear in BUILD files, the attribute must be either implicit
+ * (not available in the BUILD file, starting with '$') or late-bound (determined after the
+ * configuration is available, starting with ':')
+ */
+ public Builder add(Attribute attribute) {
Preconditions.checkState(attribute.isImplicit() || attribute.isLateBound());
Preconditions.checkState(!attributes.containsKey(attribute.getName()),
"An attribute with the name '%s' already exists.", attribute.getName());
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 8cd855ce49..3bbccdfc26 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
@@ -479,6 +479,10 @@ public final class Attribute implements Comparable<Attribute> {
return this;
}
+ public boolean isValueSet() {
+ return valueSet;
+ }
+
/**
* Sets the attribute default value to a computed default value - use
* this when the default value is a function of other attributes of the
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 c3c48972e4..ac705a5f2b 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
@@ -62,6 +62,7 @@ import com.google.devtools.build.lib.packages.RuleFactory.InvalidRuleException;
import com.google.devtools.build.lib.packages.SkylarkAspectClass;
import com.google.devtools.build.lib.packages.TargetUtils;
import com.google.devtools.build.lib.packages.TestSize;
+import com.google.devtools.build.lib.rules.SkylarkAttr.Descriptor;
import com.google.devtools.build.lib.syntax.BaseFunction;
import com.google.devtools.build.lib.syntax.BuiltinFunction;
import com.google.devtools.build.lib.syntax.ClassObject;
@@ -202,7 +203,6 @@ public class SkylarkRuleClassFunctions {
}
// TODO(bazel-team): implement attribute copy and other rule properties
-
@SkylarkSignature(name = "rule", doc =
"Creates a new rule. Store it in a global value, so that it can be loaded and called "
+ "from BUILD files.",
@@ -243,14 +243,14 @@ public class SkylarkRuleClassFunctions {
doc = "If true, the files will be generated in the genfiles directory instead of the "
+ "bin directory. Unless you need it for compatibility with existing rules "
+ "(e.g. when generating header files for C++), do not set this flag."),
- @Param(name = "fragments", type = SkylarkList.class, generic1 = String.class,
- defaultValue = "[]",
- 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.")},
+ @Param(name = "fragments", type = SkylarkList.class, generic1 = String.class,
+ defaultValue = "[]",
+ 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
@@ -265,19 +265,8 @@ public class SkylarkRuleClassFunctions {
// We'll set the name later, pass the empty string for now.
RuleClass.Builder builder = new RuleClass.Builder("", type, true, parent);
- ImmutableList.Builder<Pair<String, SkylarkAttr.Descriptor>> attributes =
- ImmutableList.builder();
-
- if (attrs != Runtime.NONE) {
- for (Map.Entry<String, SkylarkAttr.Descriptor> attr :
- castMap(attrs, String.class, SkylarkAttr.Descriptor.class, "attrs").entrySet()) {
- SkylarkAttr.Descriptor attrDescriptor = attr.getValue();
- String attrName =
- attributeToNative(attr.getKey(), ast.getLocation(),
- attrDescriptor.getAttributeBuilder().hasLateBoundValue());
- attributes.add(Pair.of(attrName, attrDescriptor));
- }
- }
+ ImmutableList<Pair<String, SkylarkAttr.Descriptor>> attributes =
+ attrObjectToAttributesList(attrs, ast);
if (executable || test) {
addAttribute(
ast.getLocation(),
@@ -292,16 +281,18 @@ public class SkylarkRuleClassFunctions {
if (implicitOutputs != Runtime.NONE) {
if (implicitOutputs instanceof BaseFunction) {
BaseFunction func = (BaseFunction) implicitOutputs;
- SkylarkCallbackFunction callback =
- new SkylarkCallbackFunction(func, ast, funcallEnv);
+ SkylarkCallbackFunction callback = new SkylarkCallbackFunction(func, ast, 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"))));
+ ImmutableMap.copyOf(
+ castMap(
+ implicitOutputs,
+ String.class,
+ String.class,
+ "implicit outputs of the rule class"))));
}
}
@@ -312,27 +303,48 @@ public class SkylarkRuleClassFunctions {
registerRequiredFragments(fragments, hostFragments, builder);
builder.setConfiguredTargetFunction(implementation);
builder.setRuleDefinitionEnvironment(funcallEnv);
- return new RuleFunction(builder, type, attributes.build(), ast.getLocation());
+ return new RuleFunction(builder, type, attributes, ast.getLocation());
}
+ private void registerRequiredFragments(
+ SkylarkList fragments, SkylarkList hostFragments, RuleClass.Builder builder)
+ throws EvalException {
+ Map<ConfigurationTransition, ImmutableSet<String>> map = new HashMap<>();
+ addFragmentsToMap(map, fragments, NONE); // NONE represents target configuration
+ addFragmentsToMap(map, hostFragments, HOST);
- private void registerRequiredFragments(
- SkylarkList fragments, SkylarkList hostFragments, RuleClass.Builder builder)
- throws EvalException {
- Map<ConfigurationTransition, ImmutableSet<String>> map = new HashMap<>();
- addFragmentsToMap(map, fragments, NONE); // NONE represents target configuration
- addFragmentsToMap(map, hostFragments, HOST);
+ builder.requiresConfigurationFragments(new SkylarkModuleNameResolver(), map);
+ }
- builder.requiresConfigurationFragments(new SkylarkModuleNameResolver(), map);
- }
+ private void addFragmentsToMap(
+ Map<ConfigurationTransition, ImmutableSet<String>> map,
+ SkylarkList fragments,
+ ConfigurationTransition config)
+ throws EvalException {
+ if (!fragments.isEmpty()) {
+ map.put(config, ImmutableSet.copyOf(fragments.getContents(String.class, "fragments")));
+ }
+ }
+ };
- private void addFragmentsToMap(Map<ConfigurationTransition, ImmutableSet<String>> map,
- SkylarkList fragments, ConfigurationTransition config) throws EvalException {
- if (!fragments.isEmpty()) {
- map.put(config, ImmutableSet.copyOf(fragments.getContents(String.class, "fragments")));
+ protected static ImmutableList<Pair<String, Descriptor>> attrObjectToAttributesList(
+ Object attrs, FuncallExpression ast) throws EvalException {
+ ImmutableList.Builder<Pair<String, Descriptor>> attributes = ImmutableList.builder();
+
+ if (attrs != Runtime.NONE) {
+ for (Map.Entry<String, Descriptor> attr :
+ castMap(attrs, String.class, Descriptor.class, "attrs").entrySet()) {
+ Descriptor attrDescriptor = attr.getValue();
+ String attrName =
+ attributeToNative(
+ attr.getKey(),
+ ast.getLocation(),
+ attrDescriptor.getAttributeBuilder().hasLateBoundValue());
+ attributes.add(Pair.of(attrName, attrDescriptor));
}
}
- };
+ return attributes.build();
+ }
private static void addAttribute(
Location location, RuleClass.Builder builder, Attribute attribute) throws EvalException {
@@ -356,12 +368,7 @@ public class SkylarkRuleClassFunctions {
generic1 = String.class,
defaultValue = "[]"
),
- @Param(
- name = "extra_deps",
- type = SkylarkList.class,
- generic1 = String.class,
- defaultValue = "[]"
- )
+ @Param(name = "attrs", type = Map.class, noneable = true, defaultValue = "None")
},
useEnvironment = true,
useAst = true
@@ -371,29 +378,37 @@ public class SkylarkRuleClassFunctions {
public SkylarkAspect invoke(
BaseFunction implementation,
SkylarkList attributeAspects,
- SkylarkList extraDeps,
+ Object attrs,
FuncallExpression ast,
- Environment funcallEnv) throws EvalException {
- ImmutableList.Builder<String> attributeListBuilder = ImmutableList.builder();
+ Environment funcallEnv)
+ throws EvalException {
+ ImmutableList.Builder<String> attrAspects = ImmutableList.builder();
for (Object attributeAspect : attributeAspects) {
- attributeListBuilder.add(STRING.convert(attributeAspect, "attr_aspects"));
+ attrAspects.add(STRING.convert(attributeAspect, "attr_aspects"));
}
- ImmutableList.Builder<Label> extraDepsBuilder = ImmutableList.builder();
- for (Object extraDep : extraDeps) {
- String extraDepsString = STRING.convert(extraDep, "extra_deps");
- Label label;
- try {
- label = Label.parseAbsolute(extraDepsString);
- } catch (LabelSyntaxException e) {
- throw new EvalException(ast.getLocation(), e.getMessage());
+
+ ImmutableList<Pair<String, SkylarkAttr.Descriptor>> attributes =
+ attrObjectToAttributesList(attrs, ast);
+ for (Pair<String, Descriptor> attribute : attributes) {
+ String nativeName = attribute.getFirst();
+ if (!Attribute.isImplicit(nativeName)) {
+ throw new EvalException(
+ ast.getLocation(),
+ String.format(
+ "Aspect attribute '%s' must be implicit (its name should start with '_').",
+ nativeName));
+ }
+
+ String skylarkName = "_" + nativeName.substring(1);
+
+ if (!attribute.getSecond().getAttributeBuilder().isValueSet()) {
+ throw new EvalException(
+ ast.getLocation(),
+ String.format("Aspect attribute '%s' has no default value.", skylarkName));
}
- extraDepsBuilder.add(label);
}
- return new SkylarkAspect(implementation,
- attributeListBuilder.build(),
- extraDepsBuilder.build(),
- funcallEnv);
+ return new SkylarkAspect(implementation, attrAspects.build(), attributes, funcallEnv);
}
};
@@ -634,17 +649,18 @@ public class SkylarkRuleClassFunctions {
public static final class SkylarkAspect implements SkylarkValue {
private final BaseFunction implementation;
private final ImmutableList<String> attributeAspects;
- private final ImmutableList<Label> extraDeps;
+ private final ImmutableList<Pair<String, Descriptor>> attributes;
private final Environment funcallEnv;
private Exported exported;
public SkylarkAspect(
BaseFunction implementation,
ImmutableList<String> attributeAspects,
- ImmutableList<Label> extraDeps, Environment funcallEnv) {
+ ImmutableList<Pair<String, Descriptor>> attributes,
+ Environment funcallEnv) {
this.implementation = implementation;
this.attributeAspects = attributeAspects;
- this.extraDeps = extraDeps;
+ this.attributes = attributes;
this.funcallEnv = funcallEnv;
}
@@ -660,8 +676,8 @@ public class SkylarkRuleClassFunctions {
return funcallEnv;
}
- public ImmutableList<Label> getExtraDeps() {
- return extraDeps;
+ public ImmutableList<Pair<String, Descriptor>> getAttributes() {
+ return attributes;
}
@Override
@@ -737,9 +753,11 @@ public class SkylarkRuleClassFunctions {
for (String attributeAspect : skylarkAspect.getAttributeAspects()) {
builder.attributeAspect(attributeAspect, this);
}
- builder.add(attr("$extra_deps", LABEL_LIST).value(skylarkAspect.getExtraDeps()));
+ ImmutableList<Pair<String, Descriptor>> attributes = skylarkAspect.getAttributes();
+ for (Pair<String, Descriptor> attribute : attributes) {
+ builder.add(attribute.second.getAttributeBuilder().build(attribute.first));
+ }
this.aspectDefinition = builder.build();
-
}
@Override
diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleConfiguredTargetBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleConfiguredTargetBuilder.java
index 31693fc200..ad7ff5e088 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleConfiguredTargetBuilder.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleConfiguredTargetBuilder.java
@@ -27,6 +27,7 @@ import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.events.Location;
import com.google.devtools.build.lib.packages.Rule;
import com.google.devtools.build.lib.packages.TargetUtils;
+import com.google.devtools.build.lib.rules.SkylarkRuleContext.Kind;
import com.google.devtools.build.lib.syntax.BaseFunction;
import com.google.devtools.build.lib.syntax.ClassObject;
import com.google.devtools.build.lib.syntax.ClassObject.SkylarkClassObject;
@@ -53,7 +54,7 @@ public final class SkylarkRuleConfiguredTargetBuilder {
throws InterruptedException {
String expectFailure = ruleContext.attributes().get("expect_failure", Type.STRING);
try (Mutability mutability = Mutability.create("configured target")) {
- SkylarkRuleContext skylarkRuleContext = new SkylarkRuleContext(ruleContext);
+ SkylarkRuleContext skylarkRuleContext = new SkylarkRuleContext(ruleContext, Kind.RULE);
Environment env = Environment.builder(mutability)
.setSkylark()
.setGlobals(
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 54c9b7ff65..d335cc4f53 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
@@ -13,11 +13,13 @@
// limitations under the License.
package com.google.devtools.build.lib.rules;
+import com.google.common.base.Function;
import com.google.common.cache.CacheBuilder;
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.ImmutableMap.Builder;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.actions.Artifact;
@@ -34,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.cmdline.Label;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
+import com.google.devtools.build.lib.events.Location;
import com.google.devtools.build.lib.packages.Attribute;
import com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition;
import com.google.devtools.build.lib.packages.BuildType;
@@ -57,6 +60,7 @@ import com.google.devtools.build.lib.util.Preconditions;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
@@ -94,6 +98,53 @@ public final class SkylarkRuleContext {
return Class.forName(classPath);
}
});
+ public static final String EXECUTABLE_DOC =
+ "A <code>struct</code> containing executable files defined in label type "
+ + "attributes marked as <code>executable=True</code>. The struct fields correspond "
+ + "to the attribute names. Each struct value is always a <code>file</code>s or "
+ + "<code>None</code>. If an optional attribute is not specified in the rule "
+ + "then the corresponding struct value is <code>None</code>. If a label type is not "
+ + "marked as <code>executable=True</code>, no corresponding struct field is generated.";
+ public static final String FILES_DOC =
+ "A <code>struct</code> containing files defined in label or label list "
+ + "type attributes. The struct fields correspond to the attribute names. The struct "
+ + "values are <code>list</code> of <code>file</code>s. If an optional attribute is "
+ + "not specified in the rule, an empty list is generated."
+ + "It is a shortcut for:"
+ + "<pre class=language-python>[f for t in ctx.attr.<ATTR> for f in t.files]</pre>";
+ public static final String FILE_DOC =
+ "A <code>struct</code> containing files defined in label type "
+ + "attributes marked as <code>single_file=True</code>. The struct fields correspond "
+ + "to the attribute names. The struct value is always a <code>file</code> or "
+ + "<code>None</code>. If an optional attribute is not specified in the rule "
+ + "then the corresponding struct value is <code>None</code>. If a label type is not "
+ + "marked as <code>single_file=True</code>, no corresponding struct field is generated. "
+ + "It is a shortcut for:"
+ + "<pre class=language-python>list(ctx.attr.<ATTR>.files)[0]</pre>";
+ public static final String ATTR_DOC =
+ "A struct to access the values of the attributes. The values are provided by "
+ + "the user (if not, a default value is used).";
+ public static final String OUTPUTS_DOC =
+ "A <code>struct</code> containing all the output files."
+ + " The struct is generated the following way:<br>"
+ + "<ul><li>If the rule is marked as <code>executable=True</code> the struct has an "
+ + "\"executable\" field with the rules default executable <code>file</code> value."
+ + "<li>For every entry in the rule's <code>outputs</code> dict an attr is generated with "
+ + "the same name and the corresponding <code>file</code> value."
+ + "<li>For every output type attribute a struct attribute is generated with the "
+ + "same name and the corresponding <code>file</code> value or <code>None</code>, "
+ + "if no value is specified in the rule."
+ + "<li>For every output list type attribute a struct attribute is generated with the "
+ + "same name and corresponding <code>list</code> of <code>file</code>s value "
+ + "(an empty list if no value is specified in the rule).</ul>";
+ public static final Function<Attribute, Object> ATTRIBUTE_VALUE_EXTRACTOR_FOR_ASPECT =
+ new Function<Attribute, Object>() {
+ @Nullable
+ @Override
+ public Object apply(Attribute attribute) {
+ return attribute.getDefaultValue(null);
+ }
+ };
private final RuleContext ruleContext;
@@ -101,94 +152,134 @@ public final class SkylarkRuleContext {
private final FragmentCollection hostFragments;
- // TODO(bazel-team): support configurable attributes.
- private final SkylarkClassObject attrObject;
-
- private final SkylarkClassObject outputsObject;
-
- private final SkylarkClassObject executableObject;
-
- private final SkylarkClassObject fileObject;
-
- private final SkylarkClassObject filesObject;
+ private final ImmutableMap<String, String> makeVariables;
+ private final SkylarkRuleAttributesCollection attributesCollection;
+ private final SkylarkRuleAttributesCollection ruleAttributesCollection;
// TODO(bazel-team): we only need this because of the css_binary rule.
- private final ImmutableMap<Artifact, Label> artifactLabelMap;
-
- private final ImmutableMap<Artifact, FilesToRunProvider> executableRunfilesMap;
+ private final ImmutableMap<Artifact, Label> artifactsLabelMap;
+ private final SkylarkClassObject outputsObject;
- private final ImmutableMap<String, String> makeVariables;
+ /**
+ * Determines whether this context is for rule implementation or for aspect implementation.
+ */
+ public enum Kind {
+ RULE,
+ ASPECT
+ }
/**
* Creates a new SkylarkRuleContext using ruleContext.
- * @throws InterruptedException
+ * @throws InterruptedException
*/
- public SkylarkRuleContext(RuleContext ruleContext) throws EvalException, InterruptedException {
+ public SkylarkRuleContext(RuleContext ruleContext, Kind kind)
+ throws EvalException, InterruptedException {
this.ruleContext = Preconditions.checkNotNull(ruleContext);
fragments = new FragmentCollection(ruleContext, ConfigurationTransition.NONE);
hostFragments = new FragmentCollection(ruleContext, ConfigurationTransition.HOST);
- HashMap<String, Object> outputsBuilder = new HashMap<>();
- if (ruleContext.getRule().getRuleClassObject().outputsDefaultExecutable()) {
- addOutput(outputsBuilder, "executable", ruleContext.createOutputArtifact());
- }
- ImplicitOutputsFunction implicitOutputsFunction =
- ruleContext.getRule().getRuleClassObject().getImplicitOutputsFunction();
-
- if (implicitOutputsFunction instanceof SkylarkImplicitOutputsFunction) {
- SkylarkImplicitOutputsFunction func = (SkylarkImplicitOutputsFunction)
- ruleContext.getRule().getRuleClassObject().getImplicitOutputsFunction();
- for (Map.Entry<String, String> entry : func.calculateOutputs(
- RawAttributeMapper.of(ruleContext.getRule())).entrySet()) {
- addOutput(outputsBuilder, entry.getKey(),
- ruleContext.getImplicitOutputArtifact(entry.getValue()));
+ if (kind == Kind.RULE) {
+ Collection<Attribute> attributes = ruleContext.getRule().getAttributes();
+ HashMap<String, Object> outputsBuilder = new HashMap<>();
+ if (ruleContext.getRule().getRuleClassObject().outputsDefaultExecutable()) {
+ addOutput(outputsBuilder, "executable", ruleContext.createOutputArtifact());
}
- }
+ ImplicitOutputsFunction implicitOutputsFunction =
+ ruleContext.getRule().getRuleClassObject().getImplicitOutputsFunction();
- ImmutableMap.Builder<Artifact, Label> artifactLabelMapBuilder =
- ImmutableMap.builder();
- for (Attribute a : ruleContext.getRule().getAttributes()) {
- String attrName = a.getName();
- Type<?> type = a.getType();
- if (type != BuildType.OUTPUT && type != BuildType.OUTPUT_LIST) {
- continue;
- }
- ImmutableList.Builder<Artifact> artifactsBuilder = ImmutableList.builder();
- for (OutputFile outputFile : ruleContext.getRule().getOutputFileMap().get(attrName)) {
- Artifact artifact = ruleContext.createOutputArtifact(outputFile);
- artifactsBuilder.add(artifact);
- artifactLabelMapBuilder.put(artifact, outputFile.getLabel());
+ if (implicitOutputsFunction instanceof SkylarkImplicitOutputsFunction) {
+ SkylarkImplicitOutputsFunction func =
+ (SkylarkImplicitOutputsFunction)
+ ruleContext.getRule().getRuleClassObject().getImplicitOutputsFunction();
+ for (Map.Entry<String, String> entry :
+ func.calculateOutputs(RawAttributeMapper.of(ruleContext.getRule())).entrySet()) {
+ addOutput(
+ outputsBuilder,
+ entry.getKey(),
+ ruleContext.getImplicitOutputArtifact(entry.getValue()));
+ }
}
- ImmutableList<Artifact> artifacts = artifactsBuilder.build();
- if (type == BuildType.OUTPUT) {
- if (artifacts.size() == 1) {
- addOutput(outputsBuilder, attrName, Iterables.getOnlyElement(artifacts));
+ Builder<Artifact, Label> artifactLabelMapBuilder = ImmutableMap.builder();
+ for (Attribute a : attributes) {
+ String attrName = a.getName();
+ Type<?> type = a.getType();
+ if (type != BuildType.OUTPUT && type != BuildType.OUTPUT_LIST) {
+ continue;
+ }
+ ImmutableList.Builder<Artifact> artifactsBuilder = ImmutableList.builder();
+ for (OutputFile outputFile : ruleContext.getRule().getOutputFileMap().get(attrName)) {
+ Artifact artifact = ruleContext.createOutputArtifact(outputFile);
+ artifactsBuilder.add(artifact);
+ artifactLabelMapBuilder.put(artifact, outputFile.getLabel());
+ }
+ ImmutableList<Artifact> artifacts = artifactsBuilder.build();
+
+ if (type == BuildType.OUTPUT) {
+ if (artifacts.size() == 1) {
+ addOutput(outputsBuilder, attrName, Iterables.getOnlyElement(artifacts));
+ } else {
+ addOutput(outputsBuilder, attrName, Runtime.NONE);
+ }
+ } else if (type == BuildType.OUTPUT_LIST) {
+ addOutput(outputsBuilder, attrName, new MutableList(artifacts));
} else {
- addOutput(outputsBuilder, attrName, Runtime.NONE);
+ throw new IllegalArgumentException(
+ "Type of " + attrName + "(" + type + ") is not output type ");
}
- } else if (type == BuildType.OUTPUT_LIST) {
- addOutput(outputsBuilder, attrName, new MutableList(artifacts));
- } else {
- throw new IllegalArgumentException(
- "Type of " + attrName + "(" + type + ") is not output type ");
}
+
+ this.artifactsLabelMap = artifactLabelMapBuilder.build();
+ this.outputsObject =
+ new SkylarkClassObject(
+ outputsBuilder,
+ "No attribute '%s' in outputs. Make sure you declared a rule output with this name.");
+
+ this.attributesCollection =
+ buildAttributesCollection(
+ attributes, ruleContext, attributeValueExtractorForRule(ruleContext));
+ this.ruleAttributesCollection = null;
+ } else { // ASPECT
+ this.artifactsLabelMap = ImmutableMap.of();
+ this.outputsObject = null;
+ this.attributesCollection =
+ buildAttributesCollection(
+ ruleContext.getAspectAttributes().values(),
+ ruleContext,
+ ATTRIBUTE_VALUE_EXTRACTOR_FOR_ASPECT);
+ this.ruleAttributesCollection =
+ buildAttributesCollection(
+ ruleContext.getRule().getAttributes(),
+ ruleContext,
+ attributeValueExtractorForRule(ruleContext));
}
- artifactLabelMap = artifactLabelMapBuilder.build();
- outputsObject =
- new SkylarkClassObject(
- outputsBuilder,
- "No attribute '%s' in outputs. Make sure you declared a rule output with this name.");
-
- ImmutableMap.Builder<String, Object> attrBuilder = new ImmutableMap.Builder<>();
- ImmutableMap.Builder<String, Object> executableBuilder = new ImmutableMap.Builder<>();
- ImmutableMap.Builder<Artifact, FilesToRunProvider> executableRunfilesbuilder =
- new ImmutableMap.Builder<>();
- ImmutableMap.Builder<String, Object> fileBuilder = new ImmutableMap.Builder<>();
- ImmutableMap.Builder<String, Object> filesBuilder = new ImmutableMap.Builder<>();
- for (Attribute a : ruleContext.getRule().getAttributes()) {
+
+ makeVariables = ruleContext.getConfigurationMakeVariableContext().collectMakeVariables();
+ }
+
+ private Function<Attribute, Object> attributeValueExtractorForRule(
+ final RuleContext ruleContext) {
+ return new Function<Attribute, Object>() {
+ @Nullable
+ @Override
+ public Object apply(Attribute attribute) {
+ return ruleContext.attributes().get(attribute.getName(), attribute.getType());
+ }
+ };
+ }
+
+ private static SkylarkRuleAttributesCollection buildAttributesCollection(
+ Collection<Attribute> attributes,
+ RuleContext ruleContext,
+ Function<Attribute, Object> attributeValueExtractor) {
+ Builder<String, Object> attrBuilder = new Builder<>();
+ Builder<String, Object> executableBuilder = new Builder<>();
+ Builder<Artifact, FilesToRunProvider> executableRunfilesbuilder = new Builder<>();
+ Builder<String, Object> fileBuilder = new Builder<>();
+ Builder<String, Object> filesBuilder = new Builder<>();
+ for (Attribute a : attributes) {
Type<?> type = a.getType();
- Object val = ruleContext.attributes().get(a.getName(), type);
+ Object val = attributeValueExtractor.apply(a);
if (type != BuildType.LABEL && type != BuildType.LABEL_LIST) {
attrBuilder.put(a.getPublicName(), val == null ? Runtime.NONE
// Attribute values should be type safe
@@ -231,28 +322,77 @@ public final class SkylarkRuleContext {
attrBuilder.put(skyname, new MutableList(allPrereq));
}
}
- attrObject =
- new SkylarkClassObject(
- attrBuilder.build(),
- "No attribute '%s' in attr. Make sure you declared a rule attribute with this name.");
- executableObject =
- new SkylarkClassObject(
- executableBuilder.build(),
- "No attribute '%s' in executable. Make sure there is a label type attribute marked "
- + "as 'executable' with this name");
- fileObject =
- new SkylarkClassObject(
- fileBuilder.build(),
- "No attribute '%s' in file. Make sure there is a label type attribute marked "
- + "as 'single_file' with this name");
- filesObject =
- new SkylarkClassObject(
- filesBuilder.build(),
- "No attribute '%s' in files. Make sure there is a label or label_list type attribute "
- + "with this name");
- executableRunfilesMap = executableRunfilesbuilder.build();
- makeVariables = ruleContext.getConfigurationMakeVariableContext().collectMakeVariables();
+ return new SkylarkRuleAttributesCollection(
+ attrBuilder.build(),
+ executableBuilder.build(),
+ fileBuilder.build(),
+ filesBuilder.build(),
+ executableRunfilesbuilder.build());
+ }
+
+ @SkylarkModule(
+ name = "rule_attributes",
+ doc = "Information about attributes of a rule an aspect is applied to."
+ )
+ private static class SkylarkRuleAttributesCollection {
+ private final SkylarkClassObject attrObject;
+ private final SkylarkClassObject executableObject;
+ private final SkylarkClassObject fileObject;
+ private final SkylarkClassObject filesObject;
+ private final ImmutableMap<Artifact, FilesToRunProvider> executableRunfilesMap;
+
+ private SkylarkRuleAttributesCollection(
+ ImmutableMap<String, Object> attrs,
+ ImmutableMap<String, Object> executables,
+ ImmutableMap<String, Object> singleFiles,
+ ImmutableMap<String, Object> files,
+ ImmutableMap<Artifact, FilesToRunProvider> executableRunfilesMap) {
+ attrObject =
+ new SkylarkClassObject(
+ attrs,
+ "No attribute '%s' in attr. Make sure you declared a rule attribute with this name.");
+ executableObject =
+ new SkylarkClassObject(
+ executables,
+ "No attribute '%s' in executable. Make sure there is a label type attribute marked "
+ + "as 'executable' with this name");
+ fileObject =
+ new SkylarkClassObject(
+ singleFiles,
+ "No attribute '%s' in file. Make sure there is a label type attribute marked "
+ + "as 'single_file' with this name");
+ filesObject =
+ new SkylarkClassObject(
+ files,
+ "No attribute '%s' in files. Make sure there is a label or label_list type attribute "
+ + "with this name");
+ this.executableRunfilesMap = executableRunfilesMap;
+ }
+
+ @SkylarkCallable(name = "attr", structField = true, doc = ATTR_DOC)
+ public SkylarkClassObject getAttr() {
+ return attrObject;
+ }
+
+ @SkylarkCallable(name = "executable", structField = true, doc = EXECUTABLE_DOC)
+ public SkylarkClassObject getExecutable() {
+ return executableObject;
+ }
+
+ @SkylarkCallable(name = "file", structField = true, doc = FILE_DOC)
+ public SkylarkClassObject getFile() {
+ return fileObject;
+ }
+
+ @SkylarkCallable(name = "files", structField = true, doc = FILES_DOC)
+ public SkylarkClassObject getFiles() {
+ return filesObject;
+ }
+
+ public ImmutableMap<Artifact, FilesToRunProvider> getExecutableRunfilesMap() {
+ return executableRunfilesMap;
+ }
}
private void addOutput(HashMap<String, Object> outputsBuilder, String key, Object value)
@@ -270,55 +410,33 @@ public final class SkylarkRuleContext {
return ruleContext;
}
- @SkylarkCallable(name = "attr", structField = true,
- doc = "A struct to access the values of the attributes. The values are provided by "
- + "the user (if not, a default value is used).")
+ @SkylarkCallable(name = "attr", structField = true, doc = ATTR_DOC)
public SkylarkClassObject getAttr() {
- return attrObject;
+ return attributesCollection.getAttr();
}
/**
* <p>See {@link RuleContext#getExecutablePrerequisite(String, Mode)}.
*/
- @SkylarkCallable(name = "executable", structField = true,
- doc = "A <code>struct</code> containing executable files defined in label type "
- + "attributes marked as <code>executable=True</code>. The struct fields correspond "
- + "to the attribute names. Each struct value is always a <code>file</code>s or "
- + "<code>None</code>. If an optional attribute is not specified in the rule "
- + "then the corresponding struct value is <code>None</code>. If a label type is not "
- + "marked as <code>executable=True</code>, no corresponding struct field is generated.")
+ @SkylarkCallable(name = "executable", structField = true, doc = EXECUTABLE_DOC)
public SkylarkClassObject getExecutable() {
- return executableObject;
+ return attributesCollection.getExecutable();
}
/**
* See {@link RuleContext#getPrerequisiteArtifact(String, Mode)}.
*/
- @SkylarkCallable(name = "file", structField = true,
- doc = "A <code>struct</code> containing files defined in label type "
- + "attributes marked as <code>single_file=True</code>. The struct fields correspond "
- + "to the attribute names. The struct value is always a <code>file</code> or "
- + "<code>None</code>. If an optional attribute is not specified in the rule "
- + "then the corresponding struct value is <code>None</code>. If a label type is not "
- + "marked as <code>single_file=True</code>, no corresponding struct field is generated. "
- + "It is a shortcut for:"
- + "<pre class=language-python>list(ctx.attr.<ATTR>.files)[0]</pre>")
+ @SkylarkCallable(name = "file", structField = true, doc = FILE_DOC)
public SkylarkClassObject getFile() {
- return fileObject;
+ return attributesCollection.getFile();
}
/**
* See {@link RuleContext#getPrerequisiteArtifacts(String, Mode)}.
*/
- @SkylarkCallable(name = "files", structField = true,
- doc = "A <code>struct</code> containing files defined in label or label list "
- + "type attributes. The struct fields correspond to the attribute names. The struct "
- + "values are <code>list</code> of <code>file</code>s. If an optional attribute is "
- + "not specified in the rule, an empty list is generated."
- + "It is a shortcut for:"
- + "<pre class=language-python>[f for t in ctx.attr.<ATTR> for f in t.files]</pre>")
+ @SkylarkCallable(name = "files", structField = true, doc = FILES_DOC)
public SkylarkClassObject getFiles() {
- return filesObject;
+ return attributesCollection.getFiles();
}
@SkylarkCallable(name = "workspace_name", structField = true,
@@ -368,23 +486,22 @@ public final class SkylarkRuleContext {
return ruleContext.getHostConfiguration();
}
- @SkylarkCallable(structField = true,
- doc = "A <code>struct</code> containing all the output files."
- + " The struct is generated the following way:<br>"
- + "<ul><li>If the rule is marked as <code>executable=True</code> the struct has an "
- + "\"executable\" field with the rules default executable <code>file</code> value."
- + "<li>For every entry in the rule's <code>outputs</code> dict an attr is generated with "
- + "the same name and the corresponding <code>file</code> value."
- + "<li>For every output type attribute a struct attribute is generated with the "
- + "same name and the corresponding <code>file</code> value or <code>None</code>, "
- + "if no value is specified in the rule."
- + "<li>For every output list type attribute a struct attribute is generated with the "
- + "same name and corresponding <code>list</code> of <code>file</code>s value "
- + "(an empty list if no value is specified in the rule).</ul>")
- public SkylarkClassObject outputs() {
+ @SkylarkCallable(structField = true, doc = OUTPUTS_DOC)
+ public SkylarkClassObject outputs() throws EvalException {
+ if (outputsObject == null) {
+ throw new EvalException(Location.BUILTIN, "'outputs' is not defined");
+ }
return outputsObject;
}
+ @SkylarkCallable(structField = true, doc = "Returns rule attributes descriptor")
+ public SkylarkRuleAttributesCollection rule() throws EvalException {
+ if (ruleAttributesCollection == null) {
+ throw new EvalException(Location.BUILTIN, "'rule' is not defined");
+ }
+ return ruleAttributesCollection;
+ }
+
@SkylarkCallable(structField = true,
doc = "Dictionary (String to String) of configuration variables")
public ImmutableMap<String, String> var() {
@@ -407,16 +524,19 @@ public final class SkylarkRuleContext {
return new MutableList(options); // no env is provided, so it's effectively immutable
}
- @SkylarkCallable(doc =
- "Expands all references to labels embedded within a string for all files using a mapping "
- + "from definition labels (i.e. the label in the output type attribute) to files. Deprecated.",
- documented = false)
- public String expand(@Nullable String expression,
- SkylarkList artifacts, Label labelResolver) throws EvalException, FuncallException {
+ @SkylarkCallable(
+ doc =
+ "Expands all references to labels embedded within a string for all files using a mapping "
+ + "from definition labels (i.e. the label in the output type attribute) to files. "
+ + "Deprecated.",
+ documented = false
+ )
+ public String expand(@Nullable String expression, SkylarkList artifacts, Label labelResolver)
+ throws EvalException, FuncallException {
try {
Map<Label, Iterable<Artifact>> labelMap = new HashMap<>();
for (Artifact artifact : artifacts.getContents(Artifact.class, "artifacts")) {
- labelMap.put(artifactLabelMap.get(artifact), ImmutableList.of(artifact));
+ labelMap.put(artifactsLabelMap.get(artifact), ImmutableList.of(artifact));
}
return LabelExpander.expand(expression, labelMap, labelResolver);
} catch (NotUniqueExpansionException e) {
@@ -510,8 +630,9 @@ public final class SkylarkRuleContext {
});
}
+
FilesToRunProvider getExecutableRunfiles(Artifact executable) {
- return executableRunfilesMap.get(executable);
+ return attributesCollection.getExecutableRunfilesMap().get(executable);
}
@SkylarkCallable(name = "info_file", structField = true, documented = false,
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkAspectFactory.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkAspectFactory.java
index 794d746f1e..c2d489cf86 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkAspectFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkAspectFactory.java
@@ -24,6 +24,7 @@ import com.google.devtools.build.lib.events.Location;
import com.google.devtools.build.lib.packages.AspectParameters;
import com.google.devtools.build.lib.rules.SkylarkRuleClassFunctions.SkylarkAspect;
import com.google.devtools.build.lib.rules.SkylarkRuleContext;
+import com.google.devtools.build.lib.rules.SkylarkRuleContext.Kind;
import com.google.devtools.build.lib.syntax.ClassObject.SkylarkClassObject;
import com.google.devtools.build.lib.syntax.Environment;
import com.google.devtools.build.lib.syntax.EvalException;
@@ -50,7 +51,7 @@ public class SkylarkAspectFactory implements ConfiguredAspectFactory {
try (Mutability mutability = Mutability.create("aspect")) {
SkylarkRuleContext skylarkRuleContext;
try {
- skylarkRuleContext = new SkylarkRuleContext(ruleContext);
+ skylarkRuleContext = new SkylarkRuleContext(ruleContext, Kind.ASPECT);
} catch (EvalException e) {
ruleContext.ruleError(e.getMessage());
return null;