diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkBaseExternalContext.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkBaseExternalContext.java index 4d1af807045492..ee1b77a16e2aa6 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkBaseExternalContext.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkBaseExternalContext.java @@ -1185,6 +1185,9 @@ public String readFile(Object path, String watch, StarlarkThread thread) p.toString(), getIdentifyingStringForLogging(), thread.getCallerLocation()); env.getListener().post(w); maybeWatch(p, ShouldWatch.fromString(watch)); + if (p.isDir()) { + throw Starlark.errorf("attempting to read() a directory: %s", p); + } try { return FileSystemUtils.readContent(p.getPath(), ISO_8859_1); } catch (IOException e) { @@ -1270,9 +1273,6 @@ protected void maybeWatch(StarlarkPath starlarkPath, ShouldWatch shouldWatch) if (fileValue == null) { throw new NeedsSkyframeRestartException(); } - if (!fileValue.isFile() || fileValue.isSpecialFile()) { - throw Starlark.errorf("Not a regular file: %s", pair.second.asPath().getPathString()); - } recordedFileInputs.put(pair.first, RepoRecordedInput.File.fileValueToMarkerValue(fileValue)); } catch (IOException e) { @@ -1283,12 +1283,19 @@ protected void maybeWatch(StarlarkPath starlarkPath, ShouldWatch shouldWatch) @StarlarkMethod( name = "watch", doc = - "Tells Bazel to watch for changes to the given file. Any changes to the file will " + "Tells Bazel to watch for changes to the given path, whether or not it exists, or " + + "whether it's a file or a directory. Any changes to the file or directory will " + "invalidate this repository or module extension, and cause it to be refetched or " - + "re-evaluated next time.
Note that attempting to watch files inside the repo " - + "currently being fetched, or inside the working directory of the current module " - + "extension, will result in an error. A module extension attempting to watch a file " - + "outside the current Bazel workspace will also result in an error.", + + "re-evaluated next time.
\"Changes\" include changes to the contents of the file "
+ + "(if the path is a file); if the path was a file but is now a directory, or vice "
+ + "versa; and if the path starts or stops existing. Notably, this does not "
+ + "include changes to any files under the directory if the path is a directory. For "
+ // TODO: add `watch_dir()`
+ + "that, use watch_dir()
instead.
Note "
+ + "that attempting to watch paths inside the repo currently being fetched, or inside "
+ + "the working directory of the current module extension, will result in an error. A "
+ + "module extension attempting to watch a path outside the current Bazel workspace "
+ + "will also result in an error.",
parameters = {
@Param(
name = "path",
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkPath.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkPath.java
index 8f0193410bbb4d..4823c294c1188a 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkPath.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkPath.java
@@ -77,7 +77,6 @@ public String getBasename() {
@StarlarkMethod(
name = "readdir",
- structField = false,
doc = "The list of entries in the directory denoted by this path.")
public ImmutableList Note that "
+ + "accessing this field does not cause the path to be watched. If you'd "
+ + "like the repo rule or module extension to be sensitive to the path's existence, "
+ + "use the Note that accessing this field does "
+ + "not cause the path to be watched. If you'd like the repo rule or module "
+ + "extension to be sensitive to whether the path is a directory or a file, use the "
+ + "watch()
method on the context object.")
public boolean exists() {
return path.exists();
}
+ @StarlarkMethod(
+ name = "is_dir",
+ structField = true,
+ doc =
+ "Returns true if this path points to a directory.watch()
method on the context object.")
+ public boolean isDir() {
+ return path.isDirectory();
+ }
+
@StarlarkMethod(
name = "realpath",
structField = true,
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkRepositoryContext.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkRepositoryContext.java
index 8e051c7981f0dd..6d69087374669d 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkRepositoryContext.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkRepositoryContext.java
@@ -198,9 +198,21 @@ private StarlarkPath externalPath(String method, Object pathObject)
@ParamType(type = Label.class),
@ParamType(type = StarlarkPath.class)
},
- doc = "The path of the symlink to create, relative to the repository directory."),
+ doc = "The path of the symlink to create."),
+ @Param(
+ name = "watch_target",
+ defaultValue = "'auto'",
+ positional = false,
+ named = true,
+ doc =
+ "whether to watch the symlink target. Can be the string "
+ + "'yes', 'no', or 'auto'. Passing 'yes' is equivalent to immediately invoking "
+ + "the watch()
method; passing 'no' does "
+ + "not attempt to watch the path; passing 'auto' will only attempt to watch "
+ + "the file when it is legal to do so (see watch()
docs for more "
+ + "information."),
})
- public void symlink(Object target, Object linkName, StarlarkThread thread)
+ public void symlink(Object target, Object linkName, String watchTarget, StarlarkThread thread)
throws RepositoryFunctionException, EvalException, InterruptedException {
StarlarkPath targetPath = getPath("symlink()", target);
StarlarkPath linkPath = getPath("symlink()", linkName);
@@ -211,6 +223,7 @@ public void symlink(Object target, Object linkName, StarlarkThread thread)
getIdentifyingStringForLogging(),
thread.getCallerLocation());
env.getListener().post(w);
+ maybeWatch(targetPath, ShouldWatch.fromString(watchTarget));
try {
checkInOutputDirectory("write", linkPath);
makeDirectories(linkPath.getPath());
@@ -269,12 +282,25 @@ public void symlink(Object target, Object linkName, StarlarkThread thread)
defaultValue = "True",
named = true,
doc = "set the executable flag on the created file, true by default."),
+ @Param(
+ name = "watch_template",
+ defaultValue = "'auto'",
+ positional = false,
+ named = true,
+ doc =
+ "whether to watch the template file. Can be the string "
+ + "'yes', 'no', or 'auto'. Passing 'yes' is equivalent to immediately invoking "
+ + "the watch()
method; passing 'no' does "
+ + "not attempt to watch the file; passing 'auto' will only attempt to watch "
+ + "the file when it is legal to do so (see watch()
docs for more "
+ + "information."),
})
public void createFileFromTemplate(
Object path,
Object template,
Dict, ?> substitutions, // watch()
method; passing 'no' does "
+ + "not attempt to watch the file; passing 'auto' will only attempt to watch "
+ + "the file when it is legal to do so (see watch()
docs for more "
+ + "information."),
})
- public void patch(Object patchFile, StarlarkInt stripI, StarlarkThread thread)
+ public void patch(Object patchFile, StarlarkInt stripI, String watchPatch, StarlarkThread thread)
throws EvalException, RepositoryFunctionException, InterruptedException {
int strip = Starlark.toInt(stripI, "strip");
StarlarkPath starlarkPath = getPath("patch()", patchFile);
@@ -397,6 +439,10 @@ public void patch(Object patchFile, StarlarkInt stripI, StarlarkThread thread)
getIdentifyingStringForLogging(),
thread.getCallerLocation());
env.getListener().post(w);
+ if (starlarkPath.isDir()) {
+ throw Starlark.errorf("attempting to use a directory as patch file: %s", starlarkPath);
+ }
+ maybeWatch(starlarkPath, ShouldWatch.fromString(watchPatch));
try {
PatchUtil.apply(starlarkPath.getPath(), strip, workingDirectory);
} catch (PatchFailedException e) {
@@ -457,12 +503,25 @@ public void patch(Object patchFile, StarlarkInt stripI, StarlarkThread thread)
+ " any directory prefix adjustment. This can be used to extract archives that"
+ " contain non-Unicode filenames, or which have files that would extract to"
+ " the same path on case-insensitive filesystems."),
+ @Param(
+ name = "watch_archive",
+ defaultValue = "'auto'",
+ positional = false,
+ named = true,
+ doc =
+ "whether to watch the archive file. Can be the string "
+ + "'yes', 'no', or 'auto'. Passing 'yes' is equivalent to immediately invoking "
+ + "the watch()
method; passing 'no' does "
+ + "not attempt to watch the file; passing 'auto' will only attempt to watch "
+ + "the file when it is legal to do so (see watch()
docs for more "
+ + "information."),
})
public void extract(
Object archive,
Object output,
String stripPrefix,
Dict, ?> renameFiles, //