diff options
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/rules/fileset/FilesetLinks.java')
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/rules/fileset/FilesetLinks.java | 218 |
1 files changed, 218 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/rules/fileset/FilesetLinks.java b/src/main/java/com/google/devtools/build/lib/rules/fileset/FilesetLinks.java new file mode 100644 index 0000000000..d523edc356 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/fileset/FilesetLinks.java @@ -0,0 +1,218 @@ +// Copyright 2014 Google Inc. 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.rules.fileset; + +import com.google.common.base.Preconditions; +import com.google.devtools.build.lib.syntax.FilesetEntry; +import com.google.devtools.build.lib.vfs.Path; +import com.google.devtools.build.lib.vfs.PathFragment; + +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * FilesetLinks manages the set of links added to a Fileset. If two links conflict, the first wins. + * + * <p>FilesetLinks is FileSystem-aware. For example, if you first create a link + * (a/b/c, foo), a subsequent call to link (a/b, bar) is a no-op. + * This is because the first link requires us to create a directory "a/b", + * so "a/b" cannot also link to "bar". + * + * <p>TODO(bazel-team): Consider warning if we have such a conflict; we don't do that currently. + */ +public interface FilesetLinks { + + /** + * Get late directory information for a source. + * + * @param src The source to search for. + * @return The late directory info, or null if none was found. + */ + public LateDirectoryInfo getLateDirectoryInfo(PathFragment src); + + public boolean putLateDirectoryInfo(PathFragment src, LateDirectoryInfo lateDir); + + /** + * Add specified file as a symlink. + * + * The behavior when the target file is a symlink depends on the + * symlinkBehavior parameter (see comments for FilesetEntry.SymlinkBehavior). + * + * @param src The root-relative symlink path. + * @param target The symlink target. + */ + public void addFile(PathFragment src, Path target, String metadata, + FilesetEntry.SymlinkBehavior symlinkBehavior) + throws IOException; + + /** + * Add all late directories as symlinks. This function should be called only + * after all recursions have completed, but before getData or getSymlinks are + * called. + */ + public void addLateDirectories() throws IOException; + + /** + * Adds the given symlink to the tree. + * + * @param fromFrag The root-relative symlink path. + * @param toFrag The symlink target. + * @return true iff the symlink was added. + */ + public boolean addLink(PathFragment fromFrag, PathFragment toFrag, String dataVal); + + /** + * @return The unmodifiable map of symlinks. + */ + public Map<PathFragment, PathFragment> getSymlinks(); + + /** + * @return The unmodifiable map of metadata. + */ + public Map<PathFragment, String> getData(); + + /** + * A data structure for containing all the information about a directory that + * is late-added. This means the directory is skipped unless we need to + * recurse into it later. If the directory is never recursed into, we will + * create a symlink directly to it. + */ + public static final class LateDirectoryInfo { + // The constructors are private. Use the factory functions below to create + // instances of this class. + + /** Construct a stub LateDirectoryInfo object. */ + private LateDirectoryInfo() { + this.added = new AtomicBoolean(true); + + // Shut up the compiler. + this.target = null; + this.src = null; + this.pkgMode = SubpackageMode.IGNORE; + this.metadata = null; + this.symlinkBehavior = null; + } + + /** Construct a normal LateDirectoryInfo object. */ + private LateDirectoryInfo(Path target, PathFragment src, SubpackageMode pkgMode, + String metadata, FilesetEntry.SymlinkBehavior symlinkBehavior) { + this.target = target; + this.src = src; + this.pkgMode = pkgMode; + this.metadata = metadata; + this.symlinkBehavior = symlinkBehavior; + this.added = new AtomicBoolean(false); + } + + /** @return The target path for the symlink. The target is the referent. */ + public Path getTarget() { + return target; + } + + /** + * @return The source path for the symlink. The source is the place the + * symlink will be written. */ + public PathFragment getSrc() { + return src; + } + + /** + * @return Whether we should show a warning if we cross a package boundary + * when recursing into this directory. + */ + public SubpackageMode getPkgMode() { + return pkgMode; + } + + /** + * @return The metadata we will write into the manifest if we symlink to + * this directory. + */ + public String getMetadata() { + return metadata; + } + + /** + * @return How to perform the symlinking if the source happens to be a + * symlink itself. + */ + public FilesetEntry.SymlinkBehavior getTargetSymlinkBehavior() { + return Preconditions.checkNotNull(symlinkBehavior, + "should not call this method on stub instances"); + } + + /** + * Atomically checks if the late directory has been added to the manifest + * and marks it as added. If this function returns true, it is the + * responsibility of the caller to recurse into the late directory. + * Otherwise, some other caller has already, or is in the process of + * recursing into it. + * @return Whether the caller should recurse into the late directory. + */ + public boolean shouldAdd() { + return !added.getAndSet(true); + } + + /** + * Create a stub LateDirectoryInfo that is already marked as added. + * @return The new LateDirectoryInfo object. + */ + public static LateDirectoryInfo createStub() { + return new LateDirectoryInfo(); + } + + /** + * Create a LateDirectoryInfo object with the specified attributes. + * @param target The directory to which the symlinks will refer. + * @param src The location at which to create the symlink. + * @param pkgMode How to handle recursion into another package. + * @param metadata The metadata for the directory to write into the + * manifest if we symlink it directly. + * @return The new LateDirectoryInfo object. + */ + public static LateDirectoryInfo create(Path target, PathFragment src, SubpackageMode pkgMode, + String metadata, FilesetEntry.SymlinkBehavior symlinkBehavior) { + return new LateDirectoryInfo(target, src, pkgMode, metadata, symlinkBehavior); + } + + /** + * The target directory to which the symlink will point. + * Note this is a real path on the filesystem and can't be compared to src + * or any source (key) in the links map. + */ + private final Path target; + + /** The referent of the symlink. */ + private final PathFragment src; + + /** Whether to show cross package boundary warnings / errors. */ + private final SubpackageMode pkgMode; + + /** The metadata to write into the manifest file. */ + private final String metadata; + + /** How to perform the symlinking if the source happens to be a symlink itself. */ + private final FilesetEntry.SymlinkBehavior symlinkBehavior; + + /** Whether the directory has already been recursed into. */ + private final AtomicBoolean added; + } + + /** How to handle filesets that cross subpackages. */ + public static enum SubpackageMode { + ERROR, WARNING, IGNORE; + } +} |