Skip to content

Commit

Permalink
bzlmod: multiple_version_override
Browse files Browse the repository at this point in the history
(#13316)

Adds support for the "diamond" case of multiple_version_override: that is, multiple different modules can depend on different versions of the same module.

The "fork" case is still unchecked (so if you use `bazel_dep(B@1.0, repo_name='B1'); bazel_dep(B@2.0, repo_name='B2')` it won't throw an error). The check will be added in a next CL as I figured this one is already huge enough :)

PiperOrigin-RevId: 384894458
  • Loading branch information
Wyverald authored and Copybara-Service committed Jul 15, 2021
1 parent d382e91 commit af17598
Show file tree
Hide file tree
Showing 8 changed files with 1,014 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ java_library(
"ModuleFileGlobals.java",
"ModuleFileValue.java",
"ModuleOverride.java",
"MultipleVersionOverride.java",
"NonRegistryOverride.java",
"RegistryOverride.java",
"SelectionFunction.java",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.lib.bazel.bzlmod.ModuleFileFunction.ModuleFileFunctionException;
import com.google.devtools.build.lib.bazel.bzlmod.Version.ParseException;
import com.google.devtools.build.lib.starlarkbuildapi.repository.ModuleFileGlobalsApi;
import java.util.HashMap;
Expand All @@ -28,7 +27,7 @@
import net.starlark.java.eval.StarlarkInt;

/** Implementation of the global functions available to a module file. */
public class ModuleFileGlobals implements ModuleFileGlobalsApi<ModuleFileFunctionException> {
public class ModuleFileGlobals implements ModuleFileGlobalsApi {

private boolean moduleCalled = false;
private final Module.Builder module = Module.builder();
Expand Down Expand Up @@ -120,6 +119,24 @@ public void singleVersionOverride(
patchStrip.toInt("single_version_override.patch_strip")));
}

@Override
public void multipleVersionOverride(String moduleName, Iterable<?> versions, String registry)
throws EvalException {
ImmutableList.Builder<Version> parsedVersionsBuilder = new ImmutableList.Builder<>();
try {
for (String version : checkAllStrings(versions, "versions")) {
parsedVersionsBuilder.add(Version.parse(version));
}
} catch (ParseException e) {
throw new EvalException("Invalid version in multiple_version_override()", e);
}
ImmutableList<Version> parsedVersions = parsedVersionsBuilder.build();
if (parsedVersions.size() < 2) {
throw new EvalException("multiple_version_override() must specify at least 2 versions");
}
addOverride(moduleName, MultipleVersionOverride.create(parsedVersions, registry));
}

@Override
public void archiveOverride(
String moduleName,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright 2021 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;

/**
* Specifies that the module should still come from a registry, but multiple versions of it should
* be allowed to coexist.
*/
@AutoValue
public abstract class MultipleVersionOverride implements RegistryOverride {

public static MultipleVersionOverride create(ImmutableList<Version> versions, String registry) {
return new AutoValue_MultipleVersionOverride(versions, registry);
}

/** The versions of this module that should coexist. */
public abstract ImmutableList<Version> getVersions();

@Override
public abstract String getRegistry();
}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -180,11 +180,6 @@ public int compareTo(Version o) {
return Objects.compare(this, o, COMPARATOR);
}

/** Returns the higher of two versions. */
public static Version max(@Nullable Version a, @Nullable Version b) {
return Objects.compare(a, b, COMPARATOR) >= 0 ? a : b;
}

@Override
public final String toString() {
return getOriginal();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

/** A collection of global Starlark build API functions that apply to MODULE.bazel files. */
@DocumentMethods
public interface ModuleFileGlobalsApi<ModuleFileFunctionExceptionT extends Exception> {
public interface ModuleFileGlobalsApi {

@StarlarkMethod(
name = "module",
Expand Down Expand Up @@ -55,16 +55,17 @@ public interface ModuleFileGlobalsApi<ModuleFileFunctionExceptionT extends Excep
defaultValue = "''"),
@Param(
name = "compatibility_level",
// TODO(wyv): See X for more details; also mention multiple_version_override
// TODO(wyv): See X for more details
doc =
"The compatibility level of the module; this should be changed every time a major"
+ " incompatible change is introduced. This is essentially the \"major"
+ " version\" of the module in terms of SemVer, except that it's not embedded"
+ " in the version string itself, but exists as a separate field. Modules with"
+ " different compatibility levels participate in version resolution as if"
+ " they're modules with different names, but the final dependency graph cannot"
+ " contain multiple modules with the same name but different compatibility"
+ " levels.",
+ " they're modules with different names, but the final dependency graph"
+ " cannot contain multiple modules with the same name but different"
+ " compatibility levels (unless <code>multiple_version_override</code> is in"
+ " effect; see there for more details).",
named = true,
positional = false,
defaultValue = "0"),
Expand Down Expand Up @@ -115,7 +116,7 @@ void bazelDep(String name, String version, String repoName)
@Param(
name = "version",
doc =
"Override the declared version of this module in the dependency graph. In other"
"Overrides the declared version of this module in the dependency graph. In other"
+ " words, this module will be \"pinned\" to this override version. This"
+ " attribute can be omitted if all one wants to override is the registry or"
+ " the patches. ",
Expand Down Expand Up @@ -155,6 +156,44 @@ void singleVersionOverride(
StarlarkInt patchStrip)
throws EvalException;

@StarlarkMethod(
name = "multiple_version_override",
doc =
"Specifies that a dependency should still come from a registry, but multiple versions of"
+ " it should be allowed to coexist. This directive can only be used by the root"
+ " module; in other words, if a module specifies any overrides, it cannot be used"
+ " as a dependency by others.",
parameters = {
@Param(
name = "module_name",
doc = "The name of the Bazel module dependency to apply this override to.",
named = true,
positional = false),
@Param(
name = "versions",
// TODO(wyv): See X for more details
doc =
"Explicitly specifies the versions allowed to coexist. These versions must already"
+ " be present in the dependency graph pre-selection. Dependencies on this"
+ " module will be \"upgraded\" to the nearest higher allowed version at the"
+ " same compatibility level, whereas dependencies that have a higher version"
+ " than any allowed versions at the same compatibility level will cause an"
+ " error.",
allowedTypes = {@ParamType(type = Iterable.class, generic1 = String.class)},
named = true,
positional = false),
@Param(
name = "registry",
doc =
"Overrides the registry for this module; instead of finding this module from the"
+ " default list of registries, the given registry should be used.",
named = true,
positional = false,
defaultValue = "''"),
})
void multipleVersionOverride(String moduleName, Iterable<?> versions, String registry)
throws EvalException;

@StarlarkMethod(
name = "archive_override",
doc =
Expand Down Expand Up @@ -213,7 +252,7 @@ void archiveOverride(
String stripPrefix,
Iterable<?> patches,
StarlarkInt patchStrip)
throws EvalException, ModuleFileFunctionExceptionT;
throws EvalException;

@StarlarkMethod(
name = "git_override",
Expand Down Expand Up @@ -257,7 +296,7 @@ void archiveOverride(
})
void gitOverride(
String moduleName, String remote, String commit, Iterable<?> patches, StarlarkInt patchStrip)
throws EvalException, ModuleFileFunctionExceptionT;
throws EvalException;

@StarlarkMethod(
name = "local_path_override",
Expand All @@ -278,6 +317,4 @@ void gitOverride(
positional = false),
})
void localPathOverride(String moduleName, String path) throws EvalException;

// TODO(wyv): multiple_version_override
}
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,8 @@ public void testRootModule() throws Exception {
"bazel_dep(name='B',version='1.0')",
"bazel_dep(name='C',version='2.0',repo_name='see')",
"single_version_override(module_name='D',version='18')",
"local_path_override(module_name='E',path='somewhere/else')");
"local_path_override(module_name='E',path='somewhere/else')",
"multiple_version_override(module_name='F',versions=['1.0','2.0'])");
FakeRegistry registry = registryFactory.newFakeRegistry();
ModuleFileFunction.REGISTRIES.set(differencer, ImmutableList.of(registry.getUrl()));

Expand All @@ -195,7 +196,10 @@ public void testRootModule() throws Exception {
assertThat(moduleFileValue.getOverrides())
.containsExactly(
"D", SingleVersionOverride.create(Version.parse("18"), "", ImmutableList.of(), 0),
"E", LocalPathOverride.create("somewhere/else"));
"E", LocalPathOverride.create("somewhere/else"),
"F",
MultipleVersionOverride.create(
ImmutableList.of(Version.parse("1.0"), Version.parse("2.0")), ""));
}

@Test
Expand Down

0 comments on commit af17598

Please sign in to comment.