diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BUILD b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BUILD new file mode 100644 index 00000000000000..4c2dcf4f97a7a4 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BUILD @@ -0,0 +1,70 @@ +load("@rules_java//java:defs.bzl", "java_library") + +package( + default_visibility = ["//src:__subpackages__"], +) + +filegroup( + name = "srcs", + srcs = glob(["*"]), + visibility = ["//src:__subpackages__"], +) + +java_library( + name = "repo_rule_value", + srcs = [ + "BzlmodRepoRuleValue.java", + ], + deps = [ + "//src/main/java/com/google/devtools/build/lib/concurrent", + "//src/main/java/com/google/devtools/build/lib/packages", + "//src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec", + "//src/main/java/com/google/devtools/build/skyframe:skyframe-objects", + "//third_party:guava", + ], +) + +java_library( + name = "repo_spec_values", + srcs = [ + "RepoSpec.java", + "RepoSpecsValue.java", + ], + deps = [ + "//src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec:autocodec-annotation", + "//src/main/java/com/google/devtools/build/skyframe:skyframe-objects", + "//third_party:guava", + "//third_party:auto_value", + ], +) + +java_library( + name = "repo_spec_functions", + srcs = [ + "RepoSpecsFunction.java", + ], + deps = [ + ":repo_spec_values", + "//src/main/java/com/google/devtools/build/lib/actions:file_metadata", + "//src/main/java/com/google/devtools/build/lib/bazel/repository/downloader", + "//src/main/java/com/google/devtools/build/lib/pkgcache", + "//src/main/java/com/google/devtools/build/lib/skyframe:precomputed_value", + "//src/main/java/com/google/devtools/build/lib/starlarkbuildapi/repository", + "//src/main/java/com/google/devtools/build/lib/vfs", + "//src/main/java/com/google/devtools/build/lib/vfs:pathfragment", + "//src/main/java/com/google/devtools/build/skyframe", + "//src/main/java/com/google/devtools/build/skyframe:skyframe-objects", + "//third_party:gson", + "//third_party:guava", + ], +) + +java_library( + name = "repo_rule_creator", + srcs = ["BzlmodRepoRuleCreator.java"], + deps = [ + "//src/main/java/com/google/devtools/build/lib/events", + "//src/main/java/com/google/devtools/build/lib/packages", + "//src/main/java/net/starlark/java/eval", + ], +) diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BzlmodRepoRuleCreator.java b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BzlmodRepoRuleCreator.java new file mode 100644 index 00000000000000..fc4e5b749411b0 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BzlmodRepoRuleCreator.java @@ -0,0 +1,29 @@ +// 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.devtools.build.lib.events.EventHandler; +import com.google.devtools.build.lib.packages.Package; +import com.google.devtools.build.lib.packages.Rule; +import com.google.devtools.build.lib.packages.RuleFactory.InvalidRuleException; + +import net.starlark.java.eval.StarlarkSemantics; + +import java.util.Map; + +public interface BzlmodRepoRuleCreator { + Rule createRule(Package.Builder pkg, StarlarkSemantics semantics, Map kwargs, EventHandler handler) + throws InterruptedException, InvalidRuleException; +} diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BzlmodRepoRuleValue.java b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BzlmodRepoRuleValue.java new file mode 100644 index 00000000000000..f08d589c1b229d --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BzlmodRepoRuleValue.java @@ -0,0 +1,77 @@ +// 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.common.collect.Interner; +import com.google.devtools.build.lib.concurrent.BlazeInterners; +import com.google.devtools.build.lib.packages.Rule; +import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec; +import com.google.devtools.build.skyframe.AbstractSkyKey; +import com.google.devtools.build.skyframe.SkyFunctionName; +import com.google.devtools.build.skyframe.SkyValue; + +public class BzlmodRepoRuleValue implements SkyValue { + public static final SkyFunctionName BZLMOD_REPO_RULE = + SkyFunctionName.createHermetic("BZLMOD_REPO_RULE"); + + private final Rule rule; + + public BzlmodRepoRuleValue(Rule rule) { + this.rule = rule; + } + + public Rule getRule() { + return rule; + } + + public static Key key(String repositoryName) { + return Key.create(repositoryName); + } + + /** Represents an unsuccessful repository lookup. */ + public static final class RepoRuleNotFoundValue extends BzlmodRepoRuleValue { + private RepoRuleNotFoundValue() { + super(null); + } + + @Override + public Rule getRule() { + throw new IllegalStateException(); + } + } + + public static final RepoRuleNotFoundValue REPO_RULE_NOT_FOUND_VALUE = new RepoRuleNotFoundValue(); + + /** Argument for the SkyKey to request a BzlmodRepoRuleValue. */ + @AutoCodec + public static class Key extends AbstractSkyKey { + private static final Interner interner = BlazeInterners.newWeakInterner(); + + private Key(String arg) { + super(arg); + } + + @AutoCodec.VisibleForSerialization + @AutoCodec.Instantiator + static Key create(String arg) { + return interner.intern(new Key(arg)); + } + + @Override + public SkyFunctionName functionName() { + return BZLMOD_REPO_RULE; + } + } +} diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/RepoSpec.java b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/RepoSpec.java new file mode 100644 index 00000000000000..00d025a4225377 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/RepoSpec.java @@ -0,0 +1,51 @@ +// 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.ImmutableMap; + +import javax.annotation.Nullable; + +@AutoValue +public abstract class RepoSpec { + + public static RepoSpec create(String bzlFile, String ruleClassName, ImmutableMap attributes) { + return new AutoValue_RepoSpec(bzlFile, ruleClassName, attributes); + } + + public static RepoSpec create(String ruleClassName, ImmutableMap attributes) { + return new AutoValue_RepoSpec(null, ruleClassName, attributes); + } + + // The label string for the bzl file this repository rule is defined in, null for native repo rule + @Nullable + public abstract String getBzlFile(); + + public abstract String getRuleClassName(); + + public abstract ImmutableMap getAttributes(); + + public boolean isNativeRepoRule() { + return getBzlFile() == null; + } + + // Return a string representing the rule class + // eg. Native repo rule: local_repository + // Starlark repo rule: //:repo.bzl%my_repo + public String getRuleClass() { + return (isNativeRepoRule() ? "" : getBzlFile() + "%") + getRuleClassName(); + } +} diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/RepoSpecsFunction.java b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/RepoSpecsFunction.java new file mode 100644 index 00000000000000..410a9c130295bf --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/RepoSpecsFunction.java @@ -0,0 +1,65 @@ +// 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.common.collect.ImmutableMap; +import com.google.devtools.build.skyframe.SkyFunction; +import com.google.devtools.build.skyframe.SkyFunctionException; +import com.google.devtools.build.skyframe.SkyKey; +import com.google.devtools.build.skyframe.SkyValue; + +import javax.annotation.Nullable; + +public class RepoSpecsFunction implements SkyFunction { + + @Nullable + @Override + public SkyValue compute(SkyKey skyKey, Environment env) + throws SkyFunctionException, InterruptedException { + + if (skyKey == RepoSpecsValue.KEY_FOR_OVERRIDE_DEP) { + return computeForOverrideDep(env); + } else if (skyKey == RepoSpecsValue.KEY_FOR_BAZEL_MODULE) { + return computeForBazelModule(env); + } else if (skyKey == RepoSpecsValue.KEY_FOR_MODULE_RULE) { + return computeForModuleRule(env); + } + throw new IllegalArgumentException("Unrecognized key: " + skyKey.toString()); + } + + @Nullable + private SkyValue computeForOverrideDep(Environment env) { + ImmutableMap.Builder repositories = ImmutableMap.builder(); + return new RepoSpecsValue(repositories.build()); + } + + @Nullable + private SkyValue computeForBazelModule(Environment env) { + ImmutableMap.Builder repositories = ImmutableMap.builder(); + return new RepoSpecsValue(repositories.build()); + } + + @Nullable + private SkyValue computeForModuleRule(Environment env) { + ImmutableMap.Builder repositories = ImmutableMap.builder(); + return new RepoSpecsValue(repositories.build()); + } + + @Nullable + @Override + public String extractTag(SkyKey skyKey) { + return null; + } +} diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/RepoSpecsValue.java b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/RepoSpecsValue.java new file mode 100644 index 00000000000000..7be10c22736428 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/RepoSpecsValue.java @@ -0,0 +1,45 @@ +// 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.common.collect.ImmutableMap; +import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec; +import com.google.devtools.build.skyframe.SkyFunctionName; +import com.google.devtools.build.skyframe.SkyKey; +import com.google.devtools.build.skyframe.SkyValue; + +public class RepoSpecsValue implements SkyValue { + public static final SkyFunctionName REPO_SPECS = + SkyFunctionName.createHermetic("REPO_SPECS"); + + @AutoCodec + public static final SkyKey KEY_FOR_OVERRIDE_DEP = () -> REPO_SPECS; + + @AutoCodec + public static final SkyKey KEY_FOR_BAZEL_MODULE = () -> REPO_SPECS; + + @AutoCodec + public static final SkyKey KEY_FOR_MODULE_RULE = () -> REPO_SPECS; + + private final ImmutableMap repositories; + + public RepoSpecsValue(ImmutableMap repositories) { + this.repositories = repositories; + } + + public RepoSpec getRepository(String repositoryName) { + return repositories.get(repositoryName); + } +} diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/BUILD b/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/BUILD index d70250d7403722..af4a0d0a72ecdb 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/BUILD +++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/BUILD @@ -17,6 +17,7 @@ java_library( "//src/main/java/com/google/devtools/build/lib/actions:file_metadata", "//src/main/java/com/google/devtools/build/lib/analysis:analysis_cluster", "//src/main/java/com/google/devtools/build/lib/analysis:blaze_directories", + "//src/main/java/com/google/devtools/build/lib/bazel/bzlmod:repo_rule_creator", "//src/main/java/com/google/devtools/build/lib/bazel/debug:workspace-rule-event", "//src/main/java/com/google/devtools/build/lib/bazel/repository", "//src/main/java/com/google/devtools/build/lib/bazel/repository/cache", diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkRepositoryModule.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkRepositoryModule.java index 1e8453d8283370..45c2111727be20 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkRepositoryModule.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkRepositoryModule.java @@ -23,6 +23,7 @@ import com.google.devtools.build.docgen.annot.DocCategory; import com.google.devtools.build.lib.analysis.BaseRuleClasses; import com.google.devtools.build.lib.analysis.starlark.StarlarkAttrModule.Descriptor; +import com.google.devtools.build.lib.bazel.bzlmod.BzlmodRepoRuleCreator; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.cmdline.LabelSyntaxException; import com.google.devtools.build.lib.events.EventHandler; @@ -36,6 +37,8 @@ import com.google.devtools.build.lib.packages.Rule; import com.google.devtools.build.lib.packages.RuleClass; import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType; +import com.google.devtools.build.lib.packages.RuleFactory; +import com.google.devtools.build.lib.packages.RuleFactory.BuildLangTypedAttributeValuesMap; import com.google.devtools.build.lib.packages.RuleFactory.InvalidRuleException; import com.google.devtools.build.lib.packages.StarlarkExportable; import com.google.devtools.build.lib.packages.WorkspaceFactoryHelper; @@ -50,8 +53,11 @@ import net.starlark.java.eval.Sequence; import net.starlark.java.eval.Starlark; import net.starlark.java.eval.StarlarkCallable; +import net.starlark.java.eval.StarlarkSemantics; import net.starlark.java.eval.StarlarkThread; +import net.starlark.java.eval.StarlarkThread.CallStackEntry; import net.starlark.java.eval.Tuple; +import net.starlark.java.syntax.Location; /** * The Starlark module containing the definition of {@code repository_rule} function to define a @@ -119,7 +125,7 @@ public StarlarkCallable repositoryRule( "A callable value that may be invoked during evaluation of the WORKSPACE file to" + " instantiate and return a repository rule.") private static final class RepositoryRuleFunction - implements StarlarkCallable, StarlarkExportable { + implements StarlarkCallable, StarlarkExportable, BzlmodRepoRuleCreator { private final RuleClass.Builder builder; private final StarlarkCallable implementation; private Label extensionLabel; @@ -213,6 +219,18 @@ public Object call(StarlarkThread thread, Tuple args, Dict kwarg throw Starlark.errorf("%s", e.getMessage()); } } + + @Override + public Rule createRule(Package.Builder pkg, StarlarkSemantics semantics, Map kwargs, EventHandler handler) + throws InterruptedException, InvalidRuleException { + RuleClass ruleClass = builder.build(exportedName, exportedName); + BuildLangTypedAttributeValuesMap attributeValues = new BuildLangTypedAttributeValuesMap(kwargs); + ImmutableList.Builder callStack = ImmutableList.builder(); + // TODO(pcloudy): Optimize the callstack + callStack.add(new CallStackEntry("RepositoryRuleFunction.createRule", Location.BUILTIN)); + return RuleFactory.createRule( + pkg, ruleClass, attributeValues, handler, semantics, callStack.build()); + } } @Override diff --git a/src/main/java/com/google/devtools/build/lib/cmdline/LabelConstants.java b/src/main/java/com/google/devtools/build/lib/cmdline/LabelConstants.java index 96c0e31c4863d3..9751f9c7abe5b0 100644 --- a/src/main/java/com/google/devtools/build/lib/cmdline/LabelConstants.java +++ b/src/main/java/com/google/devtools/build/lib/cmdline/LabelConstants.java @@ -36,6 +36,8 @@ public class LabelConstants { public static final PathFragment WORKSPACE_FILE_NAME = PathFragment.create("WORKSPACE"); public static final PathFragment WORKSPACE_DOT_BAZEL_FILE_NAME = PathFragment.create("WORKSPACE.bazel"); + public static final PathFragment MODULE_DOT_BAZEL_FILE_NAME = + PathFragment.create("MODULE.bazel"); public static final String DEFAULT_REPOSITORY_DIRECTORY = "__main__"; // With this prefix, non-main repositories are symlinked under diff --git a/src/main/java/com/google/devtools/build/lib/packages/BazelStarlarkEnvironment.java b/src/main/java/com/google/devtools/build/lib/packages/BazelStarlarkEnvironment.java index 46039c6f44e723..c93e91bf624316 100644 --- a/src/main/java/com/google/devtools/build/lib/packages/BazelStarlarkEnvironment.java +++ b/src/main/java/com/google/devtools/build/lib/packages/BazelStarlarkEnvironment.java @@ -17,12 +17,14 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Sets; + +import net.starlark.java.eval.FlagGuardedValue; +import net.starlark.java.eval.Starlark; + import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; -import net.starlark.java.eval.FlagGuardedValue; -import net.starlark.java.eval.Starlark; // TODO(adonovan): move skyframe.PackageFunction into lib.packages so we needn't expose this and // the other env-building functions. @@ -57,6 +59,8 @@ public final class BazelStarlarkEnvironment { private final ImmutableMap workspaceBzlEnv; /** The top-level predeclared symbols for a bzl module in the {@code @_builtins} pseudo-repo. */ private final ImmutableMap builtinsBzlEnv; + /** The top-level predeclared symbols for a bzl module in the Bzlmod system. */ + private final ImmutableMap bzlmodBzlEnv; BazelStarlarkEnvironment( RuleClassProvider ruleClassProvider, @@ -73,6 +77,7 @@ public final class BazelStarlarkEnvironment { this.uninjectedBuildBzlEnv = createUninjectedBuildBzlEnv(ruleClassProvider, uninjectedBuildBzlNativeBindings); this.workspaceBzlEnv = createWorkspaceBzlEnv(ruleClassProvider, workspaceBzlNativeBindings); + this.bzlmodBzlEnv = createWorkspaceBzlEnv(ruleClassProvider, workspaceBzlNativeBindings); this.builtinsBzlEnv = createBuiltinsBzlEnv( ruleClassProvider, uninjectedBuildBzlNativeBindings, uninjectedBuildBzlEnv); @@ -126,6 +131,11 @@ public ImmutableMap getBuiltinsBzlEnv() { return builtinsBzlEnv; } + /** Returns the environment for Bzlmod-loaded bzl files. */ + public ImmutableMap getBzlmodBzlEnv() { + return bzlmodBzlEnv; + } + /** * Produces everything that would be in the "native" object for BUILD-loaded bzl files if builtins * injection didn't happen. diff --git a/src/main/java/com/google/devtools/build/lib/packages/RuleFactory.java b/src/main/java/com/google/devtools/build/lib/packages/RuleFactory.java index 858c76888f8d48..449db0855d25f2 100644 --- a/src/main/java/com/google/devtools/build/lib/packages/RuleFactory.java +++ b/src/main/java/com/google/devtools/build/lib/packages/RuleFactory.java @@ -73,7 +73,7 @@ public RuleClass getRuleClass(String ruleClassName) { *

It is the caller's responsibility to add the rule to the package (the caller may choose not * to do so if, for example, the rule has errors). */ - static Rule createRule( + public static Rule createRule( Package.Builder pkgBuilder, RuleClass ruleClass, BuildLangTypedAttributeValuesMap attributeValues, @@ -203,7 +203,7 @@ public static Rule createAndAddRule( * not be constructed. It contains an error message. */ public static class InvalidRuleException extends Exception { - private InvalidRuleException(String message) { + public InvalidRuleException(String message) { super(message); } } diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/BUILD b/src/main/java/com/google/devtools/build/lib/skyframe/BUILD index 89bcca62c72539..107d9fd3fc58d6 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/BUILD +++ b/src/main/java/com/google/devtools/build/lib/skyframe/BUILD @@ -27,6 +27,7 @@ java_library( "BuildConfigurationFunction.java", "BuildInfoCollectionFunction.java", "BzlLoadFunction.java", + "BzlmodRepoRuleFunction.java", "CompletionFunction.java", "ConfiguredTargetFunction.java", "ConstraintValueLookupUtil.java", @@ -270,6 +271,10 @@ java_library( "//src/main/java/com/google/devtools/build/lib/analysis:workspace_status_action", "//src/main/java/com/google/devtools/build/lib/analysis/platform", "//src/main/java/com/google/devtools/build/lib/analysis/platform:utils", + "//src/main/java/com/google/devtools/build/lib/bazel/bzlmod:repo_spec_functions", + "//src/main/java/com/google/devtools/build/lib/bazel/bzlmod:repo_spec_values", + "//src/main/java/com/google/devtools/build/lib/bazel/bzlmod:repo_rule_creator", + "//src/main/java/com/google/devtools/build/lib/bazel/bzlmod:repo_rule_value", "//src/main/java/com/google/devtools/build/lib/bugreport", "//src/main/java/com/google/devtools/build/lib/buildeventstream", "//src/main/java/com/google/devtools/build/lib/buildeventstream/proto:build_event_stream_java_proto", diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/BzlLoadFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/BzlLoadFunction.java index 90b89b56f3a0c3..ff4b41e0910ddc 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/BzlLoadFunction.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/BzlLoadFunction.java @@ -835,6 +835,9 @@ private static ImmutableMap getRepositoryMapping .getRepositoryMapping() .getOrDefault(enclosingFileLabel.getRepository(), ImmutableMap.of()); } + } else if (key instanceof BzlLoadValue.KeyForBzlmod) { + // TODO(pcloudy): Implement repo mapping for bzlmod repos + return ImmutableMap.of(); } else { // We are fully done with workspace evaluation so we should get the mappings from the // final RepositoryMappingValue @@ -1041,6 +1044,8 @@ private ImmutableMap getAndDigestPredeclaredEnvironment( return builtins.predeclaredForBuildBzl; } else if (key instanceof BzlLoadValue.KeyForWorkspace) { return starlarkEnv.getWorkspaceBzlEnv(); + } else if (key instanceof BzlLoadValue.KeyForBzlmod) { + return starlarkEnv.getBzlmodBzlEnv(); } else if (key instanceof BzlLoadValue.KeyForBuiltins) { return starlarkEnv.getBuiltinsBzlEnv(); } else { diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/BzlLoadValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/BzlLoadValue.java index ecd9398ee9cf40..df2173b270928d 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/BzlLoadValue.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/BzlLoadValue.java @@ -315,6 +315,57 @@ public int hashCode() { } } + /** + * 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 = Preconditions.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. */ static Key keyForBuild(Label label) { return keyInterner.intern(new KeyForBuild(label, /*isBuildPrelude=*/ false)); @@ -341,4 +392,9 @@ static Key keyForBuiltins(Label label) { static Key keyForBuildPrelude(Label label) { return keyInterner.intern(new KeyForBuild(label, /*isBuildPrelude=*/ true)); } + + /** Constructs a key for loading a .bzl for Bzlmod repos */ + static Key keyForBzlmod(Label label) { + return keyInterner.intern(new KeyForBzlmod(label)); + } } diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/BzlmodRepoRuleFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/BzlmodRepoRuleFunction.java new file mode 100644 index 00000000000000..87c08511453b89 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/skyframe/BzlmodRepoRuleFunction.java @@ -0,0 +1,258 @@ +// 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.skyframe; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.devtools.build.lib.analysis.BlazeDirectories; +import com.google.devtools.build.lib.bazel.bzlmod.BzlmodRepoRuleCreator; +import com.google.devtools.build.lib.bazel.bzlmod.BzlmodRepoRuleValue; +import com.google.devtools.build.lib.bazel.bzlmod.BzlmodRepoRuleValue.Key; +import com.google.devtools.build.lib.bazel.bzlmod.RepoSpec; +import com.google.devtools.build.lib.bazel.bzlmod.RepoSpecsValue; +import com.google.devtools.build.lib.cmdline.Label; +import com.google.devtools.build.lib.cmdline.LabelConstants; +import com.google.devtools.build.lib.cmdline.PackageIdentifier; +import com.google.devtools.build.lib.events.StoredEventHandler; +import com.google.devtools.build.lib.packages.NoSuchPackageException; +import com.google.devtools.build.lib.packages.Package; +import com.google.devtools.build.lib.packages.PackageFactory; +import com.google.devtools.build.lib.packages.Rule; +import com.google.devtools.build.lib.packages.RuleClass; +import com.google.devtools.build.lib.packages.RuleClassProvider; +import com.google.devtools.build.lib.packages.RuleFactory; +import com.google.devtools.build.lib.packages.RuleFactory.BuildLangTypedAttributeValuesMap; +import com.google.devtools.build.lib.packages.RuleFactory.InvalidRuleException; +import com.google.devtools.build.lib.server.FailureDetails.PackageLoading; +import com.google.devtools.build.lib.util.Pair; +import com.google.devtools.build.lib.vfs.Root; +import com.google.devtools.build.lib.vfs.RootedPath; +import com.google.devtools.build.skyframe.SkyFunction; +import com.google.devtools.build.skyframe.SkyFunctionException; +import com.google.devtools.build.skyframe.SkyFunctionException.Transience; +import com.google.devtools.build.skyframe.SkyKey; +import com.google.devtools.build.skyframe.SkyValue; + +import net.starlark.java.eval.Module; +import net.starlark.java.eval.StarlarkSemantics; +import net.starlark.java.eval.StarlarkThread.CallStackEntry; +import net.starlark.java.syntax.Location; + +import javax.annotation.Nullable; + +public class BzlmodRepoRuleFunction implements SkyFunction { + + private static final String TOOLS_REPO = "bazel_tools"; + + private final PackageFactory packageFactory; + private final RuleClassProvider ruleClassProvider; + private final BlazeDirectories directories; + private static final PackageIdentifier rootPackage = PackageIdentifier.createInMainRepo(""); + + public BzlmodRepoRuleFunction( + PackageFactory packageFactory, + RuleClassProvider ruleClassProvider, + BlazeDirectories directories) { + this.packageFactory = packageFactory; + this.ruleClassProvider = ruleClassProvider; + this.directories = directories; + } + + @Nullable + @Override + public SkyValue compute(SkyKey skyKey, Environment env) + throws SkyFunctionException, InterruptedException { + StarlarkSemantics starlarkSemantics = PrecomputedValue.STARLARK_SEMANTICS.get(env); + if (starlarkSemantics == null) { + return null; + } + + String repositoryName = ((Key) skyKey).argument(); + RepoSpec repoSpec; + + // @bazel_tools is a special repo that we pull from the extracted install dir. + if (repositoryName.equals(TOOLS_REPO)) { + repoSpec = RepoSpec.create( + "local_repository", + ImmutableMap.of( + "name", "bazel_tools", "path", + directories.getEmbeddedBinariesRoot().getChild("embedded_tools").getPathString())); + return getRuleFromSpec(repoSpec, starlarkSemantics, env); + } + + // Look for repositories defined by non-registry overrides. + RepoSpecsValue overrideDepRepos = + (RepoSpecsValue) env.getValue(RepoSpecsValue.KEY_FOR_OVERRIDE_DEP); + if (overrideDepRepos == null) { + return null; + } + repoSpec = overrideDepRepos.getRepository(repositoryName); + if (repoSpec != null) { + return getRuleFromSpec(repoSpec, starlarkSemantics, env); + } + + // Look for repositories derived from native Bazel Modules + RepoSpecsValue bazelModuleRepos = + (RepoSpecsValue) env.getValue(RepoSpecsValue.KEY_FOR_BAZEL_MODULE); + if (bazelModuleRepos == null) { + return null; + } + repoSpec = bazelModuleRepos.getRepository(repositoryName); + if (repoSpec != null) { + return getRuleFromSpec(repoSpec, starlarkSemantics, env); + } + + // Look for repositories derived from module rules if the repo is not requested for module rule + // resolution. + RepoSpecsValue moduleRuleRepos = + (RepoSpecsValue) env.getValue(RepoSpecsValue.KEY_FOR_MODULE_RULE); + if (moduleRuleRepos == null) { + return null; + } + repoSpec = moduleRuleRepos.getRepository(repositoryName); + if (repoSpec != null) { + return getRuleFromSpec(repoSpec, starlarkSemantics, env); + } + + return BzlmodRepoRuleValue.REPO_RULE_NOT_FOUND_VALUE; + } + + private BzlmodRepoRuleValue getRuleFromSpec(RepoSpec repoSpec, + StarlarkSemantics starlarkSemantics, Environment env) + throws BzlmodRepoRuleFunctionException, InterruptedException { + if (repoSpec.isNativeRepoRule()) { + return getNativeRepoRule(repoSpec, starlarkSemantics, env); + } else { + return getStarlarkRepoRule(repoSpec, starlarkSemantics, env); + } + } + + // Create the external package builder, which is only for the convenience of creating + // repository rules. + private Package.Builder getExternalPackageBuilder(StarlarkSemantics semantics) { + RootedPath bzlmodFile = + RootedPath.toRootedPath( + Root.fromPath(directories.getWorkspace()), LabelConstants.MODULE_DOT_BAZEL_FILE_NAME); + + Package.Builder pkg = + packageFactory.newExternalPackageBuilder( + bzlmodFile, ruleClassProvider.getRunfilesPrefix(), semantics); + return pkg; + } + + private BzlmodRepoRuleValue getNativeRepoRule( + RepoSpec repoSpec, StarlarkSemantics semantics, Environment env) + throws InterruptedException, BzlmodRepoRuleFunctionException { + RuleClass ruleClass = ruleClassProvider.getRuleClassMap().get(repoSpec.getRuleClassName()); + BuildLangTypedAttributeValuesMap attributeValues = + new BuildLangTypedAttributeValuesMap(repoSpec.getAttributes()); + ImmutableList.Builder callStack = ImmutableList.builder(); + callStack.add(new CallStackEntry("BzlmodRepoRuleFunction.getNativeRepoRule", Location.BUILTIN)); + Rule rule; + try { + Package.Builder pkg = getExternalPackageBuilder(semantics); + rule = RuleFactory.createRule( + pkg, ruleClass, attributeValues, env.getListener(), semantics, callStack.build()); + // We need to actually build the package so that the rule has the correct package reference. + pkg.build(); + } catch (InvalidRuleException e) { + throw new BzlmodRepoRuleFunctionException(e, Transience.PERSISTENT); + } catch (NoSuchPackageException e) { + throw new BzlmodRepoRuleFunctionException(e, Transience.PERSISTENT); + } + return new BzlmodRepoRuleValue(rule); + } + + private BzlmodRepoRuleValue getStarlarkRepoRule( + RepoSpec repoSpec, StarlarkSemantics semantics, Environment env) + throws InterruptedException, BzlmodRepoRuleFunctionException { + ImmutableList> programLoads = + ImmutableList.of(Pair.of(repoSpec.getBzlFile(), Location.BUILTIN)); + + ImmutableList