aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/main/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkExecutionResult.java39
-rw-r--r--src/main/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryContext.java48
-rwxr-xr-xsrc/test/shell/bazel/skylark_repository_test.sh32
3 files changed, 104 insertions, 15 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkExecutionResult.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkExecutionResult.java
index b4181aa449..ad2e7b60a8 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkExecutionResult.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkExecutionResult.java
@@ -13,6 +13,7 @@
// limitations under the License.
package com.google.devtools.build.lib.bazel.repository.skylark;
+import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.lib.events.Location;
import com.google.devtools.build.lib.shell.BadExitStatusException;
import com.google.devtools.build.lib.shell.Command;
@@ -23,9 +24,11 @@ import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
import com.google.devtools.build.lib.syntax.EvalException;
import com.google.devtools.build.lib.util.Preconditions;
+import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
/**
* A structure callable from Skylark that stores the result of repository_ctx.execute() method. It
@@ -89,9 +92,12 @@ final class SkylarkExecutionResult {
/**
* Returns a Builder that can be used to execute a command and build an execution result.
+ *
+ * @param environment pass through the list of environment variables from the client to be passed
+ * to the execution environment.
*/
- public static Builder builder() {
- return new Builder();
+ public static Builder builder(Map<String, String> environment) {
+ return new Builder(environment);
}
/**
@@ -100,9 +106,15 @@ final class SkylarkExecutionResult {
static final class Builder {
private final List<String> args = new ArrayList<>();
+ private File directory = null;
+ private final ImmutableMap.Builder<String, String> envBuilder = ImmutableMap.builder();
private long timeout = -1;
private boolean executed = false;
+ private Builder(Map<String, String> environment) {
+ envBuilder.putAll(environment);
+ }
+
/**
* Adds arguments to the list of arguments to pass to the command. The first argument is
* expected to be the binary to execute. The subsequent arguments are the arguments passed
@@ -125,6 +137,25 @@ final class SkylarkExecutionResult {
}
/**
+ * Set the path to the directory to execute the result process. This method must be called
+ * before calling {@link #execute()}.
+ */
+ Builder setDirectory(File path) throws EvalException {
+ this.directory = path;
+ return this;
+ }
+
+ /**
+ * Add an environment variables to be added to the list of environment variables. For all
+ * key <code>k</code> of <code>variables</code>, the resulting process will have the variable
+ * <code>k=variables.get(k)</code> defined.
+ */
+ Builder addEnvironmentVariables(Map<String, String> variables) {
+ this.envBuilder.putAll(variables);
+ return this;
+ }
+
+ /**
* Sets the timeout, in milliseconds, after which the executed command will be terminated.
*/
Builder setTimeout(long timeout) {
@@ -140,6 +171,7 @@ final class SkylarkExecutionResult {
Preconditions.checkArgument(timeout > 0, "Timeout must be set prior to calling execute().");
Preconditions.checkArgument(!args.isEmpty(), "No command specified.");
Preconditions.checkState(!executed, "Command was already executed, cannot re-use builder.");
+ Preconditions.checkNotNull(directory, "Directory must be set before calling execute().");
executed = true;
try {
@@ -147,7 +179,8 @@ final class SkylarkExecutionResult {
for (int i = 0; i < args.size(); i++) {
argsArray[i] = args.get(i);
}
- CommandResult result = new Command(argsArray).execute(new byte[]{}, timeout, false);
+ Command command = new Command(argsArray, envBuilder.build(), directory);
+ CommandResult result = command.execute(new byte[]{}, timeout, false);
return new SkylarkExecutionResult(result);
} catch (BadExitStatusException e) {
return new SkylarkExecutionResult(e.getResult());
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryContext.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryContext.java
index e4dd0293c8..35b4e71556 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryContext.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryContext.java
@@ -293,23 +293,57 @@ public class SkylarkRepositoryContext {
return osObject;
}
+ private void createOutputDirectory() throws RepositoryFunctionException {
+ try {
+ if (!outputDirectory.exists()) {
+ makeDirectories(outputDirectory);
+ outputDirectory.createDirectory();
+ }
+ } catch (IOException e) {
+ throw new RepositoryFunctionException(e, Transience.TRANSIENT);
+ }
+ }
+
@SkylarkCallable(
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."
+ + " command. The <code>environment</code> map can be used to override some environment"
+ + " variables to be passed to the process."
)
- public SkylarkExecutionResult execute(List<Object> arguments, long timeout) throws EvalException {
- return SkylarkExecutionResult.builder()
- .addArguments(arguments).setTimeout(timeout / 1000).execute();
+ public SkylarkExecutionResult execute(List<Object> arguments, Integer timeout,
+ Map<String, String> environment) throws EvalException, RepositoryFunctionException {
+ createOutputDirectory();
+ return SkylarkExecutionResult.builder(osObject.getEnvironmentVariables())
+ .addArguments(arguments)
+ .setDirectory(outputDirectory.getPathFile())
+ .addEnvironmentVariables(environment)
+ .setTimeout(timeout.longValue() * 1000)
+ .execute();
+ }
+
+ @SkylarkCallable(name = "execute", documented = false)
+ public SkylarkExecutionResult execute(List<Object> arguments)
+ throws EvalException, RepositoryFunctionException {
+ createOutputDirectory();
+ return SkylarkExecutionResult.builder(osObject.getEnvironmentVariables())
+ .setDirectory(outputDirectory.getPathFile())
+ .addArguments(arguments)
+ .setTimeout(600000)
+ .execute();
}
@SkylarkCallable(name = "execute", documented = false)
- public SkylarkExecutionResult execute(List<Object> arguments) throws EvalException {
- return SkylarkExecutionResult.builder()
- .addArguments(arguments).setTimeout(600000).execute();
+ public SkylarkExecutionResult execute(List<Object> arguments, Integer timeout)
+ throws EvalException, RepositoryFunctionException {
+ createOutputDirectory();
+ return SkylarkExecutionResult.builder(osObject.getEnvironmentVariables())
+ .setDirectory(outputDirectory.getPathFile())
+ .addArguments(arguments)
+ .setTimeout(timeout.longValue() * 1000)
+ .execute();
}
@SkylarkCallable(
diff --git a/src/test/shell/bazel/skylark_repository_test.sh b/src/test/shell/bazel/skylark_repository_test.sh
index d20cb8b033..7d5f30b379 100755
--- a/src/test/shell/bazel/skylark_repository_test.sh
+++ b/src/test/shell/bazel/skylark_repository_test.sh
@@ -318,6 +318,8 @@ function test_skylark_repository_which_and_execute() {
# Our custom repository rule
cat >test.bzl <<EOF
def _impl(repository_ctx):
+ # Symlink so a repository is created
+ repository_ctx.symlink(repository_ctx.path("$repo2"), repository_ctx.path(""))
bash = repository_ctx.which("bash")
if bash == None:
fail("Bash not found!")
@@ -326,12 +328,10 @@ def _impl(repository_ctx):
fail("bin.sh not found!")
result = repository_ctx.execute([bash, "--version"])
if result.return_code != 0:
- fail("Non-zero return code from bash: " + result.return_code)
+ fail("Non-zero return code from bash: " + str(result.return_code))
if result.stderr != "":
fail("Non-empty error output: " + result.stderr)
print(result.stdout)
- # Symlink so a repository is created
- repository_ctx.symlink(repository_ctx.path("$repo2"), repository_ctx.path(""))
repo = repository_rule(implementation=_impl, local=True)
EOF
@@ -344,19 +344,41 @@ function test_skylark_repository_execute_stderr() {
cat >test.bzl <<EOF
def _impl(repository_ctx):
+ # Symlink so a repository is created
+ repository_ctx.symlink(repository_ctx.path("$repo2"), repository_ctx.path(""))
result = repository_ctx.execute([str(repository_ctx.which("bash")), "-c", "echo erf >&2; exit 1"])
if result.return_code != 1:
- fail("Incorrect return code from bash (should be 1): " + result.return_code)
+ fail("Incorrect return code from bash: %s != 1\n%s" % (result.return_code, result.stderr))
if result.stdout != "":
fail("Non-empty output: %s (stderr was %s)" % (result.stdout, result.stderr))
print(result.stderr)
+repo = repository_rule(implementation=_impl, local=True)
+EOF
+
+ bazel build @foo//:bar >& $TEST_log || fail "Failed to build"
+ expect_log "erf"
+}
+
+
+function test_skylark_repository_execute_env_and_workdir() {
+ setup_skylark_repository
+
+ cat >test.bzl <<EOF
+def _impl(repository_ctx):
# Symlink so a repository is created
repository_ctx.symlink(repository_ctx.path("$repo2"), repository_ctx.path(""))
+ result = repository_ctx.execute(
+ [str(repository_ctx.which("bash")), "-c", "echo PWD=\$PWD TOTO=\$TOTO"],
+ 1000000,
+ { "TOTO": "titi" })
+ if result.return_code != 0:
+ fail("Incorrect return code from bash: %s != 0\n%s" % (result.return_code, result.stderr))
+ print(result.stdout)
repo = repository_rule(implementation=_impl, local=True)
EOF
bazel build @foo//:bar >& $TEST_log || fail "Failed to build"
- expect_log "erf"
+ expect_log "PWD=$repo2 TOTO=titi"
}
function test_skylark_repository_environ() {