Refactor Skylark rules and attributes in preparation to Skylark aspects.
1. attr.<type> functions return a wrapper object instead of
Attribute.Builder dierctly.
2. RuleClass is created once per the life-time of RuleFunction, during
export
3. Attributes are added to the RuleClass at exporting.
--
MOS_MIGRATED_REVID=108774581
diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java
index 5d87753..f445eb66 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java
@@ -184,11 +184,11 @@
return builder;
}
- private static Attribute.Builder<?> createAttribute(
+ private static Descriptor createAttribute(
Map<String, Object> kwargs, Type<?> type, FuncallExpression ast, Environment env)
throws EvalException {
try {
- return createAttribute(type, kwargs, ast, env);
+ return new Descriptor(createAttribute(type, kwargs, ast, env));
} catch (ConversionException e) {
throw new EvalException(ast.getLocation(), e.getMessage());
}
@@ -198,7 +198,7 @@
name = "int",
doc = "Creates an attribute of type int.",
objectType = SkylarkAttr.class,
- returnType = Attribute.Builder.class,
+ returnType = Descriptor.class,
optionalNamedOnly = {
@Param(
name = DEFAULT_ARG,
@@ -221,7 +221,7 @@
)
private static BuiltinFunction integer =
new BuiltinFunction("int") {
- public Attribute.Builder<?> invoke(
+ public Descriptor invoke(
Integer defaultInt,
Boolean mandatory,
SkylarkList values,
@@ -243,7 +243,7 @@
name = "string",
doc = "Creates an attribute of type string.",
objectType = SkylarkAttr.class,
- returnType = Attribute.Builder.class,
+ returnType = Descriptor.class,
optionalNamedOnly = {
@Param(
name = DEFAULT_ARG,
@@ -266,7 +266,7 @@
)
private static BuiltinFunction string =
new BuiltinFunction("string") {
- public Attribute.Builder<?> invoke(
+ public Descriptor invoke(
String defaultString,
Boolean mandatory,
SkylarkList values,
@@ -291,7 +291,7 @@
+ "If you need a dependency that the user cannot overwrite, make the attribute "
+ "private (starts with <code>_</code>).",
objectType = SkylarkAttr.class,
- returnType = Attribute.Builder.class,
+ returnType = Descriptor.class,
optionalNamedOnly = {
@Param(
name = DEFAULT_ARG,
@@ -349,7 +349,7 @@
)
private static BuiltinFunction label =
new BuiltinFunction("label") {
- public Attribute.Builder<?> invoke(
+ public Descriptor invoke(
Object defaultO,
Boolean executable,
Object allowFiles,
@@ -390,7 +390,7 @@
name = "string_list",
doc = "Creates an attribute of type list of strings",
objectType = SkylarkAttr.class,
- returnType = Attribute.Builder.class,
+ returnType = Descriptor.class,
optionalPositionals = {
@Param(
name = DEFAULT_ARG,
@@ -409,7 +409,7 @@
)
private static BuiltinFunction stringList =
new BuiltinFunction("string_list") {
- public Attribute.Builder<?> invoke(
+ public Descriptor invoke(
SkylarkList defaultList,
Boolean mandatory,
Boolean nonEmpty,
@@ -430,7 +430,7 @@
name = "int_list",
doc = "Creates an attribute of type list of ints",
objectType = SkylarkAttr.class,
- returnType = Attribute.Builder.class,
+ returnType = Descriptor.class,
optionalPositionals = {
@Param(
name = DEFAULT_ARG,
@@ -449,7 +449,7 @@
)
private static BuiltinFunction intList =
new BuiltinFunction("int_list") {
- public Attribute.Builder<?> invoke(
+ public Descriptor invoke(
SkylarkList defaultList,
Boolean mandatory,
Boolean nonEmpty,
@@ -472,7 +472,7 @@
"Creates an attribute of type list of labels. "
+ "See <a href=\"attr.html#label\">label</a> for more information.",
objectType = SkylarkAttr.class,
- returnType = Attribute.Builder.class,
+ returnType = Descriptor.class,
optionalNamedOnly = {
@Param(
name = DEFAULT_ARG,
@@ -529,7 +529,7 @@
)
private static BuiltinFunction labelList =
new BuiltinFunction("label_list") {
- public Attribute.Builder<?> invoke(
+ public Descriptor invoke(
Object defaultList,
Object allowFiles,
Object allowRules,
@@ -570,7 +570,7 @@
name = "bool",
doc = "Creates an attribute of type bool. Its default value is False.",
objectType = SkylarkAttr.class,
- returnType = Attribute.Builder.class,
+ returnType = Descriptor.class,
optionalNamedOnly = {
@Param(name = DEFAULT_ARG, type = Boolean.class, defaultValue = "False", doc = DEFAULT_DOC),
@Param(name = MANDATORY_ARG, type = Boolean.class, defaultValue = "False", doc = MANDATORY_DOC
@@ -581,7 +581,7 @@
)
private static BuiltinFunction bool =
new BuiltinFunction("bool") {
- public Attribute.Builder<?> invoke(
+ public Descriptor invoke(
Boolean defaultO, Boolean mandatory, FuncallExpression ast, Environment env)
throws EvalException {
env.checkLoadingPhase("attr.bool", ast.getLocation());
@@ -600,7 +600,7 @@
+ "The user provides a file name (string) and the rule must create an action that "
+ "generates the file.",
objectType = SkylarkAttr.class,
- returnType = Attribute.Builder.class,
+ returnType = Descriptor.class,
optionalNamedOnly = {
@Param(
name = DEFAULT_ARG,
@@ -617,7 +617,7 @@
)
private static BuiltinFunction output =
new BuiltinFunction("output") {
- public Attribute.Builder<?> invoke(
+ public Descriptor invoke(
Object defaultO, Boolean mandatory, FuncallExpression ast, Environment env)
throws EvalException {
env.checkLoadingPhase("attr.output", ast.getLocation());
@@ -635,7 +635,7 @@
"Creates an attribute of type list of outputs. Its default value is <code>[]</code>. "
+ "See <a href=\"attr.html#output\">output</a> above for more information.",
objectType = SkylarkAttr.class,
- returnType = Attribute.Builder.class,
+ returnType = Descriptor.class,
optionalNamedOnly = {
@Param(
name = DEFAULT_ARG,
@@ -654,7 +654,7 @@
)
private static BuiltinFunction outputList =
new BuiltinFunction("output_list") {
- public Attribute.Builder<?> invoke(
+ public Descriptor invoke(
SkylarkList defaultList,
Boolean mandatory,
Boolean nonEmpty,
@@ -677,7 +677,7 @@
"Creates an attribute of type dictionary, mapping from string to string. "
+ "Its default value is dict().",
objectType = SkylarkAttr.class,
- returnType = Attribute.Builder.class,
+ returnType = Descriptor.class,
optionalNamedOnly = {
@Param(name = DEFAULT_ARG, type = Map.class, defaultValue = "{}", doc = DEFAULT_DOC),
@Param(name = MANDATORY_ARG, type = Boolean.class, defaultValue = "False", doc = MANDATORY_DOC
@@ -690,7 +690,7 @@
)
private static BuiltinFunction stringDict =
new BuiltinFunction("string_dict") {
- public Attribute.Builder<?> invoke(
+ public Descriptor invoke(
Map<?, ?> defaultO,
Boolean mandatory,
Boolean nonEmpty,
@@ -713,7 +713,7 @@
"Creates an attribute of type dictionary, mapping from string to list of string. "
+ "Its default value is dict().",
objectType = SkylarkAttr.class,
- returnType = Attribute.Builder.class,
+ returnType = Descriptor.class,
optionalNamedOnly = {
@Param(name = DEFAULT_ARG, type = Map.class, defaultValue = "{}", doc = DEFAULT_DOC),
@Param(name = MANDATORY_ARG, type = Boolean.class, defaultValue = "False", doc = MANDATORY_DOC
@@ -726,7 +726,7 @@
)
private static BuiltinFunction stringListDict =
new BuiltinFunction("string_list_dict") {
- public Attribute.Builder<?> invoke(
+ public Descriptor invoke(
Map<?, ?> defaultO,
Boolean mandatory,
Boolean nonEmpty,
@@ -748,7 +748,7 @@
doc = "Creates an attribute of type license. Its default value is NO_LICENSE.",
// TODO(bazel-team): Implement proper license support for Skylark.
objectType = SkylarkAttr.class,
- returnType = Attribute.Builder.class,
+ returnType = Descriptor.class,
optionalNamedOnly = {
// TODO(bazel-team): ensure this is the correct default value
@Param(name = DEFAULT_ARG, defaultValue = "None", noneable = true, doc = DEFAULT_DOC),
@@ -760,7 +760,7 @@
)
private static BuiltinFunction license =
new BuiltinFunction("license") {
- public Attribute.Builder<?> invoke(
+ public Descriptor invoke(
Object defaultO, Boolean mandatory, FuncallExpression ast, Environment env)
throws EvalException {
env.checkLoadingPhase("attr.license", ast.getLocation());
@@ -772,6 +772,21 @@
}
};
+ /**
+ * A descriptor of an attribute defined in Skylark.
+ */
+ public static final class Descriptor {
+ private final Attribute.Builder<?> attributeBuilder;
+
+ public Descriptor(Attribute.Builder<?> attributeBuilder) {
+ this.attributeBuilder = attributeBuilder;
+ }
+
+ public Attribute.Builder<?> getAttributeBuilder() {
+ return attributeBuilder;
+ }
+ }
+
static {
SkylarkSignatureProcessor.configureSkylarkFunctions(SkylarkAttr.class);
}
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 ed58924..63b6307 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
@@ -84,6 +84,7 @@
import com.google.devtools.build.lib.syntax.SkylarkValue;
import com.google.devtools.build.lib.syntax.Type;
import com.google.devtools.build.lib.syntax.Type.ConversionException;
+import com.google.devtools.build.lib.util.Pair;
import java.util.HashMap;
import java.util.Map;
@@ -264,18 +265,22 @@
// 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, Attribute.Builder> attr :
- castMap(attrs, String.class, Attribute.Builder.class, "attrs").entrySet()) {
- Attribute.Builder<?> attrBuilder = (Attribute.Builder<?>) attr.getValue();
+ 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(), attrBuilder.hasLateBoundValue());
- addAttribute(builder, attrBuilder.build(attrName));
+ attributeToNative(attr.getKey(), ast.getLocation(),
+ attrDescriptor.getAttributeBuilder().hasLateBoundValue());
+ attributes.add(Pair.of(attrName, attrDescriptor));
}
}
if (executable || test) {
addAttribute(
+ ast.getLocation(),
builder,
attr("$is_executable", BOOLEAN)
.value(true)
@@ -307,16 +312,9 @@
registerRequiredFragments(fragments, hostFragments, builder);
builder.setConfiguredTargetFunction(implementation);
builder.setRuleDefinitionEnvironment(funcallEnv);
- return new RuleFunction(builder, type);
+ return new RuleFunction(builder, type, attributes.build(), ast.getLocation());
}
- private void addAttribute(RuleClass.Builder builder, Attribute attribute) throws EvalException {
- try {
- builder.addOrOverrideAttribute(attribute);
- } catch (IllegalArgumentException ex) {
- throw new EvalException(location, ex);
- }
- }
private void registerRequiredFragments(
SkylarkList fragments, SkylarkList hostFragments, RuleClass.Builder builder)
@@ -336,6 +334,16 @@
}
};
+ private static void addAttribute(
+ Location location, RuleClass.Builder builder, Attribute attribute) throws EvalException {
+ try {
+ builder.addOrOverrideAttribute(attribute);
+ } catch (IllegalArgumentException ex) {
+ throw new EvalException(location, ex);
+ }
+ }
+
+
@SkylarkSignature(
name = "aspect",
returnType = SkylarkAspect.class,
@@ -367,17 +375,22 @@
/** The implementation for the magic function "rule" that creates Skylark rule classes */
public static final class RuleFunction extends BaseFunction {
- // Note that this means that we can reuse the same builder.
- // This is fine since we don't modify the builder from here.
- private final RuleClass.Builder builder;
- private final RuleClassType type;
- private Label skylarkLabel;
- private String ruleClassName;
+ private RuleClass.Builder builder;
- public RuleFunction(Builder builder, RuleClassType type) {
+ private RuleClass ruleClass;
+ private final RuleClassType type;
+ private ImmutableList<Pair<String, SkylarkAttr.Descriptor>> attributes;
+ private final Location definitionLocation;
+ private Label skylarkLabel;
+
+ public RuleFunction(Builder builder, RuleClassType type,
+ ImmutableList<Pair<String, SkylarkAttr.Descriptor>> attributes,
+ Location definitionLocation) {
super("rule", FunctionSignature.KWARGS);
this.builder = builder;
this.type = type;
+ this.attributes = attributes;
+ this.definitionLocation = definitionLocation;
}
@Override
@@ -387,15 +400,10 @@
throws EvalException, InterruptedException, ConversionException {
env.checkLoadingPhase(getName(), ast.getLocation());
try {
- if (ruleClassName == null || skylarkLabel == null) {
+ if (ruleClass == null) {
throw new EvalException(ast.getLocation(),
"Invalid rule class hasn't been exported by a Skylark file");
}
- if (type == RuleClassType.TEST != TargetUtils.isTestRuleName(ruleClassName)) {
- throw new EvalException(ast.getLocation(), "Invalid rule class name '" + ruleClassName
- + "', test rule class names must end with '_test' and other rule classes must not");
- }
- RuleClass ruleClass = builder.build(ruleClassName);
PackageContext pkgContext = (PackageContext) env.lookup(PackageFactory.PKG_CONTEXT);
return RuleFactory.createAndAddRule(
pkgContext, ruleClass, (Map<String, Object>) args[0], ast, env);
@@ -407,18 +415,32 @@
/**
* Export a RuleFunction from a Skylark file with a given name.
*/
- void export(Label skylarkLabel, String ruleClassName) {
+ void export(Label skylarkLabel, String ruleClassName) throws EvalException {
+ Preconditions.checkState(ruleClass == null && builder != null);
this.skylarkLabel = skylarkLabel;
- this.ruleClassName = ruleClassName;
+ if (type == RuleClassType.TEST != TargetUtils.isTestRuleName(ruleClassName)) {
+ throw new EvalException(definitionLocation, "Invalid rule class name '" + ruleClassName
+ + "', test rule class names must end with '_test' and other rule classes must not");
+ }
+ for (Pair<String, SkylarkAttr.Descriptor> attribute : attributes) {
+ addAttribute(definitionLocation, builder,
+ attribute.getSecond().getAttributeBuilder().build(attribute.getFirst()));
+ }
+ this.ruleClass = builder.build(ruleClassName);
+
+ this.builder = null;
+ this.attributes = null;
}
@VisibleForTesting
- public RuleClass.Builder getBuilder() {
- return builder;
+ public RuleClass getRuleClass() {
+ Preconditions.checkState(ruleClass != null && builder == null);
+ return ruleClass;
}
}
- public static void exportRuleFunctionsAndAspects(Environment env, Label skylarkLabel) {
+ public static void exportRuleFunctionsAndAspects(Environment env, Label skylarkLabel)
+ throws EvalException {
for (String name : env.getGlobals().getDirectVariableNames()) {
try {
Object value = env.lookup(name);
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupFunction.java
index 9baa935..5eed6a6 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupFunction.java
@@ -34,6 +34,7 @@
import com.google.devtools.build.lib.rules.SkylarkRuleClassFunctions;
import com.google.devtools.build.lib.syntax.BuildFileAST;
import com.google.devtools.build.lib.syntax.Environment.Extension;
+import com.google.devtools.build.lib.syntax.EvalException;
import com.google.devtools.build.lib.syntax.LoadStatement;
import com.google.devtools.build.lib.syntax.Mutability;
import com.google.devtools.build.lib.vfs.PathFragment;
@@ -376,7 +377,11 @@
mutability, eventHandler, ast.getContentHashCode(), importMap)
.setupOverride("native", packageFactory.getNativeModule());
ast.exec(extensionEnv, eventHandler);
- SkylarkRuleClassFunctions.exportRuleFunctionsAndAspects(extensionEnv, extensionLabel);
+ try {
+ SkylarkRuleClassFunctions.exportRuleFunctionsAndAspects(extensionEnv, extensionLabel);
+ } catch (EvalException e) {
+ eventHandler.handle(Event.error(e.getLocation(), e.getMessage()));
+ }
Event.replayEventsOn(env.getListener(), eventHandler.getEvents());
if (eventHandler.hasErrors()) {
diff --git a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleClassFunctionsTest.java b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleClassFunctionsTest.java
index 23458c7..bca93ac 100644
--- a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleClassFunctionsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleClassFunctionsTest.java
@@ -28,12 +28,16 @@
import com.google.devtools.build.lib.packages.PredicateWithMessage;
import com.google.devtools.build.lib.packages.RuleClass;
import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType;
+import com.google.devtools.build.lib.rules.SkylarkAttr;
import com.google.devtools.build.lib.rules.SkylarkFileType;
+import com.google.devtools.build.lib.rules.SkylarkRuleClassFunctions;
import com.google.devtools.build.lib.rules.SkylarkRuleClassFunctions.RuleFunction;
import com.google.devtools.build.lib.skylark.util.SkylarkTestCase;
+import com.google.devtools.build.lib.syntax.EvalException;
import com.google.devtools.build.lib.syntax.Type;
import com.google.devtools.build.lib.util.FileTypeSet;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -71,15 +75,22 @@
@Test
public void testCannotOverrideBuiltInAttribute() throws Exception {
- checkEvalError(
- "There is already a built-in attribute 'tags' which cannot be overridden",
- "def impl(ctx): return",
- "r = rule(impl, attrs = {'tags': attr.string_list()})");
+ ev.setFailFast(true);
+ try {
+ evalAndExport(
+ "def impl(ctx): return", "r = rule(impl, attrs = {'tags': attr.string_list()})");
+ Assert.fail("Expected error '"
+ + "There is already a built-in attribute 'tags' which cannot be overridden"
+ + "' but got no error");
+ } catch (IllegalArgumentException | EvalException e) {
+ assertThat(e).hasMessage(
+ "There is already a built-in attribute 'tags' which cannot be overridden");
+ }
}
@Test
public void testImplicitArgsAttribute() throws Exception {
- eval(
+ evalAndExport(
"def _impl(ctx):",
" pass",
"exec_rule = rule(implementation = _impl, executable = True)",
@@ -89,59 +100,57 @@
}
private RuleClass getRuleClass(String name) throws Exception {
- return ((RuleFunction) lookup(name)).getBuilder().build(name);
+ return ((RuleFunction) lookup(name)).getRuleClass();
}
private void registerDummyUserDefinedFunction() throws Exception {
eval("def impl():\n" + " return 0\n");
}
+ private Attribute.Builder<?> evalAttributeDefinition(String... lines) throws Exception {
+ return ((SkylarkAttr.Descriptor) evalRuleClassCode(lines)).getAttributeBuilder();
+ }
+
@Test
public void testAttrWithOnlyType() throws Exception {
- Object result = evalRuleClassCode("attr.string_list()");
- Attribute attr = ((Attribute.Builder<?>) result).build("a1");
+ Attribute attr = evalAttributeDefinition("attr.string_list()").build("a1");
assertEquals(Type.STRING_LIST, attr.getType());
}
@Test
public void testOutputListAttr() throws Exception {
- Object result = evalRuleClassCode("attr.output_list()");
- Attribute attr = ((Attribute.Builder<?>) result).build("a1");
+ Attribute attr = evalAttributeDefinition("attr.output_list()").build("a1");
assertEquals(BuildType.OUTPUT_LIST, attr.getType());
}
@Test
public void testIntListAttr() throws Exception {
- Object result = evalRuleClassCode("attr.int_list()");
- Attribute attr = ((Attribute.Builder<?>) result).build("a1");
+ Attribute attr = evalAttributeDefinition("attr.int_list()").build("a1");
assertEquals(Type.INTEGER_LIST, attr.getType());
}
@Test
public void testOutputAttr() throws Exception {
- Object result = evalRuleClassCode("attr.output()");
- Attribute attr = ((Attribute.Builder<?>) result).build("a1");
+ Attribute attr = evalAttributeDefinition("attr.output()").build("a1");
assertEquals(BuildType.OUTPUT, attr.getType());
}
@Test
public void testStringDictAttr() throws Exception {
- Object result = evalRuleClassCode("attr.string_dict(default = {'a': 'b'})");
- Attribute attr = ((Attribute.Builder<?>) result).build("a1");
+ Attribute attr = evalAttributeDefinition("attr.string_dict(default = {'a': 'b'})").build("a1");
assertEquals(Type.STRING_DICT, attr.getType());
}
@Test
public void testStringListDictAttr() throws Exception {
- Object result = evalRuleClassCode("attr.string_list_dict(default = {'a': ['b', 'c']})");
- Attribute attr = ((Attribute.Builder<?>) result).build("a1");
+ Attribute attr = evalAttributeDefinition("attr.string_list_dict(default = {'a': ['b', 'c']})")
+ .build("a1");
assertEquals(Type.STRING_LIST_DICT, attr.getType());
}
@Test
public void testAttrAllowedFileTypesAnyFile() throws Exception {
- Object result = evalRuleClassCode("attr.label_list(allow_files = True)");
- Attribute attr = ((Attribute.Builder<?>) result).build("a1");
+ Attribute attr = evalAttributeDefinition("attr.label_list(allow_files = True)").build("a1");
assertEquals(FileTypeSet.ANY_FILE, attr.getAllowedFileTypesPredicate());
}
@@ -154,17 +163,17 @@
@Test
public void testAttrWithSkylarkFileType() throws Exception {
- Object result = evalRuleClassCode("attr.label_list(allow_files = FileType(['.xml']))");
- Attribute attr = ((Attribute.Builder<?>) result).build("a1");
+ Attribute attr = evalAttributeDefinition("attr.label_list(allow_files = FileType(['.xml']))")
+ .build("a1");
assertTrue(attr.getAllowedFileTypesPredicate().apply("a.xml"));
assertFalse(attr.getAllowedFileTypesPredicate().apply("a.txt"));
}
@Test
public void testAttrWithProviders() throws Exception {
- Object result =
- evalRuleClassCode("attr.label_list(allow_files = True, providers = ['a', 'b'])");
- Attribute attr = ((Attribute.Builder<?>) result).build("a1");
+ Attribute attr =
+ evalAttributeDefinition("attr.label_list(allow_files = True, providers = ['a', 'b'])")
+ .build("a1");
assertEquals(ImmutableSet.of("a", "b"), attr.getMandatoryProviders());
}
@@ -191,16 +200,14 @@
}
@Test
public void testAttrAllowedRuleClassesSpecificRuleClasses() throws Exception {
- Object result =
- evalRuleClassCode("attr.label_list(allow_rules = ['java_binary'], allow_files = True)");
- Attribute attr = ((Attribute.Builder<?>) result).build("a");
+ Attribute attr = evalAttributeDefinition(
+ "attr.label_list(allow_rules = ['java_binary'], allow_files = True)").build("a");
assertTrue(attr.getAllowedRuleClassesPredicate().apply(ruleClass("java_binary")));
assertFalse(attr.getAllowedRuleClassesPredicate().apply(ruleClass("genrule")));
}
@Test
public void testAttrDefaultValue() throws Exception {
- Object result = evalRuleClassCode("attr.string(default = 'some value')");
- Attribute attr = ((Attribute.Builder<?>) result).build("a1");
+ Attribute attr = evalAttributeDefinition("attr.string(default = 'some value')").build("a1");
assertEquals("some value", attr.getDefaultValueForTesting());
}
@@ -215,16 +222,14 @@
@Test
public void testAttrMandatory() throws Exception {
- Object result = evalRuleClassCode("attr.string(mandatory=True)");
- Attribute attr = ((Attribute.Builder<?>) result).build("a1");
+ Attribute attr = evalAttributeDefinition("attr.string(mandatory=True)").build("a1");
assertTrue(attr.isMandatory());
assertFalse(attr.isNonEmpty());
}
@Test
public void testAttrNonEmpty() throws Exception {
- Object result = evalRuleClassCode("attr.string_list(non_empty=True)");
- Attribute attr = ((Attribute.Builder<?>) result).build("a1");
+ Attribute attr = evalAttributeDefinition("attr.string_list(non_empty=True)").build("a1");
assertTrue(attr.isNonEmpty());
assertFalse(attr.isMandatory());
}
@@ -237,15 +242,14 @@
@Test
public void testAttrCfg() throws Exception {
- Object result = evalRuleClassCode("attr.label(cfg = HOST_CFG, allow_files = True)");
- Attribute attr = ((Attribute.Builder<?>) result).build("a1");
+ Attribute attr = evalAttributeDefinition("attr.label(cfg = HOST_CFG, allow_files = True)")
+ .build("a1");
assertEquals(ConfigurationTransition.HOST, attr.getConfigurationTransition());
}
@Test
public void testAttrValues() throws Exception {
- Object result = evalRuleClassCode("attr.string(values = ['ab', 'cd'])");
- Attribute attr = ((Attribute.Builder<?>) result).build("a1");
+ Attribute attr = evalAttributeDefinition("attr.string(values = ['ab', 'cd'])").build("a1");
PredicateWithMessage<Object> predicate = attr.getAllowedValues();
assertThat(predicate.apply("ab")).isTrue();
assertThat(predicate.apply("xy")).isFalse();
@@ -253,8 +257,7 @@
@Test
public void testAttrIntValues() throws Exception {
- Object result = evalRuleClassCode("attr.int(values = [1, 2])");
- Attribute attr = ((Attribute.Builder<?>) result).build("a1");
+ Attribute attr = evalAttributeDefinition("attr.int(values = [1, 2])").build("a1");
PredicateWithMessage<Object> predicate = attr.getAllowedValues();
assertThat(predicate.apply(2)).isTrue();
assertThat(predicate.apply(3)).isFalse();
@@ -262,8 +265,8 @@
@Test
public void testRuleImplementation() throws Exception {
- eval("def impl(ctx): return None", "rule1 = rule(impl)");
- RuleClass c = ((RuleFunction) lookup("rule1")).getBuilder().build("rule1");
+ evalAndExport("def impl(ctx): return None", "rule1 = rule(impl)");
+ RuleClass c = ((RuleFunction) lookup("rule1")).getRuleClass();
assertEquals("impl", c.getConfiguredTargetFunction().getName());
}
@@ -277,46 +280,55 @@
"attr.string(default=attr_value)");
}
+ private static final Label FAKE_LABEL = Label.parseAbsoluteUnchecked("//fake/label.bzl");
+
@Test
public void testRuleAddAttribute() throws Exception {
- eval("def impl(ctx): return None", "r1 = rule(impl, attrs={'a1': attr.string()})");
- RuleClass c = ((RuleFunction) lookup("r1")).getBuilder().build("r1");
+ evalAndExport("def impl(ctx): return None", "r1 = rule(impl, attrs={'a1': attr.string()})");
+ RuleClass c = ((RuleFunction) lookup("r1")).getRuleClass();
assertTrue(c.hasAttr("a1", Type.STRING));
}
+ protected void evalAndExport(String... lines) throws Exception {
+ eval(lines);
+ SkylarkRuleClassFunctions.exportRuleFunctionsAndAspects(ev.getEnvironment(), FAKE_LABEL);
+ }
+
@Test
public void testOutputToGenfiles() throws Exception {
- eval("def impl(ctx): pass", "r1 = rule(impl, output_to_genfiles=True)");
- RuleClass c = ((RuleFunction) lookup("r1")).getBuilder().build("r1");
+ evalAndExport("def impl(ctx): pass", "r1 = rule(impl, output_to_genfiles=True)");
+ RuleClass c = ((RuleFunction) lookup("r1")).getRuleClass();
assertFalse(c.hasBinaryOutput());
}
@Test
public void testRuleAddMultipleAttributes() throws Exception {
- eval(
+ evalAndExport(
"def impl(ctx): return None",
"r1 = rule(impl,",
" attrs = {",
" 'a1': attr.label_list(allow_files=True),",
" 'a2': attr.int()",
"})");
- RuleClass c = ((RuleFunction) lookup("r1")).getBuilder().build("r1");
+ RuleClass c = ((RuleFunction) lookup("r1")).getRuleClass();
assertTrue(c.hasAttr("a1", BuildType.LABEL_LIST));
assertTrue(c.hasAttr("a2", Type.INTEGER));
}
@Test
public void testRuleAttributeFlag() throws Exception {
- eval(
+ evalAndExport(
"def impl(ctx): return None",
"r1 = rule(impl, attrs = {'a1': attr.string(mandatory=True)})");
- RuleClass c = ((RuleFunction) lookup("r1")).getBuilder().build("r1");
+ RuleClass c = ((RuleFunction) lookup("r1")).getRuleClass();
assertTrue(c.getAttributeByName("a1").isMandatory());
}
@Test
public void testRuleOutputs() throws Exception {
- eval("def impl(ctx): return None", "r1 = rule(impl, outputs = {'a': 'a.txt'})");
- RuleClass c = ((RuleFunction) lookup("r1")).getBuilder().build("r1");
+ evalAndExport(
+ "def impl(ctx): return None",
+ "r1 = rule(impl, outputs = {'a': 'a.txt'})");
+ RuleClass c = ((RuleFunction) lookup("r1")).getRuleClass();
ImplicitOutputsFunction function = c.getImplicitOutputsFunction();
assertEquals("a.txt", Iterables.getOnlyElement(function.getImplicitOutputs(null)));
}
@@ -351,14 +363,15 @@
registerDummyUserDefinedFunction();
checkErrorContains(
"Illegal argument: "
- + "expected <String, Builder> type for 'attrs' but got <string, string> instead",
+ + "expected <String, Descriptor> type for 'attrs' but got <string, string> instead",
"rule(impl, attrs = {'a1': 'some text'})");
}
@Test
public void testLabel() throws Exception {
Object result = evalRuleClassCode("Label('//foo/foo:foo')");
- assertEquals("//foo/foo:foo", ((Label) result).toString());
+ assertThat(result).isInstanceOf(Label.class);
+ assertEquals("//foo/foo:foo", result.toString());
}
@Test
@@ -380,19 +393,22 @@
@Test
public void testRuleLabelDefaultValue() throws Exception {
- eval(
+ evalAndExport(
"def impl(ctx): return None\n"
+ "r1 = rule(impl, attrs = {'a1': "
+ "attr.label(default = Label('//foo:foo'), allow_files=True)})");
- RuleClass c = ((RuleFunction) lookup("r1")).getBuilder().build("r1");
+ RuleClass c = ((RuleFunction) lookup("r1")).getRuleClass();
Attribute a = c.getAttributeByName("a1");
- assertEquals("//foo:foo", ((Label) a.getDefaultValueForTesting()).toString());
+ assertThat(a.getDefaultValueForTesting()).isInstanceOf(Label.class);
+ assertEquals("//foo:foo", a.getDefaultValueForTesting().toString());
}
@Test
public void testIntDefaultValue() throws Exception {
- eval("def impl(ctx): return None", "r1 = rule(impl, attrs = {'a1': attr.int(default = 40+2)})");
- RuleClass c = ((RuleFunction) lookup("r1")).getBuilder().build("r1");
+ evalAndExport(
+ "def impl(ctx): return None",
+ "r1 = rule(impl, attrs = {'a1': attr.int(default = 40+2)})");
+ RuleClass c = ((RuleFunction) lookup("r1")).getRuleClass();
Attribute a = c.getAttributeByName("a1");
assertEquals(42, a.getDefaultValueForTesting());
}
@@ -406,8 +422,8 @@
@Test
public void testRuleInheritsBaseRuleAttributes() throws Exception {
- eval("def impl(ctx): return None", "r1 = rule(impl)");
- RuleClass c = ((RuleFunction) lookup("r1")).getBuilder().build("r1");
+ evalAndExport("def impl(ctx): return None", "r1 = rule(impl)");
+ RuleClass c = ((RuleFunction) lookup("r1")).getRuleClass();
assertTrue(c.hasAttr("tags", Type.STRING_LIST));
assertTrue(c.hasAttr("visibility", BuildType.NODEP_LABEL_LIST));
assertTrue(c.hasAttr("deprecation", Type.STRING));
diff --git a/tools/build_rules/py_rules.bzl b/tools/build_rules/py_rules.bzl
index 5ce8a58..d735b39 100644
--- a/tools/build_rules/py_rules.bzl
+++ b/tools/build_rules/py_rules.bzl
@@ -115,6 +115,7 @@
py_test = rule(
py_binary_impl,
+ test = True,
executable = True,
attrs = py_attrs,
outputs = py_binary_outputs)