aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleClassFunctions.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleClassFunctions.java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleClassFunctions.java1190
1 files changed, 1190 insertions, 0 deletions
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
new file mode 100644
index 0000000000..e716d00b11
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleClassFunctions.java
@@ -0,0 +1,1190 @@
+// Copyright 2014 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 static com.google.devtools.build.lib.analysis.BaseRuleClasses.RUN_UNDER;
+import static com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition.DATA;
+import static com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition.HOST;
+import static com.google.devtools.build.lib.packages.Attribute.attr;
+import static com.google.devtools.build.lib.packages.BuildType.LABEL;
+import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST;
+import static com.google.devtools.build.lib.packages.BuildType.LICENSE;
+import static com.google.devtools.build.lib.syntax.SkylarkType.castMap;
+import static com.google.devtools.build.lib.syntax.Type.BOOLEAN;
+import static com.google.devtools.build.lib.syntax.Type.INTEGER;
+import static com.google.devtools.build.lib.syntax.Type.STRING;
+import static com.google.devtools.build.lib.syntax.Type.STRING_LIST;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.analysis.ActionsProvider;
+import com.google.devtools.build.lib.analysis.BaseRuleClasses;
+import com.google.devtools.build.lib.analysis.DefaultProvider;
+import com.google.devtools.build.lib.analysis.OutputGroupProvider;
+import com.google.devtools.build.lib.analysis.PlatformSemantics;
+import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
+import com.google.devtools.build.lib.analysis.skylark.SkylarkAttr.Descriptor;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
+import com.google.devtools.build.lib.cmdline.LabelValidator;
+import com.google.devtools.build.lib.collect.nestedset.NestedSet;
+import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
+import com.google.devtools.build.lib.collect.nestedset.Order;
+import com.google.devtools.build.lib.events.Location;
+import com.google.devtools.build.lib.packages.Attribute;
+import com.google.devtools.build.lib.packages.AttributeMap;
+import com.google.devtools.build.lib.packages.AttributeValueSource;
+import com.google.devtools.build.lib.packages.ImplicitOutputsFunction.SkylarkImplicitOutputsFunctionWithCallback;
+import com.google.devtools.build.lib.packages.ImplicitOutputsFunction.SkylarkImplicitOutputsFunctionWithMap;
+import com.google.devtools.build.lib.packages.Info;
+import com.google.devtools.build.lib.packages.NativeProvider;
+import com.google.devtools.build.lib.packages.Package.NameConflictException;
+import com.google.devtools.build.lib.packages.PackageFactory;
+import com.google.devtools.build.lib.packages.PackageFactory.PackageContext;
+import com.google.devtools.build.lib.packages.PredicateWithMessage;
+import com.google.devtools.build.lib.packages.Provider;
+import com.google.devtools.build.lib.packages.RuleClass;
+import com.google.devtools.build.lib.packages.RuleClass.Builder;
+import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType;
+import com.google.devtools.build.lib.packages.RuleFactory;
+import com.google.devtools.build.lib.packages.RuleFactory.BuildLangTypedAttributeValuesMap;
+import com.google.devtools.build.lib.packages.RuleFactory.InvalidRuleException;
+import com.google.devtools.build.lib.packages.SkylarkAspect;
+import com.google.devtools.build.lib.packages.SkylarkExportable;
+import com.google.devtools.build.lib.packages.SkylarkProvider;
+import com.google.devtools.build.lib.packages.TargetUtils;
+import com.google.devtools.build.lib.packages.TestSize;
+import com.google.devtools.build.lib.rules.test.TestConfiguration;
+import com.google.devtools.build.lib.skylarkinterface.Param;
+import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter;
+import com.google.devtools.build.lib.skylarkinterface.SkylarkSignature;
+import com.google.devtools.build.lib.syntax.BaseFunction;
+import com.google.devtools.build.lib.syntax.BuiltinFunction;
+import com.google.devtools.build.lib.syntax.ClassObject;
+import com.google.devtools.build.lib.syntax.Environment;
+import com.google.devtools.build.lib.syntax.EvalException;
+import com.google.devtools.build.lib.syntax.EvalUtils;
+import com.google.devtools.build.lib.syntax.FuncallExpression;
+import com.google.devtools.build.lib.syntax.FunctionSignature;
+import com.google.devtools.build.lib.syntax.Runtime;
+import com.google.devtools.build.lib.syntax.SkylarkCallbackFunction;
+import com.google.devtools.build.lib.syntax.SkylarkDict;
+import com.google.devtools.build.lib.syntax.SkylarkList;
+import com.google.devtools.build.lib.syntax.SkylarkNestedSet;
+import com.google.devtools.build.lib.syntax.SkylarkSignatureProcessor;
+import com.google.devtools.build.lib.syntax.SkylarkUtils;
+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 com.google.devtools.build.lib.util.Preconditions;
+import com.google.protobuf.TextFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+
+/**
+ * A helper class to provide an easier API for Skylark rule definitions.
+ */
+public class SkylarkRuleClassFunctions {
+
+ // TODO(bazel-team): Copied from ConfiguredRuleClassProvider for the transition from built-in
+ // rules to skylark extensions. Using the same instance would require a large refactoring.
+ // If we don't want to support old built-in rules and Skylark simultaneously
+ // (except for transition phase) it's probably OK.
+ private static final LoadingCache<String, Label> labelCache =
+ CacheBuilder.newBuilder().build(new CacheLoader<String, Label>() {
+ @Override
+ public Label load(String from) throws Exception {
+ try {
+ return Label.parseAbsolute(from, false);
+ } catch (LabelSyntaxException e) {
+ throw new Exception(from);
+ }
+ }
+ });
+
+ // TODO(bazel-team): Remove the code duplication (BaseRuleClasses and this class).
+ /** Parent rule class for non-executable non-test Skylark rules. */
+ public static final RuleClass baseRule =
+ BaseRuleClasses.commonCoreAndSkylarkAttributes(
+ PlatformSemantics.platformAttributes(
+ BaseRuleClasses.nameAttribute(
+ new RuleClass.Builder("$base_rule", RuleClassType.ABSTRACT, true))
+ .add(attr("expect_failure", STRING))))
+ .build();
+
+ /** Parent rule class for executable non-test Skylark rules. */
+ public static final RuleClass binaryBaseRule =
+ new RuleClass.Builder("$binary_base_rule", RuleClassType.ABSTRACT, true, baseRule)
+ .add(attr("args", STRING_LIST))
+ .add(attr("output_licenses", LICENSE))
+ .build();
+
+ /** Parent rule class for test Skylark rules. */
+ public static final RuleClass getTestBaseRule(String toolsRepository) {
+ return new RuleClass.Builder("$test_base_rule", RuleClassType.ABSTRACT, true, baseRule)
+ .requiresConfigurationFragments(TestConfiguration.class)
+ .add(
+ attr("size", STRING)
+ .value("medium")
+ .taggable()
+ .nonconfigurable("used in loading phase rule validation logic"))
+ .add(
+ attr("timeout", STRING)
+ .taggable()
+ .nonconfigurable("used in loading phase rule validation logic")
+ .value(
+ new Attribute.ComputedDefault() {
+ @Override
+ public Object getDefault(AttributeMap rule) {
+ TestSize size = TestSize.getTestSize(rule.get("size", Type.STRING));
+ if (size != null) {
+ String timeout = size.getDefaultTimeout().toString();
+ if (timeout != null) {
+ return timeout;
+ }
+ }
+ return "illegal";
+ }
+ }))
+ .add(
+ attr("flaky", BOOLEAN)
+ .value(false)
+ .taggable()
+ .nonconfigurable("taggable - called in Rule.getRuleTags"))
+ .add(attr("shard_count", INTEGER).value(-1))
+ .add(
+ attr("local", BOOLEAN)
+ .value(false)
+ .taggable()
+ .nonconfigurable(
+ "policy decision: this should be consistent across configurations"))
+ .add(attr("args", STRING_LIST))
+ // Input files for every test action
+ .add(
+ attr("$test_runtime", LABEL_LIST)
+ .cfg(HOST)
+ .value(
+ ImmutableList.of(
+ labelCache.getUnchecked(toolsRepository + "//tools/test:runtime"))))
+ // Input files for test actions collecting code coverage
+ .add(
+ attr("$coverage_support", LABEL)
+ .cfg(HOST)
+ .value(labelCache.getUnchecked("//tools/defaults:coverage_support")))
+ // Used in the one-per-build coverage report generation action.
+ .add(
+ attr("$coverage_report_generator", LABEL)
+ .cfg(HOST)
+ .value(labelCache.getUnchecked("//tools/defaults:coverage_report_generator"))
+ .singleArtifact())
+ .add(attr(":run_under", LABEL).cfg(DATA).value(RUN_UNDER))
+ .build();
+ }
+
+ @SkylarkSignature(
+ name = "struct",
+ returnType = Info.class,
+ doc =
+ "Creates an immutable struct using the keyword arguments as attributes. It is used to "
+ + "group 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."),
+ useLocation = true
+ )
+ private static final Provider struct = NativeProvider.STRUCT;
+
+ @SkylarkSignature(
+ name = "DefaultInfo",
+ returnType = Provider.class,
+ doc =
+ "A provider that is provided by every rule, even if it is not returned explicitly. "
+ + "A <code>DefaultInfo</code> accepts the following parameters:"
+ + "<ul>"
+ + "<li><code>files</code></li>"
+ + "<li><code>runfiles</code></li>"
+ + "<li><code>data_runfiles</code></li>"
+ + "<li><code>default_runfiles</code></li>"
+ + "</ul>"
+ + "Each instance of the default provider contains the following standard "
+ + "fields: "
+ + "<ul>"
+ + "<li><code>files</code></li>"
+ + "<li><code>files_to_run</code></li>"
+ + "<li><code>data_runfiles</code></li>"
+ + "<li><code>default_runfiles</code></li>"
+ + "</ul>"
+ )
+ private static final Provider defaultInfo = DefaultProvider.SKYLARK_CONSTRUCTOR;
+
+ @SkylarkSignature(
+ name = "OutputGroupInfo",
+ returnType = Provider.class,
+ doc =
+ "Provides information about output groups the rule provides.<br>"
+ + "Instantiate this provider with <br>"
+ + "<pre class=language-python>"
+ + "OutputGroupInfo(group1 = &lt;files&gt;, group2 = &lt;files&gt;...)</pre>"
+ + "See <a href=\"../rules.html#output-groups\">Output Groups</a> for more information"
+ )
+ private static final Provider outputGroupInfo = OutputGroupProvider.SKYLARK_CONSTRUCTOR;
+
+ // TODO(bazel-team): Move to a "testing" namespace module. Normally we'd pass an objectType
+ // to @SkylarkSignature to do this, but that doesn't work here because we're exposing an already-
+ // configured BaseFunction, rather than defining a new BuiltinFunction. This should wait for
+ // better support from the Skylark/Java interface, or perhaps support for first-class modules.
+ @SkylarkSignature(
+ name = "Actions",
+ returnType = SkylarkProvider.class,
+ doc =
+ "<i>(Note: This is a provider type. Don't instantiate it yourself; use it to retrieve a "
+ + "provider object from a <a href=\"Target.html\">Target</a>.)</i>"
+ + "<br/><br/>"
+ + "Provides access to the <a href=\"Action.html\">actions</a> generated by a rule. "
+ + "There is one field, <code>by_file</code>, which is a dictionary from an output "
+ + "of the rule to its corresponding generating action. "
+ + "<br/><br/>"
+ + "This is designed for testing rules, and should not be accessed outside "
+ + "of test logic. This provider is only available for targets generated by rules"
+ + " that have <a href=\"globals.html#rule._skylark_testable\">_skylark_testable</a> "
+ + "set to <code>True</code>."
+ )
+ private static final Provider actions = ActionsProvider.SKYLARK_CONSTRUCTOR;
+
+ @SkylarkSignature(
+ name = "provider",
+ returnType = Provider.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)\n"
+ + "print(d.x + d.y) # prints 5</pre>",
+ parameters = {
+ @Param(
+ name = "doc",
+ type = String.class,
+ defaultValue = "''",
+ doc =
+ "A description of the provider that can be extracted by documentation generating tools."
+ )
+ },
+ useLocation = true
+ )
+ private static final BuiltinFunction provider =
+ new BuiltinFunction("provider") {
+ public Provider invoke(String doc, Location location) {
+ return new SkylarkProvider(
+ "<no name>", // name is set on export.
+ location);
+ }
+ };
+
+ // TODO(bazel-team): implement attribute copy and other rule properties
+ @SkylarkSignature(
+ name = "rule",
+ doc =
+ "Creates a new rule. Store it in a global value, so that it can be loaded and called "
+ + "from BUILD files.",
+ returnType = BaseFunction.class,
+ parameters = {
+ @Param(
+ name = "implementation",
+ type = BaseFunction.class,
+ doc =
+ "the function implementing this rule, must have exactly one parameter: "
+ + "<a href=\"ctx.html\">ctx</a>. The function is called during the analysis "
+ + "phase for each instance of the rule. It can access the attributes "
+ + "provided by the user. It must create actions to generate all the declared "
+ + "outputs."
+ ),
+ @Param(
+ name = "test",
+ type = Boolean.class,
+ defaultValue = "False",
+ doc =
+ "Whether this rule is a test rule. "
+ + "If True, the rule must end with <code>_test</code> (otherwise it must "
+ + "not), and there must be an action that generates "
+ + "<code>ctx.outputs.executable</code>."
+ ),
+ @Param(
+ name = "attrs",
+ type = SkylarkDict.class,
+ noneable = true,
+ defaultValue = "None",
+ doc =
+ "dictionary to declare all the attributes of the rule. It maps from an attribute "
+ + "name to an attribute object (see <a href=\"attr.html\">attr</a> module). "
+ + "Attributes starting with <code>_</code> are private, and can be used to "
+ + "add an implicit dependency on a label. The attribute <code>name</code> is "
+ + "implicitly added and must not be specified. Attributes "
+ + "<code>visibility</code>, <code>deprecation</code>, <code>tags</code>, "
+ + "<code>testonly</code>, and <code>features</code> are implicitly added and "
+ + "cannot be overriden."
+ ),
+ // TODO(bazel-team): need to give the types of these builtin attributes
+ @Param(
+ name = "outputs",
+ type = SkylarkDict.class,
+ callbackEnabled = true,
+ noneable = true,
+ defaultValue = "None",
+ doc =
+ "outputs of this rule. "
+ + "It is a dictionary mapping from string to a template name. "
+ + "For example: <code>{\"ext\": \"%{name}.ext\"}</code>. <br>"
+ + "The dictionary key becomes an attribute in <code>ctx.outputs</code>. "
+ + "Similar to computed dependency rule attributes, you can also specify the "
+ + "name of a function that returns the dictionary. This function can access "
+ + "all rule attributes that are listed as parameters in its function "
+ + "signature. For example, <code>outputs = _my_func</code> with "
+ + "<code>def _my_func(srcs, deps):</code> has access to the attributes "
+ + "'srcs' and 'deps' (if defined)."
+ ),
+ @Param(
+ name = "executable",
+ type = Boolean.class,
+ defaultValue = "False",
+ doc =
+ "whether this rule is marked as executable or not. If True, "
+ + "there must be an action that generates "
+ + "<code>ctx.outputs.executable</code>."
+ ),
+ @Param(
+ name = "output_to_genfiles",
+ type = Boolean.class,
+ defaultValue = "False",
+ doc =
+ "If true, the files will be generated in the genfiles directory instead of the "
+ + "bin directory. Unless you need it for compatibility with existing rules "
+ + "(e.g. when generating header files for C++), do not set this flag."
+ ),
+ @Param(
+ name = "fragments",
+ type = SkylarkList.class,
+ generic1 = String.class,
+ defaultValue = "[]",
+ doc =
+ "List of names of configuration fragments that the rule requires "
+ + "in target configuration."
+ ),
+ @Param(
+ name = "host_fragments",
+ type = SkylarkList.class,
+ generic1 = String.class,
+ defaultValue = "[]",
+ doc =
+ "List of names of configuration fragments that the rule requires "
+ + "in host configuration."
+ ),
+ @Param(
+ name = "_skylark_testable",
+ type = Boolean.class,
+ defaultValue = "False",
+ doc =
+ "<i>(Experimental)</i><br/><br/>"
+ + "If true, this rule will expose its actions for inspection by rules that "
+ + "depend on it via an <a href=\"globals.html#Actions\">Actions</a> "
+ + "provider. The provider is also available to the rule itself by calling "
+ + "<a href=\"ctx.html#created_actions\">ctx.created_actions()</a>."
+ + "<br/><br/>"
+ + "This should only be used for testing the analysis-time behavior of "
+ + "Skylark rules. This flag may be removed in the future."
+ ),
+ @Param(
+ name = "toolchains",
+ type = SkylarkList.class,
+ generic1 = String.class,
+ defaultValue = "[]",
+ doc =
+ "<i>(Experimental)</i><br/><br/>"
+ + "If set, the set of toolchains this rule requires. Toolchains will be "
+ + "found by checking the current platform, and provided to the rule "
+ + "implementation via <code>ctx.toolchain</code>."
+ ),
+ @Param(
+ name = "doc",
+ type = String.class,
+ defaultValue = "''",
+ doc = "A description of the rule that can be extracted by documentation generating tools."
+ )
+ },
+ useAst = true,
+ useEnvironment = true
+ )
+ private static final BuiltinFunction rule =
+ new BuiltinFunction("rule") {
+ @SuppressWarnings({"rawtypes", "unchecked"}) // castMap produces
+ // an Attribute.Builder instead of a Attribute.Builder<?> but it's OK.
+ public BaseFunction invoke(
+ BaseFunction implementation,
+ Boolean test,
+ Object attrs,
+ Object implicitOutputs,
+ Boolean executable,
+ Boolean outputToGenfiles,
+ SkylarkList fragments,
+ SkylarkList hostFragments,
+ Boolean skylarkTestable,
+ SkylarkList<String> toolchains,
+ String doc,
+ FuncallExpression ast,
+ Environment funcallEnv)
+ throws EvalException, ConversionException {
+ funcallEnv.checkLoadingOrWorkspacePhase("rule", ast.getLocation());
+ RuleClassType type = test ? RuleClassType.TEST : RuleClassType.NORMAL;
+ RuleClass parent =
+ test
+ ? getTestBaseRule(SkylarkUtils.getToolsRepository(funcallEnv))
+ : (executable ? binaryBaseRule : baseRule);
+
+ // We'll set the name later, pass the empty string for now.
+ RuleClass.Builder builder = new RuleClass.Builder("", type, true, parent);
+ ImmutableList<Pair<String, SkylarkAttr.Descriptor>> attributes =
+ attrObjectToAttributesList(attrs, ast);
+
+ if (skylarkTestable) {
+ builder.setSkylarkTestable();
+ }
+
+ if (executable || test) {
+ addAttribute(
+ ast.getLocation(),
+ builder,
+ attr("$is_executable", BOOLEAN)
+ .value(true)
+ .nonconfigurable("Called from RunCommand.isExecutable, which takes a Target")
+ .build());
+ builder.setOutputsDefaultExecutable();
+ }
+
+ if (implicitOutputs != Runtime.NONE) {
+ if (implicitOutputs instanceof BaseFunction) {
+ BaseFunction func = (BaseFunction) implicitOutputs;
+ SkylarkCallbackFunction callback = new SkylarkCallbackFunction(func, ast, funcallEnv);
+ builder.setImplicitOutputsFunction(
+ new SkylarkImplicitOutputsFunctionWithCallback(callback, ast.getLocation()));
+ } else {
+ builder.setImplicitOutputsFunction(
+ new SkylarkImplicitOutputsFunctionWithMap(
+ ImmutableMap.copyOf(
+ castMap(
+ implicitOutputs,
+ String.class,
+ String.class,
+ "implicit outputs of the rule class"))));
+ }
+ }
+
+ if (outputToGenfiles) {
+ builder.setOutputToGenfiles();
+ }
+
+ builder.requiresConfigurationFragmentsBySkylarkModuleName(
+ fragments.getContents(String.class, "fragments"));
+ builder.requiresHostConfigurationFragmentsBySkylarkModuleName(
+ hostFragments.getContents(String.class, "host_fragments"));
+ builder.setConfiguredTargetFunction(implementation);
+ builder.setRuleDefinitionEnvironment(funcallEnv);
+ builder.addRequiredToolchains(collectToolchainLabels(toolchains, ast));
+
+ return new RuleFunction(builder, type, attributes, ast.getLocation());
+ }
+ };
+
+ protected static ImmutableList<Pair<String, Descriptor>> attrObjectToAttributesList(
+ Object attrs, FuncallExpression ast) throws EvalException {
+ ImmutableList.Builder<Pair<String, Descriptor>> attributes = ImmutableList.builder();
+
+ if (attrs != Runtime.NONE) {
+ for (Map.Entry<String, Descriptor> attr :
+ castMap(attrs, String.class, Descriptor.class, "attrs").entrySet()) {
+ Descriptor attrDescriptor = attr.getValue();
+ AttributeValueSource source = attrDescriptor.getValueSource();
+ String attrName = source.convertToNativeName(attr.getKey(), ast.getLocation());
+ attributes.add(Pair.of(attrName, attrDescriptor));
+ }
+ }
+ return attributes.build();
+ }
+
+ 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);
+ }
+ }
+
+ private static ImmutableList<Label> collectToolchainLabels(
+ Iterable<String> rawLabels, FuncallExpression ast) throws EvalException {
+ ImmutableList.Builder<Label> requiredToolchains = new ImmutableList.Builder<>();
+ for (String rawLabel : rawLabels) {
+ try {
+ Label toolchainLabel = Label.parseAbsolute(rawLabel);
+ requiredToolchains.add(toolchainLabel);
+ } catch (LabelSyntaxException e) {
+ throw new EvalException(
+ ast.getLocation(),
+ String.format("Unable to parse toolchain %s: %s", rawLabel, e.getMessage()),
+ e);
+ }
+ }
+
+ return requiredToolchains.build();
+ }
+
+ @SkylarkSignature(
+ name = "aspect",
+ doc =
+ "Creates a new aspect. The result of this function must be stored in a global value. "
+ + "Please see the <a href=\"../aspects.md\">introduction to Aspects</a> for more "
+ + "details.",
+ returnType = SkylarkAspect.class,
+ parameters = {
+ @Param(
+ name = "implementation",
+ type = BaseFunction.class,
+ doc =
+ "the function implementing this aspect. Must have two parameters: "
+ + "<a href=\"Target.html\">Target</a> (the target to which the aspect is "
+ + "applied) and <a href=\"ctx.html\">ctx</a>. Attributes of the target are "
+ + "available via ctx.rule field. The function is called during the analysis "
+ + "phase for each application of an aspect to a target."
+ ),
+ @Param(
+ name = "attr_aspects",
+ type = SkylarkList.class,
+ generic1 = String.class,
+ defaultValue = "[]",
+ doc =
+ "List of attribute names. The aspect propagates along dependencies specified "
+ + "by attributes of a target with this name. The list can also contain a single "
+ + "string '*': in that case aspect propagates along all dependencies of a target."
+ ),
+ @Param(
+ name = "attrs",
+ type = SkylarkDict.class,
+ noneable = true,
+ defaultValue = "None",
+ doc =
+ "dictionary to declare all the attributes of the aspect. "
+ + "It maps from an attribute name to an attribute object "
+ + "(see <a href=\"attr.html\">attr</a> module). "
+ + "Aspect attributes are available to implementation function as fields of ctx "
+ + "parameter. Implicit attributes starting with <code>_</code> must have default "
+ + "values, and have type <code>label</code> or <code>label_list</code>. "
+ + "Explicit attributes must have type <code>string</code>, and must use the "
+ + "<code>values</code> restriction. If explicit attributes are present, the "
+ + "aspect can only be used with rules that have attributes of the same name and "
+ + "type, with valid values."
+ ),
+ @Param(
+ name = "required_aspect_providers",
+ type = SkylarkList.class,
+ defaultValue = "[]",
+ // todo(dslomov): Document once it works.
+ doc = "<not available>"
+ ),
+ @Param(
+ name = "provides",
+ type = SkylarkList.class,
+ defaultValue = "[]",
+ // todo(dslomov): Document once it works.
+ doc = "<not available>"
+ ),
+ @Param(
+ name = "fragments",
+ type = SkylarkList.class,
+ generic1 = String.class,
+ defaultValue = "[]",
+ doc =
+ "List of names of configuration fragments that the aspect requires "
+ + "in target configuration."
+ ),
+ @Param(
+ name = "host_fragments",
+ type = SkylarkList.class,
+ generic1 = String.class,
+ defaultValue = "[]",
+ doc =
+ "List of names of configuration fragments that the aspect requires "
+ + "in host configuration."
+ ),
+ @Param(
+ name = "toolchains",
+ type = SkylarkList.class,
+ generic1 = String.class,
+ defaultValue = "[]",
+ doc =
+ "<i>(Experimental)</i><br/><br/>"
+ + "If set, the set of toolchains this rule requires. Toolchains will be "
+ + "found by checking the current platform, and provided to the rule "
+ + "implementation via <code>ctx.toolchain</code>."
+ ),
+ @Param(
+ name = "doc",
+ type = String.class,
+ defaultValue = "''",
+ doc = "A description of the aspect that can be extracted by documentation generating tools."
+ )
+ },
+ useEnvironment = true,
+ useAst = true
+ )
+ private static final BuiltinFunction aspect =
+ new BuiltinFunction("aspect") {
+ public SkylarkAspect invoke(
+ BaseFunction implementation,
+ SkylarkList attributeAspects,
+ Object attrs,
+ SkylarkList requiredAspectProvidersArg,
+ SkylarkList providesArg,
+ SkylarkList fragments,
+ SkylarkList hostFragments,
+ SkylarkList<String> toolchains,
+ String doc,
+ FuncallExpression ast,
+ Environment funcallEnv)
+ throws EvalException {
+ ImmutableList.Builder<String> attrAspects = ImmutableList.builder();
+ for (Object attributeAspect : attributeAspects) {
+ String attrName = STRING.convert(attributeAspect, "attr_aspects");
+
+ if (attrName.equals("*") && attributeAspects.size() != 1) {
+ throw new EvalException(
+ ast.getLocation(), "'*' must be the only string in 'attr_aspects' list");
+ }
+
+ if (!attrName.startsWith("_")) {
+ attrAspects.add(attrName);
+ } else {
+ // Implicit attribute names mean either implicit or late-bound attributes
+ // (``$attr`` or ``:attr``). Depend on both.
+ attrAspects.add(
+ AttributeValueSource.COMPUTED_DEFAULT.convertToNativeName(attrName, location));
+ attrAspects.add(
+ AttributeValueSource.LATE_BOUND.convertToNativeName(attrName, location));
+ }
+ }
+
+ ImmutableList<Pair<String, SkylarkAttr.Descriptor>> descriptors =
+ attrObjectToAttributesList(attrs, ast);
+ ImmutableList.Builder<Attribute> attributes = ImmutableList.builder();
+ ImmutableSet.Builder<String> requiredParams = ImmutableSet.builder();
+ for (Pair<String, Descriptor> nameDescriptorPair : descriptors) {
+ String nativeName = nameDescriptorPair.first;
+ boolean hasDefault = nameDescriptorPair.second.hasDefault();
+ Attribute attribute = nameDescriptorPair.second.build(nameDescriptorPair.first);
+ if (attribute.getType() == Type.STRING
+ && ((String) attribute.getDefaultValue(null)).isEmpty()) {
+ hasDefault = false; // isValueSet() is always true for attr.string.
+ }
+ if (!Attribute.isImplicit(nativeName)) {
+ if (!attribute.checkAllowedValues() || attribute.getType() != Type.STRING) {
+ throw new EvalException(
+ ast.getLocation(),
+ String.format(
+ "Aspect parameter attribute '%s' must have type 'string' and use the "
+ + "'values' restriction.",
+ nativeName));
+ }
+ if (!hasDefault) {
+ requiredParams.add(nativeName);
+ } else {
+ PredicateWithMessage<Object> allowed = attribute.getAllowedValues();
+ Object defaultVal = attribute.getDefaultValue(null);
+ if (!allowed.apply(defaultVal)) {
+ throw new EvalException(
+ ast.getLocation(),
+ String.format(
+ "Aspect parameter attribute '%s' has a bad default value: %s",
+ nativeName, allowed.getErrorReason(defaultVal)));
+ }
+ }
+ } else if (!hasDefault) { // Implicit attribute
+ String skylarkName = "_" + nativeName.substring(1);
+ throw new EvalException(
+ ast.getLocation(),
+ String.format("Aspect attribute '%s' has no default value.", skylarkName));
+ }
+ attributes.add(attribute);
+ }
+
+ for (Object o : providesArg) {
+ if (!SkylarkAttr.isProvider(o)) {
+ throw new EvalException(
+ ast.getLocation(),
+ String.format(
+ "Illegal argument: element in 'provides' is of unexpected type. "
+ + "Should be list of providers, but got %s. ",
+ EvalUtils.getDataTypeName(o, true)));
+ }
+ }
+
+ return new SkylarkAspect(
+ implementation,
+ attrAspects.build(),
+ attributes.build(),
+ SkylarkAttr.buildProviderPredicate(
+ requiredAspectProvidersArg, "required_aspect_providers", ast.getLocation()),
+ SkylarkAttr.getSkylarkProviderIdentifiers(providesArg, ast.getLocation()),
+ requiredParams.build(),
+ ImmutableSet.copyOf(fragments.getContents(String.class, "fragments")),
+ ImmutableSet.copyOf(hostFragments.getContents(String.class, "host_fragments")),
+ collectToolchainLabels(toolchains, ast),
+ funcallEnv);
+ }
+ };
+
+ /** The implementation for the magic function "rule" that creates Skylark rule classes */
+ public static final class RuleFunction extends BaseFunction implements SkylarkExportable {
+ private RuleClass.Builder builder;
+
+ 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
+ @SuppressWarnings("unchecked") // the magic hidden $pkg_context variable is guaranteed
+ // to be a PackageContext
+ public Object call(Object[] args, FuncallExpression ast, Environment env)
+ throws EvalException, InterruptedException, ConversionException {
+ env.checkLoadingPhase(getName(), ast.getLocation());
+ if (ruleClass == null) {
+ throw new EvalException(ast.getLocation(),
+ "Invalid rule class hasn't been exported by a Skylark file");
+ }
+
+ for (Attribute attribute : ruleClass.getAttributes()) {
+ // TODO(dslomov): If a Skylark parameter extractor is specified for this aspect, its
+ // attributes may not be required.
+ for (Map.Entry<String, ImmutableSet<String>> attrRequirements :
+ attribute.getRequiredAspectParameters().entrySet()) {
+ for (String required : attrRequirements.getValue()) {
+ if (!ruleClass.hasAttr(required, Type.STRING)) {
+ throw new EvalException(definitionLocation, String.format(
+ "Aspect %s requires rule %s to specify attribute '%s' with type string.",
+ attrRequirements.getKey(),
+ ruleClass.getName(),
+ required));
+ }
+ }
+ }
+ }
+
+ BuildLangTypedAttributeValuesMap attributeValues =
+ new BuildLangTypedAttributeValuesMap((Map<String, Object>) args[0]);
+ try {
+ PackageContext pkgContext = (PackageContext) env.lookup(PackageFactory.PKG_CONTEXT);
+ if (pkgContext == null) {
+ throw new EvalException(ast.getLocation(),
+ "Cannot instantiate a rule when loading a .bzl file. Rules can only be called from "
+ + "a BUILD file (possibly via a macro).");
+ }
+ RuleFactory.createAndAddRule(
+ pkgContext,
+ ruleClass,
+ attributeValues,
+ ast,
+ env,
+ pkgContext.getAttributeContainerFactory().apply(ruleClass));
+ return Runtime.NONE;
+ } catch (InvalidRuleException | NameConflictException e) {
+ throw new EvalException(ast.getLocation(), e.getMessage());
+ }
+ }
+
+ /**
+ * Export a RuleFunction from a Skylark file with a given name.
+ */
+ 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)) {
+ 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) {
+ SkylarkAttr.Descriptor descriptor = attribute.getSecond();
+
+ addAttribute(definitionLocation, builder,
+ descriptor.build(attribute.getFirst()));
+ }
+ this.ruleClass = builder.build(ruleClassName);
+
+ this.builder = null;
+ this.attributes = null;
+ }
+
+ @VisibleForTesting
+ public RuleClass getRuleClass() {
+ Preconditions.checkState(ruleClass != null && builder == null);
+ return ruleClass;
+ }
+
+ @Override
+ public boolean isExported() {
+ return skylarkLabel != null;
+ }
+
+ @Override
+ public void repr(SkylarkPrinter printer) {
+ printer.append("<rule>");
+ }
+ }
+
+ /**
+ * All classes of values that need special processing after they are exported from an extension
+ * file.
+ *
+ * <p>Order in list is significant: all {@link SkylarkAspect}s need to be exported before {@link
+ * RuleFunction}s etc.
+ */
+ private static final ImmutableList<Class<? extends SkylarkExportable>> EXPORTABLES =
+ ImmutableList.of(SkylarkProvider.class, SkylarkAspect.class, RuleFunction.class);
+
+ @SkylarkSignature(
+ name = "Label",
+ doc =
+ "Creates a Label referring to a BUILD target. Use "
+ + "this function only when you want to give a default value for the label attributes. "
+ + "The argument must refer to an absolute label. "
+ + "Example: <br><pre class=language-python>Label(\"//tools:default\")</pre>",
+ returnType = Label.class,
+ objectType = Label.class,
+ parameters = {
+ @Param(name = "label_string", type = String.class, doc = "the label string."),
+ @Param(
+ name = "relative_to_caller_repository",
+ type = Boolean.class,
+ defaultValue = "False",
+ named = true,
+ positional = false,
+ doc =
+ "whether the label should be resolved relative to the label of the file this "
+ + "function is called from."
+ )
+ },
+ useLocation = true,
+ useEnvironment = true
+ )
+ private static final BuiltinFunction label =
+ new BuiltinFunction("Label") {
+ @SuppressWarnings({"unchecked", "unused"})
+ public Label invoke(
+ String labelString, Boolean relativeToCallerRepository, Location loc, Environment env)
+ throws EvalException {
+ Label parentLabel = null;
+ if (relativeToCallerRepository) {
+ parentLabel = env.getCallerLabel();
+ } else {
+ parentLabel = env.getGlobals().getTransitiveLabel();
+ }
+ try {
+ if (parentLabel != null) {
+ LabelValidator.parseAbsoluteLabel(labelString);
+ labelString = parentLabel.getRelative(labelString).getUnambiguousCanonicalForm();
+ }
+ return labelCache.get(labelString);
+ } catch (LabelValidator.BadLabelException | LabelSyntaxException | ExecutionException e) {
+ throw new EvalException(loc, "Illegal absolute label syntax: " + labelString);
+ }
+ }
+ };
+
+ // We want the Label ctor to show up under the Label documentation, but to be a "global
+ // function." Thus, we create a global Label object here, which just points to the Skylark
+ // function above.
+ @SkylarkSignature(name = "Label",
+ documented = false)
+ private static final BuiltinFunction globalLabel = label;
+
+ @SkylarkSignature(
+ name = "FileType",
+ doc =
+ "Deprecated. Creates a file filter from a list of strings. For example, to match "
+ + "files ending with .cc or .cpp, use: "
+ + "<pre class=language-python>FileType([\".cc\", \".cpp\"])</pre>",
+ returnType = SkylarkFileType.class,
+ objectType = SkylarkFileType.class,
+ parameters = {
+ @Param(
+ name = "types",
+ type = SkylarkList.class,
+ generic1 = String.class,
+ defaultValue = "[]",
+ doc = "a list of the accepted file extensions."
+ )
+ }
+ )
+ private static final BuiltinFunction fileType =
+ new BuiltinFunction("FileType") {
+ public SkylarkFileType invoke(SkylarkList types) throws EvalException {
+ return SkylarkFileType.of(types.getContents(String.class, "types"));
+ }
+ };
+
+ // We want the FileType ctor to show up under the FileType documentation, but to be a "global
+ // function." Thus, we create a global FileType object here, which just points to the Skylark
+ // function above.
+ @SkylarkSignature(name = "FileType",
+ documented = false)
+ private static final BuiltinFunction globalFileType = fileType;
+
+ @SkylarkSignature(
+ name = "to_proto",
+ doc =
+ "Creates a text message from the struct parameter. This method only works if all "
+ + "struct elements (recursively) are strings, ints, booleans, other structs or a "
+ + "list of these types. Quotes and new lines in strings are escaped. "
+ + "Keys are iterated in the sorted order. "
+ + "Examples:<br><pre class=language-python>"
+ + "struct(key=123).to_proto()\n# key: 123\n\n"
+ + "struct(key=True).to_proto()\n# key: true\n\n"
+ + "struct(key=[1, 2, 3]).to_proto()\n# key: 1\n# key: 2\n# key: 3\n\n"
+ + "struct(key='text').to_proto()\n# key: \"text\"\n\n"
+ + "struct(key=struct(inner_key='text')).to_proto()\n"
+ + "# key {\n# inner_key: \"text\"\n# }\n\n"
+ + "struct(key=[struct(inner_key=1), struct(inner_key=2)]).to_proto()\n"
+ + "# key {\n# inner_key: 1\n# }\n# key {\n# inner_key: 2\n# }\n\n"
+ + "struct(key=struct(inner_key=struct(inner_inner_key='text'))).to_proto()\n"
+ + "# key {\n# inner_key {\n# inner_inner_key: \"text\"\n# }\n# }\n</pre>",
+ objectType = Info.class,
+ returnType = String.class,
+ parameters = {
+ // TODO(bazel-team): shouldn't we accept any ClassObject?
+ @Param(name = "self", type = Info.class, doc = "this struct.")
+ },
+ useLocation = true
+ )
+ private static final BuiltinFunction toProto =
+ new BuiltinFunction("to_proto") {
+ public String invoke(Info self, Location loc) throws EvalException {
+ StringBuilder sb = new StringBuilder();
+ printProtoTextMessage(self, sb, 0, loc);
+ return sb.toString();
+ }
+
+ private void printProtoTextMessage(
+ ClassObject object, StringBuilder sb, int indent, Location loc) throws EvalException {
+ // For determinism sort the keys alphabetically
+ List<String> keys = new ArrayList<>(object.getKeys());
+ Collections.sort(keys);
+ for (String key : keys) {
+ printProtoTextMessage(key, object.getValue(key), sb, indent, loc);
+ }
+ }
+
+ private void printProtoTextMessage(
+ String key, Object value, StringBuilder sb, int indent, Location loc, String container)
+ throws EvalException {
+ if (value instanceof ClassObject) {
+ print(sb, key + " {", indent);
+ printProtoTextMessage((ClassObject) value, sb, indent + 1, loc);
+ print(sb, "}", indent);
+ } else if (value instanceof String) {
+ print(
+ sb,
+ key + ": \"" + escapeDoubleQuotesAndBackslashesAndNewlines((String) value) + "\"",
+ indent);
+ } else if (value instanceof Integer) {
+ print(sb, key + ": " + value, indent);
+ } else if (value instanceof Boolean) {
+ // We're relying on the fact that Java converts Booleans to Strings in the same way
+ // as the protocol buffers do.
+ print(sb, key + ": " + value, indent);
+ } else {
+ throw new EvalException(
+ loc,
+ "Invalid text format, expected a struct, a string, a bool, or an int but got a "
+ + EvalUtils.getDataTypeName(value)
+ + " for "
+ + container
+ + " '"
+ + key
+ + "'");
+ }
+ }
+
+ private void printProtoTextMessage(
+ String key, Object value, StringBuilder sb, int indent, Location loc)
+ throws EvalException {
+ if (value instanceof SkylarkList) {
+ for (Object item : ((SkylarkList) value)) {
+ // TODO(bazel-team): There should be some constraint on the fields of the structs
+ // in the same list but we ignore that for now.
+ printProtoTextMessage(key, item, sb, indent, loc, "list element in struct field");
+ }
+ } else {
+ printProtoTextMessage(key, value, sb, indent, loc, "struct field");
+ }
+ }
+
+ private void print(StringBuilder sb, String text, int indent) {
+ for (int i = 0; i < indent; i++) {
+ sb.append(" ");
+ }
+ sb.append(text);
+ sb.append("\n");
+ }
+ };
+
+ /**
+ * Escapes the given string for use in proto/JSON string.
+ *
+ * <p>This escapes double quotes, backslashes, and newlines.
+ */
+ private static String escapeDoubleQuotesAndBackslashesAndNewlines(String string) {
+ return TextFormat.escapeDoubleQuotesAndBackslashes(string).replace("\n", "\\n");
+ }
+
+ @SkylarkSignature(
+ name = "to_json",
+ doc =
+ "Creates a JSON string from the struct parameter. This method only works if all "
+ + "struct elements (recursively) are strings, ints, booleans, other structs or a "
+ + "list of these types. Quotes and new lines in strings are escaped. "
+ + "Examples:<br><pre class=language-python>"
+ + "struct(key=123).to_json()\n# {\"key\":123}\n\n"
+ + "struct(key=True).to_json()\n# {\"key\":true}\n\n"
+ + "struct(key=[1, 2, 3]).to_json()\n# {\"key\":[1,2,3]}\n\n"
+ + "struct(key='text').to_json()\n# {\"key\":\"text\"}\n\n"
+ + "struct(key=struct(inner_key='text')).to_json()\n"
+ + "# {\"key\":{\"inner_key\":\"text\"}}\n\n"
+ + "struct(key=[struct(inner_key=1), struct(inner_key=2)]).to_json()\n"
+ + "# {\"key\":[{\"inner_key\":1},{\"inner_key\":2}]}\n\n"
+ + "struct(key=struct(inner_key=struct(inner_inner_key='text'))).to_json()\n"
+ + "# {\"key\":{\"inner_key\":{\"inner_inner_key\":\"text\"}}}\n</pre>",
+ objectType = Info.class,
+ returnType = String.class,
+ parameters = {
+ // TODO(bazel-team): shouldn't we accept any ClassObject?
+ @Param(name = "self", type = Info.class, doc = "this struct.")
+ },
+ useLocation = true
+ )
+ private static final BuiltinFunction toJson =
+ new BuiltinFunction("to_json") {
+ public String invoke(Info self, Location loc) throws EvalException {
+ StringBuilder sb = new StringBuilder();
+ printJson(self, sb, loc, "struct field", null);
+ return sb.toString();
+ }
+
+ private void printJson(
+ Object value, StringBuilder sb, Location loc, String container, String key)
+ throws EvalException {
+ if (value == Runtime.NONE) {
+ sb.append("null");
+ } else if (value instanceof ClassObject) {
+ sb.append("{");
+
+ String join = "";
+ for (String subKey : ((ClassObject) value).getKeys()) {
+ sb.append(join);
+ join = ",";
+ sb.append("\"");
+ sb.append(subKey);
+ sb.append("\":");
+ printJson(((ClassObject) value).getValue(subKey), sb, loc, "struct field", subKey);
+ }
+ sb.append("}");
+ } else if (value instanceof List) {
+ sb.append("[");
+ String join = "";
+ for (Object item : ((List) value)) {
+ sb.append(join);
+ join = ",";
+ printJson(item, sb, loc, "list element in struct field", key);
+ }
+ sb.append("]");
+ } else if (value instanceof String) {
+ sb.append("\"");
+ sb.append(jsonEscapeString((String) value));
+ sb.append("\"");
+ } else if (value instanceof Integer || value instanceof Boolean) {
+ sb.append(value);
+ } else {
+ String errorMessage =
+ "Invalid text format, expected a struct, a string, a bool, or an int "
+ + "but got a "
+ + EvalUtils.getDataTypeName(value)
+ + " for "
+ + container;
+ if (key != null) {
+ errorMessage += " '" + key + "'";
+ }
+ throw new EvalException(loc, errorMessage);
+ }
+ }
+
+ private String jsonEscapeString(String string) {
+ return escapeDoubleQuotesAndBackslashesAndNewlines(string)
+ .replace("\r", "\\r")
+ .replace("\t", "\\t");
+ }
+ };
+
+ @SkylarkSignature(name = "output_group",
+ documented = false, // TODO(dslomov): document.
+ objectType = TransitiveInfoCollection.class,
+ returnType = SkylarkNestedSet.class,
+ parameters = {
+ @Param(name = "self", type = TransitiveInfoCollection.class, doc =
+ "this target"
+ ),
+ @Param(name = "group_name", type = String.class, doc =
+ "Output group name"
+ )
+ }
+ )
+ private static final BuiltinFunction output_group = new BuiltinFunction("output_group") {
+ public SkylarkNestedSet invoke(TransitiveInfoCollection self, String group) {
+ OutputGroupProvider provider = OutputGroupProvider.get(self);
+ NestedSet<Artifact> result = provider != null
+ ? provider.getOutputGroup(group)
+ : NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER);
+ return SkylarkNestedSet.of(Artifact.class, result);
+ }
+ };
+
+ static {
+ SkylarkSignatureProcessor.configureSkylarkFunctions(SkylarkRuleClassFunctions.class);
+ }
+}