| // 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(); |
| } |
| } |
| } |