From 8c6220a19b65dac3e675a97a59a146b8e294c404 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?X=C3=B9d=C5=8Dng=20Y=C3=A1ng?= Date: Tue, 7 May 2024 17:55:52 -0400 Subject: [PATCH] [7.2.0] Detect extension staleness due root module name/version change (#22282) Fixes #22121 Closes #22123. PiperOrigin-RevId: 630277746 Change-Id: I893a69b0048e933554f515684c6ab1fdae541c0b Co-authored-by: Fabian Meumertzheim --- MODULE.bazel.lock | 2 +- .../lib/bazel/bzlmod/AbridgedModule.java | 2 + .../devtools/build/lib/bazel/bzlmod/BUILD | 1 + .../lib/bazel/bzlmod/BazelLockFileModule.java | 88 ++++++------ .../lib/bazel/bzlmod/GsonTypeAdapterUtil.java | 2 +- .../bazel/bzlmod/ModuleExtensionUsage.java | 17 +-- .../bzlmod/SingleExtensionEvalFunction.java | 11 +- .../bzlmod/SingleExtensionUsagesFunction.java | 1 + .../bzlmod/SingleExtensionUsagesValue.java | 41 +++++- .../py/bazel/bzlmod/bazel_lockfile_test.py | 126 ++++++++++++++++-- src/test/tools/bzlmod/MODULE.bazel.lock | 26 ++-- 11 files changed, 228 insertions(+), 89 deletions(-) diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 29be9f7b32b0b3..6607247aba7ca9 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -2860,7 +2860,7 @@ "bzlTransitiveDigest": "6vqJ6yadlUU86rSzRzNsiG2x5RGOCIg0gyw3j7iFVK4=", "recordedFileInputs": { "@@//MODULE.bazel": "8a870538c6859098c30a59a03523177a26377a76965a0e1a07cdeab366fa3a6b", - "@@//src/test/tools/bzlmod/MODULE.bazel.lock": "07c9574a5800bd17becceef205668343d970125cf533b0cb808112a8db528fb8" + "@@//src/test/tools/bzlmod/MODULE.bazel.lock": "016e032583a71cfb46aa46af96b70666123e74547f55b26da3fe682e583270fd" }, "recordedDirentsInputs": {}, "envVariables": {}, diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/AbridgedModule.java b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/AbridgedModule.java index 215d935c20618f..90e2e4d0022087 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/AbridgedModule.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/AbridgedModule.java @@ -16,12 +16,14 @@ package com.google.devtools.build.lib.bazel.bzlmod; import com.google.auto.value.AutoValue; +import com.ryanharter.auto.value.gson.GenerateTypeAdapter; /** * An abridged version of a {@link Module}, with a reduced set of information available, used for * module extension resolution. */ @AutoValue +@GenerateTypeAdapter public abstract class AbridgedModule { public abstract String getName(); diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BUILD b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BUILD index 78fad249478d4c..c2979e052de77a 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BUILD +++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BUILD @@ -104,6 +104,7 @@ java_library( "//src/main/java/com/google/devtools/build/lib/bazel/bzlmod:module_extension", "//src/main/java/com/google/devtools/build/lib/bazel/repository:repository_options", "//src/main/java/com/google/devtools/build/lib/cmdline", + "//src/main/java/com/google/devtools/build/lib/skyframe:skyframe_cluster", "//src/main/java/com/google/devtools/build/lib/util:abrupt_exit_exception", "//src/main/java/com/google/devtools/build/lib/vfs", "//src/main/java/com/google/devtools/build/skyframe", 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 8fd593546ff7a6..7521bf3d7adc9e 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 @@ -25,14 +25,13 @@ import com.google.devtools.build.lib.cmdline.LabelConstants; import com.google.devtools.build.lib.runtime.BlazeModule; import com.google.devtools.build.lib.runtime.CommandEnvironment; +import com.google.devtools.build.lib.skyframe.SkyframeExecutor; import com.google.devtools.build.lib.util.AbruptExitException; import com.google.devtools.build.lib.vfs.FileSystemUtils; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.Root; import com.google.devtools.build.lib.vfs.RootedPath; -import com.google.devtools.build.skyframe.MemoizingEvaluator; import java.io.IOException; -import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -44,7 +43,7 @@ */ public class BazelLockFileModule extends BlazeModule { - private MemoizingEvaluator evaluator; + private SkyframeExecutor executor; private Path workspaceRoot; @Nullable private BazelModuleResolutionEvent moduleResolutionEvent; @@ -52,7 +51,7 @@ public class BazelLockFileModule extends BlazeModule { @Override public void beforeCommand(CommandEnvironment env) { - evaluator = env.getSkyframeExecutor().getEvaluator(); + executor = env.getSkyframeExecutor(); workspaceRoot = env.getWorkspace(); RepositoryOptions options = env.getOptions().getOptions(RepositoryOptions.class); if (options.lockfileMode.equals(LockfileMode.UPDATE)) { @@ -77,7 +76,8 @@ public void afterCommand() throws AbruptExitException { // first if needed. Map newExtensionInfos = new ConcurrentHashMap<>(); - evaluator + executor + .getEvaluator() .getInMemoryGraph() .parallelForEach( entry -> { @@ -89,13 +89,23 @@ public void afterCommand() throws AbruptExitException { } }); + BazelDepGraphValue depGraphValue; + try { + depGraphValue = + (BazelDepGraphValue) executor.getEvaluator().getExistingValue(BazelDepGraphValue.KEY); + } catch (InterruptedException e) { + // Not thrown in Bazel. + throw new IllegalStateException(e); + } + BazelLockFileValue oldLockfile = moduleResolutionEvent.getOnDiskLockfileValue(); // Create an updated version of the lockfile, keeping only the extension results from the old // lockfile that are still up-to-date and adding the newly resolved extension results. BazelLockFileValue newLockfile = moduleResolutionEvent.getResolutionOnlyLockfileValue().toBuilder() .setModuleExtensions( - combineModuleExtensions(oldLockfile.getModuleExtensions(), newExtensionInfos)) + combineModuleExtensions( + oldLockfile.getModuleExtensions(), newExtensionInfos, depGraphValue)) .build(); // Write the new value to the file, but only if needed. This is not just a performance @@ -119,7 +129,8 @@ public void afterCommand() throws AbruptExitException { ModuleExtensionId, ImmutableMap> oldExtensionInfos, - Map newExtensionInfos) { + Map newExtensionInfos, + BazelDepGraphValue depGraphValue) { Map> updatedExtensionMap = new HashMap<>(); @@ -136,8 +147,8 @@ public void afterCommand() throws AbruptExitException { if (shouldKeepExtension( moduleExtensionId, firstEntryFactors, - firstEntryExtension.getUsagesDigest(), - newExtensionInfos.get(moduleExtensionId))) { + newExtensionInfos.get(moduleExtensionId), + depGraphValue)) { updatedExtensionMap.put(moduleExtensionId, factorToLockedExtension); } } @@ -174,47 +185,46 @@ public void afterCommand() throws AbruptExitException { } /** - * Decide whether to keep this extension or not depending on all of: + * Keep an extension if and only if: * *
    - *
  1. If its dependency on os & arch didn't change - *
  2. If its usages haven't changed + *
  3. it still has usages + *
  4. its dependency on os & arch didn't change + *
  5. it hasn't become reproducible *
* + * We do not check for changes in the usage hash of the extension or e.g. hashes of files accessed + * during the evaluation. These values are checked in SingleExtensionEvalFunction. + * * @param lockedExtensionKey object holding the old extension id and state of os and arch - * @param oldUsagesDigest the digest of usages of this extension in the existing lockfile + * @param newExtensionInfo the in-memory lockfile entry produced by the most recent up-to-date + * evaluation of this extension (if any) + * @param depGraphValue the dep graph value * @return True if this extension should still be in lockfile, false otherwise */ private boolean shouldKeepExtension( - ModuleExtensionId extensionId, + ModuleExtensionId moduleExtensionId, ModuleExtensionEvalFactors lockedExtensionKey, - byte[] oldUsagesDigest, - @Nullable LockFileModuleExtension.WithFactors newExtensionInfo) { - - // If there is a new event for this extension, compare it with the existing ones - if (newExtensionInfo != null) { - boolean doNotLockExtension = !newExtensionInfo.moduleExtension().shouldLockExtension(); - boolean dependencyOnOsChanged = - lockedExtensionKey.getOs().isEmpty() - != newExtensionInfo.extensionFactors().getOs().isEmpty(); - boolean dependencyOnArchChanged = - lockedExtensionKey.getArch().isEmpty() - != newExtensionInfo.extensionFactors().getArch().isEmpty(); - if (doNotLockExtension || dependencyOnOsChanged || dependencyOnArchChanged) { - return false; - } + @Nullable LockFileModuleExtension.WithFactors newExtensionInfo, + BazelDepGraphValue depGraphValue) { + if (!depGraphValue.getExtensionUsagesTable().containsRow(moduleExtensionId)) { + return false; + } + + // If there is no new extension info, the properties of the existing extension entry can't have + // changed. + if (newExtensionInfo == null) { + return true; } - // Otherwise, compare the current usages of this extension with the ones in the lockfile. We - // trim the usages to only the information that influences the evaluation of the extension so - // that irrelevant changes (e.g. locations or imports) don't cause the extension to be removed. - // Note: Extension results can still be stale for other reasons, e.g. because their transitive - // bzl hash changed, but such changes will be detected in SingleExtensionEvalFunction. - return Arrays.equals( - ModuleExtensionUsage.hashForEvaluation( - GsonTypeAdapterUtil.createModuleExtensionUsagesHashGson(), - moduleResolutionEvent.getExtensionUsagesById().row(extensionId)), - oldUsagesDigest); + boolean doNotLockExtension = !newExtensionInfo.moduleExtension().shouldLockExtension(); + boolean dependencyOnOsChanged = + lockedExtensionKey.getOs().isEmpty() + != newExtensionInfo.extensionFactors().getOs().isEmpty(); + boolean dependencyOnArchChanged = + lockedExtensionKey.getArch().isEmpty() + != newExtensionInfo.extensionFactors().getArch().isEmpty(); + return !doNotLockExtension && !dependencyOnOsChanged && !dependencyOnArchChanged; } /** 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 265139bd860048..a90bfb91619eee 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 @@ -482,7 +482,7 @@ public static Gson createLockFileGson(Path moduleFilePath, Path workspaceRoot) { .create(); } - public static Gson createModuleExtensionUsagesHashGson() { + public static Gson createSingleExtensionUsagesValueHashGson() { return newGsonBuilder().create(); } diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleExtensionUsage.java b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleExtensionUsage.java index 2b1c541f2ac698..31657e5dee765d 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleExtensionUsage.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleExtensionUsage.java @@ -19,12 +19,8 @@ import com.google.auto.value.AutoValue; import com.google.common.collect.ImmutableBiMap; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Maps; -import com.google.common.hash.Hashing; import com.google.errorprone.annotations.CanIgnoreReturnValue; -import com.google.gson.Gson; import com.ryanharter.auto.value.gson.GenerateTypeAdapter; import java.util.Optional; import net.starlark.java.syntax.Location; @@ -94,22 +90,11 @@ public static Builder builder() { return new AutoValue_ModuleExtensionUsage.Builder(); } - /** - * Turns the given collection of usages for a particular extension into a hash that can be - * compared for equality with another hash obtained in this way and compares equal only if the two - * original collections of usages are equivalent for the purpose of evaluating the extension. - */ - static byte[] hashForEvaluation(Gson gson, ImmutableMap usages) { - ImmutableMap trimmedUsages = - ImmutableMap.copyOf(Maps.transformValues(usages, ModuleExtensionUsage::trimForEvaluation)); - return Hashing.sha256().hashUnencodedChars(gson.toJson(trimmedUsages)).asBytes(); - } - /** * Returns a new usage with all information removed that does not influence the evaluation of the * extension. */ - private ModuleExtensionUsage trimForEvaluation() { + ModuleExtensionUsage trimForEvaluation() { // We start with the full usage and selectively remove information that does not influence the // evaluation of the extension. Compared to explicitly copying over the parts that do, this // preserves correctness in case new fields are added without updating this code. diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/SingleExtensionEvalFunction.java b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/SingleExtensionEvalFunction.java index dacc947c398e23..9bf5373f040255 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/SingleExtensionEvalFunction.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/SingleExtensionEvalFunction.java @@ -223,9 +223,9 @@ public SkyValue compute(SkyKey skyKey, Environment env) LockFileModuleExtension.builder() .setBzlTransitiveDigest(extension.getBzlTransitiveDigest()) .setUsagesDigest( - ModuleExtensionUsage.hashForEvaluation( - GsonTypeAdapterUtil.createModuleExtensionUsagesHashGson(), - usagesValue.getExtensionUsages())) + SingleExtensionUsagesValue.hashForEvaluation( + GsonTypeAdapterUtil.createSingleExtensionUsagesValueHashGson(), + usagesValue)) .setRecordedFileInputs(moduleExtensionResult.getRecordedFileInputs()) .setRecordedDirentsInputs(moduleExtensionResult.getRecordedDirentsInputs()) .setEnvVariables(extension.getEnvVars()) @@ -280,9 +280,8 @@ private SingleExtensionValue tryGettingValueFromLockFile( // Check extension data in lockfile is still valid, disregarding usage information that is not // relevant for the evaluation of the extension. if (!Arrays.equals( - ModuleExtensionUsage.hashForEvaluation( - GsonTypeAdapterUtil.createModuleExtensionUsagesHashGson(), - usagesValue.getExtensionUsages()), + SingleExtensionUsagesValue.hashForEvaluation( + GsonTypeAdapterUtil.createSingleExtensionUsagesValueHashGson(), usagesValue), lockedExtension.getUsagesDigest())) { diffRecorder.record("The usages of the extension '" + extensionId + "' have changed"); } diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/SingleExtensionUsagesFunction.java b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/SingleExtensionUsagesFunction.java index cedd1bae2ba890..870eb6c276b39b 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/SingleExtensionUsagesFunction.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/SingleExtensionUsagesFunction.java @@ -49,6 +49,7 @@ public SkyValue compute(SkyKey skyKey, Environment env) throws InterruptedExcept } ModuleExtensionId id = (ModuleExtensionId) skyKey.argument(); + // We never request an extension without usages in Skyframe. ImmutableTable usagesTable = bazelDepGraphValue.getExtensionUsagesTable(); return SingleExtensionUsagesValue.create( diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/SingleExtensionUsagesValue.java b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/SingleExtensionUsagesValue.java index 48f17b2740a949..ded469ead31699 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/SingleExtensionUsagesValue.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/SingleExtensionUsagesValue.java @@ -18,6 +18,8 @@ import com.google.auto.value.AutoValue; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; +import com.google.common.hash.Hashing; import com.google.devtools.build.lib.cmdline.RepositoryMapping; import com.google.devtools.build.lib.skyframe.SkyFunctions; import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec; @@ -25,9 +27,17 @@ import com.google.devtools.build.skyframe.SkyFunctionName; import com.google.devtools.build.skyframe.SkyKey; import com.google.devtools.build.skyframe.SkyValue; +import com.google.gson.Gson; +import com.ryanharter.auto.value.gson.GenerateTypeAdapter; -/** The result of {@link SingleExtensionUsagesFunction}. */ +/** + * The result of {@link SingleExtensionUsagesFunction}. + * + *

When adding or exposing new fields to extensions, make sure to update {@link + * #trimForEvaluation()} as well. + */ @AutoValue +@GenerateTypeAdapter public abstract class SingleExtensionUsagesValue implements SkyValue { /** All usages of this extension, by the key of the module where the usage occurs. */ // Note: Equality of SingleExtensionUsagesValue does not check for equality of the order of the @@ -54,6 +64,35 @@ public static SingleExtensionUsagesValue create( extensionUsages, extensionUniqueName, abridgedModules, repoMappings); } + /** + * Turns the given usages value for a particular extension into a hash that can be compared for + * equality with another hash obtained in this way and compares equal only if the two values are + * equivalent for the purpose of evaluating the extension. + */ + static byte[] hashForEvaluation(Gson gson, SingleExtensionUsagesValue usagesValue) { + return Hashing.sha256() + .hashUnencodedChars(gson.toJson(usagesValue.trimForEvaluation())) + .asBytes(); + } + + /** + * Returns a new value with only the information that influences the evaluation of the extension + * and isn't tracked elsewhere. + */ + SingleExtensionUsagesValue trimForEvaluation() { + return SingleExtensionUsagesValue.create( + ImmutableMap.copyOf( + Maps.transformValues(getExtensionUsages(), ModuleExtensionUsage::trimForEvaluation)), + // extensionUniqueName: Not accessible to the extension's implementation function. + // TODO: Reconsider this when resolving #19055. + "", + getAbridgedModules(), + // repoMappings: The usage of repo mappings by the extension's implementation function is + // tracked on the level of individual entries and all label attributes are provided as + // `Label`, which exclusively reference canonical repository names. + ImmutableMap.of()); + } + public static Key key(ModuleExtensionId id) { return Key.create(id); } diff --git a/src/test/py/bazel/bzlmod/bazel_lockfile_test.py b/src/test/py/bazel/bzlmod/bazel_lockfile_test.py index aad717fbc909ce..fb04b521cbb98f 100644 --- a/src/test/py/bazel/bzlmod/bazel_lockfile_test.py +++ b/src/test/py/bazel/bzlmod/bazel_lockfile_test.py @@ -1352,9 +1352,6 @@ def testExtensionEvaluationOnlyRerunOnRelevantUsagesChanges(self): 'generatedRepoSpecs' ]['dep']['attributes']['value'], ) - # The usages of ext_2 changed, but the extension is not re-evaluated, - # so its previous, now stale resolution result must have been removed. - self.assertNotIn(ext_2_key, lockfile['moduleExtensions']) # The only usage of ext_3 was removed. self.assertNotIn(ext_3_key, lockfile['moduleExtensions']) @@ -1550,15 +1547,7 @@ def testExtensionEvaluationRerunsIfDepGraphOrderChanges(self): ], ) _, _, stderr = self.RunBazel(['build', '//:all']) - stderr = '\n'.join(stderr) - - self.assertNotIn('Ext is being evaluated', stderr) - with open('MODULE.bazel.lock', 'r') as json_file: - lockfile = json.load(json_file) - # The order of usages of ext changed, but the extension - # is not re-evaluated, so its previous, now stale - # resolution result must have been removed. - self.assertNotIn(ext_key, lockfile['moduleExtensions']) + self.assertNotIn('Ext is being evaluated', '\n'.join(stderr)) # Trigger evaluation of the extension. _, _, stderr = self.RunBazel(['build', '@dep//:all']) @@ -2222,6 +2211,119 @@ def testForceWatchingDirentsOutsideWorkspaceFails(self): 'attempted to watch path outside workspace', '\n'.join(stderr) ) + def testModuleExtensionRerunsOnNameChange(self): + self.ScratchFile( + 'MODULE.bazel', + [ + ( + 'module(name = "old_name", version = "1.2.3", repo_name =' + ' "fixed_name")' + ), + 'lockfile_ext = use_extension("//:extension.bzl", "lockfile_ext")', + 'use_repo(lockfile_ext, "hello")', + ], + ) + self.ScratchFile('BUILD.bazel') + self.ScratchFile( + 'extension.bzl', + [ + 'def _repo_rule_impl(ctx):', + ' ctx.file("BUILD", "filegroup(name=\'lala\')")', + '', + 'repo_rule = repository_rule(implementation=_repo_rule_impl)', + '', + 'def _module_ext_impl(ctx):', + ' print("I am running the extension: " + ctx.modules[0].name)', + ' repo_rule(name="hello")', + '', + 'lockfile_ext = module_extension(', + ' implementation=_module_ext_impl', + ')', + ], + ) + + _, _, stderr = self.RunBazel(['build', '@hello//:all']) + stderr = ''.join(stderr) + self.assertIn('I am running the extension: old_name', stderr) + + # Shutdown bazel to empty cache and run with no changes + self.RunBazel(['shutdown']) + _, _, stderr = self.RunBazel(['build', '@hello//:all']) + stderr = ''.join(stderr) + self.assertNotIn('I am running the extension: old_name', stderr) + + # Update module name and rerun + self.RunBazel(['shutdown']) + self.ScratchFile( + 'MODULE.bazel', + [ + ( + 'module(name = "new_name", version = "1.2.3", repo_name =' + ' "fixed_name")' + ), + 'lockfile_ext = use_extension("//:extension.bzl", "lockfile_ext")', + 'use_repo(lockfile_ext, "hello")', + ], + ) + _, _, stderr = self.RunBazel(['build', '@hello//:all']) + stderr = ''.join(stderr) + self.assertIn('I am running the extension: new_name', stderr) + + def testModuleExtensionRerunsOnVersionChange(self): + self.ScratchFile( + 'MODULE.bazel', + [ + 'module(name = "old_name", version = "1.2.3")', + 'lockfile_ext = use_extension("extension.bzl", "lockfile_ext")', + 'use_repo(lockfile_ext, "hello")', + ], + ) + self.ScratchFile('BUILD.bazel') + self.ScratchFile( + 'extension.bzl', + [ + 'def _repo_rule_impl(ctx):', + ' ctx.file("BUILD", "filegroup(name=\'lala\')")', + '', + 'repo_rule = repository_rule(implementation=_repo_rule_impl)', + '', + 'def _module_ext_impl(ctx):', + ( + ' print("I am running the extension: " +' + ' ctx.modules[0].version)' + ), + ' repo_rule(name="hello")', + '', + 'lockfile_ext = module_extension(', + ' implementation=_module_ext_impl', + ')', + ], + ) + + _, _, stderr = self.RunBazel(['build', '@hello//:all']) + stderr = ''.join(stderr) + self.assertIn('I am running the extension: 1.2.3', stderr) + + # Shutdown bazel to empty cache and run with no changes + self.RunBazel(['shutdown']) + _, _, stderr = self.RunBazel(['build', '@hello//:all']) + stderr = ''.join(stderr) + self.assertNotIn('I am running the extension: 1.2.3', stderr) + + # Update module name and rerun + self.RunBazel(['shutdown']) + self.ScratchFile( + 'MODULE.bazel', + [ + 'module(name = "old_name", version = "4.5.6")', + 'lockfile_ext = use_extension("extension.bzl", "lockfile_ext")', + 'use_repo(lockfile_ext, "hello")', + ], + ) + _, _, stderr = self.RunBazel(['build', '@hello//:all']) + stderr = ''.join(stderr) + self.assertIn('I am running the extension: 4.5.6', stderr) + if __name__ == '__main__': absltest.main() diff --git a/src/test/tools/bzlmod/MODULE.bazel.lock b/src/test/tools/bzlmod/MODULE.bazel.lock index 713ac10f54fa8c..bbaad7fbe104b8 100644 --- a/src/test/tools/bzlmod/MODULE.bazel.lock +++ b/src/test/tools/bzlmod/MODULE.bazel.lock @@ -1061,7 +1061,7 @@ "@@apple_support~//crosstool:setup.bzl%apple_cc_configure_extension": { "general": { "bzlTransitiveDigest": "pMLFCYaRPkgXPQ8vtuNkMfiHfPmRBy6QJfnid4sWfv0=", - "usagesDigest": "G+Wh7ELKrSnuypJ3qascrgbwzrS7Psg28ta9k4FxTH0=", + "usagesDigest": "fe2odiE5nvy4BXvMJSkQEP0BFhJ/bMKhVBFkh+VCfqU=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, @@ -1089,7 +1089,7 @@ "@@bazel_tools//tools/android:android_extensions.bzl%remote_android_tools_extensions": { "general": { "bzlTransitiveDigest": "7AujGJs5Ol86xb0COHH2NGNawngyUwI8KmJ0yVuSZ+E=", - "usagesDigest": "LPw+9iUcnRY1k89ZEMEZ/AtOYIK2LgSeKnaiYVCDaag=", + "usagesDigest": "dw7C7ErPIkjYn3yY10kLuOatAlp+XNoJ1RbOr/1/3Tg=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, @@ -1117,7 +1117,7 @@ "@@bazel_tools//tools/cpp:cc_configure.bzl%cc_configure_extension": { "general": { "bzlTransitiveDigest": "PHpT2yqMGms2U4L3E/aZ+WcQalmZWm+ILdP3yiLsDhA=", - "usagesDigest": "+YUfVsec8fjmT1xohAjYSdZ308PSnMfO5f1mLULO1sE=", + "usagesDigest": "gWYd8gJQoq+NpmwavSUrR13PCKHYkpwE7EvqssERMc8=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, @@ -1145,7 +1145,7 @@ "@@bazel_tools//tools/osx:xcode_configure.bzl%xcode_configure_extension": { "general": { "bzlTransitiveDigest": "Qh2bWTU6QW6wkrd87qrU4YeY+SG37Nvw3A0PR4Y0L2Y=", - "usagesDigest": "KB6YWX9sttGdrsit5PNIqvCglz3pKdbLHZ3+3LaOEmY=", + "usagesDigest": "6IVLzvb1OpngnM2xlJMGcF4ORDez1NantK5MY6HVXRo=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, @@ -1165,7 +1165,7 @@ "@@bazel_tools//tools/sh:sh_configure.bzl%sh_configure_extension": { "general": { "bzlTransitiveDigest": "hp4NgmNjEg5+xgvzfh6L83bt9/aiiWETuNpwNuF1MSU=", - "usagesDigest": "HJTM/9PgqPKgJxJkdQZIZ++0UHXdTqGOip8ROTW9lYI=", + "usagesDigest": "OFE3XiZmPfwxbY+NUdJN9QP5yiQTMoPN6zW5aV3n4fY=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, @@ -1182,7 +1182,7 @@ "@@bazel_tools//tools/test:extensions.bzl%remote_coverage_tools_extension": { "general": { "bzlTransitiveDigest": "93Butk3OEer7nGZxtwV/qwe+zpX6eMh1OIi+8Lq+6nY=", - "usagesDigest": "O3U7dkKl24l4dKwsK8Y1uf1SAa/eEwLfw4BRsHGugwc=", + "usagesDigest": "fsG10p+ckw98K4MfcQu0IYsMwidhFcUesWaQcPdj7vY=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, @@ -1204,7 +1204,7 @@ "@@buildozer~//:buildozer_binary.bzl%buildozer_binary": { "general": { "bzlTransitiveDigest": "EleDU/FQ1+e/RgkW3aIDmdaxZEthvoWQhsqFTxiSgMI=", - "usagesDigest": "iUUnI5Kz29mJCp8hciKngpgoHU7Mkfe+Fid0AOgNOHo=", + "usagesDigest": "z0uLgf24hCzSZAiM0N9nal6UjWKM4JAmNbm56LP0vU0=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, @@ -1230,7 +1230,7 @@ "@@platforms//host:extension.bzl%host_platform": { "general": { "bzlTransitiveDigest": "xelQcPZH8+tmuOHVjL9vDxMnnQNMlwj0SlvgoqBkm4U=", - "usagesDigest": "n09YBuQPPWD2ruteuyEi0J/rtBYGRWuaq/aBOPdseDE=", + "usagesDigest": "/wi7fMPr4cwiSnBi3fXdHVOtA47+TSmMiv8XIxSTiTE=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, @@ -1247,7 +1247,7 @@ "@@rules_java~//java:extensions.bzl%toolchains": { "general": { "bzlTransitiveDigest": "g6ePEnDe+cica1jeM4cr5pdiFxzK27/ZwwgBz0qcotU=", - "usagesDigest": "1/Dwy6r0Nf/YUE94OFi5Yy0Mo+TrPxA3U8hBaEzYH5s=", + "usagesDigest": "eBHBwdDAsDtvJ8WuWgXTHIEveUYrhzF+zdOY4oGoDP8=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, @@ -1813,7 +1813,7 @@ "@@rules_jvm_external~//:extensions.bzl%maven": { "general": { "bzlTransitiveDigest": "s/CmQ+qQgXPq+FR5/0BRBmZ1xJgvIA9uQa2rGcsiSUA=", - "usagesDigest": "L+U25+BzBiliO0i3XrvqC5up0f210dPakCjqg5MBrp4=", + "usagesDigest": "zel/el3Hu7qA5ChuCSQc5Vo46WXLZmMwpH8aUfhD5wM=", "recordedFileInputs": { "@@rules_jvm_external~//rules_jvm_external_deps_install.json": "10442a5ae27d9ff4c2003e5ab71643bf0d8b48dcf968b4173fa274c3232a8c06" }, @@ -2837,7 +2837,7 @@ "@@rules_jvm_external~//:non-module-deps.bzl%non_module_deps": { "general": { "bzlTransitiveDigest": "lN2hsTr54Jl4C0fITlL6g7MlqCCtezLQgeENrmdNBtg=", - "usagesDigest": "HWGzpnxaDzDwBkFxYvT/plvwcfNrPZGTg9ZYibwxNGw=", + "usagesDigest": "auoSltLJ8wMZ3iqVKYWQsyC9CgcCDt85yHgTXFuQEvE=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, @@ -2865,7 +2865,7 @@ "@@rules_python~//python/extensions:python.bzl%python": { "general": { "bzlTransitiveDigest": "V8RFcR2uLgEQnCHK7DYDVnthwCmRsY0zbyChXOGKGGE=", - "usagesDigest": "2dLy/xmv7+TD8WRYYIxtxZMeS4nrJZGIDSMkYRE0elw=", + "usagesDigest": "gGVuRK4jgFR/T75mVSEYMFObc6AY82n+c1P5B+5hUkE=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, @@ -2895,7 +2895,7 @@ "@@rules_python~//python/extensions/private:internal_deps.bzl%internal_deps": { "general": { "bzlTransitiveDigest": "WppdGtl0faMPVX6wx7nN1JlV+cw9XwxYaGfXa5nWObM=", - "usagesDigest": "MZDuELgGnEk5m0vRt+s02bhPv0B21Oi1jm1KuRqZ5FY=", + "usagesDigest": "/sVN6+OrINK3/oVrgWGYeYt+N1f1J1KnGaHR2AUoIMY=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {},