| // Copyright 2022 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.ImmutableBiMap; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.common.collect.ImmutableSetMultimap; |
| import com.google.common.collect.ImmutableSortedMap; |
| import com.google.devtools.build.lib.skyframe.SkyFunctions; |
| import com.google.devtools.build.lib.skyframe.serialization.autocodec.SerializationConstant; |
| import com.google.devtools.build.skyframe.SkyKey; |
| import com.google.devtools.build.skyframe.SkyValue; |
| import com.google.errorprone.annotations.CanIgnoreReturnValue; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| /** |
| * The result of running Bazel module inspection pre-processing, containing the un-pruned and |
| * augmented wrappers of the Bazel module dependency graph (post-version-resolution). |
| */ |
| @AutoValue |
| public abstract class BazelModuleInspectorValue implements SkyValue { |
| |
| @SerializationConstant |
| public static final SkyKey KEY = () -> SkyFunctions.BAZEL_MODULE_INSPECTION; |
| |
| public static BazelModuleInspectorValue create( |
| ImmutableMap<ModuleKey, AugmentedModule> depGraph, |
| ImmutableMap<String, ImmutableSet<ModuleKey>> modulesIndex, |
| ImmutableSetMultimap<ModuleExtensionId, String> extensionToRepoInternalNames) { |
| return new AutoValue_BazelModuleInspectorValue( |
| depGraph, modulesIndex, extensionToRepoInternalNames); |
| } |
| |
| /** |
| * The (bidirectional) inspection dep graph, containing wrappers of the {@link Module}, augmented |
| * with references to dependants. The order is non-deterministic, inherited from the {@code |
| * completeDepGraph} of {@link BazelModuleResolutionValue}. For any KEY in the returned map, it's |
| * guaranteed that {@code depGraph[KEY].getKey() == KEY}. |
| */ |
| public abstract ImmutableMap<ModuleKey, AugmentedModule> getDepGraph(); |
| |
| /** |
| * Index of all module keys mentioned in the un-pruned dep graph (loaded or not) for easy lookup. |
| * It is a map from <i>module name</i> to the set of {@link ModuleKey}s that point to a version of |
| * that module. |
| */ |
| public abstract ImmutableMap<String, ImmutableSet<ModuleKey>> getModulesIndex(); |
| |
| /** |
| * A mapping from a module extension ID, to the list of "internal" names of the repos generated by |
| * that extension. The "internal" name is the name directly used by the extension when |
| * instantiating a repo rule. |
| */ |
| public abstract ImmutableSetMultimap<ModuleExtensionId, String> getExtensionToRepoInternalNames(); |
| |
| /** |
| * A wrapper for {@link Module}, augmented with references to dependants (and also those who are |
| * not used in the final dep graph). |
| */ |
| @AutoValue |
| public abstract static class AugmentedModule { |
| /** Name of the module. Same as in {@link Module}. */ |
| public abstract String getName(); |
| |
| /** Version of the module. Same as in {@link Module}. */ |
| public abstract Version getVersion(); |
| |
| /** {@link ModuleKey} of this module. Same as in {@link Module} */ |
| public abstract ModuleKey getKey(); |
| |
| /** |
| * The set of modules in the resolved dep graph that depend on this module |
| * <strong>after</strong> the module resolution. |
| */ |
| public abstract ImmutableSet<ModuleKey> getDependants(); |
| |
| /** |
| * The set of modules in the complete dep graph that originally depended on this module *before* |
| * the module resolution (can contain unused nodes). |
| */ |
| public abstract ImmutableSet<ModuleKey> getOriginalDependants(); |
| |
| /** |
| * A bi-map from the repo names of the resolved dependencies of this module to their actual |
| * module keys. The inverse lookup is used to check how and why the dependency changed using the |
| * other maps. |
| */ |
| public abstract ImmutableBiMap<String, ModuleKey> getDeps(); |
| |
| /** |
| * A bi-map from the repo names of the unused dependencies of this module to their actual module |
| * keys. The inverse lookup is used to check how and why the dependency changed using the other |
| * maps. |
| */ |
| public abstract ImmutableBiMap<String, ModuleKey> getUnusedDeps(); |
| |
| /** |
| * A map from the repo name of the dependencies of this module to the rules that were used for |
| * their resolution (can be either the original dependency, changed by the Minimal-Version |
| * Selection algorithm or by an override rule. |
| */ |
| public abstract ImmutableMap<String, ResolutionReason> getDepReasons(); |
| |
| /** |
| * Shortcut for retrieving the sorted union of both used and unused deps based on the unused |
| * flag. |
| */ |
| public ImmutableSortedMap<ModuleKey, String> getAllDeps(boolean unused) { |
| if (!unused) { |
| return ImmutableSortedMap.copyOf(getDeps().inverse(), ModuleKey.LEXICOGRAPHIC_COMPARATOR); |
| } else { |
| Map<ModuleKey, String> map = new HashMap<>(); |
| map.putAll(getDeps().inverse()); |
| map.putAll(getUnusedDeps().inverse()); |
| return ImmutableSortedMap.copyOf(map, ModuleKey.LEXICOGRAPHIC_COMPARATOR); |
| } |
| } |
| |
| /** |
| * Flag that tell whether the module was loaded and added to the dependency graph. Modules |
| * overridden by {@code single_version_override} and {@link NonRegistryOverride} are not loaded |
| * so their {@code originalDeps} are (yet) unknown. |
| */ |
| public abstract boolean isLoaded(); |
| |
| /** Flag for checking whether the module is present in the resolved dep graph. */ |
| public boolean isUsed() { |
| return getKey().equals(ModuleKey.ROOT) || !getDependants().isEmpty(); |
| } |
| |
| /** Returns a new {@link AugmentedModule.Builder} with {@code key} set. */ |
| public static AugmentedModule.Builder builder(ModuleKey key) { |
| return new AutoValue_BazelModuleInspectorValue_AugmentedModule.Builder() |
| .setName(key.getName()) |
| .setVersion(key.getVersion()) |
| .setKey(key) |
| .setLoaded(false); |
| } |
| |
| /** Builder type for {@link AugmentedModule}. */ |
| @AutoValue.Builder |
| public abstract static class Builder { |
| public abstract AugmentedModule.Builder setName(String value); |
| |
| public abstract AugmentedModule.Builder setVersion(Version value); |
| |
| public abstract AugmentedModule.Builder setKey(ModuleKey value); |
| |
| public abstract AugmentedModule.Builder setLoaded(boolean value); |
| |
| abstract ImmutableSet.Builder<ModuleKey> originalDependantsBuilder(); |
| |
| @CanIgnoreReturnValue |
| public AugmentedModule.Builder addOriginalDependant(ModuleKey depKey) { |
| originalDependantsBuilder().add(depKey); |
| return this; |
| } |
| |
| abstract ImmutableSet.Builder<ModuleKey> dependantsBuilder(); |
| |
| @CanIgnoreReturnValue |
| public AugmentedModule.Builder addDependant(ModuleKey depKey) { |
| dependantsBuilder().add(depKey); |
| return this; |
| } |
| |
| abstract ImmutableBiMap.Builder<String, ModuleKey> depsBuilder(); |
| |
| @CanIgnoreReturnValue |
| public AugmentedModule.Builder addDep(String repoName, ModuleKey key) { |
| depsBuilder().put(repoName, key); |
| return this; |
| } |
| |
| abstract ImmutableBiMap.Builder<String, ModuleKey> unusedDepsBuilder(); |
| |
| @CanIgnoreReturnValue |
| public AugmentedModule.Builder addUnusedDep(String repoName, ModuleKey key) { |
| unusedDepsBuilder().put(repoName, key); |
| return this; |
| } |
| |
| abstract ImmutableMap.Builder<String, ResolutionReason> depReasonsBuilder(); |
| |
| @CanIgnoreReturnValue |
| public AugmentedModule.Builder addDepReason(String repoName, ResolutionReason reason) { |
| depReasonsBuilder().put(repoName, reason); |
| return this; |
| } |
| |
| public abstract AugmentedModule build(); |
| } |
| |
| /** The reason why a final dependency of a module was resolved the way it was. */ |
| public enum ResolutionReason { |
| /** The dependency is the original dependency defined in the MODULE.bazel file. */ |
| ORIGINAL(""), |
| /** The dependency was replaced by the Minimal-Version Selection algorithm. */ |
| MINIMAL_VERSION_SELECTION("MVS"), |
| /** The dependency was replaced by a {@code single_version_override} rule. */ |
| SINGLE_VERSION_OVERRIDE("SVO"), |
| /** The dependency was replaced by a {@code multiple_version_override} rule. */ |
| MULTIPLE_VERSION_OVERRIDE("MVO"), |
| /** The dependency was replaced by one of the {@link NonRegistryOverride} rules. */ |
| ARCHIVE_OVERRIDE("archive"), |
| GIT_OVERRIDE("git"), |
| LOCAL_PATH_OVERRIDE("local"); |
| |
| private final String label; |
| |
| ResolutionReason(String label) { |
| this.label = label; |
| } |
| |
| public String getLabel() { |
| return label; |
| } |
| } |
| } |
| } |