Skip to content

Commit aba1186

Browse files
Wyveraldcopybara-github
authored andcommitted
Fix watching paths in undefined repos in repo rules
Watching a path in an undefined repo (eg. `$output_base/external/i_am_undefined/blah`) currently causes an NPE because we expect it to need a skyframe restart when it actually doesn't. The underlying reason is rather convoluted, but the gist is that we request `RepositoryDirectoryValue.Key(@@i_am_undefined)` explicitly, when we don't actually have to -- we can simply use `FileValue.Key($output_base/external/i_am_undefined/blah` directly, and rely on the fact that `FileFunction` knows to request the corresponding `RepositoryDirectoryValue` and behave appropriately when the repo is undefined. (This is due to logic in `ExternalFilesHelper.maybeHandleExternalFile`.) Additionally, this PR removes the `rctx.symlink(watch_target=)` parameter. The symlink target's existence and its contents should not affect the creation of the symlink at all, so there's no reason to ever watch it. Fixes #21483. Closes #21562. PiperOrigin-RevId: 612898526 Change-Id: I30deb9d9b2d19e0869517f4b6b126078446745b4
1 parent 8bb0bb3 commit aba1186

File tree

5 files changed

+92
-102
lines changed

5 files changed

+92
-102
lines changed

src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkBaseExternalContext.java

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@
5959
import com.google.devtools.build.lib.runtime.RepositoryRemoteExecutor;
6060
import com.google.devtools.build.lib.runtime.RepositoryRemoteExecutor.ExecutionResult;
6161
import com.google.devtools.build.lib.skyframe.ActionEnvironmentFunction;
62-
import com.google.devtools.build.lib.skyframe.DirectoryListingValue;
6362
import com.google.devtools.build.lib.util.OsUtils;
6463
import com.google.devtools.build.lib.util.io.OutErr;
6564
import com.google.devtools.build.lib.vfs.FileSystemUtils;
@@ -1280,20 +1279,16 @@ protected void maybeWatch(StarlarkPath starlarkPath, ShouldWatch shouldWatch)
12801279
if (repoCacheFriendlyPath == null) {
12811280
return;
12821281
}
1283-
RootedPath rootedPath = repoCacheFriendlyPath.getRootedPath(env, directories);
1284-
if (rootedPath == null) {
1285-
throw new NeedsSkyframeRestartException();
1286-
}
12871282
try {
1283+
var recordedInput = new RepoRecordedInput.File(repoCacheFriendlyPath);
12881284
FileValue fileValue =
1289-
(FileValue) env.getValueOrThrow(FileValue.key(rootedPath), IOException.class);
1285+
(FileValue) env.getValueOrThrow(recordedInput.getSkyKey(directories), IOException.class);
12901286
if (fileValue == null) {
12911287
throw new NeedsSkyframeRestartException();
12921288
}
12931289

12941290
recordedFileInputs.put(
1295-
new RepoRecordedInput.File(repoCacheFriendlyPath),
1296-
RepoRecordedInput.File.fileValueToMarkerValue(fileValue));
1291+
recordedInput, RepoRecordedInput.File.fileValueToMarkerValue(fileValue));
12971292
} catch (IOException e) {
12981293
throw new RepositoryFunctionException(e, Transience.TRANSIENT);
12991294
}
@@ -1305,17 +1300,13 @@ protected void maybeWatchDirents(Path path, ShouldWatch shouldWatch)
13051300
if (repoCacheFriendlyPath == null) {
13061301
return;
13071302
}
1308-
RootedPath rootedPath = repoCacheFriendlyPath.getRootedPath(env, directories);
1309-
if (env.valuesMissing()) {
1310-
throw new NeedsSkyframeRestartException();
1311-
}
1312-
if (env.getValue(DirectoryListingValue.key(rootedPath)) == null) {
1303+
var recordedInput = new RepoRecordedInput.Dirents(repoCacheFriendlyPath);
1304+
if (env.getValue(recordedInput.getSkyKey(directories)) == null) {
13131305
throw new NeedsSkyframeRestartException();
13141306
}
13151307
try {
13161308
recordedDirentsInputs.put(
1317-
new RepoRecordedInput.Dirents(repoCacheFriendlyPath),
1318-
RepoRecordedInput.Dirents.getDirentsMarkerValue(path));
1309+
recordedInput, RepoRecordedInput.Dirents.getDirentsMarkerValue(path));
13191310
} catch (IOException e) {
13201311
throw new RepositoryFunctionException(e, Transience.TRANSIENT);
13211312
}

src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkRepositoryContext.java

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
import com.google.devtools.build.lib.vfs.FileSystemUtils;
4545
import com.google.devtools.build.lib.vfs.Path;
4646
import com.google.devtools.build.lib.vfs.PathFragment;
47-
import com.google.devtools.build.lib.vfs.RootedPath;
4847
import com.google.devtools.build.lib.vfs.SyscallCache;
4948
import com.google.devtools.build.skyframe.SkyFunction.Environment;
5049
import com.google.devtools.build.skyframe.SkyFunctionException.Transience;
@@ -209,20 +208,8 @@ private StarlarkPath externalPath(String method, Object pathObject)
209208
@ParamType(type = StarlarkPath.class)
210209
},
211210
doc = "The path of the symlink to create."),
212-
@Param(
213-
name = "watch_target",
214-
defaultValue = "'auto'",
215-
positional = false,
216-
named = true,
217-
doc =
218-
"whether to <a href=\"#watch\">watch</a> the symlink target. Can be the string "
219-
+ "'yes', 'no', or 'auto'. Passing 'yes' is equivalent to immediately invoking "
220-
+ "the <a href=\"#watch\"><code>watch()</code></a> method; passing 'no' does "
221-
+ "not attempt to watch the path; passing 'auto' will only attempt to watch "
222-
+ "the file when it is legal to do so (see <code>watch()</code> docs for more "
223-
+ "information."),
224211
})
225-
public void symlink(Object target, Object linkName, String watchTarget, StarlarkThread thread)
212+
public void symlink(Object target, Object linkName, StarlarkThread thread)
226213
throws RepositoryFunctionException, EvalException, InterruptedException {
227214
StarlarkPath targetPath = getPath("symlink()", target);
228215
StarlarkPath linkPath = getPath("symlink()", linkName);
@@ -233,7 +220,6 @@ public void symlink(Object target, Object linkName, String watchTarget, Starlark
233220
getIdentifyingStringForLogging(),
234221
thread.getCallerLocation());
235222
env.getListener().post(w);
236-
maybeWatch(targetPath, ShouldWatch.fromString(watchTarget));
237223
try {
238224
checkInOutputDirectory("write", linkPath);
239225
makeDirectories(linkPath.getPath());
@@ -605,20 +591,16 @@ public void watchTree(Object path)
605591
if (repoCacheFriendlyPath == null) {
606592
return;
607593
}
608-
RootedPath rootedPath = repoCacheFriendlyPath.getRootedPath(env, directories);
609-
if (rootedPath == null) {
610-
throw new NeedsSkyframeRestartException();
611-
}
612594
try {
595+
var recordedInput = new RepoRecordedInput.DirTree(repoCacheFriendlyPath);
613596
DirectoryTreeDigestValue digestValue =
614597
(DirectoryTreeDigestValue)
615-
env.getValueOrThrow(DirectoryTreeDigestValue.key(rootedPath), IOException.class);
598+
env.getValueOrThrow(recordedInput.getSkyKey(directories), IOException.class);
616599
if (digestValue == null) {
617600
throw new NeedsSkyframeRestartException();
618601
}
619602

620-
recordedDirTreeInputs.put(
621-
new RepoRecordedInput.DirTree(repoCacheFriendlyPath), digestValue.hexDigest());
603+
recordedDirTreeInputs.put(recordedInput, digestValue.hexDigest());
622604
} catch (IOException e) {
623605
throw new RepositoryFunctionException(e, Transience.TRANSIENT);
624606
}

src/main/java/com/google/devtools/build/lib/rules/repository/RepoRecordedInput.java

Lines changed: 38 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.google.common.io.BaseEncoding;
2424
import com.google.devtools.build.lib.actions.FileValue;
2525
import com.google.devtools.build.lib.analysis.BlazeDirectories;
26+
import com.google.devtools.build.lib.cmdline.LabelConstants;
2627
import com.google.devtools.build.lib.cmdline.LabelValidator;
2728
import com.google.devtools.build.lib.cmdline.RepositoryName;
2829
import com.google.devtools.build.lib.skyframe.ActionEnvironmentFunction;
@@ -115,8 +116,7 @@ public static boolean areAllValuesUpToDate(
115116
throws InterruptedException {
116117
env.getValuesAndExceptions(
117118
recordedInputValues.keySet().stream()
118-
.map(RepoRecordedInput::getSkyKey)
119-
.filter(Objects::nonNull)
119+
.map(rri -> rri.getSkyKey(directories))
120120
.collect(toImmutableSet()));
121121
if (env.valuesMissing()) {
122122
return false;
@@ -157,18 +157,14 @@ public int compareTo(RepoRecordedInput o) {
157157
/** Returns the parser object for this type of recorded inputs. */
158158
public abstract Parser getParser();
159159

160-
/**
161-
* Returns the {@link SkyKey} that is necessary to determine {@link #isUpToDate}. Can be null if
162-
* no SkyKey is needed.
163-
*/
164-
@Nullable
165-
public abstract SkyKey getSkyKey();
160+
/** Returns the {@link SkyKey} that is necessary to determine {@link #isUpToDate}. */
161+
public abstract SkyKey getSkyKey(BlazeDirectories directories);
166162

167163
/**
168164
* Returns whether the given {@code oldValue} is still up-to-date for this recorded input. This
169-
* method can assume that {@link #getSkyKey()} is already evaluated; it can request further
170-
* Skyframe evaluations, and if any values are missing, this method can return any value (doesn't
171-
* matter what) and will be reinvoked after a Skyframe restart.
165+
* method can assume that {@link #getSkyKey(BlazeDirectories)} is already evaluated; it can
166+
* request further Skyframe evaluations, and if any values are missing, this method can return any
167+
* value (doesn't matter what) and will be reinvoked after a Skyframe restart.
172168
*/
173169
public abstract boolean isUpToDate(
174170
Environment env, BlazeDirectories directories, @Nullable String oldValue)
@@ -226,32 +222,25 @@ public static RepoCacheFriendlyPath parse(String s) {
226222
return createOutsideWorkspace(PathFragment.create(s));
227223
}
228224

229-
/**
230-
* If resolving this path requires getting a {@link RepositoryDirectoryValue}, this method
231-
* returns the appropriate key; otherwise it returns null.
232-
*/
233-
@Nullable
234-
public final RepositoryDirectoryValue.Key getRepoDirSkyKeyOrNull() {
235-
if (repoName().isEmpty() || repoName().get().isMain()) {
236-
return null;
237-
}
238-
return RepositoryDirectoryValue.key(repoName().get());
239-
}
240-
241-
public final RootedPath getRootedPath(Environment env, BlazeDirectories directories)
242-
throws InterruptedException {
225+
/** Returns the rooted path corresponding to this "repo-friendly path". */
226+
public final RootedPath getRootedPath(BlazeDirectories directories) {
243227
Root root;
244228
if (repoName().isEmpty()) {
245229
root = Root.absoluteRoot(directories.getOutputBase().getFileSystem());
246230
} else if (repoName().get().isMain()) {
247231
root = Root.fromPath(directories.getWorkspace());
248232
} else {
249-
RepositoryDirectoryValue repoDirValue =
250-
(RepositoryDirectoryValue) env.getValue(getRepoDirSkyKeyOrNull());
251-
if (repoDirValue == null || !repoDirValue.repositoryExists()) {
252-
return null;
253-
}
254-
root = Root.fromPath(repoDirValue.getPath());
233+
// This path is from an external repo. We just directly fabricate the path here instead of
234+
// requesting the appropriate RepositoryDirectoryValue, since we can rely on the various
235+
// other SkyFunctions (such as FileStateFunction and DirectoryListingStateFunction) to do
236+
// that for us instead. This also sidesteps an awkward situation when the external repo in
237+
// question is not defined.
238+
root =
239+
Root.fromPath(
240+
directories
241+
.getOutputBase()
242+
.getRelative(LabelConstants.EXTERNAL_REPOSITORY_LOCATION)
243+
.getRelative(repoName().get().getName()));
255244
}
256245
return RootedPath.toRootedPath(root, path());
257246
}
@@ -331,23 +320,18 @@ public static String fileValueToMarkerValue(FileValue fileValue) throws IOExcept
331320
return BaseEncoding.base16().lowerCase().encode(digest);
332321
}
333322

334-
@Override
335323
@Nullable
336-
public SkyKey getSkyKey() {
337-
return path.getRepoDirSkyKeyOrNull();
324+
public SkyKey getSkyKey(BlazeDirectories directories) {
325+
return FileValue.key(path.getRootedPath(directories));
338326
}
339327

340328
@Override
341329
public boolean isUpToDate(
342330
Environment env, BlazeDirectories directories, @Nullable String oldValue)
343331
throws InterruptedException {
344-
RootedPath rootedPath = path.getRootedPath(env, directories);
345-
if (rootedPath == null) {
346-
return false;
347-
}
348-
SkyKey fileKey = FileValue.key(rootedPath);
349332
try {
350-
FileValue fileValue = (FileValue) env.getValueOrThrow(fileKey, IOException.class);
333+
FileValue fileValue =
334+
(FileValue) env.getValueOrThrow(getSkyKey(directories), IOException.class);
351335
if (fileValue == null) {
352336
return false;
353337
}
@@ -406,25 +390,22 @@ public Parser getParser() {
406390
return PARSER;
407391
}
408392

409-
@Nullable
410393
@Override
411-
public SkyKey getSkyKey() {
412-
return path.getRepoDirSkyKeyOrNull();
394+
public SkyKey getSkyKey(BlazeDirectories directories) {
395+
return DirectoryListingValue.key(path.getRootedPath(directories));
413396
}
414397

415398
@Override
416399
public boolean isUpToDate(
417400
Environment env, BlazeDirectories directories, @Nullable String oldValue)
418401
throws InterruptedException {
419-
RootedPath rootedPath = path.getRootedPath(env, directories);
420-
if (rootedPath == null) {
421-
return false;
422-
}
423-
if (env.getValue(DirectoryListingValue.key(rootedPath)) == null) {
402+
SkyKey skyKey = getSkyKey(directories);
403+
if (env.getValue(skyKey) == null) {
424404
return false;
425405
}
426406
try {
427-
return oldValue.equals(getDirentsMarkerValue(rootedPath.asPath()));
407+
return oldValue.equals(
408+
getDirentsMarkerValue(((DirectoryListingValue.Key) skyKey).argument().asPath()));
428409
} catch (IOException e) {
429410
return false;
430411
}
@@ -493,22 +474,17 @@ public Parser getParser() {
493474
return PARSER;
494475
}
495476

496-
@Nullable
497477
@Override
498-
public SkyKey getSkyKey() {
499-
return path.getRepoDirSkyKeyOrNull();
478+
public SkyKey getSkyKey(BlazeDirectories directories) {
479+
return DirectoryTreeDigestValue.key(path.getRootedPath(directories));
500480
}
501481

502482
@Override
503483
public boolean isUpToDate(
504484
Environment env, BlazeDirectories directories, @Nullable String oldValue)
505485
throws InterruptedException {
506-
RootedPath rootedPath = path.getRootedPath(env, directories);
507-
if (rootedPath == null) {
508-
return false;
509-
}
510486
DirectoryTreeDigestValue value =
511-
(DirectoryTreeDigestValue) env.getValue(DirectoryTreeDigestValue.key(rootedPath));
487+
(DirectoryTreeDigestValue) env.getValue(getSkyKey(directories));
512488
if (value == null) {
513489
return false;
514490
}
@@ -565,7 +541,7 @@ public String toStringInternal() {
565541
}
566542

567543
@Override
568-
public SkyKey getSkyKey() {
544+
public SkyKey getSkyKey(BlazeDirectories directories) {
569545
return ActionEnvironmentFunction.key(name);
570546
}
571547

@@ -575,7 +551,7 @@ public boolean isUpToDate(
575551
throws InterruptedException {
576552
String v = PrecomputedValue.REPO_ENV.get(env).get(name);
577553
if (v == null) {
578-
v = ((ClientEnvironmentValue) env.getValue(getSkyKey())).getValue();
554+
v = ((ClientEnvironmentValue) env.getValue(getSkyKey(directories))).getValue();
579555
}
580556
// Note that `oldValue` can be null if the env var was not set.
581557
return Objects.equals(oldValue, v);
@@ -636,7 +612,7 @@ public String toStringInternal() {
636612
}
637613

638614
@Override
639-
public SkyKey getSkyKey() {
615+
public SkyKey getSkyKey(BlazeDirectories directories) {
640616
// Since we only record repo mapping entries for repos defined in Bzlmod, we can request the
641617
// WORKSPACE-less version of the main repo mapping (as no repos defined in Bzlmod can see
642618
// stuff from WORKSPACE).
@@ -649,7 +625,8 @@ public SkyKey getSkyKey() {
649625
public boolean isUpToDate(
650626
Environment env, BlazeDirectories directories, @Nullable String oldValue)
651627
throws InterruptedException {
652-
RepositoryMappingValue repoMappingValue = (RepositoryMappingValue) env.getValue(getSkyKey());
628+
RepositoryMappingValue repoMappingValue =
629+
(RepositoryMappingValue) env.getValue(getSkyKey(directories));
653630
return repoMappingValue != RepositoryMappingValue.NOT_FOUND_VALUE
654631
&& RepositoryName.createUnvalidated(oldValue)
655632
.equals(repoMappingValue.getRepositoryMapping().get(apparentName));

src/test/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkRepositoryContextTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ public void testSymlink() throws Exception {
459459
setUpContextForRule("test");
460460
context.createFile(context.path("foo"), "foobar", true, true, thread);
461461

462-
context.symlink(context.path("foo"), context.path("bar"), "auto", thread);
462+
context.symlink(context.path("foo"), context.path("bar"), thread);
463463
testOutputFile(outputDirectory.getChild("bar"), "foobar");
464464

465465
assertThat(context.path("bar").realpath()).isEqualTo(context.path("foo"));

0 commit comments

Comments
 (0)