#!/usr/bin/python # vim: set fileencoding=utf-8 : # # (C) 2006,2007 Guido Guenther # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """run commands to build a debian package out of a git repository""" import sys import os, os.path import errno import pipes import time import gbp.deb_utils as du from gbp.git_utils import (GitRepositoryError, GitRepository, build_tag) from gbp.command_wrappers import (GitTag, Command, RunAtCommand, CommandExecFailed, PristineTar, RemoveTree) from gbp.config import GbpOptionParser from gbp.errors import GbpError def git_archive_pipe(prefix, pipe, output, treeish): """run the git_archive pipe""" pipe.prepend('git-archive --format=tar --prefix=%s/ %s' % (prefix, treeish), '.-') try: ret = pipe.copy('', output) if ret: print >>sys.stderr, "Error creating %s: %d" % (output, ret) return False except OSError, err: print >>sys.stderr, "Error creating %s: %s" % (output, err[0]) return False except: print >>sys.stderr, "Error creating %s" % (output,) return False return True def git_archive(cp, output_dir, treeish): "create an orig.tar.gz in output_dir using git_archive" output = os.path.join(output_dir, du.orig_file(cp)) prefix = "%s-%s" % (cp['Source'], cp['Upstream-Version']) gzip_opts = "-9 -n" pipe = pipes.Template() pipe.append('gzip -c %s' % gzip_opts, '--') return git_archive_pipe(prefix, pipe, output, treeish) def dump_tree(export_dir, treeish): "dump a tree to output_dir" output_dir = os.path.dirname(export_dir) prefix = os.path.basename(export_dir) pipe = pipes.Template() pipe.append('tar -C %s -xf -' % output_dir, '-.') return git_archive_pipe(prefix, pipe, '', treeish) def move_old_export(target): """move a build tree away if it exists""" try: os.mkdir(target) except OSError, (e, msg): if e == errno.EEXIST: os.rename(target, "%s.obsolete.%s" % (target, time.time())) def prepare_output_dir(dir): output_dir = dir if not dir: output_dir = '..' output_dir = os.path.abspath(output_dir) try: os.mkdir(output_dir) except OSError, (e, msg): if e != errno.EEXIST: raise GbpError, "Cannot create output dir %s" % output_dir return output_dir def pristine_tar_build_orig(repo, cp, output_dir, options): """ build orig using pristine-tar @return: True: orig.tar.gz build, False: noop """ if options.pristine_tar: pt = PristineTar() if not repo.has_branch(pt.branch): print >>sys.stderr, 'Pristine-tar branch "%s" not found' % pt.branch pt.checkout(os.path.join(output_dir, du.orig_file(cp))) return True else: return False def git_archive_build_orig(repo, cp, output_dir, options): """build orig using git-archive""" # --upstream-branch was given on the command line, so use this: if options.upstream_branch != GbpOptionParser.defaults['upstream-branch']: upstream_tree = options.upstream_branch else: upstream_tree = build_tag(options.upstream_tag, cp['Upstream-Version']) # fall back to the upstream-branch tip if the tag doesn't exist if not repo.has_treeish(upstream_tree): upstream_tree = GbpOptionParser.defaults['upstream-branch'] print "%s does not exist, creating from '%s'" % (du.orig_file(cp), upstream_tree) if not repo.has_treeish(upstream_tree): raise GbpError # git-ls-tree printed an error message already if not git_archive(cp, output_dir, upstream_tree): raise GbpError, "Cannot create upstream tarball at '%s'" % output_dir def main(argv): changelog = 'debian/changelog' default_tree = 'HEAD' retval = 0 prefix = "git-" args = [ arg for arg in argv[1:] if arg.find('--%s' % prefix) == 0 ] dpkg_args = [ arg for arg in argv[1:] if arg.find('--%s' % prefix) == -1 ] for arg in [ "--help", "-h", "--version" ]: if arg in dpkg_args: args.append(arg) parser = GbpOptionParser(command=os.path.basename(argv[0]), prefix=prefix) parser.add_option("--git-ignore-new", action="store_true", dest="ignore_new", default=False, help="build with uncommited changes in the source tree") parser.add_option("--git-verbose", action="store_true", dest="verbose", default=False, help="verbose command execution") parser.add_config_file_option(option_name="builder", dest="builder", help="command to build the package, default is '%(builder)s'") parser.add_config_file_option(option_name="cleaner", dest="cleaner", help="command to build the package, default is '%(cleaner)s'") parser.add_config_file_option(option_name="upstream-branch", dest="upstream_branch", help="upstream branch, default is '%(upstream-branch)s'") parser.add_config_file_option(option_name="debian-branch", dest='debian_branch', help="branch the debian patch is being developed on, default is '%(debian-branch)s'") parser.add_option("--git-tag", action="store_true", dest="tag", default=False, help="tag after a successful build") parser.add_config_file_option(option_name="sign-tags", dest="sign_tags", help="sign git tags", action="store_true") parser.add_config_file_option(option_name="no-create-orig", dest="no_create_orig", help="don't create orig.tar.gz", action="store_true") parser.add_config_file_option(option_name="posttag", dest="posttag", help="hook to execute after a successfull tag operation") parser.add_config_file_option(option_name="keyid", dest="keyid", help="GPG keyid to sign tags with") parser.add_config_file_option(option_name="debian-tag", dest="debian_tag", help="format string for debian tags, default is '%(debian-tag)s'") parser.add_config_file_option(option_name="upstream-tag", dest="upstream_tag", help="format string for upstream tags, default is '%(upstream-tag)s'") parser.add_config_file_option(option_name="export-dir", dest="export_dir", help="before building export into EXPORT_DIR") parser.add_config_file_option(option_name="tarball-dir", dest="tarball_dir", help="location to look for external tarballs") parser.add_config_file_option(option_name="pristine-tar", dest="pristine_tar", help="Use pristine-tar to create .orig.tar.gz", action="store_true") parser.add_option("--git-export", dest="treeish", default=default_tree, help="export treeish object TREEISH, default is '%s'" % default_tree) (options, args) = parser.parse_args(args) if options.verbose: Command.verbose = True try: repo = GitRepository(os.path.curdir) except GitRepositoryError: print >>sys.stderr, "%s is not a git repository" % (os.path.abspath('.')) return 1 else: repo_dir = os.path.abspath(os.path.curdir) try: if not options.ignore_new: Command(options.cleaner, shell=True)() (ret, out) = repo.is_clean() if not ret: print >>sys.stderr, "You have uncommitted changes in your source tree:" print >>sys.stderr, out raise GbpError, "Use --git-ignore-new to ignore." branch = repo.get_branch() if branch != options.debian_branch: print >>sys.stderr, "You are not on branch '%s' but on '%s'" % (options.debian_branch, branch) raise GbpError, "Use --git-ignore-new to ignore or --git-debian-branch to set the branch name." try: cp = du.parse_changelog(changelog) except du.NoChangelogError: raise GbpError, "'%s' does not exist, not a debian package" % changelog except du.ParseChangeLogError, err: raise GbpError, "Error parsing Changelog: %s" % err output_dir = prepare_output_dir(options.export_dir) if options.tarball_dir: tarball_dir = options.tarball_dir else: tarball_dir = output_dir if not repo.has_treeish(options.treeish): raise GbpError # git-ls-tree printed an error message already # Export to another build dir if requested: if options.export_dir: tmp_dir = os.path.join(output_dir, "%s-tmp" % cp['Source']) print "Exporting '%s' to '%s'" % (options.treeish, tmp_dir) dump_tree(tmp_dir, options.treeish) cp = du.parse_changelog(os.path.join(tmp_dir, 'debian', 'changelog')) if du.is_native(cp): version = cp['Debian-Version'] else: version = cp['Upstream-Version'] export_dir = os.path.join(output_dir, "%s-%s" % (cp['Source'], version)) print "Moving '%s' to '%s'" % (tmp_dir, export_dir) move_old_export(export_dir) os.rename(tmp_dir, export_dir) # Get/build the orig.tar.gz if necessary: if not du.is_native(cp): if du.has_orig(cp, output_dir): pass elif options.tarball_dir: # separate tarball dir specified print "Getting orig tarbball from %s" % tarball_dir if not du.copy_orig(cp, tarball_dir, output_dir): raise GbpError, "Cannot copy orig tarball from %s" % tarball_dir elif not options.no_create_orig: if not pristine_tar_build_orig(repo, cp, output_dir, options): git_archive_build_orig(repo, cp, output_dir, options) if options.export_dir: build_dir = export_dir else: build_dir = repo_dir RunAtCommand(options.builder, dpkg_args, shell=True)(dir=build_dir) if options.tag: try: version = cp['Version'] except KeyError: raise GbpError, "Can't parse version from changelog" else: print "Tagging %s" % version GitTag(options.sign_tags, options.keyid)(build_tag(options.debian_tag, version), msg="Debian release %s" % version) if(options.posttag): Command(options.posttag, shell=True)() except CommandExecFailed: retval = 1 except GbpError, err: if len(err.__str__()): print >>sys.stderr, err retval = 1 if options.export_dir and not retval: RemoveTree(export_dir)() return retval if __name__ == '__main__': sys.exit(main(sys.argv)) # vim:et:ts=4:sw=4:et:sts=4:ai:set list listchars=tab\:»·,trail\:·: