diff options
author | Dmitry Lomov <dslomov@google.com> | 2016-08-10 18:29:04 +0000 |
---|---|---|
committer | Yue Gan <yueg@google.com> | 2016-08-11 09:14:21 +0000 |
commit | 8a07a959e10040642cdf2e6fa21edcd6a1e9212b (patch) | |
tree | e0109611dcfe55906c217b86aa411d7a74670185 /src/main/java/com | |
parent | f2f8408c4fa242ceb9352b49e50eb09863285dca (diff) |
Add 'provider()' function.
--
MOS_MIGRATED_REVID=129889793
Diffstat (limited to 'src/main/java/com')
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); + } } } } |