summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTristan Maat <tm@tlater.net>2018-04-06 16:02:07 +0000
committerTristan Maat <tristan.maat@codethink.co.uk>2018-04-13 12:51:17 +0100
commit299df2339fee78af2a18faa656e85f01b972926d (patch)
tree12a70345cde2483fe5ea707b1fecd7e7ce7abe0b
parent596264d1e4cdcdf61d1f81b6d6b11ca504048a35 (diff)
downloadbuildstream-299df2339fee78af2a18faa656e85f01b972926d.tar.gz
Add element.prepare method
This is one of the tasks of #209
-rw-r--r--buildstream/_workspaces.py9
-rw-r--r--buildstream/buildelement.py30
-rw-r--r--buildstream/element.py40
-rw-r--r--buildstream/plugins/elements/import.py6
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')