/*
* 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.
*/
package com.facebook.buck.cli;
import com.facebook.buck.event.BuckEventBus;
import com.facebook.buck.json.BuildFileParseException;
import com.facebook.buck.log.Logger;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.model.BuildTargetException;
import com.facebook.buck.parser.BuildTargetPatternTargetNodeParser;
import com.facebook.buck.parser.Parser;
import com.facebook.buck.parser.ParserConfig;
import com.facebook.buck.parser.SpeculativeParsing;
import com.facebook.buck.parser.TargetNodeSpec;
import com.facebook.buck.query.QueryBuildTarget;
import com.facebook.buck.query.QueryFileTarget;
import com.facebook.buck.query.QueryTarget;
import com.facebook.buck.rules.Cell;
import com.facebook.buck.util.MoreMaps;
import com.google.common.base.Functions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.util.concurrent.ListeningExecutorService;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class TargetPatternEvaluator {
private static final Logger LOG = Logger.get(TargetPatternEvaluator.class);
private final Parser parser;
private final BuckEventBus eventBus;
private final boolean enableProfiling;
private final Path projectRoot;
private final CommandLineTargetNodeSpecParser targetNodeSpecParser;
private final BuckConfig buckConfig;
private final Cell rootCell;
private Map<String, ImmutableSet<QueryTarget>> resolvedTargets = new HashMap<>();
public TargetPatternEvaluator(
Cell rootCell,
BuckConfig buckConfig,
Parser parser,
BuckEventBus eventBus,
boolean enableProfiling) {
this.rootCell = rootCell;
this.parser = parser;
this.eventBus = eventBus;
this.enableProfiling = enableProfiling;
this.buckConfig = buckConfig;
this.projectRoot = rootCell.getFilesystem().getRootPath();
this.targetNodeSpecParser =
new CommandLineTargetNodeSpecParser(buckConfig, new BuildTargetPatternTargetNodeParser());
}
/** Attempts to parse and load the given collection of patterns. */
public void preloadTargetPatterns(Iterable<String> patterns, ListeningExecutorService executor)
throws InterruptedException, BuildFileParseException, BuildTargetException, IOException {
resolveTargetPatterns(patterns, executor);
}
ImmutableMap<String, ImmutableSet<QueryTarget>> resolveTargetPatterns(
Iterable<String> patterns, ListeningExecutorService executor)
throws InterruptedException, BuildFileParseException, BuildTargetException, IOException {
ImmutableMap.Builder<String, ImmutableSet<QueryTarget>> resolved = ImmutableMap.builder();
Map<String, String> unresolved = new HashMap<>();
for (String pattern : patterns) {
// First check if this pattern was resolved before.
ImmutableSet<QueryTarget> targets = resolvedTargets.get(pattern);
if (targets != null) {
resolved.put(pattern, targets);
continue;
}
// Check if this is an alias.
ImmutableSet<BuildTarget> aliasTargets = buckConfig.getBuildTargetsForAlias(pattern);
if (!aliasTargets.isEmpty()) {
for (BuildTarget alias : aliasTargets) {
unresolved.put(alias.getFullyQualifiedName(), pattern);
}
} else {
// Check if the pattern corresponds to a build target or a path.
if (pattern.contains("//") || pattern.startsWith(":")) {
unresolved.put(pattern, pattern);
} else {
ImmutableSet<QueryTarget> fileTargets = resolveFilePattern(pattern);
resolved.put(pattern, fileTargets);
resolvedTargets.put(pattern, fileTargets);
}
}
}
// Resolve any remaining target patterns using the parser.
ImmutableMap<String, ImmutableSet<QueryTarget>> results =
MoreMaps.transformKeys(
resolveBuildTargetPatterns(ImmutableList.copyOf(unresolved.keySet()), executor),
Functions.forMap(unresolved));
resolved.putAll(results);
resolvedTargets.putAll(results);
return resolved.build();
}
ImmutableSet<QueryTarget> resolveFilePattern(String pattern) throws IOException {
ImmutableSet<Path> filePaths =
PathArguments.getCanonicalFilesUnderProjectRoot(projectRoot, ImmutableList.of(pattern))
.relativePathsUnderProjectRoot;
ImmutableSet.Builder<QueryTarget> builder = ImmutableSortedSet.naturalOrder();
for (Path filePath : filePaths) {
builder.add(QueryFileTarget.of(filePath));
}
return builder.build();
}
ImmutableMap<String, ImmutableSet<QueryTarget>> resolveBuildTargetPatterns(
List<String> patterns, ListeningExecutorService executor)
throws InterruptedException, BuildFileParseException, BuildTargetException, IOException {
// Build up an ordered list of patterns and pass them to the parse to get resolved in one go.
// The returned list of nodes maintains the spec list ordering.
List<TargetNodeSpec> specs = new ArrayList<>();
for (String pattern : patterns) {
specs.addAll(targetNodeSpecParser.parse(rootCell.getCellPathResolver(), pattern));
}
ImmutableList<ImmutableSet<BuildTarget>> buildTargets =
parser.resolveTargetSpecs(
eventBus,
rootCell,
enableProfiling,
executor,
specs,
SpeculativeParsing.of(false),
// We disable mapping //path/to:lib to //path/to:lib#default,static
// because the query engine doesn't handle flavors very well.
ParserConfig.ApplyDefaultFlavorsMode.DISABLED);
LOG.verbose("Resolved target patterns %s -> targets %s", patterns, buildTargets);
// Convert the ordered result into a result map of pattern to set of resolved targets.
ImmutableMap.Builder<String, ImmutableSet<QueryTarget>> queryTargets = ImmutableMap.builder();
for (int index = 0; index < buildTargets.size(); index++) {
ImmutableSet<BuildTarget> targets = buildTargets.get(index);
// Sorting to have predictable results across different java libraries implementations.
ImmutableSet.Builder<QueryTarget> builder = ImmutableSortedSet.naturalOrder();
for (BuildTarget target : targets) {
builder.add(QueryBuildTarget.of(target));
}
queryTargets.put(patterns.get(index), builder.build());
}
return queryTargets.build();
}
}