blob: 807c7118c4a3a8940e1d10eed6a399823adad55f [file] [log] [blame]
// 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;
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 java.util.Map;
import java.util.Optional;
import java.util.function.UnaryOperator;
import javax.annotation.Nullable;
/**
* Represents a node in the external dependency graph.
*
* <p>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).
*/
@AutoValue
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:
*
* <ul>
* <li>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.
* <li>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.
* </ul>
*/
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<String> 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<String> 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<String> 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.
*/
public abstract ImmutableMap<String, ModuleKey> 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<String, ModuleKey> getOriginalDeps();
/**
* Returns a {@link RepositoryMapping} with only Bazel module repos and no repos from module
* extensions. For the full mapping, see {@link BazelModuleResolutionValue#getFullRepoMapping}.
*/
public final RepositoryMapping getRepoMappingWithBazelDepsOnly() {
ImmutableMap.Builder<String, RepositoryName> mapping = ImmutableMap.builder();
// If this is the root module, then the main repository should be visible as `@`.
if (getKey().equals(ModuleKey.ROOT)) {
mapping.put("", RepositoryName.MAIN);
}
// Every module should be able to reference itself as @<module repo name>.
// If this is the root module, this perfectly falls into @<module repo name> => @
if (!getRepoName().isEmpty()) {
mapping.put(getRepoName(), getCanonicalRepoName());
}
for (Map.Entry<String, ModuleKey> dep : getDeps().entrySet()) {
// Special note: if `dep` is actually the root module, its ModuleKey would be ROOT whose
// canonicalRepoName is the empty string. This perfectly maps to the main repo ("@").
mapping.put(dep.getKey(), dep.getValue().getCanonicalRepoName());
}
return RepositoryMapping.create(mapping.buildOrThrow(), getCanonicalRepoName());
}
/**
* The registry where this module came from. Must be null iff the module has a {@link
* NonRegistryOverride}.
*/
@Nullable
public abstract Registry getRegistry();
/** The module extensions used in this module. */
public abstract ImmutableList<ModuleExtensionUsage> 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<ModuleKey> transform) {
return toBuilder()
.setDeps(ImmutableMap.copyOf(Maps.transformValues(getDeps(), transform::apply)))
.build();
}
/** 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);
abstract ImmutableList.Builder<String> bazelCompatibilityBuilder();
@CanIgnoreReturnValue
public final Builder addBazelCompatibilityValues(Iterable<String> values) {
bazelCompatibilityBuilder().addAll(values);
return this;
}
abstract ImmutableList.Builder<String> executionPlatformsToRegisterBuilder();
@CanIgnoreReturnValue
public final Builder addExecutionPlatformsToRegister(Iterable<String> values) {
executionPlatformsToRegisterBuilder().addAll(values);
return this;
}
abstract ImmutableList.Builder<String> toolchainsToRegisterBuilder();
@CanIgnoreReturnValue
public final Builder addToolchainsToRegister(Iterable<String> values) {
toolchainsToRegisterBuilder().addAll(values);
return this;
}
public abstract Builder setOriginalDeps(ImmutableMap<String, ModuleKey> value);
public abstract Builder setDeps(ImmutableMap<String, ModuleKey> value);
abstract ImmutableMap.Builder<String, ModuleKey> depsBuilder();
@CanIgnoreReturnValue
public Builder addDep(String depRepoName, ModuleKey depKey) {
depsBuilder().put(depRepoName, depKey);
return this;
}
abstract ImmutableMap.Builder<String, ModuleKey> originalDepsBuilder();
@CanIgnoreReturnValue
public Builder addOriginalDep(String depRepoName, ModuleKey depKey) {
originalDepsBuilder().put(depRepoName, depKey);
return this;
}
public abstract Builder setRegistry(Registry value);
public abstract Builder setExtensionUsages(ImmutableList<ModuleExtensionUsage> value);
abstract ImmutableList.Builder<ModuleExtensionUsage> extensionUsagesBuilder();
@CanIgnoreReturnValue
public Builder addExtensionUsage(ModuleExtensionUsage value) {
extensionUsagesBuilder().add(value);
return this;
}
abstract String getName();
abstract Optional<String> getRepoName();
abstract Module autoBuild();
final Module build() {
if (getRepoName().isEmpty()) {
setRepoName(getName());
}
return autoBuild();
}
}
}