| // Copyright 2014 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.skyframe; |
| |
| import static com.google.common.base.Preconditions.checkNotNull; |
| |
| import com.google.common.annotations.VisibleForTesting; |
| import com.google.common.collect.Interner; |
| import com.google.devtools.build.lib.cmdline.Label; |
| import com.google.devtools.build.lib.concurrent.BlazeInterners; |
| import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; |
| import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec; |
| import com.google.devtools.build.lib.vfs.Root; |
| import com.google.devtools.build.lib.vfs.RootedPath; |
| import com.google.devtools.build.skyframe.SkyFunctionName; |
| import com.google.devtools.build.skyframe.SkyKey; |
| import com.google.devtools.build.skyframe.SkyValue; |
| import java.util.Objects; |
| import net.starlark.java.eval.Module; |
| |
| /** |
| * A value that represents the .bzl module loaded by a Starlark {@code load()} statement. |
| * |
| * <p>The key consists of an absolute {@link Label} and the context in which the load occurs. The |
| * Label should not reference the special {@code external} package. |
| * |
| * <p>This value is also used to represent the special prelude file that may be implicitly loaded |
| * and sourced by BUILD files. The prelude file need not end in ".bzl". |
| */ |
| public class BzlLoadValue implements SkyValue { |
| |
| private final Module module; // .bzl module (and indirectly, the entire load DAG) |
| private final byte[] transitiveDigest; // of .bzl file and load dependencies |
| |
| @VisibleForTesting |
| public BzlLoadValue(Module module, byte[] transitiveDigest) { |
| this.module = checkNotNull(module); |
| this.transitiveDigest = checkNotNull(transitiveDigest); |
| } |
| |
| /** Returns the .bzl module. */ |
| public Module getModule() { |
| return module; |
| } |
| |
| /** Returns the digest of the .bzl module and its transitive load dependencies. */ |
| public byte[] getTransitiveDigest() { |
| return transitiveDigest; |
| } |
| |
| private static final Interner<Key> keyInterner = BlazeInterners.newWeakInterner(); |
| |
| /** SkyKey for a Starlark load. */ |
| public abstract static class Key implements SkyKey { |
| |
| /** |
| * Returns the absolute label of the .bzl file to be loaded. |
| * |
| * <p>For {@link KeyForBuiltins}, it must begin with {@code @_builtins//:}. (It is legal for |
| * other keys to use {@code @_builtins}, but since no real repo by that name may be defined, |
| * they won't evaluate to a successful result.) |
| */ |
| abstract Label getLabel(); |
| |
| /** Returns true if this is a request for the special BUILD prelude file. */ |
| boolean isBuildPrelude() { |
| return false; |
| } |
| |
| /** Returns true if this is a request for a builtins bzl file. */ |
| boolean isBuiltins() { |
| return false; |
| } |
| |
| /** |
| * Constructs a new key suitable for evaluating a {@code load()} dependency of this key's .bzl |
| * file. |
| * |
| * <p>The new key uses the given label but the same contextual information -- whether the |
| * top-level requesting value is a BUILD or WORKSPACE file, and if it's a WORKSPACE, its |
| * chunking info. |
| */ |
| abstract Key getKeyForLoad(Label loadLabel); |
| |
| /** |
| * Constructs an BzlCompileValue key suitable for retrieving the Starlark code for this .bzl, |
| * given the Root in which to find its file. |
| */ |
| abstract BzlCompileValue.Key getCompileKey(Root root); |
| |
| @Override |
| public SkyFunctionName functionName() { |
| return SkyFunctions.BZL_LOAD; |
| } |
| } |
| |
| /** A key for loading a .bzl during package loading (BUILD evaluation). */ |
| @Immutable |
| @AutoCodec.VisibleForSerialization |
| static final class KeyForBuild extends Key { |
| |
| private final Label label; |
| |
| /** |
| * True if this is the special prelude file, whose declarations are implicitly loaded by all |
| * BUILD files. |
| */ |
| private final boolean isBuildPrelude; |
| |
| private KeyForBuild(Label label, boolean isBuildPrelude) { |
| this.label = checkNotNull(label); |
| this.isBuildPrelude = isBuildPrelude; |
| } |
| |
| @Override |
| Label getLabel() { |
| return label; |
| } |
| |
| @Override |
| boolean isBuildPrelude() { |
| return isBuildPrelude; |
| } |
| |
| @Override |
| Key getKeyForLoad(Label loadLabel) { |
| // Note that the returned key always has !isBuildPrelude. I.e., if the prelude file loads |
| // another .bzl, the loaded .bzl is processed as normal with no special prelude magic. This is |
| // because 1) only the prelude file, not its dependencies, should automatically re-export its |
| // loaded symbols; and 2) we don't want prelude-loaded modules to end up cloned if they're |
| // also loaded through normal means. |
| return keyForBuild(loadLabel); |
| } |
| |
| @Override |
| BzlCompileValue.Key getCompileKey(Root root) { |
| if (isBuildPrelude) { |
| return BzlCompileValue.keyForBuildPrelude(root, label); |
| } else { |
| return BzlCompileValue.key(root, label); |
| } |
| } |
| |
| // TODO(brandjon): Use something more similar to AbstractSkyKey's stringification (same below). |
| @Override |
| public String toString() { |
| return label.toString(); |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (this == obj) { |
| return true; |
| } |
| if (!(obj instanceof KeyForBuild)) { |
| return false; |
| } |
| KeyForBuild other = (KeyForBuild) obj; |
| return this.label.equals(other.label) && this.isBuildPrelude == other.isBuildPrelude; |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash(KeyForBuild.class, label, isBuildPrelude); |
| } |
| } |
| |
| /** |
| * A key for loading a .bzl during WORKSPACE evaluation. |
| * |
| * <p>This needs to track "chunking" information, i.e. a sequence number indicating which segment |
| * of the WORKSPACE file we are in the process of evaluating. This helps determine the appropriate |
| * repository remapping value to use. |
| */ |
| // TODO(brandjon): Question: It looks like the chunk number doesn't play any role in deciding |
| // whether or not a repo is available for load()ing. Are we tracking incremental dependencies |
| // correctly? For instance, if a repository declaration moves from one workspace chunk to another, |
| // are we reevaluating whether its loads are still valid? AI: fix if broken, improve this comment |
| // if not broken. |
| @Immutable |
| @AutoCodec.VisibleForSerialization |
| static final class KeyForWorkspace extends Key { |
| |
| private final Label label; |
| private final int workspaceChunk; |
| private final RootedPath workspacePath; |
| |
| private KeyForWorkspace(Label label, int workspaceChunk, RootedPath workspacePath) { |
| this.label = checkNotNull(label); |
| this.workspaceChunk = workspaceChunk; |
| this.workspacePath = checkNotNull(workspacePath); |
| } |
| |
| @Override |
| Label getLabel() { |
| return label; |
| } |
| |
| int getWorkspaceChunk() { |
| return workspaceChunk; |
| } |
| |
| RootedPath getWorkspacePath() { |
| return workspacePath; |
| } |
| |
| @Override |
| Key getKeyForLoad(Label loadLabel) { |
| return keyForWorkspace(loadLabel, workspaceChunk, workspacePath); |
| } |
| |
| @Override |
| BzlCompileValue.Key getCompileKey(Root root) { |
| return BzlCompileValue.key(root, label); |
| } |
| |
| @Override |
| public String toString() { |
| return label + " (in workspace)"; |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (this == obj) { |
| return true; |
| } |
| if (!(obj instanceof KeyForWorkspace)) { |
| return false; |
| } |
| KeyForWorkspace other = (KeyForWorkspace) obj; |
| return label.equals(other.label) |
| && workspaceChunk == other.workspaceChunk |
| && workspacePath.equals(other.workspacePath); |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash(KeyForWorkspace.class, label, workspaceChunk, workspacePath); |
| } |
| } |
| |
| /** |
| * A key for loading a .bzl during {@code @_builtins} evaluation. |
| * |
| * <p>This kind of key is only requested by {@link StarlarkBuiltinsFunction} and its transitively |
| * loaded {@link BzlLoadFunction} calls. |
| * |
| * <p>The label must have {@link StarlarkBuiltinsValue#BUILTINS_REPO} as its repository component. |
| * (It is valid for other key types to use that repo name, but since it is not a real repository |
| * and cannot be fetched, any attempt to resolve such a key would fail.) |
| */ |
| // TODO(#11437): Prevent users from trying to declare a repo named "@_builtins". |
| @Immutable |
| @AutoCodec.VisibleForSerialization |
| static final class KeyForBuiltins extends Key { |
| |
| private final Label label; |
| |
| private KeyForBuiltins(Label label) { |
| this.label = checkNotNull(label); |
| if (!StarlarkBuiltinsValue.isBuiltinsRepo(label.getRepository())) { |
| throw new IllegalArgumentException("repository name for builtins key must be '@_builtins'"); |
| } |
| } |
| |
| @Override |
| Label getLabel() { |
| return label; |
| } |
| |
| @Override |
| boolean isBuiltins() { |
| return true; |
| } |
| |
| @Override |
| Key getKeyForLoad(Label label) { |
| return keyForBuiltins(label); |
| } |
| |
| @Override |
| BzlCompileValue.Key getCompileKey(Root root) { |
| return BzlCompileValue.keyForBuiltins(root, label); |
| } |
| |
| @Override |
| public String toString() { |
| return label.toString(); |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (this == obj) { |
| return true; |
| } |
| if (!(obj instanceof KeyForBuiltins)) { |
| return false; |
| } |
| return this.label.equals(((KeyForBuiltins) obj).label); |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash(KeyForBuiltins.class, label); |
| } |
| } |
| |
| /** A key for loading a .bzl to get the repo rule required by Bzlmod generated repositories. */ |
| @Immutable |
| @AutoCodec.VisibleForSerialization |
| static final class KeyForBzlmod extends Key { |
| |
| private final Label label; |
| |
| private KeyForBzlmod(Label label) { |
| this.label = checkNotNull(label); |
| } |
| |
| @Override |
| Label getLabel() { |
| return label; |
| } |
| |
| @Override |
| Key getKeyForLoad(Label loadLabel) { |
| return keyForBzlmod(loadLabel); |
| } |
| |
| @Override |
| BzlCompileValue.Key getCompileKey(Root root) { |
| return BzlCompileValue.key(root, label); |
| } |
| |
| @Override |
| public String toString() { |
| return label + " (in Bzlmod)"; |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (this == obj) { |
| return true; |
| } |
| if (!(obj instanceof KeyForBzlmod)) { |
| return false; |
| } |
| KeyForBzlmod other = (KeyForBzlmod) obj; |
| return label.equals(other.label); |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash(KeyForBzlmod.class, label); |
| } |
| } |
| |
| /** Constructs a key for loading a regular (non-workspace) .bzl file, from the .bzl's label. */ |
| public static Key keyForBuild(Label label) { |
| return keyInterner.intern(new KeyForBuild(label, /*isBuildPrelude=*/ false)); |
| } |
| |
| /** |
| * Constructs a key for loading a .bzl file from the context of evaluating the WORKSPACE file. |
| * |
| * @param label the label of the bzl file being loaded |
| * @param workspaceChunk the workspace chunk that the load statement originated from. If the bzl |
| * file is loaded more than once, this is the chunk that it was first loaded from |
| * @param workspacePath the path of the workspace file for the project |
| */ |
| static Key keyForWorkspace(Label label, int workspaceChunk, RootedPath workspacePath) { |
| return keyInterner.intern(new KeyForWorkspace(label, workspaceChunk, workspacePath)); |
| } |
| |
| /** Constructs a key for loading a .bzl file within the {@code @_builtins} pseudo-repository. */ |
| static Key keyForBuiltins(Label label) { |
| return keyInterner.intern(new KeyForBuiltins(label)); |
| } |
| |
| /** Constructs a key for loading the special prelude .bzl. */ |
| static Key keyForBuildPrelude(Label label) { |
| return keyInterner.intern(new KeyForBuild(label, /*isBuildPrelude=*/ true)); |
| } |
| |
| /** Constructs a key for loading a .bzl for Bzlmod repos */ |
| public static Key keyForBzlmod(Label label) { |
| return keyInterner.intern(new KeyForBzlmod(label)); |
| } |
| } |