[gnome-ostree/wip/gjs-round2] Work on porting build to gjs



commit 42d33012e0aefaee1b78b733b439681a11ed4e0b
Author: Colin Walters <walters verbum org>
Date:   Sun Dec 9 18:59:58 2012 -0500

    Work on porting build to gjs

 src/ostbuild/js/build.js                 |  721 ++++++++++++++++++++++++++++++
 src/ostbuild/js/procutil.js              |   25 +-
 src/ostbuild/js/resolve.js               |    2 -
 src/ostbuild/pyostbuild/builtin_build.py |   60 +--
 src/ostbuild/pyostbuild/main.py          |    2 +-
 5 files changed, 766 insertions(+), 44 deletions(-)
---
diff --git a/src/ostbuild/js/build.js b/src/ostbuild/js/build.js
new file mode 100644
index 0000000..d6eb33b
--- /dev/null
+++ b/src/ostbuild/js/build.js
@@ -0,0 +1,721 @@
+// Copyright (C) 2011 Colin Walters <walters verbum org>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+// Boston, MA 02111-1307, USA.
+
+const GLib = imports.gi.GLib;
+const Gio = imports.gi.Gio;
+const Lang = imports.lang;
+const Format = imports.format;
+
+const GSystem = imports.gi.GSystem;
+
+const Task = imports.task;
+const JsonDB = imports.jsondb;
+const ProcUtil = imports.procutil;
+const JsonUtil = imports.jsonutil;
+const Snapshot = imports.snapshot;
+const Config = imports.config;
+const BuildUtil = imports.buildutil;
+const Vcs = imports.vcs;
+const ArgParse = imports.argparse;
+
+const OPT_COMMON_CFLAGS = {'i686': '-O2 -g -m32 -march=i686 -mtune=atom -fasynchronous-unwind-tables',
+                           'x86_64': '-O2 -g -m64 -mtune=generic'}
+
+var loop = GLib.MainLoop.new(null, true);
+
+const Build = new Lang.Class({
+    Name: "Build",
+
+    def _resolve_refs(self, refs):
+        if len(refs) == 0:
+            return []
+        args = ['ostree', '--repo=' + self.repo, 'rev-parse']
+        args.extend(refs)
+        output = run_sync_get_output(args)
+        return output.split('\n')
+
+    def _clean_stale_buildroots(self, buildroot_cachedir, keep_root):
+        roots = os.listdir(buildroot_cachedir)
+        root_mtimes = {}
+        for root in roots:
+            path = os.path.join(buildroot_cachedir, root)
+            root_mtimes[root] = os.lstat(path).st_mtime
+        roots.sort(lambda a,b: cmp(root_mtimes[a], root_mtimes[b]))
+        remaining = roots[:-2]
+        for root in remaining:
+            path = os.path.join(buildroot_cachedir, root)
+            if path == keep_root:
+                continue
+            log("Removing old cached buildroot %s" % (root, ))
+            shutil.rmtree(path)
+
+    def _compose_buildroot(self, workdir, component_name, architecture):
+        starttime = time.time()
+
+        buildname = '%s/%s/%s' % (this._snapshot['prefix'], component_name, architecture)
+        buildroot_cachedir = os.path.join(self.workdir, 'roots', buildname)
+        fileutil.ensure_dir(buildroot_cachedir)
+
+        components = this._snapshot['components']
+        component = None
+        build_dependencies = []
+        for component in components:
+            if component['name'] == component_name:
+                break
+            build_dependencies.append(component)
+
+        ref_to_rev = {}
+
+        prefix = this._snapshot['prefix']
+
+        arch_buildroot_name = 'bases/%s/%s-%s-devel' % (this._snapshot['base']['name'],
+                                                        prefix,
+                                                        architecture)
+
+        log("Computing buildroot contents")
+
+        arch_buildroot_rev = run_sync_get_output(['ostree', '--repo=' + self.repo, 'rev-parse',
+                                                  arch_buildroot_name]).strip()
+
+        ref_to_rev[arch_buildroot_name] = arch_buildroot_rev
+        checkout_trees = [(arch_buildroot_name, '/')]
+        refs_to_resolve = []
+        for dependency in build_dependencies:
+            buildname = 'components/%s/%s/%s' % (prefix, dependency['name'], architecture)
+            refs_to_resolve.append(buildname)
+            checkout_trees.append((buildname, '/runtime'))
+            checkout_trees.append((buildname, '/devel'))
+
+        resolved_refs = self._resolve_refs(refs_to_resolve)
+        for ref,rev in zip(refs_to_resolve, resolved_refs):
+            ref_to_rev[ref] = rev
+
+        sha = hashlib.sha256()
+
+        uid = os.getuid()
+        gid = os.getgid()
+        etc_passwd = 'root:x:0:0:root:/root:/bin/bash\nbuilduser:x:%u:%u:builduser:/:/bin/bash\n' % (uid, gid)
+        etc_group = 'root:x:0:root\nbuilduser:x:%u:builduser\n' % (gid, )
+
+        (fd, tmppath) = tempfile.mkstemp(suffix='.txt', prefix='ostbuild-buildroot-')
+        f = os.fdopen(fd, 'w')
+        for (branch, subpath) in checkout_trees:
+            f.write(ref_to_rev[branch])
+            f.write('\0')
+            f.write(subpath)
+            f.write('\0')
+        f.close()
+
+        f = open(tmppath)
+        buf = f.read(8192)
+        while buf != '':
+            sha.update(buf)
+            buf = f.read(8192)
+        f.close()
+
+        sha.update(etc_passwd)
+        sha.update(etc_group)
+
+        new_root_cacheid = sha.hexdigest()
+
+        cached_root = os.path.join(buildroot_cachedir, new_root_cacheid)
+        if os.path.isdir(cached_root):
+            log("Reusing cached buildroot: %s" % (cached_root, ))
+            self._clean_stale_buildroots(buildroot_cachedir, os.path.basename(cached_root))
+            os.unlink(tmppath)
+            return cached_root
+
+        if len(checkout_trees) > 0:
+            log("composing buildroot from %d parents (last: %r)" % (len(checkout_trees),
+                                                                    checkout_trees[-1][0]))
+
+        cached_root_tmp = cached_root + '.tmp'
+        if os.path.isdir(cached_root_tmp):
+            shutil.rmtree(cached_root_tmp)
+        run_sync(['ostree', '--repo=' + self.repo,
+                  'checkout', '--user-mode', '--union',
+                  '--from-file=' + tmppath, cached_root_tmp])
+
+        os.unlink(tmppath)
+
+        builddir_tmp = os.path.join(cached_root_tmp, 'ostbuild')
+        fileutil.ensure_dir(os.path.join(builddir_tmp, 'source', component_name))
+        fileutil.ensure_dir(os.path.join(builddir_tmp, 'results'))
+        f = open(os.path.join(cached_root_tmp, 'etc', 'passwd'), 'w')
+        f.write(etc_passwd)
+        f.close()
+        f = open(os.path.join(cached_root_tmp, 'etc', 'group'), 'w')
+        f.write(etc_group)
+        f.close()
+        os.rename(cached_root_tmp, cached_root)
+
+        self._clean_stale_buildroots(buildroot_cachedir, os.path.basename(cached_root))
+
+        endtime = time.time()
+        log("Composed buildroot; %d seconds elapsed" % (int(endtime - starttime),))
+
+        return cached_root
+
+    def _analyze_build_failure(self, t, architecture, component, component_srcdir,
+                               current_vcs_version, previous_vcs_version):
+        # Dump last bit of log
+        print "LOGFILE: " + t.logfile_path
+        f = open(t.logfile_path)
+        lines = f.readlines()
+        lines = lines[-250:]
+        for line in lines:
+            print "| " + line.strip()
+        f.close()
+        if (current_vcs_version is not None and previous_vcs_version is not None):
+            git_args = ['git', 'log', '--format=short']
+            git_args.append(previous_vcs_version + '...' + current_vcs_version)
+            subproc_env = dict(os.environ)
+            subproc_env['GIT_PAGER'] = 'cat'
+            run_sync(git_args, cwd=component_srcdir, stdin=open('/dev/null'),
+                     stdout=sys.stdout, env=subproc_env, log_success=False)
+        else:
+            log("No previous build; skipping source diff")
+
+    def _needs_rebuild(self, previous_metadata, new_metadata):
+        build_keys = ['config-opts', 'src', 'revision']
+        for k in build_keys:
+            if (k not in new_metadata):
+                return 'key %r removed from new_metadata' % (k, )
+            if k in previous_metadata:
+                oldval = previous_metadata[k]
+                newval = new_metadata[k]
+                if oldval != newval:
+                    return 'key %r differs (%r -> %r)' % (k, oldval, newval)
+
+            if (k not in new_metadata) or (previous_metadata[k] != new_metadata[k]):
+                return 'key %r differs' % (k, )
+            
+        if 'patches' in previous_metadata:
+            if 'patches' not in new_metadata:
+                return 'patches differ'
+            old_patches = previous_metadata['patches']
+            new_patches = new_metadata['patches']
+            old_files = old_patches['files']
+            new_files = new_patches['files']
+            if len(old_files) != len(new_files):
+                return 'patches differ'
+            old_sha256sums = old_patches.get('files_sha256sums')
+            new_sha256sums = new_patches.get('files_sha256sums')
+            if ((old_sha256sums is None or new_sha256sums is None) or
+                len(old_sha256sums) != len(new_sha256sums) or
+                old_sha256sums != new_sha256sums):
+                return 'patch sha256sums differ'
+        return None
+
+    def _compute_sha256sums_for_patches(self, patchdir, component):
+        patches = buildutil.get_patch_paths_for_component(patchdir, component)
+        result = []
+
+        for patch in patches:
+            csum = hashlib.sha256()
+            f = open(patch)
+            patchdata = f.read()
+            csum.update(patchdata)
+            f.close()
+            result.append(csum.hexdigest())
+        return result
+
+    def _save_component_build(self, buildname, expanded_component):
+        build_ref = 'components/%s' % (buildname, )
+        cachedata = dict(expanded_component)
+        cachedata['ostree'] = run_sync_get_output(['ostree', '--repo=' + self.repo,
+                                                   'rev-parse', build_ref])
+        self._component_build_cache[buildname] = cachedata
+        fileutil.write_json_file_atomic(self._component_build_cache_path, self._component_build_cache)
+        return cachedata['ostree']
+
+    function _buildOneComponent(component, architecture, cancellable) {
+        let basename = component['name'];
+
+        let buildname = Format.vprintf('%s/%s/%s', [this._snapshot['prefix'], basename, architecture]);
+        let buildRef = 'components/' + buildname;
+
+        let currentVcsVersion = component['revision'];
+        let expandedComponent = Snapshot.expandComponent(this._snapshot, component);
+        let previousMetadata = this._componentBuildCache[buildname];
+        let wasInBuildCache = (previousMetadata != null);
+	let previousBuildVersion;
+        if wasInBuildCache:
+            previousBuildVersion = previousMetadata['ostree'];
+        else:
+            previousBuildVersion = ProcUtil.runSyncGetOutputUTF8StrippedOrNull(['ostree', '--repo=' + self.repo.get_path(),
+										'rev-parse', buildRef]);
+	let previousVcsVersion;
+        if (previousMetadata != null) {
+            previousVcsVersion = previousMetadata['revision'];
+        } else if (previousBuildVersion != null) {
+            let jsonstr = ProcUtil.runSyncGetOutputUTF8(['ostree', '--repo=' + self.repo.get_path(),
+							 'cat', previousBuildVersion,
+							 '/_ostbuild-meta.json']);
+	    previousMetadata = JSON.parse(jsonstr);
+            previousVcsVersion = previousMetadata['revision'];
+        } else {
+            log("No previous build for " + buildname);
+            previousVcsVersion = null;
+	}
+
+        if expandedComponent['patches'] {
+            patchesRevision = expandedComponent['patches']['revision'];
+	    let patchdir;
+            if (this.args.patches_path) {
+                patchdir = Gio.File.new_for_path(this.args.patches_path);
+            else if (this._cachedPatchdirRevision == patchesRevision)
+                patchdir = this._patchdir;
+            else {
+                patchdir = Vcs.checkoutPatches(this._mirrordir,
+                                               this._patchdir,
+                                               expandedComponent,
+                                               this.args.patches_path);
+                self.cached_patchdir_revision = patches_revision
+	    }
+            if ((previous_metadata is not None) and
+                'patches' in previous_metadata and
+                'revision' in previous_metadata['patches'] and
+                previous_metadata['patches']['revision'] == patches_revision):
+                # Copy over the sha256sums
+                expanded_component['patches'] = previous_metadata['patches']
+            else:
+                patches_sha256sums = self._compute_sha256sums_for_patches(patchdir, expanded_component)
+                expanded_component['patches']['files_sha256sums'] = patches_sha256sums
+        else:
+            patchdir = None
+
+        force_rebuild = (self.buildopts.force_rebuild or
+                         basename in self.force_build_components or
+                         expanded_component['src'].startswith('local:'))
+
+        if previous_metadata is not None:
+            rebuild_reason = self._needs_rebuild(previous_metadata, expanded_component)
+            if rebuild_reason is None:
+                if not force_rebuild:
+                    log("Reusing cached build of %s at %s" % (buildname, previous_vcs_version)) 
+                    if not was_in_build_cache:
+                        return self._save_component_build(buildname, expanded_component)
+                    return previous_build_version
+                else:
+                    log("Build forced regardless") 
+            else:
+                log("Need rebuild of %s: %s" % (buildname, rebuild_reason, ) )
+
+        taskdir = task.TaskDir(os.path.join(self.workdir, 'tasks'))
+        build_taskset = taskdir.get(buildname)
+        t = build_taskset.start()
+        workdir = t.path
+
+        temp_metadata_path = os.path.join(workdir, '_ostbuild-meta.json')
+        fileutil.write_json_file_atomic(temp_metadata_path, expanded_component)
+
+        checkoutdir = os.path.join(self.workdir, 'checkouts')
+        component_src = os.path.join(checkoutdir, buildname)
+        fileutil.ensure_parent_dir(component_src)
+        child_args = ['ostbuild', 'checkout', '--snapshot=' + this._snapshot_path,
+                      '--checkoutdir=' + component_src,
+                      '--metadata-path=' + temp_metadata_path]
+        if not self.buildopts.no_clean:
+            child_args.append('--clean')
+        child_args.extend(['--overwrite', basename])
+        if self.args.patches_path:
+            child_args.append('--patches-path=' + self.args.patches_path)
+        elif patchdir is not None:
+            child_args.append('--patches-path=' + patchdir)
+        run_sync(child_args)
+
+        os.unlink(temp_metadata_path)
+
+        component_resultdir = os.path.join(workdir, 'results')
+        fileutil.ensure_dir(component_resultdir)
+
+        self._write_status('Building ' +  build_ref)
+
+        rootdir = self._compose_buildroot(workdir, basename, architecture)
+
+        tmpdir=os.path.join(workdir, 'tmp')
+        fileutil.ensure_dir(tmpdir)
+
+        src_compile_one_path = os.path.join(LIBDIR, 'ostbuild', 'ostree-build-compile-one')
+        dest_compile_one_path = os.path.join(rootdir, 'ostree-build-compile-one')
+        shutil.copy(src_compile_one_path, dest_compile_one_path)
+        os.chmod(dest_compile_one_path, 0755)
+        
+        chroot_sourcedir = os.path.join('/ostbuild', 'source', basename)
+
+        current_machine = os.uname()[4]
+        if current_machine != architecture:
+            child_args = ['setarch', architecture]
+        else:
+            child_args = []
+        child_args.extend(buildutil.get_base_user_chroot_args())
+        child_args.extend([
+                '--mount-readonly', '/',
+                '--mount-proc', '/proc', 
+                '--mount-bind', '/dev', '/dev',
+                '--mount-bind', tmpdir, '/tmp',
+                '--mount-bind', component_src, chroot_sourcedir,
+                '--mount-bind', component_resultdir, '/ostbuild/results',
+                '--chdir', chroot_sourcedir])
+        child_args.extend([rootdir, '/ostree-build-compile-one',
+                           '--ostbuild-resultdir=/ostbuild/results',
+                           '--ostbuild-meta=_ostbuild-meta.json'])
+        env_copy = dict(buildutil.BUILD_ENV)
+        env_copy['PWD'] = chroot_sourcedir
+        env_copy['CFLAGS'] = OPT_COMMON_CFLAGS[architecture]
+        env_copy['CXXFLAGS'] = OPT_COMMON_CFLAGS[architecture]
+
+        success = run_sync(child_args, stdout=t.logfile_stream,
+                           stderr=t.logfile_stream, env=env_copy,
+                           fatal_on_error=False)
+        if not success:
+            build_taskset.finish(False)
+            self._analyze_build_failure(t, architecture, component, component_src,
+                                        current_vcs_version, previous_vcs_version)
+            self._write_status('Failed building ' + build_ref)
+            fatal("Exiting due to build failure in component:%s arch:%s" % (component, architecture))
+
+        recorded_meta_path = os.path.join(component_resultdir, '_ostbuild-meta.json')
+        fileutil.write_json_file_atomic(recorded_meta_path, expanded_component)
+
+        args = ['ostree', '--repo=' + self.repo,
+                'commit', '-b', build_ref, '-s', 'Build',
+                '--owner-uid=0', '--owner-gid=0', '--no-xattrs', 
+                '--skip-if-unchanged']
+
+        setuid_files = expanded_component.get('setuid', [])
+        statoverride_path = None
+        if len(setuid_files) > 0:
+            (fd, statoverride_path) = tempfile.mkstemp(suffix='.txt', prefix='ostbuild-statoverride-')
+            f = os.fdopen(fd, 'w')
+            for path in setuid_files:
+                f.write('+2048 ' + path)
+                f.write('\n')
+            f.close()
+            args.append('--statoverride=' + statoverride_path)
+
+        run_sync(args, stdout=t.logfile_stream,
+                 stderr=t.logfile_stream,
+                 cwd=component_resultdir)
+        if statoverride_path is not None:
+            os.unlink(statoverride_path)
+
+        if not self.args.no_clean_results:
+            if os.path.islink(component_src):
+                os.unlink(component_src)
+            else:
+                shutil.rmtree(component_src)
+            shutil.rmtree(component_resultdir)
+
+        shutil.rmtree(tmpdir)
+
+        ostree_revision = self._save_component_build(buildname, expanded_component)
+
+        build_taskset.finish(True)
+
+        return ostree_revision
+    },
+
+    function _composeOneTarget(self, target, componentBuildRevs, cancellable) {
+        let base = target['base'];
+        let baseName = 'bases/' + base['name'];
+        let runtimeName = 'bases/' + base['runtime'];
+        let develName = 'bases/' + base['devel'];
+
+	let rootdir = self.workdir.get_child('roots');
+        let composeRootdir = rootdir.get_child(target['name']);
+	GSystem.shutil_rm_rf(composeRootdir);
+        GSystem.file_ensure_directory(composeRootdir, cancellable);
+
+        let relatedRefs = {};
+        let baseRevision = ProcUtil.runSyncGetOutputUTF8Stripped(['ostree', '--repo=' + this.repo.get_path(),
+								  'rev-parse', baseName], cancellable);
+
+        let runtimeRevision = ProcUtil.runSyncGetOutputUTF8Stripped(['ostree', '--repo=' + self.repo.get_path(),
+								     'rev-parse', runtimeName], cancellable);
+        relatedRefs[runtimeName] = runtimeRevision;
+        develRevision = ProcUtil.runSyncGetOutputUTF8Stripped(['ostree', '--repo=' + self.repo.get_path(),
+							       'rev-parse', develName], cancellable);
+        relatedRefs[develName] = develRevision;
+
+	for (let name in componentBuildRevs) {
+	    let rev = componentBuildRevs[name];
+            let buildRef = 'components/' + this._snapshot['prefix'] + '/' + name;
+            relatedRefs[buildRef] = rev;
+	}
+
+	let [relatedTmpPath, stream] = Gio.File.new_tmp("ostbuild-compose-XXXXXX.txt");
+	let dataOut = Gio.DataOutputStream.new(stream);
+	for (let name in relatedRefs) {
+	    let rev = relatedRefs[name];
+	    dataOut.put_string(name, cancellable);
+	    dataOut.put_string(' ', cancellable);
+	    dataOut.put_string(rev, cancellable);
+	    dataOut.put_string('\n', cancellable);
+	}
+	dataOut.close(cancellable);
+
+        let composeContents = [[baseRevision, '/']];
+        for (let i = 0; i < target['contents'].length; i++) {
+	    let treeContent = target['contents'][i];
+            let name = treeContent['name'];
+            let rev = componentBuildRevs[name];
+            let subtrees = treeContent['trees'];
+            for (let j = 0; j < subtrees.length; j++) {
+		let subpath = subtrees[j];
+                compose_contents.append([rev, subpath]);
+	    }
+	}
+
+	let [contentsTmpPath, stream] = Gio.File.new_tmp("ostbuild-compose-XXXXXX.txt");
+	let dataOut = Gio.DataOutputStream.new(stream);
+	for (let i = 0; i < composeContents.length; i++) {
+	    let [branch, subpath] = composeContents[i];
+            dataOut.put_string(branch, cancellable);
+	    dataOut.put_byte(0, cancellable);
+            dataOut.put_string(subpath, cancellable);
+	    dataOut.put_byte(0, cancellable);
+	}
+        dataOut.close(cancellable);
+
+        ProcUtil.runSync(['ostree', '--repo=' + self.repo.get_path(),
+			  'checkout', '--user-mode', '--no-triggers', '--union', 
+			  '--from-file=' + contentsTmpPath.get_path(), composeRootdir.get_path()],
+			cancellable);
+        GSystem.file_unlink(contentsTmppath, cancellable);
+
+        contentsPath = composeRootdir.get_child('contents.json');
+        JsonUtil.writeJsonFileAtomic(contentsPath, this._snapshot);
+
+        let treename = 'trees/' + target['name'];
+        
+        ProcUtil.runSync(['ostree', '--repo=' + self.repo.get_path(),
+			 'commit', '-b', treename, '-s', 'Compose',
+			 '--owner-uid=0', '--owner-gid=0', '--no-xattrs', 
+			 '--related-objects-file=' + relatedTmpPath.get_path(),
+			 '--skip-if-unchanged'
+			 ], cancellable, {cwd: composeRootdir.get_path()});
+        GSystem.file_unlink(relatedTmpPath, cancellable);
+        GSystem.shutil_rm_rf(composeRootdir, cancellable);
+    },
+
+    /* Build the Yocto base system. */
+    _buildBase: function(architecture, cancellable) {
+        let basemeta = this._snapshot['base'];
+        let checkoutdir = this.workdir.get_child('checkouts').get_child(basemeta['name']);
+	GSystem.file_ensure_directory(checkoutdir, cancellable);
+
+	let ftype = checkoutdir.query_file_type(Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, cancellable);
+        if (ftype == Gio.FileType.SYMBOLIC_LINK)
+	    GSystem.file_unlink(checkoutdir, cancellable);
+
+        let [keytype, uri] = Vcs.parseSrcKey(basemeta['src']);
+        if (keytype == 'local') {
+	    GSystem.shutil_rm_rf(checkoutdir, cancellable);
+	    checkoutdir.make_symbolic_link(uri, cancellable);
+        } else {
+            Vcs.getVcsCheckout(this._mirrordir, keytype, uri, checkoutdir,
+                               basemeta['revision'], cancellable,
+                               {overwrite:false});
+	}
+
+        let builddirName = Format.vprintf('build-%s-%s', [basemeta['name'], architecture]);
+        let builddir = this.workdir.get_child(builddirName);
+
+        // Just keep reusing the old working directory downloads and sstate
+        let oldBuilddir = this.workdir.get_child('build-' + basemeta['name']);
+        let sstateDir = oldBuilddir.get_child('sstate-cache');
+        let downloads = oldBuilddir.get_child('downloads');
+
+        let cmd = ['linux-user-chroot', '--unshare-pid', '/',
+		   LIBDIR + '/ostbuild/ostree-build-yocto',
+		   checkoutdir.get_path(), builddir.get_path(), architecture,
+		   this.repo.get_path()];
+        // We specifically want to kill off any environment variables jhbuild
+        // may have set.
+        env = {};
+	Lang.copyProperties(BuildUtil.BUILD_ENV, env);
+        env['DL_DIR'] = downloads.get_path();
+        env['SSTATE_DIR'] = sstate_dir.get_path();
+        ProcUtil.runSync(cmd, cancellable, {env:env});
+    },
+        
+    execute: function(argv) {
+	let cancellable = null;
+
+        let parser = new ArgParse.ArgumentParser("Build multiple components and generate trees");
+        parser.addArgument('--prefix');
+        parser.addArgument('--src-snapshot');
+        parser.addArgument('--patches-path');
+        parser.addArgument('components', {nargs:'*'});
+        
+        let args = parser.parseArgs(argv);
+
+	this.config = Config.get();
+	this.workdir = Gio.File.new_for_path(this.config.getGlobal('workdir'));
+	this.mirrordir = Gio.File.new_for_path(this.config.getGlobal('mirrordir'));
+	this.prefix = args.prefix || this.config.getPrefix();
+	this._snapshotDir = this.workdir.get_child('snapshots');
+
+	this._srcDb = new JsonDB.JsonDB(this._snapshotDir, this.prefix + '-src-snapshot');
+	this._snapshot = Snapshot.load(this._srcDb, this.prefix, args.snapshot, cancellable);
+        
+	
+        this.forceBuildComponents = {};
+        this.cachedPatchdirRevision = null;
+
+	this.repo = this.workdir.get_child('repo');
+
+        GSystem.file_ensure_directory(this.repo, cancellable);
+        if (!this.repo.get_child('objects').query_exists(cancellable)) {
+            ProcUtil.runSync(['ostree', '--repo=' + self.repo.get_path(), 'init', '--archive'],
+			     cancellable);
+	}
+
+        let components = this._snapshot['components'];
+
+        let prefix = this._snapshot['prefix'];
+        let basePrefix = this._snapshot['base']['name'] + '/' + prefix;
+        let architectures = this._snapshot['architectures'];
+
+        for (let i = 0; i < architectures.length; i++) {
+            this._buildBase(architectures[i], cancellable);
+	}
+
+        let componentToArches = {};
+
+        let runtimeComponents = [];
+        let develComponents = [];
+
+        for (let i = 0; i < components.length; i++) {
+	    let component = components[i];
+            let name = component['name']
+
+            let isRuntime = (component['component'] || 'runtime') == 'runtime';
+
+            if (isRuntime) {
+                runtimeComponents.push(component);
+	    }
+            develComponents.push(component);
+
+	    let isNoarch = component['noarch'] || false;
+	    let componentArches;
+            if (isNoarch) {
+                // Just use the first specified architecture
+                componentArches = [architectures[0]];
+            } else {
+                componentArches = component['architectures'] || architectures;
+	    }
+            componentToArches[name] = componentArches;
+	}
+
+        for (let i = 0; i < args.components.length; i++) {
+	    let name = args.components[i];
+            let component = Snapshot.getComponent(this._snapshot, name);
+            this._forceBuildComponents[name] = true;
+	}
+
+        let componentsToBuild = [];
+        let componentSkippedCount = 0;
+        let componentBuildRevs = {};
+
+        for (let i = 0; i < components.length; i++) {
+	    let component = components[i];
+            for (let j = 0; j < architectures.length; j++) {
+                componentsToBuild.push([component, architectures[j]]);
+	    }
+	}
+
+        this._componentBuildCachePath = this.workdir.get_child('component-builds.json');
+        if (this._componentBuildCachePath.query_exists(cancellable)) {
+            this._componentBuildCache = JsonUtil.loadJson(self._componentBuildCachePath));
+        } else {
+            this._componentBuildCache = {};
+	}
+
+        for (let i = 0; i < componentsToBuild.length; i++) {
+	    let [component, architecture] = componentsToBuild[i];
+            let archname = component['name'] + '/' + architecture;
+            let buildRev = this._buildOneComponent(component, architecture, cancellable);
+            this._componentBuildRevs[archname] = buildRev;
+	}
+
+        let targetsList = [];
+        for (let targetComponentType in {'runtime', 'devel'}) {
+            for (let i = 0; i < architectures.length; i++) {
+		let architecture = architectures[i];
+                let target = {};
+                targetsList.push(target);
+                target['name'] = prefix + '-' + architecture + '-' + target_component_type;
+
+                let runtimeRef = base_prefix + '-' + architecture + '-runtime';
+                let buildrootRef = base_prefix + '-' + architecture + '-devel';
+		let baseRef;
+                if (targetComponentType == 'runtime') {
+                    baseRef = runtimeRef;
+                } else {
+                    baseFef = buildrootRef;
+		}
+                target['base'] = {'name': baseRef,
+                                  'runtime': runtimeRef,
+                                  'devel': buildrootRef};
+
+		let targetComponents;
+                if (targetComponentType == 'runtime') {
+                    targetComponents = runtimeComponents;
+                } else {
+                    targetComponents = develComponents;
+		}
+                    
+                let contents = [];
+                for (let i = 0; i < target_components.length; i++) {
+		    let component = target_components[i];
+                    if component['bootstrap'] {
+                        continue;
+		    }
+                    let buildsForComponent = componentToArches[component['name']];
+                    if (buildsForComponent.indexOf(architecture) == -1) {
+			continue;
+		    }
+                    let binaryName = component['name'] + '/' + architecture;
+                    let componentRef = {'name': binaryName};
+                    if (targetComponentType == 'runtime') {
+                        componentRef['trees'] = ['/runtime'];
+                    } else {
+                        componentRef['trees'] = ['/runtime', '/devel', '/doc']
+		    }
+                    contents.push(componentRef);
+		}
+                target['contents'] = contents;
+	    }
+	}
+
+        for (let i = 0; i < targets_list.length; i++) {
+	    let target = targets_list[i];
+            log(Format.vprintf("Composing %r from %d components", [target['name'], target['contents'].length]));
+            this._composeOneTarget(target, componentBuildRevs, cancellable);
+	}
+    }
+});
+
+
+var app = new Build();
+GLib.idle_add(GLib.PRIORITY_DEFAULT,
+	      function() { try { app.execute(ARGV); } finally { loop.quit(); }; return false; });
+loop.run();
+
diff --git a/src/ostbuild/js/procutil.js b/src/ostbuild/js/procutil.js
index a42b654..ed76aee 100644
--- a/src/ostbuild/js/procutil.js
+++ b/src/ostbuild/js/procutil.js
@@ -5,11 +5,19 @@ const GSystem = imports.gi.GSystem;
 const Params = imports.params;
 
 function _setContextFromParams(context, params) {
-    params = Params.parse(params, {cwd: null});
+    params = Params.parse(params, {cwd: null,
+				   env: null,
+				   stderr: null });
     if (typeof(params.cwd) == 'string')
 	context.set_cwd(params.cwd);
     else if (params.cwd)
 	context.set_cwd(params.cwd.get_path());
+
+    if (params.env)
+	context.set_environment(params.env);
+
+    if (params.stderr)
+	context.set_stderr_disposition(params.stderr);
 }
 
 function _wait_sync_check_internal(proc, cancellable) {
@@ -65,6 +73,21 @@ function runSyncGetOutputUTF8(args, cancellable, params) {
     return _runSyncGetOutputInternal(args, cancellable, params, false);
 }
 
+function runSyncGetOutputUTF8Stripped(args, cancellable, params) {
+    return _runSyncGetOutputInternal(args, cancellable, params, false).replace(/[ \n]+$/, '');
+}
+
+function runSyncGetOutputUTF8StrippedOrNull(args, cancellable, params) {
+    try {
+	params.stderr = Gio.SubprocessStreamDisposition.NULL;
+	return runSyncGetOutputUTF8Stripped(args, cancellable, params);
+    } catch (e) {
+	if (e.domain == GLib.spawn_exit_error_quark())
+	    return null;
+	throw e;
+    }
+}
+
 function asyncWaitCheckFinish(process, result) {
     let [waitSuccess, estatus] = process.wait_finish(result);
     let success = false;
diff --git a/src/ostbuild/js/resolve.js b/src/ostbuild/js/resolve.js
index 48c547e..025d1a6 100644
--- a/src/ostbuild/js/resolve.js
+++ b/src/ostbuild/js/resolve.js
@@ -49,8 +49,6 @@ const Resolve = new Lang.Class({
 					help:"Also perform a git fetch"});
         parser.addArgument('--fetch-keep-going', {action:'storeTrue',
 						  help:"Don't exit on fetch failures"});
-        parser.addArgument('--stamp-file',
-                           {help: "If manifest changes, create this file"});
         parser.addArgument('components', {nargs:'*',
 					  help:"List of component names to git fetch"});
 
diff --git a/src/ostbuild/pyostbuild/builtin_build.py b/src/ostbuild/pyostbuild/builtin_build.py
index e86f40c..ee7bc28 100755
--- a/src/ostbuild/pyostbuild/builtin_build.py
+++ b/src/ostbuild/pyostbuild/builtin_build.py
@@ -1,43 +1,23 @@
-# Copyright (C) 2011 Colin Walters <walters verbum org>
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the
-# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-# Boston, MA 02111-1307, USA.
-
-import os,sys,subprocess,tempfile,re,shutil
-import argparse
-import time
-import urlparse
-import hashlib
-import json
-from StringIO import StringIO
-
-from . import builtins
-from .ostbuildlog import log, fatal
-from .subprocess_helpers import run_sync, run_sync_get_output
-from .subprocess_helpers import run_sync_monitor_log_file
-from . import ostbuildrc
-from . import buildutil
-from . import task
-from . import fileutil
-from . import kvfile
-from . import snapshot
-from . import odict
-from . import vcs
-
-OPT_COMMON_CFLAGS = {'i686': '-O2 -g -m32 -march=i686 -mtune=atom -fasynchronous-unwind-tables',
-                     'x86_64': '-O2 -g -m64 -mtune=generic'}
+// Copyright (C) 2011 Colin Walters <walters verbum org>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+// Boston, MA 02111-1307, USA.
+
+
+const OPT_COMMON_CFLAGS = {'i686': '-O2 -g -m32 -march=i686 -mtune=atom -fasynchronous-unwind-tables',
+                           'x86_64': '-O2 -g -m64 -mtune=generic'}
 
 class BuildOptions(object):
     pass
diff --git a/src/ostbuild/pyostbuild/main.py b/src/ostbuild/pyostbuild/main.py
index 708e3dd..df97a03 100755
--- a/src/ostbuild/pyostbuild/main.py
+++ b/src/ostbuild/pyostbuild/main.py
@@ -29,7 +29,7 @@ JS_BUILTINS = {'autobuilder': "Run resolve and build",
                'prefix': "Display or modify \"prefix\" (build target)",
                'git-mirror': "Update internal git mirror for one or more components",
                'resolve': "Expand git revisions in source to exact targets",
-               'qemu-pull-deploy': "Copy from local repository into qemu disk and deploy"};
+               'build': "Build multiple components and generate trees"};
 
 def usage(ecode):
     print "Builtins:"



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]