// Copyright 2018 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.devtools.build.lib.skyframe; import static com.google.common.collect.ImmutableSet.toImmutableSet; import com.google.common.base.Function; import com.google.common.base.Objects; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; import com.google.devtools.build.lib.cmdline.RepositoryName; import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.events.ExtendedEventHandler; import com.google.devtools.build.lib.vfs.PathFragment; import com.google.devtools.build.lib.vfs.Root; import com.google.devtools.build.lib.vfs.RootedPath; import com.google.devtools.build.skyframe.SkyKey; import com.google.devtools.build.skyframe.SkyValue; import com.google.devtools.build.skyframe.WalkableGraph; import java.util.List; import java.util.Map; import java.util.Set; /** Looks up values under {@link TraversalInfo}s of given roots in a {@link WalkableGraph}. */ public class TraversalInfoRootPackageExtractor implements RootPackageExtractor { public Iterable getPackagesFromRoots( WalkableGraph graph, List roots, ExtendedEventHandler eventHandler, RepositoryName repository, PathFragment directory, ImmutableSet blacklistedSubdirectories, ImmutableSet excludedSubdirectories) throws InterruptedException { // If we found a TargetsBelowDirectory pattern in the universe that contains this directory, // then we can look for packages in and under it in the graph. If we didn't find one, then the // directory wasn't in the universe, so return an empty list. ImmutableList.Builder builder = ImmutableList.builder(); for (Root root : roots) { RootedPath rootedDir = RootedPath.toRootedPath(root, directory); TraversalInfo info = new TraversalInfo(rootedDir, blacklistedSubdirectories, excludedSubdirectories); collectPackagesUnder(graph, eventHandler, repository, ImmutableSet.of(info), builder); } return builder.build(); } private void collectPackagesUnder( WalkableGraph graph, ExtendedEventHandler eventHandler, final RepositoryName repository, Set traversals, ImmutableList.Builder builder) throws InterruptedException { Map traversalToKeyMap = Maps.asMap( traversals, new Function() { @Override public SkyKey apply(TraversalInfo traversalInfo) { return CollectPackagesUnderDirectoryValue.key( repository, traversalInfo.rootedDir, traversalInfo.blacklistedSubdirectories); } }); Map values = graph.getSuccessfulValues(traversalToKeyMap.values()); ImmutableSet.Builder subdirTraversalBuilder = ImmutableSet.builder(); for (Map.Entry entry : traversalToKeyMap.entrySet()) { TraversalInfo info = entry.getKey(); SkyKey key = entry.getValue(); SkyValue val = values.get(key); CollectPackagesUnderDirectoryValue collectPackagesValue = (CollectPackagesUnderDirectoryValue) val; if (collectPackagesValue != null) { if (collectPackagesValue.isDirectoryPackage()) { builder.add(info.rootedDir.getRootRelativePath()); } if (collectPackagesValue.getErrorMessage() != null) { eventHandler.handle(Event.error(collectPackagesValue.getErrorMessage())); } ImmutableMap subdirectoryTransitivelyContainsPackages = collectPackagesValue.getSubdirectoryTransitivelyContainsPackagesOrErrors(); for (RootedPath subdirectory : subdirectoryTransitivelyContainsPackages.keySet()) { if (subdirectoryTransitivelyContainsPackages.get(subdirectory)) { PathFragment subdirectoryRelativePath = subdirectory.getRootRelativePath(); ImmutableSet blacklistedSubdirectoriesBeneathThisSubdirectory = info.blacklistedSubdirectories .stream() .filter(pathFragment -> pathFragment.startsWith(subdirectoryRelativePath)) .collect(toImmutableSet()); ImmutableSet excludedSubdirectoriesBeneathThisSubdirectory = info.excludedSubdirectories .stream() .filter(pathFragment -> pathFragment.startsWith(subdirectoryRelativePath)) .collect(toImmutableSet()); if (!excludedSubdirectoriesBeneathThisSubdirectory.contains(subdirectoryRelativePath)) { subdirTraversalBuilder.add( new TraversalInfo( subdirectory, blacklistedSubdirectoriesBeneathThisSubdirectory, excludedSubdirectoriesBeneathThisSubdirectory)); } } } } } ImmutableSet subdirTraversals = subdirTraversalBuilder.build(); if (!subdirTraversals.isEmpty()) { collectPackagesUnder(graph, eventHandler, repository, subdirTraversals, builder); } } private static final class TraversalInfo { private final RootedPath rootedDir; // Set of blacklisted directories. The graph is assumed to be prepopulated with // CollectPackagesUnderDirectoryValue nodes whose keys have blacklisted packages embedded in // them. Therefore, we need to be careful to request and use the same sort of keys here in our // traversal. private final ImmutableSet blacklistedSubdirectories; // Set of directories, targets under which should be excluded from the traversal results. // Excluded directory information isn't part of the graph keys in the prepopulated graph, so we // need to perform the filtering ourselves. private final ImmutableSet excludedSubdirectories; private TraversalInfo( RootedPath rootedDir, ImmutableSet blacklistedSubdirectories, ImmutableSet excludedSubdirectories) { this.rootedDir = rootedDir; this.blacklistedSubdirectories = blacklistedSubdirectories; this.excludedSubdirectories = excludedSubdirectories; } @Override public int hashCode() { return Objects.hashCode(rootedDir, blacklistedSubdirectories, excludedSubdirectories); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj instanceof TraversalInfo) { TraversalInfo otherTraversal = (TraversalInfo) obj; return Objects.equal(rootedDir, otherTraversal.rootedDir) && Objects.equal(blacklistedSubdirectories, otherTraversal.blacklistedSubdirectories) && Objects.equal(excludedSubdirectories, otherTraversal.excludedSubdirectories); } return false; } } }