/*
* Copyright 2016-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.
*/
package com.facebook.buck.rules.macros;
import com.facebook.buck.event.PerfEventId;
import com.facebook.buck.event.SimplePerfEvent;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.model.MacroException;
import com.facebook.buck.parser.BuildTargetPatternParser;
import com.facebook.buck.query.QueryBuildTarget;
import com.facebook.buck.query.QueryException;
import com.facebook.buck.query.QueryExpression;
import com.facebook.buck.query.QueryTarget;
import com.facebook.buck.rules.BuildRuleResolver;
import com.facebook.buck.rules.CellPathResolver;
import com.facebook.buck.rules.TargetGraph;
import com.facebook.buck.rules.query.GraphEnhancementQueryEnvironment;
import com.facebook.buck.rules.query.Query;
import com.facebook.buck.util.HumanReadableException;
import com.google.common.base.CharMatcher;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;
/** Abstract base class for the query_targets and query_outputs macros */
public abstract class QueryMacroExpander<T extends QueryMacro> extends AbstractMacroExpander<T> {
private ListeningExecutorService executorService;
private Optional<TargetGraph> targetGraph;
public QueryMacroExpander(Optional<TargetGraph> targetGraph) {
this.targetGraph = targetGraph;
this.executorService = MoreExecutors.newDirectExecutorService();
}
private Stream<BuildTarget> extractTargets(
BuildTarget target,
CellPathResolver cellNames,
Optional<BuildRuleResolver> resolver,
T input) {
String queryExpression = CharMatcher.anyOf("\"'").trimFrom(input.getQuery().getQuery());
final GraphEnhancementQueryEnvironment env =
new GraphEnhancementQueryEnvironment(
resolver,
targetGraph,
cellNames,
BuildTargetPatternParser.forBaseName(target.getBaseName()),
ImmutableSet.of());
try {
QueryExpression parsedExp = QueryExpression.parse(queryExpression, env);
HashSet<String> targetLiterals = new HashSet<>();
parsedExp.collectTargetPatterns(targetLiterals);
return targetLiterals
.stream()
.flatMap(
pattern -> {
try {
return env.getTargetsMatchingPattern(pattern, executorService).stream();
} catch (Exception e) {
throw new HumanReadableException(
e, "Error parsing target expression %s for target %s", pattern, target);
}
})
.map(
queryTarget -> {
Preconditions.checkState(queryTarget instanceof QueryBuildTarget);
return ((QueryBuildTarget) queryTarget).getBuildTarget();
});
} catch (QueryException e) {
throw new HumanReadableException("Error executing query in macro for target %s", target, e);
}
}
Stream<QueryTarget> resolveQuery(
BuildTarget target,
CellPathResolver cellNames,
final BuildRuleResolver resolver,
String queryExpression)
throws MacroException {
GraphEnhancementQueryEnvironment env =
new GraphEnhancementQueryEnvironment(
Optional.of(resolver),
targetGraph,
cellNames,
BuildTargetPatternParser.forBaseName(target.getBaseName()),
ImmutableSet.of());
try (SimplePerfEvent.Scope ignored =
SimplePerfEvent.scope(
Optional.ofNullable(resolver.getEventBus()),
PerfEventId.of("resolve_query_macro"),
"target",
target.toString())) {
QueryExpression parsedExp = QueryExpression.parse(queryExpression, env);
Set<QueryTarget> queryTargets = parsedExp.eval(env, executorService);
return queryTargets.stream();
} catch (QueryException e) {
throw new MacroException("Error parsing/executing query from macro", e);
} catch (InterruptedException e) {
throw new MacroException("Error executing query", e);
}
}
abstract T fromQuery(Query query);
@Override
protected final T parse(
BuildTarget target, CellPathResolver cellNames, ImmutableList<String> input)
throws MacroException {
if (input.size() != 1) {
throw new MacroException("One quoted query expression is expected");
}
return fromQuery(Query.of(input.get(0)));
}
abstract boolean detectsTargetGraphOnlyDeps();
@Override
public void extractParseTimeDepsFrom(
BuildTarget target,
CellPathResolver cellNames,
T input,
ImmutableCollection.Builder<BuildTarget> buildDepsBuilder,
ImmutableCollection.Builder<BuildTarget> targetGraphOnlyDepsBuilder) {
extractTargets(target, cellNames, Optional.empty(), input)
.forEach(
(detectsTargetGraphOnlyDeps() ? targetGraphOnlyDepsBuilder : buildDepsBuilder)::add);
}
}