[6.3.0] Fix absolute file paths showing up in lockfiles (#18993)
* Redact absolute root module file path in `MODULE.bazel.lock`
The absolute path of the root module file in Starlark `Location`s is replaced with a constant when writing to the lock file.
Work towards #18936
Closes #18949.
PiperOrigin-RevId: 549118781
Change-Id: Ie689f7b5edf92296772c605845d694d872074214
* Use a label as the location of the MODULE.bazel file of non-registry overrides
Fixes https://github.com/bazelbuild/bazel/issues/18936
PiperOrigin-RevId: 549310245
Change-Id: I852570fbc81c1592c2fc0b3848d4b58e1c9ffb7d
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BazelLockFileFunction.java b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BazelLockFileFunction.java
index 41cff39..1451f43 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BazelLockFileFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BazelLockFileFunction.java
@@ -15,7 +15,6 @@
package com.google.devtools.build.lib.bazel.bzlmod;
-import static com.google.devtools.build.lib.bazel.bzlmod.GsonTypeAdapterUtil.LOCKFILE_GSON;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.collect.ImmutableList;
@@ -93,7 +92,13 @@
BazelLockFileValue bazelLockFileValue;
try {
String json = FileSystemUtils.readContent(lockfilePath.asPath(), UTF_8);
- bazelLockFileValue = LOCKFILE_GSON.fromJson(json, BazelLockFileValue.class);
+ bazelLockFileValue =
+ GsonTypeAdapterUtil.createLockFileGson(
+ lockfilePath
+ .asPath()
+ .getParentDirectory()
+ .getRelative(LabelConstants.MODULE_DOT_BAZEL_FILE_NAME))
+ .fromJson(json, BazelLockFileValue.class);
} catch (FileNotFoundException e) {
bazelLockFileValue = EMPTY_LOCKFILE;
}
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BazelLockFileModule.java b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BazelLockFileModule.java
index 5bd3b40..fd2bfdd 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BazelLockFileModule.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BazelLockFileModule.java
@@ -14,7 +14,6 @@
package com.google.devtools.build.lib.bazel.bzlmod;
-import static com.google.devtools.build.lib.bazel.bzlmod.GsonTypeAdapterUtil.LOCKFILE_GSON;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.collect.ImmutableMap;
@@ -136,7 +135,14 @@
public static void updateLockfile(RootedPath lockfilePath, BazelLockFileValue updatedLockfile) {
try {
FileSystemUtils.writeContent(
- lockfilePath.asPath(), UTF_8, LOCKFILE_GSON.toJson(updatedLockfile));
+ lockfilePath.asPath(),
+ UTF_8,
+ GsonTypeAdapterUtil.createLockFileGson(
+ lockfilePath
+ .asPath()
+ .getParentDirectory()
+ .getRelative(LabelConstants.MODULE_DOT_BAZEL_FILE_NAME))
+ .toJson(updatedLockfile));
} catch (IOException e) {
logger.atSevere().withCause(e).log(
"Error while updating MODULE.bazel.lock file: %s", e.getMessage());
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/GsonTypeAdapterUtil.java b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/GsonTypeAdapterUtil.java
index c558e80..313471c8 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/GsonTypeAdapterUtil.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/GsonTypeAdapterUtil.java
@@ -20,11 +20,13 @@
import static com.google.devtools.build.lib.bazel.bzlmod.DelegateTypeAdapterFactory.IMMUTABLE_MAP;
import static com.google.devtools.build.lib.bazel.bzlmod.DelegateTypeAdapterFactory.IMMUTABLE_SET;
+import com.google.auto.value.AutoValue;
import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
import com.google.devtools.build.lib.bazel.bzlmod.Version.ParseException;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
+import com.google.devtools.build.lib.vfs.Path;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonParseException;
@@ -42,6 +44,7 @@
import java.util.List;
import java.util.Optional;
import javax.annotation.Nullable;
+import net.starlark.java.syntax.Location;
/**
* Utility class to hold type adapters and helper methods to get gson registered with type adapters
@@ -188,24 +191,102 @@
}
}
- public static final Gson LOCKFILE_GSON =
- new GsonBuilder()
- .setPrettyPrinting()
- .disableHtmlEscaping()
- .enableComplexMapKeySerialization()
- .registerTypeAdapterFactory(GenerateTypeAdapter.FACTORY)
- .registerTypeAdapterFactory(DICT)
- .registerTypeAdapterFactory(IMMUTABLE_MAP)
- .registerTypeAdapterFactory(IMMUTABLE_LIST)
- .registerTypeAdapterFactory(IMMUTABLE_BIMAP)
- .registerTypeAdapterFactory(IMMUTABLE_SET)
- .registerTypeAdapterFactory(OPTIONAL)
- .registerTypeAdapter(Version.class, VERSION_TYPE_ADAPTER)
- .registerTypeAdapter(ModuleKey.class, MODULE_KEY_TYPE_ADAPTER)
- .registerTypeAdapter(ModuleExtensionId.class, MODULE_EXTENSION_ID_TYPE_ADAPTER)
- .registerTypeAdapter(AttributeValues.class, new AttributeValuesAdapter())
- .registerTypeAdapter(byte[].class, BYTE_ARRAY_TYPE_ADAPTER)
- .create();
+ /**
+ * A variant of {@link Location} that converts the absolute path to the root module file to a
+ * constant and back.
+ */
+ // protected only for @AutoValue
+ @GenerateTypeAdapter
+ @AutoValue
+ protected abstract static class RootModuleFileEscapingLocation {
+ // This marker string is neither a valid absolute path nor a valid URL and thus cannot conflict
+ // with any real module file location.
+ private static final String ROOT_MODULE_FILE_LABEL = "@@//:MODULE.bazel";
+
+ public abstract String file();
+
+ public abstract int line();
+
+ public abstract int column();
+
+ public Location toLocation(String moduleFilePath) {
+ String file;
+ if (file().equals(ROOT_MODULE_FILE_LABEL)) {
+ file = moduleFilePath;
+ } else {
+ file = file();
+ }
+ return Location.fromFileLineColumn(file, line(), column());
+ }
+
+ public static RootModuleFileEscapingLocation fromLocation(
+ Location location, String moduleFilePath) {
+ String file;
+ if (location.file().equals(moduleFilePath)) {
+ file = ROOT_MODULE_FILE_LABEL;
+ } else {
+ file = location.file();
+ }
+ return new AutoValue_GsonTypeAdapterUtil_RootModuleFileEscapingLocation(
+ file, location.line(), location.column());
+ }
+ }
+
+ private static final class LocationTypeAdapterFactory implements TypeAdapterFactory {
+
+ private final String moduleFilePath;
+
+ public LocationTypeAdapterFactory(Path moduleFilePath) {
+ this.moduleFilePath = moduleFilePath.getPathString();
+ }
+
+ @Nullable
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+ if (typeToken.getRawType() != Location.class) {
+ return null;
+ }
+ TypeAdapter<RootModuleFileEscapingLocation> relativizedLocationTypeAdapter =
+ gson.getAdapter(RootModuleFileEscapingLocation.class);
+ return (TypeAdapter<T>)
+ new TypeAdapter<Location>() {
+
+ @Override
+ public void write(JsonWriter jsonWriter, Location location) throws IOException {
+ relativizedLocationTypeAdapter.write(
+ jsonWriter,
+ RootModuleFileEscapingLocation.fromLocation(location, moduleFilePath));
+ }
+
+ @Override
+ public Location read(JsonReader jsonReader) throws IOException {
+ return relativizedLocationTypeAdapter.read(jsonReader).toLocation(moduleFilePath);
+ }
+ };
+ }
+ }
+
+ public static Gson createLockFileGson(Path moduleFilePath) {
+ return new GsonBuilder()
+ .setPrettyPrinting()
+ .disableHtmlEscaping()
+ .enableComplexMapKeySerialization()
+ .registerTypeAdapterFactory(GenerateTypeAdapter.FACTORY)
+ .registerTypeAdapterFactory(DICT)
+ .registerTypeAdapterFactory(IMMUTABLE_MAP)
+ .registerTypeAdapterFactory(IMMUTABLE_LIST)
+ .registerTypeAdapterFactory(IMMUTABLE_BIMAP)
+ .registerTypeAdapterFactory(IMMUTABLE_SET)
+ .registerTypeAdapterFactory(OPTIONAL)
+ .registerTypeAdapterFactory(new LocationTypeAdapterFactory(moduleFilePath))
+ .registerTypeAdapter(Version.class, VERSION_TYPE_ADAPTER)
+ .registerTypeAdapter(ModuleKey.class, MODULE_KEY_TYPE_ADAPTER)
+ .registerTypeAdapter(ModuleExtensionId.class, MODULE_EXTENSION_ID_TYPE_ADAPTER)
+ .registerTypeAdapter(AttributeValues.class, new AttributeValuesAdapter())
+ .registerTypeAdapter(byte[].class, BYTE_ARRAY_TYPE_ADAPTER)
+ .create();
+ }
private GsonTypeAdapterUtil() {}
}
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileFunction.java b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileFunction.java
index 89792cb..3d62263 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileFunction.java
@@ -24,7 +24,9 @@
import com.google.devtools.build.lib.actions.FileValue;
import com.google.devtools.build.lib.bazel.bzlmod.ModuleFileValue.NonRootModuleFileValue;
import com.google.devtools.build.lib.bazel.bzlmod.ModuleFileValue.RootModuleFileValue;
+import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.cmdline.LabelConstants;
+import com.google.devtools.build.lib.cmdline.PackageIdentifier;
import com.google.devtools.build.lib.cmdline.RepositoryName;
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.packages.StarlarkExportable;
@@ -37,6 +39,7 @@
import com.google.devtools.build.lib.util.Fingerprint;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.PathFragment;
import com.google.devtools.build.lib.vfs.Root;
import com.google.devtools.build.lib.vfs.RootedPath;
import com.google.devtools.build.skyframe.SkyFunction;
@@ -199,11 +202,11 @@
if (env.getValue(FileValue.key(moduleFilePath)) == null) {
return null;
}
- ModuleFile moduleFile = readModuleFile(moduleFilePath.asPath());
- String moduleFileHash = new Fingerprint().addBytes(moduleFile.getContent()).hexDigestAndReset();
+ byte[] moduleFileContents = readModuleFile(moduleFilePath.asPath());
+ String moduleFileHash = new Fingerprint().addBytes(moduleFileContents).hexDigestAndReset();
ModuleFileGlobals moduleFileGlobals =
execModuleFile(
- moduleFile,
+ ModuleFile.create(moduleFileContents, moduleFilePath.asPath().toString()),
/* registry= */ null,
ModuleKey.ROOT,
/* ignoreDevDeps= */ Objects.requireNonNull(IGNORE_DEV_DEPS.get(env)),
@@ -335,8 +338,15 @@
if (env.getValue(FileValue.key(moduleFilePath)) == null) {
return null;
}
+ Label moduleFileLabel =
+ Label.createUnvalidated(
+ PackageIdentifier.create(key.getCanonicalRepoName(), PathFragment.EMPTY_FRAGMENT),
+ LabelConstants.MODULE_DOT_BAZEL_FILE_NAME.getBaseName());
GetModuleFileResult result = new GetModuleFileResult();
- result.moduleFile = readModuleFile(moduleFilePath.asPath());
+ result.moduleFile =
+ ModuleFile.create(
+ readModuleFile(moduleFilePath.asPath()),
+ moduleFileLabel.getUnambiguousCanonicalForm());
return result;
}
@@ -392,10 +402,9 @@
throw errorf(Code.MODULE_NOT_FOUND, "module not found in registries: %s", key);
}
- private static ModuleFile readModuleFile(Path path) throws ModuleFileFunctionException {
+ private static byte[] readModuleFile(Path path) throws ModuleFileFunctionException {
try {
- return ModuleFile.create(
- FileSystemUtils.readWithKnownFileSize(path, path.getFileSize()), path.getPathString());
+ return FileSystemUtils.readWithKnownFileSize(path, path.getFileSize());
} catch (IOException e) {
throw errorf(Code.MODULE_NOT_FOUND, "MODULE.bazel expected but not found at %s", path);
}
diff --git a/src/test/py/bazel/bzlmod/bazel_lockfile_test.py b/src/test/py/bazel/bzlmod/bazel_lockfile_test.py
index 03381c1..1bbed92 100644
--- a/src/test/py/bazel/bzlmod/bazel_lockfile_test.py
+++ b/src/test/py/bazel/bzlmod/bazel_lockfile_test.py
@@ -505,6 +505,70 @@
lockfile = json.loads(f.read().strip())
self.assertEqual(len(lockfile['moduleExtensions']), 0)
+ def testNoAbsoluteRootModuleFilePath(self):
+ self.ScratchFile(
+ 'MODULE.bazel',
+ [
+ 'ext = use_extension("extension.bzl", "ext")',
+ 'ext.dep(generate = True)',
+ 'use_repo(ext, ext_hello = "hello")',
+ 'other_ext = use_extension("extension.bzl", "other_ext")',
+ 'other_ext.dep(generate = False)',
+ 'use_repo(other_ext, other_ext_hello = "hello")',
+ ],
+ )
+ self.ScratchFile('BUILD.bazel')
+ self.ScratchFile(
+ 'extension.bzl',
+ [
+ 'def _repo_rule_impl(ctx):',
+ ' ctx.file("WORKSPACE")',
+ ' ctx.file("BUILD", "filegroup(name=\'lala\')")',
+ '',
+ 'repo_rule = repository_rule(implementation=_repo_rule_impl)',
+ '',
+ 'def _module_ext_impl(ctx):',
+ ' for mod in ctx.modules:',
+ ' for dep in mod.tags.dep:',
+ ' if dep.generate:',
+ ' repo_rule(name="hello")',
+ '',
+ '_dep = tag_class(attrs={"generate": attr.bool()})',
+ 'ext = module_extension(',
+ ' implementation=_module_ext_impl,',
+ ' tag_classes={"dep": _dep},',
+ ')',
+ 'other_ext = module_extension(',
+ ' implementation=_module_ext_impl,',
+ ' tag_classes={"dep": _dep},',
+ ')',
+ ],
+ )
+
+ # Paths to module files in error message always use forward slashes as
+ # separators, even on Windows.
+ module_file_path = self.Path('MODULE.bazel').replace('\\', '/')
+
+ self.RunBazel(['build', '--nobuild', '@ext_hello//:all'])
+ with open(self.Path('MODULE.bazel.lock'), 'r') as f:
+ self.assertNotIn(module_file_path, f.read())
+
+ self.RunBazel(['shutdown'])
+ exit_code, _, stderr = self.RunBazel(
+ ['build', '--nobuild', '@other_ext_hello//:all'], allow_failure=True
+ )
+ self.AssertNotExitCode(exit_code, 0, stderr)
+ self.assertIn(
+ (
+ 'ERROR: module extension "other_ext" from "//:extension.bzl" does '
+ 'not generate repository "hello", yet it is imported as '
+ '"other_ext_hello" in the usage at '
+ + module_file_path
+ + ':4:26'
+ ),
+ stderr,
+ )
+
if __name__ == '__main__':
unittest.main()
diff --git a/src/test/py/bazel/bzlmod/bazel_module_test.py b/src/test/py/bazel/bzlmod/bazel_module_test.py
index 682e58a..97d1455 100644
--- a/src/test/py/bazel/bzlmod/bazel_module_test.py
+++ b/src/test/py/bazel/bzlmod/bazel_module_test.py
@@ -484,6 +484,258 @@
_, stdout, _ = self.RunBazel(['run', '//:main'], allow_failure=False)
self.assertIn('main function => aaa@1.2', stdout)
+ def testNativeModuleNameAndVersion(self):
+ self.main_registry.setModuleBasePath('projects')
+ projects_dir = self.main_registry.projects
+
+ self.ScratchFile(
+ 'MODULE.bazel',
+ [
+ 'module(name="root",version="0.1")',
+ 'bazel_dep(name="foo",version="1.0")',
+ 'report_ext = use_extension("@foo//:ext.bzl", "report_ext")',
+ 'use_repo(report_ext, "report_repo")',
+ 'bazel_dep(name="bar")',
+ 'local_path_override(module_name="bar",path="bar")',
+ ],
+ )
+ self.ScratchFile('WORKSPACE')
+ self.ScratchFile(
+ 'WORKSPACE.bzlmod', ['local_repository(name="quux",path="quux")']
+ )
+ self.ScratchFile(
+ 'BUILD',
+ [
+ 'load("@foo//:report.bzl", "report")',
+ 'report()',
+ ],
+ )
+ # foo: a repo defined by a normal Bazel module. Also hosts the extension
+ # `report_ext` which generates a repo `report_repo`.
+ self.main_registry.createLocalPathModule('foo', '1.0', 'foo')
+ projects_dir.joinpath('foo').mkdir(exist_ok=True)
+ scratchFile(projects_dir.joinpath('foo', 'WORKSPACE'))
+ scratchFile(
+ projects_dir.joinpath('foo', 'BUILD'),
+ [
+ 'load(":report.bzl", "report")',
+ 'report()',
+ ],
+ )
+ scratchFile(
+ projects_dir.joinpath('foo', 'report.bzl'),
+ [
+ 'def report():',
+ ' repo = native.repository_name()',
+ ' name = str(native.module_name())',
+ ' version = str(native.module_version())',
+ ' print("@" + repo + " reporting in: " + name + "@" + version)',
+ ' native.filegroup(name="a")',
+ ],
+ )
+ scratchFile(
+ projects_dir.joinpath('foo', 'ext.bzl'),
+ [
+ 'def _report_repo(rctx):',
+ ' rctx.file("BUILD",',
+ ' "load(\\"@foo//:report.bzl\\", \\"report\\")\\n" +',
+ ' "report()")',
+ 'report_repo = repository_rule(_report_repo)',
+ 'report_ext = module_extension(',
+ ' lambda mctx: report_repo(name="report_repo"))',
+ ],
+ )
+ # bar: a repo defined by a Bazel module with a non-registry override
+ self.ScratchFile('bar/WORKSPACE')
+ self.ScratchFile(
+ 'bar/MODULE.bazel',
+ [
+ 'module(name="bar", version="2.0")',
+ 'bazel_dep(name="foo",version="1.0")',
+ ],
+ )
+ self.ScratchFile(
+ 'bar/BUILD',
+ [
+ 'load("@foo//:report.bzl", "report")',
+ 'report()',
+ ],
+ )
+ # quux: a repo defined by WORKSPACE
+ self.ScratchFile('quux/WORKSPACE')
+ self.ScratchFile(
+ 'quux/BUILD',
+ [
+ 'load("@foo//:report.bzl", "report")',
+ 'report()',
+ ],
+ )
+
+ _, _, stderr = self.RunBazel(
+ [
+ 'build',
+ ':a',
+ '@foo//:a',
+ '@report_repo//:a',
+ '@bar//:a',
+ '@quux//:a',
+ ],
+ )
+ stderr = '\n'.join(stderr)
+ self.assertIn('@@ reporting in: root@0.1', stderr)
+ self.assertIn('@@foo~1.0 reporting in: foo@1.0', stderr)
+ self.assertIn(
+ '@@foo~1.0~report_ext~report_repo reporting in: foo@1.0', stderr
+ )
+ self.assertIn('@@bar~override reporting in: bar@2.0', stderr)
+ self.assertIn('@@quux reporting in: None@None', stderr)
+
+ def testWorkspaceToolchainRegistrationWithPlatformsConstraint(self):
+ """Regression test for https://github.com/bazelbuild/bazel/issues/17289."""
+ self.ScratchFile('MODULE.bazel')
+ self.ScratchFile(
+ 'WORKSPACE', ['register_toolchains("//:my_toolchain_toolchain")']
+ )
+ os.remove(self.Path('WORKSPACE.bzlmod'))
+
+ self.ScratchFile(
+ 'BUILD.bazel',
+ [
+ 'load(":defs.bzl", "get_host_os", "my_consumer", "my_toolchain")',
+ 'toolchain_type(name = "my_toolchain_type")',
+ 'my_toolchain(',
+ ' name = "my_toolchain",',
+ ' my_value = "Hello, Bzlmod!",',
+ ')',
+ 'toolchain(',
+ ' name = "my_toolchain_toolchain",',
+ ' toolchain = ":my_toolchain",',
+ ' toolchain_type = ":my_toolchain_type",',
+ ' target_compatible_with = [',
+ ' "@platforms//os:" + get_host_os(),',
+ ' ],',
+ ')',
+ 'my_consumer(',
+ ' name = "my_consumer",',
+ ')',
+ ],
+ )
+
+ self.ScratchFile(
+ 'defs.bzl',
+ [
+ (
+ 'load("@local_config_platform//:constraints.bzl",'
+ ' "HOST_CONSTRAINTS")'
+ ),
+ 'def _my_toolchain_impl(ctx):',
+ ' return [',
+ ' platform_common.ToolchainInfo(',
+ ' my_value = ctx.attr.my_value,',
+ ' ),',
+ ' ]',
+ 'my_toolchain = rule(',
+ ' implementation = _my_toolchain_impl,',
+ ' attrs = {',
+ ' "my_value": attr.string(),',
+ ' },',
+ ')',
+ 'def _my_consumer(ctx):',
+ ' my_toolchain_info = ctx.toolchains["//:my_toolchain_type"]',
+ ' out = ctx.actions.declare_file(ctx.attr.name)',
+ (
+ ' ctx.actions.write(out, "my_value ='
+ ' {}".format(my_toolchain_info.my_value))'
+ ),
+ ' return [DefaultInfo(files = depset([out]))]',
+ 'my_consumer = rule(',
+ ' implementation = _my_consumer,',
+ ' attrs = {},',
+ ' toolchains = ["//:my_toolchain_type"],',
+ ')',
+ 'def get_host_os():',
+ ' for constraint in HOST_CONSTRAINTS:',
+ ' if constraint.startswith("@platforms//os:"):',
+ ' return constraint.removeprefix("@platforms//os:")',
+ ],
+ )
+
+ self.RunBazel([
+ 'build',
+ '//:my_consumer',
+ '--toolchain_resolution_debug=//:my_toolchain_type',
+ ])
+ with open(self.Path('bazel-bin/my_consumer'), 'r') as f:
+ self.assertEqual(f.read().strip(), 'my_value = Hello, Bzlmod!')
+
+ def testModuleExtensionWithRuleError(self):
+ self.ScratchFile(
+ 'MODULE.bazel',
+ [
+ 'ext = use_extension("extensions.bzl", "ext")',
+ 'use_repo(ext, "ext")',
+ ],
+ )
+ self.ScratchFile('BUILD')
+ self.ScratchFile(
+ 'extensions.bzl',
+ [
+ 'def _rule_impl(ctx):',
+ ' print("RULE CALLED")',
+ 'init_rule = rule(_rule_impl)',
+ 'def ext_impl(module_ctx):',
+ ' init_rule()',
+ 'ext = module_extension(implementation = ext_impl,)',
+ ],
+ )
+ exit_code, _, stderr = self.RunBazel(
+ ['build', '--nobuild', '@ext//:all'],
+ allow_failure=True,
+ )
+ self.AssertExitCode(exit_code, 48, stderr)
+ self.assertIn(
+ 'Error in init_rule: A rule can only be instantiated in a BUILD file, '
+ 'or a macro invoked from a BUILD file',
+ stderr,
+ )
+
+ def testLocationRoot(self):
+ """Tests that the reported location of the MODULE.bazel file of the root module is as expected."""
+ self.ScratchFile('MODULE.bazel', ['wat'])
+ _, _, stderr = self.RunBazel(['build', '@what'], allow_failure=True)
+ self.assertIn(
+ 'ERROR: ' + self.Path('MODULE.bazel').replace('\\', '/'),
+ '\n'.join(stderr).replace('\\', '/'),
+ )
+
+ def testLocationRegistry(self):
+ """Tests that the reported location of the MODULE.bazel file of a module from a registry is as expected."""
+ self.ScratchFile('MODULE.bazel', ['bazel_dep(name="hello",version="1.0")'])
+ self.main_registry.createCcModule(
+ 'hello', '1.0', extra_module_file_contents=['wat']
+ )
+ _, _, stderr = self.RunBazel(['build', '@what'], allow_failure=True)
+ self.assertIn(
+ 'ERROR: '
+ + self.main_registry.getURL()
+ + '/modules/hello/1.0/MODULE.bazel',
+ '\n'.join(stderr),
+ )
+
+ def testLocationNonRegistry(self):
+ """Tests that the reported location of the MODULE.bazel file of a module with a non-registry override is as expected."""
+ self.ScratchFile(
+ 'MODULE.bazel',
+ [
+ 'bazel_dep(name="hello")',
+ 'local_path_override(module_name="hello",path="hello")',
+ ],
+ )
+ self.ScratchFile('hello/MODULE.bazel', ['wat'])
+ self.ScratchFile('hello/WORKSPACE.bazel')
+ _, _, stderr = self.RunBazel(['build', '@what'], allow_failure=True)
+ self.assertIn('ERROR: @@hello~override//:MODULE.bazel', '\n'.join(stderr))
+
if __name__ == '__main__':
unittest.main()