| // Copyright 2019 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.packages; |
| |
| import static com.google.devtools.build.lib.packages.WorkspaceFactoryHelper.originatesInWorkspaceSuffix; |
| import static net.starlark.java.eval.Starlark.NONE; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.devtools.build.lib.cmdline.BazelModuleContext; |
| import com.google.devtools.build.lib.cmdline.Label; |
| import com.google.devtools.build.lib.cmdline.Label.RepoContext; |
| import com.google.devtools.build.lib.cmdline.LabelSyntaxException; |
| import com.google.devtools.build.lib.cmdline.LabelValidator; |
| import com.google.devtools.build.lib.cmdline.RepositoryMapping; |
| import com.google.devtools.build.lib.cmdline.RepositoryName; |
| import com.google.devtools.build.lib.cmdline.TargetParsingException; |
| import com.google.devtools.build.lib.cmdline.TargetPattern; |
| import com.google.devtools.build.lib.packages.RuleFactory.InvalidRuleException; |
| import com.google.devtools.build.lib.starlarkbuildapi.WorkspaceGlobalsApi; |
| import com.google.devtools.build.lib.vfs.PathFragment; |
| import java.util.List; |
| import javax.annotation.Nullable; |
| import net.starlark.java.eval.EvalException; |
| import net.starlark.java.eval.Module; |
| import net.starlark.java.eval.Sequence; |
| import net.starlark.java.eval.Starlark; |
| import net.starlark.java.eval.StarlarkThread; |
| |
| /** A collection of global Starlark build API functions that apply to WORKSPACE files. */ |
| public class WorkspaceGlobals implements WorkspaceGlobalsApi { |
| |
| private final boolean allowWorkspaceFunction; |
| private final ImmutableMap<String, RuleClass> ruleClassMap; |
| |
| public WorkspaceGlobals( |
| boolean allowWorkspaceFunction, ImmutableMap<String, RuleClass> ruleClassMap) { |
| this.allowWorkspaceFunction = allowWorkspaceFunction; |
| this.ruleClassMap = ruleClassMap; |
| } |
| |
| @Override |
| public void workspace( |
| String name, |
| StarlarkThread thread) |
| throws EvalException, InterruptedException { |
| if (!allowWorkspaceFunction) { |
| throw Starlark.errorf( |
| "workspace() function should be used only at the top of the WORKSPACE file"); |
| } |
| RepositoryName.validateUserProvidedRepoName(name); |
| String errorMessage = LabelValidator.validateTargetName(name); |
| if (errorMessage != null) { |
| throw Starlark.errorf("%s", errorMessage); |
| } |
| // Add entry in repository map from "@name" --> "@" to avoid issue where bazel |
| // treats references to @name as a separate external repo |
| PackageFactory.getContext(thread) |
| .pkgBuilder |
| .setWorkspaceName(name) |
| .addRepositoryMappingEntry(RepositoryName.MAIN, name, RepositoryName.MAIN); |
| } |
| |
| private static RepositoryName getCurrentRepoName(StarlarkThread thread) { |
| @Nullable // moduleContext is null if we're called directly from a WORKSPACE file. |
| BazelModuleContext moduleContext = |
| BazelModuleContext.of(Module.ofInnermostEnclosingStarlarkFunction(thread)); |
| if (moduleContext == null) { |
| return RepositoryName.MAIN; |
| } |
| return moduleContext.label().getRepository(); |
| } |
| |
| private static ImmutableList<TargetPattern> parsePatterns( |
| List<String> patterns, Package.Builder builder, StarlarkThread thread) throws EvalException { |
| RepositoryName myName = getCurrentRepoName(thread); |
| RepositoryMapping renaming = builder.getRepositoryMappingFor(myName); |
| TargetPattern.Parser parser = |
| new TargetPattern.Parser(PathFragment.EMPTY_FRAGMENT, myName, renaming); |
| ImmutableList.Builder<TargetPattern> parsedPatterns = ImmutableList.builder(); |
| for (String pattern : patterns) { |
| try { |
| parsedPatterns.add(parser.parse(pattern)); |
| } catch (TargetParsingException e) { |
| throw Starlark.errorf("error parsing target pattern \"%s\": %s", pattern, e.getMessage()); |
| } |
| } |
| return parsedPatterns.build(); |
| } |
| |
| @Override |
| public void registerExecutionPlatforms(Sequence<?> platformLabels, StarlarkThread thread) |
| throws EvalException { |
| // Add to the package definition for later. |
| Package.Builder builder = PackageFactory.getContext(thread).pkgBuilder; |
| List<String> patterns = Sequence.cast(platformLabels, String.class, "platform_labels"); |
| builder.addRegisteredExecutionPlatforms(parsePatterns(patterns, builder, thread)); |
| } |
| |
| @Override |
| public void registerToolchains(Sequence<?> toolchainLabels, StarlarkThread thread) |
| throws EvalException { |
| // Add to the package definition for later. |
| Package.Builder builder = PackageFactory.getContext(thread).pkgBuilder; |
| List<String> patterns = Sequence.cast(toolchainLabels, String.class, "toolchain_labels"); |
| builder.addRegisteredToolchains( |
| parsePatterns(patterns, builder, thread), |
| originatesInWorkspaceSuffix(thread.getCallStack())); |
| } |
| |
| @Override |
| public void bind(String name, Object actual, StarlarkThread thread) |
| throws EvalException, InterruptedException { |
| Label nameLabel; |
| try { |
| nameLabel = Label.parseCanonical("//external:" + name); |
| } catch (LabelSyntaxException e) { |
| throw Starlark.errorf("%s", e.getMessage()); |
| } |
| try { |
| Package.Builder builder = PackageFactory.getContext(thread).pkgBuilder; |
| RuleClass ruleClass = ruleClassMap.get("bind"); |
| RepositoryName currentRepo = getCurrentRepoName(thread); |
| WorkspaceFactoryHelper.addBindRule( |
| builder, |
| ruleClass, |
| nameLabel, |
| actual == NONE |
| ? null |
| : Label.parseWithRepoContext( |
| (String) actual, |
| RepoContext.of(currentRepo, builder.getRepositoryMappingFor(currentRepo))), |
| thread.getCallStack()); |
| } catch (InvalidRuleException | Package.NameConflictException | LabelSyntaxException e) { |
| throw Starlark.errorf("%s", e.getMessage()); |
| } |
| } |
| } |