From c1fea137312248d606bbb73bac1ab4a4e87557a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?X=C3=B9d=C5=8Dng=20Y=C3=A1ng?= Date: Sat, 22 Apr 2023 07:47:24 +1000 Subject: [PATCH] Introduce max_compatibility_level for bazel_dep (#18178) * Add InterimModule to represent a Module before resolution finishes The class Module currently holds data that's no longer needed after resolution finishes (such as the compatibility level, bazel compatibility, the registry where it comes from, etc). Conversely, the repo spec field is not computed until the end of resolution. To remove runtime checks and reduce cognitive overhead, this CL splits the Module class into two; one only used after resolution finishes (Module), and one only used before (InterimModule). This allows us to introduce max_compatibility_level in a follow CL. Work towards https://github.com/bazelbuild/bazel/issues/17378 Co-authored-by: Brentley Jones PiperOrigin-RevId: 525780111 Change-Id: I2df8d78d324b3c8744ba0a4eda405d162e9bbb8c * Selection with max_compatibility_level See code comments for more details. tl;dr: we use the new `bazel_dep(max_compatibility_level=)` attribute to influence version selection. Fixes https://github.com/bazelbuild/bazel/issues/17378 RELNOTES: Added a new `max_compatibility_level` attribute to the `bazel_dep` directive, which allows version selection to upgrade a dependency up to the specified compatibility level. Co-authored-by: Brentley Jones PiperOrigin-RevId: 526118928 Change-Id: I332eb3761e0dee0cb7f318cb5d8d1780fca91be8 --------- Co-authored-by: Brentley Jones --- .../devtools/build/lib/bazel/bzlmod/BUILD | 2 + .../bzlmod/BazelModuleInspectorFunction.java | 16 +- .../bzlmod/BazelModuleResolutionFunction.java | 127 ++- .../bzlmod/BazelModuleResolutionValue.java | 4 +- .../build/lib/bazel/bzlmod/Discovery.java | 39 +- .../build/lib/bazel/bzlmod/InterimModule.java | 213 +++++ .../build/lib/bazel/bzlmod/Module.java | 180 +--- .../build/lib/bazel/bzlmod/ModuleBase.java | 78 ++ .../lib/bazel/bzlmod/ModuleFileFunction.java | 6 +- .../lib/bazel/bzlmod/ModuleFileGlobals.java | 33 +- .../lib/bazel/bzlmod/ModuleFileValue.java | 6 +- .../build/lib/bazel/bzlmod/Selection.java | 265 ++++-- .../lib/skyframe/BzlmodRepoRuleFunction.java | 5 +- .../bzlmod/BazelDepGraphFunctionTest.java | 42 +- .../bzlmod/BazelLockFileFunctionTest.java | 31 +- .../BazelModuleInspectorFunctionTest.java | 110 +-- .../lib/bazel/bzlmod/BzlmodTestUtil.java | 89 +- .../build/lib/bazel/bzlmod/DiscoveryTest.java | 66 +- .../lib/bazel/bzlmod/InterimModuleTest.java | 51 ++ .../bazel/bzlmod/ModuleFileFunctionTest.java | 29 +- .../build/lib/bazel/bzlmod/ModuleTest.java | 26 +- .../build/lib/bazel/bzlmod/SelectionTest.java | 819 +++++++++++++----- .../bazel/bzlmod/StarlarkBazelModuleTest.java | 12 +- 23 files changed, 1505 insertions(+), 744 deletions(-) create mode 100644 src/main/java/com/google/devtools/build/lib/bazel/bzlmod/InterimModule.java create mode 100644 src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleBase.java create mode 100644 src/test/java/com/google/devtools/build/lib/bazel/bzlmod/InterimModuleTest.java 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 bd02cc51fcdec3..5c4f4adbab73bf 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 @@ -94,8 +94,10 @@ java_library( "BazelModuleResolutionValue.java", "BzlmodFlagsAndEnvVars.java", "GitOverride.java", + "InterimModule.java", "LocalPathOverride.java", "Module.java", + "ModuleBase.java", "ModuleExtensionEvalStarlarkThreadContext.java", "ModuleFileValue.java", "ModuleOverride.java", diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BazelModuleInspectorFunction.java b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BazelModuleInspectorFunction.java index 0394de65277e22..c2c3ffe57a5751 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BazelModuleInspectorFunction.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BazelModuleInspectorFunction.java @@ -55,7 +55,7 @@ public SkyValue compute(SkyKey skyKey, Environment env) return null; } ImmutableMap overrides = root.getOverrides(); - ImmutableMap unprunedDepGraph = resolutionValue.getUnprunedDepGraph(); + ImmutableMap unprunedDepGraph = resolutionValue.getUnprunedDepGraph(); ImmutableMap resolvedDepGraph = resolutionValue.getResolvedDepGraph(); ImmutableMap depGraph = @@ -74,7 +74,7 @@ public SkyValue compute(SkyKey skyKey, Environment env) } public static ImmutableMap computeAugmentedGraph( - ImmutableMap unprunedDepGraph, + ImmutableMap unprunedDepGraph, ImmutableSet usedModules, ImmutableMap overrides) { Map depGraphAugmentBuilder = new HashMap<>(); @@ -83,9 +83,9 @@ public static ImmutableMap computeAugmentedGraph( // to their children AugmentedModule as dependant. Also fill in their own AugmentedModule // with a map from their dependencies to the resolution reason that was applied to each. // The newly created graph will also contain ModuleAugments for non-loaded modules. - for (Entry e : unprunedDepGraph.entrySet()) { + for (Entry e : unprunedDepGraph.entrySet()) { ModuleKey parentKey = e.getKey(); - Module parentModule = e.getValue(); + InterimModule parentModule = e.getValue(); AugmentedModule.Builder parentBuilder = depGraphAugmentBuilder @@ -95,10 +95,10 @@ public static ImmutableMap computeAugmentedGraph( .setLoaded(true); for (String childDep : parentModule.getDeps().keySet()) { - ModuleKey originalKey = parentModule.getOriginalDeps().get(childDep); - Module originalModule = unprunedDepGraph.get(originalKey); - ModuleKey key = parentModule.getDeps().get(childDep); - Module module = unprunedDepGraph.get(key); + ModuleKey originalKey = parentModule.getOriginalDeps().get(childDep).toModuleKey(); + InterimModule originalModule = unprunedDepGraph.get(originalKey); + ModuleKey key = parentModule.getDeps().get(childDep).toModuleKey(); + InterimModule module = unprunedDepGraph.get(key); AugmentedModule.Builder originalChildBuilder = depGraphAugmentBuilder.computeIfAbsent(originalKey, AugmentedModule::builder); diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BazelModuleResolutionFunction.java b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BazelModuleResolutionFunction.java index 3c9f1c825e6c97..39092c993d6b0b 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BazelModuleResolutionFunction.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BazelModuleResolutionFunction.java @@ -21,8 +21,10 @@ 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.devtools.build.lib.analysis.BlazeVersionInfo; import com.google.devtools.build.lib.bazel.BazelVersion; +import com.google.devtools.build.lib.bazel.bzlmod.InterimModule.DepSpec; import com.google.devtools.build.lib.bazel.bzlmod.ModuleFileValue.RootModuleFileValue; import com.google.devtools.build.lib.bazel.bzlmod.Version.ParseException; import com.google.devtools.build.lib.bazel.repository.RepositoryOptions.BazelCompatibilityMode; @@ -78,18 +80,18 @@ public SkyValue compute(SkyKey skyKey, Environment env) if (root == null) { return null; } - ImmutableMap initialDepGraph = Discovery.run(env, root); + ImmutableMap initialDepGraph = Discovery.run(env, root); if (initialDepGraph == null) { return null; } - BazelModuleResolutionValue selectionResultValue; + Selection.Result selectionResult; try { - selectionResultValue = Selection.run(initialDepGraph, root.getOverrides()); + selectionResult = Selection.run(initialDepGraph, root.getOverrides()); } catch (ExternalDepsException e) { throw new BazelModuleResolutionFunctionException(e, Transience.PERSISTENT); } - ImmutableMap resolvedDepGraph = selectionResultValue.getResolvedDepGraph(); + ImmutableMap resolvedDepGraph = selectionResult.getResolvedDepGraph(); verifyRootModuleDirectDepsAreAccurate( initialDepGraph.get(ModuleKey.ROOT), @@ -109,40 +111,15 @@ public SkyValue compute(SkyKey skyKey, Environment env) Objects.requireNonNull(ALLOWED_YANKED_VERSIONS.get(env))), env.getListener()); - // Add repo spec to each module and remove registry - try { - ImmutableMap.Builder mapBuilder = ImmutableMap.builder(); - for (Map.Entry entry : resolvedDepGraph.entrySet()) { - Module module = entry.getValue(); - // Only change modules with registry (not overridden) - if (module.getRegistry() != null) { - RepoSpec moduleRepoSpec = - module - .getRegistry() - .getRepoSpec(module.getKey(), module.getCanonicalRepoName(), env.getListener()); - ModuleOverride override = root.getOverrides().get(entry.getKey().getName()); - moduleRepoSpec = maybeAppendAdditionalPatches(moduleRepoSpec, override); - module = module.toBuilder().setRepoSpec(moduleRepoSpec).setRegistry(null).build(); - } - mapBuilder.put(entry.getKey(), module); - } - resolvedDepGraph = mapBuilder.buildOrThrow(); - } catch (IOException e) { - throw new BazelModuleResolutionFunctionException( - ExternalDepsException.withMessage( - Code.ERROR_ACCESSING_REGISTRY, - "Unable to get module repo spec from registry: %s", - e.getMessage()), - Transience.PERSISTENT); - } + ImmutableMap finalDepGraph = + computeFinalDepGraph(resolvedDepGraph, root.getOverrides(), env.getListener()); - return BazelModuleResolutionValue.create( - resolvedDepGraph, selectionResultValue.getUnprunedDepGraph()); + return BazelModuleResolutionValue.create(finalDepGraph, selectionResult.getUnprunedDepGraph()); } private static void verifyRootModuleDirectDepsAreAccurate( - Module discoveredRootModule, - Module resolvedRootModule, + InterimModule discoveredRootModule, + InterimModule resolvedRootModule, CheckDirectDepsMode mode, EventHandler eventHandler) throws BazelModuleResolutionFunctionException { @@ -151,14 +128,14 @@ private static void verifyRootModuleDirectDepsAreAccurate( } boolean failure = false; - for (Map.Entry dep : discoveredRootModule.getDeps().entrySet()) { - ModuleKey resolved = resolvedRootModule.getDeps().get(dep.getKey()); - if (!dep.getValue().equals(resolved)) { + for (Map.Entry dep : discoveredRootModule.getDeps().entrySet()) { + ModuleKey resolved = resolvedRootModule.getDeps().get(dep.getKey()).toModuleKey(); + if (!dep.getValue().toModuleKey().equals(resolved)) { String message = String.format( "For repository '%s', the root module requires module version %s, but got %s in the" + " resolved dependency graph.", - dep.getKey(), dep.getValue(), resolved); + dep.getKey(), dep.getValue().toModuleKey(), resolved); if (mode == CheckDirectDepsMode.WARNING) { eventHandler.handle(Event.warn(message)); } else { @@ -177,7 +154,9 @@ private static void verifyRootModuleDirectDepsAreAccurate( } public static void checkBazelCompatibility( - ImmutableCollection modules, BazelCompatibilityMode mode, EventHandler eventHandler) + ImmutableCollection modules, + BazelCompatibilityMode mode, + EventHandler eventHandler) throws BazelModuleResolutionFunctionException { if (mode == BazelCompatibilityMode.OFF) { return; @@ -189,7 +168,7 @@ public static void checkBazelCompatibility( } BazelVersion curVersion = BazelVersion.parse(currentBazelVersion); - for (Module module : modules) { + for (InterimModule module : modules) { for (String compatVersion : module.getBazelCompatibility()) { if (!curVersion.satisfiesCompatibility(compatVersion)) { String message = @@ -306,14 +285,14 @@ private boolean parseModuleKeysFromString( return false; } - private void verifyYankedVersions( - ImmutableMap depGraph, + private static void verifyYankedVersions( + ImmutableMap depGraph, Optional> allowedYankedVersions, ExtendedEventHandler eventHandler) throws BazelModuleResolutionFunctionException, InterruptedException { // Check whether all resolved modules are either not yanked or allowed. Modules with a // NonRegistryOverride are ignored as their metadata is not available whatsoever. - for (Module m : depGraph.values()) { + for (InterimModule m : depGraph.values()) { if (m.getKey().equals(ModuleKey.ROOT) || m.getRegistry() == null) { continue; } @@ -349,7 +328,7 @@ private void verifyYankedVersions( } } - private RepoSpec maybeAppendAdditionalPatches(RepoSpec repoSpec, ModuleOverride override) { + private static RepoSpec maybeAppendAdditionalPatches(RepoSpec repoSpec, ModuleOverride override) { if (!(override instanceof SingleVersionOverride)) { return repoSpec; } @@ -369,6 +348,66 @@ private RepoSpec maybeAppendAdditionalPatches(RepoSpec repoSpec, ModuleOverride .build(); } + @Nullable + private static RepoSpec computeRepoSpec( + InterimModule interimModule, ModuleOverride override, ExtendedEventHandler eventHandler) + throws BazelModuleResolutionFunctionException, InterruptedException { + if (interimModule.getRegistry() == null) { + // This module has a non-registry override. We don't need to store the repo spec in this case. + return null; + } + try { + RepoSpec moduleRepoSpec = + interimModule + .getRegistry() + .getRepoSpec( + interimModule.getKey(), interimModule.getCanonicalRepoName(), eventHandler); + return maybeAppendAdditionalPatches(moduleRepoSpec, override); + } catch (IOException e) { + throw new BazelModuleResolutionFunctionException( + ExternalDepsException.withMessage( + Code.ERROR_ACCESSING_REGISTRY, + "Unable to get module repo spec from registry: %s", + e.getMessage()), + Transience.PERSISTENT); + } + } + + /** + * Builds a {@link Module} from an {@link InterimModule}, discarding unnecessary fields and adding + * extra necessary ones (such as the repo spec). + */ + static Module moduleFromInterimModule( + InterimModule interim, ModuleOverride override, ExtendedEventHandler eventHandler) + throws BazelModuleResolutionFunctionException, InterruptedException { + return Module.builder() + .setName(interim.getName()) + .setVersion(interim.getVersion()) + .setKey(interim.getKey()) + .setRepoName(interim.getRepoName()) + .setExecutionPlatformsToRegister(interim.getExecutionPlatformsToRegister()) + .setToolchainsToRegister(interim.getToolchainsToRegister()) + .setDeps(ImmutableMap.copyOf(Maps.transformValues(interim.getDeps(), DepSpec::toModuleKey))) + .setRepoSpec(computeRepoSpec(interim, override, eventHandler)) + .setExtensionUsages(interim.getExtensionUsages()) + .build(); + } + + private static ImmutableMap computeFinalDepGraph( + ImmutableMap resolvedDepGraph, + ImmutableMap overrides, + ExtendedEventHandler eventHandler) + throws BazelModuleResolutionFunctionException, InterruptedException { + ImmutableMap.Builder finalDepGraph = ImmutableMap.builder(); + for (Map.Entry entry : resolvedDepGraph.entrySet()) { + finalDepGraph.put( + entry.getKey(), + moduleFromInterimModule( + entry.getValue(), overrides.get(entry.getKey().getName()), eventHandler)); + } + return finalDepGraph.buildOrThrow(); + } + static class BazelModuleResolutionFunctionException extends SkyFunctionException { BazelModuleResolutionFunctionException(ExternalDepsException e, Transience transience) { super(e, transience); diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BazelModuleResolutionValue.java b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BazelModuleResolutionValue.java index f2c734647478ed..19c642cfa5d7f7 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BazelModuleResolutionValue.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BazelModuleResolutionValue.java @@ -44,11 +44,11 @@ abstract class BazelModuleResolutionValue implements SkyValue { * overridden by {@code single_version_override} or {@link NonRegistryOverride}, only by {@code * multiple_version_override}. */ - abstract ImmutableMap getUnprunedDepGraph(); + abstract ImmutableMap getUnprunedDepGraph(); static BazelModuleResolutionValue create( ImmutableMap resolvedDepGraph, - ImmutableMap unprunedDepGraph) { + ImmutableMap unprunedDepGraph) { return new AutoValue_BazelModuleResolutionValue(resolvedDepGraph, unprunedDepGraph); } } diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/Discovery.java b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/Discovery.java index 7fb3a67f044d84..d041c3ca0cda97 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/Discovery.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/Discovery.java @@ -16,9 +16,9 @@ package com.google.devtools.build.lib.bazel.bzlmod; import com.google.common.collect.ImmutableMap; +import com.google.devtools.build.lib.bazel.bzlmod.InterimModule.DepSpec; import com.google.devtools.build.lib.bazel.bzlmod.ModuleFileValue.RootModuleFileValue; import com.google.devtools.build.skyframe.SkyFunction.Environment; -import com.google.devtools.build.skyframe.SkyFunctionException; import com.google.devtools.build.skyframe.SkyKey; import com.google.devtools.build.skyframe.SkyframeLookupResult; import java.util.ArrayDeque; @@ -42,23 +42,24 @@ private Discovery() {} * dependency is missing and this function needs a restart). */ @Nullable - public static ImmutableMap run(Environment env, RootModuleFileValue root) - throws SkyFunctionException, InterruptedException { + public static ImmutableMap run( + Environment env, RootModuleFileValue root) throws InterruptedException { String rootModuleName = root.getModule().getName(); ImmutableMap overrides = root.getOverrides(); - Map depGraph = new HashMap<>(); - depGraph.put(ModuleKey.ROOT, rewriteDepKeys(root.getModule(), overrides, rootModuleName)); + Map depGraph = new HashMap<>(); + depGraph.put(ModuleKey.ROOT, rewriteDepSpecs(root.getModule(), overrides, rootModuleName)); Queue unexpanded = new ArrayDeque<>(); unexpanded.add(ModuleKey.ROOT); while (!unexpanded.isEmpty()) { Set unexpandedSkyKeys = new HashSet<>(); while (!unexpanded.isEmpty()) { - Module module = depGraph.get(unexpanded.remove()); - for (ModuleKey depKey : module.getDeps().values()) { - if (depGraph.containsKey(depKey)) { + InterimModule module = depGraph.get(unexpanded.remove()); + for (DepSpec depSpec : module.getDeps().values()) { + if (depGraph.containsKey(depSpec.toModuleKey())) { continue; } - unexpandedSkyKeys.add(ModuleFileValue.key(depKey, overrides.get(depKey.getName()))); + unexpandedSkyKeys.add( + ModuleFileValue.key(depSpec.toModuleKey(), overrides.get(depSpec.getName()))); } } SkyframeLookupResult result = env.getValuesAndExceptions(unexpandedSkyKeys); @@ -70,7 +71,7 @@ public static ImmutableMap run(Environment env, RootModuleFil depGraph.put(depKey, null); } else { depGraph.put( - depKey, rewriteDepKeys(moduleFileValue.getModule(), overrides, rootModuleName)); + depKey, rewriteDepSpecs(moduleFileValue.getModule(), overrides, rootModuleName)); unexpanded.add(depKey); } } @@ -81,16 +82,16 @@ public static ImmutableMap run(Environment env, RootModuleFil return ImmutableMap.copyOf(depGraph); } - private static Module rewriteDepKeys( - Module module, ImmutableMap overrides, String rootModuleName) { - return module.withDepKeysTransformed( - depKey -> { - if (rootModuleName.equals(depKey.getName())) { - return ModuleKey.ROOT; + private static InterimModule rewriteDepSpecs( + InterimModule module, ImmutableMap overrides, String rootModuleName) { + return module.withDepSpecsTransformed( + depSpec -> { + if (rootModuleName.equals(depSpec.getName())) { + return DepSpec.fromModuleKey(ModuleKey.ROOT); } - Version newVersion = depKey.getVersion(); - @Nullable ModuleOverride override = overrides.get(depKey.getName()); + Version newVersion = depSpec.getVersion(); + @Nullable ModuleOverride override = overrides.get(depSpec.getName()); if (override instanceof NonRegistryOverride) { newVersion = Version.EMPTY; } else if (override instanceof SingleVersionOverride) { @@ -100,7 +101,7 @@ private static Module rewriteDepKeys( } } - return ModuleKey.create(depKey.getName(), newVersion); + return DepSpec.create(depSpec.getName(), newVersion, depSpec.getMaxCompatibilityLevel()); }); } } diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/InterimModule.java b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/InterimModule.java new file mode 100644 index 00000000000000..06f5051a7c0c3a --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/InterimModule.java @@ -0,0 +1,213 @@ +// Copyright 2023 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package com.google.devtools.build.lib.bazel.bzlmod; + +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.errorprone.annotations.CanIgnoreReturnValue; +import com.ryanharter.auto.value.gson.GenerateTypeAdapter; +import java.util.Optional; +import java.util.function.UnaryOperator; +import javax.annotation.Nullable; + +/** + * Represents a node in the external dependency graph during module resolution (discovery & + * selection). + * + *

In particular, it represents a specific version of a module; there can be multiple {@link + * InterimModule}s in a dependency graph with the same name but with different versions (such as + * after discovery but before selection, or when there's a multiple_version_override in play). + * + *

Compared to {@link Module}, which is used after module resolution, this class holds some more + * information that's useful only during resolution, such as the {@code max_compatibility_level} for + * each dep, the {@code compatibility_level}, the {@code registry} the module comes from, etc. + */ +@AutoValue +@GenerateTypeAdapter +public abstract class InterimModule extends ModuleBase { + + /** + * The compatibility level of the module, which essentially signifies the "major version" of the + * module in terms of SemVer. + */ + public abstract int getCompatibilityLevel(); + + /** List of bazel compatible versions that would run/fail this module */ + public abstract ImmutableList getBazelCompatibility(); + + /** The specification of a dependency. */ + @AutoValue + public abstract static class DepSpec { + public abstract String getName(); + + public abstract Version getVersion(); + + public abstract int getMaxCompatibilityLevel(); + + public static DepSpec create(String name, Version version, int maxCompatibilityLevel) { + return new AutoValue_InterimModule_DepSpec(name, version, maxCompatibilityLevel); + } + + public static DepSpec fromModuleKey(ModuleKey key) { + return create(key.getName(), key.getVersion(), -1); + } + + public final ModuleKey toModuleKey() { + return ModuleKey.create(getName(), getVersion()); + } + } + + /** + * The resolved direct dependencies of this module, which can be either the original ones, + * overridden by a {@code single_version_override}, by a {@code multiple_version_override}, or by + * a {@link NonRegistryOverride} (the version will be ""). The key type is the repo name of the + * dep. + */ + public abstract ImmutableMap getDeps(); + + /** + * The original direct dependencies of this module as they are declared in their MODULE file. The + * key type is the repo name of the dep. + */ + public abstract ImmutableMap getOriginalDeps(); + + /** + * The registry where this module came from. Must be null iff the module has a {@link + * NonRegistryOverride}. + */ + @Nullable + public abstract Registry getRegistry(); + + /** Returns a {@link Builder} that starts out with the same fields as this object. */ + abstract Builder toBuilder(); + + /** Returns a new, empty {@link Builder}. */ + public static Builder builder() { + return new AutoValue_InterimModule.Builder() + .setName("") + .setVersion(Version.EMPTY) + .setKey(ModuleKey.ROOT) + .setCompatibilityLevel(0); + } + + /** + * Returns a new {@link InterimModule} with all values in {@link #getDeps} transformed using the + * given function. + */ + public InterimModule withDepSpecsTransformed(UnaryOperator transform) { + return toBuilder() + .setDeps(ImmutableMap.copyOf(Maps.transformValues(getDeps(), transform::apply))) + .build(); + } + + /** Builder type for {@link InterimModule}. */ + @AutoValue.Builder + public abstract static class Builder { + /** Optional; defaults to the empty string. */ + public abstract Builder setName(String value); + + /** Optional; defaults to {@link Version#EMPTY}. */ + public abstract Builder setVersion(Version value); + + /** Optional; defaults to {@link ModuleKey#ROOT}. */ + public abstract Builder setKey(ModuleKey value); + + /** Optional; defaults to {@code 0}. */ + public abstract Builder setCompatibilityLevel(int value); + + /** Optional; defaults to {@link #setName}. */ + public abstract Builder setRepoName(String value); + + public abstract Builder setBazelCompatibility(ImmutableList value); + + abstract ImmutableList.Builder bazelCompatibilityBuilder(); + + @CanIgnoreReturnValue + public final Builder addBazelCompatibilityValues(Iterable values) { + bazelCompatibilityBuilder().addAll(values); + return this; + } + + public abstract Builder setExecutionPlatformsToRegister(ImmutableList value); + + abstract ImmutableList.Builder executionPlatformsToRegisterBuilder(); + + @CanIgnoreReturnValue + public final Builder addExecutionPlatformsToRegister(Iterable values) { + executionPlatformsToRegisterBuilder().addAll(values); + return this; + } + + public abstract Builder setToolchainsToRegister(ImmutableList value); + + abstract ImmutableList.Builder toolchainsToRegisterBuilder(); + + @CanIgnoreReturnValue + public final Builder addToolchainsToRegister(Iterable values) { + toolchainsToRegisterBuilder().addAll(values); + return this; + } + + public abstract Builder setOriginalDeps(ImmutableMap value); + + public abstract Builder setDeps(ImmutableMap value); + + abstract ImmutableMap.Builder depsBuilder(); + + @CanIgnoreReturnValue + public Builder addDep(String depRepoName, DepSpec depSpec) { + depsBuilder().put(depRepoName, depSpec); + return this; + } + + abstract ImmutableMap.Builder originalDepsBuilder(); + + @CanIgnoreReturnValue + public Builder addOriginalDep(String depRepoName, DepSpec depSpec) { + originalDepsBuilder().put(depRepoName, depSpec); + return this; + } + + public abstract Builder setRegistry(Registry value); + + public abstract Builder setExtensionUsages(ImmutableList value); + + abstract ImmutableList.Builder extensionUsagesBuilder(); + + @CanIgnoreReturnValue + public Builder addExtensionUsage(ModuleExtensionUsage value) { + extensionUsagesBuilder().add(value); + return this; + } + + abstract ModuleKey getKey(); + + abstract String getName(); + + abstract Optional getRepoName(); + + abstract InterimModule autoBuild(); + + final InterimModule build() { + if (getRepoName().isEmpty()) { + setRepoName(getName()); + } + return autoBuild(); + } + } +} diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/Module.java b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/Module.java index 86fae25df40b45..36166367cb42cc 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/Module.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/Module.java @@ -18,103 +18,32 @@ 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.devtools.build.lib.cmdline.RepositoryMapping; import com.google.devtools.build.lib.cmdline.RepositoryName; import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.ryanharter.auto.value.gson.GenerateTypeAdapter; import java.util.Map; -import java.util.Optional; -import java.util.function.UnaryOperator; import javax.annotation.Nullable; /** * Represents a node in the external dependency graph. * *

In particular, it represents a specific version of a module; there can be multiple {@link - * Module}s in a dependency graph with the same name but with different versions (such as after - * discovery but before selection, or when there's a multiple_version_override in play). + * Module}s in a dependency graph with the same name but with different versions (when there's a + * multiple_version_override in play). + * + *

For the intermediate type used during module resolution, see {@link InterimModule}. */ @AutoValue @GenerateTypeAdapter -public abstract class Module { - - /** - * The name of the module, as specified in this module's MODULE.bazel file. Can be empty if this - * is the root module. - */ - public abstract String getName(); - - /** - * The version of the module, as specified in this module's MODULE.bazel file. Can be empty if - * this is the root module, or if this module comes from a {@link NonRegistryOverride}. - */ - public abstract Version getVersion(); - - /** - * The key of this module in the dependency graph. Note that, although a {@link ModuleKey} is also - * just a (name, version) pair, its semantics differ from {@link #getName} and {@link - * #getVersion}, which are always as specified in the MODULE.bazel file. The {@link ModuleKey} - * returned by this method, however, will have the following special semantics: - * - *

    - *
  • The name of the {@link ModuleKey} is the same as {@link #getName}, unless this is the - * root module, in which case the name of the {@link ModuleKey} must be empty. - *
  • The version of the {@link ModuleKey} is the same as {@link #getVersion}, unless this is - * the root module OR this module has a {@link NonRegistryOverride}, in which case the - * version of the {@link ModuleKey} must be empty. - *
- */ - public abstract ModuleKey getKey(); - - public final RepositoryName getCanonicalRepoName() { - return getKey().getCanonicalRepoName(); - } - - /** - * The compatibility level of the module, which essentially signifies the "major version" of the - * module in terms of SemVer. - */ - public abstract int getCompatibilityLevel(); - - /** - * The name of the repository representing this module, as seen by the module itself. By default, - * the name of the repo is the name of the module. This can be specified to ease migration for - * projects that have been using a repo name for itself that differs from its module name. - */ - public abstract String getRepoName(); - - /** List of bazel compatible versions that would run/fail this module */ - public abstract ImmutableList getBazelCompatibility(); - - /** - * Target patterns identifying execution platforms to register when this module is selected. Note - * that these are what was written in module files verbatim, and don't contain canonical repo - * names. - */ - public abstract ImmutableList getExecutionPlatformsToRegister(); +public abstract class Module extends ModuleBase { /** - * Target patterns identifying toolchains to register when this module is selected. Note that - * these are what was written in module files verbatim, and don't contain canonical repo names. - */ - public abstract ImmutableList getToolchainsToRegister(); - - /** - * The resolved direct dependencies of this module, which can be either the original ones, - * overridden by a {@code single_version_override}, by a {@code multiple_version_override}, or by - * a {@link NonRegistryOverride} (the version will be ""). The key type is the repo name of the - * dep, and the value type is the ModuleKey (name+version) of the dep. + * The resolved direct dependencies of this module. The key type is the repo name of the dep, and + * the value type is the ModuleKey ({@link #getKey()}) of the dep. */ public abstract ImmutableMap getDeps(); - /** - * The original direct dependencies of this module as they are declared in their MODULE file. The - * key type is the repo name of the dep, and the value type is the ModuleKey (name+version) of the - * dep. - */ - public abstract ImmutableMap getOriginalDeps(); - /** * Returns a {@link RepositoryMapping} with only Bazel module repos and no repos from module * extensions. For the full mapping, see {@link BazelDepGraphValue#getFullRepoMapping}. @@ -138,97 +67,33 @@ public final RepositoryMapping getRepoMappingWithBazelDepsOnly() { return RepositoryMapping.create(mapping.buildOrThrow(), getCanonicalRepoName()); } - // TODO(salmasamy) create two modules (One with registry, one with repospec and only necessary - // things for lockfile) - /** - * The registry where this module came from. Must be null iff the module has a {@link - * NonRegistryOverride}. Set to null after running selection and verifying yanked versions. - */ - @Nullable - public abstract Registry getRegistry(); - /** - * The repo spec for this module (information about the attributes of its repository rule) Filled - * after running selection to avoid extra calls to the registry. + * The repo spec for this module (information about the attributes of its repository rule). This + * is only non-null for modules coming from registries (i.e. without non-registry overrides). */ @Nullable public abstract RepoSpec getRepoSpec(); - /** The module extensions used in this module. */ - public abstract ImmutableList getExtensionUsages(); - - /** Returns a {@link Builder} that starts out with the same fields as this object. */ - abstract Builder toBuilder(); - /** Returns a new, empty {@link Builder}. */ public static Builder builder() { - return new AutoValue_Module.Builder() - .setName("") - .setVersion(Version.EMPTY) - .setKey(ModuleKey.ROOT) - .setCompatibilityLevel(0); - } - - /** - * Returns a new {@link Module} with all values in {@link #getDeps} transformed using the given - * function. - */ - public Module withDepKeysTransformed(UnaryOperator transform) { - return toBuilder() - .setDeps(ImmutableMap.copyOf(Maps.transformValues(getDeps(), transform::apply))) - .build(); + return new AutoValue_Module.Builder(); } /** Builder type for {@link Module}. */ @AutoValue.Builder public abstract static class Builder { - /** Optional; defaults to the empty string. */ public abstract Builder setName(String value); - /** Optional; defaults to {@link Version#EMPTY}. */ public abstract Builder setVersion(Version value); - /** Optional; defaults to {@link ModuleKey#ROOT}. */ public abstract Builder setKey(ModuleKey value); - /** Optional; defaults to {@code 0}. */ - public abstract Builder setCompatibilityLevel(int value); - - /** Optional; defaults to {@link #setName}. */ public abstract Builder setRepoName(String value); - public abstract Builder setBazelCompatibility(ImmutableList value); - - abstract ImmutableList.Builder bazelCompatibilityBuilder(); - - @CanIgnoreReturnValue - public final Builder addBazelCompatibilityValues(Iterable values) { - bazelCompatibilityBuilder().addAll(values); - return this; - } - public abstract Builder setExecutionPlatformsToRegister(ImmutableList value); - abstract ImmutableList.Builder executionPlatformsToRegisterBuilder(); - - @CanIgnoreReturnValue - public final Builder addExecutionPlatformsToRegister(Iterable values) { - executionPlatformsToRegisterBuilder().addAll(values); - return this; - } - public abstract Builder setToolchainsToRegister(ImmutableList value); - abstract ImmutableList.Builder toolchainsToRegisterBuilder(); - - @CanIgnoreReturnValue - public final Builder addToolchainsToRegister(Iterable values) { - toolchainsToRegisterBuilder().addAll(values); - return this; - } - - public abstract Builder setOriginalDeps(ImmutableMap value); - public abstract Builder setDeps(ImmutableMap value); abstract ImmutableMap.Builder depsBuilder(); @@ -239,16 +104,6 @@ public Builder addDep(String depRepoName, ModuleKey depKey) { return this; } - abstract ImmutableMap.Builder originalDepsBuilder(); - - @CanIgnoreReturnValue - public Builder addOriginalDep(String depRepoName, ModuleKey depKey) { - originalDepsBuilder().put(depRepoName, depKey); - return this; - } - - public abstract Builder setRegistry(Registry value); - public abstract Builder setRepoSpec(RepoSpec value); public abstract Builder setExtensionUsages(ImmutableList value); @@ -261,19 +116,6 @@ public Builder addExtensionUsage(ModuleExtensionUsage value) { return this; } - abstract ModuleKey getKey(); - - abstract String getName(); - - abstract Optional getRepoName(); - - abstract Module autoBuild(); - - final Module build() { - if (getRepoName().isEmpty()) { - setRepoName(getName()); - } - return autoBuild(); - } + abstract Module build(); } } diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleBase.java b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleBase.java new file mode 100644 index 00000000000000..67e8d37588a5e1 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleBase.java @@ -0,0 +1,78 @@ +// Copyright 2023 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package com.google.devtools.build.lib.bazel.bzlmod; + +import com.google.common.collect.ImmutableList; +import com.google.devtools.build.lib.cmdline.RepositoryName; + +/** Represents a node in the external dependency graph. */ +abstract class ModuleBase { + + /** + * The name of the module, as specified in this module's MODULE.bazel file. Can be empty if this + * is the root module. + */ + public abstract String getName(); + + /** + * The version of the module, as specified in this module's MODULE.bazel file. Can be empty if + * this is the root module, or if this module comes from a {@link NonRegistryOverride}. + */ + public abstract Version getVersion(); + + /** + * The key of this module in the dependency graph. Note that, although a {@link ModuleKey} is also + * just a (name, version) pair, its semantics differ from {@link #getName} and {@link + * #getVersion}, which are always as specified in the MODULE.bazel file. The {@link ModuleKey} + * returned by this method, however, will have the following special semantics: + * + *
    + *
  • The name of the {@link ModuleKey} is the same as {@link #getName}, unless this is the + * root module, in which case the name of the {@link ModuleKey} must be empty. + *
  • The version of the {@link ModuleKey} is the same as {@link #getVersion}, unless this is + * the root module OR this module has a {@link NonRegistryOverride}, in which case the + * version of the {@link ModuleKey} must be empty. + *
+ */ + public abstract ModuleKey getKey(); + + public final RepositoryName getCanonicalRepoName() { + return getKey().getCanonicalRepoName(); + } + + /** + * The name of the repository representing this module, as seen by the module itself. By default, + * the name of the repo is the name of the module. This can be specified to ease migration for + * projects that have been using a repo name for itself that differs from its module name. + */ + public abstract String getRepoName(); + + /** + * Target patterns identifying execution platforms to register when this module is selected. Note + * that these are what was written in module files verbatim, and don't contain canonical repo + * names. + */ + public abstract ImmutableList getExecutionPlatformsToRegister(); + + /** + * Target patterns identifying toolchains to register when this module is selected. Note that + * these are what was written in module files verbatim, and don't contain canonical repo names. + */ + public abstract ImmutableList getToolchainsToRegister(); + + /** The module extensions used in this module. */ + public abstract ImmutableList getExtensionUsages(); +} 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 d9714602c8f71c..18efa92425018a 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 @@ -123,7 +123,7 @@ public SkyValue compute(SkyKey skyKey, Environment env) env); // Perform some sanity checks. - Module module = moduleFileGlobals.buildModule(); + InterimModule module = moduleFileGlobals.buildModule(); if (!module.getName().equals(moduleKey.getName())) { throw errorf( Code.BAD_MODULE, @@ -159,12 +159,12 @@ private SkyValue computeForRootModule(StarlarkSemantics starlarkSemantics, Envir ModuleFileGlobals moduleFileGlobals = execModuleFile( moduleFile, - /*registry=*/ null, + /* registry= */ null, ModuleKey.ROOT, /* ignoreDevDeps= */ Objects.requireNonNull(IGNORE_DEV_DEPS.get(env)), starlarkSemantics, env); - Module module = moduleFileGlobals.buildModule(); + InterimModule module = moduleFileGlobals.buildModule(); ImmutableMap moduleOverrides = moduleFileGlobals.buildOverrides(); Map commandOverrides = MODULE_OVERRIDES.get(env); diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileGlobals.java b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileGlobals.java index a141e2c4c7df49..c2800b99f70759 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileGlobals.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileGlobals.java @@ -26,6 +26,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.devtools.build.docgen.annot.DocumentMethods; +import com.google.devtools.build.lib.bazel.bzlmod.InterimModule.DepSpec; import com.google.devtools.build.lib.bazel.bzlmod.ModuleFileGlobals.ModuleExtensionUsageBuilder.ModuleExtensionProxy; import com.google.devtools.build.lib.bazel.bzlmod.Version.ParseException; import com.google.devtools.build.lib.cmdline.RepositoryName; @@ -64,8 +65,8 @@ public class ModuleFileGlobals { private boolean moduleCalled = false; private boolean hadNonModuleCall = false; private final boolean ignoreDevDeps; - private final Module.Builder module; - private final Map deps = new LinkedHashMap<>(); + private final InterimModule.Builder module; + private final Map deps = new LinkedHashMap<>(); private final List extensionUsageBuilders = new ArrayList<>(); private final Map overrides = new HashMap<>(); private final Map repoNameUsages = new HashMap<>(); @@ -75,7 +76,7 @@ public ModuleFileGlobals( ModuleKey key, @Nullable Registry registry, boolean ignoreDevDeps) { - module = Module.builder().setKey(key).setRegistry(registry); + module = InterimModule.builder().setKey(key).setRegistry(registry); this.ignoreDevDeps = ignoreDevDeps; if (ModuleKey.ROOT.equals(key)) { overrides.putAll(builtinModules); @@ -85,7 +86,7 @@ public ModuleFileGlobals( // The built-in module does not depend on itself. continue; } - deps.put(builtinModule, ModuleKey.create(builtinModule, Version.EMPTY)); + deps.put(builtinModule, DepSpec.create(builtinModule, Version.EMPTY, -1)); try { addRepoNameUsage(builtinModule, "as a built-in dependency", Location.BUILTIN); } catch (EvalException e) { @@ -280,6 +281,16 @@ private static ImmutableList checkAllCompatibilityVersions( named = true, positional = false, defaultValue = "''"), + @Param( + name = "max_compatibility_level", + doc = + "The maximum compatibility_level supported for the module to be added" + + " as a direct dependency. The version of the module implies the minimum" + + " compatibility_level supported, as well as the maximum if this attribute is" + + " not specified.", + named = true, + positional = false, + defaultValue = "-1"), @Param( name = "repo_name", doc = @@ -299,7 +310,12 @@ private static ImmutableList checkAllCompatibilityVersions( }, useStarlarkThread = true) public void bazelDep( - String name, String version, String repoName, boolean devDependency, StarlarkThread thread) + String name, + String version, + StarlarkInt maxCompatibilityLevel, + String repoName, + boolean devDependency, + StarlarkThread thread) throws EvalException { hadNonModuleCall = true; if (repoName.isEmpty()) { @@ -315,7 +331,10 @@ public void bazelDep( RepositoryName.validateUserProvidedRepoName(repoName); if (!(ignoreDevDeps && devDependency)) { - deps.put(repoName, ModuleKey.create(name, parsedVersion)); + deps.put( + repoName, + DepSpec.create( + name, parsedVersion, maxCompatibilityLevel.toInt("max_compatibility_level"))); } addRepoNameUsage(repoName, "by a bazel_dep", thread.getCallerLocation()); @@ -879,7 +898,7 @@ public void localPathOverride(String moduleName, String path) throws EvalExcepti addOverride(moduleName, LocalPathOverride.create(path)); } - public Module buildModule() { + public InterimModule buildModule() { return module .setDeps(ImmutableMap.copyOf(deps)) .setOriginalDeps(ImmutableMap.copyOf(deps)) diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileValue.java b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileValue.java index 48d47e8e87ac02..01628b8df17bc7 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileValue.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileValue.java @@ -38,7 +38,7 @@ public abstract class ModuleFileValue implements SkyValue { * module might not match the one in the requesting {@link SkyKey} in certain circumstances (for * example, for the root module, or when non-registry overrides are in play. */ - public abstract Module getModule(); + public abstract InterimModule getModule(); /** The hash string of Module.bazel (using SHA256) */ public abstract String getModuleFileHash(); @@ -47,7 +47,7 @@ public abstract class ModuleFileValue implements SkyValue { @AutoValue public abstract static class NonRootModuleFileValue extends ModuleFileValue { - public static NonRootModuleFileValue create(Module module, String moduleFileHash) { + public static NonRootModuleFileValue create(InterimModule module, String moduleFileHash) { return new AutoValue_ModuleFileValue_NonRootModuleFileValue(module, moduleFileHash); } } @@ -72,7 +72,7 @@ public abstract static class RootModuleFileValue extends ModuleFileValue { getNonRegistryOverrideCanonicalRepoNameLookup(); public static RootModuleFileValue create( - Module module, + InterimModule module, String moduleFileHash, ImmutableMap overrides, ImmutableMap nonRegistryOverrideCanonicalRepoNameLookup) { diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/Selection.java b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/Selection.java index bcacb7b1928ba9..361f5231fe850c 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/Selection.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/Selection.java @@ -15,6 +15,10 @@ package com.google.devtools.build.lib.bazel.bzlmod; +import static com.google.common.collect.ImmutableList.toImmutableList; +import static com.google.common.collect.ImmutableSortedMap.toImmutableSortedMap; +import static java.util.Comparator.naturalOrder; + import com.google.auto.value.AutoValue; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; @@ -22,14 +26,19 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSortedSet; +import com.google.common.collect.Lists; import com.google.common.collect.Maps; +import com.google.devtools.build.lib.bazel.bzlmod.InterimModule.DepSpec; import com.google.devtools.build.lib.server.FailureDetails.ExternalDeps.Code; import java.util.ArrayDeque; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.Queue; import java.util.Set; +import java.util.function.Function; import javax.annotation.Nullable; /** @@ -62,11 +71,41 @@ * be removed before the end of selection (by becoming unreachable, for example), otherwise * it'll be an error since they're not allowed by the override (these versions are in * selection groups that have no valid target allowed version). + *
  • Things get even more complicated with max_compatibility_level. The difference this + * introduces is that each "DepSpec" could be satisfied by one of multiple choices. (Without + * max_compatibility_level, there is always only one choice.) So what we do is go through all + * the combinations of possible choices for each distinct DepSpec, and for each combination, + * see if the resulting dep graph is valid. As soon as we find a valid combination, we return + * that result. The distinct DepSpecs are sorted by the order they first appear in the dep + * graph if we BFS from the root module. The combinations are attempted in the typical + * cartesian product order (see {@link Lists#cartesianProduct}); the "version choices" of each + * DepSpec are sorted from low to high. * */ final class Selection { private Selection() {} + /** The result of selection. */ + @AutoValue + abstract static class Result { + /** Final dep graph sorted in BFS iteration order, with unused modules removed. */ + abstract ImmutableMap getResolvedDepGraph(); + + /** + * Un-pruned dep graph, with updated dep keys, and additionally containing the unused modules + * which were initially discovered (and their MODULE.bazel files loaded). Does not contain + * modules overridden by {@code single_version_override} or {@link NonRegistryOverride}, only by + * {@code multiple_version_override}. + */ + abstract ImmutableMap getUnprunedDepGraph(); + + static Result create( + ImmutableMap resolvedDepGraph, + ImmutableMap unprunedDepGraph) { + return new AutoValue_Selection_Result(resolvedDepGraph, unprunedDepGraph); + } + } + /** During selection, a version is selected for each distinct "selection group". */ @AutoValue abstract static class SelectionGroup { @@ -102,7 +141,8 @@ static ModuleNameAndCompatibilityLevel create(String moduleName, int compatibili */ private static ImmutableMap> computeAllowedVersionSets( - ImmutableMap overrides, ImmutableMap depGraph) + ImmutableMap overrides, + ImmutableMap depGraph) throws ExternalDepsException { Map> allowedVersionSets = new HashMap<>(); @@ -114,7 +154,8 @@ static ModuleNameAndCompatibilityLevel create(String moduleName, int compatibili } ImmutableList allowedVersions = ((MultipleVersionOverride) override).getVersions(); for (Version allowedVersion : allowedVersions) { - Module allowedVersionModule = depGraph.get(ModuleKey.create(moduleName, allowedVersion)); + InterimModule allowedVersionModule = + depGraph.get(ModuleKey.create(moduleName, allowedVersion)); if (allowedVersionModule == null) { throw ExternalDepsException.withMessage( Code.VERSION_RESOLUTION_ERROR, @@ -143,7 +184,7 @@ static ModuleNameAndCompatibilityLevel create(String moduleName, int compatibili * used to compute its targetAllowedVersion. */ private static SelectionGroup computeSelectionGroup( - Module module, + InterimModule module, ImmutableMap> allowedVersionSets) { ImmutableSortedSet allowedVersionSet = @@ -152,10 +193,11 @@ private static SelectionGroup computeSelectionGroup( module.getName(), module.getCompatibilityLevel())); if (allowedVersionSet == null) { // This means that this module has no multiple-version override. - return SelectionGroup.create(module.getName(), module.getCompatibilityLevel(), Version.EMPTY); + return SelectionGroup.create( + module.getKey().getName(), module.getCompatibilityLevel(), Version.EMPTY); } return SelectionGroup.create( - module.getName(), + module.getKey().getName(), module.getCompatibilityLevel(), // We use the `ceiling` method here to quickly locate the lowest allowed version that's // still no lower than this module's version. @@ -166,10 +208,94 @@ private static SelectionGroup computeSelectionGroup( } /** - * Runs module selection (aka version resolution). Returns a {@link BazelModuleResolutionValue}. + * Computes the possible list of ModuleKeys a single given DepSpec can resolve to. This is + * normally just one ModuleKey, but when max_compatibility_level is involved, multiple choices may + * be possible. + */ + private static ImmutableList computePossibleResolutionResultsForOneDepSpec( + DepSpec depSpec, + ImmutableMap selectionGroups, + Map selectedVersions) { + int minCompatibilityLevel = selectionGroups.get(depSpec.toModuleKey()).getCompatibilityLevel(); + int maxCompatibilityLevel = + depSpec.getMaxCompatibilityLevel() < 0 + ? minCompatibilityLevel + : depSpec.getMaxCompatibilityLevel(); + // First find the selection groups that this DepSpec could use. + return Maps.filterKeys( + selectedVersions, + group -> + group.getModuleName().equals(depSpec.getName()) + && group.getCompatibilityLevel() >= minCompatibilityLevel + && group.getCompatibilityLevel() <= maxCompatibilityLevel + && group.getTargetAllowedVersion().compareTo(depSpec.getVersion()) >= 0) + .entrySet() + .stream() + // Collect into an ImmutableSortedMap so that: + // 1. The final list is sorted by compatibility level, guaranteeing lowest version first; + // 2. Only one ModuleKey is attempted per compatibility level, so that in the case of a + // multiple-version override, we only try the lowest allowed version in that + // compatibility level (note the Comparators::min call). + .collect( + toImmutableSortedMap( + naturalOrder(), + e -> e.getKey().getCompatibilityLevel(), + e -> e.getValue(), + Comparators::min)) + .values() + .stream() + .map(v -> ModuleKey.create(depSpec.getName(), v)) + .collect(toImmutableList()); + } + + /** + * Computes the possible list of ModuleKeys a DepSpec can resolve to, for all distinct DepSpecs in + * the dependency graph. + */ + private static ImmutableMap> computePossibleResolutionResults( + ImmutableMap depGraph, + ImmutableMap selectionGroups, + Map selectedVersions) { + // Important that we use a LinkedHashMap here to ensure reproducibility. + Map> results = new LinkedHashMap<>(); + for (InterimModule module : depGraph.values()) { + for (DepSpec depSpec : module.getDeps().values()) { + results.computeIfAbsent( + depSpec, + ds -> + computePossibleResolutionResultsForOneDepSpec( + ds, selectionGroups, selectedVersions)); + } + } + return ImmutableMap.copyOf(results); + } + + /** + * Given the possible list of ModuleKeys each DepSpec can resolve to, enumerate through all the + * possible resolution strategies. Each strategy assigns each DepSpec to a single ModuleKey out of + * its possible list. */ - public static BazelModuleResolutionValue run( - ImmutableMap depGraph, ImmutableMap overrides) + private static List> enumerateStrategies( + ImmutableMap> possibleResolutionResults) { + Map depSpecToPosition = new HashMap<>(); + int position = 0; + for (DepSpec depSpec : possibleResolutionResults.keySet()) { + depSpecToPosition.put(depSpec, position++); + } + return Lists.transform( + Lists.cartesianProduct(possibleResolutionResults.values().asList()), + (List choices) -> + (DepSpec depSpec) -> choices.get(depSpecToPosition.get(depSpec))); + // TODO(wyv): There are some strategies that we could eliminate earlier. For example, the + // strategy where (foo@1.1, maxCL=3) resolves to foo@2.0 and (foo@1.2, maxCL=3) resolves to + // foo@3.0 is obviously not valid. All foo@? should resolve to the same version (assuming no + // multiple-version override). + } + + /** Runs module selection (aka version resolution). */ + public static Result run( + ImmutableMap depGraph, + ImmutableMap overrides) throws ExternalDepsException { // For any multiple-version overrides, build a mapping from (moduleName, compatibilityLevel) to // the set of allowed versions. @@ -193,42 +319,55 @@ public static BazelModuleResolutionValue run( selectedVersions.merge(selectionGroup, key.getVersion(), Comparators::max); } - // Build a new dep graph where deps with unselected versions are removed. - ImmutableMap.Builder newDepGraphBuilder = new ImmutableMap.Builder<>(); - - // Also keep a version of the full dep graph with updated deps. - ImmutableMap.Builder unprunedDepGraphBuilder = new ImmutableMap.Builder<>(); - for (Module module : depGraph.values()) { - // Rewrite deps to point to the selected version. - ModuleKey key = module.getKey(); - Module updatedModule = - module.withDepKeysTransformed( - depKey -> - ModuleKey.create( - depKey.getName(), selectedVersions.get(selectionGroups.get(depKey)))); - - // Add all updated modules to the un-pruned dep graph. - unprunedDepGraphBuilder.put(key, updatedModule); - - // Remove any dep whose version isn't selected from the resolved graph. - Version selectedVersion = selectedVersions.get(selectionGroups.get(module.getKey())); - if (module.getKey().getVersion().equals(selectedVersion)) { - newDepGraphBuilder.put(key, updatedModule); + // Compute the possible list of ModuleKeys that each DepSpec could resolve to. + ImmutableMap> possibleResolutionResults = + computePossibleResolutionResults(depGraph, selectionGroups, selectedVersions); + for (Map.Entry> e : possibleResolutionResults.entrySet()) { + if (e.getValue().isEmpty()) { + throw ExternalDepsException.withMessage( + Code.VERSION_RESOLUTION_ERROR, + "Unexpected error: %s has no valid resolution result", + e.getKey()); } } - ImmutableMap newDepGraph = newDepGraphBuilder.buildOrThrow(); - ImmutableMap unprunedDepGraph = unprunedDepGraphBuilder.buildOrThrow(); - - // Further, removes unreferenced modules from the graph. We can find out which modules are - // referenced by collecting deps transitively from the root. - // We can also take this opportunity to check that none of the remaining modules conflict with - // each other (e.g. same module name but different compatibility levels, or not satisfying - // multiple_version_override). - ImmutableMap prunedDepGraph = - new DepGraphWalker(newDepGraph, overrides, selectionGroups).walk(); - - // Return the result containing both the pruned and un-pruned dep graphs - return BazelModuleResolutionValue.create(prunedDepGraph, unprunedDepGraph); + + // Each DepSpec may resolve to one or more ModuleKeys. We try out every single possible + // combination; in other words, we enumerate through the cartesian product of the "possible + // resolution result" set for every distinct DepSpec. Each element of this cartesian product is + // essentially a mapping from DepSpecs to ModuleKeys; we can call this mapping a "resolution + // strategy". + // + // Given a resolution strategy, we can walk through the graph from the root module, and see if + // the strategy yields a valid graph (only containing the nodes reachable from the root). If the + // graph is invalid (for example, because there are modules with different compatibility + // levels), we try the next resolution strategy. When all strategies are exhausted, we know + // there is no way to achieve a valid selection result, so we report the failure from the time + // we attempted to walk the graph using the first resolution strategy. + DepGraphWalker depGraphWalker = new DepGraphWalker(depGraph, overrides, selectionGroups); + ExternalDepsException firstFailure = null; + for (Function resolutionStrategy : + enumerateStrategies(possibleResolutionResults)) { + try { + ImmutableMap prunedDepGraph = + depGraphWalker.walk(resolutionStrategy); + // If the call above didn't throw, we have a valid graph. Go ahead and produce a result! + ImmutableMap unprunedDepGraph = + ImmutableMap.copyOf( + Maps.transformValues( + depGraph, + module -> + module.withDepSpecsTransformed( + depSpec -> DepSpec.fromModuleKey(resolutionStrategy.apply(depSpec))))); + return Result.create(prunedDepGraph, unprunedDepGraph); + } catch (ExternalDepsException e) { + if (firstFailure == null) { + firstFailure = e; + } + } + } + // firstFailure cannot be null, since enumerateStrategies(...) cannot be empty, since no + // element of possibleResolutionResults is empty. + throw firstFailure; } /** @@ -237,27 +376,27 @@ public static BazelModuleResolutionValue run( */ static class DepGraphWalker { private static final Joiner JOINER = Joiner.on(", "); - private final ImmutableMap oldDepGraph; + private final ImmutableMap oldDepGraph; private final ImmutableMap overrides; private final ImmutableMap selectionGroups; - private final HashMap moduleByName; DepGraphWalker( - ImmutableMap oldDepGraph, + ImmutableMap oldDepGraph, ImmutableMap overrides, ImmutableMap selectionGroups) { this.oldDepGraph = oldDepGraph; this.overrides = overrides; this.selectionGroups = selectionGroups; - this.moduleByName = new HashMap<>(); } /** * Walks the old dep graph and builds a new dep graph containing only deps reachable from the * root module. The returned map has a guaranteed breadth-first iteration order. */ - ImmutableMap walk() throws ExternalDepsException { - ImmutableMap.Builder newDepGraph = ImmutableMap.builder(); + ImmutableMap walk(Function resolutionStrategy) + throws ExternalDepsException { + HashMap moduleByName = new HashMap<>(); + ImmutableMap.Builder newDepGraph = ImmutableMap.builder(); Set known = new HashSet<>(); Queue toVisit = new ArrayDeque<>(); toVisit.add(ModuleKeyAndDependent.create(ModuleKey.ROOT, null)); @@ -265,12 +404,16 @@ ImmutableMap walk() throws ExternalDepsException { while (!toVisit.isEmpty()) { ModuleKeyAndDependent moduleKeyAndDependent = toVisit.remove(); ModuleKey key = moduleKeyAndDependent.getModuleKey(); - Module module = oldDepGraph.get(key); - visit(key, module, moduleKeyAndDependent.getDependent()); - - for (ModuleKey depKey : module.getDeps().values()) { - if (known.add(depKey)) { - toVisit.add(ModuleKeyAndDependent.create(depKey, key)); + InterimModule module = + oldDepGraph + .get(key) + .withDepSpecsTransformed( + depSpec -> DepSpec.fromModuleKey(resolutionStrategy.apply(depSpec))); + visit(key, module, moduleKeyAndDependent.getDependent(), moduleByName); + + for (DepSpec depSpec : module.getDeps().values()) { + if (known.add(depSpec.toModuleKey())) { + toVisit.add(ModuleKeyAndDependent.create(depSpec.toModuleKey(), key)); } } newDepGraph.put(key, module); @@ -278,7 +421,11 @@ ImmutableMap walk() throws ExternalDepsException { return newDepGraph.buildOrThrow(); } - void visit(ModuleKey key, Module module, @Nullable ModuleKey from) + void visit( + ModuleKey key, + InterimModule module, + @Nullable ModuleKey from, + HashMap moduleByName) throws ExternalDepsException { ModuleOverride override = overrides.get(key.getName()); if (override instanceof MultipleVersionOverride) { @@ -321,10 +468,10 @@ void visit(ModuleKey key, Module module, @Nullable ModuleKey from) // Make sure that we don't have `module` depending on the same dependency version twice. HashMap depKeyToRepoName = new HashMap<>(); - for (Map.Entry depEntry : module.getDeps().entrySet()) { + for (Map.Entry depEntry : module.getDeps().entrySet()) { String repoName = depEntry.getKey(); - ModuleKey depKey = depEntry.getValue(); - String previousRepoName = depKeyToRepoName.put(depKey, repoName); + DepSpec depSpec = depEntry.getValue(); + String previousRepoName = depKeyToRepoName.put(depSpec.toModuleKey(), repoName); if (previousRepoName != null) { throw ExternalDepsException.withMessage( Code.VERSION_RESOLUTION_ERROR, @@ -332,7 +479,7 @@ void visit(ModuleKey key, Module module, @Nullable ModuleKey from) + " multiple_version_override if you want to depend on multiple versions of" + " %s simultaneously", key, - depKey, + depSpec.toModuleKey(), repoName, previousRepoName, key.getName()); diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/BzlmodRepoRuleFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/BzlmodRepoRuleFunction.java index e4942e3efadb7d..a7c5c48e0357b4 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/BzlmodRepoRuleFunction.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/BzlmodRepoRuleFunction.java @@ -14,7 +14,6 @@ package com.google.devtools.build.lib.skyframe; -import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; @@ -163,9 +162,7 @@ private Optional checkRepoFromBazelModules( if (moduleKey == null) { return Optional.empty(); } - com.google.devtools.build.lib.bazel.bzlmod.Module module = - bazelDepGraphValue.getDepGraph().get(moduleKey); - return Optional.of(checkNotNull(module.getRepoSpec())); + return Optional.of(bazelDepGraphValue.getDepGraph().get(moduleKey).getRepoSpec()); } @Nullable diff --git a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BazelDepGraphFunctionTest.java b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BazelDepGraphFunctionTest.java index b1979e9a6515ef..3e10c004b73f03 100644 --- a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BazelDepGraphFunctionTest.java +++ b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BazelDepGraphFunctionTest.java @@ -17,6 +17,7 @@ import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.truth.Truth.assertThat; +import static com.google.devtools.build.lib.bazel.bzlmod.BzlmodTestUtil.buildModule; import static com.google.devtools.build.lib.bazel.bzlmod.BzlmodTestUtil.createModuleKey; import static com.google.devtools.build.lib.bazel.bzlmod.BzlmodTestUtil.createRepositoryMapping; import static org.junit.Assert.fail; @@ -162,9 +163,7 @@ public void createValue_basic() throws Exception { ImmutableMap.builder() .put( ModuleKey.ROOT, - Module.builder() - .setName("my_root") - .setVersion(Version.parse("1.0")) + buildModule("my_root", "1.0") .setKey(ModuleKey.ROOT) .addDep("my_dep_1", createModuleKey("dep", "1.0")) .addDep("my_dep_2", createModuleKey("dep", "2.0")) @@ -172,33 +171,14 @@ public void createValue_basic() throws Exception { .build()) .put( createModuleKey("dep", "1.0"), - Module.builder() - .setName("dep") - .setVersion(Version.parse("1.0")) - .setKey(createModuleKey("dep", "1.0")) + buildModule("dep", "1.0") .addDep("rules_java", createModuleKey("rules_java", "")) .build()) - .put( - createModuleKey("dep", "2.0"), - Module.builder() - .setName("dep") - .setVersion(Version.parse("2.0")) - .setKey(createModuleKey("dep", "2.0")) - .build()) - .put( - createModuleKey("rules_cc", "1.0"), - Module.builder() - .setName("rules_cc") - .setVersion(Version.parse("1.0")) - .setKey(createModuleKey("rules_cc", "1.0")) - .build()) + .put(createModuleKey("dep", "2.0"), buildModule("dep", "2.0").build()) + .put(createModuleKey("rules_cc", "1.0"), buildModule("rules_cc", "1.0").build()) .put( createModuleKey("rules_java", ""), - Module.builder() - .setName("rules_java") - .setVersion(Version.parse("1.0")) - .setKey(createModuleKey("rules_java", "")) - .build()) + buildModule("rules_java", "1.0").setKey(createModuleKey("rules_java", "")).build()) .buildOrThrow(); resolutionFunctionMock.setDepGraph(depGraph); @@ -248,9 +228,7 @@ public void createValue_moduleExtensions() throws Exception { "module(name='my_root', version='1.0')"); Module root = - Module.builder() - .setName("root") - .setVersion(Version.parse("1.0")) + buildModule("root", "1.0") .setKey(ModuleKey.ROOT) .addDep("rje", createModuleKey("rules_jvm_external", "1.0")) .addDep("rpy", createModuleKey("rules_python", "2.0")) @@ -261,9 +239,7 @@ public void createValue_moduleExtensions() throws Exception { .build(); ModuleKey depKey = createModuleKey("dep", "2.0"); Module dep = - Module.builder() - .setName("dep") - .setVersion(Version.parse("2.0")) + buildModule("dep", "2.0") .setKey(depKey) .addDep("rules_python", createModuleKey("rules_python", "2.0")) .addExtensionUsage( @@ -352,7 +328,7 @@ public void useExtensionBadLabelFails() throws Exception { "module(name='module', version='1.0')"); Module root = - Module.builder() + buildModule("module", "1.0") .addExtensionUsage(createModuleExtensionUsage("@foo//:defs.bzl", "bar")) .build(); ImmutableMap depGraph = ImmutableMap.of(ModuleKey.ROOT, root); diff --git a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BazelLockFileFunctionTest.java b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BazelLockFileFunctionTest.java index 9a20ae0f24a551..e10d52d15924fb 100644 --- a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BazelLockFileFunctionTest.java +++ b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BazelLockFileFunctionTest.java @@ -67,7 +67,6 @@ import com.google.devtools.build.skyframe.SkyFunctionName; import com.google.devtools.build.skyframe.SkyKey; import com.google.devtools.build.skyframe.SkyValue; -import java.io.IOException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import javax.annotation.Nullable; @@ -221,9 +220,10 @@ public void simpleModule() throws Exception { RootModuleFileValue rootValue = rootResult.get(ModuleFileValue.KEY_FOR_ROOT_MODULE); ImmutableMap depGraph = - ImmutableMap.builder() - .put(ModuleKey.ROOT, rootValue.getModule()) - .buildOrThrow(); + ImmutableMap.of( + ModuleKey.ROOT, + BazelModuleResolutionFunction.moduleFromInterimModule( + rootValue.getModule(), null, null)); UpdateLockFileKey key = UpdateLockFileKey.create("moduleHash", depGraph, rootValue.getOverrides()); @@ -259,9 +259,10 @@ public void moduleWithFlags() throws Exception { RootModuleFileValue rootValue = rootResult.get(ModuleFileValue.KEY_FOR_ROOT_MODULE); ImmutableMap depGraph = - ImmutableMap.builder() - .put(ModuleKey.ROOT, rootValue.getModule()) - .buildOrThrow(); + ImmutableMap.of( + ModuleKey.ROOT, + BazelModuleResolutionFunction.moduleFromInterimModule( + rootValue.getModule(), null, null)); ImmutableList yankedVersions = ImmutableList.of("2.4", "2.3"); LocalPathOverride override = LocalPathOverride.create("override_path"); @@ -300,7 +301,7 @@ public void moduleWithFlags() throws Exception { } @Test - public void moduleWithLocalOverrides() throws IOException, InterruptedException { + public void moduleWithLocalOverrides() throws Exception { scratch.file( rootDirectory.getRelative("MODULE.bazel").getPathString(), "module(name='root',version='0.1')", @@ -319,9 +320,10 @@ public void moduleWithLocalOverrides() throws IOException, InterruptedException RootModuleFileValue rootValue = rootResult.get(ModuleFileValue.KEY_FOR_ROOT_MODULE); ImmutableMap depGraph = - ImmutableMap.builder() - .put(ModuleKey.ROOT, rootValue.getModule()) - .buildOrThrow(); + ImmutableMap.of( + ModuleKey.ROOT, + BazelModuleResolutionFunction.moduleFromInterimModule( + rootValue.getModule(), null, null)); UpdateLockFileKey key = UpdateLockFileKey.create("moduleHash", depGraph, rootValue.getOverrides()); @@ -363,9 +365,10 @@ public void fullModule() throws Exception { RootModuleFileValue rootValue = rootResult.get(ModuleFileValue.KEY_FOR_ROOT_MODULE); ImmutableMap depGraph = - ImmutableMap.builder() - .put(ModuleKey.ROOT, rootValue.getModule()) - .buildOrThrow(); + ImmutableMap.of( + ModuleKey.ROOT, + BazelModuleResolutionFunction.moduleFromInterimModule( + rootValue.getModule(), null, null)); UpdateLockFileKey key = UpdateLockFileKey.create("moduleHash", depGraph, rootValue.getOverrides()); diff --git a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BazelModuleInspectorFunctionTest.java b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BazelModuleInspectorFunctionTest.java index ce2027c7b6c27c..1fb7765c802bbc 100644 --- a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BazelModuleInspectorFunctionTest.java +++ b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BazelModuleInspectorFunctionTest.java @@ -24,7 +24,7 @@ import com.google.common.collect.ImmutableSet; import com.google.devtools.build.lib.bazel.bzlmod.BazelModuleInspectorValue.AugmentedModule; import com.google.devtools.build.lib.bazel.bzlmod.BazelModuleInspectorValue.AugmentedModule.ResolutionReason; -import com.google.devtools.build.lib.bazel.bzlmod.BzlmodTestUtil.ModuleBuilder; +import com.google.devtools.build.lib.bazel.bzlmod.BzlmodTestUtil.InterimModuleBuilder; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -35,25 +35,25 @@ public class BazelModuleInspectorFunctionTest { @Test public void testDiamond_simple() throws Exception { - ImmutableMap unprunedDepGraph = - ImmutableMap.builder() + ImmutableMap unprunedDepGraph = + ImmutableMap.builder() .put( - ModuleBuilder.create("aaa", Version.EMPTY) + InterimModuleBuilder.create("aaa", Version.EMPTY) .setKey(ModuleKey.ROOT) .addDep("bbb_from_aaa", createModuleKey("bbb", "1.0")) .addDep("ccc_from_aaa", createModuleKey("ccc", "2.0")) .buildEntry()) .put( - ModuleBuilder.create("bbb", "1.0") + InterimModuleBuilder.create("bbb", "1.0") .addDep("ddd_from_bbb", createModuleKey("ddd", "2.0")) .addOriginalDep("ddd_from_bbb", createModuleKey("ddd", "1.0")) .buildEntry()) .put( - ModuleBuilder.create("ccc", "2.0") + InterimModuleBuilder.create("ccc", "2.0") .addDep("ddd_from_ccc", createModuleKey("ddd", "2.0")) .buildEntry()) - .put(ModuleBuilder.create("ddd", "1.0", 1).buildEntry()) - .put(ModuleBuilder.create("ddd", "2.0", 1).buildEntry()) + .put(InterimModuleBuilder.create("ddd", "1.0", 1).buildEntry()) + .put(InterimModuleBuilder.create("ddd", "2.0", 1).buildEntry()) .buildOrThrow(); ImmutableSet usedModules = @@ -91,29 +91,29 @@ public void testDiamond_simple() throws Exception { @Test public void testDiamond_withFurtherRemoval() throws Exception { - ImmutableMap unprunedDepGraph = - ImmutableMap.builder() + ImmutableMap unprunedDepGraph = + ImmutableMap.builder() .put( - ModuleBuilder.create("aaa", Version.EMPTY) + InterimModuleBuilder.create("aaa", Version.EMPTY) .setKey(ModuleKey.ROOT) .addDep("bbb", createModuleKey("bbb", "1.0")) .addDep("ccc", createModuleKey("ccc", "2.0")) .buildEntry()) .put( - ModuleBuilder.create("bbb", "1.0") + InterimModuleBuilder.create("bbb", "1.0") .addDep("ddd", createModuleKey("ddd", "2.0")) .addOriginalDep("ddd", createModuleKey("ddd", "1.0")) .buildEntry()) .put( - ModuleBuilder.create("ccc", "2.0") + InterimModuleBuilder.create("ccc", "2.0") .addDep("ddd", createModuleKey("ddd", "2.0")) .buildEntry()) - .put(ModuleBuilder.create("ddd", "2.0").buildEntry()) + .put(InterimModuleBuilder.create("ddd", "2.0").buildEntry()) .put( - ModuleBuilder.create("ddd", "1.0") + InterimModuleBuilder.create("ddd", "1.0") .addDep("eee", createModuleKey("eee", "1.0")) .buildEntry()) - .put(ModuleBuilder.create("eee", "1.0").buildEntry()) + .put(InterimModuleBuilder.create("eee", "1.0").buildEntry()) .buildOrThrow(); ImmutableSet usedModules = @@ -154,27 +154,27 @@ public void testDiamond_withFurtherRemoval() throws Exception { @Test public void testCircularDependencyDueToSelection() throws Exception { - ImmutableMap unprunedDepGraph = - ImmutableMap.builder() + ImmutableMap unprunedDepGraph = + ImmutableMap.builder() .put( - ModuleBuilder.create("aaa", Version.EMPTY) + InterimModuleBuilder.create("aaa", Version.EMPTY) .setKey(ModuleKey.ROOT) .addDep("bbb", createModuleKey("bbb", "1.0")) .buildEntry()) .put( - ModuleBuilder.create("bbb", "1.0") + InterimModuleBuilder.create("bbb", "1.0") .addDep("ccc", createModuleKey("ccc", "2.0")) .buildEntry()) .put( - ModuleBuilder.create("ccc", "2.0") + InterimModuleBuilder.create("ccc", "2.0") .addDep("bbb", createModuleKey("bbb", "1.0")) .addOriginalDep("bbb", createModuleKey("bbb", "1.0-pre")) .buildEntry()) .put( - ModuleBuilder.create("bbb", "1.0-pre") + InterimModuleBuilder.create("bbb", "1.0-pre") .addDep("ddd", createModuleKey("ddd", "1.0")) .buildEntry()) - .put(ModuleBuilder.create("ddd", "1.0").buildEntry()) + .put(InterimModuleBuilder.create("ddd", "1.0").buildEntry()) .buildOrThrow(); ImmutableSet usedModules = @@ -210,23 +210,23 @@ public void testSingleVersionOverride_withRemoval() throws Exception { // single_version_override (ccc, 2.0) // aaa -> bbb 1.0 -> ccc 1.0 -> ddd 1.0 // ccc 2.0 -> ddd 2.0 - ImmutableMap unprunedDepGraph = - ImmutableMap.builder() + ImmutableMap unprunedDepGraph = + ImmutableMap.builder() .put( - ModuleBuilder.create("aaa", Version.EMPTY) + InterimModuleBuilder.create("aaa", Version.EMPTY) .setKey(ModuleKey.ROOT) .addDep("bbb", createModuleKey("bbb", "1.0")) .buildEntry()) .put( - ModuleBuilder.create("bbb", "1.0") + InterimModuleBuilder.create("bbb", "1.0") .addDep("ccc", createModuleKey("ccc", "2.0")) .addOriginalDep("ccc", createModuleKey("ccc", "1.0")) .buildEntry()) .put( - ModuleBuilder.create("ccc", "2.0") + InterimModuleBuilder.create("ccc", "2.0") .addDep("ddd", createModuleKey("ddd", "2.0")) .buildEntry()) - .put(ModuleBuilder.create("ddd", "2.0").buildEntry()) + .put(InterimModuleBuilder.create("ddd", "2.0").buildEntry()) .buildOrThrow(); ImmutableMap overrides = @@ -271,20 +271,20 @@ public void testNonRegistryOverride_withRemoval() throws Exception { // archive_override "file://users/user/bbb.zip" // aaa -> bbb 1.0 -> ccc 1.0 (not loaded) // (local) bbb 1.0-hotfix -> ccc 1.1 - ImmutableMap unprunedDepGraph = - ImmutableMap.builder() + ImmutableMap unprunedDepGraph = + ImmutableMap.builder() .put( - ModuleBuilder.create("aaa", Version.EMPTY) + InterimModuleBuilder.create("aaa", Version.EMPTY) .setKey(ModuleKey.ROOT) .addDep("bbb", createModuleKey("bbb", "")) .addOriginalDep("bbb", createModuleKey("bbb", "1.0")) .buildEntry()) .put( - ModuleBuilder.create("bbb", "1.0") + InterimModuleBuilder.create("bbb", "1.0") .setKey(createModuleKey("bbb", "")) .addDep("ccc", createModuleKey("ccc", "1.1")) .buildEntry()) - .put(ModuleBuilder.create("ccc", "1.1").buildEntry()) + .put(InterimModuleBuilder.create("ccc", "1.1").buildEntry()) .buildOrThrow(); ImmutableMap overrides = @@ -328,27 +328,27 @@ public void testMultipleVersionOverride_simpleSnapToHigher() throws Exception { // \-> ccc 2.0 // multiple_version_override ccc: [1.5, 2.0] // multiple_version_override bbb: [1.0, 2.0] - ImmutableMap unprunedDepGraph = - ImmutableMap.builder() + ImmutableMap unprunedDepGraph = + ImmutableMap.builder() .put( - ModuleBuilder.create("aaa", Version.EMPTY) + InterimModuleBuilder.create("aaa", Version.EMPTY) .setKey(ModuleKey.ROOT) .addDep("bbb1", createModuleKey("bbb", "1.0")) .addDep("bbb2", createModuleKey("bbb", "2.0")) .addDep("ccc", createModuleKey("ccc", "2.0")) .buildEntry()) .put( - ModuleBuilder.create("bbb", "1.0") + InterimModuleBuilder.create("bbb", "1.0") .addDep("ccc", createModuleKey("ccc", "1.5")) .addOriginalDep("ccc", createModuleKey("ccc", "1.0")) .buildEntry()) .put( - ModuleBuilder.create("bbb", "2.0") + InterimModuleBuilder.create("bbb", "2.0") .addDep("ccc", createModuleKey("ccc", "1.5")) .buildEntry()) - .put(ModuleBuilder.create("ccc", "1.0").buildEntry()) - .put(ModuleBuilder.create("ccc", "1.5").buildEntry()) - .put(ModuleBuilder.create("ccc", "2.0").buildEntry()) + .put(InterimModuleBuilder.create("ccc", "1.0").buildEntry()) + .put(InterimModuleBuilder.create("ccc", "1.5").buildEntry()) + .put(InterimModuleBuilder.create("ccc", "2.0").buildEntry()) .buildOrThrow(); ImmutableMap overrides = @@ -412,10 +412,10 @@ public void testMultipleVersionOverride_badDepsUnreferenced() throws Exception { // \ \-> bbb4@1.1 // \-> bbb4@1.1 // ccc@1.5 and ccc@3.0, the versions violating the allowlist, are gone. - ImmutableMap unprunedDepGraph = - ImmutableMap.builder() + ImmutableMap unprunedDepGraph = + ImmutableMap.builder() .put( - ModuleBuilder.create("aaa", Version.EMPTY) + InterimModuleBuilder.create("aaa", Version.EMPTY) .setKey(ModuleKey.ROOT) .addDep("bbb1", createModuleKey("bbb1", "1.0")) .addDep("bbb2", createModuleKey("bbb2", "1.1")) @@ -425,29 +425,29 @@ public void testMultipleVersionOverride_badDepsUnreferenced() throws Exception { .addOriginalDep("bbb4", createModuleKey("bbb4", "1.0")) .buildEntry()) .put( - ModuleBuilder.create("bbb1", "1.0") + InterimModuleBuilder.create("bbb1", "1.0") .addDep("ccc", createModuleKey("ccc", "1.0")) .addDep("bbb2", createModuleKey("bbb2", "1.1")) .buildEntry()) .put( - ModuleBuilder.create("bbb2", "1.0") + InterimModuleBuilder.create("bbb2", "1.0") .addDep("ccc", createModuleKey("ccc", "1.5")) .buildEntry()) - .put(ModuleBuilder.create("bbb2", "1.1").buildEntry()) + .put(InterimModuleBuilder.create("bbb2", "1.1").buildEntry()) .put( - ModuleBuilder.create("bbb3", "1.0") + InterimModuleBuilder.create("bbb3", "1.0") .addDep("ccc", createModuleKey("ccc", "2.0")) .addDep("bbb4", createModuleKey("bbb4", "1.1")) .buildEntry()) .put( - ModuleBuilder.create("bbb4", "1.0") + InterimModuleBuilder.create("bbb4", "1.0") .addDep("ccc", createModuleKey("ccc", "3.0")) .buildEntry()) - .put(ModuleBuilder.create("bbb4", "1.1").buildEntry()) - .put(ModuleBuilder.create("ccc", "1.0", 1).buildEntry()) - .put(ModuleBuilder.create("ccc", "1.5", 1).buildEntry()) - .put(ModuleBuilder.create("ccc", "2.0", 2).buildEntry()) - .put(ModuleBuilder.create("ccc", "3.0", 3).buildEntry()) + .put(InterimModuleBuilder.create("bbb4", "1.1").buildEntry()) + .put(InterimModuleBuilder.create("ccc", "1.0", 1).buildEntry()) + .put(InterimModuleBuilder.create("ccc", "1.5", 1).buildEntry()) + .put(InterimModuleBuilder.create("ccc", "2.0", 2).buildEntry()) + .put(InterimModuleBuilder.create("ccc", "3.0", 3).buildEntry()) .buildOrThrow(); ImmutableMap overrides = diff --git a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BzlmodTestUtil.java b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BzlmodTestUtil.java index 484e40e0dce23e..fcd4913153e4d9 100644 --- a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BzlmodTestUtil.java +++ b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BzlmodTestUtil.java @@ -19,6 +19,7 @@ import com.google.common.collect.ImmutableMap; import com.google.devtools.build.lib.bazel.bzlmod.BazelModuleInspectorValue.AugmentedModule; import com.google.devtools.build.lib.bazel.bzlmod.BazelModuleInspectorValue.AugmentedModule.ResolutionReason; +import com.google.devtools.build.lib.bazel.bzlmod.InterimModule.DepSpec; import com.google.devtools.build.lib.bazel.bzlmod.Version.ParseException; import com.google.devtools.build.lib.cmdline.RepositoryMapping; import com.google.devtools.build.lib.cmdline.RepositoryName; @@ -43,21 +44,41 @@ public static ModuleKey createModuleKey(String name, String version) { } } + public static DepSpec createDepSpec(String name, String version, int maxCompatibilityLevel) { + try { + return DepSpec.create(name, Version.parse(version), maxCompatibilityLevel); + } catch (Version.ParseException e) { + throw new IllegalArgumentException(e); + } + } + + public static Module.Builder buildModule(String name, String version) throws Exception { + return Module.builder() + .setName(name) + .setVersion(Version.parse(version)) + .setRepoName(name) + .setKey(createModuleKey(name, version)) + .setExtensionUsages(ImmutableList.of()) + .setExecutionPlatformsToRegister(ImmutableList.of()) + .setToolchainsToRegister(ImmutableList.of()); + } + /** Builder class to create a {@code Entry} entry faster inside UnitTests */ - static final class ModuleBuilder { - Module.Builder builder; + static final class InterimModuleBuilder { + InterimModule.Builder builder; ModuleKey key; - ImmutableMap.Builder deps = new ImmutableMap.Builder<>(); - ImmutableMap.Builder originalDeps = new ImmutableMap.Builder<>(); + ImmutableMap.Builder deps = new ImmutableMap.Builder<>(); + ImmutableMap.Builder originalDeps = new ImmutableMap.Builder<>(); - private ModuleBuilder() {} + private InterimModuleBuilder() {} - public static ModuleBuilder create(String name, Version version, int compatibilityLevel) { - ModuleBuilder moduleBuilder = new ModuleBuilder(); + public static InterimModuleBuilder create( + String name, Version version, int compatibilityLevel) { + InterimModuleBuilder moduleBuilder = new InterimModuleBuilder(); ModuleKey key = ModuleKey.create(name, version); moduleBuilder.key = key; moduleBuilder.builder = - Module.builder() + InterimModule.builder() .setName(name) .setVersion(version) .setKey(key) @@ -65,84 +86,96 @@ public static ModuleBuilder create(String name, Version version, int compatibili return moduleBuilder; } - public static ModuleBuilder create(String name, String version, int compatibilityLevel) + public static InterimModuleBuilder create(String name, String version, int compatibilityLevel) throws ParseException { return create(name, Version.parse(version), compatibilityLevel); } - public static ModuleBuilder create(String name, String version) throws ParseException { + public static InterimModuleBuilder create(String name, String version) throws ParseException { return create(name, Version.parse(version), 0); } - public static ModuleBuilder create(String name, Version version) throws ParseException { + public static InterimModuleBuilder create(String name, Version version) throws ParseException { return create(name, version, 0); } @CanIgnoreReturnValue - public ModuleBuilder addDep(String depRepoName, ModuleKey key) { - deps.put(depRepoName, key); + public InterimModuleBuilder addDep(String depRepoName, ModuleKey key) { + deps.put(depRepoName, DepSpec.fromModuleKey(key)); + return this; + } + + @CanIgnoreReturnValue + public InterimModuleBuilder addDep(String depRepoName, DepSpec depSpec) { + deps.put(depRepoName, depSpec); + return this; + } + + @CanIgnoreReturnValue + public InterimModuleBuilder addOriginalDep(String depRepoName, ModuleKey key) { + originalDeps.put(depRepoName, DepSpec.fromModuleKey(key)); return this; } @CanIgnoreReturnValue - public ModuleBuilder addOriginalDep(String depRepoName, ModuleKey key) { - originalDeps.put(depRepoName, key); + public InterimModuleBuilder addOriginalDep(String depRepoName, DepSpec depSpec) { + originalDeps.put(depRepoName, depSpec); return this; } @CanIgnoreReturnValue - public ModuleBuilder setKey(ModuleKey value) { + public InterimModuleBuilder setKey(ModuleKey value) { this.key = value; this.builder.setKey(value); return this; } @CanIgnoreReturnValue - public ModuleBuilder setRepoName(String value) { + public InterimModuleBuilder setRepoName(String value) { this.builder.setRepoName(value); return this; } @CanIgnoreReturnValue - public ModuleBuilder setRegistry(FakeRegistry value) { + public InterimModuleBuilder setRegistry(FakeRegistry value) { this.builder.setRegistry(value); return this; } @CanIgnoreReturnValue - public ModuleBuilder addExecutionPlatformsToRegister(ImmutableList value) { + public InterimModuleBuilder addExecutionPlatformsToRegister(ImmutableList value) { this.builder.addExecutionPlatformsToRegister(value); return this; } @CanIgnoreReturnValue - public ModuleBuilder addToolchainsToRegister(ImmutableList value) { + public InterimModuleBuilder addToolchainsToRegister(ImmutableList value) { this.builder.addToolchainsToRegister(value); return this; } @CanIgnoreReturnValue - public ModuleBuilder addExtensionUsage(ModuleExtensionUsage value) { + public InterimModuleBuilder addExtensionUsage(ModuleExtensionUsage value) { this.builder.addExtensionUsage(value); return this; } - public Map.Entry buildEntry() { - Module module = this.build(); + public Map.Entry buildEntry() { + InterimModule module = this.build(); return new SimpleEntry<>(this.key, module); } - public Module build() { - ImmutableMap builtDeps = this.deps.buildOrThrow(); + public InterimModule build() { + ImmutableMap builtDeps = this.deps.buildOrThrow(); /* Copy dep entries that have not been changed to original deps */ - ImmutableMap initOriginalDeps = this.originalDeps.buildOrThrow(); - for (Entry e : builtDeps.entrySet()) { + ImmutableMap initOriginalDeps = this.originalDeps.buildOrThrow(); + for (Entry e : builtDeps.entrySet()) { if (!initOriginalDeps.containsKey(e.getKey())) { originalDeps.put(e); } } - ImmutableMap builtOriginalDeps = this.originalDeps.buildOrThrow(); + ImmutableMap builtOriginalDeps = this.originalDeps.buildOrThrow(); return this.builder.setDeps(builtDeps).setOriginalDeps(builtOriginalDeps).build(); } diff --git a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/DiscoveryTest.java b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/DiscoveryTest.java index 94fa9f1dae216f..de1f2794c2c6e5 100644 --- a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/DiscoveryTest.java +++ b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/DiscoveryTest.java @@ -29,7 +29,7 @@ import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider; import com.google.devtools.build.lib.analysis.ServerDirectories; import com.google.devtools.build.lib.analysis.util.AnalysisMock; -import com.google.devtools.build.lib.bazel.bzlmod.BzlmodTestUtil.ModuleBuilder; +import com.google.devtools.build.lib.bazel.bzlmod.BzlmodTestUtil.InterimModuleBuilder; import com.google.devtools.build.lib.bazel.bzlmod.ModuleFileValue.RootModuleFileValue; import com.google.devtools.build.lib.bazel.repository.starlark.StarlarkRepositoryModule; import com.google.devtools.build.lib.clock.BlazeClock; @@ -91,11 +91,11 @@ abstract static class DiscoveryValue implements SkyValue { static final SkyFunctionName FUNCTION_NAME = SkyFunctionName.createHermetic("test_discovery"); static final SkyKey KEY = () -> FUNCTION_NAME; - static DiscoveryValue create(ImmutableMap depGraph) { + static DiscoveryValue create(ImmutableMap depGraph) { return new AutoValue_DiscoveryTest_DiscoveryValue(depGraph); } - abstract ImmutableMap getDepGraph(); + abstract ImmutableMap getDepGraph(); } static class DiscoveryFunction implements SkyFunction { @@ -107,7 +107,7 @@ public SkyValue compute(SkyKey skyKey, Environment env) if (root == null) { return null; } - ImmutableMap depGraph = Discovery.run(env, root); + ImmutableMap depGraph = Discovery.run(env, root); return depGraph == null ? null : DiscoveryValue.create(depGraph); } } @@ -226,20 +226,20 @@ public void testSimpleDiamond() throws Exception { DiscoveryValue discoveryValue = result.get(DiscoveryValue.KEY); assertThat(discoveryValue.getDepGraph().entrySet()) .containsExactly( - ModuleBuilder.create("aaa", "0.1") + InterimModuleBuilder.create("aaa", "0.1") .setKey(ModuleKey.ROOT) .addDep("bbb", createModuleKey("bbb", "1.0")) .addDep("ccc", createModuleKey("ccc", "2.0")) .buildEntry(), - ModuleBuilder.create("bbb", "1.0") + InterimModuleBuilder.create("bbb", "1.0") .addDep("ddd", createModuleKey("ddd", "3.0")) .setRegistry(registry) .buildEntry(), - ModuleBuilder.create("ccc", "2.0") + InterimModuleBuilder.create("ccc", "2.0") .addDep("ddd", createModuleKey("ddd", "3.0")) .setRegistry(registry) .buildEntry(), - ModuleBuilder.create("ddd", "3.0").setRegistry(registry).buildEntry()); + InterimModuleBuilder.create("ddd", "3.0").setRegistry(registry).buildEntry()); } @Test @@ -268,13 +268,13 @@ public void testDevDependency() throws Exception { DiscoveryValue discoveryValue = result.get(DiscoveryValue.KEY); assertThat(discoveryValue.getDepGraph().entrySet()) .containsExactly( - ModuleBuilder.create("aaa", "0.1") + InterimModuleBuilder.create("aaa", "0.1") .setKey(ModuleKey.ROOT) .addDep("bbb", createModuleKey("bbb", "1.0")) .addDep("ccc", createModuleKey("ccc", "1.0")) .buildEntry(), - ModuleBuilder.create("bbb", "1.0").setRegistry(registry).buildEntry(), - ModuleBuilder.create("ccc", "1.0").setRegistry(registry).buildEntry()); + InterimModuleBuilder.create("bbb", "1.0").setRegistry(registry).buildEntry(), + InterimModuleBuilder.create("ccc", "1.0").setRegistry(registry).buildEntry()); } @Test @@ -304,11 +304,11 @@ public void testIgnoreDevDependency() throws Exception { DiscoveryValue discoveryValue = result.get(DiscoveryValue.KEY); assertThat(discoveryValue.getDepGraph().entrySet()) .containsExactly( - ModuleBuilder.create("aaa", "0.1") + InterimModuleBuilder.create("aaa", "0.1") .setKey(ModuleKey.ROOT) .addDep("bbb", createModuleKey("bbb", "1.0")) .buildEntry(), - ModuleBuilder.create("bbb", "1.0").setRegistry(registry).buildEntry()); + InterimModuleBuilder.create("bbb", "1.0").setRegistry(registry).buildEntry()); } @Test @@ -336,15 +336,15 @@ public void testCircularDependency() throws Exception { DiscoveryValue discoveryValue = result.get(DiscoveryValue.KEY); assertThat(discoveryValue.getDepGraph().entrySet()) .containsExactly( - ModuleBuilder.create("aaa", "0.1") + InterimModuleBuilder.create("aaa", "0.1") .setKey(ModuleKey.ROOT) .addDep("bbb", createModuleKey("bbb", "1.0")) .buildEntry(), - ModuleBuilder.create("bbb", "1.0") + InterimModuleBuilder.create("bbb", "1.0") .addDep("ccc", createModuleKey("ccc", "2.0")) .setRegistry(registry) .buildEntry(), - ModuleBuilder.create("ccc", "2.0") + InterimModuleBuilder.create("ccc", "2.0") .addDep("bbb", createModuleKey("bbb", "1.0")) .setRegistry(registry) .buildEntry()); @@ -373,11 +373,11 @@ public void testCircularDependencyOnRootModule() throws Exception { DiscoveryValue discoveryValue = result.get(DiscoveryValue.KEY); assertThat(discoveryValue.getDepGraph().entrySet()) .containsExactly( - ModuleBuilder.create("aaa", "0.1") + InterimModuleBuilder.create("aaa", "0.1") .setKey(ModuleKey.ROOT) .addDep("bbb", createModuleKey("bbb", "1.0")) .buildEntry(), - ModuleBuilder.create("bbb", "1.0") + InterimModuleBuilder.create("bbb", "1.0") .addDep("aaa", ModuleKey.ROOT) .addOriginalDep("aaa", createModuleKey("aaa", "2.0")) .setRegistry(registry) @@ -409,16 +409,16 @@ public void testSingleVersionOverride() throws Exception { DiscoveryValue discoveryValue = result.get(DiscoveryValue.KEY); assertThat(discoveryValue.getDepGraph().entrySet()) .containsExactly( - ModuleBuilder.create("aaa", "0.1") + InterimModuleBuilder.create("aaa", "0.1") .setKey(ModuleKey.ROOT) .addDep("bbb", createModuleKey("bbb", "0.1")) .buildEntry(), - ModuleBuilder.create("bbb", "0.1") + InterimModuleBuilder.create("bbb", "0.1") .addDep("ccc", createModuleKey("ccc", "2.0")) .addOriginalDep("ccc", createModuleKey("ccc", "1.0")) .setRegistry(registry) .buildEntry(), - ModuleBuilder.create("ccc", "2.0").setRegistry(registry).buildEntry()); + InterimModuleBuilder.create("ccc", "2.0").setRegistry(registry).buildEntry()); } @Test @@ -451,15 +451,15 @@ public void testRegistryOverride() throws Exception { DiscoveryValue discoveryValue = result.get(DiscoveryValue.KEY); assertThat(discoveryValue.getDepGraph().entrySet()) .containsExactly( - ModuleBuilder.create("aaa", "0.1") + InterimModuleBuilder.create("aaa", "0.1") .setKey(ModuleKey.ROOT) .addDep("bbb", createModuleKey("bbb", "0.1")) .buildEntry(), - ModuleBuilder.create("bbb", "0.1") + InterimModuleBuilder.create("bbb", "0.1") .addDep("ccc", createModuleKey("ccc", "1.0")) .setRegistry(registry1) .buildEntry(), - ModuleBuilder.create("ccc", "1.0") + InterimModuleBuilder.create("ccc", "1.0") .addDep("bbb", createModuleKey("bbb", "0.1")) .setRegistry(registry2) .buildEntry()); @@ -493,16 +493,18 @@ public void testLocalPathOverride() throws Exception { DiscoveryValue discoveryValue = result.get(DiscoveryValue.KEY); assertThat(discoveryValue.getDepGraph().entrySet()) .containsExactly( - ModuleBuilder.create("aaa", "0.1") + InterimModuleBuilder.create("aaa", "0.1") .setKey(ModuleKey.ROOT) .addDep("bbb", createModuleKey("bbb", "0.1")) .buildEntry(), - ModuleBuilder.create("bbb", "0.1") + InterimModuleBuilder.create("bbb", "0.1") .addDep("ccc", createModuleKey("ccc", "")) .addOriginalDep("ccc", createModuleKey("ccc", "1.0")) .setRegistry(registry) .buildEntry(), - ModuleBuilder.create("ccc", "2.0").setKey(createModuleKey("ccc", "")).buildEntry()); + InterimModuleBuilder.create("ccc", "2.0") + .setKey(createModuleKey("ccc", "")) + .buildEntry()); } @Test @@ -541,26 +543,26 @@ public void testBuiltinModules_forRoot() throws Exception { DiscoveryValue discoveryValue = result.get(DiscoveryValue.KEY); assertThat(discoveryValue.getDepGraph().entrySet()) .containsExactly( - ModuleBuilder.create("", "") + InterimModuleBuilder.create("", "") .addDep("bazel_tools", createModuleKey("bazel_tools", "")) .addDep("local_config_platform", createModuleKey("local_config_platform", "")) .addDep("foo", createModuleKey("foo", "2.0")) .buildEntry(), - ModuleBuilder.create("bazel_tools", "1.0") + InterimModuleBuilder.create("bazel_tools", "1.0") .setKey(createModuleKey("bazel_tools", "")) .addDep("local_config_platform", createModuleKey("local_config_platform", "")) .addDep("foo", createModuleKey("foo", "1.0")) .buildEntry(), - ModuleBuilder.create("local_config_platform", "") + InterimModuleBuilder.create("local_config_platform", "") .setKey(createModuleKey("local_config_platform", "")) .addDep("bazel_tools", createModuleKey("bazel_tools", "")) .buildEntry(), - ModuleBuilder.create("foo", "1.0") + InterimModuleBuilder.create("foo", "1.0") .addDep("bazel_tools", createModuleKey("bazel_tools", "")) .addDep("local_config_platform", createModuleKey("local_config_platform", "")) .setRegistry(registry) .buildEntry(), - ModuleBuilder.create("foo", "2.0") + InterimModuleBuilder.create("foo", "2.0") .addDep("bazel_tools", createModuleKey("bazel_tools", "")) .addDep("local_config_platform", createModuleKey("local_config_platform", "")) .setRegistry(registry) diff --git a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/InterimModuleTest.java b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/InterimModuleTest.java new file mode 100644 index 00000000000000..dd65628c9bac18 --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/InterimModuleTest.java @@ -0,0 +1,51 @@ +// Copyright 2023 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +package com.google.devtools.build.lib.bazel.bzlmod; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.devtools.build.lib.bazel.bzlmod.BzlmodTestUtil.createModuleKey; + +import com.google.devtools.build.lib.bazel.bzlmod.BzlmodTestUtil.InterimModuleBuilder; +import com.google.devtools.build.lib.bazel.bzlmod.InterimModule.DepSpec; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests for {@link InterimModule}. */ +@RunWith(JUnit4.class) +public class InterimModuleTest { + + @Test + public void withDepSpecsTransformed() throws Exception { + assertThat( + InterimModuleBuilder.create("", "") + .addDep("dep_foo", createModuleKey("foo", "1.0")) + .addDep("dep_bar", createModuleKey("bar", "2.0")) + .build() + .withDepSpecsTransformed( + depSpec -> + DepSpec.fromModuleKey( + createModuleKey( + depSpec.getName() + "_new", + depSpec.getVersion().getOriginal() + ".1")))) + .isEqualTo( + InterimModuleBuilder.create("", "") + .addDep("dep_foo", createModuleKey("foo_new", "1.0.1")) + .addOriginalDep("dep_foo", createModuleKey("foo", "1.0")) + .addDep("dep_bar", createModuleKey("bar_new", "2.0.1")) + .addOriginalDep("dep_bar", createModuleKey("bar", "2.0")) + .build()); + } +} diff --git a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileFunctionTest.java b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileFunctionTest.java index d55e990a487364..d654cd8427b83a 100644 --- a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileFunctionTest.java +++ b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileFunctionTest.java @@ -30,7 +30,7 @@ import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider; import com.google.devtools.build.lib.analysis.ServerDirectories; import com.google.devtools.build.lib.analysis.util.AnalysisMock; -import com.google.devtools.build.lib.bazel.bzlmod.BzlmodTestUtil.ModuleBuilder; +import com.google.devtools.build.lib.bazel.bzlmod.BzlmodTestUtil.InterimModuleBuilder; import com.google.devtools.build.lib.bazel.bzlmod.ModuleFileValue.RootModuleFileValue; import com.google.devtools.build.lib.bazel.repository.starlark.StarlarkRepositoryModule; import com.google.devtools.build.lib.clock.BlazeClock; @@ -202,7 +202,7 @@ public void testRootModule() throws Exception { RootModuleFileValue rootModuleFileValue = result.get(ModuleFileValue.KEY_FOR_ROOT_MODULE); assertThat(rootModuleFileValue.getModule()) .isEqualTo( - ModuleBuilder.create("aaa", "0.1", 4) + InterimModuleBuilder.create("aaa", "0.1", 4) .setKey(ModuleKey.ROOT) .addExecutionPlatformsToRegister( ImmutableList.of("//my:platform", "//my:platform2")) @@ -250,7 +250,7 @@ public void testRootModule_noModuleFunctionIsOkay() throws Exception { RootModuleFileValue rootModuleFileValue = result.get(ModuleFileValue.KEY_FOR_ROOT_MODULE); assertThat(rootModuleFileValue.getModule()) .isEqualTo( - ModuleBuilder.create("", "") + InterimModuleBuilder.create("", "") .setKey(ModuleKey.ROOT) .addDep("bbb", createModuleKey("bbb", "1.0")) .build()); @@ -303,7 +303,7 @@ public void testRegistriesCascade() throws Exception { ModuleFileValue moduleFileValue = result.get(skyKey); assertThat(moduleFileValue.getModule()) .isEqualTo( - ModuleBuilder.create("bbb", "1.0") + InterimModuleBuilder.create("bbb", "1.0") .addDep("ccc", createModuleKey("ccc", "2.0")) .setRegistry(registry2) .build()); @@ -341,7 +341,7 @@ public void testLocalPathOverride() throws Exception { ModuleFileValue moduleFileValue = result.get(skyKey); assertThat(moduleFileValue.getModule()) .isEqualTo( - ModuleBuilder.create("bbb", "1.0") + InterimModuleBuilder.create("bbb", "1.0") .setKey(createModuleKey("bbb", "")) .addDep("ccc", createModuleKey("ccc", "2.0")) .build()); @@ -392,7 +392,7 @@ public void testCommandLineModuleOverrides() throws Exception { ModuleFileValue moduleFileValue = result.get(skyKey); assertThat(moduleFileValue.getModule()) .isEqualTo( - ModuleBuilder.create("bbb", "1.0") + InterimModuleBuilder.create("bbb", "1.0") .setKey(createModuleKey("bbb", "")) .addDep("ccc", createModuleKey("ccc", "2.0")) .build()); @@ -430,7 +430,7 @@ public void testRegistryOverride() throws Exception { ModuleFileValue moduleFileValue = result.get(skyKey); assertThat(moduleFileValue.getModule()) .isEqualTo( - ModuleBuilder.create("bbb", "1.0", 6) + InterimModuleBuilder.create("bbb", "1.0", 6) .addDep("ccc", createModuleKey("ccc", "3.0")) .setRegistry(registry2) .build()); @@ -469,7 +469,7 @@ public void testModuleExtensions_good() throws Exception { ModuleFileValue moduleFileValue = result.get(skyKey); assertThat(moduleFileValue.getModule()) .isEqualTo( - ModuleBuilder.create("mymod", "1.0") + InterimModuleBuilder.create("mymod", "1.0") .addDep("rules_jvm_external", createModuleKey("rules_jvm_external", "2.0")) .setRegistry(registry) .addExtensionUsage( @@ -590,7 +590,7 @@ public void testModuleExtensions_duplicateProxy_asRoot() throws Exception { ModuleFileValue moduleFileValue = result.get(skyKey); assertThat(moduleFileValue.getModule()) .isEqualTo( - ModuleBuilder.create("", "") + InterimModuleBuilder.create("", "") .setKey(ModuleKey.ROOT) .addExtensionUsage( ModuleExtensionUsage.builder() @@ -687,7 +687,7 @@ public void testModuleExtensions_duplicateProxy_asDep() throws Exception { ModuleFileValue moduleFileValue = result.get(skyKey); assertThat(moduleFileValue.getModule()) .isEqualTo( - ModuleBuilder.create("mymod", "1.0") + InterimModuleBuilder.create("mymod", "1.0") .setRegistry(registry) .addExtensionUsage( ModuleExtensionUsage.builder() @@ -901,7 +901,7 @@ public void testBuiltinModules_forRoot() throws Exception { RootModuleFileValue moduleFileValue = result.get(skyKey); assertThat(moduleFileValue.getModule()) .isEqualTo( - ModuleBuilder.create("", "") + InterimModuleBuilder.create("", "") .addDep("bazel_tools", createModuleKey("bazel_tools", "")) .addDep("local_config_platform", createModuleKey("local_config_platform", "")) .addDep("foo", createModuleKey("foo", "1.0")) @@ -938,7 +938,7 @@ public void testBuiltinModules_forBuiltinModules() throws Exception { ModuleFileValue moduleFileValue = result.get(skyKey); assertThat(moduleFileValue.getModule()) .isEqualTo( - ModuleBuilder.create("bazel_tools", "1.0") + InterimModuleBuilder.create("bazel_tools", "1.0") .setKey(createModuleKey("bazel_tools", "")) .addDep("local_config_platform", createModuleKey("local_config_platform", "")) .addDep("foo", createModuleKey("foo", "2.0")) @@ -962,7 +962,10 @@ public void moduleRepoName() throws Exception { RootModuleFileValue rootModuleFileValue = result.get(ModuleFileValue.KEY_FOR_ROOT_MODULE); assertThat(rootModuleFileValue.getModule()) .isEqualTo( - ModuleBuilder.create("aaa", "0.1").setKey(ModuleKey.ROOT).setRepoName("bbb").build()); + InterimModuleBuilder.create("aaa", "0.1") + .setKey(ModuleKey.ROOT) + .setRepoName("bbb") + .build()); } @Test diff --git a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleTest.java b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleTest.java index 2428755a9ab1b1..d0a5b62ce9bb05 100644 --- a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleTest.java +++ b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleTest.java @@ -15,10 +15,10 @@ package com.google.devtools.build.lib.bazel.bzlmod; import static com.google.common.truth.Truth.assertThat; +import static com.google.devtools.build.lib.bazel.bzlmod.BzlmodTestUtil.buildModule; import static com.google.devtools.build.lib.bazel.bzlmod.BzlmodTestUtil.createModuleKey; import static com.google.devtools.build.lib.bazel.bzlmod.BzlmodTestUtil.createRepositoryMapping; -import com.google.devtools.build.lib.bazel.bzlmod.BzlmodTestUtil.ModuleBuilder; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -27,31 +27,11 @@ @RunWith(JUnit4.class) public class ModuleTest { - @Test - public void withDepKeysTransformed() throws Exception { - assertThat( - ModuleBuilder.create("", "") - .addDep("dep_foo", createModuleKey("foo", "1.0")) - .addDep("dep_bar", createModuleKey("bar", "2.0")) - .build() - .withDepKeysTransformed( - key -> - createModuleKey( - key.getName() + "_new", key.getVersion().getOriginal() + ".1"))) - .isEqualTo( - ModuleBuilder.create("", "") - .addDep("dep_foo", createModuleKey("foo_new", "1.0.1")) - .addOriginalDep("dep_foo", createModuleKey("foo", "1.0")) - .addDep("dep_bar", createModuleKey("bar_new", "2.0.1")) - .addOriginalDep("dep_bar", createModuleKey("bar", "2.0")) - .build()); - } - @Test public void getRepoMapping() throws Exception { ModuleKey key = createModuleKey("test_module", "1.0"); Module module = - ModuleBuilder.create(key.getName(), key.getVersion()) + buildModule("test_module", "1.0") .addDep("my_foo", createModuleKey("foo", "1.0")) .addDep("my_bar", createModuleKey("bar", "2.0")) .addDep("my_root", ModuleKey.ROOT) @@ -73,7 +53,7 @@ public void getRepoMapping() throws Exception { @Test public void getRepoMapping_asMainModule() throws Exception { Module module = - ModuleBuilder.create("test_module", "1.0") + buildModule("test_module", "1.0") .setKey(ModuleKey.ROOT) .addDep("my_foo", createModuleKey("foo", "1.0")) .addDep("my_bar", createModuleKey("bar", "2.0")) diff --git a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/SelectionTest.java b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/SelectionTest.java index 2aed3463828d41..5cc7c3b0b46db5 100644 --- a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/SelectionTest.java +++ b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/SelectionTest.java @@ -16,12 +16,13 @@ package com.google.devtools.build.lib.bazel.bzlmod; import static com.google.common.truth.Truth.assertThat; +import static com.google.devtools.build.lib.bazel.bzlmod.BzlmodTestUtil.createDepSpec; import static com.google.devtools.build.lib.bazel.bzlmod.BzlmodTestUtil.createModuleKey; import static org.junit.Assert.assertThrows; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.google.devtools.build.lib.bazel.bzlmod.BzlmodTestUtil.ModuleBuilder; +import com.google.devtools.build.lib.bazel.bzlmod.BzlmodTestUtil.InterimModuleBuilder; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -32,167 +33,282 @@ public class SelectionTest { @Test public void diamond_simple() throws Exception { - ImmutableMap depGraph = - ImmutableMap.builder() + ImmutableMap depGraph = + ImmutableMap.builder() .put( - ModuleBuilder.create("aaa", Version.EMPTY) + InterimModuleBuilder.create("aaa", Version.EMPTY) .setKey(ModuleKey.ROOT) .addDep("bbb_from_aaa", createModuleKey("bbb", "1.0")) .addDep("ccc_from_aaa", createModuleKey("ccc", "2.0")) .buildEntry()) .put( - ModuleBuilder.create("bbb", "1.0") + InterimModuleBuilder.create("bbb", "1.0") .addDep("ddd_from_bbb", createModuleKey("ddd", "1.0")) .buildEntry()) .put( - ModuleBuilder.create("ccc", "2.0") + InterimModuleBuilder.create("ccc", "2.0") .addDep("ddd_from_ccc", createModuleKey("ddd", "2.0")) .buildEntry()) - .put(ModuleBuilder.create("ddd", "1.0", 1).buildEntry()) - .put(ModuleBuilder.create("ddd", "2.0", 1).buildEntry()) + .put(InterimModuleBuilder.create("ddd", "1.0", 1).buildEntry()) + .put(InterimModuleBuilder.create("ddd", "2.0", 1).buildEntry()) .buildOrThrow(); - BazelModuleResolutionValue selectionResult = - Selection.run(depGraph, /* overrides= */ ImmutableMap.of()); + Selection.Result selectionResult = Selection.run(depGraph, /* overrides= */ ImmutableMap.of()); assertThat(selectionResult.getResolvedDepGraph().entrySet()) .containsExactly( - ModuleBuilder.create("aaa", Version.EMPTY) + InterimModuleBuilder.create("aaa", Version.EMPTY) .setKey(ModuleKey.ROOT) .addDep("bbb_from_aaa", createModuleKey("bbb", "1.0")) .addDep("ccc_from_aaa", createModuleKey("ccc", "2.0")) .buildEntry(), - ModuleBuilder.create("bbb", "1.0") + InterimModuleBuilder.create("bbb", "1.0") .addDep("ddd_from_bbb", createModuleKey("ddd", "2.0")) .addOriginalDep("ddd_from_bbb", createModuleKey("ddd", "1.0")) .buildEntry(), - ModuleBuilder.create("ccc", "2.0") + InterimModuleBuilder.create("ccc", "2.0") .addDep("ddd_from_ccc", createModuleKey("ddd", "2.0")) .buildEntry(), - ModuleBuilder.create("ddd", "2.0", 1).buildEntry()) + InterimModuleBuilder.create("ddd", "2.0", 1).buildEntry()) .inOrder(); assertThat(selectionResult.getUnprunedDepGraph().entrySet()) .containsExactly( - ModuleBuilder.create("aaa", Version.EMPTY) + InterimModuleBuilder.create("aaa", Version.EMPTY) .setKey(ModuleKey.ROOT) .addDep("bbb_from_aaa", createModuleKey("bbb", "1.0")) .addDep("ccc_from_aaa", createModuleKey("ccc", "2.0")) .buildEntry(), - ModuleBuilder.create("bbb", "1.0") + InterimModuleBuilder.create("bbb", "1.0") .addDep("ddd_from_bbb", createModuleKey("ddd", "2.0")) .addOriginalDep("ddd_from_bbb", createModuleKey("ddd", "1.0")) .buildEntry(), - ModuleBuilder.create("ccc", "2.0") + InterimModuleBuilder.create("ccc", "2.0") .addDep("ddd_from_ccc", createModuleKey("ddd", "2.0")) .buildEntry(), - ModuleBuilder.create("ddd", "1.0", 1).buildEntry(), - ModuleBuilder.create("ddd", "2.0", 1).buildEntry()); + InterimModuleBuilder.create("ddd", "1.0", 1).buildEntry(), + InterimModuleBuilder.create("ddd", "2.0", 1).buildEntry()); + } + + @Test + public void diamond_withIgnoredNonAffectingMaxCompatibilityLevel() throws Exception { + ImmutableMap depGraph = + ImmutableMap.builder() + .put( + InterimModuleBuilder.create("aaa", Version.EMPTY) + .setKey(ModuleKey.ROOT) + .addDep("bbb_from_aaa", createModuleKey("bbb", "1.0")) + .addDep("ccc_from_aaa", createModuleKey("ccc", "2.0")) + .buildEntry()) + .put( + InterimModuleBuilder.create("bbb", "1.0") + .addDep("ddd_from_bbb", createDepSpec("ddd", "1.0", 3)) + .buildEntry()) + .put( + InterimModuleBuilder.create("ccc", "2.0") + .addDep("ddd_from_ccc", createModuleKey("ddd", "2.0")) + .buildEntry()) + .put(InterimModuleBuilder.create("ddd", "1.0", 1).buildEntry()) + .put(InterimModuleBuilder.create("ddd", "2.0", 1).buildEntry()) + .buildOrThrow(); + + Selection.Result selectionResult = Selection.run(depGraph, /* overrides= */ ImmutableMap.of()); + assertThat(selectionResult.getResolvedDepGraph().entrySet()) + .containsExactly( + InterimModuleBuilder.create("aaa", Version.EMPTY) + .setKey(ModuleKey.ROOT) + .addDep("bbb_from_aaa", createModuleKey("bbb", "1.0")) + .addDep("ccc_from_aaa", createModuleKey("ccc", "2.0")) + .buildEntry(), + InterimModuleBuilder.create("bbb", "1.0") + .addDep("ddd_from_bbb", createModuleKey("ddd", "2.0")) + .addOriginalDep("ddd_from_bbb", createDepSpec("ddd", "1.0", 3)) + .buildEntry(), + InterimModuleBuilder.create("ccc", "2.0") + .addDep("ddd_from_ccc", createModuleKey("ddd", "2.0")) + .buildEntry(), + InterimModuleBuilder.create("ddd", "2.0", 1).buildEntry()) + .inOrder(); + + assertThat(selectionResult.getUnprunedDepGraph().entrySet()) + .containsExactly( + InterimModuleBuilder.create("aaa", Version.EMPTY) + .setKey(ModuleKey.ROOT) + .addDep("bbb_from_aaa", createModuleKey("bbb", "1.0")) + .addDep("ccc_from_aaa", createModuleKey("ccc", "2.0")) + .buildEntry(), + InterimModuleBuilder.create("bbb", "1.0") + .addDep("ddd_from_bbb", createModuleKey("ddd", "2.0")) + .addOriginalDep("ddd_from_bbb", createDepSpec("ddd", "1.0", 3)) + .buildEntry(), + InterimModuleBuilder.create("ccc", "2.0") + .addDep("ddd_from_ccc", createModuleKey("ddd", "2.0")) + .buildEntry(), + InterimModuleBuilder.create("ddd", "1.0", 1).buildEntry(), + InterimModuleBuilder.create("ddd", "2.0", 1).buildEntry()); + } + + @Test + public void diamond_withSelectedNonAffectingMaxCompatibilityLevel() throws Exception { + ImmutableMap depGraph = + ImmutableMap.builder() + .put( + InterimModuleBuilder.create("aaa", Version.EMPTY) + .setKey(ModuleKey.ROOT) + .addDep("bbb_from_aaa", createModuleKey("bbb", "1.0")) + .addDep("ccc_from_aaa", createModuleKey("ccc", "2.0")) + .buildEntry()) + .put( + InterimModuleBuilder.create("bbb", "1.0") + .addDep("ddd_from_bbb", createModuleKey("ddd", "1.0")) + .buildEntry()) + .put( + InterimModuleBuilder.create("ccc", "2.0") + .addDep("ddd_from_ccc", createDepSpec("ddd", "2.0", 4)) + .buildEntry()) + .put(InterimModuleBuilder.create("ddd", "1.0", 1).buildEntry()) + .put(InterimModuleBuilder.create("ddd", "2.0", 1).buildEntry()) + .buildOrThrow(); + + Selection.Result selectionResult = Selection.run(depGraph, /* overrides= */ ImmutableMap.of()); + assertThat(selectionResult.getResolvedDepGraph().entrySet()) + .containsExactly( + InterimModuleBuilder.create("aaa", Version.EMPTY) + .setKey(ModuleKey.ROOT) + .addDep("bbb_from_aaa", createModuleKey("bbb", "1.0")) + .addDep("ccc_from_aaa", createModuleKey("ccc", "2.0")) + .buildEntry(), + InterimModuleBuilder.create("bbb", "1.0") + .addDep("ddd_from_bbb", createModuleKey("ddd", "2.0")) + .addOriginalDep("ddd_from_bbb", createModuleKey("ddd", "1.0")) + .buildEntry(), + InterimModuleBuilder.create("ccc", "2.0") + .addDep("ddd_from_ccc", createModuleKey("ddd", "2.0")) + .addOriginalDep("ddd_from_ccc", createDepSpec("ddd", "2.0", 4)) + .buildEntry(), + InterimModuleBuilder.create("ddd", "2.0", 1).buildEntry()) + .inOrder(); + + assertThat(selectionResult.getUnprunedDepGraph().entrySet()) + .containsExactly( + InterimModuleBuilder.create("aaa", Version.EMPTY) + .setKey(ModuleKey.ROOT) + .addDep("bbb_from_aaa", createModuleKey("bbb", "1.0")) + .addDep("ccc_from_aaa", createModuleKey("ccc", "2.0")) + .buildEntry(), + InterimModuleBuilder.create("bbb", "1.0") + .addDep("ddd_from_bbb", createModuleKey("ddd", "2.0")) + .addOriginalDep("ddd_from_bbb", createModuleKey("ddd", "1.0")) + .buildEntry(), + InterimModuleBuilder.create("ccc", "2.0") + .addDep("ddd_from_ccc", createModuleKey("ddd", "2.0")) + .addOriginalDep("ddd_from_ccc", createDepSpec("ddd", "2.0", 4)) + .buildEntry(), + InterimModuleBuilder.create("ddd", "1.0", 1).buildEntry(), + InterimModuleBuilder.create("ddd", "2.0", 1).buildEntry()); } @Test public void diamond_withFurtherRemoval() throws Exception { - ImmutableMap depGraph = - ImmutableMap.builder() + ImmutableMap depGraph = + ImmutableMap.builder() .put( - ModuleBuilder.create("aaa", Version.EMPTY) + InterimModuleBuilder.create("aaa", Version.EMPTY) .setKey(ModuleKey.ROOT) .addDep("bbb", createModuleKey("bbb", "1.0")) .addDep("ccc", createModuleKey("ccc", "2.0")) .buildEntry()) .put( - ModuleBuilder.create("bbb", "1.0") + InterimModuleBuilder.create("bbb", "1.0") .addDep("ddd", createModuleKey("ddd", "1.0")) .buildEntry()) .put( - ModuleBuilder.create("ccc", "2.0") + InterimModuleBuilder.create("ccc", "2.0") .addDep("ddd", createModuleKey("ddd", "2.0")) .buildEntry()) .put( - ModuleBuilder.create("ddd", "1.0") + InterimModuleBuilder.create("ddd", "1.0") .addDep("eee", createModuleKey("eee", "1.0")) .buildEntry()) - .put(ModuleBuilder.create("ddd", "2.0").buildEntry()) + .put(InterimModuleBuilder.create("ddd", "2.0").buildEntry()) // Only D@1.0 needs E. When D@1.0 is removed, E should be gone as well (even though // E@1.0 is selected for E). - .put(ModuleBuilder.create("eee", "1.0").buildEntry()) + .put(InterimModuleBuilder.create("eee", "1.0").buildEntry()) .build(); - BazelModuleResolutionValue selectionResult = - Selection.run(depGraph, /* overrides= */ ImmutableMap.of()); + Selection.Result selectionResult = Selection.run(depGraph, /* overrides= */ ImmutableMap.of()); assertThat(selectionResult.getResolvedDepGraph().entrySet()) .containsExactly( - ModuleBuilder.create("aaa", Version.EMPTY) + InterimModuleBuilder.create("aaa", Version.EMPTY) .setKey(ModuleKey.ROOT) .addDep("bbb", createModuleKey("bbb", "1.0")) .addDep("ccc", createModuleKey("ccc", "2.0")) .buildEntry(), - ModuleBuilder.create("bbb", "1.0") + InterimModuleBuilder.create("bbb", "1.0") .addDep("ddd", createModuleKey("ddd", "2.0")) .addOriginalDep("ddd", createModuleKey("ddd", "1.0")) .buildEntry(), - ModuleBuilder.create("ccc", "2.0") + InterimModuleBuilder.create("ccc", "2.0") .addDep("ddd", createModuleKey("ddd", "2.0")) .buildEntry(), - ModuleBuilder.create("ddd", "2.0").buildEntry()) + InterimModuleBuilder.create("ddd", "2.0").buildEntry()) .inOrder(); assertThat(selectionResult.getUnprunedDepGraph().entrySet()) .containsExactly( - ModuleBuilder.create("aaa", Version.EMPTY) + InterimModuleBuilder.create("aaa", Version.EMPTY) .setKey(ModuleKey.ROOT) .addDep("bbb", createModuleKey("bbb", "1.0")) .addDep("ccc", createModuleKey("ccc", "2.0")) .buildEntry(), - ModuleBuilder.create("bbb", "1.0") + InterimModuleBuilder.create("bbb", "1.0") .addDep("ddd", createModuleKey("ddd", "2.0")) .addOriginalDep("ddd", createModuleKey("ddd", "1.0")) .buildEntry(), - ModuleBuilder.create("ccc", "2.0") + InterimModuleBuilder.create("ccc", "2.0") .addDep("ddd", createModuleKey("ddd", "2.0")) .buildEntry(), - ModuleBuilder.create("ddd", "2.0").buildEntry(), - ModuleBuilder.create("ddd", "1.0") + InterimModuleBuilder.create("ddd", "2.0").buildEntry(), + InterimModuleBuilder.create("ddd", "1.0") .addDep("eee", createModuleKey("eee", "1.0")) .buildEntry(), - ModuleBuilder.create("eee", "1.0").buildEntry()); + InterimModuleBuilder.create("eee", "1.0").buildEntry()); } @Test public void circularDependencyDueToSelection() throws Exception { - ImmutableMap depGraph = - ImmutableMap.builder() + ImmutableMap depGraph = + ImmutableMap.builder() .put( - ModuleBuilder.create("aaa", Version.EMPTY) + InterimModuleBuilder.create("aaa", Version.EMPTY) .setKey(ModuleKey.ROOT) .addDep("bbb", createModuleKey("bbb", "1.0")) .buildEntry()) .put( - ModuleBuilder.create("bbb", "1.0") + InterimModuleBuilder.create("bbb", "1.0") .addDep("ccc", createModuleKey("ccc", "2.0")) .buildEntry()) .put( - ModuleBuilder.create("ccc", "2.0") + InterimModuleBuilder.create("ccc", "2.0") .addDep("bbb", createModuleKey("bbb", "1.0-pre")) .buildEntry()) .put( - ModuleBuilder.create("bbb", "1.0-pre") + InterimModuleBuilder.create("bbb", "1.0-pre") .addDep("ddd", createModuleKey("ddd", "1.0")) .buildEntry()) - .put(ModuleBuilder.create("ddd", "1.0").buildEntry()) + .put(InterimModuleBuilder.create("ddd", "1.0").buildEntry()) .buildOrThrow(); - BazelModuleResolutionValue selectionResult = - Selection.run(depGraph, /* overrides= */ ImmutableMap.of()); + Selection.Result selectionResult = Selection.run(depGraph, /* overrides= */ ImmutableMap.of()); assertThat(selectionResult.getResolvedDepGraph().entrySet()) .containsExactly( - ModuleBuilder.create("aaa", Version.EMPTY) + InterimModuleBuilder.create("aaa", Version.EMPTY) .setKey(ModuleKey.ROOT) .addDep("bbb", createModuleKey("bbb", "1.0")) .buildEntry(), - ModuleBuilder.create("bbb", "1.0") + InterimModuleBuilder.create("bbb", "1.0") .addDep("ccc", createModuleKey("ccc", "2.0")) .buildEntry(), - ModuleBuilder.create("ccc", "2.0") + InterimModuleBuilder.create("ccc", "2.0") .addDep("bbb", createModuleKey("bbb", "1.0")) .addOriginalDep("bbb", createModuleKey("bbb", "1.0-pre")) .buildEntry()) @@ -201,65 +317,331 @@ public void circularDependencyDueToSelection() throws Exception { assertThat(selectionResult.getUnprunedDepGraph().entrySet()) .containsExactly( - ModuleBuilder.create("aaa", Version.EMPTY) + InterimModuleBuilder.create("aaa", Version.EMPTY) .setKey(ModuleKey.ROOT) .addDep("bbb", createModuleKey("bbb", "1.0")) .buildEntry(), - ModuleBuilder.create("bbb", "1.0") + InterimModuleBuilder.create("bbb", "1.0") .addDep("ccc", createModuleKey("ccc", "2.0")) .buildEntry(), - ModuleBuilder.create("ccc", "2.0") + InterimModuleBuilder.create("ccc", "2.0") .addDep("bbb", createModuleKey("bbb", "1.0")) .addOriginalDep("bbb", createModuleKey("bbb", "1.0-pre")) .buildEntry(), - ModuleBuilder.create("bbb", "1.0-pre") + InterimModuleBuilder.create("bbb", "1.0-pre") .addDep("ddd", createModuleKey("ddd", "1.0")) .buildEntry(), - ModuleBuilder.create("ddd", "1.0").buildEntry()); + InterimModuleBuilder.create("ddd", "1.0").buildEntry()); } @Test public void differentCompatibilityLevelIsRejected() throws Exception { - ImmutableMap depGraph = - ImmutableMap.builder() + ImmutableMap depGraph = + ImmutableMap.builder() .put( - ModuleBuilder.create("aaa", Version.EMPTY) + InterimModuleBuilder.create("aaa", Version.EMPTY) .setKey(ModuleKey.ROOT) .addDep("bbb_from_aaa", createModuleKey("bbb", "1.0")) .addDep("ccc_from_aaa", createModuleKey("ccc", "2.0")) .buildEntry()) .put( - ModuleBuilder.create("bbb", "1.0") + InterimModuleBuilder.create("bbb", "1.0") .addDep("ddd_from_bbb", createModuleKey("ddd", "1.0")) .buildEntry()) .put( - ModuleBuilder.create("ccc", "2.0") + InterimModuleBuilder.create("ccc", "2.0") .addDep("ddd_from_ccc", createModuleKey("ddd", "2.0")) .buildEntry()) - .put(ModuleBuilder.create("ddd", "1.0", 1).buildEntry()) - .put(ModuleBuilder.create("ddd", "2.0", 2).buildEntry()) + .put(InterimModuleBuilder.create("ddd", "1.0", 1).buildEntry()) + .put(InterimModuleBuilder.create("ddd", "2.0", 2).buildEntry()) + .buildOrThrow(); + + ExternalDepsException e = + assertThrows( + ExternalDepsException.class, + () -> Selection.run(depGraph, /* overrides= */ ImmutableMap.of())); + String error = e.getMessage(); + assertThat(error).contains("bbb@1.0 depends on ddd@1.0 with compatibility level 1"); + assertThat(error).contains("ccc@2.0 depends on ddd@2.0 with compatibility level 2"); + assertThat(error).contains("which is different"); + } + + @Test + public void differentCompatibilityLevelWithMaxCompatibilityLevelIsRejected() throws Exception { + ImmutableMap depGraph = + ImmutableMap.builder() + .put( + InterimModuleBuilder.create("aaa", Version.EMPTY) + .setKey(ModuleKey.ROOT) + .addDep("bbb_from_aaa", createModuleKey("bbb", "1.0")) + .addDep("ccc_from_aaa", createModuleKey("ccc", "2.0")) + .buildEntry()) + .put( + InterimModuleBuilder.create("bbb", "1.0") + .addDep("ddd_from_bbb", createModuleKey("ddd", "1.0")) + .buildEntry()) + .put( + InterimModuleBuilder.create("ccc", "2.0") + .addDep("ddd_from_ccc", createDepSpec("ddd", "2.0", 3)) + .buildEntry()) + .put(InterimModuleBuilder.create("ddd", "1.0", 1).buildEntry()) + .put(InterimModuleBuilder.create("ddd", "2.0", 2).buildEntry()) .buildOrThrow(); ExternalDepsException e = assertThrows( ExternalDepsException.class, - () -> Selection.run(depGraph, /*overrides=*/ ImmutableMap.of())); + () -> Selection.run(depGraph, /* overrides= */ ImmutableMap.of())); String error = e.getMessage(); assertThat(error).contains("bbb@1.0 depends on ddd@1.0 with compatibility level 1"); assertThat(error).contains("ccc@2.0 depends on ddd@2.0 with compatibility level 2"); assertThat(error).contains("which is different"); } + @Test + public void maxCompatibilityBasedSelection() throws Exception { + ImmutableMap depGraph = + ImmutableMap.builder() + .put( + InterimModuleBuilder.create("aaa", Version.EMPTY) + .setKey(ModuleKey.ROOT) + .addDep("bbb_from_aaa", createModuleKey("bbb", "1.0")) + .addDep("ccc_from_aaa", createModuleKey("ccc", "2.0")) + .buildEntry()) + .put( + InterimModuleBuilder.create("bbb", "1.0") + .addDep("ddd_from_bbb", createDepSpec("ddd", "1.0", 2)) + .buildEntry()) + .put( + InterimModuleBuilder.create("ccc", "2.0") + .addDep("ddd_from_ccc", createModuleKey("ddd", "2.0")) + .buildEntry()) + .put(InterimModuleBuilder.create("ddd", "1.0", 1).buildEntry()) + .put(InterimModuleBuilder.create("ddd", "2.0", 2).buildEntry()) + .buildOrThrow(); + + Selection.Result selectionResult = Selection.run(depGraph, /* overrides= */ ImmutableMap.of()); + assertThat(selectionResult.getResolvedDepGraph().entrySet()) + .containsExactly( + InterimModuleBuilder.create("aaa", Version.EMPTY) + .setKey(ModuleKey.ROOT) + .addDep("bbb_from_aaa", createModuleKey("bbb", "1.0")) + .addDep("ccc_from_aaa", createModuleKey("ccc", "2.0")) + .buildEntry(), + InterimModuleBuilder.create("bbb", "1.0") + .addDep("ddd_from_bbb", createModuleKey("ddd", "2.0")) + .addOriginalDep("ddd_from_bbb", createDepSpec("ddd", "1.0", 2)) + .buildEntry(), + InterimModuleBuilder.create("ccc", "2.0") + .addDep("ddd_from_ccc", createModuleKey("ddd", "2.0")) + .buildEntry(), + InterimModuleBuilder.create("ddd", "2.0", 2).buildEntry()) + .inOrder(); + + assertThat(selectionResult.getUnprunedDepGraph().entrySet()) + .containsExactly( + InterimModuleBuilder.create("aaa", Version.EMPTY) + .setKey(ModuleKey.ROOT) + .addDep("bbb_from_aaa", createModuleKey("bbb", "1.0")) + .addDep("ccc_from_aaa", createModuleKey("ccc", "2.0")) + .buildEntry(), + InterimModuleBuilder.create("bbb", "1.0") + .addDep("ddd_from_bbb", createModuleKey("ddd", "2.0")) + .addOriginalDep("ddd_from_bbb", createDepSpec("ddd", "1.0", 2)) + .buildEntry(), + InterimModuleBuilder.create("ccc", "2.0") + .addDep("ddd_from_ccc", createModuleKey("ddd", "2.0")) + .buildEntry(), + InterimModuleBuilder.create("ddd", "1.0", 1).buildEntry(), + InterimModuleBuilder.create("ddd", "2.0", 2).buildEntry()); + } + + @Test + public void maxCompatibilityBasedSelection_sameVersion() throws Exception { + ImmutableMap depGraph = + ImmutableMap.builder() + .put( + InterimModuleBuilder.create("aaa", Version.EMPTY) + .setKey(ModuleKey.ROOT) + .addDep("bbb_from_aaa", createModuleKey("bbb", "1.0")) + .addDep("ccc_from_aaa", createModuleKey("ccc", "2.0")) + .buildEntry()) + .put( + InterimModuleBuilder.create("bbb", "1.0") + .addDep("ddd_from_bbb", createDepSpec("ddd", "2.0", 3)) + .buildEntry()) + .put( + InterimModuleBuilder.create("ccc", "2.0") + .addDep("ddd_from_ccc", createModuleKey("ddd", "2.0")) + .buildEntry()) + .put(InterimModuleBuilder.create("ddd", "2.0", 2).buildEntry()) + .buildOrThrow(); + + Selection.Result selectionResult = Selection.run(depGraph, /* overrides= */ ImmutableMap.of()); + assertThat(selectionResult.getResolvedDepGraph().entrySet()) + .containsExactly( + InterimModuleBuilder.create("aaa", Version.EMPTY) + .setKey(ModuleKey.ROOT) + .addDep("bbb_from_aaa", createModuleKey("bbb", "1.0")) + .addDep("ccc_from_aaa", createModuleKey("ccc", "2.0")) + .buildEntry(), + InterimModuleBuilder.create("bbb", "1.0") + .addDep("ddd_from_bbb", createModuleKey("ddd", "2.0")) + .addOriginalDep("ddd_from_bbb", createDepSpec("ddd", "2.0", 3)) + .buildEntry(), + InterimModuleBuilder.create("ccc", "2.0") + .addDep("ddd_from_ccc", createModuleKey("ddd", "2.0")) + .buildEntry(), + InterimModuleBuilder.create("ddd", "2.0", 2).buildEntry()) + .inOrder(); + + assertThat(selectionResult.getUnprunedDepGraph().entrySet()) + .containsExactly( + InterimModuleBuilder.create("aaa", Version.EMPTY) + .setKey(ModuleKey.ROOT) + .addDep("bbb_from_aaa", createModuleKey("bbb", "1.0")) + .addDep("ccc_from_aaa", createModuleKey("ccc", "2.0")) + .buildEntry(), + InterimModuleBuilder.create("bbb", "1.0") + .addDep("ddd_from_bbb", createModuleKey("ddd", "2.0")) + .addOriginalDep("ddd_from_bbb", createDepSpec("ddd", "2.0", 3)) + .buildEntry(), + InterimModuleBuilder.create("ccc", "2.0") + .addDep("ddd_from_ccc", createModuleKey("ddd", "2.0")) + .buildEntry(), + InterimModuleBuilder.create("ddd", "2.0", 2).buildEntry()); + } + + @Test + public void maxCompatibilityBasedSelection_limitedToMax() throws Exception { + ImmutableMap depGraph = + ImmutableMap.builder() + .put( + InterimModuleBuilder.create("aaa", Version.EMPTY) + .setKey(ModuleKey.ROOT) + .addDep("bbb_from_aaa", createModuleKey("bbb", "1.0")) + .addDep("ccc_from_aaa", createModuleKey("ccc", "2.0")) + .buildEntry()) + .put( + InterimModuleBuilder.create("bbb", "1.0") + .addDep("ddd_from_bbb", createDepSpec("ddd", "1.0", 2)) + .buildEntry()) + .put( + InterimModuleBuilder.create("ccc", "2.0") + .addDep("ddd_from_ccc", createModuleKey("ddd", "3.0")) + .buildEntry()) + .put(InterimModuleBuilder.create("ddd", "1.0", 1).buildEntry()) + .put(InterimModuleBuilder.create("ddd", "3.0", 3).buildEntry()) + .buildOrThrow(); + + ExternalDepsException e = + assertThrows( + ExternalDepsException.class, + () -> Selection.run(depGraph, /* overrides= */ ImmutableMap.of())); + String error = e.getMessage(); + assertThat(error).contains("bbb@1.0 depends on ddd@1.0 with compatibility level 1"); + assertThat(error).contains("ccc@2.0 depends on ddd@3.0 with compatibility level 3"); + assertThat(error).contains("which is different"); + } + + @Test + public void maxCompatibilityBasedSelection_unreferencedNotSelected() throws Exception { + // aaa 1.0 -> bbb 1.0 -> ccc 2.0 + // \-> ccc 1.0 (max_compatibility_level=2) + // \-> ddd 1.0 -> bbb 1.1 + // \-> eee 1.0 -> ccc 1.1 + ImmutableMap depGraph = + ImmutableMap.builder() + .put( + InterimModuleBuilder.create("aaa", "1.0") + .setKey(ModuleKey.ROOT) + .addDep("bbb", createModuleKey("bbb", "1.0")) + .addDep("ccc", createDepSpec("ccc", "1.0", 2)) + .addDep("ddd", createModuleKey("ddd", "1.0")) + .addDep("eee", createModuleKey("eee", "1.0")) + .buildEntry()) + .put( + InterimModuleBuilder.create("bbb", "1.0") + .addDep("ccc", createModuleKey("ccc", "2.0")) + .buildEntry()) + .put(InterimModuleBuilder.create("ccc", "2.0", 2).buildEntry()) + .put(InterimModuleBuilder.create("ccc", "1.0", 1).buildEntry()) + .put( + InterimModuleBuilder.create("ddd", "1.0") + .addDep("bbb", createModuleKey("bbb", "1.1")) + .buildEntry()) + .put(InterimModuleBuilder.create("bbb", "1.1").buildEntry()) + .put( + InterimModuleBuilder.create("eee", "1.0") + .addDep("ccc", createModuleKey("ccc", "1.1")) + .buildEntry()) + .put(InterimModuleBuilder.create("ccc", "1.1", 1).buildEntry()) + .buildOrThrow(); + + // After selection, ccc 2.0 is gone, so ccc 1.0 (max_compatibility_level=2) doesn't upgrade to + // ccc 2.0, and only upgrades to ccc 1.1 + // aaa 1.0 -> bbb 1.1 + // \-> ccc 1.1 + // \-> ddd 1.0 -> bbb 1.1 + // \-> eee 1.0 -> ccc 1.1 + Selection.Result selectionResult = Selection.run(depGraph, /* overrides= */ ImmutableMap.of()); + assertThat(selectionResult.getResolvedDepGraph().entrySet()) + .containsExactly( + InterimModuleBuilder.create("aaa", "1.0") + .setKey(ModuleKey.ROOT) + .addDep("bbb", createModuleKey("bbb", "1.1")) + .addOriginalDep("bbb", createModuleKey("bbb", "1.0")) + .addDep("ccc", createModuleKey("ccc", "1.1")) + .addOriginalDep("ccc", createDepSpec("ccc", "1.0", 2)) + .addDep("ddd", createModuleKey("ddd", "1.0")) + .addDep("eee", createModuleKey("eee", "1.0")) + .buildEntry(), + InterimModuleBuilder.create("bbb", "1.1").buildEntry(), + InterimModuleBuilder.create("ccc", "1.1", 1).buildEntry(), + InterimModuleBuilder.create("ddd", "1.0") + .addDep("bbb", createModuleKey("bbb", "1.1")) + .buildEntry(), + InterimModuleBuilder.create("eee", "1.0") + .addDep("ccc", createModuleKey("ccc", "1.1")) + .buildEntry()) + .inOrder(); + + assertThat(selectionResult.getUnprunedDepGraph().entrySet()) + .containsExactly( + InterimModuleBuilder.create("aaa", "1.0") + .setKey(ModuleKey.ROOT) + .addDep("bbb", createModuleKey("bbb", "1.1")) + .addOriginalDep("bbb", createModuleKey("bbb", "1.0")) + .addDep("ccc", createModuleKey("ccc", "1.1")) + .addOriginalDep("ccc", createDepSpec("ccc", "1.0", 2)) + .addDep("ddd", createModuleKey("ddd", "1.0")) + .addDep("eee", createModuleKey("eee", "1.0")) + .buildEntry(), + InterimModuleBuilder.create("bbb", "1.0") + .addDep("ccc", createModuleKey("ccc", "2.0")) + .buildEntry(), + InterimModuleBuilder.create("bbb", "1.1").buildEntry(), + InterimModuleBuilder.create("ccc", "1.0", 1).buildEntry(), + InterimModuleBuilder.create("ccc", "1.1", 1).buildEntry(), + InterimModuleBuilder.create("ccc", "2.0", 2).buildEntry(), + InterimModuleBuilder.create("ddd", "1.0") + .addDep("bbb", createModuleKey("bbb", "1.1")) + .buildEntry(), + InterimModuleBuilder.create("eee", "1.0") + .addDep("ccc", createModuleKey("ccc", "1.1")) + .buildEntry()); + } + @Test public void differentCompatibilityLevelIsOkIfUnreferenced() throws Exception { // aaa 1.0 -> bbb 1.0 -> ccc 2.0 // \-> ccc 1.0 // \-> ddd 1.0 -> bbb 1.1 // \-> eee 1.0 -> ccc 1.1 - ImmutableMap depGraph = - ImmutableMap.builder() + ImmutableMap depGraph = + ImmutableMap.builder() .put( - ModuleBuilder.create("aaa", "1.0") + InterimModuleBuilder.create("aaa", "1.0") .setKey(ModuleKey.ROOT) .addDep("bbb", createModuleKey("bbb", "1.0")) .addDep("ccc", createModuleKey("ccc", "1.0")) @@ -267,21 +649,21 @@ public void differentCompatibilityLevelIsOkIfUnreferenced() throws Exception { .addDep("eee", createModuleKey("eee", "1.0")) .buildEntry()) .put( - ModuleBuilder.create("bbb", "1.0") + InterimModuleBuilder.create("bbb", "1.0") .addDep("ccc", createModuleKey("ccc", "2.0")) .buildEntry()) - .put(ModuleBuilder.create("ccc", "2.0", 2).buildEntry()) - .put(ModuleBuilder.create("ccc", "1.0", 1).buildEntry()) + .put(InterimModuleBuilder.create("ccc", "2.0", 2).buildEntry()) + .put(InterimModuleBuilder.create("ccc", "1.0", 1).buildEntry()) .put( - ModuleBuilder.create("ddd", "1.0") + InterimModuleBuilder.create("ddd", "1.0") .addDep("bbb", createModuleKey("bbb", "1.1")) .buildEntry()) - .put(ModuleBuilder.create("bbb", "1.1").buildEntry()) + .put(InterimModuleBuilder.create("bbb", "1.1").buildEntry()) .put( - ModuleBuilder.create("eee", "1.0") + InterimModuleBuilder.create("eee", "1.0") .addDep("ccc", createModuleKey("ccc", "1.1")) .buildEntry()) - .put(ModuleBuilder.create("ccc", "1.1", 1).buildEntry()) + .put(InterimModuleBuilder.create("ccc", "1.1", 1).buildEntry()) .buildOrThrow(); // After selection, ccc 2.0 is gone, so we're okay. @@ -289,11 +671,10 @@ public void differentCompatibilityLevelIsOkIfUnreferenced() throws Exception { // \-> ccc 1.1 // \-> ddd 1.0 -> bbb 1.1 // \-> eee 1.0 -> ccc 1.1 - BazelModuleResolutionValue selectionResult = - Selection.run(depGraph, /* overrides= */ ImmutableMap.of()); + Selection.Result selectionResult = Selection.run(depGraph, /* overrides= */ ImmutableMap.of()); assertThat(selectionResult.getResolvedDepGraph().entrySet()) .containsExactly( - ModuleBuilder.create("aaa", "1.0") + InterimModuleBuilder.create("aaa", "1.0") .setKey(ModuleKey.ROOT) .addDep("bbb", createModuleKey("bbb", "1.1")) .addOriginalDep("bbb", createModuleKey("bbb", "1.0")) @@ -302,19 +683,19 @@ public void differentCompatibilityLevelIsOkIfUnreferenced() throws Exception { .addDep("ddd", createModuleKey("ddd", "1.0")) .addDep("eee", createModuleKey("eee", "1.0")) .buildEntry(), - ModuleBuilder.create("bbb", "1.1").buildEntry(), - ModuleBuilder.create("ccc", "1.1", 1).buildEntry(), - ModuleBuilder.create("ddd", "1.0") + InterimModuleBuilder.create("bbb", "1.1").buildEntry(), + InterimModuleBuilder.create("ccc", "1.1", 1).buildEntry(), + InterimModuleBuilder.create("ddd", "1.0") .addDep("bbb", createModuleKey("bbb", "1.1")) .buildEntry(), - ModuleBuilder.create("eee", "1.0") + InterimModuleBuilder.create("eee", "1.0") .addDep("ccc", createModuleKey("ccc", "1.1")) .buildEntry()) .inOrder(); assertThat(selectionResult.getUnprunedDepGraph().entrySet()) .containsExactly( - ModuleBuilder.create("aaa", "1.0") + InterimModuleBuilder.create("aaa", "1.0") .setKey(ModuleKey.ROOT) .addDep("bbb", createModuleKey("bbb", "1.1")) .addOriginalDep("bbb", createModuleKey("bbb", "1.0")) @@ -323,33 +704,33 @@ public void differentCompatibilityLevelIsOkIfUnreferenced() throws Exception { .addDep("ddd", createModuleKey("ddd", "1.0")) .addDep("eee", createModuleKey("eee", "1.0")) .buildEntry(), - ModuleBuilder.create("bbb", "1.0") + InterimModuleBuilder.create("bbb", "1.0") .addDep("ccc", createModuleKey("ccc", "2.0")) .buildEntry(), - ModuleBuilder.create("bbb", "1.1").buildEntry(), - ModuleBuilder.create("ccc", "1.0", 1).buildEntry(), - ModuleBuilder.create("ccc", "1.1", 1).buildEntry(), - ModuleBuilder.create("ccc", "2.0", 2).buildEntry(), - ModuleBuilder.create("ddd", "1.0") + InterimModuleBuilder.create("bbb", "1.1").buildEntry(), + InterimModuleBuilder.create("ccc", "1.0", 1).buildEntry(), + InterimModuleBuilder.create("ccc", "1.1", 1).buildEntry(), + InterimModuleBuilder.create("ccc", "2.0", 2).buildEntry(), + InterimModuleBuilder.create("ddd", "1.0") .addDep("bbb", createModuleKey("bbb", "1.1")) .buildEntry(), - ModuleBuilder.create("eee", "1.0") + InterimModuleBuilder.create("eee", "1.0") .addDep("ccc", createModuleKey("ccc", "1.1")) .buildEntry()); } @Test public void multipleVersionOverride_fork_allowedVersionMissingInDepGraph() throws Exception { - ImmutableMap depGraph = - ImmutableMap.builder() + ImmutableMap depGraph = + ImmutableMap.builder() .put( - ModuleBuilder.create("aaa", Version.EMPTY) + InterimModuleBuilder.create("aaa", Version.EMPTY) .setKey(ModuleKey.ROOT) .addDep("bbb1", createModuleKey("bbb", "1.0")) .addDep("bbb2", createModuleKey("bbb", "2.0")) .buildEntry()) - .put(ModuleBuilder.create("bbb", "1.0").buildEntry()) - .put(ModuleBuilder.create("bbb", "2.0").buildEntry()) + .put(InterimModuleBuilder.create("bbb", "1.0").buildEntry()) + .put(InterimModuleBuilder.create("bbb", "2.0").buildEntry()) .buildOrThrow(); ImmutableMap overrides = ImmutableMap.of( @@ -370,16 +751,16 @@ public void multipleVersionOverride_fork_allowedVersionMissingInDepGraph() throw @Test public void multipleVersionOverride_fork_goodCase() throws Exception { // For more complex good cases, see the "diamond" test cases below. - ImmutableMap depGraph = - ImmutableMap.builder() + ImmutableMap depGraph = + ImmutableMap.builder() .put( - ModuleBuilder.create("aaa", Version.EMPTY) + InterimModuleBuilder.create("aaa", Version.EMPTY) .setKey(ModuleKey.ROOT) .addDep("bbb1", createModuleKey("bbb", "1.0")) .addDep("bbb2", createModuleKey("bbb", "2.0")) .buildEntry()) - .put(ModuleBuilder.create("bbb", "1.0").buildEntry()) - .put(ModuleBuilder.create("bbb", "2.0").buildEntry()) + .put(InterimModuleBuilder.create("bbb", "1.0").buildEntry()) + .put(InterimModuleBuilder.create("bbb", "2.0").buildEntry()) .buildOrThrow(); ImmutableMap overrides = ImmutableMap.of( @@ -387,16 +768,16 @@ public void multipleVersionOverride_fork_goodCase() throws Exception { MultipleVersionOverride.create( ImmutableList.of(Version.parse("1.0"), Version.parse("2.0")), "")); - BazelModuleResolutionValue selectionResult = Selection.run(depGraph, overrides); + Selection.Result selectionResult = Selection.run(depGraph, overrides); assertThat(selectionResult.getResolvedDepGraph().entrySet()) .containsExactly( - ModuleBuilder.create("aaa", Version.EMPTY) + InterimModuleBuilder.create("aaa", Version.EMPTY) .setKey(ModuleKey.ROOT) .addDep("bbb1", createModuleKey("bbb", "1.0")) .addDep("bbb2", createModuleKey("bbb", "2.0")) .buildEntry(), - ModuleBuilder.create("bbb", "1.0").buildEntry(), - ModuleBuilder.create("bbb", "2.0").buildEntry()) + InterimModuleBuilder.create("bbb", "1.0").buildEntry(), + InterimModuleBuilder.create("bbb", "2.0").buildEntry()) .inOrder(); assertThat(selectionResult.getUnprunedDepGraph()) @@ -405,18 +786,18 @@ public void multipleVersionOverride_fork_goodCase() throws Exception { @Test public void multipleVersionOverride_fork_sameVersionUsedTwice() throws Exception { - ImmutableMap depGraph = - ImmutableMap.builder() + ImmutableMap depGraph = + ImmutableMap.builder() .put( - ModuleBuilder.create("aaa", Version.EMPTY) + InterimModuleBuilder.create("aaa", Version.EMPTY) .setKey(ModuleKey.ROOT) .addDep("bbb1", createModuleKey("bbb", "1.0")) .addDep("bbb2", createModuleKey("bbb", "1.3")) .addDep("bbb3", createModuleKey("bbb", "1.5")) .buildEntry()) - .put(ModuleBuilder.create("bbb", "1.0").buildEntry()) - .put(ModuleBuilder.create("bbb", "1.3").buildEntry()) - .put(ModuleBuilder.create("bbb", "1.5").buildEntry()) + .put(InterimModuleBuilder.create("bbb", "1.0").buildEntry()) + .put(InterimModuleBuilder.create("bbb", "1.3").buildEntry()) + .put(InterimModuleBuilder.create("bbb", "1.5").buildEntry()) .buildOrThrow(); ImmutableMap overrides = ImmutableMap.of( @@ -435,24 +816,24 @@ public void multipleVersionOverride_fork_sameVersionUsedTwice() throws Exception @Test public void multipleVersionOverride_diamond_differentCompatibilityLevels() throws Exception { - ImmutableMap depGraph = - ImmutableMap.builder() + ImmutableMap depGraph = + ImmutableMap.builder() .put( - ModuleBuilder.create("aaa", Version.EMPTY) + InterimModuleBuilder.create("aaa", Version.EMPTY) .setKey(ModuleKey.ROOT) .addDep("bbb_from_aaa", createModuleKey("bbb", "1.0")) .addDep("ccc_from_aaa", createModuleKey("ccc", "2.0")) .buildEntry()) .put( - ModuleBuilder.create("bbb", "1.0") + InterimModuleBuilder.create("bbb", "1.0") .addDep("ddd_from_bbb", createModuleKey("ddd", "1.0")) .buildEntry()) .put( - ModuleBuilder.create("ccc", "2.0") + InterimModuleBuilder.create("ccc", "2.0") .addDep("ddd_from_ccc", createModuleKey("ddd", "2.0")) .buildEntry()) - .put(ModuleBuilder.create("ddd", "1.0", 1).buildEntry()) - .put(ModuleBuilder.create("ddd", "2.0", 2).buildEntry()) + .put(InterimModuleBuilder.create("ddd", "1.0", 1).buildEntry()) + .put(InterimModuleBuilder.create("ddd", "2.0", 2).buildEntry()) .buildOrThrow(); ImmutableMap overrides = ImmutableMap.of( @@ -460,22 +841,22 @@ public void multipleVersionOverride_diamond_differentCompatibilityLevels() throw MultipleVersionOverride.create( ImmutableList.of(Version.parse("1.0"), Version.parse("2.0")), "")); - BazelModuleResolutionValue selectionResult = Selection.run(depGraph, overrides); + Selection.Result selectionResult = Selection.run(depGraph, overrides); assertThat(selectionResult.getResolvedDepGraph().entrySet()) .containsExactly( - ModuleBuilder.create("aaa", Version.EMPTY) + InterimModuleBuilder.create("aaa", Version.EMPTY) .setKey(ModuleKey.ROOT) .addDep("bbb_from_aaa", createModuleKey("bbb", "1.0")) .addDep("ccc_from_aaa", createModuleKey("ccc", "2.0")) .buildEntry(), - ModuleBuilder.create("bbb", "1.0") + InterimModuleBuilder.create("bbb", "1.0") .addDep("ddd_from_bbb", createModuleKey("ddd", "1.0")) .buildEntry(), - ModuleBuilder.create("ccc", "2.0") + InterimModuleBuilder.create("ccc", "2.0") .addDep("ddd_from_ccc", createModuleKey("ddd", "2.0")) .buildEntry(), - ModuleBuilder.create("ddd", "1.0", 1).buildEntry(), - ModuleBuilder.create("ddd", "2.0", 2).buildEntry()) + InterimModuleBuilder.create("ddd", "1.0", 1).buildEntry(), + InterimModuleBuilder.create("ddd", "2.0", 2).buildEntry()) .inOrder(); assertThat(selectionResult.getUnprunedDepGraph()) @@ -484,24 +865,24 @@ public void multipleVersionOverride_diamond_differentCompatibilityLevels() throw @Test public void multipleVersionOverride_diamond_sameCompatibilityLevel() throws Exception { - ImmutableMap depGraph = - ImmutableMap.builder() + ImmutableMap depGraph = + ImmutableMap.builder() .put( - ModuleBuilder.create("aaa", Version.EMPTY) + InterimModuleBuilder.create("aaa", Version.EMPTY) .setKey(ModuleKey.ROOT) .addDep("bbb_from_aaa", createModuleKey("bbb", "1.0")) .addDep("ccc_from_aaa", createModuleKey("ccc", "2.0")) .buildEntry()) .put( - ModuleBuilder.create("bbb", "1.0") + InterimModuleBuilder.create("bbb", "1.0") .addDep("ddd_from_bbb", createModuleKey("ddd", "1.0")) .buildEntry()) .put( - ModuleBuilder.create("ccc", "2.0") + InterimModuleBuilder.create("ccc", "2.0") .addDep("ddd_from_ccc", createModuleKey("ddd", "2.0")) .buildEntry()) - .put(ModuleBuilder.create("ddd", "1.0").buildEntry()) - .put(ModuleBuilder.create("ddd", "2.0").buildEntry()) + .put(InterimModuleBuilder.create("ddd", "1.0").buildEntry()) + .put(InterimModuleBuilder.create("ddd", "2.0").buildEntry()) .buildOrThrow(); ImmutableMap overrides = ImmutableMap.of( @@ -509,22 +890,22 @@ public void multipleVersionOverride_diamond_sameCompatibilityLevel() throws Exce MultipleVersionOverride.create( ImmutableList.of(Version.parse("1.0"), Version.parse("2.0")), "")); - BazelModuleResolutionValue selectionResult = Selection.run(depGraph, overrides); + Selection.Result selectionResult = Selection.run(depGraph, overrides); assertThat(selectionResult.getResolvedDepGraph().entrySet()) .containsExactly( - ModuleBuilder.create("aaa", Version.EMPTY) + InterimModuleBuilder.create("aaa", Version.EMPTY) .setKey(ModuleKey.ROOT) .addDep("bbb_from_aaa", createModuleKey("bbb", "1.0")) .addDep("ccc_from_aaa", createModuleKey("ccc", "2.0")) .buildEntry(), - ModuleBuilder.create("bbb", "1.0") + InterimModuleBuilder.create("bbb", "1.0") .addDep("ddd_from_bbb", createModuleKey("ddd", "1.0")) .buildEntry(), - ModuleBuilder.create("ccc", "2.0") + InterimModuleBuilder.create("ccc", "2.0") .addDep("ddd_from_ccc", createModuleKey("ddd", "2.0")) .buildEntry(), - ModuleBuilder.create("ddd", "1.0").buildEntry(), - ModuleBuilder.create("ddd", "2.0").buildEntry()) + InterimModuleBuilder.create("ddd", "1.0").buildEntry(), + InterimModuleBuilder.create("ddd", "2.0").buildEntry()) .inOrder(); assertThat(selectionResult.getUnprunedDepGraph()) @@ -538,10 +919,10 @@ public void multipleVersionOverride_diamond_snappingToNextHighestVersion() throw // \-> bbb3@1.0 -> ccc@1.5 // \-> bbb4@1.0 -> ccc@1.7 [allowed] // \-> bbb5@1.0 -> ccc@2.0 [allowed] - ImmutableMap depGraph = - ImmutableMap.builder() + ImmutableMap depGraph = + ImmutableMap.builder() .put( - ModuleBuilder.create("aaa", Version.EMPTY) + InterimModuleBuilder.create("aaa", Version.EMPTY) .setKey(ModuleKey.ROOT) .addDep("bbb1", createModuleKey("bbb1", "1.0")) .addDep("bbb2", createModuleKey("bbb2", "1.0")) @@ -550,30 +931,30 @@ public void multipleVersionOverride_diamond_snappingToNextHighestVersion() throw .addDep("bbb5", createModuleKey("bbb5", "1.0")) .buildEntry()) .put( - ModuleBuilder.create("bbb1", "1.0") + InterimModuleBuilder.create("bbb1", "1.0") .addDep("ccc", createModuleKey("ccc", "1.0")) .buildEntry()) .put( - ModuleBuilder.create("bbb2", "1.0") + InterimModuleBuilder.create("bbb2", "1.0") .addDep("ccc", createModuleKey("ccc", "1.3")) .buildEntry()) .put( - ModuleBuilder.create("bbb3", "1.0") + InterimModuleBuilder.create("bbb3", "1.0") .addDep("ccc", createModuleKey("ccc", "1.5")) .buildEntry()) .put( - ModuleBuilder.create("bbb4", "1.0") + InterimModuleBuilder.create("bbb4", "1.0") .addDep("ccc", createModuleKey("ccc", "1.7")) .buildEntry()) .put( - ModuleBuilder.create("bbb5", "1.0") + InterimModuleBuilder.create("bbb5", "1.0") .addDep("ccc", createModuleKey("ccc", "2.0")) .buildEntry()) - .put(ModuleBuilder.create("ccc", "1.0", 1).buildEntry()) - .put(ModuleBuilder.create("ccc", "1.3", 1).buildEntry()) - .put(ModuleBuilder.create("ccc", "1.5", 1).buildEntry()) - .put(ModuleBuilder.create("ccc", "1.7", 1).buildEntry()) - .put(ModuleBuilder.create("ccc", "2.0", 2).buildEntry()) + .put(InterimModuleBuilder.create("ccc", "1.0", 1).buildEntry()) + .put(InterimModuleBuilder.create("ccc", "1.3", 1).buildEntry()) + .put(InterimModuleBuilder.create("ccc", "1.5", 1).buildEntry()) + .put(InterimModuleBuilder.create("ccc", "1.7", 1).buildEntry()) + .put(InterimModuleBuilder.create("ccc", "2.0", 2).buildEntry()) .buildOrThrow(); ImmutableMap overrides = ImmutableMap.of( @@ -587,10 +968,10 @@ public void multipleVersionOverride_diamond_snappingToNextHighestVersion() throw // \-> bbb3@1.0 -> ccc@1.7 [originally ccc@1.5] // \-> bbb4@1.0 -> ccc@1.7 [allowed] // \-> bbb5@1.0 -> ccc@2.0 [allowed] - BazelModuleResolutionValue selectionResult = Selection.run(depGraph, overrides); + Selection.Result selectionResult = Selection.run(depGraph, overrides); assertThat(selectionResult.getResolvedDepGraph().entrySet()) .containsExactly( - ModuleBuilder.create("aaa", Version.EMPTY) + InterimModuleBuilder.create("aaa", Version.EMPTY) .setKey(ModuleKey.ROOT) .addDep("bbb1", createModuleKey("bbb1", "1.0")) .addDep("bbb2", createModuleKey("bbb2", "1.0")) @@ -598,31 +979,31 @@ public void multipleVersionOverride_diamond_snappingToNextHighestVersion() throw .addDep("bbb4", createModuleKey("bbb4", "1.0")) .addDep("bbb5", createModuleKey("bbb5", "1.0")) .buildEntry(), - ModuleBuilder.create("bbb1", "1.0") + InterimModuleBuilder.create("bbb1", "1.0") .addDep("ccc", createModuleKey("ccc", "1.3")) .addOriginalDep("ccc", createModuleKey("ccc", "1.0")) .buildEntry(), - ModuleBuilder.create("bbb2", "1.0") + InterimModuleBuilder.create("bbb2", "1.0") .addDep("ccc", createModuleKey("ccc", "1.3")) .buildEntry(), - ModuleBuilder.create("bbb3", "1.0") + InterimModuleBuilder.create("bbb3", "1.0") .addDep("ccc", createModuleKey("ccc", "1.7")) .addOriginalDep("ccc", createModuleKey("ccc", "1.5")) .buildEntry(), - ModuleBuilder.create("bbb4", "1.0") + InterimModuleBuilder.create("bbb4", "1.0") .addDep("ccc", createModuleKey("ccc", "1.7")) .buildEntry(), - ModuleBuilder.create("bbb5", "1.0") + InterimModuleBuilder.create("bbb5", "1.0") .addDep("ccc", createModuleKey("ccc", "2.0")) .buildEntry(), - ModuleBuilder.create("ccc", "1.3", 1).buildEntry(), - ModuleBuilder.create("ccc", "1.7", 1).buildEntry(), - ModuleBuilder.create("ccc", "2.0", 2).buildEntry()) + InterimModuleBuilder.create("ccc", "1.3", 1).buildEntry(), + InterimModuleBuilder.create("ccc", "1.7", 1).buildEntry(), + InterimModuleBuilder.create("ccc", "2.0", 2).buildEntry()) .inOrder(); assertThat(selectionResult.getUnprunedDepGraph().entrySet()) .containsExactly( - ModuleBuilder.create("aaa", Version.EMPTY) + InterimModuleBuilder.create("aaa", Version.EMPTY) .setKey(ModuleKey.ROOT) .addDep("bbb1", createModuleKey("bbb1", "1.0")) .addDep("bbb2", createModuleKey("bbb2", "1.0")) @@ -630,28 +1011,28 @@ public void multipleVersionOverride_diamond_snappingToNextHighestVersion() throw .addDep("bbb4", createModuleKey("bbb4", "1.0")) .addDep("bbb5", createModuleKey("bbb5", "1.0")) .buildEntry(), - ModuleBuilder.create("bbb1", "1.0") + InterimModuleBuilder.create("bbb1", "1.0") .addDep("ccc", createModuleKey("ccc", "1.3")) .addOriginalDep("ccc", createModuleKey("ccc", "1.0")) .buildEntry(), - ModuleBuilder.create("bbb2", "1.0") + InterimModuleBuilder.create("bbb2", "1.0") .addDep("ccc", createModuleKey("ccc", "1.3")) .buildEntry(), - ModuleBuilder.create("bbb3", "1.0") + InterimModuleBuilder.create("bbb3", "1.0") .addDep("ccc", createModuleKey("ccc", "1.7")) .addOriginalDep("ccc", createModuleKey("ccc", "1.5")) .buildEntry(), - ModuleBuilder.create("bbb4", "1.0") + InterimModuleBuilder.create("bbb4", "1.0") .addDep("ccc", createModuleKey("ccc", "1.7")) .buildEntry(), - ModuleBuilder.create("bbb5", "1.0") + InterimModuleBuilder.create("bbb5", "1.0") .addDep("ccc", createModuleKey("ccc", "2.0")) .buildEntry(), - ModuleBuilder.create("ccc", "1.0", 1).buildEntry(), - ModuleBuilder.create("ccc", "1.3", 1).buildEntry(), - ModuleBuilder.create("ccc", "1.5", 1).buildEntry(), - ModuleBuilder.create("ccc", "1.7", 1).buildEntry(), - ModuleBuilder.create("ccc", "2.0", 2).buildEntry()); + InterimModuleBuilder.create("ccc", "1.0", 1).buildEntry(), + InterimModuleBuilder.create("ccc", "1.3", 1).buildEntry(), + InterimModuleBuilder.create("ccc", "1.5", 1).buildEntry(), + InterimModuleBuilder.create("ccc", "1.7", 1).buildEntry(), + InterimModuleBuilder.create("ccc", "2.0", 2).buildEntry()); } @Test @@ -659,30 +1040,30 @@ public void multipleVersionOverride_diamond_dontSnapToDifferentCompatibility() t // aaa --> bbb1@1.0 -> ccc@1.0 [allowed] // \-> bbb2@1.0 -> ccc@1.7 // \-> bbb3@1.0 -> ccc@2.0 [allowed] - ImmutableMap depGraph = - ImmutableMap.builder() + ImmutableMap depGraph = + ImmutableMap.builder() .put( - ModuleBuilder.create("aaa", Version.EMPTY) + InterimModuleBuilder.create("aaa", Version.EMPTY) .setKey(ModuleKey.ROOT) .addDep("bbb1", createModuleKey("bbb1", "1.0")) .addDep("bbb2", createModuleKey("bbb2", "1.0")) .addDep("bbb3", createModuleKey("bbb3", "1.0")) .buildEntry()) .put( - ModuleBuilder.create("bbb1", "1.0") + InterimModuleBuilder.create("bbb1", "1.0") .addDep("ccc", createModuleKey("ccc", "1.0")) .buildEntry()) .put( - ModuleBuilder.create("bbb2", "1.0") + InterimModuleBuilder.create("bbb2", "1.0") .addDep("ccc", createModuleKey("ccc", "1.7")) .buildEntry()) .put( - ModuleBuilder.create("bbb3", "1.0") + InterimModuleBuilder.create("bbb3", "1.0") .addDep("ccc", createModuleKey("ccc", "2.0")) .buildEntry()) - .put(ModuleBuilder.create("ccc", "1.0", 1).buildEntry()) - .put(ModuleBuilder.create("ccc", "1.7", 1).buildEntry()) - .put(ModuleBuilder.create("ccc", "2.0", 2).buildEntry()) + .put(InterimModuleBuilder.create("ccc", "1.0", 1).buildEntry()) + .put(InterimModuleBuilder.create("ccc", "1.7", 1).buildEntry()) + .put(InterimModuleBuilder.create("ccc", "2.0", 2).buildEntry()) .buildOrThrow(); ImmutableMap overrides = ImmutableMap.of( @@ -704,30 +1085,30 @@ public void multipleVersionOverride_diamond_unknownCompatibility() throws Except // aaa --> bbb1@1.0 -> ccc@1.0 [allowed] // \-> bbb2@1.0 -> ccc@2.0 [allowed] // \-> bbb3@1.0 -> ccc@3.0 - ImmutableMap depGraph = - ImmutableMap.builder() + ImmutableMap depGraph = + ImmutableMap.builder() .put( - ModuleBuilder.create("aaa", Version.EMPTY) + InterimModuleBuilder.create("aaa", Version.EMPTY) .setKey(ModuleKey.ROOT) .addDep("bbb1", createModuleKey("bbb1", "1.0")) .addDep("bbb2", createModuleKey("bbb2", "1.0")) .addDep("bbb3", createModuleKey("bbb3", "1.0")) .buildEntry()) .put( - ModuleBuilder.create("bbb1", "1.0") + InterimModuleBuilder.create("bbb1", "1.0") .addDep("ccc", createModuleKey("ccc", "1.0")) .buildEntry()) .put( - ModuleBuilder.create("bbb2", "1.0") + InterimModuleBuilder.create("bbb2", "1.0") .addDep("ccc", createModuleKey("ccc", "2.0")) .buildEntry()) .put( - ModuleBuilder.create("bbb3", "1.0") + InterimModuleBuilder.create("bbb3", "1.0") .addDep("ccc", createModuleKey("ccc", "3.0")) .buildEntry()) - .put(ModuleBuilder.create("ccc", "1.0", 1).buildEntry()) - .put(ModuleBuilder.create("ccc", "2.0", 2).buildEntry()) - .put(ModuleBuilder.create("ccc", "3.0", 3).buildEntry()) + .put(InterimModuleBuilder.create("ccc", "1.0", 1).buildEntry()) + .put(InterimModuleBuilder.create("ccc", "2.0", 2).buildEntry()) + .put(InterimModuleBuilder.create("ccc", "3.0", 3).buildEntry()) .buildOrThrow(); ImmutableMap overrides = ImmutableMap.of( @@ -752,10 +1133,10 @@ public void multipleVersionOverride_diamond_badVersionsAreOkayIfUnreferenced() t // \-> bbb3@1.0 --> ccc@2.0 [allowed] // \ \-> bbb4@1.1 // \-> bbb4@1.0 --> ccc@3.0 - ImmutableMap depGraph = - ImmutableMap.builder() + ImmutableMap depGraph = + ImmutableMap.builder() .put( - ModuleBuilder.create("aaa", Version.EMPTY) + InterimModuleBuilder.create("aaa", Version.EMPTY) .setKey(ModuleKey.ROOT) .addDep("bbb1", createModuleKey("bbb1", "1.0")) .addDep("bbb2", createModuleKey("bbb2", "1.0")) @@ -763,29 +1144,29 @@ public void multipleVersionOverride_diamond_badVersionsAreOkayIfUnreferenced() t .addDep("bbb4", createModuleKey("bbb4", "1.0")) .buildEntry()) .put( - ModuleBuilder.create("bbb1", "1.0") + InterimModuleBuilder.create("bbb1", "1.0") .addDep("ccc", createModuleKey("ccc", "1.0")) .addDep("bbb2", createModuleKey("bbb2", "1.1")) .buildEntry()) .put( - ModuleBuilder.create("bbb2", "1.0") + InterimModuleBuilder.create("bbb2", "1.0") .addDep("ccc", createModuleKey("ccc", "1.5")) .buildEntry()) - .put(ModuleBuilder.create("bbb2", "1.1").buildEntry()) + .put(InterimModuleBuilder.create("bbb2", "1.1").buildEntry()) .put( - ModuleBuilder.create("bbb3", "1.0") + InterimModuleBuilder.create("bbb3", "1.0") .addDep("ccc", createModuleKey("ccc", "2.0")) .addDep("bbb4", createModuleKey("bbb4", "1.1")) .buildEntry()) .put( - ModuleBuilder.create("bbb4", "1.0") + InterimModuleBuilder.create("bbb4", "1.0") .addDep("ccc", createModuleKey("ccc", "3.0")) .buildEntry()) - .put(ModuleBuilder.create("bbb4", "1.1").buildEntry()) - .put(ModuleBuilder.create("ccc", "1.0", 1).buildEntry()) - .put(ModuleBuilder.create("ccc", "1.5", 1).buildEntry()) - .put(ModuleBuilder.create("ccc", "2.0", 2).buildEntry()) - .put(ModuleBuilder.create("ccc", "3.0", 3).buildEntry()) + .put(InterimModuleBuilder.create("bbb4", "1.1").buildEntry()) + .put(InterimModuleBuilder.create("ccc", "1.0", 1).buildEntry()) + .put(InterimModuleBuilder.create("ccc", "1.5", 1).buildEntry()) + .put(InterimModuleBuilder.create("ccc", "2.0", 2).buildEntry()) + .put(InterimModuleBuilder.create("ccc", "3.0", 3).buildEntry()) .buildOrThrow(); ImmutableMap overrides = ImmutableMap.of( @@ -800,10 +1181,10 @@ public void multipleVersionOverride_diamond_badVersionsAreOkayIfUnreferenced() t // \ \-> bbb4@1.1 // \-> bbb4@1.1 // ccc@1.5 and ccc@3.0, the versions violating the allowlist, are gone. - BazelModuleResolutionValue selectionResult = Selection.run(depGraph, overrides); + Selection.Result selectionResult = Selection.run(depGraph, overrides); assertThat(selectionResult.getResolvedDepGraph().entrySet()) .containsExactly( - ModuleBuilder.create("aaa", Version.EMPTY) + InterimModuleBuilder.create("aaa", Version.EMPTY) .setKey(ModuleKey.ROOT) .addDep("bbb1", createModuleKey("bbb1", "1.0")) .addDep("bbb2", createModuleKey("bbb2", "1.1")) @@ -812,23 +1193,23 @@ public void multipleVersionOverride_diamond_badVersionsAreOkayIfUnreferenced() t .addDep("bbb4", createModuleKey("bbb4", "1.1")) .addOriginalDep("bbb4", createModuleKey("bbb4", "1.0")) .buildEntry(), - ModuleBuilder.create("bbb1", "1.0") + InterimModuleBuilder.create("bbb1", "1.0") .addDep("ccc", createModuleKey("ccc", "1.0")) .addDep("bbb2", createModuleKey("bbb2", "1.1")) .buildEntry(), - ModuleBuilder.create("bbb2", "1.1").buildEntry(), - ModuleBuilder.create("bbb3", "1.0") + InterimModuleBuilder.create("bbb2", "1.1").buildEntry(), + InterimModuleBuilder.create("bbb3", "1.0") .addDep("ccc", createModuleKey("ccc", "2.0")) .addDep("bbb4", createModuleKey("bbb4", "1.1")) .buildEntry(), - ModuleBuilder.create("bbb4", "1.1").buildEntry(), - ModuleBuilder.create("ccc", "1.0", 1).buildEntry(), - ModuleBuilder.create("ccc", "2.0", 2).buildEntry()) + InterimModuleBuilder.create("bbb4", "1.1").buildEntry(), + InterimModuleBuilder.create("ccc", "1.0", 1).buildEntry(), + InterimModuleBuilder.create("ccc", "2.0", 2).buildEntry()) .inOrder(); assertThat(selectionResult.getUnprunedDepGraph().entrySet()) .containsExactly( - ModuleBuilder.create("aaa", Version.EMPTY) + InterimModuleBuilder.create("aaa", Version.EMPTY) .setKey(ModuleKey.ROOT) .addDep("bbb1", createModuleKey("bbb1", "1.0")) .addDep("bbb2", createModuleKey("bbb2", "1.1")) @@ -837,25 +1218,25 @@ public void multipleVersionOverride_diamond_badVersionsAreOkayIfUnreferenced() t .addDep("bbb4", createModuleKey("bbb4", "1.1")) .addOriginalDep("bbb4", createModuleKey("bbb4", "1.0")) .buildEntry(), - ModuleBuilder.create("bbb1", "1.0") + InterimModuleBuilder.create("bbb1", "1.0") .addDep("ccc", createModuleKey("ccc", "1.0")) .addDep("bbb2", createModuleKey("bbb2", "1.1")) .buildEntry(), - ModuleBuilder.create("bbb2", "1.0") + InterimModuleBuilder.create("bbb2", "1.0") .addDep("ccc", createModuleKey("ccc", "1.5")) .buildEntry(), - ModuleBuilder.create("bbb2", "1.1").buildEntry(), - ModuleBuilder.create("bbb3", "1.0") + InterimModuleBuilder.create("bbb2", "1.1").buildEntry(), + InterimModuleBuilder.create("bbb3", "1.0") .addDep("ccc", createModuleKey("ccc", "2.0")) .addDep("bbb4", createModuleKey("bbb4", "1.1")) .buildEntry(), - ModuleBuilder.create("bbb4", "1.0") + InterimModuleBuilder.create("bbb4", "1.0") .addDep("ccc", createModuleKey("ccc", "3.0")) .buildEntry(), - ModuleBuilder.create("bbb4", "1.1").buildEntry(), - ModuleBuilder.create("ccc", "1.0", 1).buildEntry(), - ModuleBuilder.create("ccc", "1.5", 1).buildEntry(), - ModuleBuilder.create("ccc", "2.0", 2).buildEntry(), - ModuleBuilder.create("ccc", "3.0", 3).buildEntry()); + InterimModuleBuilder.create("bbb4", "1.1").buildEntry(), + InterimModuleBuilder.create("ccc", "1.0", 1).buildEntry(), + InterimModuleBuilder.create("ccc", "1.5", 1).buildEntry(), + InterimModuleBuilder.create("ccc", "2.0", 2).buildEntry(), + InterimModuleBuilder.create("ccc", "3.0", 3).buildEntry()); } } diff --git a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/StarlarkBazelModuleTest.java b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/StarlarkBazelModuleTest.java index 3f655551f24383..9dbb6904cba911 100644 --- a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/StarlarkBazelModuleTest.java +++ b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/StarlarkBazelModuleTest.java @@ -16,6 +16,7 @@ package com.google.devtools.build.lib.bazel.bzlmod; import static com.google.common.truth.Truth.assertThat; +import static com.google.devtools.build.lib.bazel.bzlmod.BzlmodTestUtil.buildModule; import static com.google.devtools.build.lib.bazel.bzlmod.BzlmodTestUtil.buildTag; import static com.google.devtools.build.lib.bazel.bzlmod.BzlmodTestUtil.createModuleKey; import static com.google.devtools.build.lib.bazel.bzlmod.BzlmodTestUtil.createTagClass; @@ -84,9 +85,7 @@ public void basic() throws Exception { .build()))) .build(); Module module = - Module.builder() - .setName("foo") - .setVersion(Version.parse("1.0")) + buildModule("foo", "1.0") .setKey(createModuleKey("foo", "")) .addDep("bar", createModuleKey("bar", "2.0")) .build(); @@ -128,12 +127,7 @@ public void unknownTagClass() throws Exception { ModuleExtensionUsage usage = getBaseUsageBuilder().addTag(buildTag("blep").build()).build(); ModuleExtension extension = getBaseExtensionBuilder().setTagClasses(ImmutableMap.of("dep", createTagClass())).build(); - Module module = - Module.builder() - .setName("foo") - .setVersion(Version.parse("1.0")) - .setKey(createModuleKey("foo", "")) - .build(); + Module module = buildModule("foo", "1.0").setKey(createModuleKey("foo", "")).build(); AbridgedModule abridgedModule = AbridgedModule.from(module); ExternalDepsException e =