// 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 com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.devtools.build.lib.analysis.config.HostTransition; import com.google.devtools.build.lib.analysis.config.transitions.PatchTransition; import com.google.devtools.build.lib.analysis.config.transitions.SplitTransition; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.events.Location; import com.google.devtools.build.lib.packages.Attribute; import com.google.devtools.build.lib.packages.Attribute.AllowedValueSet; import com.google.devtools.build.lib.packages.Attribute.SkylarkComputedDefaultTemplate; import com.google.devtools.build.lib.packages.Attribute.SplitTransitionProvider; import com.google.devtools.build.lib.packages.AttributeValueSource; import com.google.devtools.build.lib.packages.BuildType; import com.google.devtools.build.lib.packages.Provider; import com.google.devtools.build.lib.packages.SkylarkAspect; import com.google.devtools.build.lib.packages.SkylarkProviderIdentifier; import com.google.devtools.build.lib.skylarkinterface.Param; import com.google.devtools.build.lib.skylarkinterface.ParamType; import com.google.devtools.build.lib.skylarkinterface.SkylarkModule; import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory; import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter; import com.google.devtools.build.lib.skylarkinterface.SkylarkSignature; import com.google.devtools.build.lib.skylarkinterface.SkylarkValue; import com.google.devtools.build.lib.syntax.BuiltinFunction; 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.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.SkylarkSignatureProcessor; import com.google.devtools.build.lib.syntax.SkylarkType; 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.syntax.Type.LabelClass; import com.google.devtools.build.lib.syntax.UserDefinedFunction; import com.google.devtools.build.lib.util.FileType; import com.google.devtools.build.lib.util.FileTypeSet; import java.util.List; import java.util.Map; import javax.annotation.Nullable; /** * A helper class to provide Attr module in Skylark. * *

It exposes functions (for example, 'attr.string', 'attr.label_list', etc.) to Skylark users. * The functions are executed through reflection. As everywhere in Skylark, arguments are * type-checked with the signature and cannot be null. */ @SkylarkModule( name = "attr", namespace = true, category = SkylarkModuleCategory.BUILTIN, doc = "Module for creating new attributes. " + "They are only for use with rule or " + "aspect. " + "See example of use." ) public final class SkylarkAttr implements SkylarkValue { // Arguments private static final String ALLOW_FILES_ARG = "allow_files"; private static final String ALLOW_FILES_DOC = "Whether File targets are allowed. Can be True, False (default), or a list of file " + "extensions that are allowed (for example, [\".cc\", \".cpp\"])."; private static final String ALLOW_RULES_ARG = "allow_rules"; private static final String ALLOW_RULES_DOC = "Which rule targets (name of the classes) are allowed. This is deprecated (kept only for " + "compatibility), use providers instead."; private static final String ASPECTS_ARG = "aspects"; private static final String ASPECTS_ARG_DOC = "Aspects that should be applied to the dependency or dependencies specified by this " + "attribute."; private static final String CONFIGURATION_ARG = "cfg"; private static final String CONFIGURATION_DOC = "Configuration of the attribute. It can be " + "either \"data\", \"host\", or \"target\"."; private static final String DEFAULT_ARG = "default"; // A trailing space is required because it's often prepended to other sentences private static final String DEFAULT_DOC = "The default value of the attribute. "; private static final String DOC_ARG = "doc"; private static final String DOC_DOC = "A description of the attribute that can be extracted by documentation generating tools."; private static final String EXECUTABLE_ARG = "executable"; private static final String EXECUTABLE_DOC = "True if the label has to be executable. This means the label must refer to an " + "executable file, or to a rule that outputs an executable file. Access the label " + "with ctx.executable.<attribute_name>."; private static final String FLAGS_ARG = "flags"; private static final String FLAGS_DOC = "Deprecated, will be removed."; private static final String MANDATORY_ARG = "mandatory"; private static final String MANDATORY_DOC = "True if the value must be explicitly specified."; private static final String NON_EMPTY_ARG = "non_empty"; private static final String NON_EMPTY_DOC = "True if the attribute must not be empty. Deprecated: Use allow_empty instead."; private static final String ALLOW_EMPTY_ARG = "allow_empty"; private static final String ALLOW_EMPTY_DOC = "True if the attribute can be empty."; private static final String PROVIDERS_ARG = "providers"; private static final String PROVIDERS_DOC = "Mandatory providers list. It should be either a list of providers, or a " + "list of lists of providers. Every dependency should provide ALL providers " + "from at least ONE of these lists. A single list of providers will be " + "automatically converted to a list containing one list of providers."; private static final String SINGLE_FILE_ARG = "single_file"; private static final String ALLOW_SINGLE_FILE_ARG = "allow_single_file"; private static final String VALUES_ARG = "values"; private static final String VALUES_DOC = "The list of allowed values for the attribute. An error is raised if any other " + "value is given."; private static boolean containsNonNoneKey(SkylarkDict arguments, String key) { return arguments.containsKey(key) && arguments.get(key) != Runtime.NONE; } private static void setAllowedFileTypes( String attr, Object fileTypesObj, FuncallExpression ast, Attribute.Builder builder) throws EvalException { if (fileTypesObj == Boolean.TRUE) { builder.allowedFileTypes(FileTypeSet.ANY_FILE); } else if (fileTypesObj == Boolean.FALSE) { builder.allowedFileTypes(FileTypeSet.NO_FILE); } else if (fileTypesObj instanceof SkylarkFileType) { // TODO(laurentlb): deprecated, to be removed builder.allowedFileTypes(((SkylarkFileType) fileTypesObj).getFileTypeSet()); } else if (fileTypesObj instanceof SkylarkList) { List arg = SkylarkList.castSkylarkListOrNoneToList( fileTypesObj, String.class, "allow_files argument"); builder.allowedFileTypes(FileType.of(arg)); } else { throw new EvalException( ast.getLocation(), attr + " should be a boolean or a string list"); } } private static Attribute.Builder createAttribute( Type type, SkylarkDict arguments, FuncallExpression ast, Environment env) throws EvalException { // 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). return createAttribute(type, arguments, ast, env, ""); } private static Attribute.Builder createAttribute( Type type, SkylarkDict arguments, FuncallExpression ast, Environment env, String name) throws EvalException { Attribute.Builder builder = Attribute.attr(name, type); Object defaultValue = arguments.get(DEFAULT_ARG); if (!EvalUtils.isNullOrNone(defaultValue)) { if (defaultValue instanceof UserDefinedFunction) { // Computed attribute. Non label type attributes already caused a type check error. SkylarkCallbackFunction callback = new SkylarkCallbackFunction( (UserDefinedFunction) defaultValue, ast, env.getSemantics()); // SkylarkComputedDefaultTemplate needs to know the names of all attributes that it depends // on. However, this method does not know anything about other attributes. // We solve this problem by asking the SkylarkCallbackFunction for the parameter names used // in the function definition, which must be the names of attributes used by the callback. builder.value( new SkylarkComputedDefaultTemplate( type, callback.getParameterNames(), callback, ast.getLocation())); } else if (defaultValue instanceof SkylarkLateBoundDefault) { builder.value((SkylarkLateBoundDefault) defaultValue); } else { builder.defaultValue(defaultValue, env.getGlobals().getTransitiveLabel(), DEFAULT_ARG); } } for (String flag : SkylarkList.castSkylarkListOrNoneToList( arguments.get(FLAGS_ARG), String.class, FLAGS_ARG)) { builder.setPropertyFlag(flag); } if (containsNonNoneKey(arguments, MANDATORY_ARG) && (Boolean) arguments.get(MANDATORY_ARG)) { builder.setPropertyFlag("MANDATORY"); } // TODO(laurentlb): Deprecated, remove in August 2016 (use allow_empty instead). if (containsNonNoneKey(arguments, NON_EMPTY_ARG) && (Boolean) arguments.get(NON_EMPTY_ARG)) { builder.setPropertyFlag("NON_EMPTY"); } if (containsNonNoneKey(arguments, ALLOW_EMPTY_ARG) && !(Boolean) arguments.get(ALLOW_EMPTY_ARG)) { builder.setPropertyFlag("NON_EMPTY"); } if (containsNonNoneKey(arguments, EXECUTABLE_ARG) && (Boolean) arguments.get(EXECUTABLE_ARG)) { builder.setPropertyFlag("EXECUTABLE"); if (!containsNonNoneKey(arguments, CONFIGURATION_ARG)) { throw new EvalException( ast.getLocation(), "cfg parameter is mandatory when executable=True is provided. Please see " + "https://www.bazel.build/versions/master/docs/skylark/rules.html#configurations " + "for more details."); } } // TODO(laurentlb): Deprecated, remove in August 2016 (use allow_single_file). if (containsNonNoneKey(arguments, SINGLE_FILE_ARG) && (Boolean) arguments.get(SINGLE_FILE_ARG)) { if (containsNonNoneKey(arguments, ALLOW_SINGLE_FILE_ARG)) { throw new EvalException( ast.getLocation(), "Cannot specify both single_file (deprecated) and allow_single_file"); } builder.setPropertyFlag("SINGLE_ARTIFACT"); } if (containsNonNoneKey(arguments, ALLOW_FILES_ARG) && containsNonNoneKey(arguments, ALLOW_SINGLE_FILE_ARG)) { throw new EvalException( ast.getLocation(), "Cannot specify both allow_files and allow_single_file"); } if (containsNonNoneKey(arguments, ALLOW_FILES_ARG)) { Object fileTypesObj = arguments.get(ALLOW_FILES_ARG); setAllowedFileTypes(ALLOW_FILES_ARG, fileTypesObj, ast, builder); } else if (containsNonNoneKey(arguments, ALLOW_SINGLE_FILE_ARG)) { Object fileTypesObj = arguments.get(ALLOW_SINGLE_FILE_ARG); setAllowedFileTypes(ALLOW_SINGLE_FILE_ARG, fileTypesObj, ast, builder); builder.setPropertyFlag("SINGLE_ARTIFACT"); } else if (type.getLabelClass() == LabelClass.DEPENDENCY) { builder.allowedFileTypes(FileTypeSet.NO_FILE); } Object ruleClassesObj = arguments.get(ALLOW_RULES_ARG); if (ruleClassesObj != null && ruleClassesObj != Runtime.NONE) { builder.allowedRuleClasses( SkylarkList.castSkylarkListOrNoneToList( ruleClassesObj, String.class, "allowed rule classes for attribute definition")); } List values = SkylarkList.castSkylarkListOrNoneToList( arguments.get(VALUES_ARG), Object.class, VALUES_ARG); if (!Iterables.isEmpty(values)) { builder.allowedValues(new AllowedValueSet(values)); } if (containsNonNoneKey(arguments, PROVIDERS_ARG)) { Object obj = arguments.get(PROVIDERS_ARG); SkylarkType.checkType(obj, SkylarkList.class, PROVIDERS_ARG); ImmutableList> providersList = buildProviderPredicate( (SkylarkList) obj, PROVIDERS_ARG, ast.getLocation()); // If there is at least one empty set, there is no restriction. if (providersList.stream().noneMatch(ImmutableSet::isEmpty)) { builder.mandatoryProvidersList(providersList); } } if (containsNonNoneKey(arguments, CONFIGURATION_ARG)) { Object trans = arguments.get(CONFIGURATION_ARG); if (trans.equals("data")) { builder.cfg((PatchTransition) SkylarkUtils.getLipoDataTransition(env)); } else if (trans.equals("host")) { builder.cfg(HostTransition.INSTANCE); } else if (trans instanceof SplitTransition) { builder.cfg((SplitTransition) trans); } else if (trans instanceof SplitTransitionProvider) { builder.cfg((SplitTransitionProvider) trans); } else if (!trans.equals("target")) { throw new EvalException(ast.getLocation(), "cfg must be either 'data', 'host', or 'target'."); } } if (containsNonNoneKey(arguments, ASPECTS_ARG)) { Object obj = arguments.get(ASPECTS_ARG); SkylarkType.checkType(obj, SkylarkList.class, ASPECTS_ARG); List aspects = ((SkylarkList) obj).getContents(SkylarkAspect.class, "aspects"); for (SkylarkAspect aspect : aspects) { aspect.attachToAttribute(builder, ast.getLocation()); } } return builder; } /** * Builds a list of sets of accepted providers from Skylark list {@code obj}. * The list can either be a list of providers (in that case the result is a list with one * set) or a list of lists of providers (then the result is the list of sets). * @param argumentName used in error messages. * @param location location for error messages. */ static ImmutableList> buildProviderPredicate( SkylarkList obj, String argumentName, Location location) throws EvalException { if (obj.isEmpty()) { return ImmutableList.of(); } boolean isListOfProviders = true; for (Object o : obj) { if (!isProvider(o)) { isListOfProviders = false; break; } } if (isListOfProviders) { return ImmutableList.of(getSkylarkProviderIdentifiers(obj, location)); } else { return getProvidersList(obj, argumentName, location); } } /** * Returns true if {@code o} is a Skylark provider (either a declared provider or * a legacy provider name. */ static boolean isProvider(Object o) { return o instanceof String || o instanceof Provider; } /** * Converts Skylark identifiers of providers (either a string or a provider value) * to their internal representations. */ static ImmutableSet getSkylarkProviderIdentifiers( SkylarkList list, Location location) throws EvalException { ImmutableList.Builder result = ImmutableList.builder(); for (Object obj : list) { if (obj instanceof String) { result.add(SkylarkProviderIdentifier.forLegacy((String) obj)); } else if (obj instanceof Provider) { Provider constructor = (Provider) obj; if (!constructor.isExported()) { throw new EvalException(location, "Providers should be top-level values in extension files that define them."); } result.add(SkylarkProviderIdentifier.forKey(constructor.getKey())); } } return ImmutableSet.copyOf(result.build()); } private static ImmutableList> getProvidersList( SkylarkList skylarkList, String argumentName, Location location) throws EvalException { ImmutableList.Builder> providersList = ImmutableList.builder(); String errorMsg = "Illegal argument: element in '%s' is of unexpected type. " + "Either all elements should be providers, " + "or all elements should be lists of providers, but got %s."; for (Object o : skylarkList) { if (!(o instanceof SkylarkList)) { throw new EvalException(location, String.format(errorMsg, PROVIDERS_ARG, "an element of type " + EvalUtils.getDataTypeName(o, true))); } for (Object value : (SkylarkList) o) { if (!isProvider(value)) { throw new EvalException(location, String.format(errorMsg, argumentName, "list with an element of type " + EvalUtils.getDataTypeNameFromClass(value.getClass()))); } } providersList.add(getSkylarkProviderIdentifiers((SkylarkList) o, location)); } return providersList.build(); } private static Descriptor createAttrDescriptor( String name, SkylarkDict kwargs, Type type, FuncallExpression ast, Environment env) throws EvalException { try { return new Descriptor(name, createAttribute(type, kwargs, ast, env)); } catch (ConversionException e) { throw new EvalException(ast.getLocation(), e.getMessage()); } } private static final Map, String> whyNotConfigurable = ImmutableMap., String>builder() .put(BuildType.LICENSE, "loading phase license checking logic assumes non-configurable values") .put(BuildType.OUTPUT, "output paths are part of the static graph structure") .build(); /** * If the given attribute type is non-configurable, returns the reason why. Otherwise, returns * {@code null}. */ @Nullable public static String maybeGetNonConfigurableReason(Type type) { return whyNotConfigurable.get(type); } private static Descriptor createNonconfigurableAttrDescriptor( String name, SkylarkDict kwargs, Type type, FuncallExpression ast, Environment env) throws EvalException { String whyNotConfigurableReason = Preconditions.checkNotNull(maybeGetNonConfigurableReason(type), type); try { return new Descriptor( name, createAttribute(type, kwargs, ast, env).nonconfigurable(whyNotConfigurableReason)); } catch (ConversionException e) { throw new EvalException(ast.getLocation(), e.getMessage()); } } @Override public void repr(SkylarkPrinter printer) { printer.append(""); } @SkylarkSignature( name = "int", doc = "Creates an attribute of type int.", objectType = SkylarkAttr.class, returnType = Descriptor.class, parameters = { @Param( name = DEFAULT_ARG, type = Integer.class, defaultValue = "0", doc = DEFAULT_DOC, named = true, positional = false ), @Param( name = DOC_ARG, type = String.class, defaultValue = "''", doc = DOC_DOC, named = true, positional = false ), @Param( name = MANDATORY_ARG, type = Boolean.class, defaultValue = "False", doc = MANDATORY_DOC, named = true, positional = false ), @Param( name = VALUES_ARG, type = SkylarkList.class, generic1 = Integer.class, defaultValue = "[]", doc = VALUES_DOC, named = true, positional = false ) }, useAst = true, useEnvironment = true ) private static BuiltinFunction integer = new BuiltinFunction("int") { public Descriptor invoke( Integer defaultInt, String doc, Boolean mandatory, SkylarkList values, FuncallExpression ast, Environment env) throws EvalException { // TODO(bazel-team): Replace literal strings with constants. env.checkLoadingOrWorkspacePhase("attr.int", ast.getLocation()); return createAttrDescriptor( getName(), EvalUtils.optionMap( env, DEFAULT_ARG, defaultInt, MANDATORY_ARG, mandatory, VALUES_ARG, values), Type.INTEGER, ast, env); } }; @SkylarkSignature( name = "configuration_field", returnType = SkylarkLateBoundDefault.class, // TODO(cparsons): Provide a link to documentation for available SkylarkConfigurationFields. doc = "References a late-bound default value for an attribute of type " + "label. 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 be private.", parameters = { @Param( name = "fragment", type = String.class, doc = "The name of a configuration fragment which contains the late-bound value." ), @Param( name = "name", type = String.class, doc = "The name of the value to obtain from the configuration fragment."), }, useLocation = true, useEnvironment = true ) private static final BuiltinFunction configurationField = new BuiltinFunction("configuration_field") { public SkylarkLateBoundDefault invoke( String fragment, String name, Location loc, Environment env) throws EvalException { Class fragmentClass = SkylarkUtils.getFragmentMap(env).get(fragment); if (fragmentClass == null) { throw new EvalException( loc, String.format("invalid configuration fragment name '%s'", fragment)); } try { return SkylarkLateBoundDefault.forConfigurationField( fragmentClass, name, SkylarkUtils.getToolsRepository(env)); } catch (SkylarkLateBoundDefault.InvalidConfigurationFieldException exception) { throw new EvalException(loc, exception); } } }; @SkylarkSignature( name = "string", doc = "Creates an attribute of type string.", objectType = SkylarkAttr.class, returnType = Descriptor.class, parameters = { @Param( name = DEFAULT_ARG, type = String.class, defaultValue = "''", doc = DEFAULT_DOC, named = true, positional = false ), @Param( name = DOC_ARG, type = String.class, defaultValue = "''", doc = DOC_DOC, named = true, positional = false ), @Param( name = MANDATORY_ARG, type = Boolean.class, defaultValue = "False", doc = MANDATORY_DOC, named = true, positional = false ), @Param( name = VALUES_ARG, type = SkylarkList.class, generic1 = String.class, defaultValue = "[]", doc = VALUES_DOC, named = true, positional = false ) }, useAst = true, useEnvironment = true ) private static BuiltinFunction string = new BuiltinFunction("string") { public Descriptor invoke( String defaultString, String doc, Boolean mandatory, SkylarkList values, FuncallExpression ast, Environment env) throws EvalException { env.checkLoadingOrWorkspacePhase("attr.string", ast.getLocation()); return createAttrDescriptor( getName(), EvalUtils.optionMap( env, DEFAULT_ARG, defaultString, MANDATORY_ARG, mandatory, VALUES_ARG, values), Type.STRING, ast, env); } }; @SkylarkSignature( name = "label", doc = "Creates an attribute of type Target which is the target " + "referred to by the 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.", objectType = SkylarkAttr.class, returnType = Descriptor.class, parameters = { @Param( name = DEFAULT_ARG, allowedTypes = { @ParamType(type = Label.class), @ParamType(type = String.class), @ParamType(type = SkylarkLateBoundDefault.class) }, callbackEnabled = true, noneable = true, defaultValue = "None", named = true, positional = false, doc = DEFAULT_DOC + "Use a string or the Label " + "function to specify a default value, for example, " + "attr.label(default = \"//a:b\")." ), @Param( name = DOC_ARG, type = String.class, defaultValue = "''", doc = DOC_DOC, named = true, positional = false ), @Param( name = EXECUTABLE_ARG, type = Boolean.class, defaultValue = "False", named = true, positional = false, doc = EXECUTABLE_DOC ), @Param( name = ALLOW_FILES_ARG, defaultValue = "None", named = true, positional = false, doc = ALLOW_FILES_DOC ), @Param( name = ALLOW_SINGLE_FILE_ARG, defaultValue = "None", named = true, positional = false, doc = "This is similar to allow_files, with the restriction that the label must " + "correspond to a single File. " + "Access it through ctx.file.<attribute_name>." ), @Param( name = MANDATORY_ARG, type = Boolean.class, defaultValue = "False", named = true, positional = false, doc = MANDATORY_DOC ), @Param( name = PROVIDERS_ARG, type = SkylarkList.class, defaultValue = "[]", named = true, positional = false, doc = PROVIDERS_DOC ), @Param( name = ALLOW_RULES_ARG, type = SkylarkList.class, generic1 = String.class, noneable = true, defaultValue = "None", named = true, positional = false, doc = ALLOW_RULES_DOC ), @Param( name = SINGLE_FILE_ARG, type = Boolean.class, defaultValue = "False", named = true, positional = false, doc = "Deprecated: Use allow_single_file instead. " + "If True, the label must correspond to a single File. " + "Access it through ctx.file.<attribute_name>." ), @Param( name = CONFIGURATION_ARG, type = Object.class, noneable = true, defaultValue = "None", named = true, positional = false, doc = CONFIGURATION_DOC + " This parameter is required if executable is True." ), @Param( name = ASPECTS_ARG, type = SkylarkList.class, generic1 = SkylarkAspect.class, defaultValue = "[]", named = true, positional = false, doc = ASPECTS_ARG_DOC ), }, useAst = true, useEnvironment = true ) private static BuiltinFunction label = new BuiltinFunction("label") { public Descriptor invoke( Object defaultO, String doc, Boolean executable, Object allowFiles, Object allowSingleFile, Boolean mandatory, SkylarkList providers, Object allowRules, Boolean singleFile, Object cfg, SkylarkList aspects, FuncallExpression ast, Environment env) throws EvalException { env.checkLoadingOrWorkspacePhase("attr.label", ast.getLocation()); try { Attribute.Builder attribute = createAttribute( BuildType.LABEL, EvalUtils.optionMap( env, DEFAULT_ARG, defaultO, EXECUTABLE_ARG, executable, ALLOW_FILES_ARG, allowFiles, ALLOW_SINGLE_FILE_ARG, allowSingleFile, MANDATORY_ARG, mandatory, PROVIDERS_ARG, providers, ALLOW_RULES_ARG, allowRules, SINGLE_FILE_ARG, singleFile, CONFIGURATION_ARG, cfg, ASPECTS_ARG, aspects), ast, env, "label"); return new Descriptor(getName(), attribute); } catch (EvalException e) { throw new EvalException(ast.getLocation(), e.getMessage(), e); } } }; @SkylarkSignature( name = "string_list", doc = "Creates an attribute which is a list of " + "strings.", objectType = SkylarkAttr.class, returnType = Descriptor.class, parameters = { @Param( name = DEFAULT_ARG, type = SkylarkList.class, generic1 = String.class, defaultValue = "[]", doc = DEFAULT_DOC ), @Param( name = DOC_ARG, type = String.class, defaultValue = "''", doc = DOC_DOC, named = true, positional = false ), @Param( name = MANDATORY_ARG, type = Boolean.class, defaultValue = "False", doc = MANDATORY_DOC ), @Param( name = NON_EMPTY_ARG, type = Boolean.class, defaultValue = "False", doc = NON_EMPTY_DOC ), @Param( name = ALLOW_EMPTY_ARG, type = Boolean.class, defaultValue = "True", doc = ALLOW_EMPTY_DOC ) }, useAst = true, useEnvironment = true ) private static BuiltinFunction stringList = new BuiltinFunction("string_list") { public Descriptor invoke( SkylarkList defaultList, String doc, Boolean mandatory, Boolean nonEmpty, Boolean allowEmpty, FuncallExpression ast, Environment env) throws EvalException { env.checkLoadingOrWorkspacePhase("attr.string_list", ast.getLocation()); return createAttrDescriptor( getName(), EvalUtils.optionMap( env, DEFAULT_ARG, defaultList, MANDATORY_ARG, mandatory, NON_EMPTY_ARG, nonEmpty, ALLOW_EMPTY_ARG, allowEmpty), Type.STRING_LIST, ast, env); } }; @SkylarkSignature( name = "int_list", doc = "Creates an attribute which is a list of ints.", objectType = SkylarkAttr.class, returnType = Descriptor.class, parameters = { @Param( name = DEFAULT_ARG, type = SkylarkList.class, generic1 = Integer.class, defaultValue = "[]", doc = DEFAULT_DOC ), @Param( name = DOC_ARG, type = String.class, defaultValue = "''", doc = DOC_DOC, named = true, positional = false ), @Param( name = MANDATORY_ARG, type = Boolean.class, defaultValue = "False", doc = MANDATORY_DOC ), @Param( name = NON_EMPTY_ARG, type = Boolean.class, defaultValue = "False", doc = NON_EMPTY_DOC ), @Param( name = ALLOW_EMPTY_ARG, type = Boolean.class, defaultValue = "True", doc = ALLOW_EMPTY_DOC ) }, useAst = true, useEnvironment = true ) private static BuiltinFunction intList = new BuiltinFunction("int_list") { public Descriptor invoke( SkylarkList defaultList, String doc, Boolean mandatory, Boolean nonEmpty, Boolean allowEmpty, FuncallExpression ast, Environment env) throws EvalException { env.checkLoadingOrWorkspacePhase("attr.int_list", ast.getLocation()); return createAttrDescriptor( getName(), EvalUtils.optionMap( env, DEFAULT_ARG, defaultList, MANDATORY_ARG, mandatory, NON_EMPTY_ARG, nonEmpty, ALLOW_EMPTY_ARG, allowEmpty), Type.INTEGER_LIST, ast, env); } }; @SkylarkSignature( name = "label_list", doc = "Creates an attribute which is a list of type " + "Target which are specified by the labels in the list. " + "See label for more information.", objectType = SkylarkAttr.class, returnType = Descriptor.class, parameters = { @Param( name = DEFAULT_ARG, type = SkylarkList.class, generic1 = Label.class, callbackEnabled = true, defaultValue = "[]", named = true, positional = false, doc = DEFAULT_DOC + "Use strings or the Label " + "function to specify default values, for example, " + "attr.label_list(default = [\"//a:b\", \"//a:c\"])." ), @Param( name = DOC_ARG, type = String.class, defaultValue = "''", doc = DOC_DOC, named = true, positional = false ), @Param( name = ALLOW_FILES_ARG, // bool or FileType filter defaultValue = "None", named = true, positional = false, doc = ALLOW_FILES_DOC ), @Param( name = ALLOW_RULES_ARG, type = SkylarkList.class, generic1 = String.class, noneable = true, defaultValue = "None", named = true, positional = false, doc = ALLOW_RULES_DOC ), @Param( name = PROVIDERS_ARG, type = SkylarkList.class, defaultValue = "[]", named = true, positional = false, doc = PROVIDERS_DOC ), @Param( name = FLAGS_ARG, type = SkylarkList.class, generic1 = String.class, defaultValue = "[]", named = true, positional = false, doc = FLAGS_DOC ), @Param( name = MANDATORY_ARG, type = Boolean.class, defaultValue = "False", named = true, positional = false, doc = MANDATORY_DOC ), @Param( name = NON_EMPTY_ARG, type = Boolean.class, defaultValue = "False", named = true, positional = false, doc = NON_EMPTY_DOC ), @Param( name = ALLOW_EMPTY_ARG, type = Boolean.class, defaultValue = "True", doc = ALLOW_EMPTY_DOC ), @Param( name = CONFIGURATION_ARG, type = Object.class, noneable = true, defaultValue = "None", named = true, positional = false, doc = CONFIGURATION_DOC ), @Param( name = ASPECTS_ARG, type = SkylarkList.class, generic1 = SkylarkAspect.class, defaultValue = "[]", named = true, positional = false, doc = ASPECTS_ARG_DOC ), }, useAst = true, useEnvironment = true ) private static BuiltinFunction labelList = new BuiltinFunction("label_list") { public Descriptor invoke( Object defaultList, String doc, Object allowFiles, Object allowRules, SkylarkList providers, SkylarkList flags, Boolean mandatory, Boolean nonEmpty, Boolean allowEmpty, Object cfg, SkylarkList aspects, FuncallExpression ast, Environment env) throws EvalException { env.checkLoadingOrWorkspacePhase("attr.label_list", ast.getLocation()); SkylarkDict kwargs = EvalUtils.optionMap( env, DEFAULT_ARG, defaultList, ALLOW_FILES_ARG, allowFiles, ALLOW_RULES_ARG, allowRules, PROVIDERS_ARG, providers, FLAGS_ARG, flags, MANDATORY_ARG, mandatory, NON_EMPTY_ARG, nonEmpty, ALLOW_EMPTY_ARG, allowEmpty, CONFIGURATION_ARG, cfg, ASPECTS_ARG, aspects); try { Attribute.Builder attribute = createAttribute(BuildType.LABEL_LIST, kwargs, ast, env, "label_list"); return new Descriptor(getName(), attribute); } catch (EvalException e) { throw new EvalException(ast.getLocation(), e.getMessage(), e); } } }; @SkylarkSignature( name = "label_keyed_string_dict", doc = "Creates an attribute which is a dict. Its keys are type " + "Target and are specified by the label keys of the " + "input dict. Its values are strings. See " + "label for more information.", objectType = SkylarkAttr.class, returnType = Descriptor.class, parameters = { @Param( name = DEFAULT_ARG, type = SkylarkDict.class, callbackEnabled = true, defaultValue = "{}", named = true, positional = false, doc = DEFAULT_DOC + "Use strings or the Label " + "function to specify default values, for example, " + "attr.label_keyed_string_dict(default = " + "{\"//a:b\": \"value\", \"//a:c\": \"string\"})." ), @Param( name = DOC_ARG, type = String.class, defaultValue = "''", doc = DOC_DOC, named = true, positional = false ), @Param( name = ALLOW_FILES_ARG, // bool or FileType filter defaultValue = "None", named = true, positional = false, doc = ALLOW_FILES_DOC ), @Param( name = ALLOW_RULES_ARG, type = SkylarkList.class, generic1 = String.class, noneable = true, defaultValue = "None", named = true, positional = false, doc = ALLOW_RULES_DOC ), @Param( name = PROVIDERS_ARG, type = SkylarkList.class, defaultValue = "[]", named = true, positional = false, doc = PROVIDERS_DOC ), @Param( name = FLAGS_ARG, type = SkylarkList.class, generic1 = String.class, defaultValue = "[]", named = true, positional = false, doc = FLAGS_DOC ), @Param( name = MANDATORY_ARG, type = Boolean.class, defaultValue = "False", named = true, positional = false, doc = MANDATORY_DOC ), @Param( name = NON_EMPTY_ARG, type = Boolean.class, defaultValue = "False", named = true, positional = false, doc = NON_EMPTY_DOC ), @Param( name = ALLOW_EMPTY_ARG, type = Boolean.class, defaultValue = "True", doc = ALLOW_EMPTY_DOC ), @Param( name = CONFIGURATION_ARG, type = Object.class, noneable = true, defaultValue = "None", named = true, positional = false, doc = CONFIGURATION_DOC ), @Param( name = ASPECTS_ARG, type = SkylarkList.class, generic1 = SkylarkAspect.class, defaultValue = "[]", named = true, positional = false, doc = ASPECTS_ARG_DOC ) }, useAst = true, useEnvironment = true ) private static BuiltinFunction labelKeyedStringDict = new BuiltinFunction("label_keyed_string_dict") { public Descriptor invoke( Object defaultList, String doc, Object allowFiles, Object allowRules, SkylarkList providers, SkylarkList flags, Boolean mandatory, Boolean nonEmpty, Boolean allowEmpty, Object cfg, SkylarkList aspects, FuncallExpression ast, Environment env) throws EvalException { env.checkLoadingOrWorkspacePhase("attr.label_keyed_string_dict", ast.getLocation()); SkylarkDict kwargs = EvalUtils.optionMap( env, DEFAULT_ARG, defaultList, ALLOW_FILES_ARG, allowFiles, ALLOW_RULES_ARG, allowRules, PROVIDERS_ARG, providers, FLAGS_ARG, flags, MANDATORY_ARG, mandatory, NON_EMPTY_ARG, nonEmpty, ALLOW_EMPTY_ARG, allowEmpty, CONFIGURATION_ARG, cfg, ASPECTS_ARG, aspects); try { Attribute.Builder attribute = createAttribute( BuildType.LABEL_KEYED_STRING_DICT, kwargs, ast, env, "label_keyed_string_dict"); return new Descriptor(this.getName(), attribute); } catch (EvalException e) { throw new EvalException(ast.getLocation(), e.getMessage(), e); } } }; @SkylarkSignature( name = "bool", doc = "Creates an attribute of type bool.", objectType = SkylarkAttr.class, returnType = Descriptor.class, parameters = { @Param( name = DEFAULT_ARG, type = Boolean.class, defaultValue = "False", named = true, positional = false, doc = DEFAULT_DOC ), @Param( name = DOC_ARG, type = String.class, defaultValue = "''", doc = DOC_DOC, named = true, positional = false ), @Param( name = MANDATORY_ARG, type = Boolean.class, defaultValue = "False", named = true, positional = false, doc = MANDATORY_DOC ) }, useAst = true, useEnvironment = true ) private static BuiltinFunction bool = new BuiltinFunction("bool") { public Descriptor invoke( Boolean defaultO, String doc, Boolean mandatory, FuncallExpression ast, Environment env) throws EvalException { env.checkLoadingOrWorkspacePhase("attr.bool", ast.getLocation()); return createAttrDescriptor( getName(), EvalUtils.optionMap( env, DEFAULT_ARG, defaultO, MANDATORY_ARG, mandatory), Type.BOOLEAN, ast, env); } }; @SkylarkSignature( name = "output", doc = "Creates an attribute of type output. " + "The user provides a file name (string) and the rule must create an action that " + "generates the file.", objectType = SkylarkAttr.class, returnType = Descriptor.class, parameters = { @Param( name = DEFAULT_ARG, type = Label.class, noneable = true, defaultValue = "None", named = true, positional = false, doc = DEFAULT_DOC ), @Param( name = DOC_ARG, type = String.class, defaultValue = "''", doc = DOC_DOC, named = true, positional = false ), @Param( name = MANDATORY_ARG, type = Boolean.class, defaultValue = "False", named = true, positional = false, doc = MANDATORY_DOC ) }, useAst = true, useEnvironment = true ) private static BuiltinFunction output = new BuiltinFunction("output") { public Descriptor invoke( Object defaultO, String doc, Boolean mandatory, FuncallExpression ast, Environment env) throws EvalException { env.checkLoadingOrWorkspacePhase("attr.output", ast.getLocation()); return createNonconfigurableAttrDescriptor( getName(), EvalUtils.optionMap( env, DEFAULT_ARG, defaultO, MANDATORY_ARG, mandatory), BuildType.OUTPUT, ast, env); } }; @SkylarkSignature( name = "output_list", doc = "Creates an attribute which is a list of outputs. " + "See output for more information.", objectType = SkylarkAttr.class, returnType = Descriptor.class, parameters = { @Param( name = DEFAULT_ARG, type = SkylarkList.class, generic1 = Label.class, defaultValue = "[]", named = true, positional = false, doc = DEFAULT_DOC ), @Param( name = DOC_ARG, type = String.class, defaultValue = "''", doc = DOC_DOC, named = true, positional = false ), @Param( name = MANDATORY_ARG, type = Boolean.class, defaultValue = "False", named = true, positional = false, doc = MANDATORY_DOC ), @Param( name = NON_EMPTY_ARG, type = Boolean.class, defaultValue = "False", named = true, positional = false, doc = NON_EMPTY_DOC ), @Param( name = ALLOW_EMPTY_ARG, type = Boolean.class, defaultValue = "True", doc = ALLOW_EMPTY_DOC ) }, useAst = true, useEnvironment = true ) private static BuiltinFunction outputList = new BuiltinFunction("output_list") { public Descriptor invoke( SkylarkList defaultList, String doc, Boolean mandatory, Boolean nonEmpty, Boolean allowEmpty, FuncallExpression ast, Environment env) throws EvalException { env.checkLoadingOrWorkspacePhase("attr.output_list", ast.getLocation()); return createAttrDescriptor( getName(), EvalUtils.optionMap( env, DEFAULT_ARG, defaultList, MANDATORY_ARG, mandatory, NON_EMPTY_ARG, nonEmpty, ALLOW_EMPTY_ARG, allowEmpty), BuildType.OUTPUT_LIST, ast, env); } }; @SkylarkSignature( name = "string_dict", doc = "Creates an attribute of type dict, mapping from " + "string to string.", objectType = SkylarkAttr.class, returnType = Descriptor.class, parameters = { @Param( name = DEFAULT_ARG, type = SkylarkDict.class, named = true, positional = false, defaultValue = "{}", doc = DEFAULT_DOC ), @Param( name = DOC_ARG, type = String.class, defaultValue = "''", doc = DOC_DOC, named = true, positional = false ), @Param( name = MANDATORY_ARG, type = Boolean.class, named = true, positional = false, defaultValue = "False", doc = MANDATORY_DOC ), @Param( name = NON_EMPTY_ARG, type = Boolean.class, defaultValue = "False", named = true, positional = false, doc = NON_EMPTY_DOC ), @Param( name = ALLOW_EMPTY_ARG, type = Boolean.class, defaultValue = "True", doc = ALLOW_EMPTY_DOC ) }, useAst = true, useEnvironment = true ) private static BuiltinFunction stringDict = new BuiltinFunction("string_dict") { public Descriptor invoke( SkylarkDict defaultO, String doc, Boolean mandatory, Boolean nonEmpty, Boolean allowEmpty, FuncallExpression ast, Environment env) throws EvalException { env.checkLoadingOrWorkspacePhase("attr.string_dict", ast.getLocation()); return createAttrDescriptor( getName(), EvalUtils.optionMap( env, DEFAULT_ARG, defaultO, MANDATORY_ARG, mandatory, NON_EMPTY_ARG, nonEmpty, ALLOW_EMPTY_ARG, allowEmpty), Type.STRING_DICT, ast, env); } }; @SkylarkSignature( name = "string_list_dict", doc = "Creates an attribute of type dict, mapping from " + "string to list of " + "string.", objectType = SkylarkAttr.class, returnType = Descriptor.class, parameters = { @Param( name = DEFAULT_ARG, type = SkylarkDict.class, defaultValue = "{}", named = true, positional = false, doc = DEFAULT_DOC ), @Param( name = DOC_ARG, type = String.class, defaultValue = "''", doc = DOC_DOC, named = true, positional = false ), @Param( name = MANDATORY_ARG, type = Boolean.class, defaultValue = "False", named = true, positional = false, doc = MANDATORY_DOC ), @Param( name = NON_EMPTY_ARG, type = Boolean.class, defaultValue = "False", named = true, positional = false, doc = NON_EMPTY_DOC ), @Param( name = ALLOW_EMPTY_ARG, type = Boolean.class, defaultValue = "True", doc = ALLOW_EMPTY_DOC ) }, useAst = true, useEnvironment = true ) private static BuiltinFunction stringListDict = new BuiltinFunction("string_list_dict") { public Descriptor invoke( SkylarkDict defaultO, String doc, Boolean mandatory, Boolean nonEmpty, Boolean allowEmpty, FuncallExpression ast, Environment env) throws EvalException { env.checkLoadingOrWorkspacePhase("attr.string_list_dict", ast.getLocation()); return createAttrDescriptor( getName(), EvalUtils.optionMap( env, DEFAULT_ARG, defaultO, MANDATORY_ARG, mandatory, NON_EMPTY_ARG, nonEmpty, ALLOW_EMPTY_ARG, allowEmpty), Type.STRING_LIST_DICT, ast, env); } }; @SkylarkSignature( name = "license", doc = "Creates an attribute of type license.", // TODO(bazel-team): Implement proper license support for Skylark. objectType = SkylarkAttr.class, returnType = Descriptor.class, parameters = { // TODO(bazel-team): ensure this is the correct default value @Param( name = DEFAULT_ARG, defaultValue = "None", noneable = true, named = true, positional = false, doc = DEFAULT_DOC ), @Param( name = DOC_ARG, type = String.class, defaultValue = "''", doc = DOC_DOC, named = true, positional = false ), @Param( name = MANDATORY_ARG, type = Boolean.class, defaultValue = "False", named = true, positional = false, doc = MANDATORY_DOC ) }, useAst = true, useEnvironment = true ) private static BuiltinFunction license = new BuiltinFunction("license") { public Descriptor invoke( Object defaultO, String doc, Boolean mandatory, FuncallExpression ast, Environment env) throws EvalException { env.checkLoadingOrWorkspacePhase("attr.license", ast.getLocation()); return createNonconfigurableAttrDescriptor( getName(), EvalUtils.optionMap( env, DEFAULT_ARG, defaultO, MANDATORY_ARG, mandatory), BuildType.LICENSE, ast, env); } }; /** A descriptor of an attribute defined in Skylark. */ @SkylarkModule( name = "Attribute", category = SkylarkModuleCategory.NONE, doc = "Representation of a definition of an attribute. Use the attr " + "module to create an Attribute. They are only for use with a " + "rule or an " + "aspect." ) public static final class Descriptor implements SkylarkValue { private final Attribute.Builder attributeBuilder; /** * This lock guards {@code attributeBuilder} field. * * {@link Attribute.Builder} class is not thread-safe for concurrent modification. */ private final Object lock = new Object(); private final String name; public Descriptor(String name, Attribute.Builder attributeBuilder) { this.attributeBuilder = attributeBuilder; this.name = name; } public boolean hasDefault() { synchronized (lock) { return attributeBuilder.isValueSet(); } } public AttributeValueSource getValueSource() { synchronized (lock) { return attributeBuilder.getValueSource(); } } public Attribute build(String name) { synchronized (lock) { return attributeBuilder.build(name); } } @Override public void repr(SkylarkPrinter printer) { printer.append(""); } } static { SkylarkSignatureProcessor.configureSkylarkFunctions(SkylarkAttr.class); } }