| /* |
| * Copyright 2017 The Bazel Authors. 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.idea.blaze.base.sync.sharding; |
| |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.Lists; |
| import com.google.common.collect.Maps; |
| import com.google.common.util.concurrent.Futures; |
| import com.google.common.util.concurrent.ListenableFuture; |
| import com.google.idea.blaze.base.async.FutureUtil; |
| import com.google.idea.blaze.base.async.FutureUtil.FutureResult; |
| import com.google.idea.blaze.base.bazel.BuildSystemProvider; |
| import com.google.idea.blaze.base.io.FileAttributeProvider; |
| import com.google.idea.blaze.base.model.primitives.TargetExpression; |
| import com.google.idea.blaze.base.model.primitives.WorkspacePath; |
| import com.google.idea.blaze.base.prefetch.FetchExecutor; |
| import com.google.idea.blaze.base.scope.BlazeContext; |
| import com.google.idea.blaze.base.sync.workspace.WorkspacePathResolver; |
| import com.google.idea.blaze.base.util.WorkspacePathUtil; |
| import java.io.File; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.Set; |
| import java.util.function.Predicate; |
| import java.util.stream.Collectors; |
| import javax.annotation.Nullable; |
| |
| /** |
| * Traverses blaze packages specified by wildcard target patterns, expanding to a set of |
| * single-package target patterns. |
| */ |
| class PackageLister { |
| |
| private PackageLister() {} |
| |
| /** The set of blaze packages to prefetch prior to traversing the directory tree. */ |
| static Set<File> getDirectoriesToPrefetch( |
| WorkspacePathResolver pathResolver, |
| Collection<WildcardTargetPattern> includes, |
| Predicate<WorkspacePath> excluded) { |
| Set<WorkspacePath> prefetchPaths = new HashSet<>(); |
| for (WildcardTargetPattern pattern : includes) { |
| WorkspacePath workspacePath = pattern.getBasePackage(); |
| if (excluded.test(workspacePath)) { |
| continue; |
| } |
| prefetchPaths.add(workspacePath); |
| } |
| return WorkspacePathUtil.calculateMinimalWorkspacePaths(prefetchPaths) |
| .stream() |
| .map(pathResolver::resolveToFile) |
| .collect(Collectors.toSet()); |
| } |
| |
| /** |
| * Expands all-in-package-recursive wildcard targets into all-in-single-package targets by |
| * traversing the file system, looking for child blaze packages. |
| * |
| * <p>Returns null if directory traversal failed or was cancelled. |
| */ |
| @Nullable |
| static Map<TargetExpression, List<TargetExpression>> expandPackageTargets( |
| BuildSystemProvider provider, |
| BlazeContext context, |
| WorkspacePathResolver pathResolver, |
| Collection<WildcardTargetPattern> wildcardPatterns) { |
| List<ListenableFuture<Entry<TargetExpression, List<TargetExpression>>>> futures = |
| Lists.newArrayList(); |
| for (WildcardTargetPattern pattern : wildcardPatterns) { |
| if (!pattern.isRecursive() || pattern.toString().startsWith("-")) { |
| continue; |
| } |
| File dir = pathResolver.resolveToFile(pattern.getBasePackage()); |
| if (!FileAttributeProvider.getInstance().isDirectory(dir)) { |
| continue; |
| } |
| futures.add( |
| FetchExecutor.EXECUTOR.submit( |
| () -> { |
| List<TargetExpression> expandedTargets = new ArrayList<>(); |
| traversePackageRecursively(provider, pathResolver, dir, expandedTargets); |
| return Maps.immutableEntry(pattern.originalPattern, expandedTargets); |
| })); |
| } |
| if (futures.isEmpty()) { |
| return ImmutableMap.of(); |
| } |
| FutureResult<List<Entry<TargetExpression, List<TargetExpression>>>> result = |
| FutureUtil.waitForFuture(context, Futures.allAsList(futures)) |
| .withProgressMessage("Expanding wildcard target patterns...") |
| .timed("ExpandWildcardTargets") |
| .onError("Expanding wildcard target patterns failed") |
| .run(); |
| if (!result.success()) { |
| return null; |
| } |
| return result |
| .result() |
| .stream() |
| .collect(Collectors.toMap(Entry::getKey, Entry::getValue, (x, y) -> x)); |
| } |
| |
| private static void traversePackageRecursively( |
| BuildSystemProvider provider, |
| WorkspacePathResolver pathResolver, |
| File dir, |
| List<TargetExpression> output) { |
| WorkspacePath path = pathResolver.getWorkspacePath(dir); |
| if (path == null) { |
| return; |
| } |
| if (provider.findBuildFileInDirectory(dir) != null) { |
| output.add(TargetExpression.allFromPackageNonRecursive(path)); |
| } |
| FileAttributeProvider attributeProvider = FileAttributeProvider.getInstance(); |
| File[] children = attributeProvider.listFiles(dir); |
| if (children == null) { |
| return; |
| } |
| for (File child : children) { |
| if (attributeProvider.isDirectory(child)) { |
| traversePackageRecursively(provider, pathResolver, child, output); |
| } |
| } |
| } |
| } |