diff --git a/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkGlobalsImpl.java b/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkGlobalsImpl.java index 2a5d42d1e69d79..b24c47c18f9658 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkGlobalsImpl.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkGlobalsImpl.java @@ -22,6 +22,7 @@ import com.google.devtools.build.lib.collect.nestedset.Depset; import com.google.devtools.build.lib.packages.BuildGlobals; import com.google.devtools.build.lib.packages.Proto; +import com.google.devtools.build.lib.packages.RepoCallable; import com.google.devtools.build.lib.packages.SelectorList; import com.google.devtools.build.lib.packages.StarlarkGlobals; import com.google.devtools.build.lib.packages.StarlarkNativeModule; @@ -120,4 +121,11 @@ public ImmutableMap getSclToplevels() { env.put("struct", StructProvider.STRUCT); return env.buildOrThrow(); } + + @Override + public ImmutableMap getRepoToplevels() { + ImmutableMap.Builder env = ImmutableMap.builder(); + Starlark.addMethods(env, RepoCallable.INSTANCE); + return env.buildOrThrow(); + } } 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 783396f7fe41bd..3a57971736a3f4 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 @@ -44,6 +44,7 @@ public class LabelConstants { 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 PathFragment REPO_FILE_NAME = PathFragment.create("REPO.bazel"); public static final PathFragment MODULE_LOCKFILE_NAME = PathFragment.create("MODULE.bazel.lock"); diff --git a/src/main/java/com/google/devtools/build/lib/packages/PackageArgs.java b/src/main/java/com/google/devtools/build/lib/packages/PackageArgs.java index bacf3d749f4cad..53971863fd5013 100644 --- a/src/main/java/com/google/devtools/build/lib/packages/PackageArgs.java +++ b/src/main/java/com/google/devtools/build/lib/packages/PackageArgs.java @@ -37,6 +37,8 @@ */ @AutoValue public abstract class PackageArgs { + public static final PackageArgs EMPTY = PackageArgs.builder().build(); + public static final PackageArgs DEFAULT = PackageArgs.builder() .setDefaultVisibility(RuleVisibility.PRIVATE) diff --git a/src/main/java/com/google/devtools/build/lib/packages/RepoCallable.java b/src/main/java/com/google/devtools/build/lib/packages/RepoCallable.java new file mode 100644 index 00000000000000..a78514be8f5fa5 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/packages/RepoCallable.java @@ -0,0 +1,59 @@ +// Copyright 2023 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 java.util.Map; +import net.starlark.java.annot.Param; +import net.starlark.java.annot.StarlarkMethod; +import net.starlark.java.eval.EvalException; +import net.starlark.java.eval.Starlark; +import net.starlark.java.eval.StarlarkThread; + +/** Definition of the {@code repo()} function used in REPO.bazel files. */ +public final class RepoCallable { + private RepoCallable() {} + + public static final RepoCallable INSTANCE = new RepoCallable(); + + @StarlarkMethod( + name = "repo", + documented = false, // documented separately + extraKeywords = @Param(name = "kwargs"), + useStarlarkThread = true) + public Object repoCallable(Map kwargs, StarlarkThread thread) + throws EvalException { + RepoThreadContext context = RepoThreadContext.fromOrFail(thread, "repo()"); + if (context.isRepoFunctionCalled()) { + throw Starlark.errorf("'repo' can only be called once in the REPO.bazel file"); + } + context.setRepoFunctionCalled(); + + if (kwargs.isEmpty()) { + throw Starlark.errorf("at least one argument must be given to the 'repo' function"); + } + + PackageArgs.Builder pkgArgsBuilder = PackageArgs.builder(); + for (Map.Entry kwarg : kwargs.entrySet()) { + PackageArgs.processParam( + kwarg.getKey(), + kwarg.getValue(), + "repo() argument '" + kwarg.getKey() + "'", + context.getLabelConverter(), + pkgArgsBuilder); + } + context.setPackageArgs(pkgArgsBuilder.build()); + return Starlark.NONE; + } +} diff --git a/src/main/java/com/google/devtools/build/lib/packages/RepoThreadContext.java b/src/main/java/com/google/devtools/build/lib/packages/RepoThreadContext.java new file mode 100644 index 00000000000000..25bc13e43af36a --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/packages/RepoThreadContext.java @@ -0,0 +1,63 @@ +// Copyright 2023 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 net.starlark.java.eval.EvalException; +import net.starlark.java.eval.Starlark; +import net.starlark.java.eval.StarlarkThread; + +/** Context object for a Starlark thread evaluating the REPO.bazel file. */ +public class RepoThreadContext { + private final LabelConverter labelConverter; + private PackageArgs packageArgs = PackageArgs.EMPTY; + private boolean repoFunctionCalled = false; + + public static RepoThreadContext fromOrFail(StarlarkThread thread, String what) + throws EvalException { + RepoThreadContext context = thread.getThreadLocal(RepoThreadContext.class); + if (context == null) { + throw Starlark.errorf("%s can only be called from REPO.bazel", what); + } + return context; + } + + public void storeInThread(StarlarkThread thread) { + thread.setThreadLocal(RepoThreadContext.class, this); + } + + public RepoThreadContext(LabelConverter labelConverter) { + this.labelConverter = labelConverter; + } + + public LabelConverter getLabelConverter() { + return labelConverter; + } + + public boolean isRepoFunctionCalled() { + return repoFunctionCalled; + } + + public void setRepoFunctionCalled() { + repoFunctionCalled = true; + } + + public void setPackageArgs(PackageArgs packageArgs) { + this.packageArgs = packageArgs; + } + + public PackageArgs getPackageArgs() { + return packageArgs; + } +} diff --git a/src/main/java/com/google/devtools/build/lib/packages/StarlarkGlobals.java b/src/main/java/com/google/devtools/build/lib/packages/StarlarkGlobals.java index a27d63087186db..69370613bf0ce1 100644 --- a/src/main/java/com/google/devtools/build/lib/packages/StarlarkGlobals.java +++ b/src/main/java/com/google/devtools/build/lib/packages/StarlarkGlobals.java @@ -64,4 +64,7 @@ public interface StarlarkGlobals { /** Returns the top-levels for .scl files. */ ImmutableMap getSclToplevels(); + + /** Returns the top-levels for REPO.bazel files. */ + ImmutableMap getRepoToplevels(); } diff --git a/src/main/java/com/google/devtools/build/lib/packages/StarlarkNativeModule.java b/src/main/java/com/google/devtools/build/lib/packages/StarlarkNativeModule.java index 3017d40514159a..9dbb23276648f5 100644 --- a/src/main/java/com/google/devtools/build/lib/packages/StarlarkNativeModule.java +++ b/src/main/java/com/google/devtools/build/lib/packages/StarlarkNativeModule.java @@ -68,7 +68,6 @@ import net.starlark.java.syntax.Location; /** The Starlark native module. */ -// TODO(cparsons): Move the definition of native.package() to this class. public class StarlarkNativeModule implements StarlarkNativeModuleApi { private static final GoogleLogger logger = GoogleLogger.forEnclosingClass(); 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 77a61c88c91cd1..eb243fe3e7ee03 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/BUILD +++ b/src/main/java/com/google/devtools/build/lib/skyframe/BUILD @@ -165,6 +165,8 @@ java_library( ":recursive_package_provider_backed_target_pattern_resolver", ":recursive_pkg_function", ":recursive_pkg_value", + ":repo_file_function", + ":repo_file_value", ":repository_mapping_function", ":repository_mapping_value", ":rule_configured_target_value", @@ -2191,6 +2193,40 @@ java_library( ], ) +java_library( + name = "repo_file_function", + srcs = ["RepoFileFunction.java"], + deps = [ + ":precomputed_value", + ":repo_file_value", + ":repository_mapping_value", + "//src/main/java/com/google/devtools/build/lib/actions:file_metadata", + "//src/main/java/com/google/devtools/build/lib/cmdline", + "//src/main/java/com/google/devtools/build/lib/events", + "//src/main/java/com/google/devtools/build/lib/packages", + "//src/main/java/com/google/devtools/build/lib/rules:repository/repository_directory_value", + "//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", + "//src/main/java/net/starlark/java/eval", + "//src/main/java/net/starlark/java/syntax", + "//third_party:jsr305", + ], +) + +java_library( + name = "repo_file_value", + srcs = ["RepoFileValue.java"], + deps = [ + ":sky_functions", + "//src/main/java/com/google/devtools/build/lib/cmdline", + "//src/main/java/com/google/devtools/build/lib/packages", + "//src/main/java/com/google/devtools/build/skyframe:skyframe-objects", + "//third_party:auto_value", + ], +) + java_library( name = "repository_mapping_function", srcs = ["RepositoryMappingFunction.java"], diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/BazelSkyframeExecutorConstants.java b/src/main/java/com/google/devtools/build/lib/skyframe/BazelSkyframeExecutorConstants.java index afad3a3897e704..86c80c3cf464a8 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/BazelSkyframeExecutorConstants.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/BazelSkyframeExecutorConstants.java @@ -58,10 +58,13 @@ private BazelSkyframeExecutorConstants() {} ACTION_ON_IO_EXCEPTION_READING_BUILD_FILE = ActionOnIOExceptionReadingBuildFile.UseOriginalIOException.INSTANCE; + public static final boolean USE_REPO_DOT_BAZEL = true; + public static SequencedSkyframeExecutor.Builder newBazelSkyframeExecutorBuilder() { return SequencedSkyframeExecutor.builder() .setIgnoredPackagePrefixesFunction(IGNORED_PACKAGE_PREFIXES_FUNCTION) .setActionOnIOExceptionReadingBuildFile(ACTION_ON_IO_EXCEPTION_READING_BUILD_FILE) + .setShouldUseRepoDotBazel(USE_REPO_DOT_BAZEL) .setCrossRepositoryLabelViolationStrategy(CROSS_REPOSITORY_LABEL_VIOLATION_STRATEGY) .setBuildFilesByPriority(BUILD_FILES_BY_PRIORITY) .setExternalPackageHelper(EXTERNAL_PACKAGE_HELPER); diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java index 00f1ac5b815565..9f6f48d3cf62d2 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java @@ -63,6 +63,7 @@ import com.google.devtools.build.lib.server.FailureDetails.PackageLoading.Code; import com.google.devtools.build.lib.skyframe.BzlLoadFunction.BzlLoadFailedException; import com.google.devtools.build.lib.skyframe.GlobValue.InvalidGlobPatternException; +import com.google.devtools.build.lib.skyframe.RepoFileFunction.BadRepoFileException; import com.google.devtools.build.lib.skyframe.StarlarkBuiltinsFunction.BuiltinsFailedException; import com.google.devtools.build.lib.util.DetailedExitCode; import com.google.devtools.build.lib.util.Pair; @@ -118,6 +119,7 @@ public class PackageFunction implements SkyFunction { private final ActionOnIOExceptionReadingBuildFile actionOnIOExceptionReadingBuildFile; + private final boolean shouldUseRepoDotBazel; private final GlobbingStrategy globbingStrategy; private final Function threadStateReceiverFactoryForMetrics; @@ -181,6 +183,7 @@ public PackageFunction( @Nullable BzlLoadFunction bzlLoadFunctionForInlining, @Nullable PackageProgressReceiver packageProgress, ActionOnIOExceptionReadingBuildFile actionOnIOExceptionReadingBuildFile, + boolean shouldUseRepoDotBazel, GlobbingStrategy globbingStrategy, Function threadStateReceiverFactoryForMetrics) { this.bzlLoadFunctionForInlining = bzlLoadFunctionForInlining; @@ -190,6 +193,7 @@ public PackageFunction( this.numPackagesSuccessfullyLoaded = numPackagesSuccessfullyLoaded; this.packageProgress = packageProgress; this.actionOnIOExceptionReadingBuildFile = actionOnIOExceptionReadingBuildFile; + this.shouldUseRepoDotBazel = shouldUseRepoDotBazel; this.globbingStrategy = globbingStrategy; this.threadStateReceiverFactoryForMetrics = threadStateReceiverFactoryForMetrics; } @@ -1240,6 +1244,28 @@ private LoadedPackage loadPackage( IgnoredPackagePrefixesValue repositoryIgnoredPackagePrefixes = (IgnoredPackagePrefixesValue) env.getValue(IgnoredPackagePrefixesValue.key(packageId.getRepository())); + RepoFileValue repoFileValue; + if (shouldUseRepoDotBazel) { + try { + repoFileValue = + (RepoFileValue) + env.getValueOrThrow( + RepoFileValue.key(packageId.getRepository()), + IOException.class, + BadRepoFileException.class); + } catch (IOException | BadRepoFileException e) { + throw PackageFunctionException.builder() + .setType(PackageFunctionException.Type.BUILD_FILE_CONTAINS_ERRORS) + .setPackageIdentifier(packageId) + .setTransience(Transience.PERSISTENT) + .setException(e) + .setMessage("bad REPO.bazel file") + .setPackageLoadingCode(PackageLoading.Code.BAD_REPO_FILE) + .build(); + } + } else { + repoFileValue = RepoFileValue.EMPTY; + } if (env.valuesMissing()) { return null; } @@ -1375,8 +1401,9 @@ private LoadedPackage loadPackage( .setFilename(buildFileRootedPath) .setConfigSettingVisibilityPolicy(configSettingVisibilityPolicy); - pkgBuilder.mergePackageArgsFrom( - PackageArgs.builder().setDefaultVisibility(defaultVisibility)); + pkgBuilder + .mergePackageArgsFrom(PackageArgs.builder().setDefaultVisibility(defaultVisibility)) + .mergePackageArgsFrom(repoFileValue.packageArgs()); Set globDepKeys = ImmutableSet.of(); diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/RepoFileFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/RepoFileFunction.java new file mode 100644 index 00000000000000..4d77052f11c935 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/skyframe/RepoFileFunction.java @@ -0,0 +1,195 @@ +// Copyright 2023 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.devtools.build.lib.actions.FileValue; +import com.google.devtools.build.lib.cmdline.LabelConstants; +import com.google.devtools.build.lib.cmdline.PackageIdentifier; +import com.google.devtools.build.lib.cmdline.RepositoryMapping; +import com.google.devtools.build.lib.cmdline.RepositoryName; +import com.google.devtools.build.lib.events.Event; +import com.google.devtools.build.lib.events.ExtendedEventHandler; +import com.google.devtools.build.lib.packages.BazelStarlarkEnvironment; +import com.google.devtools.build.lib.packages.LabelConverter; +import com.google.devtools.build.lib.packages.PackageArgs; +import com.google.devtools.build.lib.packages.RepoThreadContext; +import com.google.devtools.build.lib.rules.repository.RepositoryDirectoryValue; +import com.google.devtools.build.lib.vfs.FileSystemUtils; +import com.google.devtools.build.lib.vfs.Path; +import com.google.devtools.build.lib.vfs.PathFragment; +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 java.io.IOException; +import javax.annotation.Nullable; +import net.starlark.java.eval.EvalException; +import net.starlark.java.eval.Module; +import net.starlark.java.eval.Mutability; +import net.starlark.java.eval.Starlark; +import net.starlark.java.eval.StarlarkSemantics; +import net.starlark.java.eval.StarlarkThread; +import net.starlark.java.syntax.ParserInput; +import net.starlark.java.syntax.Program; +import net.starlark.java.syntax.StarlarkFile; +import net.starlark.java.syntax.SyntaxError; + +/** The function to evaluate the REPO.bazel file at the root of a repo. */ +public class RepoFileFunction implements SkyFunction { + private final BazelStarlarkEnvironment starlarkEnv; + private final Path workspaceRoot; + + public RepoFileFunction(BazelStarlarkEnvironment starlarkEnv, Path workspaceRoot) { + this.starlarkEnv = starlarkEnv; + this.workspaceRoot = workspaceRoot; + } + + @Nullable + @Override + public SkyValue compute(SkyKey skyKey, Environment env) + throws SkyFunctionException, InterruptedException { + RepositoryName repoName = (RepositoryName) skyKey.argument(); + // First we need to find the REPO.bazel file. How we do this depends on whether this is for the + // main repo or an external repo. + Path repoRoot; + if (repoName.isMain()) { + repoRoot = workspaceRoot; + } else { + RepositoryDirectoryValue repoDirValue = + (RepositoryDirectoryValue) env.getValue(RepositoryDirectoryValue.key(repoName)); + if (repoDirValue == null) { + return null; + } + repoRoot = repoDirValue.getPath(); + } + RootedPath repoFilePath = + RootedPath.toRootedPath(Root.fromPath(repoRoot), LabelConstants.REPO_FILE_NAME); + FileValue repoFileValue = (FileValue) env.getValue(FileValue.key(repoFilePath)); + if (repoFileValue == null) { + return null; + } + if (!repoFileValue.exists()) { + // It's okay to not have a REPO.bazel file. + return RepoFileValue.of(PackageArgs.EMPTY); + } + + // Now we can actually evaluate the file. + StarlarkSemantics starlarkSemantics = PrecomputedValue.STARLARK_SEMANTICS.get(env); + RepositoryMappingValue repoMapping = + (RepositoryMappingValue) env.getValue(RepositoryMappingValue.key(repoName)); + RepositoryMappingValue mainRepoMapping = + (RepositoryMappingValue) env.getValue(RepositoryMappingValue.key(RepositoryName.MAIN)); + if (env.valuesMissing()) { + return null; + } + StarlarkFile repoFile = readAndParseRepoFile(repoFilePath.asPath(), env); + PackageArgs packageArgs = + evalRepoFile( + repoFile, + repoName, + getDisplayNameForRepo(repoName, mainRepoMapping.getRepositoryMapping()), + repoMapping.getRepositoryMapping(), + starlarkSemantics, + env.getListener()); + + return RepoFileValue.of(packageArgs); + } + + private static StarlarkFile readAndParseRepoFile(Path path, Environment env) + throws RepoFileFunctionException { + byte[] contents; + try { + contents = FileSystemUtils.readWithKnownFileSize(path, path.getFileSize()); + } catch (IOException e) { + throw new RepoFileFunctionException( + new IOException("error reading REPO.bazel file at " + path, e), Transience.TRANSIENT); + } + StarlarkFile starlarkFile = + StarlarkFile.parse(ParserInput.fromUTF8(contents, path.getPathString())); + if (!starlarkFile.ok()) { + Event.replayEventsOn(env.getListener(), starlarkFile.errors()); + throw new RepoFileFunctionException( + new BadRepoFileException("error parsing REPO.bazel file at " + path)); + } + return starlarkFile; + } + + private static String getDisplayNameForRepo( + RepositoryName repoName, RepositoryMapping mainRepoMapping) { + String displayName = repoName.getDisplayForm(mainRepoMapping); + if (displayName.isEmpty()) { + return "the main repo"; + } + return displayName; + } + + private PackageArgs evalRepoFile( + StarlarkFile starlarkFile, + RepositoryName repoName, + String repoDisplayName, + RepositoryMapping repoMapping, + StarlarkSemantics starlarkSemantics, + ExtendedEventHandler handler) + throws RepoFileFunctionException, InterruptedException { + try (Mutability mu = Mutability.create("repo file", repoName)) { + Module predeclared = + Module.withPredeclared( + starlarkSemantics, starlarkEnv.getStarlarkGlobals().getRepoToplevels()); + Program program = Program.compileFile(starlarkFile, predeclared); + // TODO(wyv): check that `program` has no `def`, `if`, etc + StarlarkThread thread = new StarlarkThread(mu, starlarkSemantics); + thread.setPrintHandler(Event.makeDebugPrintHandler(handler)); + RepoThreadContext context = + new RepoThreadContext( + new LabelConverter( + PackageIdentifier.create(repoName, PathFragment.EMPTY_FRAGMENT), repoMapping)); + context.storeInThread(thread); + Starlark.execFileProgram(program, predeclared, thread); + return context.getPackageArgs(); + } catch (SyntaxError.Exception e) { + Event.replayEventsOn(handler, e.errors()); + throw new RepoFileFunctionException( + new BadRepoFileException("error parsing REPO.bazel file for " + repoDisplayName, e)); + } catch (EvalException e) { + handler.handle(Event.error(e.getMessageWithStack())); + throw new RepoFileFunctionException( + new BadRepoFileException("error evaluating REPO.bazel file for " + repoDisplayName, e)); + } + } + + /** Thrown when something is wrong with the contents of the REPO.bazel file of a certain repo. */ + public static class BadRepoFileException extends Exception { + public BadRepoFileException(String message) { + super(message); + } + + public BadRepoFileException(String message, Exception cause) { + super(message, cause); + } + } + + static class RepoFileFunctionException extends SkyFunctionException { + private RepoFileFunctionException(IOException e, Transience transience) { + super(e, transience); + } + + private RepoFileFunctionException(BadRepoFileException e) { + super(e, Transience.PERSISTENT); + } + } +} diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/RepoFileValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/RepoFileValue.java new file mode 100644 index 00000000000000..34763fee4f2c50 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/skyframe/RepoFileValue.java @@ -0,0 +1,51 @@ +// Copyright 2023 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.auto.value.AutoValue; +import com.google.devtools.build.lib.cmdline.RepositoryName; +import com.google.devtools.build.lib.packages.PackageArgs; +import com.google.devtools.build.skyframe.AbstractSkyKey; +import com.google.devtools.build.skyframe.SkyFunctionName; +import com.google.devtools.build.skyframe.SkyValue; + +/** Contains information about the REPO.bazel file at the root of a repo. */ +@AutoValue +public abstract class RepoFileValue implements SkyValue { + public static final RepoFileValue EMPTY = of(PackageArgs.EMPTY); + + public abstract PackageArgs packageArgs(); + + public static RepoFileValue of(PackageArgs packageArgs) { + return new AutoValue_RepoFileValue(packageArgs); + } + + public static Key key(RepositoryName repoName) { + return new Key(repoName); + } + + /** Key type for {@link RepoFileValue}. */ + public static class Key extends AbstractSkyKey { + + private Key(RepositoryName repoName) { + super(repoName); + } + + @Override + public SkyFunctionName functionName() { + return SkyFunctions.REPO_FILE; + } + } +} diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SequencedSkyframeExecutor.java b/src/main/java/com/google/devtools/build/lib/skyframe/SequencedSkyframeExecutor.java index d89ca18df21d28..d6e8ae89986221 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/SequencedSkyframeExecutor.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/SequencedSkyframeExecutor.java @@ -153,6 +153,7 @@ private SequencedSkyframeExecutor( ExternalPackageHelper externalPackageHelper, @Nullable SkyframeExecutorRepositoryHelpersHolder repositoryHelpersHolder, ActionOnIOExceptionReadingBuildFile actionOnIOExceptionReadingBuildFile, + boolean shouldUseRepoDotBazel, SkyKeyStateReceiver skyKeyStateReceiver, BugReporter bugReporter) { super( @@ -170,6 +171,7 @@ private SequencedSkyframeExecutor( buildFilesByPriority, externalPackageHelper, actionOnIOExceptionReadingBuildFile, + shouldUseRepoDotBazel, /* shouldUnblockCpuWorkWhenFetchingDeps= */ false, new PackageProgressReceiver(), new ConfiguredTargetProgressReceiver(), @@ -721,6 +723,7 @@ public static final class Builder { private ImmutableList buildFilesByPriority; private ExternalPackageHelper externalPackageHelper; private ActionOnIOExceptionReadingBuildFile actionOnIOExceptionReadingBuildFile; + private boolean shouldUseRepoDotBazel = true; // Fields with default values. private ImmutableMap extraSkyFunctions = ImmutableMap.of(); @@ -767,6 +770,7 @@ public SequencedSkyframeExecutor build() { externalPackageHelper, repositoryHelpersHolder, actionOnIOExceptionReadingBuildFile, + shouldUseRepoDotBazel, skyKeyStateReceiver, bugReporter); skyframeExecutor.init(); @@ -869,6 +873,12 @@ public Builder setActionOnIOExceptionReadingBuildFile( return this; } + @CanIgnoreReturnValue + public Builder setShouldUseRepoDotBazel(boolean shouldUseRepoDotBazel) { + this.shouldUseRepoDotBazel = shouldUseRepoDotBazel; + return this; + } + @CanIgnoreReturnValue public Builder setSkyframeExecutorConsumerOnInit( Consumer skyframeExecutorConsumerOnInit) { diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctions.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctions.java index b3326be03a9e59..044ddf9f1db110 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctions.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctions.java @@ -141,6 +141,7 @@ public final class SkyFunctions { SkyFunctionName.createHermetic("RESOLVED_HASH_VALUES"); public static final SkyFunctionName MODULE_FILE = SkyFunctionName.createNonHermetic("MODULE_FILE"); + public static final SkyFunctionName REPO_FILE = SkyFunctionName.createHermetic("REPO_FILE"); public static final SkyFunctionName BUILD_DRIVER = SkyFunctionName.createNonHermetic("BUILD_DRIVER"); public static final SkyFunctionName BAZEL_MODULE_RESOLUTION = diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java index 0921bfb8995faf..a90d08136afe96 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java @@ -384,6 +384,8 @@ public abstract class SkyframeExecutor implements WalkableGraphFactory { private final ActionOnIOExceptionReadingBuildFile actionOnIOExceptionReadingBuildFile; + private final boolean shouldUseRepoDotBazel; + private final boolean shouldUnblockCpuWorkWhenFetchingDeps; private final SkyKeyStateReceiver skyKeyStateReceiver; @@ -484,6 +486,7 @@ protected SkyframeExecutor( ImmutableList buildFilesByPriority, ExternalPackageHelper externalPackageHelper, ActionOnIOExceptionReadingBuildFile actionOnIOExceptionReadingBuildFile, + boolean shouldUseRepoDotBazel, boolean shouldUnblockCpuWorkWhenFetchingDeps, @Nullable PackageProgressReceiver packageProgress, @Nullable ConfiguredTargetProgressReceiver configuredTargetProgress, @@ -539,6 +542,7 @@ protected SkyframeExecutor( this.buildFilesByPriority = buildFilesByPriority; this.externalPackageHelper = externalPackageHelper; this.actionOnIOExceptionReadingBuildFile = actionOnIOExceptionReadingBuildFile; + this.shouldUseRepoDotBazel = shouldUseRepoDotBazel; this.packageProgress = packageProgress; this.configuredTargetProgress = configuredTargetProgress; this.diffAwarenessManager = @@ -619,6 +623,7 @@ private ImmutableMap skyFunctions() { bzlLoadFunctionForInliningPackageAndWorkspaceNodes, packageProgress, actionOnIOExceptionReadingBuildFile, + shouldUseRepoDotBazel, getGlobbingStrategy(), skyKeyStateReceiver::makeThreadStateReceiver)); map.put(SkyFunctions.PACKAGE_ERROR, new PackageErrorFunction()); @@ -666,6 +671,14 @@ private ImmutableMap skyFunctions() { pkgFactory, directories, bzlLoadFunctionForInliningPackageAndWorkspaceNodes)); + map.put( + SkyFunctions.REPO_FILE, + shouldUseRepoDotBazel + ? new RepoFileFunction( + ruleClassProvider.getBazelStarlarkEnvironment(), directories.getWorkspace()) + : (k, env) -> { + throw new IllegalStateException("supposed to be unused"); + }); map.put(SkyFunctions.EXTERNAL_PACKAGE, new ExternalPackageFunction(externalPackageHelper)); map.put( BzlmodRepoRuleValue.BZLMOD_REPO_RULE, diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/packages/AbstractPackageLoader.java b/src/main/java/com/google/devtools/build/lib/skyframe/packages/AbstractPackageLoader.java index dd4b5fd1a511de..cb8148e41c1748 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/packages/AbstractPackageLoader.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/packages/AbstractPackageLoader.java @@ -70,6 +70,7 @@ import com.google.devtools.build.lib.skyframe.PackageValue; import com.google.devtools.build.lib.skyframe.PrecomputedFunction; import com.google.devtools.build.lib.skyframe.PrecomputedValue; +import com.google.devtools.build.lib.skyframe.RepoFileFunction; import com.google.devtools.build.lib.skyframe.RepositoryMappingFunction; import com.google.devtools.build.lib.skyframe.SkyFunctions; import com.google.devtools.build.lib.skyframe.StarlarkBuiltinsFunction; @@ -416,6 +417,8 @@ private MemoizingEvaluator makeFreshEvaluator() { protected abstract ActionOnIOExceptionReadingBuildFile getActionOnIOExceptionReadingBuildFile(); + protected abstract boolean shouldUseRepoDotBazel(); + private ImmutableMap makeFreshSkyFunctions() { TimestampGranularityMonitor tsgm = new TimestampGranularityMonitor(BlazeClock.instance()); DefaultSyscallCache syscallCache = @@ -476,6 +479,10 @@ public String getBaseNameForLoadedPackage(PackageIdentifier packageName) { WorkspaceFileValue.WORKSPACE_FILE, new WorkspaceFileFunction( ruleClassProvider, pkgFactory, directories, /* bzlLoadFunctionForInlining= */ null)) + .put( + SkyFunctions.REPO_FILE, + new RepoFileFunction( + ruleClassProvider.getBazelStarlarkEnvironment(), directories.getWorkspace())) .put(SkyFunctions.EXTERNAL_PACKAGE, new ExternalPackageFunction(getExternalPackageHelper())) .put( BzlmodRepoRuleValue.BZLMOD_REPO_RULE, @@ -491,6 +498,7 @@ public String getBaseNameForLoadedPackage(PackageIdentifier packageName) { /* bzlLoadFunctionForInlining= */ null, /* packageProgress= */ null, getActionOnIOExceptionReadingBuildFile(), + shouldUseRepoDotBazel(), // Tell PackageFunction to optimize for our use-case of no incrementality. GlobbingStrategy.NON_SKYFRAME, k -> ThreadStateReceiver.NULL_INSTANCE)) diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/packages/BUILD b/src/main/java/com/google/devtools/build/lib/skyframe/packages/BUILD index 2b42184adf89d1..9ba1d3967aa830 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/packages/BUILD +++ b/src/main/java/com/google/devtools/build/lib/skyframe/packages/BUILD @@ -39,7 +39,6 @@ java_library( "//src/main/java/com/google/devtools/build/lib/bazel/bzlmod:repo_rule_value", "//src/main/java/com/google/devtools/build/lib/clock", "//src/main/java/com/google/devtools/build/lib/cmdline", - "//src/main/java/com/google/devtools/build/lib/collect/nestedset", "//src/main/java/com/google/devtools/build/lib/concurrent", "//src/main/java/com/google/devtools/build/lib/events", "//src/main/java/com/google/devtools/build/lib/io:file_symlink_cycle_uniqueness_function", @@ -56,6 +55,7 @@ java_library( "//src/main/java/com/google/devtools/build/lib/skyframe:package_value", "//src/main/java/com/google/devtools/build/lib/skyframe:precomputed_function", "//src/main/java/com/google/devtools/build/lib/skyframe:precomputed_value", + "//src/main/java/com/google/devtools/build/lib/skyframe:repo_file_function", "//src/main/java/com/google/devtools/build/lib/skyframe:repository_mapping_function", "//src/main/java/com/google/devtools/build/lib/skyframe:sky_functions", "//src/main/java/com/google/devtools/build/lib/skyframe:skyframe_cluster", @@ -67,6 +67,7 @@ java_library( "//src/main/java/com/google/devtools/build/skyframe:skyframe-objects", "//src/main/java/net/starlark/java/eval", "//third_party:caffeine", + "//third_party:error_prone_annotations", "//third_party:guava", "//third_party:jsr305", ], diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/packages/BazelPackageLoader.java b/src/main/java/com/google/devtools/build/lib/skyframe/packages/BazelPackageLoader.java index f1f056c188dabd..e6e7dbb64a7eda 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/packages/BazelPackageLoader.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/packages/BazelPackageLoader.java @@ -176,4 +176,9 @@ protected ExternalPackageHelper getExternalPackageHelper() { protected ActionOnIOExceptionReadingBuildFile getActionOnIOExceptionReadingBuildFile() { return BazelSkyframeExecutorConstants.ACTION_ON_IO_EXCEPTION_READING_BUILD_FILE; } + + @Override + protected boolean shouldUseRepoDotBazel() { + return BazelSkyframeExecutorConstants.USE_REPO_DOT_BAZEL; + } } diff --git a/src/main/protobuf/failure_details.proto b/src/main/protobuf/failure_details.proto index b6c7d3547c0b2a..2f10bc5c2d41b2 100644 --- a/src/main/protobuf/failure_details.proto +++ b/src/main/protobuf/failure_details.proto @@ -1245,6 +1245,7 @@ message PackageLoading { BUILTINS_INJECTION_FAILURE = 29 [(metadata) = { exit_code: 1 }]; SYMLINK_CYCLE_OR_INFINITE_EXPANSION = 30 [(metadata) = { exit_code: 1 }]; OTHER_IO_EXCEPTION = 31 [(metadata) = { exit_code: 36 }]; + BAD_REPO_FILE = 32 [(metadata) = { exit_code: 1 }]; } Code code = 1; diff --git a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleExtensionResolutionTest.java b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleExtensionResolutionTest.java index 42cc96c9e9e895..d8759fb0d8593f 100644 --- a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleExtensionResolutionTest.java +++ b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleExtensionResolutionTest.java @@ -215,6 +215,7 @@ public void setup() throws Exception { /* packageProgress= */ null, PackageFunction.ActionOnIOExceptionReadingBuildFile.UseOriginalIOException .INSTANCE, + /* shouldUseRepoDotBazel= */ true, GlobbingStrategy.SKYFRAME_HYBRID, ignored -> ThreadStateReceiver.NULL_INSTANCE)) .put( diff --git a/src/test/java/com/google/devtools/build/lib/repository/ExternalPackageHelperTest.java b/src/test/java/com/google/devtools/build/lib/repository/ExternalPackageHelperTest.java index 8f1ee414b8bafd..a051e4716ebdb1 100644 --- a/src/test/java/com/google/devtools/build/lib/repository/ExternalPackageHelperTest.java +++ b/src/test/java/com/google/devtools/build/lib/repository/ExternalPackageHelperTest.java @@ -167,6 +167,7 @@ public void createEnvironment() { null, /*packageProgress=*/ null, PackageFunction.ActionOnIOExceptionReadingBuildFile.UseOriginalIOException.INSTANCE, + /* shouldUseRepoDotBazel= */ true, GlobbingStrategy.SKYFRAME_HYBRID, k -> ThreadStateReceiver.NULL_INSTANCE)); skyFunctions.put( diff --git a/src/test/java/com/google/devtools/build/lib/rules/repository/RepositoryDelegatorTest.java b/src/test/java/com/google/devtools/build/lib/rules/repository/RepositoryDelegatorTest.java index 799e6ba5708eb5..4e79cf92f578fe 100644 --- a/src/test/java/com/google/devtools/build/lib/rules/repository/RepositoryDelegatorTest.java +++ b/src/test/java/com/google/devtools/build/lib/rules/repository/RepositoryDelegatorTest.java @@ -198,6 +198,7 @@ public void setupDelegator() throws Exception { /* packageProgress= */ null, PackageFunction.ActionOnIOExceptionReadingBuildFile.UseOriginalIOException .INSTANCE, + /* shouldUseRepoDotBazel= */ true, GlobbingStrategy.SKYFRAME_HYBRID, k -> ThreadStateReceiver.NULL_INSTANCE)) .put( diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTestCase.java b/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTestCase.java index 7373c2d3d50edb..c406b089f3d394 100644 --- a/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTestCase.java +++ b/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTestCase.java @@ -125,6 +125,7 @@ public void baseSetUp() throws Exception { /*packageProgress=*/ null, PackageFunction.ActionOnIOExceptionReadingBuildFile.UseOriginalIOException .INSTANCE, + /* shouldUseRepoDotBazel= */ true, GlobbingStrategy.SKYFRAME_HYBRID, k -> ThreadStateReceiver.NULL_INSTANCE)) .put( diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/ContainingPackageLookupFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/ContainingPackageLookupFunctionTest.java index a6f4be513fbc4c..0fc371f02083ed 100644 --- a/src/test/java/com/google/devtools/build/lib/skyframe/ContainingPackageLookupFunctionTest.java +++ b/src/test/java/com/google/devtools/build/lib/skyframe/ContainingPackageLookupFunctionTest.java @@ -119,6 +119,7 @@ public final void setUp() throws Exception { null, /*packageProgress=*/ null, PackageFunction.ActionOnIOExceptionReadingBuildFile.UseOriginalIOException.INSTANCE, + /* shouldUseRepoDotBazel= */ true, GlobbingStrategy.SKYFRAME_HYBRID, k -> ThreadStateReceiver.NULL_INSTANCE)); skyFunctions.put( diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/FileFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/FileFunctionTest.java index 21e2e82b93415f..c7d7d8870a1b8f 100644 --- a/src/test/java/com/google/devtools/build/lib/skyframe/FileFunctionTest.java +++ b/src/test/java/com/google/devtools/build/lib/skyframe/FileFunctionTest.java @@ -195,6 +195,7 @@ private MemoizingEvaluator makeEvaluator(ExternalFileAction externalFileAction) /*packageProgress=*/ null, PackageFunction.ActionOnIOExceptionReadingBuildFile.UseOriginalIOException .INSTANCE, + /* shouldUseRepoDotBazel= */ true, GlobbingStrategy.SKYFRAME_HYBRID, k -> ThreadStateReceiver.NULL_INSTANCE)) .put( diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/FilesystemValueCheckerTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/FilesystemValueCheckerTest.java index 7626cbd78bc48d..5f2fe873420f69 100644 --- a/src/test/java/com/google/devtools/build/lib/skyframe/FilesystemValueCheckerTest.java +++ b/src/test/java/com/google/devtools/build/lib/skyframe/FilesystemValueCheckerTest.java @@ -323,6 +323,7 @@ public void setUp() throws Exception { null, /* packageProgress= */ null, PackageFunction.ActionOnIOExceptionReadingBuildFile.UseOriginalIOException.INSTANCE, + /* shouldUseRepoDotBazel= */ true, GlobbingStrategy.SKYFRAME_HYBRID, k -> ThreadStateReceiver.NULL_INSTANCE)); skyFunctions.put( diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/PackageLookupFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/PackageLookupFunctionTest.java index 78f587c6a5605b..8133e1e3c9939a 100644 --- a/src/test/java/com/google/devtools/build/lib/skyframe/PackageLookupFunctionTest.java +++ b/src/test/java/com/google/devtools/build/lib/skyframe/PackageLookupFunctionTest.java @@ -130,6 +130,7 @@ public final void setUp() throws Exception { null, /*packageProgress=*/ null, PackageFunction.ActionOnIOExceptionReadingBuildFile.UseOriginalIOException.INSTANCE, + /* shouldUseRepoDotBazel= */ true, GlobbingStrategy.SKYFRAME_HYBRID, k -> ThreadStateReceiver.NULL_INSTANCE)); skyFunctions.put( diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/RecursiveFilesystemTraversalFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/RecursiveFilesystemTraversalFunctionTest.java index da98f28d0cec1f..b9604a4c42e591 100644 --- a/src/test/java/com/google/devtools/build/lib/skyframe/RecursiveFilesystemTraversalFunctionTest.java +++ b/src/test/java/com/google/devtools/build/lib/skyframe/RecursiveFilesystemTraversalFunctionTest.java @@ -182,6 +182,7 @@ public void setUp() { null, /*packageProgress=*/ null, PackageFunction.ActionOnIOExceptionReadingBuildFile.UseOriginalIOException.INSTANCE, + /* shouldUseRepoDotBazel= */ true, GlobbingStrategy.SKYFRAME_HYBRID, k -> ThreadStateReceiver.NULL_INSTANCE)); skyFunctions.put( diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/RepoFileFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/RepoFileFunctionTest.java new file mode 100644 index 00000000000000..35fb68aca952a6 --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/skyframe/RepoFileFunctionTest.java @@ -0,0 +1,136 @@ +// Copyright 2023 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.truth.Truth.assertThat; +import static com.google.devtools.build.lib.bazel.bzlmod.BzlmodTestUtil.createModuleKey; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.devtools.build.lib.analysis.RuleContext; +import com.google.devtools.build.lib.analysis.util.BuildViewTestCase; +import com.google.devtools.build.lib.bazel.bzlmod.BazelLockFileFunction; +import com.google.devtools.build.lib.bazel.bzlmod.BazelModuleResolutionFunction; +import com.google.devtools.build.lib.bazel.bzlmod.FakeRegistry; +import com.google.devtools.build.lib.bazel.bzlmod.ModuleFileFunction; +import com.google.devtools.build.lib.bazel.bzlmod.YankedVersionsUtil; +import com.google.devtools.build.lib.bazel.repository.RepositoryOptions.BazelCompatibilityMode; +import com.google.devtools.build.lib.bazel.repository.RepositoryOptions.CheckDirectDepsMode; +import com.google.devtools.build.lib.bazel.repository.RepositoryOptions.LockfileMode; +import com.google.devtools.build.lib.cmdline.Label; +import com.google.devtools.build.lib.packages.Target; +import com.google.devtools.build.lib.packages.Type; +import com.google.devtools.build.lib.skyframe.PrecomputedValue.Injected; +import com.google.devtools.build.lib.vfs.Path; +import java.io.IOException; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class RepoFileFunctionTest extends BuildViewTestCase { + + private Path moduleRoot; + private FakeRegistry registry; + + @Override + protected ImmutableList extraPrecomputedValues() { + try { + moduleRoot = scratch.dir("modules"); + } catch (IOException e) { + throw new IllegalStateException(e); + } + registry = FakeRegistry.DEFAULT_FACTORY.newFakeRegistry(moduleRoot.getPathString()); + return ImmutableList.of( + PrecomputedValue.injected( + ModuleFileFunction.REGISTRIES, ImmutableList.of(registry.getUrl())), + PrecomputedValue.injected(ModuleFileFunction.IGNORE_DEV_DEPS, false), + PrecomputedValue.injected(ModuleFileFunction.MODULE_OVERRIDES, ImmutableMap.of()), + PrecomputedValue.injected(YankedVersionsUtil.ALLOWED_YANKED_VERSIONS, ImmutableList.of()), + PrecomputedValue.injected( + BazelModuleResolutionFunction.CHECK_DIRECT_DEPENDENCIES, CheckDirectDepsMode.WARNING), + PrecomputedValue.injected( + BazelModuleResolutionFunction.BAZEL_COMPATIBILITY_MODE, BazelCompatibilityMode.ERROR), + PrecomputedValue.injected(BazelLockFileFunction.LOCKFILE_MODE, LockfileMode.UPDATE)); + } + + @Test + public void defaultVisibility() throws Exception { + scratch.overwriteFile("REPO.bazel", "repo(default_visibility=['//some:thing'])"); + scratch.overwriteFile("p/BUILD", "sh_library(name = 't')"); + Target t = getTarget("//p:t"); + assertThat(t.getVisibility().getDeclaredLabels()) + .containsExactly(Label.parseCanonical("//some:thing")); + } + + @Test + public void repoFileInTheMainRepo() throws Exception { + scratch.overwriteFile("REPO.bazel", "repo(default_deprecation='EVERYTHING IS DEPRECATED')"); + scratch.overwriteFile("abc/def/BUILD", "filegroup(name='what')"); + assertThat( + getRuleContext(getConfiguredTarget("//abc/def:what")) + .attributes() + .get("deprecation", Type.STRING)) + .isEqualTo("EVERYTHING IS DEPRECATED"); + } + + @Test + public void repoFileInAnExternalRepo() throws Exception { + setBuildLanguageOptions("--enable_bzlmod"); + scratch.overwriteFile("MODULE.bazel", "bazel_dep(name='foo',version='1.0')"); + scratch.overwriteFile("abc/def/BUILD", "filegroup(name='what')"); + registry.addModule(createModuleKey("foo", "1.0"), "module(name='foo',version='1.0')"); + scratch.overwriteFile(moduleRoot.getRelative("foo~1.0/WORKSPACE.bazel").getPathString()); + scratch.overwriteFile( + moduleRoot.getRelative("foo~1.0/REPO.bazel").getPathString(), + "repo(default_deprecation='EVERYTHING IS DEPRECATED')"); + scratch.overwriteFile( + moduleRoot.getRelative("foo~1.0/abc/def/BUILD").getPathString(), "filegroup(name='what')"); + + assertThat( + getRuleContext(getConfiguredTarget("//abc/def:what")) + .attributes() + .get("deprecation", Type.STRING)) + .isNull(); + assertThat( + getRuleContext(getConfiguredTarget("@@foo~1.0//abc/def:what")) + .attributes() + .get("deprecation", Type.STRING)) + .isEqualTo("EVERYTHING IS DEPRECATED"); + } + + @Test + public void cantCallRepoTwice() throws Exception { + scratch.overwriteFile( + "REPO.bazel", + "repo(default_deprecation='EVERYTHING IS DEPRECATED')", + "repo(features=['abc'])"); + scratch.overwriteFile("abc/def/BUILD", "filegroup(name='what')"); + reporter.removeHandler(failFastHandler); + assertTargetError("//abc/def:what", "'repo' can only be called once"); + } + + @Test + public void featureMerger() throws Exception { + scratch.overwriteFile("REPO.bazel", "repo(features=['a', 'b', 'c', '-d'])"); + scratch.overwriteFile( + "abc/def/BUILD", + "package(features=['-a','-b','d'])", + "filegroup(name='what', features=['b'])"); + RuleContext ruleContext = getRuleContext(getConfiguredTarget("//abc/def:what")); + assertThat(ruleContext.getFeatures()).containsExactly("b", "c", "d"); + assertThat(ruleContext.getDisabledFeatures()).containsExactly("a"); + } +} diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/TimestampBuilderTestCase.java b/src/test/java/com/google/devtools/build/lib/skyframe/TimestampBuilderTestCase.java index 92f0fe049c5040..030d3716bc5414 100644 --- a/src/test/java/com/google/devtools/build/lib/skyframe/TimestampBuilderTestCase.java +++ b/src/test/java/com/google/devtools/build/lib/skyframe/TimestampBuilderTestCase.java @@ -280,6 +280,7 @@ protected BuilderWithResult createBuilder( /* packageProgress= */ null, PackageFunction.ActionOnIOExceptionReadingBuildFile.UseOriginalIOException .INSTANCE, + /* shouldUseRepoDotBazel= */ true, GlobbingStrategy.SKYFRAME_HYBRID, k -> ThreadStateReceiver.NULL_INSTANCE)) .put(