diff options
Diffstat (limited to 'buildstream/sandbox/_sandboxremote.py')
-rw-r--r-- | buildstream/sandbox/_sandboxremote.py | 82 |
1 files changed, 70 insertions, 12 deletions
diff --git a/buildstream/sandbox/_sandboxremote.py b/buildstream/sandbox/_sandboxremote.py index 60a053e0c..758160219 100644 --- a/buildstream/sandbox/_sandboxremote.py +++ b/buildstream/sandbox/_sandboxremote.py @@ -19,12 +19,14 @@ # Jim MacArthur <jim.macarthur@codethink.co.uk> import os +import shlex from urllib.parse import urlparse from functools import partial import grpc -from . import Sandbox +from . import Sandbox, SandboxCommandError +from .sandbox import _SandboxBatch from ..storage._filebaseddirectory import FileBasedDirectory from ..storage._casbaseddirectory import CasBasedDirectory from .. import _signals @@ -212,7 +214,7 @@ class SandboxRemote(Sandbox): new_dir = CasBasedDirectory(self._get_context().artifactcache.cas, ref=dir_digest) self._set_virtual_directory(new_dir) - def run(self, command, flags, *, cwd=None, env=None): + def _run(self, command, flags, *, cwd, env): # Upload sources upload_vdir = self.get_virtual_directory() @@ -230,16 +232,6 @@ class SandboxRemote(Sandbox): if not cascache.verify_digest_pushed(self._get_project(), upload_vdir.ref): raise SandboxError("Failed to verify that source has been pushed to the remote artifact cache.") - # Fallback to the sandbox default settings for - # the cwd and env. - # - cwd = self._get_work_directory(cwd=cwd) - env = self._get_environment(cwd=cwd, env=env) - - # We want command args as a list of strings - if isinstance(command, str): - command = [command] - # Now transmit the command to execute operation = self.run_remote_command(command, upload_vdir.ref, cwd, env) @@ -275,3 +267,69 @@ class SandboxRemote(Sandbox): self.process_job_output(action_result.output_directories, action_result.output_files) return 0 + + def _create_batch(self, main_group, flags, *, collect=None): + return _SandboxRemoteBatch(self, main_group, flags, collect=collect) + + +# _SandboxRemoteBatch() +# +# Command batching by shell script generation. +# +class _SandboxRemoteBatch(_SandboxBatch): + + def __init__(self, sandbox, main_group, flags, *, collect=None): + super().__init__(sandbox, main_group, flags, collect=collect) + + self.script = None + self.first_command = None + self.cwd = None + self.env = None + + def execute(self): + self.script = "" + + self.main_group.execute(self) + + first = self.first_command + if first and self.sandbox.run(['sh', '-c', '-e', self.script], self.flags, cwd=first.cwd, env=first.env) != 0: + raise SandboxCommandError("Command execution failed", collect=self.collect) + + def execute_group(self, group): + group.execute_children(self) + + def execute_command(self, command): + if self.first_command is None: + # First command in batch + # Initial working directory and environment of script already matches + # the command configuration. + self.first_command = command + else: + # Change working directory for this command + if command.cwd != self.cwd: + self.script += "mkdir -p {}\n".format(command.cwd) + self.script += "cd {}\n".format(command.cwd) + + # Update environment for this command + for key in self.env.keys(): + if key not in command.env: + self.script += "unset {}\n".format(key) + for key, value in command.env.items(): + if key not in self.env or self.env[key] != value: + self.script += "export {}={}\n".format(key, shlex.quote(value)) + + # Keep track of current working directory and environment + self.cwd = command.cwd + self.env = command.env + + # Actual command execution + cmdline = ' '.join(shlex.quote(cmd) for cmd in command.command) + self.script += "(set -ex; {})".format(cmdline) + + # Error handling + label = command.label or cmdline + quoted_label = shlex.quote("'{}'".format(label)) + self.script += " || (echo Command {} failed with exitcode $? >&2 ; exit 1)\n".format(quoted_label) + + def execute_call(self, call): + raise SandboxError("SandboxRemote does not support callbacks in command batches") |