diff --git a/Makefile-ostbuild.am b/Makefile-ostbuild.am index a3b855cb..f0385694 100644 --- a/Makefile-ostbuild.am +++ b/Makefile-ostbuild.am @@ -37,6 +37,7 @@ pyostbuild_PYTHON = \ src/ostbuild/pyostbuild/builtin_prefix.py \ src/ostbuild/pyostbuild/builtin_resolve.py \ src/ostbuild/pyostbuild/builtin_init.py \ + src/ostbuild/pyostbuild/builtin_source_diff.py \ src/ostbuild/pyostbuild/builtins.py \ src/ostbuild/pyostbuild/filemonitor.py \ src/ostbuild/pyostbuild/fileutil.py \ diff --git a/src/ostbuild/pyostbuild/builtin_source_diff.py b/src/ostbuild/pyostbuild/builtin_source_diff.py new file mode 100755 index 00000000..ce81c4f2 --- /dev/null +++ b/src/ostbuild/pyostbuild/builtin_source_diff.py @@ -0,0 +1,120 @@ +# Copyright (C) 2011,2012 Colin Walters +# +# 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 +import argparse +from StringIO import StringIO +import json + +from . import builtins +from .ostbuildlog import log, fatal +from . import vcs +from .subprocess_helpers import run_sync, run_sync_get_output +from . import buildutil + +class OstbuildSourceDiff(builtins.Builtin): + name = "source-diff" + short_description = "Show differences in source code between builds" + + def __init__(self): + builtins.Builtin.__init__(self) + + def _snapshot_from_rev(self, rev): + self.init_repo() + text = run_sync_get_output(['ostree', '--repo=' + self.repo, + 'cat', rev, '/contents.json'], + log_initiation=False) + return json.loads(text) + + def execute(self, argv): + parser = argparse.ArgumentParser(description=self.short_description) + parser.add_argument('--rev-from') + parser.add_argument('--rev-to') + parser.add_argument('--snapshot-from') + parser.add_argument('--snapshot-to') + + args = parser.parse_args(argv) + self.parse_config() + + to_snap = None + from_snap = None + + if args.rev_to: + to_snap = self._snapshot_from_rev(args.rev_to) + if args.rev_from: + from_snap = self._snapshot_from_rev(args.rev_from) + if args.snapshot_from: + from_snap = json.load(open(args.snapshot_from)) + if args.snapshot_to: + to_snap = json.load(open(args.snapshot_to)) + + if to_snap is None: + fatal("One of --rev-to/--snapshot-to must be given") + if from_snap is None: + if args.rev_to: + from_snap = self._snapshot_from_rev(args.rev_to + '^') + else: + fatal("One of --rev-from/--snapshot-from must be given") + + diff_replace_re = re.compile(' [ab]') + + for from_component in from_snap['components']: + name = from_component['name'] + src = from_component['src'] + (keytype, uri) = vcs.parse_src_key(src) + if keytype == 'local': + log("Component %r has local URI" % (name, )) + continue + mirrordir = vcs.ensure_vcs_mirror(self.mirrordir, keytype, uri, from_component['branch']) + + to_component = self.find_component_in_snapshot(name, to_snap) + if to_component is None: + log("DELETED COMPONENT: %s" % (name, )) + continue + + from_revision = from_component.get('revision') + to_revision = to_component.get('revision') + if from_revision is None: + log("From component %s missing revision" % (name, )) + continue + if to_revision is None: + log("From component %s missing revision" % (name, )) + continue + + if from_revision != to_revision: + env = dict(os.environ) + env['LANG'] = 'C' + + spacename = ' ' + name + + proc = subprocess.Popen(['git', 'diff', from_revision, to_revision], + env=env, cwd=mirrordir, stdout=subprocess.PIPE) + for line in proc.stdout: + if (line.startswith('diff --git ') + or line.startswith('--- a/') + or line.startswith('+++ b/') + or line.startswith('Binary files /dev/null and b/')): + line = diff_replace_re.sub(spacename, line) + sys.stdout.write(line) + else: + sys.stdout.write(line) + proc.wait() + +builtins.register(OstbuildSourceDiff) diff --git a/src/ostbuild/pyostbuild/builtins.py b/src/ostbuild/pyostbuild/builtins.py index e97c33cd..db8b009f 100755 --- a/src/ostbuild/pyostbuild/builtins.py +++ b/src/ostbuild/pyostbuild/builtins.py @@ -122,12 +122,22 @@ class Builtin(object): meta['config-opts'] = config_opts return meta - def get_component(self, name): - assert self.snapshot is not None - for component in self.snapshot['components']: + def find_component_in_snapshot(self, name, snapshot): + for component in snapshot['components']: if component['name'] == name: return component - fatal("Couldn't find component '%s' in manifest" % (component_name, )) + return None + + def get_component(self, name, in_snapshot=None): + if in_snapshot is None: + assert self.snapshot is not None + target_snapshot = self.snapshot + else: + target_snapshot = in_snapshot + component = self.find_component_in_snapshot(self, target_snapshot) + if component is None: + fatal("Couldn't find component '%s' in manifest" % (component_name, )) + return component def get_expanded_component(self, name): return self.expand_component(self.get_component(name)) @@ -161,7 +171,9 @@ class Builtin(object): self._bin_snapshots = self.create_db('bin-snapshot') return self._bin_snapshots - def _init_repo(self): + def init_repo(self): + if self.repo is not None: + return self.repo repo = ostbuildrc.get_key('repo', default=None) if repo is not None: self.repo = repo @@ -178,7 +190,7 @@ class Builtin(object): def parse_snapshot(self, prefix, path): self.parse_prefix(prefix) - self._init_repo() + self.init_repo() if path is None: latest_path = self.get_src_snapshot_db().get_latest_path() if latest_path is None: diff --git a/src/ostbuild/pyostbuild/main.py b/src/ostbuild/pyostbuild/main.py index 4f6b27ec..0adccd68 100755 --- a/src/ostbuild/pyostbuild/main.py +++ b/src/ostbuild/pyostbuild/main.py @@ -36,6 +36,7 @@ from . import builtin_prefix from . import builtin_privhelper_deploy_qemu from . import builtin_privhelper_run_qemu from . import builtin_resolve +from . import builtin_source_diff def usage(ecode): print "Builtins:" diff --git a/src/ostbuild/pyostbuild/vcs.py b/src/ostbuild/pyostbuild/vcs.py index a6c11d82..1fa618f7 100755 --- a/src/ostbuild/pyostbuild/vcs.py +++ b/src/ostbuild/pyostbuild/vcs.py @@ -84,7 +84,7 @@ def parse_src_key(srckey): if idx < 0: raise ValueError("Invalid SRC uri=%s" % (srckey, )) keytype = srckey[:idx] - if keytype not in ['git']: + if keytype not in ['git', 'local']: raise ValueError("Unsupported SRC uri=%s" % (srckey, )) uri = srckey[idx+1:] return (keytype, uri)