| // Copyright 2015 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.actions; |
| |
| import com.google.common.base.Optional; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.common.collect.Ordering; |
| import com.google.devtools.build.lib.actions.FilesetTraversalParams.DirectTraversal; |
| import com.google.devtools.build.lib.actions.FilesetTraversalParams.DirectTraversalRoot; |
| import com.google.devtools.build.lib.actions.FilesetTraversalParams.PackageBoundaryMode; |
| import com.google.devtools.build.lib.cmdline.Label; |
| import com.google.devtools.build.lib.packages.FilesetEntry.SymlinkBehavior; |
| import com.google.devtools.build.lib.util.Fingerprint; |
| import com.google.devtools.build.lib.util.Preconditions; |
| import com.google.devtools.build.lib.vfs.Path; |
| import com.google.devtools.build.lib.vfs.PathFragment; |
| import com.google.devtools.build.lib.vfs.RootedPath; |
| |
| import java.util.Set; |
| |
| import javax.annotation.Nullable; |
| |
| /** Factory of {@link FilesetTraversalParams}. */ |
| public final class FilesetTraversalParamsFactory { |
| |
| /** |
| * Creates parameters for a recursive traversal request in a package. |
| * |
| * <p>"Recursive" means that a directory is traversed along with all of its subdirectories. Such |
| * a traversal is created when FilesetEntry.files is unspecified. |
| * |
| * @param ownerLabel the rule that created this object |
| * @param buildFile path of the BUILD file of the package to traverse |
| * @param destPath path in the Fileset's output directory that will be the root of files found |
| * in this directory |
| * @param excludes optional; set of files directly under this package's directory to exclude; |
| * files in subdirectories cannot be excluded |
| * @param symlinkBehaviorMode what to do with symlinks |
| * @param pkgBoundaryMode what to do when the traversal hits a subdirectory that is also a |
| * subpackage (contains a BUILD file) |
| */ |
| public static FilesetTraversalParams recursiveTraversalOfPackage(Label ownerLabel, |
| Artifact buildFile, PathFragment destPath, @Nullable Set<String> excludes, |
| SymlinkBehavior symlinkBehaviorMode, PackageBoundaryMode pkgBoundaryMode) { |
| Preconditions.checkState(buildFile.isSourceArtifact(), "%s", buildFile); |
| return new DirectoryTraversalParams(ownerLabel, DirectTraversalRootImpl.forPackage(buildFile), |
| true, destPath, excludes, symlinkBehaviorMode, pkgBoundaryMode, true, false); |
| } |
| |
| /** |
| * Creates parameters for a recursive traversal request in a directory. |
| * |
| * <p>"Recursive" means that a directory is traversed along with all of its subdirectories. Such |
| * a traversal is created when FilesetEntry.files is unspecified. |
| * |
| * @param ownerLabel the rule that created this object |
| * @param directoryToTraverse path of the directory to traverse |
| * @param destPath path in the Fileset's output directory that will be the root of files found |
| * in this directory |
| * @param excludes optional; set of files directly below this directory to exclude; files in |
| * subdirectories cannot be excluded |
| * @param symlinkBehaviorMode what to do with symlinks |
| * @param pkgBoundaryMode what to do when the traversal hits a subdirectory that is also a |
| * subpackage (contains a BUILD file) |
| */ |
| public static FilesetTraversalParams recursiveTraversalOfDirectory(Label ownerLabel, |
| Artifact directoryToTraverse, PathFragment destPath, @Nullable Set<String> excludes, |
| SymlinkBehavior symlinkBehaviorMode, PackageBoundaryMode pkgBoundaryMode) { |
| return new DirectoryTraversalParams(ownerLabel, |
| DirectTraversalRootImpl.forFileOrDirectory(directoryToTraverse), false, destPath, |
| excludes, symlinkBehaviorMode, pkgBoundaryMode, true, |
| !directoryToTraverse.isSourceArtifact()); |
| } |
| |
| /** |
| * Creates parameters for a file traversal request. |
| * |
| * <p>Such a traversal is created for every entry in FilesetEntry.files, when it is specified. |
| * |
| * @param ownerLabel the rule that created this object |
| * @param fileToTraverse the file to traverse; "traversal" means that if this file is actually a |
| * directory or a symlink to one then it'll be traversed as one |
| * @param destPath path in the Fileset's output directory that will be the name of this file's |
| * respective symlink there, or the root of files found (in case this is a directory) |
| * @param symlinkBehaviorMode what to do with symlinks |
| * @param pkgBoundaryMode what to do when the traversal hits a subdirectory that is also a |
| * subpackage (contains a BUILD file) |
| */ |
| public static FilesetTraversalParams fileTraversal(Label ownerLabel, Artifact fileToTraverse, |
| PathFragment destPath, SymlinkBehavior symlinkBehaviorMode, |
| PackageBoundaryMode pkgBoundaryMode) { |
| return new DirectoryTraversalParams(ownerLabel, |
| DirectTraversalRootImpl.forFileOrDirectory(fileToTraverse), false, destPath, null, |
| symlinkBehaviorMode, pkgBoundaryMode, false, !fileToTraverse.isSourceArtifact()); |
| } |
| |
| /** |
| * Creates traversal request parameters for a FilesetEntry wrapping another Fileset. |
| * |
| * @param ownerLabel the rule that created this object |
| * @param nested the traversal params that were used for the nested (inner) Fileset |
| * @param destDir path in the Fileset's output directory that will be the root of files coming |
| * from the nested Fileset |
| * @param excludes optional; set of files directly below (not in a subdirectory of) the nested |
| * Fileset that should be excluded from the outer Fileset |
| */ |
| public static FilesetTraversalParams nestedTraversal(Label ownerLabel, |
| FilesetTraversalParams nested, PathFragment destDir, @Nullable Set<String> excludes) { |
| // When srcdir is another Fileset, then files must be null so strip_prefix must also be null. |
| return new NestedTraversalParams(ownerLabel, nested, destDir, excludes); |
| } |
| |
| private abstract static class ParamsCommon implements FilesetTraversalParams { |
| private final Label ownerLabel; |
| private final PathFragment destDir; |
| private final ImmutableSet<String> excludes; |
| |
| ParamsCommon(Label ownerLabel, PathFragment destDir, @Nullable Set<String> excludes) { |
| this.ownerLabel = ownerLabel; |
| this.destDir = destDir; |
| if (excludes == null) { |
| this.excludes = ImmutableSet.<String>of(); |
| } else { |
| // Order the set for the sake of deterministic fingerprinting. |
| this.excludes = ImmutableSet.copyOf(Ordering.natural().immutableSortedCopy(excludes)); |
| } |
| } |
| |
| @Override |
| public Label getOwnerLabel() { |
| return ownerLabel; |
| } |
| |
| @Override |
| public Set<String> getExcludedFiles() { |
| return excludes; |
| } |
| |
| @Override |
| public PathFragment getDestPath() { |
| return destDir; |
| } |
| |
| protected final void commonFingerprint(Fingerprint fp) { |
| fp.addPath(destDir); |
| if (!excludes.isEmpty()) { |
| fp.addStrings(excludes); |
| } |
| } |
| } |
| |
| private static final class DirectTraversalImpl implements DirectTraversal { |
| private final DirectTraversalRoot root; |
| private final boolean isPackage; |
| private final boolean followSymlinks; |
| private final PackageBoundaryMode pkgBoundaryMode; |
| private final boolean isRecursive; |
| private final boolean isGenerated; |
| |
| DirectTraversalImpl(DirectTraversalRoot root, boolean isPackage, boolean followSymlinks, |
| PackageBoundaryMode pkgBoundaryMode, boolean isRecursive, boolean isGenerated) { |
| this.root = root; |
| this.isPackage = isPackage; |
| this.followSymlinks = followSymlinks; |
| this.pkgBoundaryMode = pkgBoundaryMode; |
| this.isRecursive = isRecursive; |
| this.isGenerated = isGenerated; |
| } |
| |
| @Override |
| public DirectTraversalRoot getRoot() { |
| return root; |
| } |
| |
| @Override |
| public boolean isPackage() { |
| return isPackage; |
| } |
| |
| @Override |
| public boolean isRecursive() { |
| return isRecursive; |
| } |
| |
| @Override |
| public boolean isGenerated() { |
| return isGenerated; |
| } |
| |
| @Override |
| public boolean isFollowingSymlinks() { |
| return followSymlinks; |
| } |
| |
| @Override |
| public PackageBoundaryMode getPackageBoundaryMode() { |
| return pkgBoundaryMode; |
| } |
| |
| void fingerprint(Fingerprint fp) { |
| fp.addPath(root.asRootedPath().asPath()); |
| fp.addBoolean(isPackage); |
| fp.addBoolean(followSymlinks); |
| fp.addBoolean(isRecursive); |
| fp.addBoolean(isGenerated); |
| pkgBoundaryMode.fingerprint(fp); |
| } |
| } |
| |
| private static final class DirectoryTraversalParams extends ParamsCommon { |
| private final DirectTraversalImpl traversal; |
| |
| DirectoryTraversalParams(Label ownerLabel, |
| DirectTraversalRoot root, |
| boolean isPackage, |
| PathFragment destPath, |
| @Nullable Set<String> excludes, |
| SymlinkBehavior symlinkBehaviorMode, |
| PackageBoundaryMode pkgBoundaryMode, |
| boolean isRecursive, |
| boolean isGenerated) { |
| super(ownerLabel, destPath, excludes); |
| traversal = new DirectTraversalImpl(root, isPackage, |
| symlinkBehaviorMode == SymlinkBehavior.DEREFERENCE, pkgBoundaryMode, isRecursive, |
| isGenerated); |
| } |
| |
| @Override |
| public Optional<DirectTraversal> getDirectTraversal() { |
| return Optional.<DirectTraversal>of(traversal); |
| } |
| |
| @Override |
| public Optional<FilesetTraversalParams> getNestedTraversal() { |
| return Optional.absent(); |
| } |
| |
| @Override |
| public void fingerprint(Fingerprint fp) { |
| commonFingerprint(fp); |
| traversal.fingerprint(fp); |
| } |
| } |
| |
| private static final class NestedTraversalParams extends ParamsCommon { |
| private final FilesetTraversalParams nested; |
| |
| public NestedTraversalParams(Label ownerLabel, FilesetTraversalParams nested, |
| PathFragment destDir, @Nullable Set<String> excludes) { |
| super(ownerLabel, destDir, excludes); |
| this.nested = nested; |
| } |
| |
| @Override |
| public Optional<DirectTraversal> getDirectTraversal() { |
| return Optional.absent(); |
| } |
| |
| @Override |
| public Optional<FilesetTraversalParams> getNestedTraversal() { |
| return Optional.of(nested); |
| } |
| |
| @Override |
| public void fingerprint(Fingerprint fp) { |
| commonFingerprint(fp); |
| nested.fingerprint(fp); |
| } |
| } |
| |
| private static final class DirectTraversalRootImpl implements DirectTraversalRoot { |
| private final Path rootDir; |
| private final PathFragment relativeDir; |
| |
| static DirectTraversalRoot forPackage(Artifact buildFile) { |
| return new DirectTraversalRootImpl(buildFile.getRoot().getPath(), |
| buildFile.getRootRelativePath().getParentDirectory()); |
| } |
| |
| static DirectTraversalRoot forFileOrDirectory(Artifact fileOrDirectory) { |
| return new DirectTraversalRootImpl(fileOrDirectory.getRoot().getPath(), |
| fileOrDirectory.getRootRelativePath()); |
| } |
| |
| private DirectTraversalRootImpl(Path rootDir, PathFragment relativeDir) { |
| this.rootDir = rootDir; |
| this.relativeDir = relativeDir; |
| } |
| |
| @Override |
| public Path getRootPart() { |
| return rootDir; |
| } |
| |
| @Override |
| public PathFragment getRelativePart() { |
| return relativeDir; |
| } |
| |
| @Override |
| public RootedPath asRootedPath() { |
| return RootedPath.toRootedPath(rootDir, relativeDir); |
| } |
| } |
| } |