[ostree/wip/ostbuild-v2: 1/2] ostbuild: Major rework



commit c8bd24d4e69a45e87921dfdf00a43f64fd3594d8
Author: Colin Walters <walters verbum org>
Date:   Fri Apr 6 15:12:57 2012 -0400

    ostbuild: Major rework

 Makefile-ostbuild.am                               |    2 +
 gnomeos/3.4/gnomeos-3.4-src.json                   |    3 +-
 gnomeos/README-build.md                            |   42 +++++++++-
 src/ostbuild/pyostbuild/builtin_build.py           |   44 +++++++---
 src/ostbuild/pyostbuild/builtin_checkout.py        |   16 +++-
 .../pyostbuild/builtin_chroot_compile_one.py       |   45 +++++++---
 src/ostbuild/pyostbuild/builtin_compile_one.py     |   25 +++---
 src/ostbuild/pyostbuild/builtin_pull_components.py |   70 +++++++++++++++
 src/ostbuild/pyostbuild/builtin_query_content.py   |   65 ++++++++++----
 .../pyostbuild/builtin_replace_component.py        |   67 ++++++++++++++
 src/ostbuild/pyostbuild/builtin_resolve.py         |   12 +++-
 src/ostbuild/pyostbuild/builtins.py                |   91 ++++++++++++++++++--
 src/ostbuild/pyostbuild/main.py                    |    2 +
 src/ostbuild/pyostbuild/subprocess_helpers.py      |    6 +-
 14 files changed, 413 insertions(+), 77 deletions(-)
---
diff --git a/Makefile-ostbuild.am b/Makefile-ostbuild.am
index 08eb8c0..2109e61 100644
--- a/Makefile-ostbuild.am
+++ b/Makefile-ostbuild.am
@@ -28,7 +28,9 @@ pyostbuild_PYTHON =					\
 	src/ostbuild/pyostbuild/builtin_chroot_run_triggers.py	\
 	src/ostbuild/pyostbuild/builtin_compile_one.py	\
 	src/ostbuild/pyostbuild/builtin_query_content.py	\
+	src/ostbuild/pyostbuild/builtin_pull_components.py	\
 	src/ostbuild/pyostbuild/builtin_resolve.py	\
+	src/ostbuild/pyostbuild/builtin_replace_component.py	\
 	src/ostbuild/pyostbuild/builtin_status.py	\
 	src/ostbuild/pyostbuild/builtins.py		\
 	src/ostbuild/pyostbuild/filemonitor.py		\
diff --git a/gnomeos/3.4/gnomeos-3.4-src.json b/gnomeos/3.4/gnomeos-3.4-src.json
index fcf7e9b..6864e0c 100644
--- a/gnomeos/3.4/gnomeos-3.4-src.json
+++ b/gnomeos/3.4/gnomeos-3.4-src.json
@@ -566,7 +566,8 @@
 		{"src": "gnome:libsoup",
 		 "config-args": ["--disable-tls-check"]},
 
-		{"src": "gnome:ostree",
+		{"src": "git:/src/ostree",
+		 "branch": "wip/ostbuild-v2",
 		 "config-opts": ["--disable-documentation",
 		                 "--with-soup-gnome"]},
 
diff --git a/gnomeos/README-build.md b/gnomeos/README-build.md
index b432276..0414576 100644
--- a/gnomeos/README-build.md
+++ b/gnomeos/README-build.md
@@ -16,6 +16,9 @@ base "runtime", and one "devel" with all of the development tools like
 gcc.  We then import that into an OSTree branch
 e.g. "bases/yocto/gnomeos-3.4-i686-devel".
 
+At present, it's still (mostly) possible to put this data on an ext4
+filesystem and boot into it.
+
 We also have a Yocto recipe "ostree-native" which generates (as you
 might guess) a native binary of ostree.  That binary is used to import
 into an "archive mode" OSTree repository.  You can see it in
@@ -26,26 +29,59 @@ can use "ostbuild" which uses "linux-user-chroot" to chroot inside,
 run a build on a source tree, and outputs binaries, which we then add
 to the build tree for the next module, and so on.
 
+The final result of all of this is that the OSTree repository gains
+new commits (which can be downloaded by clients), while still
+retaining old build history.
+
+Yocto details
+-------------
+
+I have a branch of Yocto here:
+
+https://github.com/cgwalters/poky
+
+It has a collection of patches on top of the "Edison" release of
+Yocto, some of which are hacky, others upstreamable.  The most
+important part though are the modifications to commit the generated
+root filesystem into OSTree.
+
 ostbuild details
 ----------------
 
 The simple goal of ostbuild is that it only takes as input a
-"manifest" which is basically just a list of components to build.  A
-component is a pure metadata file which includes the git repository
+"manifest" which is basically just a list of components to build.  You
+can see this here:
+
+http://git.gnome.org/browse/ostree/tree/gnomeos/3.4/gnomeos-3.4-src.json
+
+A component is a pure metadata file which includes the git repository
 URL and branch name, as well as ./configure flags (--enable-foo).
 
 There is no support for building from "tarballs" - I want the ability
 to review all of the code that goes in, and to efficiently store
-source code updates.
+source code updates.  It's also just significantly easier from an
+implementation perspective, versus having to maintain a version
+control abstraction layer.
 
 The result of a build of a component is an OSTree branch like
 "artifacts/gnomeos-3.4-i686-devel/libxslt/master".  Then, a "compose"
 process merges together the individual filesystem trees into the final
 branches (e.g. gnomeos-3.4-i686-devel).
 
+Doing local builds
+------------------
+
+This is where you want to modify one (or a few) components on top of
+what comes from the ostree.gnome.org server, and test the result
+locally.  I'm working on this.
+
 Doing a full build on your system
 ---------------------------------
 
+Following this process is equivalent to what we have set up on the
+ostree.gnome.org build server.  It will generate a completely new
+repository.
+
 srcdir=/src
 builddir=/src/build
 
diff --git a/src/ostbuild/pyostbuild/builtin_build.py b/src/ostbuild/pyostbuild/builtin_build.py
index 772aaca..cac17b3 100755
--- a/src/ostbuild/pyostbuild/builtin_build.py
+++ b/src/ostbuild/pyostbuild/builtin_build.py
@@ -38,7 +38,7 @@ class BuildOptions(object):
 
 class OstbuildBuild(builtins.Builtin):
     name = "build"
-    short_description = "Rebuild all artifacts from the given manifest"
+    short_description = "Build artifacts and compose trees from snapshot"
 
     def __init__(self):
         builtins.Builtin.__init__(self)
@@ -49,7 +49,8 @@ class OstbuildBuild(builtins.Builtin):
             args = ['setarch', architecture]
         else:
             args = []
-        args.extend(['ostbuild', 'chroot-compile-one'])
+        args.extend(['ostbuild', 'chroot-compile-one',
+                     '--snapshot=' + self.snapshot_path])
         return args
 
     def _launch_debug_shell(self, architecture, buildroot, cwd=None):
@@ -96,15 +97,11 @@ class OstbuildBuild(builtins.Builtin):
 
         checkoutdir = os.path.join(self.workdir, 'src')
         component_src = os.path.join(checkoutdir, name)
-        run_sync(['ostbuild', 'checkout', '--clean', '--overwrite', name], cwd=checkoutdir)
+        run_sync(['ostbuild', 'checkout', '--snapshot=' + self.snapshot_path,
+                  '--clean', '--overwrite', name], cwd=checkoutdir)
 
         artifact_meta = dict(component)
 
-        metadata_path = os.path.join(component_src, '_ostbuild-meta.json')
-        f = open(metadata_path, 'w')
-        json.dump(artifact_meta, f, indent=4, sort_keys=True)
-        f.close()
-
         logdir = os.path.join(self.workdir, 'logs', name)
         fileutil.ensure_dir(logdir)
         log_path = os.path.join(logdir, 'compile.log')
@@ -181,18 +178,36 @@ class OstbuildBuild(builtins.Builtin):
         resolved_contents = list(target['contents'])
         for component in resolved_contents:
             component['ostree-revision'] = branch_to_rev[component['name']]
+
         metadata = {'source': 'ostbuild compose v0',
                     'base': resolved_base, 
                     'contents': resolved_contents}
+        for k,v in self.snapshot.iteritems():
+            if k in ['components', 'targets']:
+                continue
+            metadata[k] = v
 
+        compose_contents = []
         for branch in contents:
             branch_rev = branch_to_rev[branch]
             subtrees = branch_to_subtrees[branch]
-            for subtree in subtrees:
-                run_sync(['ostree', '--repo=' + self.repo,
-                          'checkout', '--user-mode',
-                          '--union', '--subpath=' + subtree,
-                          branch_rev, compose_rootdir])
+            for subpath in subtrees:
+                compose_contents.append((branch_rev, subpath))
+
+        (fd, tmppath) = tempfile.mkstemp(suffix='.txt', prefix='ostbuild-compose-')
+        f = os.fdopen(fd, 'w')
+        for (branch, subpath) in compose_contents:
+            f.write(branch)
+            f.write('\0')
+            f.write(subpath)
+            f.write('\0')
+        f.close()
+
+        run_sync(['ostree', '--repo=' + self.repo,
+                  'checkout', '--user-mode', '--no-triggers',
+                  '--union', '--from-stdin', compose_rootdir],
+                 stdin=open(tmppath))
+        os.unlink(tmppath)
 
         contents_path = os.path.join(compose_rootdir, 'contents.json')
         f = open(contents_path, 'w')
@@ -207,6 +222,7 @@ class OstbuildBuild(builtins.Builtin):
     def execute(self, argv):
         parser = argparse.ArgumentParser(description=self.short_description)
         parser.add_argument('--skip-built', action='store_true')
+        parser.add_argument('--snapshot', required=True)
         parser.add_argument('--recompose', action='store_true')
         parser.add_argument('--skip-compose', action='store_true')
         parser.add_argument('--start-at')
@@ -218,7 +234,7 @@ class OstbuildBuild(builtins.Builtin):
         self.args = args
         
         self.parse_config()
-        self.parse_snapshot()
+        self.parse_snapshot(args.snapshot)
 
         self.buildopts = BuildOptions()
         self.buildopts.shell_on_failure = args.shell_on_failure
diff --git a/src/ostbuild/pyostbuild/builtin_checkout.py b/src/ostbuild/pyostbuild/builtin_checkout.py
index 553e505..ae94163 100755
--- a/src/ostbuild/pyostbuild/builtin_checkout.py
+++ b/src/ostbuild/pyostbuild/builtin_checkout.py
@@ -40,6 +40,7 @@ class OstbuildCheckout(builtins.Builtin):
     def execute(self, argv):
         parser = argparse.ArgumentParser(description=self.short_description)
         parser.add_argument('--overwrite', action='store_true')
+        parser.add_argument('--snapshot')
         parser.add_argument('--clean', action='store_true')
         parser.add_argument('components', nargs='*')
 
@@ -47,18 +48,20 @@ class OstbuildCheckout(builtins.Builtin):
         self.args = args
         
         self.parse_config()
-        self.parse_snapshot()
 
         if len(args.components) > 0:
             checkout_components = args.components
         else:
             checkout_components = [os.path.basename(os.getcwd())]
 
+        if args.snapshot:
+            self.parse_snapshot(args.snapshot)
+        else:
+            self.parse_active_branch()
+
         for component_name in checkout_components:
             found = False
-            component = self.snapshot['components'].get(component_name)
-            if component is None:
-                fatal("Unknown component %r" % (component_name, ))
+            component = self.get_component_meta(component_name)
             (keytype, uri) = buildutil.parse_src_key(component['src'])
             checkoutdir = os.path.join(os.getcwd(), component_name)
             fileutil.ensure_parent_dir(checkoutdir)
@@ -86,6 +89,11 @@ class OstbuildCheckout(builtins.Builtin):
                 for patch in patches['files']:
                     patch_path = os.path.join(patchdir, patch)
                     run_sync(['git', 'am', '--ignore-date', '-3', patch_path], cwd=checkoutdir)
+
+            metadata_path = os.path.join(checkoutdir, '_ostbuild-meta.json')
+            f = open(metadata_path, 'w')
+            json.dump(component, f, indent=4, sort_keys=True)
+            f.close()
         
             print "Checked out: %r" % (component_src, )
         
diff --git a/src/ostbuild/pyostbuild/builtin_chroot_compile_one.py b/src/ostbuild/pyostbuild/builtin_chroot_compile_one.py
index 0c6a96c..f3d09d8 100755
--- a/src/ostbuild/pyostbuild/builtin_chroot_compile_one.py
+++ b/src/ostbuild/pyostbuild/builtin_chroot_compile_one.py
@@ -18,6 +18,7 @@
 import os,sys,re,subprocess,tempfile,shutil
 from StringIO import StringIO
 import argparse
+import time
 import json
 
 from . import builtins
@@ -32,13 +33,15 @@ class OstbuildChrootCompileOne(builtins.Builtin):
     short_description = "Build artifacts from the current source directory in a chroot"
 
     def _compose_buildroot(self, component_name, dirpath):
+        starttime = time.time()
+
         components = self.snapshot['components']
         dependencies = buildutil.build_depends(component_name, components)
         component = components.get(component_name)
 
-        base_devel_name = 'bases/%s-%s-%s' % (self.snapshot['base-prefix'],
-                                              component['architecture'],
-                                              'devel')
+        buildroots = self.snapshot['architecture-buildroots']
+        base_devel_name = 'bases/' + buildroots[component['architecture']]
+
         checkout_trees = [(base_devel_name, '/')]
         for dependency_name in dependencies:
             buildname = 'components/%s' % (dependency_name, )
@@ -48,22 +51,41 @@ class OstbuildChrootCompileOne(builtins.Builtin):
         link_cache_dir = os.path.join(self.workdir, 'link-cache')
         fileutil.ensure_dir(link_cache_dir)
 
-        for (branch, rootpath) in checkout_trees:
-            run_sync(['ostree', '--repo=' + self.repo,
-                      'checkout', '--user-mode', '--link-cache=' + link_cache_dir,
-                      '--union', '--subpath=' + rootpath,
-                      branch, dirpath])
+        (fd, tmppath) = tempfile.mkstemp(suffix='.txt', prefix='ostbuild-buildroot-')
+        f = os.fdopen(fd, 'w')
+        for (branch, subpath) in checkout_trees:
+            f.write(branch)
+            f.write('\0')
+            f.write(subpath)
+            f.write('\0')
+        f.close()
+
+        log("roots: %r" % (checkout_trees, ))
+
+        link_cache_dir = os.path.join(self.workdir, 'link-cache')
+        fileutil.ensure_dir(link_cache_dir)
+
+        run_sync(['ostree', '--repo=' + self.repo,
+                  'checkout', '--link-cache=' + link_cache_dir,
+                  '--user-mode', '--union', '--from-stdin', dirpath],
+                 stdin=open(tmppath))
+
+        os.unlink(tmppath);
+
+        endtime = time.time()
+        log("Composed buildroot; %d seconds elapsed" % (int(endtime - starttime),))
 
     def execute(self, argv):
         parser = argparse.ArgumentParser(description=self.short_description)
         parser.add_argument('--pristine', action='store_true')
+        parser.add_argument('--snapshot', required=True)
         parser.add_argument('--name')
         parser.add_argument('--debug-shell', action='store_true')
         
         args = parser.parse_args(argv)
 
         self.parse_config()
-        self.parse_snapshot()
+        self.parse_snapshot(args.snapshot)
 
         if args.name:
             component_name = args.name
@@ -113,9 +135,6 @@ class OstbuildChrootCompileOne(builtins.Builtin):
             
         self._compose_buildroot(component_name, rootdir_tmp)
 
-        child_args = ['ostbuild', 'chroot-run-triggers', rootdir_tmp]
-        run_sync(child_args)
-
         builddir_tmp = os.path.join(rootdir_tmp, 'ostbuild')
         os.mkdir(builddir_tmp)
         os.mkdir(os.path.join(builddir_tmp, 'source'))
@@ -126,6 +145,7 @@ class OstbuildChrootCompileOne(builtins.Builtin):
         sourcedir=os.path.join(builddir, 'source', component_name)
         fileutil.ensure_dir(sourcedir)
         
+        print "cwd: %r" % (os.getcwd(), )
         output_metadata = open('_ostbuild-meta.json', 'w')
         json.dump(self.metadata, output_metadata, indent=4, sort_keys=True)
         output_metadata.close()
@@ -148,7 +168,6 @@ class OstbuildChrootCompileOne(builtins.Builtin):
                                'compile-one',
                                '--ostbuild-resultdir=/ostbuild/results',
                                '--ostbuild-meta=_ostbuild-meta.json'])
-            child_args.extend(self.metadata.get('config-opts', []))
         env_copy = dict(buildutil.BUILD_ENV)
         env_copy['PWD'] = chroot_sourcedir
         run_sync(child_args, env=env_copy, keep_stdin=args.debug_shell)
diff --git a/src/ostbuild/pyostbuild/builtin_compile_one.py b/src/ostbuild/pyostbuild/builtin_compile_one.py
index f703d0d..eef08df 100755
--- a/src/ostbuild/pyostbuild/builtin_compile_one.py
+++ b/src/ostbuild/pyostbuild/builtin_compile_one.py
@@ -57,6 +57,8 @@ class OstbuildCompileOne(builtins.Builtin):
 
     def execute(self, args):
         self.default_buildapi_jobs = ['-j', '%d' % (cpu_count() * 2, )]
+
+        starttime = time.time()
         
         uname=os.uname()
         kernel=uname[0].lower()
@@ -77,32 +79,28 @@ class OstbuildCompileOne(builtins.Builtin):
                       '--infodir=' + os.path.join(PREFIX, 'share', 'info')]
         self.makeargs = ['make']
 
-        self.ostbuild_resultdir=None
-        self.ostbuild_meta=None
+        self.ostbuild_resultdir='_ostbuild-results'
+        self.ostbuild_meta_path='_ostbuild-meta.json'
 
         chdir = None
+        opt_install = False
 
         for arg in args:
             if arg.startswith('--ostbuild-resultdir='):
                 self.ostbuild_resultdir=arg[len('--ostbuild-resultdir='):]
             elif arg.startswith('--ostbuild-meta='):
-                self.ostbuild_meta=arg[len('--ostbuild-meta='):]
+                self.ostbuild_meta_path=arg[len('--ostbuild-meta='):]
             elif arg.startswith('--chdir='):
                 os.chdir(arg[len('--chdir='):])
-            elif arg.startswith('--'):
-                self.configargs.append(arg)
             else:
                 self.makeargs.append(arg)
         
-        if self.ostbuild_resultdir is None:
-            fatal("Must specify --ostbuild-resultdir=")
-        if self.ostbuild_meta is None:
-            fatal("Must specify --ostbuild-meta=")
-
-        f = open(self.ostbuild_meta)
+        f = open(self.ostbuild_meta_path)
         self.metadata = json.load(f)
         f.close()
 
+        self.configargs.extend(self.metadata.get('config-opts', []))
+
         if self.metadata.get('rm-configure', False):
             configure_path = 'configure'
             if os.path.exists(configure_path):
@@ -235,6 +233,11 @@ class OstbuildCompileOne(builtins.Builtin):
                 except OSError, e:
                     pass
 
+        endtime = time.time()
+
+        log("Compliation succeeded; %d seconds elapsed" % (int(endtime - starttime),))
+        log("Results placed in %s" % (self.ostbuild_resultdir, ))
+
     def _install_and_unlink(self, src, dest):
         statsrc = os.lstat(src)
         dirname = os.path.dirname(dest)
diff --git a/src/ostbuild/pyostbuild/builtin_pull_components.py b/src/ostbuild/pyostbuild/builtin_pull_components.py
new file mode 100755
index 0000000..9b58e2e
--- /dev/null
+++ b/src/ostbuild/pyostbuild/builtin_pull_components.py
@@ -0,0 +1,70 @@
+# 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 copy
+import argparse
+import json
+import time
+import urlparse
+from StringIO import StringIO
+
+from . import builtins
+from .ostbuildlog import log, fatal
+from . import ostbuildrc
+from . import buildutil
+from .subprocess_helpers import run_sync, run_sync_get_output
+from . import kvfile
+from . import odict
+
+class OstbuildPullComponents(builtins.Builtin):
+    name = "pull-components"
+    short_description = "Download the component data for active branch"
+
+    def __init__(self):
+        builtins.Builtin.__init__(self)
+
+    def execute(self, argv):
+        parser = argparse.ArgumentParser(description=self.short_description)
+        parser.add_argument('targets', nargs='*')
+
+        args = parser.parse_args(argv)
+
+        self.parse_active_branch()
+
+        if len(args.targets) == 0:
+            targets = [self.active_branch]
+        else:
+            targets = args.targets
+
+        tree_contents_list = []
+        for target in targets:
+            tree_contents_path = os.path.join(self.ostree_dir, target, 'contents.json')
+            tree_contents = json.load(open(tree_contents_path))
+            tree_contents_list.append(tree_contents)
+        revisions = set()
+        for tree_contents in tree_contents_list:
+            for content_item in tree_contents['contents']:
+                revisions.add(content_item['ostree-revision'])
+        args = ['ostree-pull', '--repo=' + self.repo]
+        # FIXME FIXME - don't hardcode origin here
+        args.append('origin')
+        for revision in revisions:
+            args.append(revision)
+        run_sync(args)
+        
+builtins.register(OstbuildPullComponents)
diff --git a/src/ostbuild/pyostbuild/builtin_query_content.py b/src/ostbuild/pyostbuild/builtin_query_content.py
index 89f5839..ab49912 100755
--- a/src/ostbuild/pyostbuild/builtin_query_content.py
+++ b/src/ostbuild/pyostbuild/builtin_query_content.py
@@ -35,33 +35,60 @@ class OstbuildQueryContent(builtins.Builtin):
     def __init__(self):
         builtins.Builtin.__init__(self)
 
+    def snapshot_from_contents(self, tree_contents_to_merge):
+        snapshot = {}
+        
+        # Arbitrarily merge in non-content metadata from
+        # the first snapshot.
+        first_content = tree_contents_to_merge[0]
+        for k,v in first_content.iteritems():
+            if k in ['base', 'contents']:
+                continue
+            snapshot[k] = v
+        components = {}
+        snapshot['components'] = components
+        targets = []
+        snapshot['targets'] = targets
+        for tree_contents in tree_contents_to_merge:
+            target = {}
+            targets.append(target)
+            target['base'] = tree_contents['base']
+            target['contents'] = tree_contents['contents']
+            for content_item in target['contents']:
+                name = content_item['name']
+                if name in components:
+                    continue
+                meta = self.get_component_meta_from_revision(content_item['ostree-revision'])
+                components[name] = meta
+        return snapshot
+
     def execute(self, argv):
         parser = argparse.ArgumentParser(description=self.short_description)
-        parser.add_argument('--branch', required=True)
+        parser.add_argument('targets', nargs='*')
         parser.add_argument('--component')
 
         args = parser.parse_args(argv)
         self.args = args
         self.parse_config()
 
-        contents_json_text = run_sync_get_output(['ostree', '--repo=' + self.repo,
-                                                  'cat', args.branch, 'contents.json'])
-        
-        if args.component is None:
-            sys.stdout.write(contents_json_text)
+        self.parse_active_branch()    
+
+        if args.component is not None:
+            meta = self.get_component_meta(args.component)
+            json.dump(meta, sys.stdout,
+                      indent=4, sort_keys=True)
         else:
-            contents = json.loads(contents_json_text)
-            contents_list = contents['contents']
-            found = False
-            for content in contents_list:
-                if content['name'] != args.component:
-                    found = True
-                    break
-            if not found:
-                fatal("Unknown component '%s'" % (args.component, ))
-            ostbuildmeta_json = run_sync_get_output(['ostree', '--repo=' + self.repo,
-                                                     'cat', content['ostree-revision'],
-                                                     '/_ostbuild-meta.json'])
-            sys.stdout.write(ostbuildmeta_json)
+            if len(self.args.targets) == 0:
+                targets = [self.active_branch]
+            else:
+                targets = self.args.targets
+
+            contents_to_merge = []
+            for target in targets:
+                contents_path = os.path.join(self.ostree_dir, target, 'contents.json')
+                contents = json.load(open(contents_path))
+                contents_to_merge.append(contents)
+            snapshot = self.snapshot_from_contents(contents_to_merge)
+            json.dump(snapshot, sys.stdout, indent=4, sort_keys=True)
 
 builtins.register(OstbuildQueryContent)
diff --git a/src/ostbuild/pyostbuild/builtin_replace_component.py b/src/ostbuild/pyostbuild/builtin_replace_component.py
new file mode 100755
index 0000000..d23c9dc
--- /dev/null
+++ b/src/ostbuild/pyostbuild/builtin_replace_component.py
@@ -0,0 +1,67 @@
+# Copyright (C) 2011,2012 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.
+
+# ostbuild-compile-one-make wraps systems that implement the GNOME build API:
+# http://people.gnome.org/~walters/docs/build-api.txt
+
+import os,sys,stat,subprocess,tempfile,re,shutil
+from StringIO import StringIO
+import json
+import select,time
+import argparse
+
+from . import builtins
+from .ostbuildlog import log, fatal
+from .subprocess_helpers import run_sync, run_sync_get_output
+
+class OstbuildReplaceComponent(builtins.Builtin):
+    name = "replace-component"
+    short_description = "Replace contents of component in current filesystem"
+
+    def __init__(self):
+        builtins.Builtin.__init__(self)
+
+    def execute(self, argv):
+        parser = argparse.ArgumentParser(description=self.short_description)
+        parser.add_argument('component')
+        parser.add_argument('new-contents')
+
+        args = parser.parse_args(argv)
+        
+        self.parse_config()
+        self.parse_active_branch()
+
+        snapshot = self.get_component_snapshot(args.component)
+
+        replacing_trees = snapshot['trees']
+        previous_contents = set()
+        new_contents = set()
+        for tree in replacing_trees:
+            previous_contents_string = run_sync_get_output(['ostree', '--repo=' + self.repo,
+                                                            'ls', '--nul-filenames-only',
+                                                            '-R', snapshot['ostree-revision'],
+                                                            '/'])
+            for filename in previous_contents.split('\0'):
+                previous_contents.add(filename)
+        new_contents = run_sync_get_output(['find', '-print0'],
+                                           cwd=parser.new_contents)
+        for filename in new_contents.split('\0'):
+            new_contents.add(filename)
+
+        print "%r %r" % (previous_contents, new_contents)
+    
+builtins.register(OstbuildReplaceComponent)
diff --git a/src/ostbuild/pyostbuild/builtin_resolve.py b/src/ostbuild/pyostbuild/builtin_resolve.py
index 3486396..ea70ea5 100755
--- a/src/ostbuild/pyostbuild/builtin_resolve.py
+++ b/src/ostbuild/pyostbuild/builtin_resolve.py
@@ -19,6 +19,7 @@ import os,sys,subprocess,tempfile,re,shutil
 import copy
 import argparse
 import json
+import time
 import urlparse
 from StringIO import StringIO
 
@@ -135,6 +136,7 @@ class OstbuildResolve(builtins.Builtin):
         self.args = args
         
         self.parse_config()
+        self.repo = ostbuildrc.get_key('repo')
 
         manifest_path = self.ostbuildrc.get_key('manifest')
         self.manifest = json.load(open(manifest_path))
@@ -193,9 +195,15 @@ class OstbuildResolve(builtins.Builtin):
                 component['patches']['files'] = patch_files
 
         name_prefix = snapshot['name-prefix']
+        del snapshot['name-prefix']
         base_prefix = snapshot['base-prefix']
+        del snapshot['base-prefix']
 
         manifest_architectures = snapshot['architectures']
+        
+        snapshot['architecture-buildroots'] = {}
+        for architecture in manifest_architectures:
+            snapshot['architecture-buildroots'][architecture] = '%s-%s-devel' % (base_prefix, architecture)
 
         components_by_name = {}
         component_ordering = []
@@ -278,7 +286,9 @@ class OstbuildResolve(builtins.Builtin):
             del component['name']
         snapshot['components'] = components_by_name
 
-        out_snapshot = os.path.join(self.workdir, '%s-snapshot.json' % (name_prefix, ))
+        current_time = time.time()
+
+        out_snapshot = os.path.join(self.workdir, '%s-src-snapshot-%d.json' % (name_prefix, int(current_time)))
         f = open(out_snapshot, 'w')
         json.dump(snapshot, f, indent=4, sort_keys=True)
         f.close()
diff --git a/src/ostbuild/pyostbuild/builtins.py b/src/ostbuild/pyostbuild/builtins.py
index 64f7be4..984c751 100755
--- a/src/ostbuild/pyostbuild/builtins.py
+++ b/src/ostbuild/pyostbuild/builtins.py
@@ -19,11 +19,13 @@
 
 import os
 import sys
+import stat
 import argparse
 import json
 
 from . import ostbuildrc
 from .ostbuildlog import log, fatal
+from .subprocess_helpers import run_sync, run_sync_get_output
 
 _all_builtins = {}
 
@@ -31,9 +33,43 @@ class Builtin(object):
     name = None
     short_description = None
 
+    def __init__(self):
+        self._meta_cache = {}
+        self.manifest = None
+        self.snapshot = None
+        self.repo = None
+        self.ostree_dir = self._find_ostree_dir()
+        (self.active_branch, self.active_branch_checksum) = self._find_active_branch()
+
+    def _find_ostree_dir(self):
+        for path in ['/ostree', '/sysroot/ostree']:
+            if os.path.isdir(path):
+                return path
+        return None
+        
+    def _find_active_branch(self):
+        if self.ostree_dir is None:
+            return (None, None)
+        current_path = os.path.join(self.ostree_dir, 'current')
+        while True:
+            try:
+                target = os.path.join(self.ostree_dir, current_path)
+                stbuf = os.lstat(target)
+            except OSError, e:
+                current_path = None
+                break
+            if not stat.S_ISLNK(stbuf.st_mode):
+                break
+            current_path = os.readlink(target)
+        if current_path is not None:
+            basename = os.path.basename(current_path)
+            return basename.rsplit('-', 1)
+        else:
+            return (None, None)
+
     def parse_config(self):
         self.ostbuildrc = ostbuildrc
-        self.repo = ostbuildrc.get_key('repo')
+
         self.mirrordir = ostbuildrc.get_key('mirrordir')
         if not os.path.isdir(self.mirrordir):
             fatal("Specified mirrordir '%s' is not a directory" % (self.mirrordir, ))
@@ -42,15 +78,52 @@ class Builtin(object):
             fatal("Specified workdir '%s' is not a directory" % (self.workdir, ))
         self.patchdir = os.path.join(self.workdir, 'patches')
 
-    def parse_manifest(self):
-        self.manifest_path = ostbuildrc.get_key('manifest')
-        self.manifest = json.load(open(self.manifest_path))
-        self.name_prefix = self.manifest['name-prefix']
+    def parse_active_branch(self):
+        if self.ostree_dir is None:
+            fatal("/ostree directory not found")
+        repo_path = os.path.join(self.ostree_dir, 'repo')
+        if not os.path.isdir(repo_path):
+            fatal("Repository '%s' doesn't exist" % (repo_path, ))
+        self.repo = repo_path
+        branch_path = os.path.join(self.ostree_dir, self.active_branch)
+        contents_path = os.path.join(branch_path, 'contents.json')
+        f = open(contents_path)
+        self.active_branch_contents = json.load(f)
+        f.close()
+
+    def get_component_snapshot(self, name):
+        found = False
+        for content in self.active_branch_contents['contents']:
+            if content['name'] == name:
+                found = True
+                break
+        if not found:
+            fatal("Unknown component '%s'" % (name, ))
+        return content
 
-    def parse_snapshot(self):
-        self.parse_manifest()
-        snapshot_path = os.path.join(self.workdir, '%s-snapshot.json' % (self.name_prefix, ))
-        self.snapshot = json.load(open(snapshot_path))
+    def get_component_meta_from_revision(self, revision):
+        text = run_sync_get_output(['ostree', '--repo=' + self.repo,
+                                    'cat', revision,
+                                    '/_ostbuild-meta.json'])
+        return json.loads(text)
+
+    def get_component_meta(self, name):
+        assert self.repo is not None
+
+        if self.snapshot is not None:
+            return self.snapshot['components'][name]
+
+        meta = self._meta_cache.get(name)
+        if meta is None:
+            content = self.get_component_snapshot(name)
+            meta = self.get_component_meta_from_revision(content['ostree-revision'])
+            self._meta_cache[name] = meta
+        return meta
+
+    def parse_snapshot(self, path):
+        self.repo = ostbuildrc.get_key('repo')
+        self.snapshot_path = path
+        self.snapshot = json.load(open(self.snapshot_path))
 
     def execute(self, args):
         raise NotImplementedError()
diff --git a/src/ostbuild/pyostbuild/main.py b/src/ostbuild/pyostbuild/main.py
index 842cb5b..5b48d71 100755
--- a/src/ostbuild/pyostbuild/main.py
+++ b/src/ostbuild/pyostbuild/main.py
@@ -27,8 +27,10 @@ from . import builtin_checkout
 from . import builtin_chroot_compile_one
 from . import builtin_chroot_run_triggers
 from . import builtin_compile_one
+from . import builtin_pull_components
 from . import builtin_query_content
 from . import builtin_resolve
+from . import builtin_replace_component
 from . import builtin_status
 
 def usage(ecode):
diff --git a/src/ostbuild/pyostbuild/subprocess_helpers.py b/src/ostbuild/pyostbuild/subprocess_helpers.py
index 116d0fb..3754900 100755
--- a/src/ostbuild/pyostbuild/subprocess_helpers.py
+++ b/src/ostbuild/pyostbuild/subprocess_helpers.py
@@ -70,14 +70,16 @@ def run_sync_get_output(args, cwd=None, env=None, stdout=None, stderr=None, none
     return None
 
 def run_sync(args, cwd=None, env=None, fatal_on_error=True, keep_stdin=False,
-             log_success=True, log_initiation=True, stdout=None,
+             log_success=True, log_initiation=True, stdin=None, stdout=None,
              stderr=None):
     if log_initiation:
         log("running: %s" % (subprocess.list2cmdline(args),))
 
     env_copy = _get_env_for_cwd(cwd, env)
 
-    if keep_stdin:
+    if stdin is not None:
+        stdin_target = stdin
+    elif keep_stdin:
         stdin_target = sys.stdin
     else:
         stdin_target = open('/dev/null', 'r')



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