summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Coldrick <adam.coldrick@codethink.co.uk>2015-03-25 11:03:09 +0000
committerBaserock Gerrit <gerrit@baserock.org>2015-04-01 16:44:44 +0000
commitbd788c7219d8b59be8eb1168f2121d8b280020e6 (patch)
treef5b6d8d028f7171a0a9c01787d29c7fdfd3a842f
parentd28595c19a5b9ee5bcacc6de4319de625024cb87 (diff)
downloadmorph-bd788c7219d8b59be8eb1168f2121d8b280020e6.tar.gz
Implement partial builds
This commit allows you to build only a single chunk or stratum and its dependencies, instead of a whole system. This will be useful for developers who want to quickly check the build commands of a chunk for example. You can give more than one chunk or stratum to the command, and the build will run up to the latest one, building the dependencies of both. An example of the usage: morph build systems/devel-system-x86_64-generic.morph \ strata/build-essential.morph strata/core/curl.morph This will only build up to curl in the core stratum. Change-Id: Id55b8335fc61e8fdf847f5e383cfedd13d07e6d2
-rw-r--r--morphlib/buildcommand.py5
-rw-r--r--morphlib/plugins/build_plugin.py108
-rw-r--r--tests.build/only-build-systems.stderr4
3 files changed, 94 insertions, 23 deletions
diff --git a/morphlib/buildcommand.py b/morphlib/buildcommand.py
index be8a1507..a6be8a8c 100644
--- a/morphlib/buildcommand.py
+++ b/morphlib/buildcommand.py
@@ -119,7 +119,10 @@ class BuildCommand(object):
root_kind = root_artifact.source.morphology['kind']
if root_kind != 'system':
raise morphlib.Error(
- 'Building a %s directly is not supported' % root_kind)
+ 'In order to build this %s directly, please give the filename '
+ 'of the system which contains it, and the name of the %s. '
+ 'See `morph build --help` for more information.'
+ % (root_kind, root_kind))
def _validate_architecture(self, root_artifact):
'''Perform the validation between root and target architectures.'''
diff --git a/morphlib/plugins/build_plugin.py b/morphlib/plugins/build_plugin.py
index a5502180..f6372aed 100644
--- a/morphlib/plugins/build_plugin.py
+++ b/morphlib/plugins/build_plugin.py
@@ -13,21 +13,33 @@
# with this program. If not, see <http://www.gnu.org/licenses/>.
-import cliapp
+import collections
import contextlib
import uuid
import logging
+import cliapp
+
import morphlib
+class ComponentNotInSystemError(morphlib.Error):
+
+ def __init__(self, components, system):
+ components = ', '.join(components)
+ self.msg = ('Components %s are not in %s. Ensure you provided '
+ 'component names rather than filenames.'
+ % (components, system))
+
+
class BuildPlugin(cliapp.Plugin):
def enable(self):
self.app.add_subcommand('build-morphology', self.build_morphology,
- arg_synopsis='(REPO REF FILENAME)...')
+ arg_synopsis='REPO REF FILENAME '
+ '[COMPONENT...]')
self.app.add_subcommand('build', self.build,
- arg_synopsis='SYSTEM')
+ arg_synopsis='SYSTEM [COMPONENT...]')
self.app.add_subcommand('distbuild-morphology',
self.distbuild_morphology,
arg_synopsis='SYSTEM')
@@ -92,6 +104,8 @@ class BuildPlugin(cliapp.Plugin):
* `REPO` is a git repository URL.
* `REF` is a branch or other commit reference in that repository.
* `FILENAME` is a morphology filename at that ref.
+ * `COMPONENT...` is the names of one or more chunks or strata to
+ build. If none are given then the system at FILENAME is built.
You probably want `morph build` instead. However, in some
cases it is more convenient to not have to create a Morph
@@ -104,8 +118,14 @@ class BuildPlugin(cliapp.Plugin):
Example:
- morph build-morphology baserock:baserock/definitions \
- master devel-system-x86_64-generic.morph
+ morph build-morphology baserock:baserock/definitions \\
+ master systems/devel-system-x86_64-generic.morph
+
+ Partial build example:
+
+ morph build-morphology baserock:baserock/definitions \\
+ master systems/devel-system-x86_64-generic.morph \\
+ build-essential
'''
@@ -117,15 +137,21 @@ class BuildPlugin(cliapp.Plugin):
self.app.settings['cachedir-min-space'])
build_command = morphlib.buildcommand.BuildCommand(self.app)
- for repo_name, ref, filename in self.app.itertriplets(args):
- build_command.build(repo_name, ref, filename)
+ repo, ref, filename = args[0:3]
+ filename = morphlib.util.sanitise_morphology_path(filename)
+ component_names = [morphlib.util.sanitise_morphology_path(name)
+ for name in args[3:]]
+ self.start_build(repo, ref, build_command, filename,
+ component_names)
def build(self, args):
'''Build a system image in the current system branch
Command line arguments:
- * `SYSTEM` is the name of the system to build.
+ * `SYSTEM` is the filename of the system to build.
+ * `COMPONENT...` is the names of one or more chunks or strata to
+ build. If this is not given then the SYSTEM is built.
This builds a system image, and any of its components that
need building. The system name is the basename of the system
@@ -145,14 +171,14 @@ class BuildPlugin(cliapp.Plugin):
Example:
- morph build devel-system-x86_64-generic.morph
+ morph build systems/devel-system-x86_64-generic.morph
- '''
+ Partial build example:
- if len(args) != 1:
- raise cliapp.AppException('morph build expects exactly one '
- 'parameter: the system to build')
+ morph build systems/devel-system-x86_64-generic.morph \\
+ build-essential
+ '''
# Raise an exception if there is not enough space
morphlib.util.check_disk_available(
self.app.settings['tempdir'],
@@ -165,6 +191,7 @@ class BuildPlugin(cliapp.Plugin):
system_filename = morphlib.util.sanitise_morphology_path(args[0])
system_filename = sb.relative_to_root_repo(system_filename)
+ component_names = args[1:]
logging.debug('System branch is %s' % sb.root_directory)
@@ -178,11 +205,14 @@ class BuildPlugin(cliapp.Plugin):
build_command = morphlib.buildcommand.BuildCommand(self.app)
if self.app.settings['local-changes'] == 'include':
- self._build_with_local_changes(build_command, sb, system_filename)
+ self._build_with_local_changes(build_command, sb, system_filename,
+ component_names)
else:
- self._build_local_commit(build_command, sb, system_filename)
+ self._build_local_commit(build_command, sb, system_filename,
+ component_names)
- def _build_with_local_changes(self, build_command, sb, system_filename):
+ def _build_with_local_changes(self, build_command, sb, system_filename,
+ component_names):
'''Construct a branch including user's local changes, and build that.
It is often a slow process to check all repos in the system branch for
@@ -214,10 +244,11 @@ class BuildPlugin(cliapp.Plugin):
name=name, email=email, build_uuid=build_uuid,
status=self.app.status)
with pbb as (repo, commit, original_ref):
- build_command.build(repo, commit, system_filename,
- original_ref=original_ref)
+ self.start_build(repo, commit, build_command, system_filename,
+ component_names, original_ref=original_ref)
- def _build_local_commit(self, build_command, sb, system_filename):
+ def _build_local_commit(self, build_command, sb, system_filename,
+ component_names):
'''Build whatever commit the user has checked-out locally.
This ignores any uncommitted changes. Also, if the user has a commit
@@ -245,4 +276,41 @@ class BuildPlugin(cliapp.Plugin):
definitions_repo = morphlib.gitdir.GitDirectory(definitions_repo_path)
commit = definitions_repo.resolve_ref_to_commit(ref)
- build_command.build(root_repo_url, commit, system_filename)
+ self.start_build(root_repo_url, commit, build_command,
+ system_filename, component_names)
+
+ def _find_artifacts(self, names, root_artifact):
+ found = collections.OrderedDict()
+ not_found = names
+ for a in root_artifact.walk():
+ name = a.source.morphology['name']
+ if name in names and name not in found:
+ found[name] = a
+ not_found.remove(name)
+ return found, not_found
+
+ def start_build(self, repo, commit, bc, system_filename,
+ component_names, original_ref=None):
+ '''Actually run the build.
+
+ If a set of components was given, only build those. Otherwise,
+ build the whole system.
+
+ '''
+ self.app.status(msg='Deciding on task order')
+ srcpool = bc.create_source_pool(repo, commit, system_filename)
+ bc.validate_sources(srcpool)
+ root = bc.resolve_artifacts(srcpool)
+ if not component_names:
+ component_names = [root.source.name]
+ components, not_found = self._find_artifacts(component_names, root)
+ if not_found:
+ raise ComponentNotInSystemError(not_found, system_filename)
+
+ for name, component in components.iteritems():
+ component.build_env = root.build_env
+ bc.build_in_order(component)
+ self.app.status(msg='%(kind)s %(name)s is cached at %(path)s',
+ kind=component.source.morphology['kind'],
+ name=name,
+ path=bc.lac.artifact_filename(component))
diff --git a/tests.build/only-build-systems.stderr b/tests.build/only-build-systems.stderr
index ba7339d2..ac24ab25 100644
--- a/tests.build/only-build-systems.stderr
+++ b/tests.build/only-build-systems.stderr
@@ -1,2 +1,2 @@
-ERROR: Building a stratum directly is not supported
-ERROR: Building a chunk directly is not supported
+ERROR: In order to build this stratum directly, please give the filename of the system which contains it, and the name of the stratum. See `morph build --help` for more information.
+ERROR: In order to build this chunk directly, please give the filename of the system which contains it, and the name of the chunk. See `morph build --help` for more information.