Create SkylarkCallable.useContext and use it instead of Environment.BazelInfo
This makes the Starlark interpreter pass a StarlarkContext object to methods, which, for Bazel, should be a stand-in replacement for the previous BazelInfo object.
In a future cleanup, we can move Mutability to context, and then turn down useEnvironment. Environment is a bit too powerful an object to be passing around.
RELNOTES: None.
PiperOrigin-RevId: 224216211
diff --git a/src/main/java/com/google/devtools/build/lib/BUILD b/src/main/java/com/google/devtools/build/lib/BUILD
index 2d7a0ab..c6a35ff 100644
--- a/src/main/java/com/google/devtools/build/lib/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/BUILD
@@ -374,6 +374,18 @@
)
java_library(
+ name = "starlark_context",
+ srcs = [
+ "analysis/skylark/BazelStarlarkContext.java",
+ ],
+ deps = [
+ ":skylarkinterface",
+ "//third_party:guava",
+ "//third_party:jsr305",
+ ],
+)
+
+java_library(
name = "skylark_semantics",
srcs = [
"syntax/SkylarkSemantics.java",
@@ -493,6 +505,7 @@
":events",
":skylark_semantics",
":skylarkinterface",
+ ":starlark_context",
":syntax",
":transitive-info-provider",
":util",
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java
index ffeb3ab..b8a6589 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java
@@ -34,6 +34,7 @@
import com.google.devtools.build.lib.analysis.config.DefaultsPackage;
import com.google.devtools.build.lib.analysis.config.FragmentOptions;
import com.google.devtools.build.lib.analysis.constraints.ConstraintSemantics;
+import com.google.devtools.build.lib.analysis.skylark.BazelStarlarkContext;
import com.google.devtools.build.lib.analysis.skylark.SkylarkModules;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
@@ -811,16 +812,17 @@
EventHandler eventHandler,
String astFileContentHashCode,
Map<String, Extension> importMap) {
+ BazelStarlarkContext context =
+ new BazelStarlarkContext(toolsRepository, configurationFragmentMap);
Environment env =
Environment.builder(mutability)
.setGlobals(globals)
.setSemantics(skylarkSemantics)
.setEventHandler(eventHandler)
.setFileContentHashCode(astFileContentHashCode)
+ .setStarlarkContext(context)
.setImportedExtensions(importMap)
.build();
- SkylarkUtils.setToolsRepository(env, toolsRepository);
- SkylarkUtils.setFragmentMap(env, configurationFragmentMap);
SkylarkUtils.setPhase(env, Phase.LOADING);
return env;
}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/skylark/BazelBuildApiGlobals.java b/src/main/java/com/google/devtools/build/lib/analysis/skylark/BazelBuildApiGlobals.java
index efd87e7..7fbf62a 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/skylark/BazelBuildApiGlobals.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/skylark/BazelBuildApiGlobals.java
@@ -16,9 +16,8 @@
import com.google.devtools.build.lib.events.Location;
import com.google.devtools.build.lib.skylarkbuildapi.SkylarkBuildApiGlobals;
-import com.google.devtools.build.lib.syntax.Environment;
+import com.google.devtools.build.lib.skylarkinterface.StarlarkContext;
import com.google.devtools.build.lib.syntax.EvalException;
-import com.google.devtools.build.lib.syntax.SkylarkUtils;
/**
* Bazel implementation of {@link SkylarkBuildApiGlobals}: a collection of global skylark build
@@ -28,9 +27,9 @@
@Override
public SkylarkLateBoundDefault<?> configurationField(
- String fragment, String name, Location loc, Environment env)
- throws EvalException {
- Class<?> fragmentClass = SkylarkUtils.getFragmentMap(env).get(fragment);
+ String fragment, String name, Location loc, StarlarkContext context) throws EvalException {
+ BazelStarlarkContext bazelContext = (BazelStarlarkContext) context;
+ Class<?> fragmentClass = bazelContext.getFragmentNameToClass().get(fragment);
if (fragmentClass == null) {
throw new EvalException(
@@ -39,7 +38,7 @@
}
try {
return SkylarkLateBoundDefault.forConfigurationField(
- fragmentClass, name, SkylarkUtils.getToolsRepository(env));
+ fragmentClass, name, bazelContext.getToolsRepository());
} catch (SkylarkLateBoundDefault.InvalidConfigurationFieldException exception) {
throw new EvalException(loc, exception);
}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/skylark/BazelStarlarkContext.java b/src/main/java/com/google/devtools/build/lib/analysis/skylark/BazelStarlarkContext.java
new file mode 100644
index 0000000..7ebf761
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/analysis/skylark/BazelStarlarkContext.java
@@ -0,0 +1,70 @@
+// Copyright 2018 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.analysis.skylark;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.devtools.build.lib.skylarkinterface.StarlarkContext;
+import java.util.Objects;
+import javax.annotation.Nullable;
+
+/** An implementation of {@link StarlarkContext} containing Bazel-specific context. */
+public class BazelStarlarkContext implements StarlarkContext {
+ private final String toolsRepository;
+ @Nullable private final ImmutableMap<String, Class<?>> fragmentNameToClass;
+
+ /**
+ * @param toolsRepository the name of the tools repository, such as "bazel_tools"
+ * @param fragmentNameToClass a map from configuration fragment name to configuration fragment
+ * class, such as "apple" to AppleConfiguration.class
+ */
+ public BazelStarlarkContext(
+ String toolsRepository, ImmutableMap<String, Class<?>> fragmentNameToClass) {
+ this.toolsRepository = toolsRepository;
+ this.fragmentNameToClass = fragmentNameToClass;
+ }
+
+ /** @param toolsRepository the name of the tools repository, such as "bazel_tools" */
+ public BazelStarlarkContext(String toolsRepository) {
+ this(toolsRepository, null);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof BazelStarlarkContext)) {
+ return false;
+ }
+ BazelStarlarkContext that = (BazelStarlarkContext) obj;
+ return Objects.equals(this.toolsRepository, that.toolsRepository)
+ && Objects.equals(this.fragmentNameToClass, that.fragmentNameToClass);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(toolsRepository, fragmentNameToClass);
+ }
+
+ /** Returns the name of the tools repository, such as "bazel_tools". */
+ public String getToolsRepository() {
+ return toolsRepository;
+ }
+
+ /** Returns a map from configuration fragment name to configuration fragment class. */
+ public ImmutableMap<String, Class<?>> getFragmentNameToClass() {
+ return fragmentNameToClass;
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleClassFunctions.java b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleClassFunctions.java
index 5382551..bc9385a 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleClassFunctions.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleClassFunctions.java
@@ -72,6 +72,7 @@
import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
import com.google.devtools.build.lib.skylarkbuildapi.SkylarkRuleFunctionsApi;
import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter;
+import com.google.devtools.build.lib.skylarkinterface.StarlarkContext;
import com.google.devtools.build.lib.syntax.BaseFunction;
import com.google.devtools.build.lib.syntax.Environment;
import com.google.devtools.build.lib.syntax.EvalException;
@@ -275,17 +276,19 @@
Object analysisTest,
Object buildSetting,
FuncallExpression ast,
- Environment funcallEnv)
+ Environment funcallEnv,
+ StarlarkContext context)
throws EvalException, ConversionException {
SkylarkUtils.checkLoadingOrWorkspacePhase(funcallEnv, "rule", ast.getLocation());
+ BazelStarlarkContext bazelContext = (BazelStarlarkContext) context;
// analysis_test=true implies test=true.
test |= Boolean.TRUE.equals(analysisTest);
RuleClassType type = test ? RuleClassType.TEST : RuleClassType.NORMAL;
RuleClass parent =
test
- ? getTestBaseRule(SkylarkUtils.getToolsRepository(funcallEnv))
+ ? getTestBaseRule(bazelContext.getToolsRepository())
: (executable ? binaryBaseRule : baseRule);
// We'll set the name later, pass the empty string for now.
diff --git a/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java b/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java
index 4f5afc9..189876b 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java
@@ -22,6 +22,7 @@
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import com.google.devtools.build.lib.analysis.skylark.BazelStarlarkContext;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
import com.google.devtools.build.lib.cmdline.LabelValidator;
@@ -1612,15 +1613,17 @@
StoredEventHandler eventHandler = new StoredEventHandler();
try (Mutability mutability = Mutability.create("package %s", packageId)) {
+ BazelStarlarkContext starlarkContext =
+ new BazelStarlarkContext(ruleClassProvider.getToolsRepository());
Environment pkgEnv =
Environment.builder(mutability)
.setGlobals(BazelLibrary.GLOBALS)
.setSemantics(skylarkSemantics)
.setEventHandler(eventHandler)
.setImportedExtensions(imports)
+ .setStarlarkContext(starlarkContext)
.build();
SkylarkUtils.setPhase(pkgEnv, Phase.LOADING);
- SkylarkUtils.setToolsRepository(pkgEnv, ruleClassProvider.getToolsRepository());
pkgBuilder.setFilename(buildFilePath)
.setDefaultVisibility(defaultVisibility)
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/SkylarkBuildApiGlobals.java b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/SkylarkBuildApiGlobals.java
index ecb02bc..eedb78a 100644
--- a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/SkylarkBuildApiGlobals.java
+++ b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/SkylarkBuildApiGlobals.java
@@ -18,7 +18,7 @@
import com.google.devtools.build.lib.skylarkinterface.Param;
import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
import com.google.devtools.build.lib.skylarkinterface.SkylarkGlobalLibrary;
-import com.google.devtools.build.lib.syntax.Environment;
+import com.google.devtools.build.lib.skylarkinterface.StarlarkContext;
import com.google.devtools.build.lib.syntax.EvalException;
/**
@@ -28,36 +28,35 @@
public interface SkylarkBuildApiGlobals {
@SkylarkCallable(
- name = "configuration_field",
- // TODO(cparsons): Provide a link to documentation for available SkylarkConfigurationFields.
- doc = "References a late-bound default value for an attribute of type "
- + "<a href=\"attr.html#label\">label</a>. A value is 'late-bound' if it requires "
- + "the configuration to be built before determining the value. Any attribute using this "
- + "as a value must <a href=\"../rules.html#private-attributes\">be private</a>. "
- + "<p>Example usage: "
- + "<p>Defining a rule attribute: <br><pre class=language-python>"
- + "'_foo': attr.label(default=configuration_field(fragment='java', name='toolchain'))</pre>"
- + "<p>Accessing in rule implementation: <br><pre class=language-python>"
- + " def _rule_impl(ctx):\n"
- + " foo_info = ctx.attr._foo\n"
- + " ...</pre>",
- parameters = {
+ name = "configuration_field",
+ // TODO(cparsons): Provide a link to documentation for available SkylarkConfigurationFields.
+ doc =
+ "References a late-bound default value for an attribute of type "
+ + "<a href=\"attr.html#label\">label</a>. A value is 'late-bound' if it requires "
+ + "the configuration to be built before determining the value. Any attribute using "
+ + "this as a value must <a href=\"../rules.html#private-attributes\">be private</a>. "
+ + "<p>Example usage: "
+ + "<p>Defining a rule attribute: <br><pre class=language-python>"
+ + "'_foo': attr.label(default=configuration_field(fragment='java', "
+ + "name='toolchain'))</pre>"
+ + "<p>Accessing in rule implementation: <br><pre class=language-python>"
+ + " def _rule_impl(ctx):\n"
+ + " foo_info = ctx.attr._foo\n"
+ + " ...</pre>",
+ parameters = {
@Param(
name = "fragment",
type = String.class,
named = true,
- doc = "The name of a configuration fragment which contains the late-bound value."
- ),
+ doc = "The name of a configuration fragment which contains the late-bound value."),
@Param(
name = "name",
type = String.class,
named = true,
doc = "The name of the value to obtain from the configuration fragment."),
- },
- useLocation = true,
- useEnvironment = true
- )
+ },
+ useLocation = true,
+ useContext = true)
public LateBoundDefaultApi configurationField(
- String fragment, String name, Location loc, Environment env)
- throws EvalException;
+ String fragment, String name, Location loc, StarlarkContext context) throws EvalException;
}
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/SkylarkRuleFunctionsApi.java b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/SkylarkRuleFunctionsApi.java
index aab2e53..18543da 100644
--- a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/SkylarkRuleFunctionsApi.java
+++ b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/SkylarkRuleFunctionsApi.java
@@ -22,6 +22,7 @@
import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
import com.google.devtools.build.lib.skylarkinterface.SkylarkConstructor;
import com.google.devtools.build.lib.skylarkinterface.SkylarkGlobalLibrary;
+import com.google.devtools.build.lib.skylarkinterface.StarlarkContext;
import com.google.devtools.build.lib.syntax.BaseFunction;
import com.google.devtools.build.lib.syntax.Environment;
import com.google.devtools.build.lib.syntax.EvalException;
@@ -329,7 +330,8 @@
+ "added to this rule, with a type corresponding to the value passed in here."),
},
useAst = true,
- useEnvironment = true)
+ useEnvironment = true,
+ useContext = true)
public BaseFunction rule(
BaseFunction implementation,
Boolean test,
@@ -348,7 +350,8 @@
Object analysisTest,
Object buildSetting,
FuncallExpression ast,
- Environment funcallEnv)
+ Environment funcallEnv,
+ StarlarkContext context)
throws EvalException;
@SkylarkCallable(
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkinterface/SkylarkCallable.java b/src/main/java/com/google/devtools/build/lib/skylarkinterface/SkylarkCallable.java
index ee60453..546d255 100644
--- a/src/main/java/com/google/devtools/build/lib/skylarkinterface/SkylarkCallable.java
+++ b/src/main/java/com/google/devtools/build/lib/skylarkinterface/SkylarkCallable.java
@@ -31,12 +31,12 @@
* {@code @SkylarkModule}.
*
* <p>If a method is annotated with {@code @SkylarkCallable}, it is not allowed to have any
- * overloads or hide any static or default methods. Overriding is allowed, but the {@code
- * @SkylarkCallable} annotation itself must not be repeated on the override. This ensures that given
- * a method, we can always determine its corresponding {@code @SkylarkCallable} annotation, if it
- * has one, by scanning all methods of the same name in its class hierarchy, without worrying about
- * complications like overloading or generics. The lookup functionality is implemented by {@link
- * SkylarkInterfaceUtils#getSkylarkCallable}.
+ * overloads or hide any static or default methods. Overriding is allowed, but the
+ * {@code @SkylarkCallable} annotation itself must not be repeated on the override. This ensures
+ * that given a method, we can always determine its corresponding {@code @SkylarkCallable}
+ * annotation, if it has one, by scanning all methods of the same name in its class hierarchy,
+ * without worrying about complications like overloading or generics. The lookup functionality is
+ * implemented by {@link SkylarkInterfaceUtils#getSkylarkCallable}.
*
* <p>Methods having this annotation are required to satisfy the following (enforced by an
* annotation processor):
@@ -46,7 +46,7 @@
* <li>If structField=true, there must be zero user-supplied parameters.
* <li>The underlying java method's parameters must be supplied in the following order:
* <pre>method([positionals]*[named args]*(extra positionals list)(extra kwargs)
- * (Location)(FuncallExpression)(Envrionment)(SkylarkSemantics))</pre>
+ * (Location)(FuncallExpression)(Envrionment)(SkylarkSemantics)(StarlarkContext))</pre>
* where (extra positionals list) is a SkylarkList if extraPositionals is defined, (extra
* kwargs) is a SkylarkDict if extraKeywords is defined, and Location, FuncallExpression,
* Environment, and SkylarkSemantics are supplied by the interpreter if and only if
@@ -171,6 +171,13 @@
boolean useSkylarkSemantics() default false;
/**
+ * If true, StarlarkContext will be passed as an argument of the annotated function. (Thus, the
+ * annotated method signature must contain StarlarkContext as a parameter. See the interface-level
+ * javadoc for details.)
+ */
+ boolean useContext() default false;
+
+ /**
* If not NONE, the annotated method will only be callable if the given semantic flag is true.
* Note that at most one of {@link #enableOnlyWithFlag} and {@link #disableWithFlag} can be
* non-NONE.
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkinterface/StarlarkContext.java b/src/main/java/com/google/devtools/build/lib/skylarkinterface/StarlarkContext.java
new file mode 100644
index 0000000..a220431
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skylarkinterface/StarlarkContext.java
@@ -0,0 +1,29 @@
+// Copyright 2018 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.skylarkinterface;
+
+/**
+ * A container of application-specific context which may be passed to Starlark callable functions.
+ *
+ * <p>An application using the Starlark interpreter can provide a custom context implementation to a
+ * Starlark environment using {@link Environment#setStarlarkContext}. The context object will then
+ * be passed to any {@link SkylarkCallable} method with {@link SkylarkCallable#useContext} set to
+ * true.
+ *
+ * <p>Note that the interpreter passes a StarlarkContext to a callable method which requests it, and
+ * does no casting to an application-specific context object. The application's implementation of
+ * the callable method must cast a context object to the application-specific context itself.
+ */
+public interface StarlarkContext {}
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkinterface/processor/SkylarkCallableProcessor.java b/src/main/java/com/google/devtools/build/lib/skylarkinterface/processor/SkylarkCallableProcessor.java
index d9e1db1..ac7aab0 100644
--- a/src/main/java/com/google/devtools/build/lib/skylarkinterface/processor/SkylarkCallableProcessor.java
+++ b/src/main/java/com/google/devtools/build/lib/skylarkinterface/processor/SkylarkCallableProcessor.java
@@ -85,6 +85,8 @@
private static final String ENVIRONMENT = "com.google.devtools.build.lib.syntax.Environment";
private static final String SKYLARK_SEMANTICS =
"com.google.devtools.build.lib.syntax.SkylarkSemantics";
+ private static final String STARLARK_CONTEXT =
+ "com.google.devtools.build.lib.skylarkinterface.StarlarkContext";
@Override
public SourceVersion getSupportedSourceVersion() {
@@ -448,6 +450,17 @@
SKYLARK_SEMANTICS,
methodSignatureParams.get(currentIndex).asType().toString()));
}
+ currentIndex++;
+ }
+ if (annotation.useContext()) {
+ if (!STARLARK_CONTEXT.equals(methodSignatureParams.get(currentIndex).asType().toString())) {
+ throw new SkylarkCallableProcessorException(
+ methodElement,
+ String.format(
+ "Expected parameter index %d to be the %s type, matching useContext, "
+ + "but was %s",
+ currentIndex, STARLARK_CONTEXT, methodSignatureParams.get(currentIndex).asType()));
+ }
}
}
@@ -459,6 +472,7 @@
numExtraInterpreterParams += annotation.useAst() ? 1 : 0;
numExtraInterpreterParams += annotation.useEnvironment() ? 1 : 0;
numExtraInterpreterParams += annotation.useSkylarkSemantics() ? 1 : 0;
+ numExtraInterpreterParams += annotation.useContext() ? 1 : 0;
return numExtraInterpreterParams;
}
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Environment.java b/src/main/java/com/google/devtools/build/lib/syntax/Environment.java
index b8d20f5..3b74a32 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/Environment.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/Environment.java
@@ -28,6 +28,7 @@
import com.google.devtools.build.lib.events.Location;
import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
import com.google.devtools.build.lib.skylarkinterface.SkylarkValue;
+import com.google.devtools.build.lib.skylarkinterface.StarlarkContext;
import com.google.devtools.build.lib.syntax.Mutability.Freezable;
import com.google.devtools.build.lib.syntax.Mutability.MutabilityException;
import com.google.devtools.build.lib.syntax.Parser.ParsingLevel;
@@ -746,6 +747,8 @@
/** The semantics options that affect how Skylark code is evaluated. */
private final SkylarkSemantics semantics;
+ private final StarlarkContext starlarkContext;
+
/**
* An EventHandler for errors and warnings. This is not used in the BUILD language, however it
* might be used in Skylark code called from the BUILD language, so shouldn't be null.
@@ -874,6 +877,7 @@
GlobalFrame globalFrame,
LexicalFrame dynamicFrame,
SkylarkSemantics semantics,
+ StarlarkContext starlarkContext,
EventHandler eventHandler,
Map<String, Extension> importedExtensions,
@Nullable String fileContentHashCode,
@@ -884,6 +888,7 @@
Preconditions.checkArgument(!globalFrame.mutability().isFrozen());
Preconditions.checkArgument(!dynamicFrame.mutability().isFrozen());
this.semantics = semantics;
+ this.starlarkContext = starlarkContext;
this.eventHandler = eventHandler;
this.importedExtensions = importedExtensions;
this.callerLabel = callerLabel;
@@ -901,6 +906,7 @@
private final Mutability mutability;
@Nullable private GlobalFrame parent;
@Nullable private SkylarkSemantics semantics;
+ @Nullable private StarlarkContext starlarkContext;
@Nullable private EventHandler eventHandler;
@Nullable private Map<String, Extension> importedExtensions;
@Nullable private String fileContentHashCode;
@@ -908,6 +914,8 @@
Builder(Mutability mutability) {
this.mutability = mutability;
+ // TODO(cparsons): Require specifying a starlarkContext (or declaring use of an empty stub).
+ this.starlarkContext = new StarlarkContext() {};
}
/**
@@ -931,6 +939,16 @@
return this;
}
+ public Builder setStarlarkContext(StarlarkContext starlarkContext) {
+ this.starlarkContext = starlarkContext;
+ return this;
+ }
+
+ public Builder useEmptyStarlarkContext() {
+ this.starlarkContext = new StarlarkContext() {};
+ return this;
+ }
+
/** Sets an EventHandler for errors and warnings. */
public Builder setEventHandler(EventHandler eventHandler) {
Preconditions.checkState(this.eventHandler == null);
@@ -988,6 +1006,7 @@
globalFrame,
dynamicFrame,
semantics,
+ starlarkContext,
eventHandler,
importedExtensions,
fileContentHashCode,
@@ -1169,6 +1188,10 @@
return semantics;
}
+ public StarlarkContext getStarlarkContext() {
+ return starlarkContext;
+ }
+
public void handleEvent(Event event) {
eventHandler.handle(event);
}
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java b/src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java
index b44411a..bca295a 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java
@@ -466,7 +466,8 @@
Preconditions.checkArgument(
!methodDescriptor.isUseEnvironment()
|| !methodDescriptor.isUseSkylarkSemantics()
- || !methodDescriptor.isUseLocation(),
+ || !methodDescriptor.isUseLocation()
+ || !methodDescriptor.isUseContext(),
"Cannot be invoked on structField callables with extra interpreter params");
return methodDescriptor.call(obj, new Object[0], Location.BUILTIN, null);
}
@@ -725,6 +726,9 @@
if (method.isUseSkylarkSemantics()) {
builder.add(env.getSemantics());
}
+ if (method.isUseContext()) {
+ builder.add(env.getStarlarkContext());
+ }
}
private static String formatMethod(
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/MethodDescriptor.java b/src/main/java/com/google/devtools/build/lib/syntax/MethodDescriptor.java
index 6335f15..1ca3516 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/MethodDescriptor.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/MethodDescriptor.java
@@ -47,6 +47,7 @@
private final boolean useAst;
private final boolean useEnvironment;
private final boolean useSkylarkSemantics;
+ private final boolean useContext;
private MethodDescriptor(
Method method,
@@ -63,7 +64,8 @@
boolean useLocation,
boolean useAst,
boolean useEnvironment,
- boolean useSkylarkSemantics) {
+ boolean useSkylarkSemantics,
+ boolean useContext) {
this.method = method;
this.annotation = annotation;
this.name = name;
@@ -79,6 +81,7 @@
this.useAst = useAst;
this.useEnvironment = useEnvironment;
this.useSkylarkSemantics = useSkylarkSemantics;
+ this.useContext = useContext;
}
/** Returns the SkylarkCallable annotation corresponding to this method. */
@@ -109,7 +112,8 @@
annotation.useLocation(),
annotation.useAst(),
annotation.useEnvironment(),
- annotation.useSkylarkSemantics());
+ annotation.useSkylarkSemantics(),
+ annotation.useContext());
}
/** @return The result of this method invocation on the {@code obj} as a target. */
@@ -188,6 +192,11 @@
return useSkylarkSemantics;
}
+ /** See {@link SkylarkCallable#useContext()}. */
+ boolean isUseContext() {
+ return useContext;
+ }
+
/** @see SkylarkCallable#useLocation() */
public boolean isUseLocation() {
return useLocation;
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkUtils.java b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkUtils.java
index db46c1d..8b5b409 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkUtils.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkUtils.java
@@ -14,80 +14,13 @@
package com.google.devtools.build.lib.syntax;
-import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.lib.events.Location;
-import java.util.Objects;
/** This class contains Bazel-specific functions to extend or interoperate with Skylark. */
public final class SkylarkUtils {
- /** Bazel-specific information that we store in the Environment. */
- private static class BazelInfo {
- String toolsRepository;
- ImmutableMap<String, Class<?>> fragmentNameToClass;
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (!(obj instanceof BazelInfo)) {
- return false;
- }
- BazelInfo that = (BazelInfo) obj;
- return Objects.equals(this.toolsRepository, that.toolsRepository)
- && Objects.equals(this.fragmentNameToClass, that.fragmentNameToClass);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(toolsRepository, fragmentNameToClass);
- }
- }
-
- private static final String BAZEL_INFO_KEY = "$bazel";
private static final String PHASE_KEY = "$phase";
- private static BazelInfo getInfo(Environment env) {
- Object info = env.moduleLookup(BAZEL_INFO_KEY);
- if (info != null) {
- return (BazelInfo) info;
- }
-
- BazelInfo result = new BazelInfo();
- try {
- env.update(BAZEL_INFO_KEY, result);
- return result;
- } catch (EvalException e) {
- throw new AssertionError(e);
- }
- }
-
- public static void setToolsRepository(Environment env, String toolsRepository) {
- getInfo(env).toolsRepository = toolsRepository;
- }
-
- public static String getToolsRepository(Environment env) {
- return getInfo(env).toolsRepository;
- }
-
- /**
- * Sets, on an {@link Environment}, a {@link Map} from configuration fragment name to
- * configuration fragment class.
- */
- public static void setFragmentMap(Environment env,
- ImmutableMap<String, Class<?>> fragmentNameToClass) {
- getInfo(env).fragmentNameToClass = fragmentNameToClass;
- }
-
- /**
- * Returns the {@link Map} from configuration fragment name to configuration fragment class, as
- * set by {@link #setFragmentMap}.
- */
- public static ImmutableMap<String, Class<?>> getFragmentMap(Environment env) {
- return getInfo(env).fragmentNameToClass;
- }
-
/** A phase for enabling or disabling certain builtin functions */
public enum Phase {
WORKSPACE,
diff --git a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeBuildApiGlobals.java b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeBuildApiGlobals.java
index d23bae5..65beb6f 100644
--- a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeBuildApiGlobals.java
+++ b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeBuildApiGlobals.java
@@ -17,7 +17,7 @@
import com.google.devtools.build.lib.events.Location;
import com.google.devtools.build.lib.skylarkbuildapi.LateBoundDefaultApi;
import com.google.devtools.build.lib.skylarkbuildapi.SkylarkBuildApiGlobals;
-import com.google.devtools.build.lib.syntax.Environment;
+import com.google.devtools.build.lib.skylarkinterface.StarlarkContext;
import com.google.devtools.build.lib.syntax.EvalException;
/**
@@ -26,8 +26,8 @@
public class FakeBuildApiGlobals implements SkylarkBuildApiGlobals {
@Override
- public LateBoundDefaultApi configurationField(String fragment, String name, Location loc,
- Environment env) throws EvalException {
+ public LateBoundDefaultApi configurationField(
+ String fragment, String name, Location loc, StarlarkContext context) throws EvalException {
return new FakeLateBoundDefaultApi();
}
}
diff --git a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeSkylarkRuleFunctionsApi.java b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeSkylarkRuleFunctionsApi.java
index af8110a..7e76957 100644
--- a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeSkylarkRuleFunctionsApi.java
+++ b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeSkylarkRuleFunctionsApi.java
@@ -24,6 +24,7 @@
import com.google.devtools.build.lib.skylarkbuildapi.ProviderApi;
import com.google.devtools.build.lib.skylarkbuildapi.SkylarkAspectApi;
import com.google.devtools.build.lib.skylarkbuildapi.SkylarkRuleFunctionsApi;
+import com.google.devtools.build.lib.skylarkinterface.StarlarkContext;
import com.google.devtools.build.lib.syntax.BaseFunction;
import com.google.devtools.build.lib.syntax.Environment;
import com.google.devtools.build.lib.syntax.EvalException;
@@ -119,7 +120,8 @@
Object analysisTest,
Object buildSetting,
FuncallExpression ast,
- Environment funcallEnv)
+ Environment funcallEnv,
+ StarlarkContext context)
throws EvalException {
List<AttributeInfo> attrInfos;
ImmutableMap.Builder<String, FakeDescriptor> attrsMapBuilder = ImmutableMap.builder();
diff --git a/src/test/java/com/google/devtools/build/lib/BUILD b/src/test/java/com/google/devtools/build/lib/BUILD
index 01ee15d..89da219 100644
--- a/src/test/java/com/google/devtools/build/lib/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/BUILD
@@ -1119,6 +1119,7 @@
":testutil",
"//src/main/java/com/google/devtools/build/lib:bazel-main",
"//src/main/java/com/google/devtools/build/lib:bazel-rules",
+ "//src/main/java/com/google/devtools/build/lib:build-base",
"//src/main/java/com/google/devtools/build/lib:events",
"//src/main/java/com/google/devtools/build/lib:packages",
"//src/main/java/com/google/devtools/build/lib:util",
diff --git a/src/test/java/com/google/devtools/build/lib/skylark/util/SkylarkTestCase.java b/src/test/java/com/google/devtools/build/lib/skylark/util/SkylarkTestCase.java
index 8abf90b..94043f5 100644
--- a/src/test/java/com/google/devtools/build/lib/skylark/util/SkylarkTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/skylark/util/SkylarkTestCase.java
@@ -22,6 +22,7 @@
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.analysis.skylark.BazelStarlarkContext;
import com.google.devtools.build.lib.analysis.skylark.SkylarkModules;
import com.google.devtools.build.lib.analysis.skylark.SkylarkRuleContext;
import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
@@ -67,6 +68,7 @@
return new EvaluationTestCase() {
@Override
public Environment newEnvironment() throws Exception {
+ BazelStarlarkContext context = new BazelStarlarkContext(TestConstants.TOOLS_REPOSITORY);
Environment env =
Environment.builder(mutability)
.setSemantics(semantics)
@@ -75,13 +77,13 @@
getSkylarkGlobals()
.withLabel(
Label.parseAbsoluteUnchecked("//test:label", /*defaultToMain=*/ false)))
+ .setStarlarkContext(context)
.build()
.setupDynamic(
PackageFactory.PKG_CONTEXT,
// This dummy pkgContext works because no Skylark unit test attempts to actually
// create rules. Creating actual rules is tested in SkylarkIntegrationTest.
new PackageContext(null, null, getEventHandler(), null));
- SkylarkUtils.setToolsRepository(env, TestConstants.TOOLS_REPOSITORY);
SkylarkUtils.setPhase(env, Phase.LOADING);
return env;
}
diff --git a/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/BUILD b/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/BUILD
index 43a24e2..7b380c1 100644
--- a/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/BUILD
@@ -16,6 +16,7 @@
resources = [":ProcessorTestFiles"],
deps = [
"//src/main/java/com/google/devtools/build/lib:events",
+ "//src/main/java/com/google/devtools/build/lib:skylarkinterface",
"//src/main/java/com/google/devtools/build/lib:syntax",
"//src/main/java/com/google/devtools/build/lib/skylarkinterface/processor:annotation_preprocessor",
"//third_party:compile_testing",
diff --git a/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/SkylarkCallableProcessorTest.java b/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/SkylarkCallableProcessorTest.java
index aa78222..22c3e28 100644
--- a/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/SkylarkCallableProcessorTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/SkylarkCallableProcessorTest.java
@@ -19,6 +19,7 @@
import com.google.common.io.Resources;
import com.google.devtools.build.lib.events.Location;
+import com.google.devtools.build.lib.skylarkinterface.StarlarkContext;
import com.google.devtools.build.lib.syntax.Environment;
import com.google.testing.compile.JavaFileObjects;
import javax.tools.JavaFileObject;
@@ -143,6 +144,18 @@
}
@Test
+ public void testContextMissing() throws Exception {
+ assertAbout(javaSource())
+ .that(getFile("ContextMissing.java"))
+ .processedWith(new SkylarkCallableProcessor())
+ .failsToCompile()
+ .withErrorContaining(
+ "Expected parameter index 2 to be the "
+ + StarlarkContext.class.getCanonicalName()
+ + " type, matching useContext, but was java.lang.String");
+ }
+
+ @Test
public void testLocationMissing() throws Exception {
assertAbout(javaSource())
.that(getFile("LocationMissing.java"))
diff --git a/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/testsources/ContextMissing.java b/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/testsources/ContextMissing.java
new file mode 100644
index 0000000..1af6d4d
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/testsources/ContextMissing.java
@@ -0,0 +1,37 @@
+// Copyright 2018 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.skylarkinterface.processor.testsources;
+
+import com.google.devtools.build.lib.skylarkinterface.Param;
+import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
+
+/**
+ * Test case for a SkylarkCallable method which does not have an appropriate StarlarkContext
+ * parameter despite having useContext set.
+ */
+public class ContextMissing {
+
+ @SkylarkCallable(
+ name = "three_arg_method_missing_context",
+ documented = false,
+ parameters = {
+ @Param(name = "one", type = String.class, named = true),
+ @Param(name = "two", type = Integer.class, named = true),
+ },
+ useContext = true)
+ public String threeArgMethod(String one, Integer two, String shouldBeContext) {
+ return "bar";
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/testsources/GoldenCase.java b/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/testsources/GoldenCase.java
index 579c7e2..05ce965 100644
--- a/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/testsources/GoldenCase.java
+++ b/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/testsources/GoldenCase.java
@@ -18,6 +18,7 @@
import com.google.devtools.build.lib.skylarkinterface.Param;
import com.google.devtools.build.lib.skylarkinterface.ParamType;
import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
+import com.google.devtools.build.lib.skylarkinterface.StarlarkContext;
import com.google.devtools.build.lib.syntax.Environment;
import com.google.devtools.build.lib.syntax.FuncallExpression;
import com.google.devtools.build.lib.syntax.SkylarkDict;
@@ -157,18 +158,18 @@
}
@SkylarkCallable(
- name = "two_arg_method_with_params_and_info_and_kwargs",
- documented = false,
- parameters = {
- @Param(name = "one", type = String.class, named = true),
- @Param(name = "two", type = Integer.class, named = true),
- },
- extraKeywords = @Param(name = "kwargs"),
- useAst = true,
- useLocation = true,
- useEnvironment = true,
- useSkylarkSemantics = true
- )
+ name = "two_arg_method_with_params_and_info_and_kwargs",
+ documented = false,
+ parameters = {
+ @Param(name = "one", type = String.class, named = true),
+ @Param(name = "two", type = Integer.class, named = true),
+ },
+ extraKeywords = @Param(name = "kwargs"),
+ useAst = true,
+ useLocation = true,
+ useEnvironment = true,
+ useSkylarkSemantics = true,
+ useContext = true)
public String twoArgMethodWithParamsAndInfoAndKwargs(
String one,
Integer two,
@@ -176,7 +177,8 @@
Location location,
FuncallExpression ast,
Environment environment,
- SkylarkSemantics skylarkSemantics) {
+ SkylarkSemantics skylarkSemantics,
+ StarlarkContext context) {
return "blep";
}
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java b/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java
index e7f3861..04f20d8 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java
@@ -35,6 +35,7 @@
import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
import com.google.devtools.build.lib.skylarkinterface.SkylarkSignature;
import com.google.devtools.build.lib.skylarkinterface.SkylarkValue;
+import com.google.devtools.build.lib.skylarkinterface.StarlarkContext;
import com.google.devtools.build.lib.syntax.SkylarkList.MutableList;
import com.google.devtools.build.lib.syntax.SkylarkSemantics.FlagIdentifier;
import com.google.devtools.build.lib.testutil.TestMode;
@@ -308,15 +309,19 @@
}
@SkylarkCallable(
- name = "with_extra",
- documented = false,
- useLocation = true,
- useAst = true,
- useEnvironment = true,
- useSkylarkSemantics = true
- )
+ name = "with_extra",
+ documented = false,
+ useLocation = true,
+ useAst = true,
+ useEnvironment = true,
+ useSkylarkSemantics = true,
+ useContext = true)
public String withExtraInterpreterParams(
- Location location, FuncallExpression func, Environment env, SkylarkSemantics sem) {
+ Location location,
+ FuncallExpression func,
+ Environment env,
+ SkylarkSemantics sem,
+ StarlarkContext context) {
return "with_extra("
+ location.getStartLine()
+ ", "
@@ -325,6 +330,8 @@
+ env.isGlobal()
+ ", "
+ (sem != null)
+ + ", "
+ + (context != null)
+ ")";
}
@@ -1304,7 +1311,7 @@
new SkylarkTest()
.update("mock", new Mock())
.setUp("v = mock.with_extra()")
- .testLookup("v", "with_extra(1, 0, true, true)");
+ .testLookup("v", "with_extra(1, 0, true, true, true)");
}
@Test
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/util/EvaluationTestCase.java b/src/test/java/com/google/devtools/build/lib/syntax/util/EvaluationTestCase.java
index ac07655..f7f8ee8 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/util/EvaluationTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/util/EvaluationTestCase.java
@@ -19,6 +19,7 @@
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableMap;
import com.google.common.truth.Ordered;
+import com.google.devtools.build.lib.analysis.skylark.BazelStarlarkContext;
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.events.EventCollector;
import com.google.devtools.build.lib.events.EventKind;
@@ -63,13 +64,14 @@
* No PythonPreprocessing, mostly empty mutable Environment.
*/
public Environment newBuildEnvironment() {
+ BazelStarlarkContext context = new BazelStarlarkContext(TestConstants.TOOLS_REPOSITORY);
Environment env =
Environment.builder(mutability)
.useDefaultSemantics()
.setGlobals(BazelLibrary.GLOBALS)
.setEventHandler(getEventHandler())
+ .setStarlarkContext(context)
.build();
- SkylarkUtils.setToolsRepository(env, TestConstants.TOOLS_REPOSITORY);
SkylarkUtils.setPhase(env, Phase.LOADING);
return env;
}