// 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.bazel.repository.skylark; import static com.google.devtools.build.lib.packages.Attribute.attr; 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.STRING; import static com.google.devtools.build.lib.syntax.Type.STRING_LIST; import com.google.devtools.build.lib.analysis.BaseRuleClasses; import com.google.devtools.build.lib.cmdline.LabelSyntaxException; import com.google.devtools.build.lib.packages.AttributeValueSource; 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.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.InvalidRuleException; import com.google.devtools.build.lib.rules.SkylarkAttr.Descriptor; import com.google.devtools.build.lib.skylarkinterface.Param; 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.EvalException; 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.SkylarkDict; import com.google.devtools.build.lib.syntax.SkylarkList; import com.google.devtools.build.lib.syntax.SkylarkSignatureProcessor; import java.util.Map; /** * The Skylark module containing the definition of {@code repository_rule} function to define a * skylark remote repository. */ public class SkylarkRepositoryModule { @SkylarkSignature( name = "repository_rule", doc = "Creates a new repository rule. Store it in a global value, so that it can be loaded and " + "called from the WORKSPACE file.", returnType = BaseFunction.class, parameters = { @Param( name = "implementation", type = BaseFunction.class, doc = "the function implementing this rule, has to have exactly one parameter: " + "<code><a href=\"repository_ctx.html\">repository_ctx</a></code>. The function " + "is called during loading phase for each instance of the rule." ), @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 to a file (a repository " + "rule cannot depend on a generated artifact). The attribute " + "<code>name</code> is implicitly added and must not be specified.", named = true, positional = false ), @Param( name = "local", type = Boolean.class, defaultValue = "False", doc = "Indicate that this rule fetches everything from the local system and should be " + "reevaluated at every fetch.", named = true, positional = false ), @Param( name = "environ", type = SkylarkList.class, generic1 = String.class, defaultValue = "[]", doc = "Provides a list of environment variable that this repository rule depends on. If" + " an environment variable in that list change, the repository will be refetched.", named = true, positional = false ) }, useAst = true, useEnvironment = true ) private static final BuiltinFunction repositoryRule = new BuiltinFunction("repository_rule") { @SuppressWarnings({"rawtypes", "unused"}) // an Attribute.Builder instead of a Attribute.Builder<?> but it's OK. public BaseFunction invoke( BaseFunction implementation, Object attrs, Boolean local, SkylarkList<String> environ, FuncallExpression ast, com.google.devtools.build.lib.syntax.Environment funcallEnv) throws EvalException { funcallEnv.checkLoadingOrWorkspacePhase("repository_rule", ast.getLocation()); // We'll set the name later, pass the empty string for now. Builder builder = new Builder("", RuleClassType.WORKSPACE, true); builder.addOrOverrideAttribute(attr("$local", BOOLEAN).defaultValue(local).build()); builder.addOrOverrideAttribute(attr("$environ", STRING_LIST) .defaultValue(environ).build()); BaseRuleClasses.nameAttribute(builder); BaseRuleClasses.commonCoreAndSkylarkAttributes(builder); builder.add(attr("expect_failure", STRING)); 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()); builder.addOrOverrideAttribute(attrDescriptor.build(attrName)); } } builder.setConfiguredTargetFunction(implementation); builder.setRuleDefinitionEnvironment(funcallEnv); builder.setWorkspaceOnly(); return new RepositoryRuleFunction(builder); } }; private static final class RepositoryRuleFunction extends BaseFunction { private final Builder builder; public RepositoryRuleFunction(Builder builder) { super("repository_rule", FunctionSignature.KWARGS); this.builder = builder; } @Override public Object call( Object[] args, FuncallExpression ast, com.google.devtools.build.lib.syntax.Environment env) throws EvalException, InterruptedException { String ruleClassName = ast.getFunction().getName(); try { RuleClass ruleClass = builder.build(ruleClassName); PackageContext context = PackageFactory.getContext(env, ast); @SuppressWarnings("unchecked") Map<String, Object> attributeValues = (Map<String, Object>) args[0]; return context .getBuilder() .externalPackageData() .createAndAddRepositoryRule( context.getBuilder(), ruleClass, null, attributeValues, ast); } catch (InvalidRuleException | NameConflictException | LabelSyntaxException e) { throw new EvalException(ast.getLocation(), e.getMessage()); } } } static { SkylarkSignatureProcessor.configureSkylarkFunctions(SkylarkRepositoryModule.class); } }