diff options
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java')
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java | 350 |
1 files changed, 350 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java new file mode 100644 index 0000000000..dfd6a9276d --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java @@ -0,0 +1,350 @@ +// Copyright 2014 Google Inc. 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.rules; + +import static com.google.devtools.build.lib.syntax.SkylarkFunction.castList; + +import com.google.devtools.build.lib.events.Location; +import com.google.devtools.build.lib.packages.Attribute; +import com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition; +import com.google.devtools.build.lib.packages.Attribute.SkylarkLateBound; +import com.google.devtools.build.lib.packages.SkylarkFileType; +import com.google.devtools.build.lib.packages.Type; +import com.google.devtools.build.lib.packages.Type.ConversionException; +import com.google.devtools.build.lib.syntax.Environment; +import com.google.devtools.build.lib.syntax.EvalException; +import com.google.devtools.build.lib.syntax.FuncallExpression; +import com.google.devtools.build.lib.syntax.Label; +import com.google.devtools.build.lib.syntax.SkylarkBuiltin; +import com.google.devtools.build.lib.syntax.SkylarkBuiltin.Param; +import com.google.devtools.build.lib.syntax.SkylarkCallbackFunction; +import com.google.devtools.build.lib.syntax.SkylarkEnvironment; +import com.google.devtools.build.lib.syntax.SkylarkFunction; +import com.google.devtools.build.lib.syntax.SkylarkList; +import com.google.devtools.build.lib.syntax.SkylarkModule; +import com.google.devtools.build.lib.syntax.UserDefinedFunction; +import com.google.devtools.build.lib.util.FileTypeSet; + +import java.util.Map; + +/** + * A helper class to provide Attr module in Skylark. + */ +@SkylarkModule(name = "attr", namespace = true, onlyLoadingPhase = true, + doc = "Module for creating new attributes. " + + "They are only for use with the <code>rule</code> function.") +public final class SkylarkAttr { + + private static final String MANDATORY_DOC = + "set to true if users have to explicitely specify the value"; + + private static final String ALLOW_FILES_DOC = + "whether File targets are allowed. Can be True, False (default), or " + + "a FileType filter."; + + private static final String ALLOW_RULES_DOC = + "which rule targets (name of the classes) are allowed." + + "This is deprecated (kept only for compatiblity), use providers instead."; + + private static final String FLAGS_DOC = + "deprecated, will be removed"; + + private static final String DEFAULT_DOC = + "sets the default value of the attribute."; + + private static final String CONFIGURATION_DOC = + "configuration of the attribute. " + + "For example, use DATA_CFG or HOST_CFG."; + + private static final String EXECUTABLE_DOC = + "set to True if the labels have to be executable. Access the labels with " + + "ctx.executable.<attribute_name>"; + + private static Attribute.Builder<?> createAttribute(Type<?> type, Map<String, Object> arguments, + FuncallExpression ast, SkylarkEnvironment env) throws EvalException, ConversionException { + final Location loc = ast.getLocation(); + // We use an empty name now so that we can set it later. + // This trick makes sense only in the context of Skylark (builtin rules should not use it). + Attribute.Builder<?> builder = Attribute.attr("", type); + + Object defaultValue = arguments.get("default"); + if (defaultValue != null) { + if (defaultValue instanceof UserDefinedFunction) { + // Late bound attribute. Non label type attributes already caused a type check error. + builder.value(new SkylarkLateBound( + new SkylarkCallbackFunction((UserDefinedFunction) defaultValue, ast, env))); + } else { + builder.defaultValue(defaultValue); + } + } + + for (String flag : castList(arguments.get("flags"), String.class)) { + builder.setPropertyFlag(flag); + } + + if (arguments.containsKey("mandatory") && (Boolean) arguments.get("mandatory")) { + builder.setPropertyFlag("MANDATORY"); + } + + if (arguments.containsKey("executable") && (Boolean) arguments.get("executable")) { + builder.setPropertyFlag("EXECUTABLE"); + } + + if (arguments.containsKey("single_file") && (Boolean) arguments.get("single_file")) { + builder.setPropertyFlag("SINGLE_ARTIFACT"); + } + + if (arguments.containsKey("allow_files")) { + Object fileTypesObj = arguments.get("allow_files"); + if (fileTypesObj == Boolean.TRUE) { + builder.allowedFileTypes(FileTypeSet.ANY_FILE); + } else if (fileTypesObj == Boolean.FALSE) { + builder.allowedFileTypes(FileTypeSet.NO_FILE); + } else if (fileTypesObj instanceof SkylarkFileType) { + builder.allowedFileTypes(((SkylarkFileType) fileTypesObj).getFileTypeSet()); + } else { + throw new EvalException(loc, "allow_files should be a boolean or a filetype object."); + } + } else if (type.equals(Type.LABEL) || type.equals(Type.LABEL_LIST)) { + builder.allowedFileTypes(FileTypeSet.NO_FILE); + } + + Object ruleClassesObj = arguments.get("allow_rules"); + if (ruleClassesObj != null) { + builder.allowedRuleClasses(castList(ruleClassesObj, String.class, + "allowed rule classes for attribute definition")); + } + + if (arguments.containsKey("providers")) { + builder.mandatoryProviders(castList(arguments.get("providers"), String.class)); + } + + if (arguments.containsKey("cfg")) { + builder.cfg((ConfigurationTransition) arguments.get("cfg")); + } + return builder; + } + + private static Object createAttribute(Map<String, Object> kwargs, Type<?> type, + FuncallExpression ast, Environment env) throws EvalException { + try { + return createAttribute(type, kwargs, ast, (SkylarkEnvironment) env); + } catch (ConversionException e) { + throw new EvalException(ast.getLocation(), e.getMessage()); + } + } + + @SkylarkBuiltin(name = "int", doc = + "Creates an attribute of type int.", + objectType = SkylarkAttr.class, + returnType = Attribute.class, + optionalParams = { + @Param(name = "default", type = Integer.class, + doc = DEFAULT_DOC + " If not specified, default is 0."), + @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, doc = FLAGS_DOC), + @Param(name = "mandatory", type = Boolean.class, doc = MANDATORY_DOC), + @Param(name = "cfg", type = ConfigurationTransition.class, doc = CONFIGURATION_DOC)}) + private static SkylarkFunction integer = new SkylarkFunction("int") { + @Override + public Object call(Map<String, Object> kwargs, FuncallExpression ast, Environment env) + throws EvalException { + return createAttribute(kwargs, Type.INTEGER, ast, env); + } + }; + + @SkylarkBuiltin(name = "string", doc = + "Creates an attribute of type string.", + objectType = SkylarkAttr.class, + returnType = Attribute.class, + optionalParams = { + @Param(name = "default", type = String.class, + doc = DEFAULT_DOC + " If not specified, default is \"\"."), + @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, doc = FLAGS_DOC), + @Param(name = "mandatory", type = Boolean.class, doc = MANDATORY_DOC), + @Param(name = "cfg", type = ConfigurationTransition.class, doc = CONFIGURATION_DOC)}) + private static SkylarkFunction string = new SkylarkFunction("string") { + @Override + public Object call(Map<String, Object> kwargs, FuncallExpression ast, Environment env) + throws EvalException { + return createAttribute(kwargs, Type.STRING, ast, env); + } + }; + + @SkylarkBuiltin(name = "label", doc = + "Creates an attribute of type Label. " + + "It is the only way to specify a dependency to another target. " + + "If you need a dependency that the user cannot overwrite, make the attribute " + + "private (starts with <code>_</code>).", + objectType = SkylarkAttr.class, + returnType = Attribute.class, + optionalParams = { + @Param(name = "default", type = Label.class, callbackEnabled = true, + doc = DEFAULT_DOC + " If not specified, default is None. " + + "Use the <code>Label</code> function to specify a default value."), + @Param(name = "executable", type = Boolean.class, doc = EXECUTABLE_DOC), + @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, doc = FLAGS_DOC), + @Param(name = "allow_files", doc = ALLOW_FILES_DOC), + @Param(name = "mandatory", type = Boolean.class, doc = MANDATORY_DOC), + @Param(name = "providers", type = SkylarkList.class, generic1 = String.class, + doc = "mandatory providers every dependency has to have"), + @Param(name = "allow_rules", type = SkylarkList.class, generic1 = String.class, + doc = ALLOW_RULES_DOC), + @Param(name = "single_file", doc = + "if true, the label must correspond to a single File. " + + "Access it through ctx.file.<attribute_name>."), + @Param(name = "cfg", type = ConfigurationTransition.class, doc = CONFIGURATION_DOC)}) + private static SkylarkFunction label = new SkylarkFunction("label") { + @Override + public Object call(Map<String, Object> kwargs, FuncallExpression ast, Environment env) + throws EvalException { + return createAttribute(kwargs, Type.LABEL, ast, env); + } + }; + + @SkylarkBuiltin(name = "string_list", doc = + "Creates an attribute of type list of strings", + objectType = SkylarkAttr.class, + returnType = Attribute.class, + optionalParams = { + @Param(name = "default", type = SkylarkList.class, generic1 = String.class, + doc = DEFAULT_DOC + " If not specified, default is []."), + @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, doc = FLAGS_DOC), + @Param(name = "mandatory", type = Boolean.class, doc = MANDATORY_DOC), + @Param(name = "cfg", type = ConfigurationTransition.class, + doc = CONFIGURATION_DOC)}) + private static SkylarkFunction stringList = new SkylarkFunction("string_list") { + @Override + public Object call(Map<String, Object> kwargs, FuncallExpression ast, Environment env) + throws EvalException { + return createAttribute(kwargs, Type.STRING_LIST, ast, env); + } + }; + + @SkylarkBuiltin(name = "label_list", doc = + "Creates an attribute of type list of labels. " + + "See <code>label</code> for more information.", + objectType = SkylarkAttr.class, + returnType = Attribute.class, + optionalParams = { + @Param(name = "default", type = SkylarkList.class, generic1 = Label.class, + callbackEnabled = true, + doc = DEFAULT_DOC + " If not specified, default is []. " + + "Use the <code>Label</code> function to specify a default value."), + @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, doc = FLAGS_DOC), + @Param(name = "allow_files", doc = ALLOW_FILES_DOC), + @Param(name = "mandatory", type = Boolean.class, doc = MANDATORY_DOC), + @Param(name = "allow_rules", type = SkylarkList.class, generic1 = String.class, + doc = ALLOW_RULES_DOC), + @Param(name = "providers", type = SkylarkList.class, generic1 = String.class, + doc = "mandatory providers every dependency has to have"), + @Param(name = "cfg", type = ConfigurationTransition.class, doc = CONFIGURATION_DOC)}) + private static SkylarkFunction labelList = new SkylarkFunction("label_list") { + @Override + public Object call(Map<String, Object> kwargs, FuncallExpression ast, Environment env) + throws EvalException { + return createAttribute(kwargs, Type.LABEL_LIST, ast, env); + } + }; + + @SkylarkBuiltin(name = "bool", doc = + "Creates an attribute of type bool. Its default value is False.", + objectType = SkylarkAttr.class, + returnType = Attribute.class, + optionalParams = { + @Param(name = "default", type = Boolean.class, doc = DEFAULT_DOC), + @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, doc = FLAGS_DOC), + @Param(name = "mandatory", type = Boolean.class, doc = MANDATORY_DOC), + @Param(name = "cfg", type = ConfigurationTransition.class, doc = CONFIGURATION_DOC)}) + private static SkylarkFunction bool = new SkylarkFunction("bool") { + @Override + public Object call(Map<String, Object> kwargs, FuncallExpression ast, Environment env) + throws EvalException { + return createAttribute(kwargs, Type.BOOLEAN, ast, env); + } + }; + + @SkylarkBuiltin(name = "output", doc = + "Creates an attribute of type output. Its default value is None. " + + "The user provides a file name (string) and the rule must create an action that " + + "generates the file.", + objectType = SkylarkAttr.class, + returnType = Attribute.class, + optionalParams = { + @Param(name = "default", type = Label.class, doc = DEFAULT_DOC), + @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, doc = FLAGS_DOC), + @Param(name = "mandatory", type = Boolean.class, doc = MANDATORY_DOC), + @Param(name = "cfg", type = ConfigurationTransition.class, doc = CONFIGURATION_DOC)}) + private static SkylarkFunction output = new SkylarkFunction("output") { + @Override + public Object call(Map<String, Object> kwargs, FuncallExpression ast, Environment env) + throws EvalException { + return createAttribute(kwargs, Type.OUTPUT, ast, env); + } + }; + + @SkylarkBuiltin(name = "output_list", doc = + "Creates an attribute of type list of outputs. Its default value is []. " + + "See <code>output</code> above for more information.", + objectType = SkylarkAttr.class, + returnType = Attribute.class, + optionalParams = { + @Param(name = "default", type = SkylarkList.class, generic1 = Label.class, doc = DEFAULT_DOC), + @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, doc = FLAGS_DOC), + @Param(name = "mandatory", type = Boolean.class, doc = MANDATORY_DOC), + @Param(name = "cfg", type = ConfigurationTransition.class, doc = CONFIGURATION_DOC)}) + private static SkylarkFunction outputList = new SkylarkFunction("output_list") { + @Override + public Object call(Map<String, Object> kwargs, FuncallExpression ast, Environment env) + throws EvalException { + return createAttribute(kwargs, Type.OUTPUT_LIST, ast, env); + } + }; + + @SkylarkBuiltin(name = "string_dict", doc = + "Creates an attribute of type dictionary, mapping from string to string. " + + "Its default value is {}.", + objectType = SkylarkAttr.class, + returnType = Attribute.class, + optionalParams = { + @Param(name = "default", type = Map.class, doc = DEFAULT_DOC), + @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, doc = FLAGS_DOC), + @Param(name = "mandatory", type = Boolean.class, doc = MANDATORY_DOC), + @Param(name = "cfg", type = ConfigurationTransition.class, doc = CONFIGURATION_DOC)}) + private static SkylarkFunction stringDict = new SkylarkFunction("string_dict") { + @Override + public Object call(Map<String, Object> kwargs, FuncallExpression ast, Environment env) + throws EvalException { + return createAttribute(kwargs, Type.STRING_DICT, ast, env); + } + }; + + @SkylarkBuiltin(name = "license", doc = + "Creates an attribute of type license. Its default value is NO_LICENSE.", + // TODO(bazel-team): Implement proper license support for Skylark. + objectType = SkylarkAttr.class, + returnType = Attribute.class, + optionalParams = { + @Param(name = "default", doc = DEFAULT_DOC), + @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, doc = FLAGS_DOC), + @Param(name = "mandatory", type = Boolean.class, doc = MANDATORY_DOC), + @Param(name = "cfg", type = ConfigurationTransition.class, doc = CONFIGURATION_DOC)}) + private static SkylarkFunction license = new SkylarkFunction("license") { + @Override + public Object call(Map<String, Object> kwargs, FuncallExpression ast, Environment env) + throws EvalException { + return createAttribute(kwargs, Type.LICENSE, ast, env); + } + }; +} |