summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xmorph72
-rw-r--r--morphlib/builder2.py57
2 files changed, 124 insertions, 5 deletions
diff --git a/morph b/morph
index 0b1e57eb..e2514042 100755
--- a/morph
+++ b/morph
@@ -21,6 +21,7 @@ import collections
import json
import logging
import os
+import tempfile
import morphlib
from morphlib import buildworker
@@ -191,8 +192,7 @@ class Morph(cliapp.Application):
return pool
-
- def cmd_build(self, args):
+ def cmd_build_old(self, args):
'''Build a binary from a morphology.
Command line arguments are the repository, git tree-ish reference,
@@ -236,6 +236,74 @@ class Morph(cliapp.Application):
tempdir.remove()
+ def cmd_build(self, args):
+ '''Build a binary from a morphology.
+
+ Command line arguments are the repository, git tree-ish reference,
+ and morphology filename. Morph takes care of building all dependencies
+ before building the morphology. All generated binaries are put into the
+ cache.
+
+ (The triplet of command line arguments may be repeated as many
+ times as necessary.)
+
+ '''
+
+ logging.debug('cmd_build starting')
+
+ cachedir = self.settings['cachedir']
+ if not os.path.exists(cachedir):
+ os.mkdir(cachedir)
+
+ build_env = morphlib.buildenvironment.BuildEnvironment(self.settings)
+ ckc = morphlib.cachekeycomputer.CacheKeyComputer(build_env)
+ lac = morphlib.localartifactcache.LocalArtifactCache(cachedir)
+ lrc = morphlib.localrepocache.LocalRepoCache(
+ cachedir,
+ self.settings['git-base-url'],
+ bundle_base_url=self.settings['bundle-server'])
+
+ for repo_name, ref, filename in self._itertriplets(args):
+ logging.debug('cmd_build: %s %s %s' % (repo_name, ref, filename))
+ srcpool = self._create_source_pool(lrc, repo_name, ref, filename)
+ ar = morphlib.artifactresolver.ArtifactResolver(ckc)
+ artifacts = ar.resolve_artifacts(srcpool)
+ order = morphlib.buildorder.BuildOrder(artifacts)
+
+ needed = []
+ for group in order.groups:
+ for artifact in group:
+ if not lac.has(artifact):
+ needed.append(artifact)
+
+ for artifact in needed:
+ repo = lrc.cache_repo(repo_name)
+ repo.update()
+
+ if self.settings['bootstrap']:
+ staging_root = '/'
+ else:
+ staging_root = tempfile.mkdtemp()
+ staging_area = morphlib.stagingarea.StagingArea(staging_root)
+
+ builder = morphlib.builder2.Builder(staging_area, lac,
+ build_env,
+ self.settings['max-jobs'])
+ for group in order.groups:
+ for artifact in group:
+ if artifact in needed:
+ builder.build_and_cache(artifact)
+ # install chunks only
+ chunk_artifacts = [x
+ for x in group
+ if x.source.morphology['kind'] == 'chunk']
+ for artifact in chunk_artifacts:
+ handle = lac.get(artifact)
+ staging_area.install_artifact(handle)
+
+ if staging_root != '/':
+ staging_area.remove()
+
def cmd_show_dependencies(self, args):
'''Dumps the dependency tree of all input morphologies.'''
diff --git a/morphlib/builder2.py b/morphlib/builder2.py
index 7eff7066..394805c6 100644
--- a/morphlib/builder2.py
+++ b/morphlib/builder2.py
@@ -17,6 +17,7 @@
import json
import logging
import os
+import time
import morphlib
@@ -87,6 +88,7 @@ class BuilderBase(object):
self.artifact.cache_key)
def runcmd(self, *args, **kwargs):
+ kwargs['env'] = self.build_env.env
return self.staging_area.runcmd(*args, **kwargs)
@@ -103,14 +105,63 @@ class ChunkBuilder(BuilderBase):
return morphology[which]
def build_and_cache(self): # pragma: no cover
+ builddir = self.staging_area.builddir(self.artifact.source)
+ self.get_sources(builddir)
destdir = self.staging_area.destdir(self.artifact.source)
- self.run_commands(destdir)
+ self.run_commands(builddir, destdir)
self.assemble_chunk_artifacts(destdir)
- def run_commands(self, destdir): # pragma: no cover
+ def get_sources(self, srcdir): # pragma: no cover
+ '''Get sources from git to a source directory, for building.'''
+
+ def extract_treeish(source, destdir):
+ logging.debug('Extracting %s into %s' % (source.repo, destdir))
+ if not os.path.exists(destdir):
+ os.mkdir(destdir)
+ morphlib.git.copy_repository(source.repo.path, destdir,
+ logging.debug)
+ morphlib.git.checkout_ref(destdir, source.sha1, logging.debug)
+ morphlib.git.reset_workdir(destdir, logging.debug)
+ submodules = morphlib.git.Submodules(source.repo.path, source.sha1)
+ try:
+ submodules.load()
+ except morphlib.git.NoModulesFileError:
+ return []
+ else:
+ return [(sub.url, os.path.join(destdir, sub.path))
+ for sub in submodules]
+
+ todo = [(self.artifact.source, srcdir)]
+ while todo:
+ source, srcdir = todo.pop()
+ todo += extract_treeish(source, srcdir)
+ self.set_mtime_recursively(srcdir)
+
+ def set_mtime_recursively(self, root): # pragma: no cover
+ '''Set the mtime for every file in a directory tree to the same.
+
+ We do this because git checkout does not set the mtime to anything,
+ and some projects (binutils, gperf for example) include formatted
+ documentation and try to randomly build things or not because of
+ the timestamps. This should help us get more reliable builds.
+
+ '''
+
+ now = time.time()
+ for dirname, subdirs, basenames in os.walk(root, topdown=False):
+ for basename in basenames:
+ pathname = os.path.join(dirname, basename)
+ # we need the following check to ignore broken symlinks
+ if os.path.exists(pathname):
+ os.utime(pathname, (now, now))
+ os.utime(dirname, (now, now))
+
+
+ def run_commands(self, builddir, destdir): # pragma: no cover
m = self.artifact.source.morphology
bs = morphlib.buildsystem.lookup_build_system(m['build-system'])
+ relative_builddir = self.staging_area.relative(builddir)
relative_destdir = self.staging_area.relative(destdir)
self.build_env.env['DESTDIR'] = relative_destdir
@@ -128,7 +179,7 @@ class ChunkBuilder(BuilderBase):
self.build_env.env['MAKEFLAGS'] = '-j%s' % max_jobs
else:
self.build_env.env['MAKEFLAGS'] = '-j1'
- self.runcmd(['sh', '-c', cmd], cwd=relative_destdir)
+ self.runcmd(['sh', '-c', cmd], cwd=relative_builddir)
def assemble_chunk_artifacts(self, destdir): # pragma: no cover
ex = None # create_chunk doesn't actually use this