aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/packages
diff options
context:
space:
mode:
authorGravatar Han-Wen Nienhuys <hanwen@google.com>2016-01-15 15:54:12 +0000
committerGravatar Damien Martin-Guillerez <dmarting@google.com>2016-01-15 22:29:37 +0000
commit67e6f98d7659ac41154f93c39e548ba0ecc43c80 (patch)
treeaa1804d5058d3e0d53c58ab1af4e6301da1fd190 /src/main/java/com/google/devtools/build/lib/packages
parent8cc0541da1ec912568c8df2fc13a9b72b668cda2 (diff)
Add native.rule(NAME), which returns the attributes of a previously defined rule.
Add native.rules(), which returns all previously defined rules. These primitives can be used to write Skylark extensions that aggregate over the contents of a BUILD file, eg. def instantiate_if_needed(name): n = name + "_wrapped" if not native.rule(n): py_test(name = n , ... ) def archive_cc_src_files(tag): all_src = [] for r in native.rules().values(): if tag in r["tags"] and r["kind"] == "cc_library": all_src.append(r["srcs"]) native.genrule(cmd = "zip $@ $^", srcs = all_src, outs = ["out.zip"]) RELNOTES: Support aggregation over existing rules in Skylark extensions through native.rules and native.rule. -- MOS_MIGRATED_REVID=112249050
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/packages')
-rw-r--r--src/main/java/com/google/devtools/build/lib/packages/Package.java7
-rw-r--r--src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java111
-rw-r--r--src/main/java/com/google/devtools/build/lib/packages/SkylarkNativeModule.java133
3 files changed, 223 insertions, 28 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/packages/Package.java b/src/main/java/com/google/devtools/build/lib/packages/Package.java
index bc7804475c..ce49383f20 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/Package.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/Package.java
@@ -49,6 +49,8 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
+import javax.annotation.Nullable;
+
/**
* A package, which is a container of {@link Rule}s, each of
* which contains a dictionary of named attributes.
@@ -1045,6 +1047,11 @@ public class Package {
return Package.getTargets(targets);
}
+ @Nullable
+ public Target getTarget(String name) {
+ return targets.get(name);
+ }
+
/**
* Returns an (immutable, unordered) view of all the targets belonging to
* this package which are instances of the specified class.
diff --git a/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java b/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java
index 1de065eed8..f738a0d252 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java
@@ -66,9 +66,12 @@ import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.UnixGlob;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.TreeMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@@ -828,6 +831,112 @@ public final class PackageFactory {
}
};
+ @Nullable
+ static Map<String, Object> callGetRuleFunction(
+ String name, FuncallExpression ast, Environment env)
+ throws EvalException, ConversionException {
+ PackageContext context = getContext(env, ast);
+ Target target = context.pkgBuilder.getTarget(name);
+
+ return targetDict(target);
+ }
+
+ @Nullable
+ private static Map<String, Object> targetDict(Target target) {
+ if (target == null && !(target instanceof Rule)) {
+ return null;
+ }
+ Map<String, Object> values = new TreeMap<>();
+
+ Rule rule = (Rule) target;
+ AttributeContainer cont = rule.getAttributeContainer();
+ for (Attribute attr : rule.getAttributes()) {
+ if (!Character.isAlphabetic(attr.getName().charAt(0))) {
+ continue;
+ }
+
+ Object val = skylarkifyValue(cont.getAttr(attr.getName()), target.getPackage());
+ if (val == null) {
+ continue;
+ }
+ values.put(attr.getName(), val);
+ }
+
+ values.put("name", rule.getName());
+ values.put("kind", rule.getRuleClass());
+ return values;
+ }
+
+ /**
+ * Converts back to type that will work in BUILD and skylark,
+ * such as string instead of label, SkylarkList instead of List,
+ * Returns null if we don't want to export the value.
+ *
+ * <p>All of the types returned are immutable. If we want, we can change this to
+ * immutable in the future, but this is the safe choice for now.
+ */
+ private static Object skylarkifyValue(Object val, Package pkg) {
+ if (val == null) {
+ return null;
+ }
+ if (val instanceof Integer) {
+ return val;
+ }
+ if (val instanceof String) {
+ return val;
+ }
+ if (val instanceof Label) {
+ Label l = (Label) val;
+ if (l.getPackageName().equals(pkg.getName())) {
+ return ":" + l.getName();
+ }
+ return l.getCanonicalForm();
+ }
+ if (val instanceof List) {
+ List<Object> l = new ArrayList<>();
+ for (Object o : (List) val) {
+ l.add(skylarkifyValue(o, pkg));
+ }
+
+ return SkylarkList.Tuple.copyOf(l);
+ }
+ if (val instanceof Map) {
+ Map<Object, Object> m = new TreeMap<>();
+ for (Map.Entry<?, ?> e : ((Map<?, ?>) val).entrySet()) {
+ m.put(skylarkifyValue(e.getKey(), pkg), skylarkifyValue(e.getValue(), pkg));
+ }
+ return m;
+ }
+ if (val.getClass().isAnonymousClass()) {
+ // Computed defaults. They will be represented as
+ // "deprecation": com.google.devtools.build.lib.analysis.BaseRuleClasses$2@6960884a,
+ // Filter them until we invent something more clever.
+ return null;
+ }
+
+ // Add any types we want to allow through here.
+ return null;
+ }
+
+ static Map callGetRulesFunction(FuncallExpression ast, Environment env) throws EvalException {
+
+ PackageContext context = getContext(env, ast);
+ Collection<Target> targets = context.pkgBuilder.getTargets();
+
+ // Sort by name.
+ Map<String, Map<String, Object>> rules = new TreeMap<>();
+ for (Target t : targets) {
+ if (t instanceof Rule) {
+ Map<String, Object> m = targetDict(t);
+ Preconditions.checkNotNull(m);
+
+ rules.put(t.getName(), m);
+ }
+ }
+
+ return rules;
+ }
+
static Runtime.NoneType callPackageFunction(String name, Object packagesO, Object includesO,
FuncallExpression ast, Environment env) throws EvalException, ConversionException {
PackageContext context = getContext(env, ast);
@@ -1082,7 +1191,7 @@ public final class PackageFactory {
}
/**
- * Same as {@link #createPackage}, but does the required validation of "packageName" first,
+ * Same as createPackage, but does the required validation of "packageName" first,
* throwing a {@link NoSuchPackageException} if the name is invalid.
*/
@VisibleForTesting
diff --git a/src/main/java/com/google/devtools/build/lib/packages/SkylarkNativeModule.java b/src/main/java/com/google/devtools/build/lib/packages/SkylarkNativeModule.java
index be648b022e..ad73b169c9 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/SkylarkNativeModule.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/SkylarkNativeModule.java
@@ -26,6 +26,8 @@ import com.google.devtools.build.lib.syntax.SkylarkList;
import com.google.devtools.build.lib.syntax.SkylarkSignatureProcessor;
import com.google.devtools.build.lib.syntax.Type.ConversionException;
+import java.util.Map;
+
/**
* A class for the Skylark native module.
*/
@@ -39,34 +41,111 @@ import com.google.devtools.build.lib.syntax.Type.ConversionException;
public class SkylarkNativeModule {
// TODO(bazel-team): shouldn't we return a SkylarkList instead?
- @SkylarkSignature(name = "glob", objectType = SkylarkNativeModule.class,
- returnType = SkylarkList.class,
- doc = "Glob returns a list of every file in the current package that:<ul>\n"
- + "<li>Matches at least one pattern in <code>include</code>.</li>\n"
- + "<li>Does not match any of the patterns in <code>exclude</code> "
- + "(default <code>[]</code>).</li></ul>\n"
- + "If the <code>exclude_directories</code> argument is enabled (set to <code>1</code>), "
- + "files of type directory will be omitted from the results (default <code>1</code>).",
- mandatoryPositionals = {
- @Param(name = "include", type = SkylarkList.class, generic1 = String.class,
- defaultValue = "[]", doc = "The list of glob patterns to include.")},
- optionalPositionals = {
- @Param(name = "exclude", type = SkylarkList.class, generic1 = String.class,
- defaultValue = "[]", doc = "The list of glob patterns to exclude."),
+ @SkylarkSignature(
+ name = "glob",
+ objectType = SkylarkNativeModule.class,
+ returnType = SkylarkList.class,
+ doc =
+ "Glob returns a list of every file in the current package that:<ul>\n"
+ + "<li>Matches at least one pattern in <code>include</code>.</li>\n"
+ + "<li>Does not match any of the patterns in <code>exclude</code> "
+ + "(default <code>[]</code>).</li></ul>\n"
+ + "If the <code>exclude_directories</code> argument is enabled (set to <code>1</code>), "
+ + "files of type directory will be omitted from the results (default <code>1</code>).",
+ mandatoryPositionals = {
+ @Param(
+ name = "include",
+ type = SkylarkList.class,
+ generic1 = String.class,
+ defaultValue = "[]",
+ doc = "The list of glob patterns to include."
+ )
+ },
+ optionalPositionals = {
+ @Param(
+ name = "exclude",
+ type = SkylarkList.class,
+ generic1 = String.class,
+ defaultValue = "[]",
+ doc = "The list of glob patterns to exclude."
+ ),
// TODO(bazel-team): accept booleans as well as integers? (and eventually migrate?)
- @Param(name = "exclude_directories", type = Integer.class, defaultValue = "1",
- doc = "A flag whether to exclude directories or not.")},
- useAst = true, useEnvironment = true)
- private static final BuiltinFunction glob = new BuiltinFunction("glob") {
- public SkylarkList invoke(
- SkylarkList include, SkylarkList exclude,
- Integer excludeDirectories, FuncallExpression ast, Environment env)
- throws EvalException, ConversionException, InterruptedException {
- env.checkLoadingPhase("native.glob", ast.getLocation());
- return PackageFactory.callGlob(
- null, false, include, exclude, excludeDirectories != 0, ast, env);
- }
- };
+ @Param(
+ name = "exclude_directories",
+ type = Integer.class,
+ defaultValue = "1",
+ doc = "A flag whether to exclude directories or not."
+ )
+ },
+ useAst = true,
+ useEnvironment = true
+ )
+ private static final BuiltinFunction glob =
+ new BuiltinFunction("glob") {
+ public SkylarkList invoke(
+ SkylarkList include,
+ SkylarkList exclude,
+ Integer excludeDirectories,
+ FuncallExpression ast,
+ Environment env)
+ throws EvalException, ConversionException, InterruptedException {
+ env.checkLoadingPhase("native.glob", ast.getLocation());
+ return PackageFactory.callGlob(
+ null, false, include, exclude, excludeDirectories != 0, ast, env);
+ }
+ };
+
+ @SkylarkSignature(
+ name = "rule",
+ objectType = SkylarkNativeModule.class,
+ returnType = Object.class,
+ doc =
+ "Returns a dictionary representing the attributes of a previously defined rule, "
+ + "or None if the rule does not exist.",
+ mandatoryPositionals = {
+ @Param(name = "name", type = String.class, doc = "The name of the rule.")
+ },
+ useAst = true,
+ useEnvironment = true
+ )
+ private static final BuiltinFunction getRule =
+ new BuiltinFunction("rule") {
+ public Object invoke(String name, FuncallExpression ast, Environment env)
+ throws EvalException, InterruptedException {
+ env.checkLoadingPhase("native.rule", ast.getLocation());
+ Map<String, Object> rule = PackageFactory.callGetRuleFunction(name, ast, env);
+ if (rule != null) {
+ return rule;
+ }
+
+ return Runtime.NONE;
+ }
+ };
+
+ /*
+ If necessary, we could allow filtering by tag (anytag, alltags), name (regexp?), kind ?
+ For now, we ignore this, since users can implement it in Skylark.
+ */
+ @SkylarkSignature(
+ name = "rules",
+ objectType = SkylarkNativeModule.class,
+ returnType = Map.class,
+ doc =
+ "Returns a dict containing all the rules instantiated so far. "
+ + "The map key is the name of the rule. The map value is equivalent to the "
+ + "get_rule output for that rule.",
+ mandatoryPositionals = {},
+ useAst = true,
+ useEnvironment = true
+ )
+ private static final BuiltinFunction getRules =
+ new BuiltinFunction("rules") {
+ public Map invoke(FuncallExpression ast, Environment env)
+ throws EvalException, InterruptedException {
+ env.checkLoadingPhase("native.rules", ast.getLocation());
+ return PackageFactory.callGetRulesFunction(ast, env);
+ }
+ };
@SkylarkSignature(name = "package_group", objectType = SkylarkNativeModule.class,
returnType = Runtime.NoneType.class,