aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com
diff options
context:
space:
mode:
authorGravatar Dmitry Lomov <dslomov@google.com>2016-08-10 18:29:04 +0000
committerGravatar Yue Gan <yueg@google.com>2016-08-11 09:14:21 +0000
commit8a07a959e10040642cdf2e6fa21edcd6a1e9212b (patch)
treee0109611dcfe55906c217b86aa411d7a74670185 /src/main/java/com
parentf2f8408c4fa242ceb9352b49e50eb09863285dca (diff)
Add 'provider()' function.
-- MOS_MIGRATED_REVID=129889793
Diffstat (limited to 'src/main/java/com')
-rw-r--r--src/main/java/com/google/devtools/build/lib/packages/SkylarkAspect.java6
-rw-r--r--src/main/java/com/google/devtools/build/lib/packages/SkylarkClassObject.java12
-rw-r--r--src/main/java/com/google/devtools/build/lib/packages/SkylarkClassObjectConstructor.java82
-rw-r--r--src/main/java/com/google/devtools/build/lib/packages/SkylarkExportable.java38
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java73
5 files changed, 177 insertions, 34 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/packages/SkylarkAspect.java b/src/main/java/com/google/devtools/build/lib/packages/SkylarkAspect.java
index 1a785092b4..be74013e5e 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/SkylarkAspect.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/SkylarkAspect.java
@@ -20,14 +20,12 @@ import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory;
-import com.google.devtools.build.lib.skylarkinterface.SkylarkValue;
import com.google.devtools.build.lib.syntax.BaseFunction;
import com.google.devtools.build.lib.syntax.Environment;
import com.google.devtools.build.lib.syntax.Printer;
import com.google.devtools.build.lib.syntax.Type;
import com.google.devtools.build.lib.util.Pair;
import com.google.devtools.build.lib.util.Preconditions;
-
import javax.annotation.Nullable;
/** A Skylark value that is a result of an 'aspect(..)' function call. */
@@ -39,7 +37,7 @@ import javax.annotation.Nullable;
+ "documentation of the aspect function</a> or the "
+ "<a href=\"../aspects.md\">introduction to Aspects</a>."
)
-public class SkylarkAspect implements SkylarkValue {
+public class SkylarkAspect implements SkylarkExportable {
private final BaseFunction implementation;
private final ImmutableList<String> attributeAspects;
private final ImmutableList<Attribute> attributes;
@@ -107,6 +105,7 @@ public class SkylarkAspect implements SkylarkValue {
return paramAttributes;
}
+ @Override
public void export(Label extensionLabel, String name) {
Preconditions.checkArgument(!isExported());
this.aspectClass = new SkylarkAspectClass(extensionLabel, name);
@@ -137,6 +136,7 @@ public class SkylarkAspect implements SkylarkValue {
return builder.build();
}
+ @Override
public boolean isExported() {
return aspectClass != null;
}
diff --git a/src/main/java/com/google/devtools/build/lib/packages/SkylarkClassObject.java b/src/main/java/com/google/devtools/build/lib/packages/SkylarkClassObject.java
index f5190965b7..5e5183cbc6 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/SkylarkClassObject.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/SkylarkClassObject.java
@@ -124,6 +124,10 @@ public class SkylarkClassObject implements ClassObject, SkylarkValue, Concatable
return StructConcatter.INSTANCE;
}
+ public SkylarkClassObjectConstructor getConstructor() {
+ return constructor;
+ }
+
private static class StructConcatter implements Concatter {
private static final StructConcatter INSTANCE = new StructConcatter();
@@ -134,6 +138,12 @@ public class SkylarkClassObject implements ClassObject, SkylarkValue, Concatable
Concatable left, Concatable right, Location loc) throws EvalException {
SkylarkClassObject lval = (SkylarkClassObject) left;
SkylarkClassObject rval = (SkylarkClassObject) right;
+ if (!lval.constructor.equals(rval.constructor)) {
+ throw new EvalException(loc,
+ String.format("Cannot concat %s with %s",
+ lval.constructor.getPrintableName(),
+ rval.constructor.getPrintableName()));
+ }
SetView<String> commonFields = Sets
.intersection(lval.values.keySet(), rval.values.keySet());
if (!commonFields.isEmpty()) {
@@ -174,7 +184,7 @@ public class SkylarkClassObject implements ClassObject, SkylarkValue, Concatable
@Override
public void write(Appendable buffer, char quotationMark) {
boolean first = true;
- Printer.append(buffer, constructor.getName());
+ Printer.append(buffer, constructor.getPrintableName());
Printer.append(buffer, "(");
// Sort by key to ensure deterministic output.
for (String key : Ordering.natural().sortedCopy(values.keySet())) {
diff --git a/src/main/java/com/google/devtools/build/lib/packages/SkylarkClassObjectConstructor.java b/src/main/java/com/google/devtools/build/lib/packages/SkylarkClassObjectConstructor.java
index b451efe583..d090c950a3 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/SkylarkClassObjectConstructor.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/SkylarkClassObjectConstructor.java
@@ -14,22 +14,29 @@
package com.google.devtools.build.lib.packages;
+import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.events.Location;
+import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
import com.google.devtools.build.lib.syntax.BaseFunction;
import com.google.devtools.build.lib.syntax.Environment;
import com.google.devtools.build.lib.syntax.EvalException;
import com.google.devtools.build.lib.syntax.FuncallExpression;
import com.google.devtools.build.lib.syntax.FunctionSignature;
-import com.google.devtools.build.lib.syntax.FunctionSignature.WithValues;
import com.google.devtools.build.lib.syntax.SkylarkType;
-import com.google.devtools.build.lib.syntax.Type.ConversionException;
+import com.google.devtools.build.lib.util.Preconditions;
import java.util.Map;
+import java.util.Objects;
import javax.annotation.Nullable;
/**
* A constructor for {@link SkylarkClassObject}.
*/
-public final class SkylarkClassObjectConstructor extends BaseFunction {
+@SkylarkModule(name = "provider",
+ doc = "A constructor for simple value objects. "
+ + "See the global <a href=\"globals.html#provider\">provider</a> function "
+ + "for more details."
+)
+public final class SkylarkClassObjectConstructor extends BaseFunction implements SkylarkExportable {
/**
* "struct" function.
*/
@@ -38,7 +45,10 @@ public final class SkylarkClassObjectConstructor extends BaseFunction {
private static final FunctionSignature.WithValues<Object, SkylarkType> SIGNATURE =
- WithValues.create(FunctionSignature.KWARGS);
+ FunctionSignature.WithValues.create(FunctionSignature.KWARGS);
+
+ @Nullable
+ private Key key;
public SkylarkClassObjectConstructor(String name, Location location) {
super(name, SIGNATURE, location);
@@ -50,7 +60,7 @@ public final class SkylarkClassObjectConstructor extends BaseFunction {
@Override
protected Object call(Object[] args, @Nullable FuncallExpression ast, @Nullable Environment env)
- throws EvalException, ConversionException, InterruptedException {
+ throws EvalException, InterruptedException {
@SuppressWarnings("unchecked")
Map<String, Object> kwargs = (Map<String, Object>) args[0];
return new SkylarkClassObject(this, kwargs, ast != null ? ast.getLocation() : Location.BUILTIN);
@@ -65,6 +75,26 @@ public final class SkylarkClassObjectConstructor extends BaseFunction {
}
@Override
+ public boolean isExported() {
+ return key != null;
+ }
+
+ @Nullable
+ public Key getKey() {
+ return key;
+ }
+
+ public String getPrintableName() {
+ return key != null ? key.exportedName : getName();
+ }
+
+ @Override
+ public void export(Label extensionLabel, String exportedName) {
+ Preconditions.checkState(!isExported());
+ this.key = new Key(extensionLabel, exportedName);
+ }
+
+ @Override
public int hashCode() {
return System.identityHashCode(this);
}
@@ -73,4 +103,46 @@ public final class SkylarkClassObjectConstructor extends BaseFunction {
public boolean equals(@Nullable Object other) {
return other == this;
}
+
+ /**
+ * A serializable representation of {@link SkylarkClassObjectConstructor}
+ * that uniquely identifies all {@link SkylarkClassObjectConstructor}s that
+ * are exposed to SkyFrame.
+ */
+ public static class Key {
+ private final Label extensionLabel;
+ private final String exportedName;
+
+ public Key(Label extensionLabel, String exportedName) {
+ this.extensionLabel = Preconditions.checkNotNull(extensionLabel);
+ this.exportedName = Preconditions.checkNotNull(exportedName);
+ }
+
+ public Label getExtensionLabel() {
+ return extensionLabel;
+ }
+
+ public String getExportedName() {
+ return exportedName;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(extensionLabel, exportedName);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (!(obj instanceof Key)) {
+ return false;
+ }
+ Key other = (Key) obj;
+ return Objects.equals(this.extensionLabel, other.extensionLabel)
+ && Objects.equals(this.exportedName, other.exportedName);
+ }
+ }
}
diff --git a/src/main/java/com/google/devtools/build/lib/packages/SkylarkExportable.java b/src/main/java/com/google/devtools/build/lib/packages/SkylarkExportable.java
new file mode 100644
index 0000000000..003baa946d
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/packages/SkylarkExportable.java
@@ -0,0 +1,38 @@
+// Copyright 2016 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.packages;
+
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.skylarkinterface.SkylarkValue;
+import com.google.devtools.build.lib.syntax.EvalException;
+
+/**
+ * {@link SkylarkValue}s that need special handling when they are
+ * exported from an extension file. For example, rule definitions
+ * receive their name at the end of the execution of the .bzl file.
+ */
+public interface SkylarkExportable extends SkylarkValue {
+
+ /**
+ * Is this value already exported?
+ */
+ boolean isExported();
+
+ /**
+ * Notify the value that it is exported from {@code extensionLabel}
+ * extension with name {@code exportedName}.
+ */
+ void export(Label extensionLabel, String exportedName) throws EvalException;
+}
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 8b6f05230a..734ae0b3a1 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
@@ -64,6 +64,7 @@ import com.google.devtools.build.lib.packages.RuleFactory.InvalidRuleException;
import com.google.devtools.build.lib.packages.SkylarkAspect;
import com.google.devtools.build.lib.packages.SkylarkClassObject;
import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor;
+import com.google.devtools.build.lib.packages.SkylarkExportable;
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;
@@ -194,7 +195,7 @@ public class SkylarkRuleClassFunctions {
@SkylarkSignature(name = "struct", returnType = SkylarkClassObject.class, doc =
"Creates an immutable struct using the keyword arguments as attributes. It is used to group "
- + "multiple values together.Example:<br>"
+ + "multiple values together. Example:<br>"
+ "<pre class=\"language-python\">s = struct(x = 2, y = 3)\n"
+ "return s.x + getattr(s, \"y\") # returns 5</pre>",
extraKeywords = @Param(name = "kwargs", doc = "the struct attributes"),
@@ -202,6 +203,23 @@ public class SkylarkRuleClassFunctions {
private static final SkylarkClassObjectConstructor struct =
SkylarkClassObjectConstructor.STRUCT;
+ @SkylarkSignature(name = "provider", returnType = SkylarkClassObjectConstructor.class, doc =
+ "Creates a declared provider 'constructor'. The return value of this"
+ + "function can be used to create \"struct-like\" values. Example:<br>"
+ + "<pre class=\"language-python\">data = provider()\n"
+ + "d = data(x = 2, y = 3)"
+ + "return d.x + d.y # returns 5</pre>",
+ useLocation = true
+ )
+ private static final BuiltinFunction provider =
+ new BuiltinFunction("provider") {
+ public SkylarkClassObjectConstructor invoke(Location location) {
+ return new SkylarkClassObjectConstructor(
+ "<no name>", // name is set on export.
+ location);
+ }
+ };
+
// TODO(bazel-team): implement attribute copy and other rule properties
@SkylarkSignature(name = "rule", doc =
@@ -473,7 +491,7 @@ public class SkylarkRuleClassFunctions {
/** The implementation for the magic function "rule" that creates Skylark rule classes */
- public static final class RuleFunction extends BaseFunction {
+ public static final class RuleFunction extends BaseFunction implements SkylarkExportable {
private RuleClass.Builder builder;
private RuleClass ruleClass;
@@ -544,7 +562,7 @@ public class SkylarkRuleClassFunctions {
/**
* Export a RuleFunction from a Skylark file with a given name.
*/
- void export(Label skylarkLabel, String ruleClassName) throws EvalException {
+ public void export(Label skylarkLabel, String ruleClassName) throws EvalException {
Preconditions.checkState(ruleClass == null && builder != null);
this.skylarkLabel = skylarkLabel;
if (type == RuleClassType.TEST != TargetUtils.isTestRuleName(ruleClassName)) {
@@ -569,35 +587,40 @@ public class SkylarkRuleClassFunctions {
Preconditions.checkState(ruleClass != null && builder == null);
return ruleClass;
}
+
+ @Override
+ public boolean isExported() {
+ return skylarkLabel != null;
+ }
}
+ /**
+ * All classes of values that need special processing after they are exported
+ * from an extension file.
+ *
+ * Order in list list is significant: all {@link }SkylarkAspect}s need to be exported
+ * before {@link RuleFunction}s etc.
+ */
+ private static final List<Class<? extends SkylarkExportable>> EXPORTABLES =
+ ImmutableList.of(
+ SkylarkClassObjectConstructor.class,
+ SkylarkAspect.class,
+ RuleFunction.class);
+
public static void exportRuleFunctionsAndAspects(Environment env, Label skylarkLabel)
throws EvalException {
Set<String> globalNames = env.getGlobals().getDirectVariableNames();
- // Export aspects first since rules can depend on aspects.
- for (String name : globalNames) {
- Object value = env.lookup(name);
- if (name == null) {
- throw new AssertionError(String.format("No such variable: '%s'", name));
- }
- if (value instanceof SkylarkAspect) {
- SkylarkAspect skylarkAspect = (SkylarkAspect) value;
- if (!skylarkAspect.isExported()) {
- skylarkAspect.export(skylarkLabel, name);
+ for (Class<? extends SkylarkExportable> exportable : EXPORTABLES) {
+ for (String name : globalNames) {
+ Object value = env.lookup(name);
+ if (value == null) {
+ throw new AssertionError(String.format("No such variable: '%s'", name));
}
- }
- }
-
- for (String name : globalNames) {
- Object value = env.lookup(name);
- if (value == null) {
- throw new AssertionError(String.format("No such variable: '%s'", name));
- }
- if (value instanceof RuleFunction) {
- RuleFunction function = (RuleFunction) value;
- if (function.skylarkLabel == null) {
- function.export(skylarkLabel, name);
+ if (exportable.isInstance(value)) {
+ if (!exportable.cast(value).isExported()) {
+ exportable.cast(value).export(skylarkLabel, name);
+ }
}
}
}