// 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.rules;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.cmdline.Label;
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.ConfigurationTransition;
import com.google.devtools.build.lib.packages.Attribute.SkylarkLateBound;
import com.google.devtools.build.lib.packages.BuildType;
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.SkylarkList;
import com.google.devtools.build.lib.syntax.SkylarkModule;
import com.google.devtools.build.lib.syntax.SkylarkSignature;
import com.google.devtools.build.lib.syntax.SkylarkSignature.Param;
import com.google.devtools.build.lib.syntax.SkylarkSignatureProcessor;
import com.google.devtools.build.lib.syntax.Type;
import com.google.devtools.build.lib.syntax.Type.ConversionException;
import com.google.devtools.build.lib.syntax.UserDefinedFunction;
import com.google.devtools.build.lib.util.FileTypeSet;
import java.util.List;
import java.util.Map;
/**
* A helper class to provide Attr module in Skylark.
*
*
It exposes functions (e.g. '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,
doc =
"Module for creating new attributes. "
+ "They are only for use with the rule function."
)
public final class SkylarkAttr {
// 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 FileType filter.";
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 "
+ "compatiblity), use providers instead.";
private static final String CONFIGURATION_ARG = "cfg";
private static final String CONFIGURATION_DOC =
"configuration of the attribute. " + "For example, use DATA_CFG or HOST_CFG.";
private static final String DEFAULT_ARG = "default";
private static final String DEFAULT_DOC = "the default value of the attribute.";
private static final String EXECUTABLE_ARG = "executable";
private static final String EXECUTABLE_DOC =
"True if the labels have to be executable. This means the label must refer to an "
+ "executable file, or to a rule that outputs an executable file. Access the labels "
+ "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";
private static final String PROVIDERS_ARG = "providers";
private static final String SINGLE_FILE_ARG = "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(Map arguments, String key) {
return arguments.containsKey(key) && arguments.get(key) != Runtime.NONE;
}
private static Attribute.Builder> createAttribute(
Type> type, Map arguments, FuncallExpression ast, Environment env)
throws EvalException, ConversionException {
// 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_ARG);
if (!EvalUtils.isNullOrNone(defaultValue)) {
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 : 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");
}
if (containsNonNoneKey(arguments, NON_EMPTY_ARG) && (Boolean) arguments.get(NON_EMPTY_ARG)) {
builder.setPropertyFlag("NON_EMPTY");
}
if (containsNonNoneKey(arguments, EXECUTABLE_ARG) && (Boolean) arguments.get(EXECUTABLE_ARG)) {
builder.setPropertyFlag("EXECUTABLE");
}
if (containsNonNoneKey(arguments, SINGLE_FILE_ARG)
&& (Boolean) arguments.get(SINGLE_FILE_ARG)) {
builder.setPropertyFlag("SINGLE_ARTIFACT");
}
if (containsNonNoneKey(arguments, ALLOW_FILES_ARG)) {
Object fileTypesObj = arguments.get(ALLOW_FILES_ARG);
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(
ast.getLocation(), "allow_files should be a boolean or a filetype object.");
}
} else if (type.equals(BuildType.LABEL) || type.equals(BuildType.LABEL_LIST)) {
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