diff options
Diffstat (limited to 'buildstream')
-rw-r--r-- | buildstream/_workspaces.py | 9 | ||||
-rw-r--r-- | buildstream/buildelement.py | 30 | ||||
-rw-r--r-- | buildstream/element.py | 40 | ||||
-rw-r--r-- | buildstream/plugins/elements/import.py | 6 |
4 files changed, 70 insertions, 15 deletions
diff --git a/buildstream/_workspaces.py b/buildstream/_workspaces.py index 8561dfe54..828343538 100644 --- a/buildstream/_workspaces.py +++ b/buildstream/_workspaces.py @@ -25,10 +25,11 @@ from . import _yaml from ._exceptions import LoadError, LoadErrorReason -BST_WORKSPACE_FORMAT_VERSION = 2 +BST_WORKSPACE_FORMAT_VERSION = 3 # Hold on to a list of members which get serialized _WORKSPACE_MEMBERS = [ + 'prepared', 'path', 'last_successful', 'running_files' @@ -53,7 +54,8 @@ _WORKSPACE_MEMBERS = [ # made obsolete with failed build artifacts. # class Workspace(): - def __init__(self, project, *, path=None, last_successful=None, running_files=None): + def __init__(self, project, *, last_successful=None, path=None, prepared=False, running_files=None): + self.prepared = prepared self.last_successful = last_successful self.path = path self.running_files = running_files if running_files is not None else {} @@ -376,7 +378,7 @@ class Workspaces(): for element, config in _yaml.node_items(workspaces) } - elif version == 1 or version == BST_WORKSPACE_FORMAT_VERSION: + elif version >= 1 and version <= BST_WORKSPACE_FORMAT_VERSION: workspaces = _yaml.node_get(workspaces, dict, "workspaces", default_value={}) res = {element: self._load_workspace(self._project, node) for element, node in _yaml.node_items(workspaces)} @@ -402,6 +404,7 @@ class Workspaces(): # def _load_workspace(self, project, node): dictionary = { + 'prepared': _yaml.node_get(node, bool, 'prepared', default_value=False), 'path': _yaml.node_get(node, str, 'path'), 'last_successful': _yaml.node_get(node, str, 'last_successful', default_value=None), 'running_files': _yaml.node_get(node, dict, 'running_files', default_value=None), diff --git a/buildstream/buildelement.py b/buildstream/buildelement.py index 243491c97..6d6c200ca 100644 --- a/buildstream/buildelement.py +++ b/buildstream/buildelement.py @@ -172,20 +172,12 @@ class BuildElement(Element): # Run commands for command_name in _command_steps: commands = self.commands[command_name] - if not commands: + if not commands or command_name == 'configure-commands': continue with self.timed_activity("Running {}".format(command_name)): for cmd in commands: - self.status("Running {}".format(command_name), detail=cmd) - - # Note the -e switch to 'sh' means to exit with an error - # if any untested command fails. - # - exitcode = sandbox.run(['sh', '-c', '-e', cmd + '\n'], - SandboxFlags.ROOT_READ_ONLY) - if exitcode != 0: - raise ElementError("Command '{}' failed with exitcode {}".format(cmd, exitcode)) + self._run_command(sandbox, cmd, command_name) # %{install-root}/%{build-root} should normally not be written # to - if an element later attempts to stage to a location @@ -204,6 +196,12 @@ class BuildElement(Element): # always the /buildstream-install directory return self.get_variable('install-root') + def prepare(self, sandbox): + commands = self.commands['configure-commands'] + if commands: + for cmd in commands: + self._run_command(sandbox, cmd, 'configure-commands') + def generate_script(self): script = "" for command_name in _command_steps: @@ -226,3 +224,15 @@ class BuildElement(Element): commands.append(command) return commands + + def _run_command(self, sandbox, cmd, cmd_name): + with self.timed_activity("Running {}".format(cmd_name)): + self.status("Running {}".format(cmd_name), detail=cmd) + + # Note the -e switch to 'sh' means to exit with an error + # if any untested command fails. + # + exitcode = sandbox.run(['sh', '-c', '-e', cmd + '\n'], + SandboxFlags.ROOT_READ_ONLY) + if exitcode != 0: + raise ElementError("Command '{}' failed with exitcode {}".format(cmd, exitcode)) diff --git a/buildstream/element.py b/buildstream/element.py index 5913646ab..5df21bf72 100644 --- a/buildstream/element.py +++ b/buildstream/element.py @@ -281,6 +281,25 @@ class Element(Plugin): raise ImplError("element plugin '{kind}' does not implement stage()".format( kind=self.get_kind())) + def prepare(self, sandbox): + """Run one-off preparation commands. + + This is run before assemble(), but is guaranteed to run only + the first time if we build incrementally - this makes it + possible to run configure-like commands without causing the + entire element to rebuild. + + Args: + sandbox (:class:`.Sandbox`): The build sandbox + + Raises: + (:class:`.ElementError`): When the element raises an error + + By default, this method does nothing, but may be overriden to + allow configure-like commands. + """ + pass + def assemble(self, sandbox): """Assemble the output artifact @@ -1167,6 +1186,23 @@ class Element(Plugin): return refs + # _prepare(): + # + # Internal method for calling public abstract prepare() method. + # + def _prepare(self, sandbox): + workspace = self._get_workspace() + + # We need to ensure that the prepare() method is only called + # once in workspaces, because the changes will persist across + # incremental builds - not desirable, for example, in the case + # of autotools' `./configure`. + if not (workspace and workspace.prepared): + self.prepare(sandbox) + + if workspace: + workspace.prepared = True + # _assemble(): # # Internal method for calling public abstract assemble() method. @@ -1202,7 +1238,9 @@ class Element(Plugin): self.configure_sandbox(sandbox) # Step 2 - Stage self.stage(sandbox) - # Step 3 - Assemble + # Step 3 - Prepare + self._prepare(sandbox) + # Step 4 - Assemble collect = self.assemble(sandbox) except BstError as e: # If an error occurred assembling an element in a sandbox, diff --git a/buildstream/plugins/elements/import.py b/buildstream/plugins/elements/import.py index 23cad9cb9..dfad178b5 100644 --- a/buildstream/plugins/elements/import.py +++ b/buildstream/plugins/elements/import.py @@ -32,7 +32,7 @@ The empty configuration is as such: import os import shutil -from buildstream import BuildElement, ElementError +from buildstream import Element, BuildElement, ElementError # Element implementation for the 'import' kind. @@ -92,6 +92,10 @@ class ImportElement(BuildElement): # And we're done return '/output' + def prepare(self, sandbox): + # We inherit a non-default prepare from BuildElement. + Element.prepare(self, sandbox) + def generate_script(self): build_root = self.get_variable('build-root') install_root = self.get_variable('install-root') |