Skip to content

Commit 440c226

Browse files
fmeumbazel-io
authored andcommitted
Explain why a module hasn't been found in any registry
This makes it easier for users to discover that a module hasn't been found because its absence was recorded in the lockfile during a previous build. Fixes bazelbuild#24803 Closes bazelbuild#24804. PiperOrigin-RevId: 713142592 Change-Id: Id541f5710481bd947c09d8dba315d683a1666b1c
1 parent c8217fd commit 440c226

File tree

7 files changed

+110
-57
lines changed

7 files changed

+110
-57
lines changed

src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ java_library(
104104
"//src/main/java/com/google/devtools/build/lib/bazel/repository:repository_options",
105105
"//src/main/java/com/google/devtools/build/lib/bazel/repository/cache",
106106
"//src/main/java/com/google/devtools/build/lib/bazel/repository/downloader",
107+
"//src/main/java/com/google/devtools/build/lib/cmdline",
107108
"//src/main/java/com/google/devtools/build/lib/events",
108109
"//src/main/java/com/google/devtools/build/lib/profiler",
109110
"//src/main/java/com/google/devtools/build/lib/util:os",

src/main/java/com/google/devtools/build/lib/bazel/bzlmod/IndexRegistry.java

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import com.google.devtools.build.lib.bazel.repository.downloader.Checksum;
2727
import com.google.devtools.build.lib.bazel.repository.downloader.Checksum.MissingChecksumException;
2828
import com.google.devtools.build.lib.bazel.repository.downloader.DownloadManager;
29+
import com.google.devtools.build.lib.cmdline.LabelConstants;
2930
import com.google.devtools.build.lib.events.ExtendedEventHandler;
3031
import com.google.devtools.build.lib.events.StoredEventHandler;
3132
import com.google.devtools.build.lib.profiler.Profiler;
@@ -131,28 +132,32 @@ private String constructUrl(String base, String... segments) {
131132
return url.toString();
132133
}
133134

134-
/** Grabs a file from the given URL. Returns {@link Optional#empty} if the file doesn't exist. */
135-
private Optional<byte[]> grabFile(
135+
/** Grabs a file from the given URL. Throws {@link NotFoundException} if it doesn't exist. */
136+
private byte[] grabFile(
136137
String url,
137138
ExtendedEventHandler eventHandler,
138139
DownloadManager downloadManager,
139140
boolean useChecksum)
140-
throws IOException, InterruptedException {
141-
var maybeContent = doGrabFile(downloadManager, url, eventHandler, useChecksum);
142-
if ((knownFileHashesMode == KnownFileHashesMode.USE_AND_UPDATE
143-
|| knownFileHashesMode == KnownFileHashesMode.USE_IMMUTABLE_AND_UPDATE)
144-
&& useChecksum) {
145-
eventHandler.post(RegistryFileDownloadEvent.create(url, maybeContent));
141+
throws IOException, InterruptedException, NotFoundException {
142+
Optional<byte[]> maybeContent = Optional.empty();
143+
try {
144+
maybeContent = Optional.of(doGrabFile(downloadManager, url, eventHandler, useChecksum));
145+
return maybeContent.get();
146+
} finally {
147+
if ((knownFileHashesMode == KnownFileHashesMode.USE_AND_UPDATE
148+
|| knownFileHashesMode == KnownFileHashesMode.USE_IMMUTABLE_AND_UPDATE)
149+
&& useChecksum) {
150+
eventHandler.post(RegistryFileDownloadEvent.create(url, maybeContent));
151+
}
146152
}
147-
return maybeContent;
148153
}
149154

150-
private Optional<byte[]> doGrabFile(
155+
private byte[] doGrabFile(
151156
DownloadManager downloadManager,
152157
String rawUrl,
153158
ExtendedEventHandler eventHandler,
154159
boolean useChecksum)
155-
throws IOException, InterruptedException {
160+
throws IOException, InterruptedException, NotFoundException {
156161
Optional<Checksum> checksum;
157162
if (knownFileHashesMode != KnownFileHashesMode.IGNORE && useChecksum) {
158163
Optional<Checksum> knownChecksum = knownFileHashes.get(rawUrl);
@@ -174,7 +179,11 @@ private Optional<byte[]> doGrabFile(
174179
checksum = Optional.empty();
175180
} else {
176181
// Guarantee reproducibility by assuming that the file still doesn't exist.
177-
return Optional.empty();
182+
throw new NotFoundException(
183+
String.format(
184+
"%s: previously not found (as recorded in %s, refresh with"
185+
+ " --lockfile_mode=refresh)",
186+
rawUrl, LabelConstants.MODULE_LOCKFILE_NAME));
178187
}
179188
} else {
180189
// The file is known, download with a checksum to potentially obtain a repository cache hit
@@ -203,7 +212,7 @@ private Optional<byte[]> doGrabFile(
203212
&& !url.getProtocol().equals("file")
204213
&& vendorManager.isUrlVendored(url)) {
205214
try {
206-
return Optional.of(vendorManager.readRegistryUrl(url, checksum.get()));
215+
return vendorManager.readRegistryUrl(url, checksum.get());
207216
} catch (IOException e) {
208217
throw new IOException(
209218
String.format(
@@ -216,10 +225,9 @@ private Optional<byte[]> doGrabFile(
216225

217226
try (SilentCloseable c =
218227
Profiler.instance().profile(ProfilerTask.BZLMOD, () -> "download file: " + rawUrl)) {
219-
return Optional.of(
220-
downloadManager.downloadAndReadOneUrlForBzlmod(url, eventHandler, clientEnv, checksum));
228+
return downloadManager.downloadAndReadOneUrlForBzlmod(url, eventHandler, clientEnv, checksum);
221229
} catch (FileNotFoundException e) {
222-
return Optional.empty();
230+
throw new NotFoundException(String.format("%s: not found", rawUrl));
223231
} catch (IOException e) {
224232
// Include the URL in the exception message for easier debugging.
225233
throw new IOException(
@@ -228,14 +236,13 @@ private Optional<byte[]> doGrabFile(
228236
}
229237

230238
@Override
231-
public Optional<ModuleFile> getModuleFile(
239+
public ModuleFile getModuleFile(
232240
ModuleKey key, ExtendedEventHandler eventHandler, DownloadManager downloadManager)
233-
throws IOException, InterruptedException {
241+
throws IOException, InterruptedException, NotFoundException {
234242
String url =
235243
constructUrl(getUrl(), "modules", key.name(), key.version().toString(), "MODULE.bazel");
236-
Optional<byte[]> maybeContent =
237-
grabFile(url, eventHandler, downloadManager, /* useChecksum= */ true);
238-
return maybeContent.map(content -> ModuleFile.create(content, url));
244+
byte[] content = grabFile(url, eventHandler, downloadManager, /* useChecksum= */ true);
245+
return ModuleFile.create(content, url);
239246
}
240247

241248
/** Represents fields available in {@code bazel_registry.json} for the registry. */
@@ -286,8 +293,12 @@ private Optional<String> grabJsonFile(
286293
DownloadManager downloadManager,
287294
boolean useChecksum)
288295
throws IOException, InterruptedException {
289-
return grabFile(url, eventHandler, downloadManager, useChecksum)
290-
.map(value -> new String(value, UTF_8));
296+
try {
297+
return Optional.of(
298+
new String(grabFile(url, eventHandler, downloadManager, useChecksum), UTF_8));
299+
} catch (NotFoundException e) {
300+
return Optional.empty();
301+
}
291302
}
292303

293304
/**

src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileFunction.java

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@
7474
import java.util.List;
7575
import java.util.Map;
7676
import java.util.Objects;
77-
import java.util.Optional;
7877
import java.util.stream.Stream;
7978
import javax.annotation.Nullable;
8079
import net.starlark.java.eval.Dict;
@@ -91,6 +90,7 @@
9190
*/
9291
public class ModuleFileFunction implements SkyFunction {
9392

93+
// Never empty.
9494
public static final Precomputed<ImmutableSet<String>> REGISTRIES =
9595
new Precomputed<>("registries");
9696
public static final Precomputed<Boolean> IGNORE_DEV_DEPS =
@@ -677,14 +677,21 @@ private GetModuleFileResult getModuleFile(
677677
// Now go through the list of registries and use the first one that contains the requested
678678
// module.
679679
StoredEventHandler downloadEventHandler = new StoredEventHandler();
680+
List<String> notFoundTrace = null;
680681
for (Registry registry : registryObjects) {
681682
try {
682-
Optional<ModuleFile> maybeModuleFile =
683-
registry.getModuleFile(key, downloadEventHandler, this.downloadManager);
684-
if (maybeModuleFile.isEmpty()) {
683+
ModuleFile originalModuleFile;
684+
try {
685+
originalModuleFile =
686+
registry.getModuleFile(key, downloadEventHandler, this.downloadManager);
687+
} catch (Registry.NotFoundException e) {
688+
if (notFoundTrace == null) {
689+
notFoundTrace = new ArrayList<>();
690+
}
691+
notFoundTrace.add(e.getMessage());
685692
continue;
686693
}
687-
ModuleFile moduleFile = maybePatchModuleFile(maybeModuleFile.get(), override, env);
694+
ModuleFile moduleFile = maybePatchModuleFile(originalModuleFile, override, env);
688695
if (moduleFile == null) {
689696
return null;
690697
}
@@ -698,7 +705,11 @@ private GetModuleFileResult getModuleFile(
698705
}
699706
}
700707

701-
throw errorf(Code.MODULE_NOT_FOUND, "module not found in registries: %s", key);
708+
throw errorf(
709+
Code.MODULE_NOT_FOUND,
710+
"module %s not found in registries:\n* %s",
711+
key,
712+
String.join("\n* ", notFoundTrace));
702713
}
703714

704715
/**

src/main/java/com/google/devtools/build/lib/bazel/bzlmod/Registry.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,22 @@ public interface Registry extends NotComparableSkyValue {
2828
/** The URL that uniquely identifies the registry. */
2929
String getUrl();
3030

31+
/** Thrown when a file is not found in the registry. */
32+
final class NotFoundException extends Exception {
33+
public NotFoundException(String message) {
34+
super(message);
35+
}
36+
}
37+
3138
/**
3239
* Retrieves the contents of the module file of the module identified by {@code key} from the
33-
* registry. Returns {@code Optional.empty()} when the module is not found in this registry.
40+
* registry.
41+
*
42+
* @throws NotFoundException if the module file is not found in the registry
3443
*/
35-
Optional<ModuleFile> getModuleFile(
44+
ModuleFile getModuleFile(
3645
ModuleKey key, ExtendedEventHandler eventHandler, DownloadManager downloadManager)
37-
throws IOException, InterruptedException;
46+
throws IOException, InterruptedException, NotFoundException;
3847

3948
/**
4049
* Retrieves the {@link RepoSpec} object that indicates how the contents of the module identified

src/test/java/com/google/devtools/build/lib/bazel/bzlmod/FakeRegistry.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,16 @@ public String getUrl() {
6666
}
6767

6868
@Override
69-
public Optional<ModuleFile> getModuleFile(
70-
ModuleKey key, ExtendedEventHandler eventHandler, DownloadManager downloadManager) {
69+
public ModuleFile getModuleFile(
70+
ModuleKey key, ExtendedEventHandler eventHandler, DownloadManager downloadManager)
71+
throws NotFoundException {
7172
String uri = String.format("%s/modules/%s/%s/MODULE.bazel", url, key.name(), key.version());
7273
var maybeContent = Optional.ofNullable(modules.get(key)).map(value -> value.getBytes(UTF_8));
7374
eventHandler.post(RegistryFileDownloadEvent.create(uri, maybeContent));
74-
return maybeContent.map(content -> ModuleFile.create(content, uri));
75+
if (maybeContent.isEmpty()) {
76+
throw new NotFoundException("module not found: " + key);
77+
}
78+
return ModuleFile.create(maybeContent.get(), uri);
7579
}
7680

7781
@Override

src/test/java/com/google/devtools/build/lib/bazel/bzlmod/IndexRegistryTest.java

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,12 @@ public void testHttpUrl() throws Exception {
102102
ImmutableMap.of(),
103103
Optional.empty());
104104
assertThat(registry.getModuleFile(createModuleKey("foo", "1.0"), reporter, downloadManager))
105-
.hasValue(
105+
.isEqualTo(
106106
ModuleFile.create(
107107
"lol".getBytes(UTF_8), server.getUrl() + "/myreg/modules/foo/1.0/MODULE.bazel"));
108-
assertThat(registry.getModuleFile(createModuleKey("bar", "1.0"), reporter, downloadManager))
109-
.isEmpty();
108+
assertThrows(
109+
Registry.NotFoundException.class,
110+
() -> registry.getModuleFile(createModuleKey("bar", "1.0"), reporter, downloadManager));
110111
}
111112

112113
@Test
@@ -137,11 +138,12 @@ public void testHttpUrlWithNetrcCreds() throws Exception {
137138

138139
downloadManager.setNetrcCreds(new NetrcCredentials(netrc));
139140
assertThat(registry.getModuleFile(createModuleKey("foo", "1.0"), reporter, downloadManager))
140-
.hasValue(
141+
.isEqualTo(
141142
ModuleFile.create(
142143
"lol".getBytes(UTF_8), server.getUrl() + "/myreg/modules/foo/1.0/MODULE.bazel"));
143-
assertThat(registry.getModuleFile(createModuleKey("bar", "1.0"), reporter, downloadManager))
144-
.isEmpty();
144+
assertThrows(
145+
Registry.NotFoundException.class,
146+
() -> registry.getModuleFile(createModuleKey("bar", "1.0"), reporter, downloadManager));
145147
}
146148

147149
@Test
@@ -160,9 +162,10 @@ public void testFileUrl() throws Exception {
160162
ImmutableMap.of(),
161163
Optional.empty());
162164
assertThat(registry.getModuleFile(createModuleKey("foo", "1.0"), reporter, downloadManager))
163-
.hasValue(ModuleFile.create("lol".getBytes(UTF_8), file.toURI().toString()));
164-
assertThat(registry.getModuleFile(createModuleKey("bar", "1.0"), reporter, downloadManager))
165-
.isEmpty();
165+
.isEqualTo(ModuleFile.create("lol".getBytes(UTF_8), file.toURI().toString()));
166+
assertThrows(
167+
Registry.NotFoundException.class,
168+
() -> registry.getModuleFile(createModuleKey("bar", "1.0"), reporter, downloadManager));
166169
}
167170

168171
@Test
@@ -444,15 +447,20 @@ public void testGetModuleFileChecksums() throws Exception {
444447
ImmutableMap.of(),
445448
Optional.empty());
446449
assertThat(registry.getModuleFile(createModuleKey("foo", "1.0"), reporter, downloadManager))
447-
.hasValue(
450+
.isEqualTo(
448451
ModuleFile.create(
449452
"old".getBytes(UTF_8), server.getUrl() + "/myreg/modules/foo/1.0/MODULE.bazel"));
450453
assertThat(registry.getModuleFile(createModuleKey("foo", "2.0"), reporter, downloadManager))
451-
.hasValue(
454+
.isEqualTo(
452455
ModuleFile.create(
453456
"new".getBytes(UTF_8), server.getUrl() + "/myreg/modules/foo/2.0/MODULE.bazel"));
454-
assertThat(registry.getModuleFile(createModuleKey("bar", "1.0"), reporter, downloadManager))
455-
.isEmpty();
457+
var e =
458+
assertThrows(
459+
Registry.NotFoundException.class,
460+
() -> registry.getModuleFile(createModuleKey("bar", "1.0"), reporter, downloadManager));
461+
assertThat(e)
462+
.hasMessageThat()
463+
.isEqualTo(server.getUrl() + "/myreg/modules/bar/1.0/MODULE.bazel: not found");
456464

457465
var recordedChecksums = eventRecorder.getRecordedHashes();
458466
assertThat(
@@ -467,7 +475,7 @@ public void testGetModuleFileChecksums() throws Exception {
467475
Optional.empty())
468476
.inOrder();
469477

470-
registry =
478+
Registry registry2 =
471479
registryFactory.createRegistry(
472480
server.getUrl() + "/myreg",
473481
LockfileMode.UPDATE,
@@ -479,16 +487,25 @@ public void testGetModuleFileChecksums() throws Exception {
479487
server.unserve("/myreg/modules/foo/1.0/MODULE.bazel");
480488
server.unserve("/myreg/modules/foo/2.0/MODULE.bazel");
481489
server.serve("/myreg/modules/bar/1.0/MODULE.bazel", "no longer 404");
482-
assertThat(registry.getModuleFile(createModuleKey("foo", "1.0"), reporter, downloadManager))
483-
.hasValue(
490+
assertThat(registry2.getModuleFile(createModuleKey("foo", "1.0"), reporter, downloadManager))
491+
.isEqualTo(
484492
ModuleFile.create(
485493
"old".getBytes(UTF_8), server.getUrl() + "/myreg/modules/foo/1.0/MODULE.bazel"));
486-
assertThat(registry.getModuleFile(createModuleKey("foo", "2.0"), reporter, downloadManager))
487-
.hasValue(
494+
assertThat(registry2.getModuleFile(createModuleKey("foo", "2.0"), reporter, downloadManager))
495+
.isEqualTo(
488496
ModuleFile.create(
489497
"new".getBytes(UTF_8), server.getUrl() + "/myreg/modules/foo/2.0/MODULE.bazel"));
490-
assertThat(registry.getModuleFile(createModuleKey("bar", "1.0"), reporter, downloadManager))
491-
.isEmpty();
498+
e =
499+
assertThrows(
500+
Registry.NotFoundException.class,
501+
() ->
502+
registry2.getModuleFile(createModuleKey("bar", "1.0"), reporter, downloadManager));
503+
assertThat(e)
504+
.hasMessageThat()
505+
.isEqualTo(
506+
server.getUrl()
507+
+ "/myreg/modules/bar/1.0/MODULE.bazel: previously not found (as recorded in"
508+
+ " MODULE.bazel.lock, refresh with --lockfile_mode=refresh)");
492509
}
493510

494511
@Test

src/test/py/bazel/bzlmod/bazel_overrides_test.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -495,8 +495,8 @@ def testCmdRelativeModuleOverride(self):
495495
allow_failure=True,
496496
)
497497
self.assertIn(
498-
'ERROR: Error computing the main repository mapping: module not found'
499-
' in registries: [email protected]',
498+
'ERROR: Error computing the main repository mapping: module [email protected] not'
499+
' found in registries:',
500500
stderr,
501501
)
502502

0 commit comments

Comments
 (0)