/* * Copyright 2015-present Facebook, Inc. * * 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. */ // 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.facebook.buck.query; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.util.concurrent.ListeningExecutorService; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.function.Consumer; import javax.annotation.Nullable; /** * The environment of a Buck query that can evaluate queries to produce a result. * * <p>The query language is documented at docs/command/query.soy */ public interface QueryEnvironment { /** Type of an argument of a user-defined query function. */ enum ArgumentType { EXPRESSION, WORD, INTEGER, } /** Value of an argument of a user-defined query function. */ class Argument { private final ArgumentType type; @Nullable private final QueryExpression expression; @Nullable private final String word; private final int integer; private Argument( ArgumentType type, @Nullable QueryExpression expression, @Nullable String word, int integer) { this.type = type; this.expression = expression; this.word = word; this.integer = integer; } public static Argument of(QueryExpression expression) { return new Argument(ArgumentType.EXPRESSION, expression, null, 0); } public static Argument of(String word) { return new Argument(ArgumentType.WORD, null, word, 0); } public static Argument of(int integer) { return new Argument(ArgumentType.INTEGER, null, null, integer); } public ArgumentType getType() { return type; } public QueryExpression getExpression() { return Preconditions.checkNotNull(expression); } public String getWord() { return Preconditions.checkNotNull(word); } public int getInteger() { return integer; } @Override public String toString() { switch (type) { case WORD: return "'" + word + "'"; case EXPRESSION: return Preconditions.checkNotNull(expression).toString(); case INTEGER: return Integer.toString(integer); default: throw new IllegalStateException(); } } @Override public boolean equals(Object other) { return (other instanceof Argument) && equalTo((Argument) other); } public boolean equalTo(Argument other) { return type.equals(other.type) && integer == other.integer && Objects.equals(expression, other.expression) && Objects.equals(word, other.word); } @Override public int hashCode() { int h = 31; h = h * 17 + type.hashCode(); h = h * 17 + integer; if (expression != null) { h = h * 17 + expression.hashCode(); } if (word != null) { h = h * 17 + word.hashCode(); } return h; } } /** A user-defined query function. */ interface QueryFunction { /** Name of the function as it appears in the query language. */ String getName(); /** * The number of arguments that are required. The rest is optional. * * <p>This should be greater than or equal to zero and at smaller than or equal to the length of * the list returned by {@link #getArgumentTypes}. */ int getMandatoryArguments(); /** The types of the arguments of the function. */ ImmutableList<ArgumentType> getArgumentTypes(); /** * Called when a user-defined function is to be evaluated. * * @param env the query environment this function is evaluated in. * @param args the input arguments. These are type-checked against the specification returned by * {@link #getArgumentTypes} and {@link #getMandatoryArguments} */ ImmutableSet<QueryTarget> eval( QueryEnvironment env, ImmutableList<Argument> args, ListeningExecutorService executor) throws QueryException, InterruptedException; } /** * Returns the set of target nodes in the graph for the specified target pattern, in 'buck build' * syntax. */ ImmutableSet<QueryTarget> getTargetsMatchingPattern( String pattern, ListeningExecutorService executor) throws QueryException, InterruptedException; /** Returns the direct forward dependencies of the specified targets. */ ImmutableSet<QueryTarget> getFwdDeps(Iterable<QueryTarget> targets) throws QueryException, InterruptedException; /** * Applies {@code action} to each forward dependencies of the specified targets. * * <p>Might apply more than once to the same target, so {@code action} should be idempotent. */ default void forEachFwdDep(Iterable<QueryTarget> targets, Consumer<? super QueryTarget> action) throws QueryException, InterruptedException { getFwdDeps(targets).forEach(action); } /** Returns the direct reverse dependencies of the specified targets. */ Set<QueryTarget> getReverseDeps(Iterable<QueryTarget> targets) throws QueryException, InterruptedException; Set<QueryTarget> getInputs(QueryTarget target) throws QueryException; /** * Returns the forward transitive closure of all of the targets in "targets". Callers must ensure * that {@link #buildTransitiveClosure} has been called for the relevant subgraph. */ Set<QueryTarget> getTransitiveClosure(Set<QueryTarget> targets) throws QueryException, InterruptedException; /** * Construct the dependency graph for a depth-bounded forward transitive closure of all nodes in * "targetNodes". The identity of the calling expression is required to produce error messages. * * <p>If a larger transitive closure was already built, returns it to improve incrementality, * since all depth-constrained methods filter it after it is built anyway. */ void buildTransitiveClosure( Set<QueryTarget> targetNodes, int maxDepth, ListeningExecutorService executor) throws InterruptedException, QueryException; String getTargetKind(QueryTarget target) throws InterruptedException, QueryException; /** Returns the tests associated with the given target. */ ImmutableSet<QueryTarget> getTestsForTarget(QueryTarget target) throws InterruptedException, QueryException; /** Returns the build files that define the given targets. */ ImmutableSet<QueryTarget> getBuildFiles(Set<QueryTarget> targets) throws QueryException; /** Returns the targets that own one or more of the given files. */ ImmutableSet<QueryTarget> getFileOwners( ImmutableList<String> files, ListeningExecutorService executor) throws InterruptedException, QueryException; /** Returns the existing targets in the value of `attribute` of the given `target`. */ ImmutableSet<QueryTarget> getTargetsInAttribute(QueryTarget target, String attribute) throws InterruptedException, QueryException; /** Returns the objects in the `attribute` of the given `target` that satisfy `predicate` */ ImmutableSet<Object> filterAttributeContents( QueryTarget target, String attribute, final Predicate<Object> predicate) throws InterruptedException, QueryException; /** Returns the set of query functions implemented by this query environment. */ Iterable<QueryFunction> getFunctions(); /** List of the default query functions. */ List<QueryFunction> DEFAULT_QUERY_FUNCTIONS = ImmutableList.of( new AllPathsFunction(), new AttrFilterFunction(), new BuildFileFunction(), new DepsFunction(), new DepsFunction.FirstOrderDepsFunction(), new InputsFunction(), new FilterFunction(), new KindFunction(), new LabelsFunction(), new OwnerFunction(), new RdepsFunction(), new TestsOfFunction()); /** @return the {@link QueryTarget}s expanded from the given variable {@code name}. */ default ImmutableSet<QueryTarget> resolveTargetVariable(String name) { throw new IllegalArgumentException(String.format("unexpected target variable \"%s\"", name)); } }