bzlmod: create a base class for repository_ctx (https://github.com/bazelbuild/bazel/issues/13316) This base class contains methods to be shared between repository_ctx and module_ctx. In other words, the following methods are moved from repository_ctx to the new StarlarkBaseExternalContext: * `path`: needed to traverse the file system * `os`: needed for OS-specific information and env variables * `read`: needed to read files * `execute`: needed to perform arbitrary operations * `which`: needed to locate tools on PATH The other methods are left out because they're mostly useful for creating files, which module extensions are discouraged from (since the files created in such a way aren't accessible later, unlike those created by repo rules). We'd ideally also need an "http_get" method later. This CL also changes the handling of marker data in repository_ctx (now in the base class), so that instead of directly writing entries into a marker data map, we collect those entries into a separate map. After the context object is used, that separate map can then be read and processed (so repository_ctx can put it into marker data and module_ctx will likely want to write it into the lockfile). PiperOrigin-RevId: 393790084
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkBaseExternalContext.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkBaseExternalContext.java new file mode 100644 index 0000000..2d056aa --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkBaseExternalContext.java
@@ -0,0 +1,526 @@ +// 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.repository.starlark; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSortedMap; +import com.google.common.collect.Maps; +import com.google.devtools.build.lib.actions.FileValue; +import com.google.devtools.build.lib.bazel.debug.WorkspaceRuleEvent; +import com.google.devtools.build.lib.bazel.repository.downloader.DownloadManager; +import com.google.devtools.build.lib.cmdline.Label; +import com.google.devtools.build.lib.packages.semantics.BuildLanguageOptions; +import com.google.devtools.build.lib.profiler.Profiler; +import com.google.devtools.build.lib.profiler.ProfilerTask; +import com.google.devtools.build.lib.profiler.SilentCloseable; +import com.google.devtools.build.lib.rules.repository.RepositoryFunction; +import com.google.devtools.build.lib.rules.repository.RepositoryFunction.RepositoryFunctionException; +import com.google.devtools.build.lib.runtime.ProcessWrapper; +import com.google.devtools.build.lib.runtime.RepositoryRemoteExecutor; +import com.google.devtools.build.lib.runtime.RepositoryRemoteExecutor.ExecutionResult; +import com.google.devtools.build.lib.util.OsUtils; +import com.google.devtools.build.lib.util.io.OutErr; +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.RootedPath; +import com.google.devtools.build.lib.vfs.Symlinks; +import com.google.devtools.build.skyframe.SkyFunction.Environment; +import com.google.devtools.build.skyframe.SkyFunctionException.Transience; +import com.google.devtools.build.skyframe.SkyKey; +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.InvalidPathException; +import java.time.Duration; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.annotation.Nullable; +import net.starlark.java.annot.Param; +import net.starlark.java.annot.ParamType; +import net.starlark.java.annot.StarlarkMethod; +import net.starlark.java.eval.Dict; +import net.starlark.java.eval.EvalException; +import net.starlark.java.eval.Sequence; +import net.starlark.java.eval.Starlark; +import net.starlark.java.eval.StarlarkInt; +import net.starlark.java.eval.StarlarkSemantics; +import net.starlark.java.eval.StarlarkThread; +import net.starlark.java.eval.StarlarkValue; +import net.starlark.java.syntax.Location; + +/** A common base class for Starlark "ctx" objects related to external dependencies. */ +public abstract class StarlarkBaseExternalContext implements StarlarkValue { + /** Max. number of command line args added as a profiler description. */ + private static final int MAX_PROFILE_ARGS_LEN = 80; + + protected final Path workingDirectory; + protected final Environment env; + protected final ImmutableMap<String, String> envVariables; + private final StarlarkOS osObject; + protected final DownloadManager downloadManager; + protected final double timeoutScaling; + @Nullable private final ProcessWrapper processWrapper; + protected final StarlarkSemantics starlarkSemantics; + private final HashMap<Label, String> accumulatedFileDigests = new HashMap<>(); + private final RepositoryRemoteExecutor remoteExecutor; + + protected StarlarkBaseExternalContext( + Path workingDirectory, + Environment env, + ImmutableMap<String, String> envVariables, + DownloadManager downloadManager, + double timeoutScaling, + @Nullable ProcessWrapper processWrapper, + StarlarkSemantics starlarkSemantics, + @Nullable RepositoryRemoteExecutor remoteExecutor) { + this.workingDirectory = workingDirectory; + this.env = env; + this.envVariables = envVariables; + this.osObject = new StarlarkOS(envVariables); + this.downloadManager = downloadManager; + this.timeoutScaling = timeoutScaling; + this.processWrapper = processWrapper; + this.starlarkSemantics = starlarkSemantics; + this.remoteExecutor = remoteExecutor; + } + + /** A string that can be used to identify this context object. Used for logging purposes. */ + protected abstract String getIdentifyingStringForLogging(); + + /** Returns the file digests used by this context object so far. */ + public ImmutableMap<Label, String> getAccumulatedFileDigests() { + return ImmutableMap.copyOf(accumulatedFileDigests); + } + + @StarlarkMethod( + name = "path", + doc = + "Returns a path from a string, label or path. If the path is relative, it will resolve " + + "relative to the repository directory. If the path is a label, it will resolve to " + + "the path of the corresponding file. Note that remote repositories are executed " + + "during the analysis phase and thus cannot depends on a target result (the " + + "label should point to a non-generated file). If path is a path, it will return " + + "that path as is.", + parameters = { + @Param( + name = "path", + allowedTypes = { + @ParamType(type = String.class), + @ParamType(type = Label.class), + @ParamType(type = StarlarkPath.class) + }, + doc = "string, label or path from which to create a path from") + }) + public StarlarkPath path(Object path) throws EvalException, InterruptedException { + return getPath("path()", path); + } + + protected StarlarkPath getPath(String method, Object path) + throws EvalException, InterruptedException { + if (path instanceof String) { + PathFragment pathFragment = PathFragment.create(path.toString()); + return new StarlarkPath( + pathFragment.isAbsolute() + ? workingDirectory.getFileSystem().getPath(pathFragment) + : workingDirectory.getRelative(pathFragment)); + } else if (path instanceof Label) { + return getPathFromLabel((Label) path); + } else if (path instanceof StarlarkPath) { + return (StarlarkPath) path; + } else { + throw Starlark.errorf("%s can only take a string or a label.", method); + } + } + + @StarlarkMethod( + name = "read", + doc = "Reads the content of a file on the filesystem.", + useStarlarkThread = true, + parameters = { + @Param( + name = "path", + allowedTypes = { + @ParamType(type = String.class), + @ParamType(type = Label.class), + @ParamType(type = StarlarkPath.class) + }, + doc = "path of the file to read from."), + }) + public String readFile(Object path, StarlarkThread thread) + throws RepositoryFunctionException, EvalException, InterruptedException { + StarlarkPath p = getPath("read()", path); + WorkspaceRuleEvent w = + WorkspaceRuleEvent.newReadEvent( + p.toString(), getIdentifyingStringForLogging(), thread.getCallerLocation()); + env.getListener().post(w); + try { + return FileSystemUtils.readContent(p.getPath(), StandardCharsets.ISO_8859_1); + } catch (IOException e) { + throw new RepositoryFunctionException(e, Transience.TRANSIENT); + } + } + + // Create parent directories for the given path + protected static void makeDirectories(Path path) throws IOException { + Path parent = path.getParentDirectory(); + if (parent != null) { + parent.createDirectoryAndParents(); + } + } + + @StarlarkMethod( + name = "os", + structField = true, + doc = "A struct to access information from the system.") + public StarlarkOS getOS() { + // Historically this event reported the location of the ctx.os expression, but that's no longer + // available in the interpreter API. Now we just use a dummy location, and the user must + // manually inspect the code where this context object is used if they wish to find the + // offending ctx.os expression. + WorkspaceRuleEvent w = + WorkspaceRuleEvent.newOsEvent(getIdentifyingStringForLogging(), Location.BUILTIN); + env.getListener().post(w); + return osObject; + } + + protected static void createDirectory(Path directory) throws RepositoryFunctionException { + try { + if (!directory.exists()) { + makeDirectories(directory); + directory.createDirectory(); + } + } catch (IOException e) { + throw new RepositoryFunctionException(e, Transience.TRANSIENT); + } catch (InvalidPathException e) { + throw new RepositoryFunctionException( + Starlark.errorf("Could not create %s: %s", directory, e.getMessage()), + Transience.PERSISTENT); + } + } + + /** Whether this context supports remote execution. */ + protected abstract boolean isRemotable(); + + private boolean canExecuteRemote() { + boolean featureEnabled = + starlarkSemantics.getBool(BuildLanguageOptions.EXPERIMENTAL_REPO_REMOTE_EXEC); + boolean remoteExecEnabled = remoteExecutor != null; + return featureEnabled && isRemotable() && remoteExecEnabled; + } + + protected abstract ImmutableMap<String, String> getRemoteExecProperties() throws EvalException; + + private Map.Entry<PathFragment, Path> getRemotePathFromLabel(Label label) + throws EvalException, InterruptedException { + Path localPath = getPathFromLabel(label).getPath(); + PathFragment remotePath = + label.getPackageIdentifier().getSourceRoot().getRelative(label.getName()); + return Maps.immutableEntry(remotePath, localPath); + } + + private StarlarkExecutionResult executeRemote( + Sequence<?> argumentsUnchecked, // <String> or <Label> expected + int timeout, + Map<String, String> environment, + boolean quiet, + String workingDirectory) + throws EvalException, InterruptedException { + Preconditions.checkState(canExecuteRemote()); + + ImmutableSortedMap.Builder<PathFragment, Path> inputsBuilder = + ImmutableSortedMap.naturalOrder(); + ImmutableList.Builder<String> argumentsBuilder = ImmutableList.builder(); + for (Object argumentUnchecked : argumentsUnchecked) { + if (argumentUnchecked instanceof Label) { + Label label = (Label) argumentUnchecked; + Map.Entry<PathFragment, Path> remotePath = getRemotePathFromLabel(label); + argumentsBuilder.add(remotePath.getKey().toString()); + inputsBuilder.put(remotePath); + } else { + argumentsBuilder.add(argumentUnchecked.toString()); + } + } + + ImmutableList<String> arguments = argumentsBuilder.build(); + + try (SilentCloseable c = + Profiler.instance() + .profile(ProfilerTask.STARLARK_REPOSITORY_FN, profileArgsDesc("remote", arguments))) { + ExecutionResult result = + remoteExecutor.execute( + arguments, + inputsBuilder.build(), + getRemoteExecProperties(), + ImmutableMap.copyOf(environment), + workingDirectory, + Duration.ofSeconds(timeout)); + + String stdout = new String(result.stdout(), StandardCharsets.US_ASCII); + String stderr = new String(result.stderr(), StandardCharsets.US_ASCII); + + if (!quiet) { + OutErr outErr = OutErr.SYSTEM_OUT_ERR; + outErr.printOut(stdout); + outErr.printErr(stderr); + } + + return new StarlarkExecutionResult(result.exitCode(), stdout, stderr); + } catch (IOException e) { + throw Starlark.errorf("remote_execute failed: %s", e.getMessage()); + } + } + + private void validateExecuteArguments(Sequence<?> arguments) throws EvalException { + boolean isRemotable = isRemotable(); + for (int i = 0; i < arguments.size(); i++) { + Object arg = arguments.get(i); + if (isRemotable) { + if (!(arg instanceof String || arg instanceof Label)) { + throw Starlark.errorf("Argument %d of execute is neither a label nor a string.", i); + } + } else { + if (!(arg instanceof String || arg instanceof Label || arg instanceof StarlarkPath)) { + throw Starlark.errorf("Argument %d of execute is neither a path, label, nor string.", i); + } + } + } + } + + /** Returns the command line arguments as a string for display in the profiler. */ + private static String profileArgsDesc(String method, List<String> args) { + StringBuilder b = new StringBuilder(); + b.append(method).append(":"); + + final String sep = " "; + for (String arg : args) { + int appendLen = sep.length() + arg.length(); + int remainingLen = MAX_PROFILE_ARGS_LEN - b.length(); + + if (appendLen <= remainingLen) { + b.append(sep); + b.append(arg); + } else { + String shortenedArg = (sep + arg).substring(0, remainingLen); + b.append(shortenedArg); + b.append("..."); + break; + } + } + + return b.toString(); + } + + @StarlarkMethod( + name = "execute", + doc = + "Executes the command given by the list of arguments. The execution time of the command" + + " is limited by <code>timeout</code> (in seconds, default 600 seconds). This method" + + " returns an <code>exec_result</code> structure containing the output of the" + + " command. The <code>environment</code> map can be used to override some" + + " environment variables to be passed to the process.", + useStarlarkThread = true, + parameters = { + @Param( + name = "arguments", + doc = + "List of arguments, the first element should be the path to the program to " + + "execute."), + @Param( + name = "timeout", + named = true, + defaultValue = "600", + doc = "maximum duration of the command in seconds (default is 600 seconds)."), + @Param( + name = "environment", + defaultValue = "{}", + named = true, + doc = "force some environment variables to be set to be passed to the process."), + @Param( + name = "quiet", + defaultValue = "True", + named = true, + doc = "If stdout and stderr should be printed to the terminal."), + @Param( + name = "working_directory", + defaultValue = "\"\"", + named = true, + doc = + "Working directory for command execution.\n" + + "Can be relative to the repository root or absolute."), + }) + public StarlarkExecutionResult execute( + Sequence<?> arguments, // <String> or <StarlarkPath> or <Label> expected + StarlarkInt timeoutI, + Dict<?, ?> uncheckedEnvironment, // <String, String> expected + boolean quiet, + String overrideWorkingDirectory, + StarlarkThread thread) + throws EvalException, RepositoryFunctionException, InterruptedException { + validateExecuteArguments(arguments); + int timeout = Starlark.toInt(timeoutI, "timeout"); + + Map<String, String> forceEnvVariables = + Dict.cast(uncheckedEnvironment, String.class, String.class, "environment"); + + if (canExecuteRemote()) { + return executeRemote(arguments, timeout, forceEnvVariables, quiet, overrideWorkingDirectory); + } + + // Execute on the local/host machine + + List<String> args = new ArrayList<>(arguments.size()); + for (Object arg : arguments) { + if (arg instanceof Label) { + args.add(getPathFromLabel((Label) arg).toString()); + } else { + // String or StarlarkPath expected + args.add(arg.toString()); + } + } + + WorkspaceRuleEvent w = + WorkspaceRuleEvent.newExecuteEvent( + args, + timeout, + envVariables, + forceEnvVariables, + workingDirectory.getPathString(), + quiet, + getIdentifyingStringForLogging(), + thread.getCallerLocation()); + env.getListener().post(w); + createDirectory(workingDirectory); + + long timeoutMillis = Math.round(timeout * 1000L * timeoutScaling); + if (processWrapper != null) { + args = + processWrapper + .commandLineBuilder(args) + .setTimeout(Duration.ofMillis(timeoutMillis)) + .build(); + } + + Path workingDirectoryPath; + if (overrideWorkingDirectory != null && !overrideWorkingDirectory.isEmpty()) { + workingDirectoryPath = getPath("execute()", overrideWorkingDirectory).getPath(); + } else { + workingDirectoryPath = workingDirectory; + } + createDirectory(workingDirectoryPath); + + try (SilentCloseable c = + Profiler.instance() + .profile(ProfilerTask.STARLARK_REPOSITORY_FN, profileArgsDesc("local", args))) { + return StarlarkExecutionResult.builder(osObject.getEnvironmentVariables()) + .addArguments(args) + .setDirectory(workingDirectoryPath.getPathFile()) + .addEnvironmentVariables(forceEnvVariables) + .setTimeout(timeoutMillis) + .setQuiet(quiet) + .execute(); + } + } + + @StarlarkMethod( + name = "which", + doc = + "Returns the path of the corresponding program or None " + + "if there is no such program in the path.", + allowReturnNones = true, + useStarlarkThread = true, + parameters = { + @Param(name = "program", named = false, doc = "Program to find in the path."), + }) + @Nullable + public StarlarkPath which(String program, StarlarkThread thread) throws EvalException { + WorkspaceRuleEvent w = + WorkspaceRuleEvent.newWhichEvent( + program, getIdentifyingStringForLogging(), thread.getCallerLocation()); + env.getListener().post(w); + if (program.contains("/") || program.contains("\\")) { + throw Starlark.errorf( + "Program argument of which() may not contain a / or a \\ ('%s' given)", program); + } + if (program.length() == 0) { + throw Starlark.errorf("Program argument of which() may not be empty"); + } + try { + StarlarkPath commandPath = findCommandOnPath(program); + if (commandPath != null) { + return commandPath; + } + + if (!program.endsWith(OsUtils.executableExtension())) { + program += OsUtils.executableExtension(); + return findCommandOnPath(program); + } + } catch (IOException e) { + // IOException when checking executable file means we cannot read the file data so + // we cannot execute it, swallow the exception. + } + return null; + } + + private StarlarkPath findCommandOnPath(String program) throws IOException { + String pathEnvVariable = envVariables.get("PATH"); + if (pathEnvVariable == null) { + return null; + } + for (String p : pathEnvVariable.split(File.pathSeparator)) { + PathFragment fragment = PathFragment.create(p); + if (fragment.isAbsolute()) { + // We ignore relative path as they don't mean much here (relative to where? the workspace + // root?). + Path path = workingDirectory.getFileSystem().getPath(fragment).getChild(program.trim()); + if (path.exists() && path.isFile(Symlinks.FOLLOW) && path.isExecutable()) { + return new StarlarkPath(path); + } + } + } + return null; + } + + // Resolve the label given by value into a file path. + protected StarlarkPath getPathFromLabel(Label label) throws EvalException, InterruptedException { + RootedPath rootedPath = RepositoryFunction.getRootedPathFromLabel(label, env); + SkyKey fileSkyKey = FileValue.key(rootedPath); + FileValue fileValue; + try { + fileValue = (FileValue) env.getValueOrThrow(fileSkyKey, IOException.class); + } catch (IOException e) { + throw Starlark.errorf("%s", e.getMessage()); + } + + if (fileValue == null) { + // TODO(wyv): Find out if this is okay for module extensions + throw RepositoryFunction.restart(); + } + if (!fileValue.isFile() || fileValue.isSpecialFile()) { + throw Starlark.errorf("Not a regular file: %s", rootedPath.asPath().getPathString()); + } + + try { + accumulatedFileDigests.put(label, RepositoryFunction.fileValueToMarkerValue(fileValue)); + } catch (IOException e) { + throw Starlark.errorf("%s", e.getMessage()); + } + return new StarlarkPath(rootedPath.asPath()); + } +}
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkRepositoryContext.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkRepositoryContext.java index 89c79ff..5d8efae 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkRepositoryContext.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkRepositoryContext.java
@@ -15,19 +15,14 @@ package com.google.devtools.build.lib.bazel.repository.starlark; import com.github.difflib.patch.PatchFailedException; -import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Ascii; import com.google.common.base.Joiner; import com.google.common.base.Optional; -import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableSortedMap; -import com.google.common.collect.Maps; import com.google.devtools.build.docgen.annot.DocCategory; -import com.google.devtools.build.lib.actions.FileValue; import com.google.devtools.build.lib.bazel.debug.WorkspaceRuleEvent; import com.google.devtools.build.lib.bazel.repository.DecompressorDescriptor; import com.google.devtools.build.lib.bazel.repository.DecompressorValue; @@ -47,26 +42,17 @@ import com.google.devtools.build.lib.packages.semantics.BuildLanguageOptions; import com.google.devtools.build.lib.pkgcache.PathPackageLocator; import com.google.devtools.build.lib.profiler.Profiler; -import com.google.devtools.build.lib.profiler.ProfilerTask; import com.google.devtools.build.lib.profiler.SilentCloseable; -import com.google.devtools.build.lib.rules.repository.RepositoryFunction; import com.google.devtools.build.lib.rules.repository.RepositoryFunction.RepositoryFunctionException; import com.google.devtools.build.lib.rules.repository.WorkspaceAttributeMapper; import com.google.devtools.build.lib.runtime.ProcessWrapper; import com.google.devtools.build.lib.runtime.RepositoryRemoteExecutor; -import com.google.devtools.build.lib.runtime.RepositoryRemoteExecutor.ExecutionResult; -import com.google.devtools.build.lib.util.OsUtils; import com.google.devtools.build.lib.util.StringUtilities; -import com.google.devtools.build.lib.util.io.OutErr; 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.RootedPath; -import com.google.devtools.build.lib.vfs.Symlinks; import com.google.devtools.build.skyframe.SkyFunction.Environment; import com.google.devtools.build.skyframe.SkyFunctionException.Transience; -import com.google.devtools.build.skyframe.SkyKey; -import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.net.MalformedURLException; @@ -77,7 +63,6 @@ import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Paths; -import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; @@ -95,7 +80,6 @@ import net.starlark.java.eval.StarlarkInt; import net.starlark.java.eval.StarlarkSemantics; import net.starlark.java.eval.StarlarkThread; -import net.starlark.java.eval.StarlarkValue; import net.starlark.java.syntax.Location; /** Starlark API for the repository_rule's context. */ @@ -107,7 +91,7 @@ + " helper functions and information about attributes. You get a repository_ctx object" + " as an argument to the <code>implementation</code> function when you create a" + " repository rule.") -public class StarlarkRepositoryContext implements StarlarkValue { +public class StarlarkRepositoryContext extends StarlarkBaseExternalContext { private static final ImmutableList<String> WHITELISTED_REPOS_FOR_FLAG_ENABLED = ImmutableList.of("@rules_cc", "@bazel_tools"); private static final ImmutableList<String> WHITELISTED_PATHS_FOR_FLAG_ENABLED = @@ -115,22 +99,10 @@ "rules_cc/cc/private/toolchain/unix_cc_configure.bzl", "bazel_tools/tools/cpp/unix_cc_configure.bzl"); - /** Max. number of command line args added as a profiler description. */ - private static final int MAX_PROFILE_ARGS_LEN = 80; - private final Rule rule; private final PathPackageLocator packageLocator; - private final Path outputDirectory; private final StructImpl attrObject; - private final StarlarkOS osObject; private final ImmutableSet<PathFragment> ignoredPatterns; - private final Environment env; - private final DownloadManager downloadManager; - private final double timeoutScaling; - @Nullable private final ProcessWrapper processWrapper; - private final Map<String, String> markerData; - private final StarlarkSemantics starlarkSemantics; - private final RepositoryRemoteExecutor remoteExecutor; /** * Create a new context (repository_ctx) object for a Starlark repository rule ({@code rule} @@ -142,24 +114,25 @@ Path outputDirectory, ImmutableSet<PathFragment> ignoredPatterns, Environment environment, - Map<String, String> env, + ImmutableMap<String, String> env, DownloadManager downloadManager, double timeoutScaling, @Nullable ProcessWrapper processWrapper, - Map<String, String> markerData, StarlarkSemantics starlarkSemantics, @Nullable RepositoryRemoteExecutor remoteExecutor) throws EvalException { + super( + outputDirectory, + environment, + env, + downloadManager, + timeoutScaling, + processWrapper, + starlarkSemantics, + remoteExecutor); this.rule = rule; this.packageLocator = packageLocator; - this.outputDirectory = outputDirectory; this.ignoredPatterns = ignoredPatterns; - this.env = environment; - this.osObject = new StarlarkOS(env); - this.downloadManager = downloadManager; - this.timeoutScaling = timeoutScaling; - this.processWrapper = processWrapper; - this.markerData = markerData; WorkspaceAttributeMapper attrs = WorkspaceAttributeMapper.of(rule); ImmutableMap.Builder<String, Object> attrBuilder = new ImmutableMap.Builder<>(); for (String name : attrs.getAttributeNames()) { @@ -170,8 +143,11 @@ } } attrObject = StructProvider.STRUCT.create(attrBuilder.build(), "No such attribute '%s'"); - this.starlarkSemantics = starlarkSemantics; - this.remoteExecutor = remoteExecutor; + } + + @Override + protected String getIdentifyingStringForLogging() { + return rule.getLabel().toString(); } @StarlarkMethod( @@ -197,7 +173,7 @@ StarlarkPath starlarkPath = getPath(method, pathObject); Path path = starlarkPath.getPath(); if (packageLocator.getPathEntries().stream().noneMatch(root -> path.startsWith(root.asPath())) - || path.startsWith(outputDirectory)) { + || path.startsWith(workingDirectory)) { return starlarkPath; } Path workspaceRoot = packageLocator.getWorkspaceFile().getParentDirectory(); @@ -214,46 +190,6 @@ } @StarlarkMethod( - name = "path", - doc = - "Returns a path from a string, label or path. If the path is relative, it will resolve " - + "relative to the repository directory. If the path is a label, it will resolve to " - + "the path of the corresponding file. Note that remote repositories are executed " - + "during the analysis phase and thus cannot depends on a target result (the " - + "label should point to a non-generated file). If path is a path, it will return " - + "that path as is.", - parameters = { - @Param( - name = "path", - allowedTypes = { - @ParamType(type = String.class), - @ParamType(type = Label.class), - @ParamType(type = StarlarkPath.class) - }, - doc = "string, label or path from which to create a path from") - }) - public StarlarkPath path(Object path) throws EvalException, InterruptedException { - return getPath("path()", path); - } - - private StarlarkPath getPath(String method, Object path) - throws EvalException, InterruptedException { - if (path instanceof String) { - PathFragment pathFragment = PathFragment.create(path.toString()); - return new StarlarkPath( - pathFragment.isAbsolute() - ? outputDirectory.getFileSystem().getPath(pathFragment) - : outputDirectory.getRelative(pathFragment)); - } else if (path instanceof Label) { - return getPathFromLabel((Label) path); - } else if (path instanceof StarlarkPath) { - return (StarlarkPath) path; - } else { - throw Starlark.errorf("%s can only take a string or a label.", method); - } - } - - @StarlarkMethod( name = "report_progress", doc = "Updates the progress status for the fetching of this repository", parameters = { @@ -262,6 +198,7 @@ allowedTypes = {@ParamType(type = String.class)}, doc = "string describing the current status of the fetch progress") }) + // TODO(wyv): migrate this to the base context. public void reportProgress(String status) { final String message = status == null ? "" : status; final String id = "@" + getName(); @@ -338,7 +275,7 @@ private void checkInOutputDirectory(String operation, StarlarkPath path) throws RepositoryFunctionException { - if (!path.getPath().getPathString().startsWith(outputDirectory.getPathString())) { + if (!path.getPath().getPathString().startsWith(workingDirectory.getPathString())) { throw new RepositoryFunctionException( Starlark.errorf( "Cannot %s outside of the repository directory for path %s", operation, path), @@ -493,75 +430,8 @@ } } - @StarlarkMethod( - name = "read", - doc = "Reads the content of a file on the filesystem.", - useStarlarkThread = true, - parameters = { - @Param( - name = "path", - allowedTypes = { - @ParamType(type = String.class), - @ParamType(type = Label.class), - @ParamType(type = StarlarkPath.class) - }, - doc = "path of the file to read from."), - }) - public String readFile(Object path, StarlarkThread thread) - throws RepositoryFunctionException, EvalException, InterruptedException { - StarlarkPath p = getPath("read()", path); - WorkspaceRuleEvent w = - WorkspaceRuleEvent.newReadEvent( - p.toString(), rule.getLabel().toString(), thread.getCallerLocation()); - env.getListener().post(w); - try { - return FileSystemUtils.readContent(p.getPath(), StandardCharsets.ISO_8859_1); - } catch (IOException e) { - throw new RepositoryFunctionException(e, Transience.TRANSIENT); - } - } - - // Create parent directories for the given path - private static void makeDirectories(Path path) throws IOException { - Path parent = path.getParentDirectory(); - if (parent != null) { - parent.createDirectoryAndParents(); - } - } - - @StarlarkMethod( - name = "os", - structField = true, - doc = "A struct to access information from the system.") - public StarlarkOS getOS() { - // Historically this event reported the location of the ctx.os expression, - // but that's no longer available in the interpreter API. Now we report the - // location of the rule's implementation function, and the user must inspect - // that code manually (or in a debugger) to find the offending ctx.os expression. - WorkspaceRuleEvent w = - WorkspaceRuleEvent.newOsEvent( - rule.getLabel().toString(), - rule.getRuleClassObject().getConfiguredTargetFunction().getLocation()); - env.getListener().post(w); - return osObject; - } - - private void createDirectory(Path directory) throws RepositoryFunctionException { - try { - if (!directory.exists()) { - makeDirectories(directory); - directory.createDirectory(); - } - } catch (IOException e) { - throw new RepositoryFunctionException(e, Transience.TRANSIENT); - } catch (InvalidPathException e) { - throw new RepositoryFunctionException( - Starlark.errorf("Could not create %s: %s", directory, e.getMessage()), - Transience.PERSISTENT); - } - } - - boolean isRemotable() { + @Override + protected boolean isRemotable() { Object remotable = rule.getAttr("$remotable"); if (remotable != null) { return (Boolean) remotable; @@ -569,228 +439,13 @@ return false; } - private boolean canExecuteRemote() { - boolean featureEnabled = - starlarkSemantics.getBool(BuildLanguageOptions.EXPERIMENTAL_REPO_REMOTE_EXEC); - boolean remoteExecEnabled = remoteExecutor != null; - return featureEnabled && isRemotable() && remoteExecEnabled; - } - - private ImmutableMap<String, String> getExecProperties() throws EvalException { + @Override + protected ImmutableMap<String, String> getRemoteExecProperties() throws EvalException { return ImmutableMap.copyOf( Dict.cast( getAttr().getValue("exec_properties"), String.class, String.class, "exec_properties")); } - private Map.Entry<PathFragment, Path> getRemotePathFromLabel(Label label) - throws EvalException, InterruptedException { - Path localPath = getPathFromLabel(label).getPath(); - PathFragment remotePath = - label.getPackageIdentifier().getSourceRoot().getRelative(label.getName()); - return Maps.immutableEntry(remotePath, localPath); - } - - private StarlarkExecutionResult executeRemote( - Sequence<?> argumentsUnchecked, // <String> or <Label> expected - int timeout, - Map<String, String> environment, - boolean quiet, - String workingDirectory) - throws EvalException, InterruptedException { - Preconditions.checkState(canExecuteRemote()); - - ImmutableSortedMap.Builder<PathFragment, Path> inputsBuilder = - ImmutableSortedMap.naturalOrder(); - ImmutableList.Builder<String> argumentsBuilder = ImmutableList.builder(); - for (Object argumentUnchecked : argumentsUnchecked) { - if (argumentUnchecked instanceof Label) { - Label label = (Label) argumentUnchecked; - Map.Entry<PathFragment, Path> remotePath = getRemotePathFromLabel(label); - argumentsBuilder.add(remotePath.getKey().toString()); - inputsBuilder.put(remotePath); - } else { - argumentsBuilder.add(argumentUnchecked.toString()); - } - } - - ImmutableList<String> arguments = argumentsBuilder.build(); - - try (SilentCloseable c = - Profiler.instance() - .profile(ProfilerTask.STARLARK_REPOSITORY_FN, profileArgsDesc("remote", arguments))) { - ExecutionResult result = - remoteExecutor.execute( - arguments, - inputsBuilder.build(), - getExecProperties(), - ImmutableMap.copyOf(environment), - workingDirectory, - Duration.ofSeconds(timeout)); - - String stdout = new String(result.stdout(), StandardCharsets.US_ASCII); - String stderr = new String(result.stderr(), StandardCharsets.US_ASCII); - - if (!quiet) { - OutErr outErr = OutErr.SYSTEM_OUT_ERR; - outErr.printOut(stdout); - outErr.printErr(stderr); - } - - return new StarlarkExecutionResult(result.exitCode(), stdout, stderr); - } catch (IOException e) { - throw Starlark.errorf("remote_execute failed: %s", e.getMessage()); - } - } - - private void validateExecuteArguments(Sequence<?> arguments) throws EvalException { - boolean isRemotable = isRemotable(); - for (int i = 0; i < arguments.size(); i++) { - Object arg = arguments.get(i); - if (isRemotable) { - if (!(arg instanceof String || arg instanceof Label)) { - throw Starlark.errorf("Argument %d of execute is neither a label nor a string.", i); - } - } else { - if (!(arg instanceof String || arg instanceof Label || arg instanceof StarlarkPath)) { - throw Starlark.errorf("Argument %d of execute is neither a path, label, nor string.", i); - } - } - } - } - - /** Returns the command line arguments as a string for display in the profiler. */ - private static String profileArgsDesc(String method, List<String> args) { - StringBuilder b = new StringBuilder(); - b.append(method).append(":"); - - final String sep = " "; - for (String arg : args) { - int appendLen = sep.length() + arg.length(); - int remainingLen = MAX_PROFILE_ARGS_LEN - b.length(); - - if (appendLen <= remainingLen) { - b.append(sep); - b.append(arg); - } else { - String shortenedArg = (sep + arg).substring(0, remainingLen); - b.append(shortenedArg); - b.append("..."); - break; - } - } - - return b.toString(); - } - - @StarlarkMethod( - name = "execute", - doc = - "Executes the command given by the list of arguments. The execution time of the command" - + " is limited by <code>timeout</code> (in seconds, default 600 seconds). This method" - + " returns an <code>exec_result</code> structure containing the output of the" - + " command. The <code>environment</code> map can be used to override some" - + " environment variables to be passed to the process.", - useStarlarkThread = true, - parameters = { - @Param( - name = "arguments", - doc = - "List of arguments, the first element should be the path to the program to " - + "execute."), - @Param( - name = "timeout", - named = true, - defaultValue = "600", - doc = "maximum duration of the command in seconds (default is 600 seconds)."), - @Param( - name = "environment", - defaultValue = "{}", - named = true, - doc = "force some environment variables to be set to be passed to the process."), - @Param( - name = "quiet", - defaultValue = "True", - named = true, - doc = "If stdout and stderr should be printed to the terminal."), - @Param( - name = "working_directory", - defaultValue = "\"\"", - named = true, - doc = - "Working directory for command execution.\n" - + "Can be relative to the repository root or absolute."), - }) - public StarlarkExecutionResult execute( - Sequence<?> arguments, // <String> or <StarlarkPath> or <Label> expected - StarlarkInt timeoutI, - Dict<?, ?> uncheckedEnvironment, // <String, String> expected - boolean quiet, - String workingDirectory, - StarlarkThread thread) - throws EvalException, RepositoryFunctionException, InterruptedException { - validateExecuteArguments(arguments); - int timeout = Starlark.toInt(timeoutI, "timeout"); - - Map<String, String> environment = - Dict.cast(uncheckedEnvironment, String.class, String.class, "environment"); - - if (canExecuteRemote()) { - return executeRemote(arguments, timeout, environment, quiet, workingDirectory); - } - - // Execute on the local/host machine - - List<String> args = new ArrayList<>(arguments.size()); - for (Object arg : arguments) { - if (arg instanceof Label) { - args.add(getPathFromLabel((Label) arg).toString()); - } else { - // String or StarlarkPath expected - args.add(arg.toString()); - } - } - - WorkspaceRuleEvent w = - WorkspaceRuleEvent.newExecuteEvent( - args, - timeout, - osObject.getEnvironmentVariables(), - environment, - outputDirectory.getPathString(), - quiet, - rule.getLabel().toString(), - thread.getCallerLocation()); - env.getListener().post(w); - createDirectory(outputDirectory); - - long timeoutMillis = Math.round(timeout * 1000L * timeoutScaling); - if (processWrapper != null) { - args = - processWrapper - .commandLineBuilder(args) - .setTimeout(Duration.ofMillis(timeoutMillis)) - .build(); - } - - Path workingDirectoryPath = outputDirectory; - if (workingDirectory != null && !workingDirectory.isEmpty()) { - workingDirectoryPath = getPath("execute()", workingDirectory).getPath(); - } - createDirectory(workingDirectoryPath); - - try (SilentCloseable c = - Profiler.instance() - .profile(ProfilerTask.STARLARK_REPOSITORY_FN, profileArgsDesc("local", args))) { - return StarlarkExecutionResult.builder(osObject.getEnvironmentVariables()) - .addArguments(args) - .setDirectory(workingDirectoryPath.getPathFile()) - .addEnvironmentVariables(environment) - .setTimeout(timeoutMillis) - .setQuiet(quiet) - .execute(); - } - } - @StarlarkMethod( name = "delete", doc = @@ -857,7 +512,7 @@ starlarkPath.toString(), strip, rule.getLabel().toString(), thread.getCallerLocation()); env.getListener().post(w); try { - PatchUtil.apply(starlarkPath.getPath(), strip, outputDirectory); + PatchUtil.apply(starlarkPath.getPath(), strip, workingDirectory); } catch (PatchFailedException e) { throw new RepositoryFunctionException( Starlark.errorf("Error applying patch %s: %s", starlarkPath, e.getMessage()), @@ -867,61 +522,6 @@ } } - @StarlarkMethod( - name = "which", - doc = - "Returns the path of the corresponding program or None " - + "if there is no such program in the path.", - allowReturnNones = true, - useStarlarkThread = true, - parameters = { - @Param(name = "program", named = false, doc = "Program to find in the path."), - }) - @Nullable - public StarlarkPath which(String program, StarlarkThread thread) throws EvalException { - WorkspaceRuleEvent w = - WorkspaceRuleEvent.newWhichEvent( - program, rule.getLabel().toString(), thread.getCallerLocation()); - env.getListener().post(w); - if (program.contains("/") || program.contains("\\")) { - throw Starlark.errorf( - "Program argument of which() may not contain a / or a \\ ('%s' given)", program); - } - if (program.length() == 0) { - throw Starlark.errorf("Program argument of which() may not be empty"); - } - try { - StarlarkPath commandPath = findCommandOnPath(program); - if (commandPath != null) { - return commandPath; - } - - if (!program.endsWith(OsUtils.executableExtension())) { - program += OsUtils.executableExtension(); - return findCommandOnPath(program); - } - } catch (IOException e) { - // IOException when checking executable file means we cannot read the file data so - // we cannot execute it, swallow the exception. - } - return null; - } - - private StarlarkPath findCommandOnPath(String program) throws IOException { - for (String p : getPathEnvironment()) { - PathFragment fragment = PathFragment.create(p); - if (fragment.isAbsolute()) { - // We ignore relative path as they don't mean much here (relative to where? the workspace - // root?). - Path path = outputDirectory.getFileSystem().getPath(fragment).getChild(program.trim()); - if (path.exists() && path.isFile(Symlinks.FOLLOW) && path.isExecutable()) { - return new StarlarkPath(path); - } - } - } - return null; - } - private void warnAboutChecksumError(List<URL> urls, String errorMessage) { // Inform the user immediately, even though the file will still be downloaded. // This cannot be done by a regular error event, as all regular events are recorded @@ -1068,7 +668,7 @@ Optional.<String>absent(), outputPath.getPath(), env.getListener(), - osObject.getEnvironmentVariables(), + envVariables, getName()); if (executable) { outputPath.getPath().setExecutable(true); @@ -1317,7 +917,7 @@ java.nio.file.Path tempDirectory = Files.createTempDirectory(Paths.get(outputPath.toString()), "temp"); downloadDirectory = - outputDirectory.getFileSystem().getPath(tempDirectory.toFile().getAbsolutePath()); + workingDirectory.getFileSystem().getPath(tempDirectory.toFile().getAbsolutePath()); downloadedPath = downloadManager.download( @@ -1328,7 +928,7 @@ Optional.of(type), downloadDirectory, env.getListener(), - osObject.getEnvironmentVariables(), + envVariables, getName()); } catch (InterruptedException e) { env.getListener().post(w); @@ -1544,57 +1144,11 @@ return urls; } - // This is just for test to overwrite the path environment - private static ImmutableList<String> pathEnv = null; - - @VisibleForTesting - static void setPathEnvironment(String... pathEnv) { - StarlarkRepositoryContext.pathEnv = ImmutableList.<String>copyOf(pathEnv); - } - - private ImmutableList<String> getPathEnvironment() { - if (pathEnv != null) { - return pathEnv; - } - String pathEnviron = osObject.getEnvironmentVariables().get("PATH"); - if (pathEnviron == null) { - return ImmutableList.of(); - } - return ImmutableList.copyOf(pathEnviron.split(File.pathSeparator)); - } - @Override public String toString() { return "repository_ctx[" + rule.getLabel() + "]"; } - // Resolve the label given by value into a file path. - private StarlarkPath getPathFromLabel(Label label) throws EvalException, InterruptedException { - RootedPath rootedPath = RepositoryFunction.getRootedPathFromLabel(label, env); - SkyKey fileSkyKey = FileValue.key(rootedPath); - FileValue fileValue = null; - try { - fileValue = (FileValue) env.getValueOrThrow(fileSkyKey, IOException.class); - } catch (IOException e) { - throw Starlark.errorf("%s", e.getMessage()); - } - - if (fileValue == null) { - throw RepositoryFunction.restart(); - } - if (!fileValue.isFile() || fileValue.isSpecialFile()) { - throw Starlark.errorf("Not a regular file: %s", rootedPath.asPath().getPathString()); - } - - // A label does not contains space so it safe to use as a key. - try { - markerData.put("FILE:" + label, RepositoryFunction.fileValueToMarkerValue(fileValue)); - } catch (IOException e) { - throw Starlark.errorf("%s", e.getMessage()); - } - return new StarlarkPath(rootedPath.asPath()); - } - /** * Try to compute the paths of all attributes that are labels, including labels in list and dict * arguments. @@ -1603,6 +1157,7 @@ * exception thrown). In this way, we can enforce that all arguments are evaluated before we start * potentially more expensive operations. */ + // TODO(wyv): somehow migrate this to the base context too. public void enforceLabelAttributes() throws EvalException, InterruptedException { StructImpl attr = getAttr(); for (String name : attr.getFieldNames()) {
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkRepositoryFunction.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkRepositoryFunction.java index 44c7be0..ea3cf11 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkRepositoryFunction.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkRepositoryFunction.java
@@ -22,6 +22,7 @@ import com.google.devtools.build.lib.analysis.RuleDefinition; import com.google.devtools.build.lib.bazel.repository.RepositoryResolvedEvent; import com.google.devtools.build.lib.bazel.repository.downloader.DownloadManager; +import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.packages.BazelStarlarkContext; import com.google.devtools.build.lib.packages.Rule; @@ -170,11 +171,10 @@ outputDirectory, ignoredPatterns, env, - clientEnvironment, + ImmutableMap.copyOf(clientEnvironment), downloadManager, timeoutScaling, processWrapper, - markerData, starlarkSemantics, repositoryRemoteExecutor); @@ -224,6 +224,13 @@ env.getListener().handle(Event.debug(defInfo)); } + // Modify marker data to include the files used by the rule's implementation function. + for (Map.Entry<Label, String> entry : + starlarkRepositoryContext.getAccumulatedFileDigests().entrySet()) { + // A label does not contain spaces so it's safe to use as a key. + markerData.put("FILE:" + entry.getKey(), entry.getValue()); + } + String ruleClass = rule.getRuleClassObject().getRuleDefinitionEnvironmentLabel() + "%" + rule.getRuleClass(); if (verificationRules.contains(ruleClass)) {
diff --git a/src/test/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkRepositoryContextTest.java b/src/test/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkRepositoryContextTest.java index b4c0758..87fb22f 100644 --- a/src/test/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkRepositoryContextTest.java +++ b/src/test/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkRepositoryContextTest.java
@@ -47,11 +47,11 @@ import com.google.devtools.build.lib.vfs.Root; import com.google.devtools.build.lib.vfs.RootedPath; import com.google.devtools.build.skyframe.SkyFunction; +import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.time.Duration; -import java.util.HashMap; import java.util.Map; import javax.annotation.Nullable; import net.starlark.java.eval.Dict; @@ -128,6 +128,7 @@ protected void setUpContextForRule( Map<String, Object> kwargs, ImmutableSet<PathFragment> ignoredPathFragments, + ImmutableMap<String, String> envVariables, StarlarkSemantics starlarkSemantics, @Nullable RepositoryRemoteExecutor repoRemoteExecutor, Attribute... attributes) @@ -162,19 +163,19 @@ outputDirectory, ignoredPathFragments, environment, - ImmutableMap.of("FOO", "BAR"), + envVariables, downloader, 1.0, /*processWrapper=*/ null, - new HashMap<>(), starlarkSemantics, repoRemoteExecutor); } - protected void setUpContexForRule(String name) throws Exception { + protected void setUpContextForRule(String name) throws Exception { setUpContextForRule( ImmutableMap.of("name", name), ImmutableSet.of(), + ImmutableMap.of("FOO", "BAR"), StarlarkSemantics.DEFAULT, /* repoRemoteExecutor= */ null); } @@ -184,6 +185,7 @@ setUpContextForRule( ImmutableMap.of("name", "test", "foo", "bar"), ImmutableSet.of(), + ImmutableMap.of("FOO", "BAR"), StarlarkSemantics.DEFAULT, /* repoRemoteExecutor= */ null, Attribute.attr("foo", Type.STRING).build()); @@ -194,8 +196,12 @@ @Test public void testWhich() throws Exception { - setUpContexForRule("test"); - StarlarkRepositoryContext.setPathEnvironment("/bin", "/path/sbin", "."); + setUpContextForRule( + ImmutableMap.of("name", "test"), + ImmutableSet.of(), + ImmutableMap.of("PATH", String.join(File.pathSeparator, "/bin", "/path/sbin", ".")), + StarlarkSemantics.DEFAULT, + /* repoRemoteExecutor= */ null); scratch.file("/bin/true").setExecutable(true); scratch.file("/path/sbin/true").setExecutable(true); scratch.file("/path/sbin/false").setExecutable(true); @@ -212,7 +218,7 @@ @Test public void testFile() throws Exception { - setUpContexForRule("test"); + setUpContextForRule("test"); context.createFile(context.path("foobar"), "", true, true, thread); context.createFile(context.path("foo/bar"), "foobar", true, true, thread); context.createFile(context.path("bar/foo/bar"), "", true, true, thread); @@ -252,7 +258,7 @@ @Test public void testDelete() throws Exception { - setUpContexForRule("testDelete"); + setUpContextForRule("testDelete"); Path bar = outputDirectory.getRelative("foo/bar"); StarlarkPath barPath = context.path(bar.getPathString()); context.createFile(barPath, "content", true, true, thread); @@ -282,6 +288,7 @@ setUpContextForRule( ImmutableMap.of("name", "test"), ImmutableSet.of(PathFragment.create("under_workspace")), + ImmutableMap.of("FOO", "BAR"), StarlarkSemantics.DEFAULT, /* repoRemoteExecutor= */ null); assertThat(context.delete(underWorkspace.toString(), thread)).isTrue(); @@ -289,7 +296,7 @@ @Test public void testRead() throws Exception { - setUpContexForRule("test"); + setUpContextForRule("test"); context.createFile(context.path("foo/bar"), "foobar", true, true, thread); String content = context.readFile(context.path("foo/bar"), thread); @@ -298,7 +305,7 @@ @Test public void testPatch() throws Exception { - setUpContexForRule("test"); + setUpContextForRule("test"); StarlarkPath foo = context.path("foo"); context.createFile(foo, "line one\n", false, true, thread); StarlarkPath patchFile = context.path("my.patch"); @@ -310,7 +317,7 @@ @Test public void testCannotFindFileToPatch() throws Exception { - setUpContexForRule("test"); + setUpContextForRule("test"); StarlarkPath patchFile = context.path("my.patch"); context.createFile( context.path("my.patch"), "--- foo\n+++ foo\n" + ONE_LINE_PATCH, false, true, thread); @@ -329,7 +336,7 @@ @Test public void testPatchOutsideOfExternalRepository() throws Exception { - setUpContexForRule("test"); + setUpContextForRule("test"); StarlarkPath patchFile = context.path("my.patch"); context.createFile( context.path("my.patch"), @@ -352,7 +359,7 @@ @Test public void testPatchErrorWasThrown() throws Exception { - setUpContexForRule("test"); + setUpContextForRule("test"); StarlarkPath foo = context.path("foo"); StarlarkPath patchFile = context.path("my.patch"); context.createFile(foo, "line three\n", false, true, thread); @@ -406,6 +413,7 @@ setUpContextForRule( attrValues, ImmutableSet.of(), + ImmutableMap.of("FOO", "BAR"), StarlarkSemantics.builder() .setBool(BuildLanguageOptions.EXPERIMENTAL_REPO_REMOTE_EXEC, true) .build(), @@ -439,7 +447,7 @@ @Test public void testSymlink() throws Exception { - setUpContexForRule("test"); + setUpContextForRule("test"); context.createFile(context.path("foo"), "foobar", true, true, thread); context.symlink(context.path("foo"), context.path("bar"), thread); @@ -458,7 +466,7 @@ @Test public void testDirectoryListing() throws Exception { - setUpContexForRule("test"); + setUpContextForRule("test"); scratch.file("/my/folder/a"); scratch.file("/my/folder/b"); scratch.file("/my/folder/c");
diff --git a/src/test/shell/bazel/bazel_workspaces_test.sh b/src/test/shell/bazel/bazel_workspaces_test.sh index 527fe07..8f1663f 100755 --- a/src/test/shell/bazel/bazel_workspaces_test.sh +++ b/src/test/shell/bazel/bazel_workspaces_test.sh
@@ -509,8 +509,6 @@ build_and_process_log --exclude_rule "//external:local_config_cc" - # This assertion matches the location of the rule's implementation function. - ensure_contains_exactly 'location: .*repos.bzl:1:5' 1 ensure_contains_atleast 'rule: "//external:repo"' 1 ensure_contains_exactly 'os_event' 1 }