aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/analysis/Runfiles.java
diff options
context:
space:
mode:
authorGravatar Googler <noreply@google.com>2016-03-10 17:55:17 +0000
committerGravatar David Chen <dzc@google.com>2016-03-11 21:29:24 +0000
commitc85af31c5b0938a24cf454f437d766c65cb4c921 (patch)
tree5b9102dd2d611290ae61ba95a2fb6b57eb85b222 /src/main/java/com/google/devtools/build/lib/analysis/Runfiles.java
parent443f78fcdff99e313001a7dc06026c70f71e8792 (diff)
Expose runfiles symlink functionality in Skylark
The Skylark rule context object has a runfiles method. This adds two optional parameters to that method, "symlinks" and "root_symlinks", that expose functionality from the underlying Runfiles java class. With this functionality, one can construct links in the runfiles tree where the source and destination of the link have different names and/or relative directories. This might be useful for things like AppEngine rules where a file in a subdirectory of the source tree needs to appear in the root directory of the runfiles tree. If either new parameter is used, the runfiles is subject to stricter validity checking. This checking propagates to other runfiles that depend on it. RELNOTES: Added "root_symlinks" and "symlinks" parameters to Skylark runfiles() method. -- MOS_MIGRATED_REVID=116879064
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/analysis/Runfiles.java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/analysis/Runfiles.java41
1 files changed, 37 insertions, 4 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/Runfiles.java b/src/main/java/com/google/devtools/build/lib/analysis/Runfiles.java
index c2dfa756e7..95efeb3f29 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/Runfiles.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/Runfiles.java
@@ -79,6 +79,13 @@ public final class Runfiles {
/**
* An entry in the runfiles map.
+ *
+ * <p>build-runfiles.cc enforces the following constraints: The PathFragment must not be an
+ * absolute path, nor contain "..". Overlapping runfiles links are also refused. This is the case
+ * where you ask to create a link to "foo" and also "foo/bar.txt". I.e. you're asking it to make
+ * "foo" both a file (symlink) and a directory.
+ *
+ * <p>Links to directories are heavily discouraged.
*/
//
// O intrepid fixer or bugs and implementor of features, dare not to add a .equals() method
@@ -171,7 +178,8 @@ public final class Runfiles {
/**
* Behavior upon finding a conflict between two runfile entries. A conflict means that two
* different artifacts have the same runfiles path specified. For example, adding artifact
- * "a.foo" at path "bar" when there is already an artifact "b.foo" at path "bar".
+ * "a.foo" at path "bar" when there is already an artifact "b.foo" at path "bar". The policies
+ * are ordered from least strict to most strict.
*
* <p>Note that conflicts are found relatively late, when the manifest file is created, not when
* the symlinks are added to runfiles.
@@ -242,13 +250,15 @@ public final class Runfiles {
NestedSet<SymlinkEntry> symlinks,
NestedSet<SymlinkEntry> rootSymlinks,
NestedSet<PruningManifest> pruningManifests,
- EmptyFilesSupplier emptyFilesSupplier) {
+ EmptyFilesSupplier emptyFilesSupplier,
+ ConflictPolicy conflictPolicy) {
this.suffix = suffix;
this.unconditionalArtifacts = Preconditions.checkNotNull(artifacts);
this.symlinks = Preconditions.checkNotNull(symlinks);
this.rootSymlinks = Preconditions.checkNotNull(rootSymlinks);
this.pruningManifests = Preconditions.checkNotNull(pruningManifests);
this.emptyFilesSupplier = Preconditions.checkNotNull(emptyFilesSupplier);
+ this.conflictPolicy = conflictPolicy;
}
/**
@@ -519,9 +529,15 @@ public final class Runfiles {
return map;
}
+ /** Returns currently policy for conflicting symlink entries. */
+ public ConflictPolicy getConflictPolicy() {
+ return this.conflictPolicy;
+ }
+
/** Set whether we should warn about conflicting symlink entries. */
- public void setConflictPolicy(ConflictPolicy conflictPolicy) {
+ public Runfiles setConflictPolicy(ConflictPolicy conflictPolicy) {
this.conflictPolicy = conflictPolicy;
+ return this;
}
/**
@@ -605,6 +621,9 @@ public final class Runfiles {
NestedSetBuilder.stableOrder();
private EmptyFilesSupplier emptyFilesSupplier = DUMMY_EMPTY_FILES_SUPPLIER;
+ /** Build the Runfiles object with this policy */
+ private ConflictPolicy conflictPolicy = ConflictPolicy.IGNORE;
+
/**
* Only used for Runfiles.EMPTY.
*/
@@ -626,7 +645,7 @@ public final class Runfiles {
public Runfiles build() {
return new Runfiles(suffix, artifactsBuilder.build(), symlinksBuilder.build(),
rootSymlinksBuilder.build(), pruningManifestsBuilder.build(),
- emptyFilesSupplier);
+ emptyFilesSupplier, conflictPolicy);
}
/**
@@ -698,6 +717,16 @@ public final class Runfiles {
}
/**
+ * Adds a root symlink.
+ */
+ public Builder addRootSymlink(PathFragment link, Artifact target) {
+ Preconditions.checkNotNull(link);
+ Preconditions.checkNotNull(target);
+ rootSymlinksBuilder.add(new SymlinkEntry(link, target));
+ return this;
+ }
+
+ /**
* Adds several root symlinks.
*/
public Builder addRootSymlinks(Map<PathFragment, Artifact> symlinks) {
@@ -876,6 +905,10 @@ public final class Runfiles {
* pruning manifests in the merge.
*/
private Builder merge(Runfiles runfiles, boolean includePruningManifests) {
+ // Propagate the most strict conflict checking from merged-in runfiles
+ if (runfiles.conflictPolicy.compareTo(conflictPolicy) > 0) {
+ conflictPolicy = runfiles.conflictPolicy;
+ }
if (runfiles.isEmpty()) {
return this;
}