Repository: bazelbuild/bazel
Issue: [7.0.0rc2 regression] Can't load from `@bazel_tools//tools/jdk` `.bzl` files from module extension
Labels: type: bug
team-Rules-Java
untriaged

### Description of the bug:


When a module extension used with `use_extension` transitively loads from a `.bzl` file in `@bazel_tools//tools/jdk`, the load fails since `@bazel_tools` forwards to `@rules_java`, but [uses an empty repo mapping for `load`s](https://github.com/bazelbuild/bazel/blob/835512a65e97f77101af5411897a3371ec6df3fe/src/main/java/com/google/devtools/build/lib/skyframe/BzlLoadFunction.java#L947-L953).

This wasn't an issue before https://github.com/bazelbuild/bazel/commit/3029a7db8a877e8a616b2ebe304573fae9ea9962, where `@bazel_tools` did not load from an external repo.

### Which category does this issue belong to?


External Dependency

### What's the simplest, easiest way to reproduce this bug? Please provide a minimal example if possible.


In a `.bzl` file containing a module extension, transitively `load` from e.g. `@bazel_tools//tools/jdk:toolchain_utils.bzl`. 

Concretely, I noticed this while working on Bzlmodifying the deps of `bazel_jar_jar` in https://github.com/bazeltools/bazel_jar_jar/blob/main/jar_jar.bzl, which transitively loads `find_java_toolchain`.

### Which operating system are you running Bazel on?


_No response_

### What is the output of `bazel info release`?


_No response_

### If `bazel info release` returns `development version` or `(@non-git)`, tell us how you built Bazel.


_No response_

### What's the output of `git remote get-url origin; git rev-parse master; git rev-parse HEAD` ?


_No response_

### Is this a regression? If yes, please try to identify the Bazel commit where the bug was introduced.


3029a7db8a877e8a616b2ebe304573fae9ea9962

### Have you found anything relevant by searching the web?

_No response_

### Any other information, logs, or outputs that you want to share?

_No response_

Issue comments:
@bazel-io flag
CC @meteorcloudy @Wyverald 
I think I have a fix for this, will submit it soon.
@bazel-io fork 7.0.0
Congrats on winning the 20k issue/pr number! 🎉 😄
A fix for this issue has been included in [Bazel 7.0.0 RC5](https://releases.bazel.build/7.0.0/rc5/index.html). Please test out the release candidate and report any issues as soon as possible. Thanks!

Pull request review comments:

Patch that resolved the issue:
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/BzlLoadFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/BzlLoadFunction.java
index a1b882aab2100f..6b285c7375edb9 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/BzlLoadFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/BzlLoadFunction.java
@@ -622,6 +622,10 @@ private static boolean requiresBuiltinsInjection(BzlLoadValue.Key key) {
         || (key instanceof BzlLoadValue.KeyForBzlmod && !isFileSafeForUninjectedEvaluation(key));
   }
 
+  private static final PackageIdentifier BAZEL_TOOLS_BOOTSTRAP_RULES_PACKAGE =
+      PackageIdentifier.create(
+          RepositoryName.BAZEL_TOOLS, PathFragment.create("tools/build_defs/repo"));
+
   private static boolean isFileSafeForUninjectedEvaluation(BzlLoadValue.Key key) {
     // We don't inject _builtins for repo rules to avoid a Skyframe cycle.
     // The cycle is caused only with bzlmod because the `@_builtins` repo does not declare its own
@@ -629,9 +633,7 @@ private static boolean isFileSafeForUninjectedEvaluation(BzlLoadValue.Key key) {
     // Bazel module resolution, and if there are any non-registry overrides in the root MODULE.bazel
     // file (such as `git_override` or `archive_override`), the corresponding bzl files will be
     // evaluated.
-    return PackageIdentifier.create(
-            RepositoryName.BAZEL_TOOLS, PathFragment.create("tools/build_defs/repo"))
-        .equals(key.getLabel().getPackageIdentifier());
+    return key.getLabel().getPackageIdentifier().equals(BAZEL_TOOLS_BOOTSTRAP_RULES_PACKAGE);
   }
 
   /**
@@ -944,12 +946,12 @@ private static RepositoryMapping getRepositoryMapping(
     }
 
     if (key instanceof BzlLoadValue.KeyForBzlmod) {
-      if (repoName.equals(RepositoryName.BAZEL_TOOLS)) {
-        // Special case: we're only here to get the @bazel_tools repo (for example, for
-        // http_archive). This repo shouldn't have visibility into anything else (during repo
-        // generation), so we just return an empty repo mapping.
-        // TODO(wyv): disallow fallback.
-        return RepositoryMapping.ALWAYS_FALLBACK;
+      if (key.getLabel().getPackageIdentifier().equals(BAZEL_TOOLS_BOOTSTRAP_RULES_PACKAGE)) {
+        // Special case: we're only here to get one of the rules in the @bazel_tools repo that
+        // load Bazel modules. At this point we can't load from any other modules and thus use a
+        // repository mapping that contains only @bazel_tools itself.
+        return RepositoryMapping.create(
+            ImmutableMap.of("bazel_tools", RepositoryName.BAZEL_TOOLS), RepositoryName.BAZEL_TOOLS);
       }
       if (repoName.isMain()) {
         // Special case: when we try to run an extension in the main repo, we need to grab the repo


Test patch:
diff --git a/src/test/py/bazel/bzlmod/bazel_module_test.py b/src/test/py/bazel/bzlmod/bazel_module_test.py
index 42cef63678125d..bbb66e13121ad0 100644
--- a/src/test/py/bazel/bzlmod/bazel_module_test.py
+++ b/src/test/py/bazel/bzlmod/bazel_module_test.py
@@ -755,6 +755,36 @@ def testLocationNonRegistry(self):
     _, _, stderr = self.RunBazel(['build', '@what'], allow_failure=True)
     self.assertIn('ERROR: @@hello~override//:MODULE.bazel', '\n'.join(stderr))
 
+  def testLoadRulesJavaSymbolThroughBazelTools(self):
+    """Tests that loads from @bazel_tools that delegate to other modules resolve."""
+    self.ScratchFile(
+        'MODULE.bazel',
+        [
+            'ext = use_extension("//:ext.bzl", "ext")',
+            'use_repo(ext, "data")',
+        ],
+    )
+    self.ScratchFile('BUILD')
+    self.ScratchFile(
+        'ext.bzl',
+        [
+            (
+                "load('@bazel_tools//tools/jdk:toolchain_utils.bzl',"
+                " 'find_java_toolchain')"
+            ),
+            'def _repo_impl(ctx):',
+            "  ctx.file('WORKSPACE')",
+            "  ctx.file('BUILD', 'exports_files([\"data.txt\"])')",
+            "  ctx.file('data.txt', 'hi')",
+            'repo = repository_rule(implementation = _repo_impl)',
+            'def _ext_impl(ctx):',
+            "  repo(name='data')",
+            'ext = module_extension(implementation = _ext_impl)',
+        ],
+    )
+
+    self.RunBazel(['build', '@data//:data.txt'])
+
 
 if __name__ == '__main__':
   absltest.main()