blob: 498f27ab07811ee7f0c610967d634163d21fc18c [file] [log] [blame]
// Copyright 2024 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.commands;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.bazel.bzlmod.modcommand.InvalidArgumentException;
import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
import com.google.devtools.build.lib.cmdline.RepositoryMapping;
import com.google.devtools.build.lib.cmdline.RepositoryName;
import com.google.devtools.build.lib.rules.repository.RepositoryDirectoryValue;
import com.google.devtools.build.lib.runtime.CommandEnvironment;
import com.google.devtools.build.lib.runtime.KeepGoingOption;
import com.google.devtools.build.lib.runtime.LoadingPhaseThreadsOption;
import com.google.devtools.build.lib.skyframe.RepositoryMappingValue.RepositoryMappingResolutionException;
import com.google.devtools.build.skyframe.EvaluationContext;
import com.google.devtools.build.skyframe.EvaluationResult;
import com.google.devtools.build.skyframe.SkyKey;
import com.google.devtools.build.skyframe.SkyValue;
import java.util.List;
import net.starlark.java.eval.EvalException;
/** Fetches repositories for commands. */
final class RepositoryFetcher {
private final CommandEnvironment env;
private final LoadingPhaseThreadsOption threadsOption;
private RepositoryFetcher(
CommandEnvironment env,
LoadingPhaseThreadsOption threadsOption) {
this.env = env;
this.threadsOption = threadsOption;
}
static ImmutableMap<RepositoryName, RepositoryDirectoryValue> fetchRepos(
List<String> repos,
CommandEnvironment env,
LoadingPhaseThreadsOption threadsOption)
throws RepositoryMappingResolutionException,
InterruptedException,
RepositoryFetcherException {
return new RepositoryFetcher(env, threadsOption).fetchRepos(repos);
}
private ImmutableMap<RepositoryName, RepositoryDirectoryValue> fetchRepos(List<String> repos)
throws InterruptedException,
RepositoryFetcherException,
RepositoryMappingResolutionException {
ImmutableSet<RepositoryName> reposnames = collectRepositoryNames(repos);
EvaluationResult<SkyValue> evaluationResult = evaluateFetch(reposnames);
return reposnames.stream()
.collect(
toImmutableMap(
repoName -> repoName,
repoName ->
(RepositoryDirectoryValue)
evaluationResult.get(RepositoryDirectoryValue.key(repoName))));
}
private EvaluationResult<SkyValue> evaluateFetch(ImmutableSet<RepositoryName> reposnames)
throws InterruptedException, RepositoryFetcherException {
EvaluationContext evaluationContext =
EvaluationContext.newBuilder()
.setParallelism(threadsOption.threads)
.setEventHandler(env.getReporter())
.build();
ImmutableSet<SkyKey> repoDelegatorKeys =
reposnames.stream().map(RepositoryDirectoryValue::key).collect(toImmutableSet());
EvaluationResult<SkyValue> evaluationResult =
env.getSkyframeExecutor().prepareAndGet(repoDelegatorKeys, evaluationContext);
if (evaluationResult.hasError()) {
Exception e = evaluationResult.getError().getException();
throw new RepositoryFetcherException(
e != null ? e.getMessage() : "Unexpected error during repository fetching.");
}
return evaluationResult;
}
private ImmutableSet<RepositoryName> collectRepositoryNames(List<String> repos)
throws InterruptedException,
RepositoryFetcherException,
RepositoryMappingResolutionException {
ImmutableSet.Builder<RepositoryName> reposnames = ImmutableSet.builder();
for (String repo : repos) {
try {
reposnames.add(getRepositoryName(repo));
} catch (LabelSyntaxException | EvalException | InvalidArgumentException e) {
throw new RepositoryFetcherException("Invalid repo name: " + e.getMessage());
}
}
return reposnames.build();
}
private RepositoryName getRepositoryName(String repoName)
throws EvalException,
InterruptedException,
LabelSyntaxException,
InvalidArgumentException,
RepositoryMappingResolutionException {
if (repoName.startsWith("@@")) { // canonical RepoName
return RepositoryName.create(repoName.substring(2));
} else if (repoName.startsWith("@")) { // apparent RepoName
RepositoryName.validateUserProvidedRepoName(repoName.substring(1));
RepositoryMapping repoMapping =
env.getSkyframeExecutor()
.getMainRepoMapping(
env.getOptions().getOptions(KeepGoingOption.class).keepGoing,
threadsOption.threads,
env.getReporter());
return repoMapping.get(repoName.substring(1));
} else {
throw new InvalidArgumentException(
"The repo value has to be either apparent '@repo' or canonical '@@repo' repo name");
}
}
static class RepositoryFetcherException extends Exception {
public RepositoryFetcherException(String message) {
super(message);
}
}
}