diff options
author | 2015-04-10 00:37:55 +0000 | |
---|---|---|
committer | 2015-04-10 08:04:04 +0000 | |
commit | cc44636d2d538bc91e7291ed4607f2bdce356827 (patch) | |
tree | 6005a011781327692ee88fe090a4967da0a48385 /src/main/java/com/google/devtools/build/lib/query2/engine | |
parent | f7cf477913ae9c14f77a2d0d689b4207e3d51c37 (diff) |
Add a visibility predicate to blaze query.
The visible(x, y) query expression computes the subset of nodes in y visible
from all nodes in x.
RELNOTES: Add a visibility predicate to blaze query.
--
MOS_MIGRATED_REVID=90765071
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/query2/engine')
3 files changed, 225 insertions, 1 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/query2/engine/QueryEnvironment.java b/src/main/java/com/google/devtools/build/lib/query2/engine/QueryEnvironment.java index 46a7afd42d..1a7d8d844d 100644 --- a/src/main/java/com/google/devtools/build/lib/query2/engine/QueryEnvironment.java +++ b/src/main/java/com/google/devtools/build/lib/query2/engine/QueryEnvironment.java @@ -271,6 +271,11 @@ public interface QueryEnvironment<T> { String getLabel(T target); /** + * Returns the label of the target's package as a string, e.g. {@code //some/package} + */ + String getPackage(T target); + + /** * Returns whether the given target is a rule. */ boolean isRule(T target); @@ -331,6 +336,12 @@ public interface QueryEnvironment<T> { * @throws IllegalArgumentException if target is not a rule (according to {@link #isRule}) */ Iterable<String> getAttrAsString(T target, String attrName); + + /** + * Returns the set of package specifications the given target is visible from, represented as + * {@link QueryVisibility}s. + */ + Set<QueryVisibility<T>> getVisibility(T from) throws QueryException; } /** List of the default query functions. */ @@ -346,6 +357,7 @@ public interface QueryEnvironment<T> { new SomePathFunction(), new TestsFunction(), new DepsFunction(), - new RdepsFunction() + new RdepsFunction(), + new VisibleFunction() ); } diff --git a/src/main/java/com/google/devtools/build/lib/query2/engine/QueryVisibility.java b/src/main/java/com/google/devtools/build/lib/query2/engine/QueryVisibility.java new file mode 100644 index 0000000000..92a5200320 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/query2/engine/QueryVisibility.java @@ -0,0 +1,117 @@ +// Copyright 2015 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.query2.engine; + +import com.google.devtools.build.lib.query2.engine.QueryEnvironment.TargetAccessor; + +/** + * A predicate for targets included in the visibility list of a rule. + * + * <p>A rule's visibility is described by a set of {@link QueryVisibility}. Each element + * in the set corresponds to an entry in the rule's visibility attribute, or an entry in the + * packages attribute of an included package_group. + */ +public abstract class QueryVisibility<T> { + + /** Returns true if the visibility specification includes the given target's package. */ + public abstract boolean contains(T target); + + /** Global visibility. */ + @SuppressWarnings("unchecked") // Safe covariant cast. + public static <T> QueryVisibility<T> everything() { + return (QueryVisibility<T>) (Object) EVERYTHING; + } + + private static final QueryVisibility<?> EVERYTHING = new QueryVisibility<Object>() { + @Override + public boolean contains(Object target) { + return true; + } + + @Override + public String toString() { + return "QueryVisibility(//visibility:public)"; + } + }; + + /** + * Empty visibility. + * + * <p>This is useful as a sentinel to indicate that a package specification could be parsed into + * a {@link QueryVisibility}. Same-package visibility is handled separately by + * {@link VisibleFunction}. + */ + @SuppressWarnings("unchecked") // Safe covariant cast. + public static <T> QueryVisibility<T> nothing() { + return (QueryVisibility<T>) (Object) NOTHING; + } + + private static final QueryVisibility<?> NOTHING = new QueryVisibility<Object>() { + @Override + public boolean contains(Object target) { + return false; + } + + @Override + public String toString() { + return "QueryVisibility(//visibility:private)"; + } + }; + + /** + * Same-package visibility. + * + * <p>Targets are always visible to other targets in the same package. Additionally, targets + * under java/ are always visible to the corresponding package in javatests/. + */ + public static <T> QueryVisibility<T> samePackage(T from, TargetAccessor<T> accessor) { + return new SamePackageVisibility<>(from, accessor); + } + + private static final class SamePackageVisibility<T> extends QueryVisibility<T> { + + private static final String JAVA_PREFIX = "java/"; + private static final String JAVATESTS_PREFIX = "javatests/"; + + private final String packageName; + private final TargetAccessor<T> accessor; + + public SamePackageVisibility(T target, TargetAccessor<T> accessor) { + this.packageName = accessor.getPackage(target); + this.accessor = accessor; + } + + @Override + public boolean contains(T target) { + String other = accessor.getPackage(target); + if (packageName.equals(other)) { + return true; + } + + // packages in java/ are always visible from the corresponding package in javatests/ + if (other.startsWith(JAVATESTS_PREFIX) + && packageName.equals(JAVA_PREFIX + other.substring(JAVATESTS_PREFIX.length()))) { + return true; + } + + return false; + } + + @Override + public String toString() { + return String.format("QueryVisibility(samePackage=%s)", "<PACKAGE>"); + } + } +} diff --git a/src/main/java/com/google/devtools/build/lib/query2/engine/VisibleFunction.java b/src/main/java/com/google/devtools/build/lib/query2/engine/VisibleFunction.java new file mode 100644 index 0000000000..f268116805 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/query2/engine/VisibleFunction.java @@ -0,0 +1,95 @@ +// Copyright 2015 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.query2.engine; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.devtools.build.lib.query2.engine.QueryEnvironment.Argument; +import com.google.devtools.build.lib.query2.engine.QueryEnvironment.ArgumentType; +import com.google.devtools.build.lib.query2.engine.QueryEnvironment.QueryFunction; + +import java.util.List; +import java.util.Set; + +/** + * A visible(x, y) query expression, which computes the subset of nodes in y + * that are visible from all nodes in x. + * + * <pre>expr ::= VISIBILE '(' expr ',' expr ')'</pre> + * + * <p>Example: return targets from the package //bar/baz that are visible to //foo. + * + * <pre> + * visible(//foo, //bar/baz:*) + * </pre> + */ +public class VisibleFunction implements QueryFunction { + + @Override + public String getName() { + return "visible"; + } + + @Override + public int getMandatoryArguments() { + return 2; + } + + @Override + public List<ArgumentType> getArgumentTypes() { + return ImmutableList.of(ArgumentType.EXPRESSION, ArgumentType.EXPRESSION); + } + + @Override + public <T> Set<T> eval(QueryEnvironment<T> env, QueryExpression expression, List<Argument> args) + throws QueryException { + Set<T> toSet = args.get(0).getExpression().eval(env); + Set<T> targets = args.get(1).getExpression().eval(env); + + ImmutableSet.Builder<T> visible = ImmutableSet.builder(); + for (T target : targets) { + if (visibleToAll(env, toSet, target)) { + visible.add(target); + } + } + return visible.build(); + } + + /** + * Returns true if {@code target} is visible to all targets in {@code toSet}. + */ + private <T> boolean visibleToAll( + QueryEnvironment<T> env, Set<T> toSet, T target) throws QueryException { + for (T to : toSet) { + if (!visible(env, to, target)) { + return false; + } + } + return true; + } + + /** + * Returns true if the target {@code from} is visible to the target {@code to}. + */ + public <T> boolean visible(QueryEnvironment<T> env, T to, T from) throws QueryException { + Set<QueryVisibility<T>> visiblePackages = env.getAccessor().getVisibility(from); + for (QueryVisibility<T> spec : visiblePackages) { + if (spec.contains(to)) { + return true; + } + } + return false; + } +} |