summaryrefslogtreecommitdiff
path: root/gbp/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'gbp/scripts')
-rw-r--r--gbp/scripts/buildpackage_bb.py536
-rwxr-xr-xgbp/scripts/buildpackage_rpm.py2
-rwxr-xr-xgbp/scripts/clone_bb.py174
-rwxr-xr-xgbp/scripts/import_bb.py419
-rw-r--r--gbp/scripts/import_orig.py6
-rwxr-xr-xgbp/scripts/pq_bb.py427
-rwxr-xr-xgbp/scripts/pq_rpm.py9
-rwxr-xr-xgbp/scripts/submit_bb.py138
8 files changed, 1704 insertions, 7 deletions
diff --git a/gbp/scripts/buildpackage_bb.py b/gbp/scripts/buildpackage_bb.py
new file mode 100644
index 00000000..95c66368
--- /dev/null
+++ b/gbp/scripts/buildpackage_bb.py
@@ -0,0 +1,536 @@
+# vim: set fileencoding=utf-8 :
+#
+# (C) 2014 Intel Corporation <markus.lehtonen@linux.intel.com>
+# 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
+#
+"""Build an RPM package out of a Git repo with Bitbake meta data"""
+
+import ConfigParser
+import os, os.path
+import sys
+import shutil
+import tempfile
+
+import gbp.rpm as rpm
+from gbp.rpm.policy import RpmPkgPolicy
+from gbp.command_wrappers import Command, RunAtCommand, CommandExecFailed
+from gbp.config import GbpOptionParserBB, GbpOptionGroup
+from gbp.rpm.git import (GitRepositoryError, RpmGitRepository)
+from gbp.errors import GbpError
+import gbp.log
+import gbp.notifications
+from gbp.scripts.common.buildpackage import (index_name, wc_names, dump_tree,
+ drop_index)
+from gbp.scripts.buildpackage_rpm import (disable_hooks, get_tree,
+ get_current_branch, get_upstream_tree, get_vcs_info,
+ packaging_tag_name, create_packaging_tag, GbpAutoGenerateError)
+from gbp.scripts.import_bb import recursive_copy
+from gbp.scripts.pq_bb import update_patch_series
+from gbp.scripts.common.pq import is_pq_branch, pq_branch_base
+from gbp.bb import (bb, init_tinfoil, guess_bb_path, BBFile, bb_from_repo,
+ pkg_version, parse_bb)
+
+# pylint: disable=bad-continuation
+
+
+def guess_export_params(repo, options):
+ """Get commit and tree from where to export packaging and patches"""
+ tree = None
+ branch = None
+ if options.export in wc_names.keys() + [index_name, 'HEAD']:
+ branch = get_current_branch(repo)
+ elif options.export in repo.get_local_branches():
+ branch = options.export
+ if branch:
+ if is_pq_branch(branch, options):
+ packaging_branch = pq_branch_base(branch, options)
+ if repo.has_branch(packaging_branch):
+ gbp.log.info("It seems you're building a development/patch-"
+ "queue branch. Export target changed to '%s' and "
+ "patch-export enabled!" % packaging_branch)
+ options.patch_export = True
+ if not options.patch_export_rev:
+ options.patch_export_rev = options.export
+ options.export = packaging_branch
+ else:
+ gbp.log.warn("It seems you're building a development/patch-"
+ "queue branch. No corresponding packaging branch "
+ "found. Build may fail!")
+ if tree is None:
+ tree = get_tree(repo, options.export)
+
+ # Get recipe path
+ bb_path = guess_bb_path(options, repo, tree, bbappend=True)
+ # Adjust meta-dir accordingly
+ options.meta_dir = os.path.dirname(bb_path)
+
+ # Filter out changes in recipe directory
+ if options.patch_export:
+ relpath = os.path.relpath(os.path.abspath(options.meta_dir), repo.path)
+ if relpath != '.':
+ gbp.log.info("Auto-excluding changes under meta-dir (%s/)" %
+ relpath)
+ if options.patch_export_ignore_path:
+ options.patch_export_ignore_path += '|' + relpath + '/*'
+ else:
+ options.patch_export_ignore_path = relpath + '/*'
+ return tree
+
+def guess_export_dir(options, tinfoil, repo, treeish):
+ """Guess export directory"""
+ if not tinfoil:
+ gbp.log.err("Bitbake build environment (bb.tinfoil) not initialized, "
+ "unable to guess export directory")
+ gbp.log.err("Please use --git-export-dir or try initializing bitbake "
+ "build environment with the 'oe-init-build-env' script")
+ raise GbpError
+
+ gbp.log.info('Guessing export directory')
+ tinfoil.parseRecipes()
+
+ # Parse recipe
+ bb_path = guess_bb_path(options, repo, treeish, bbappend=True)
+ #cfg_data = bb.data.createCopy(tinfoil.config_data)
+ #bbfile = bb_from_repo(cfg_data, repo, treeish, bb_path)
+ # Use naive parsing, at least for now as the file might be .bbappend
+ bbfile = bb_from_repo(None, repo, treeish, bb_path)
+
+ pkg_name = bbfile.getVar('PN', True)
+ bb_name = os.path.basename(bb_path)
+ if bb_name.endswith('.bb'):
+ for name in tinfoil.cooker_data.pkg_fn:
+ if os.path.basename(name) == bb_name and os.path.isabs(name):
+ gbp.log.debug("Found matching recipe filename: %s" % name)
+ return os.path.dirname(name)
+ else:
+ for name, appends in tinfoil.cooker.collection.appendlist.iteritems():
+ print name, appends
+ if name.rsplit('_', 1)[0] == pkg_name:
+ gbp.log.debug("Found %s from appends" % name)
+ for append_name in appends:
+ if os.path.basename(append_name) == bb_name:
+ gbp.log.debug("Found matching recipe filename: %s" %
+ append_name)
+ return os.path.dirname(append_name)
+ export_dir = os.path.dirname(appends[-1])
+ gbp.log.debug("Using existing appends directory %s" %
+ export_dir)
+ return export_dir
+ if pkg_name in tinfoil.cooker_data.pkg_pn:
+ export_dir = os.path.dirname(tinfoil.cooker_data.pkg_pn[pkg_name][-1])
+ gbp.log.debug("Using existing package directory %s" % export_dir)
+ return export_dir
+ else:
+ pkg_ver = bbfile.getVar('PV', True)
+ raise GbpError("Package %s-%s not found under any configured layer, "
+ "please use --git-export-dir to define the export "
+ "directory" % (pkg_name, pkg_ver))
+
+def export_patches(repo, bbfile, export_treeish, options):
+ """Generate patches and update recipe"""
+ try:
+ if bbfile.getVar('SRCREV', True):
+ upstream_tree = bbfile.getVar('SRCREV', True)
+ else:
+ upstream_version = bbfile.getVar('PV', True)
+ upstream_tree = get_upstream_tree(repo, upstream_version, options)
+ update_patch_series(repo, bbfile, upstream_tree, export_treeish,
+ options)
+ except (GitRepositoryError, GbpError) as err:
+ raise GbpAutoGenerateError(str(err))
+
+
+def is_native(repo, options):
+ """Determine whether a package is native or non-native"""
+ if options.native.is_auto():
+ if repo.has_branch(options.upstream_branch):
+ return False
+ # Check remotes, too
+ for remote_branch in repo.get_remote_branches():
+ remote, branch = remote_branch.split('/', 1)
+ if branch == options.upstream_branch:
+ gbp.log.debug("Found upstream branch '%s' from remote '%s'" %
+ (remote, branch))
+ return False
+ return True
+
+ return options.native.is_on()
+
+
+def setup_builder(options, builder_args):
+ """Setup everything to use git-pbuilder"""
+ # TODO: placeholder for Bitbake: implement or remove entirely
+ pass
+
+def bb_get_local_files(bbfile, tgt_dir, whole_dir=False):
+ """Get (local) packaging files"""
+ if not whole_dir:
+ for path in bbfile.localfiles + bbfile.includes + [bbfile.bb_path]:
+ relpath = os.path.relpath(path, bbfile.bb_dir)
+ subdir = os.path.join(tgt_dir, os.path.dirname(relpath))
+ if not os.path.exists(subdir):
+ os.makedirs(subdir)
+ shutil.copy2(path, os.path.join(tgt_dir, relpath))
+ else:
+ # Simply copy whole meta dir, if requested
+ recursive_copy(bbfile.bb_dir, tgt_dir)
+
+def dump_meta(cfg_data, options, repo, treeish, dump_dir):
+ """Parse and dump meta information from a treeish"""
+ tmpdir = tempfile.mkdtemp(prefix='gbp-bb_')
+ try:
+ bb_path = guess_bb_path(options, repo, treeish, bbappend=True)
+ # Dump whole meta directory
+ dump_tree(repo, tmpdir, '%s:%s' % (treeish, os.path.dirname(bb_path)),
+ False)
+ # Parse recipe
+ full_path = os.path.join(tmpdir, os.path.basename(bb_path))
+ bbfile = BBFile(full_path, cfg_data)
+ bb_get_local_files(bbfile, dump_dir)
+ except GitRepositoryError as err:
+ raise GbpError("Git error: %s" % err)
+ finally:
+ shutil.rmtree(tmpdir)
+
+ # Re-parse recipe from final location
+ full_path = os.path.abspath(os.path.join(dump_dir,
+ os.path.basename(bb_path)))
+ return BBFile(full_path, cfg_data)
+
+
+def build_parser(name, prefix=None, git_treeish=None):
+ """Create command line parser"""
+ try:
+ parser = GbpOptionParserBB(command=os.path.basename(name),
+ prefix=prefix, git_treeish=git_treeish)
+ except ConfigParser.ParsingError, err:
+ gbp.log.err(err)
+ return None
+
+ tag_group = GbpOptionGroup(parser, "tag options",
+ "options related to git tag creation")
+ branch_group = GbpOptionGroup(parser, "branch options",
+ "branch layout options")
+ cmd_group = GbpOptionGroup(parser, "external command options",
+ "how and when to invoke external commands and hooks")
+ orig_group = GbpOptionGroup(parser, "orig tarball options",
+ "options related to the creation of the orig tarball")
+ export_group = GbpOptionGroup(parser, "export build-tree options",
+ "alternative build tree related options")
+ parser.add_option_group(tag_group)
+ parser.add_option_group(orig_group)
+ parser.add_option_group(branch_group)
+ parser.add_option_group(cmd_group)
+ parser.add_option_group(export_group)
+
+ parser.add_boolean_config_file_option(option_name = "ignore-untracked",
+ dest="ignore_untracked")
+ parser.add_boolean_config_file_option(option_name = "ignore-new",
+ dest="ignore_new")
+ parser.add_option("--git-verbose", action="store_true", dest="verbose",
+ help="verbose command execution")
+ parser.add_config_file_option(option_name="tmp-dir", dest="tmp_dir")
+ parser.add_config_file_option(option_name="color", dest="color",
+ type='tristate')
+ parser.add_config_file_option(option_name="color-scheme",
+ dest="color_scheme")
+ parser.add_config_file_option(option_name="notify", dest="notify",
+ type='tristate')
+ parser.add_config_file_option(option_name="vendor", action="store",
+ dest="vendor")
+ parser.add_config_file_option(option_name="native", dest="native",
+ type='tristate')
+ tag_group.add_option("--git-tag", action="store_true", dest="tag",
+ help="create a tag after a successful build")
+ tag_group.add_option("--git-tag-only", action="store_true", dest="tag_only",
+ help="don't build, only tag and run the posttag hook")
+ tag_group.add_option("--git-retag", action="store_true", dest="retag",
+ help="don't fail if the tag already exists")
+ tag_group.add_boolean_config_file_option(option_name="sign-tags",
+ dest="sign_tags")
+ tag_group.add_config_file_option(option_name="keyid", dest="keyid")
+ tag_group.add_config_file_option(option_name="packaging-tag",
+ dest="packaging_tag")
+ tag_group.add_config_file_option(option_name="upstream-tag",
+ dest="upstream_tag")
+ orig_group.add_config_file_option(option_name="upstream-tree",
+ dest="upstream_tree")
+ branch_group.add_config_file_option(option_name="upstream-branch",
+ dest="upstream_branch")
+ branch_group.add_config_file_option(option_name="packaging-branch",
+ dest="packaging_branch")
+ branch_group.add_config_file_option(option_name="pq-branch",
+ dest="pq_branch")
+ branch_group.add_boolean_config_file_option(option_name = "ignore-branch",
+ dest="ignore_branch")
+ cmd_group.add_config_file_option(option_name="builder", dest="builder",
+ help="command to build the package, default is "
+ "'%(builder)s'")
+ cmd_group.add_config_file_option(option_name="cleaner", dest="cleaner",
+ help="command to clean the working copy, default is "
+ "'%(cleaner)s'")
+ cmd_group.add_config_file_option(option_name="prebuild", dest="prebuild",
+ help="command to run before a build, default is "
+ "'%(prebuild)s'")
+ cmd_group.add_config_file_option(option_name="postexport",
+ dest="postexport",
+ help="command to run after exporting the source tree, "
+ "default is '%(postexport)s'")
+ cmd_group.add_config_file_option(option_name="postbuild", dest="postbuild",
+ help="hook run after a successful build, default is "
+ "'%(postbuild)s'")
+ cmd_group.add_config_file_option(option_name="posttag", dest="posttag",
+ help="hook run after a successful tag operation, default "
+ "is '%(posttag)s'")
+ cmd_group.add_boolean_config_file_option(option_name="hooks", dest="hooks")
+ export_group.add_option("--git-no-build", action="store_true",
+ dest="no_build",
+ help="Don't run builder or the associated hooks")
+ export_group.add_config_file_option(option_name="export-dir",
+ dest="export_dir", type="path",
+ help="Build topdir, also export the sources under "
+ "EXPORT_DIR, default is '%(export-dir)s'")
+ export_group.add_config_file_option("export", dest="export",
+ help="export treeish object TREEISH, default is "
+ "'%(export)s'", metavar="TREEISH")
+ export_group.add_config_file_option(option_name="meta-dir",
+ dest="meta_dir")
+ export_group.add_config_file_option(option_name="bb-file", dest="bb_file")
+ export_group.add_boolean_config_file_option("patch-export",
+ dest="patch_export")
+ export_group.add_option("--git-patch-export-rev", dest="patch_export_rev",
+ metavar="TREEISH",
+ help="[experimental] Export patches from treeish object "
+ "TREEISH")
+ export_group.add_config_file_option("patch-export-ignore-path",
+ dest="patch_export_ignore_path")
+ export_group.add_config_file_option("patch-export-compress",
+ dest="patch_export_compress")
+ export_group.add_config_file_option("patch-export-squash-until",
+ dest="patch_export_squash_until")
+ export_group.add_boolean_config_file_option(option_name="patch-numbers",
+ dest="patch_numbers")
+ export_group.add_config_file_option("bb-vcs-info", dest="bb_vcs_info")
+ return parser
+
+def parse_args(argv, prefix, git_treeish=None):
+ """Parse config and command line arguments"""
+ args = [arg for arg in argv[1:] if arg.find('--%s' % prefix) == 0]
+ builder_args = [arg for arg in argv[1:] if arg.find('--%s' % prefix) == -1]
+
+ # We handle these although they don't have a --git- prefix
+ for arg in ["--help", "-h", "--version"]:
+ if arg in builder_args:
+ args.append(arg)
+
+ parser = build_parser(argv[0], prefix=prefix, git_treeish=git_treeish)
+ if not parser:
+ return None, None, None
+ options, args = parser.parse_args(args)
+
+ options.patch_export_compress = rpm.string_to_int(
+ options.patch_export_compress)
+
+ gbp.log.setup(options.color, options.verbose, options.color_scheme)
+ if not options.hooks:
+ disable_hooks(options)
+ if options.retag:
+ if not options.tag and not options.tag_only:
+ gbp.log.err("'--%sretag' needs either '--%stag' or '--%stag-only'" %
+ (prefix, prefix, prefix))
+ return None, None, None
+
+ return options, args, builder_args
+
+
+def main(argv):
+ """Entry point for git-buildpackage-bb"""
+ retval = 0
+ prefix = "git-"
+ bbfile = None
+ dump_dir = None
+
+ if not bb:
+ return 1
+
+ options, gbp_args, builder_args = parse_args(argv, prefix)
+ if not options:
+ return 1
+
+ try:
+ repo = RpmGitRepository(os.path.curdir)
+ except GitRepositoryError:
+ gbp.log.err("%s is not a git repository" % (os.path.abspath('.')))
+ return 1
+
+ # Determine tree-ish to be exported
+ try:
+ tree = get_tree(repo, options.export)
+ except GbpError as err:
+ gbp.log.err('Failed to determine export treeish: %s' % err)
+ return 1
+ # Re-parse config options with using the per-tree config file(s) from the
+ # exported tree-ish
+ options, gbp_args, builder_args = parse_args(argv, prefix, tree)
+
+ branch = get_current_branch(repo)
+
+ try:
+ tinfoil = init_tinfoil(config_only=True)
+ #bb_cfg_data = bb.data.createCopy(tinfoil.config_data)
+ except GbpError:
+ tinfoil = None
+
+ # Use naive parsing because repository might only have .bb file
+ gbp.log.info("Using naive standalone parsing of recipes in package repo.")
+ bb_cfg_data = None
+
+ try:
+ tree = guess_export_params(repo, options)
+
+ Command(options.cleaner, shell=True)()
+ if not options.ignore_new:
+ (ret, out) = repo.is_clean(options.ignore_untracked)
+ if not ret:
+ gbp.log.err("You have uncommitted changes in your source tree:")
+ gbp.log.err(out)
+ raise GbpError("Use --git-ignore-new or --git-ignore-untracked "
+ "to ignore.")
+
+ if not options.ignore_new and not options.ignore_branch:
+ if branch != options.packaging_branch:
+ gbp.log.err("You are not on branch '%s' but on '%s'" %
+ (options.packaging_branch, branch))
+ raise GbpError("Use --git-ignore-branch to ignore or "
+ "--git-packaging-branch to set the branch name.")
+
+ if not options.tag_only:
+ # Dump/parse meta to export dir
+ if options.export_dir:
+ export_dir = os.path.abspath(options.export_dir)
+ else:
+ export_dir = guess_export_dir(options, tinfoil, repo, tree)
+ gbp.log.info("Dumping meta from tree '%s' to '%s'" %
+ (options.export, export_dir))
+ bbfile = dump_meta(bb_cfg_data, options, repo, tree,
+ export_dir)
+
+ # Setup builder opts
+ setup_builder(options, builder_args)
+
+ if is_native(repo, options) and bbfile.getVar('SRCREV') == 'HEAD':
+ # Update SRCREV for native packages that are exported from
+ # pristine repository
+ BBFile.set_var_val(bbfile.bb_path, 'SRCREV',
+ repo.rev_parse(tree))
+
+ # TODO: Re-design the handling of native packages. Updating
+ # SRCREV must probably be more explicit
+ if options.patch_export:
+ # Generate patches, if requested
+ if options.patch_export_rev:
+ patch_tree = get_tree(repo, options.patch_export_rev)
+ else:
+ patch_tree = tree
+ export_patches(repo, bbfile, patch_tree, options)
+
+ # Run postexport hook
+ if options.postexport:
+ RunAtCommand(options.postexport, shell=True,
+ extra_env={'GBP_GIT_DIR': repo.git_dir,
+ 'GBP_TMP_DIR': export_dir}
+ )(dir=export_dir)
+ # Do actual build
+ if not options.no_build:
+ if options.prebuild:
+ RunAtCommand(options.prebuild, shell=True,
+ extra_env={'GBP_GIT_DIR': repo.git_dir,
+ 'GBP_BUILD_DIR': export_dir}
+ )(dir=export_dir)
+
+ # Unlock cooker so that we are able to run external bitbake
+ if options.builder == 'bitbake' and tinfoil:
+ bb.utils.unlockfile(tinfoil.cooker.lock)
+
+ # Finally build the package:
+ bb_path = bbfile.getVar('FILE', True)
+ builder_args.extend(['-b', bb_path])
+ RunAtCommand(options.builder, builder_args, shell=True,
+ extra_env={'GBP_BUILD_DIR': export_dir})()
+
+ if options.postbuild:
+ Command(options.postbuild, shell=True,
+ extra_env={'GBP_BUILD_DIR': export_dir})()
+ else:
+ # Tag-only: we just need to parse the meta
+ bbfile = parse_bb(bb_cfg_data, options, repo, tree)
+
+ # Tag (note: tags the exported version)
+ if options.tag or options.tag_only:
+ version = pkg_version(bbfile)
+ gbp.log.info("Tagging %s" %
+ RpmPkgPolicy.compose_full_version(version))
+ commit_info = repo.get_commit_info(tree)
+ tag = packaging_tag_name(repo, version, commit_info, options)
+ if options.retag and repo.has_tag(tag):
+ repo.delete_tag(tag)
+ create_packaging_tag(repo, tag, commit=tree, version=version,
+ options=options)
+ vcs_info = get_vcs_info(repo, tag)
+ if options.posttag:
+ sha = repo.rev_parse("%s^{}" % tag)
+ Command(options.posttag, shell=True,
+ extra_env={'GBP_TAG': tag,
+ 'GBP_BRANCH': branch,
+ 'GBP_SHA1': sha})()
+ else:
+ vcs_info = get_vcs_info(repo, tree)
+ # TODO: Put VCS information to recipe
+ if options.bb_vcs_info:
+ raise GbpError("Injecting VCS info into recipe not yet supported")
+
+ except CommandExecFailed:
+ retval = 1
+ except GitRepositoryError as err:
+ gbp.log.err("Git command failed: %s" % err)
+ retval = 1
+ except GbpAutoGenerateError as err:
+ if len(err.__str__()):
+ gbp.log.err(err)
+ retval = 2
+ except GbpError, err:
+ if len(err.__str__()):
+ gbp.log.err(err)
+ retval = 1
+ finally:
+ drop_index(repo)
+ if dump_dir and os.path.exists(dump_dir):
+ shutil.rmtree(dump_dir)
+
+ if not options.tag_only:
+ if bbfile and options.notify:
+ summary = "GBP buildpackage-bb %s" % \
+ ["failed", "successful"][not retval]
+ message = ("Build of %s %s %s" % (bbfile.getVar('PN', True),
+ RpmPkgPolicy.compose_full_version(pkg_version(bbfile)),
+ ["failed", "succeeded"][not retval]))
+ if not gbp.notifications.notify(summary, message, options.notify):
+ gbp.log.err("Failed to send notification")
+ retval = 1
+
+ return retval
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/gbp/scripts/buildpackage_rpm.py b/gbp/scripts/buildpackage_rpm.py
index 8fb0b2a4..56db5e13 100755
--- a/gbp/scripts/buildpackage_rpm.py
+++ b/gbp/scripts/buildpackage_rpm.py
@@ -621,7 +621,7 @@ def main(argv):
# Get/build the orig tarball
if is_native(repo, options):
- if spec.orig_src and not options.no_create_orig:
+ if spec.orig_src:
# Just build source archive from the exported tree
gbp.log.info("Creating (native) source archive %s from '%s'" % (spec.orig_src['filename'], tree))
if spec.orig_src['compression']:
diff --git a/gbp/scripts/clone_bb.py b/gbp/scripts/clone_bb.py
new file mode 100755
index 00000000..a7e9c9f4
--- /dev/null
+++ b/gbp/scripts/clone_bb.py
@@ -0,0 +1,174 @@
+# vim: set fileencoding=utf-8 :
+#
+# (C) 2009,2010 Guido Guenther <agx@sigxcpu.org>
+# (C) 2014 Intel Corporation <markus.lehtonen@linux.intel.com>
+# 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
+#
+# inspired by dom-git-checkout
+#
+"""Clone a package Git repository from a bitbake-based distro"""
+
+import ConfigParser
+import re
+import sys
+import os, os.path
+
+from gbp.config import (GbpOptionParser, GbpOptionGroup)
+from gbp.git import GitRepositoryError
+from gbp.errors import GbpError
+import gbp.log
+from gbp.rpm.git import RpmGitRepository as GitRepository
+from gbp.bb import bb, init_tinfoil, guess_pkg
+
+# pylint: disable=bad-continuation
+
+
+def guess_remote(tinfoil, source):
+ """Guess the remote repository URL"""
+ # Try to determine if a remote URL is referenced
+ if re.match(r'[a-z]{3,5}://', source) or re.match(r'\S+@\S+', source):
+ return source, None
+
+ # Get remote repo from recipe
+ recipe = guess_pkg(tinfoil, source)
+ appends = tinfoil.cooker.collection.get_file_appends(recipe)
+ gbp.log.info("Using %s with appends %s" % (recipe, appends))
+ pkg_data = bb.cache.Cache.loadDataFull(recipe, appends, tinfoil.config_data)
+ uri = pkg_data.getVar('GBP_PACKAGING_REPO', True)
+ if not uri:
+ raise GbpError("GBP_PACKAGING_REPO not defined in recipe. Unable to "
+ "determine remote repo")
+ rev = pkg_data.getVar('GBP_PACKAGING_REV', True)
+ return uri, rev
+
+
+def build_parser(name):
+ """Create command line argument parser"""
+ try:
+ parser = GbpOptionParser(command=os.path.basename(name), prefix='',
+ usage='%prog [options] repository - clone a '
+ 'remote per-package repository')
+ except ConfigParser.ParsingError as err:
+ gbp.log.err(err)
+ return None
+
+ branch_group = GbpOptionGroup(parser, "branch options",
+ "branch tracking and layout options")
+ parser.add_option_group(branch_group)
+
+ branch_group.add_option("--all", action="store_true", dest="all",
+ help="track all branches, not only packaging and upstream")
+ branch_group.add_config_file_option(option_name="upstream-branch",
+ dest="upstream_branch")
+ branch_group.add_config_file_option(option_name="packaging-branch",
+ dest="packaging_branch")
+ branch_group.add_option("--depth", action="store", dest="depth", default=0,
+ help="git history depth (for creating shallow clones)")
+
+ parser.add_option("-v", "--verbose", action="store_true", dest="verbose",
+ help="verbose command execution")
+ parser.add_config_file_option(option_name="color", dest="color",
+ type='tristate')
+ parser.add_config_file_option(option_name="color-scheme",
+ dest="color_scheme")
+ return parser
+
+
+def parse_args (argv):
+ """Parse command line arguments"""
+ parser = build_parser(argv[0])
+ if not parser:
+ return None, None
+
+ (options, args) = parser.parse_args(argv)
+ gbp.log.setup(options.color, options.verbose, options.color_scheme)
+ return (options, args)
+
+
+def main(argv):
+ """Entry point for gbp-clone-bb"""
+ retval = 0
+
+ if not bb:
+ return 1
+
+ (options, args) = parse_args(argv)
+ if not options:
+ return 1
+
+ if len(args) < 2:
+ gbp.log.err("Need a package or repository to clone.")
+ return 1
+
+ # Determine target dir
+ clone_to = os.path.curdir
+ auto_name = False
+ if len(args) < 3:
+ if 'BUILDDIR' in os.environ:
+ clone_to = os.path.join(os.environ['BUILDDIR'], 'devel')
+ auto_name = True
+ else:
+ clone_to = args[2]
+
+ try:
+ tinfoil = init_tinfoil()
+
+ source, revision = guess_remote(tinfoil, args[1])
+
+ gbp.log.info("Cloning from %s..." % source)
+ repo = GitRepository.clone(clone_to, source, options.depth,
+ auto_name=auto_name)
+ os.chdir(repo.path)
+
+ # Reparse the config files of the cloned repository so we pick up the
+ # branch information from there:
+ (options, args) = parse_args(argv)
+
+ # Track all branches:
+ if options.all:
+ remotes = repo.get_remote_branches()
+ for remote in remotes:
+ local = remote.replace("origin/", "", 1)
+ if not repo.has_branch(local) and local != "HEAD":
+ repo.create_branch(local, remote)
+ else: # only track gbp's default branches
+ branches = [ options.packaging_branch, options.upstream_branch ]
+ gbp.log.debug('Will track branches: %s' % branches)
+ for branch in branches:
+ remote = 'origin/%s' % branch
+ if repo.has_branch(remote, remote=True) and \
+ not repo.has_branch(branch):
+ repo.create_branch(branch, remote)
+
+ gbp.log.info("Successfully cloned into %s" % clone_to)
+ if (revision and repo.rev_parse('HEAD') !=
+ repo.rev_parse('%s^0' % revision)):
+ gbp.log.info("Checking out revision %s" % revision)
+ repo.set_branch(revision)
+
+ except GitRepositoryError as err:
+ gbp.log.err("Git command failed: %s" % err)
+ retval = 1
+ except GbpError as err:
+ if len(err.__str__()):
+ gbp.log.err(err)
+ retval = 1
+
+ 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\:·:
diff --git a/gbp/scripts/import_bb.py b/gbp/scripts/import_bb.py
new file mode 100755
index 00000000..d0aeae1c
--- /dev/null
+++ b/gbp/scripts/import_bb.py
@@ -0,0 +1,419 @@
+# vim: set fileencoding=utf-8 :
+#
+# (C) 2014 Intel Corporation <markus.lehtonen@linux.intel.com>
+# 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
+"""Import an RPM package in Bitbake format"""
+
+import ConfigParser
+import sys
+import os
+import shutil
+
+import gbp.tmpfile as tempfile
+import gbp.command_wrappers as gbpc
+import gbp.log
+from gbp.rpm import RpmUpstreamSource
+from gbp.rpm.policy import RpmPkgPolicy
+from gbp.rpm.git import RpmGitRepository, GitRepositoryError
+from gbp.config import (GbpOptionParserBB, GbpOptionGroup,
+ no_upstream_branch_msg)
+from gbp.errors import GbpError
+from gbp.pkg import parse_archive_filename
+from gbp.scripts.import_srpm import move_tag_stamp, force_to_branch_head
+from gbp.bb import bb, init_tinfoil, pkg_version, guess_pkg
+
+# pylint: disable=bad-continuation
+
+NO_PACKAGING_BRANCH_MSG = """
+Repository does not have branch '%s' for meta/packaging files.
+You need to reate it or use --packaging-branch to specify it.
+"""
+
+class SkipImport(Exception):
+ """Nothing imported"""
+ pass
+
+def set_bare_repo_options(options):
+ """Modify options for import into a bare repository"""
+ if options.pristine_tar:
+ gbp.log.info("Bare repository: setting %s option '--no-pristine-tar'")
+ options.pristine_tar = False
+
+
+def build_parser(name):
+ """Create command line parser"""
+ try:
+ parser = GbpOptionParserBB(command=os.path.basename(name),
+ prefix='',
+ usage='%prog [options] /path/to/package'
+ '.src.rpm')
+ except ConfigParser.ParsingError, err:
+ gbp.log.err(err)
+ return None
+
+ import_group = GbpOptionGroup(parser, "import options",
+ "pristine-tar and filtering")
+ tag_group = GbpOptionGroup(parser, "tag options",
+ "options related to git tag creation")
+ branch_group = GbpOptionGroup(parser, "version and branch naming options",
+ "version number and branch layout options")
+
+ for group in [import_group, branch_group, tag_group ]:
+ parser.add_option_group(group)
+
+ parser.add_option("-v", "--verbose", action="store_true", dest="verbose",
+ default=False, help="verbose command execution")
+ parser.add_config_file_option(option_name="color", dest="color",
+ type='tristate')
+ parser.add_config_file_option(option_name="color-scheme",
+ dest="color_scheme")
+ parser.add_config_file_option(option_name="tmp-dir", dest="tmp_dir")
+ parser.add_config_file_option(option_name="vendor", action="store",
+ dest="vendor")
+ branch_group.add_config_file_option(option_name="packaging-branch",
+ dest="packaging_branch")
+ branch_group.add_config_file_option(option_name="upstream-branch",
+ dest="upstream_branch")
+ branch_group.add_option("--upstream-vcs-tag", dest="vcs_tag",
+ help="Upstream VCS tag on top of which to import "
+ "the orig sources")
+ branch_group.add_boolean_config_file_option(
+ option_name="create-missing-branches",
+ dest="create_missing_branches")
+
+ tag_group.add_boolean_config_file_option(option_name="sign-tags",
+ dest="sign_tags")
+ tag_group.add_config_file_option(option_name="keyid",
+ dest="keyid")
+ tag_group.add_config_file_option(option_name="packaging-tag",
+ dest="packaging_tag")
+ tag_group.add_config_file_option(option_name="upstream-tag",
+ dest="upstream_tag")
+
+ import_group.add_config_file_option(option_name="filter",
+ dest="filters", action="append")
+ import_group.add_boolean_config_file_option(option_name="pristine-tar",
+ dest="pristine_tar")
+ import_group.add_option("--allow-same-version", action="store_true",
+ dest="allow_same_version", default=False,
+ help="allow to import already imported version")
+ import_group.add_config_file_option(option_name="meta-dir",
+ dest="meta_dir")
+ return parser
+
+def parse_args(argv):
+ """Parse commandline arguments"""
+ parser = build_parser(argv[0])
+ if not parser:
+ return None, None
+
+ (options, args) = parser.parse_args(argv[1:])
+ gbp.log.setup(options.color, options.verbose, options.color_scheme)
+ return options, args
+
+
+def init_repo(path):
+ """Check and initialize Git repository"""
+ try:
+ repo = RpmGitRepository(path)
+ clean, out = repo.is_clean()
+ if not clean and not repo.is_empty():
+ gbp.log.err("Repository has uncommitted changes, commit "
+ "these first:")
+ gbp.log.err(out)
+ raise GbpError
+ except GitRepositoryError:
+ gbp.log.info("No git repository found, creating one in %s" % path)
+ repo = RpmGitRepository.create(path)
+ return repo
+
+def recursive_copy(src, dst):
+ """Recursive copy, overwriting files and preserving symlinks"""
+ # Remove existing destinations, if needed
+ if os.path.isfile(dst) or os.path.islink(dst):
+ os.unlink(dst)
+ elif (os.path.isfile(src) or os.path.islink(src)) and os.path.isdir(dst):
+ # Remove dst dir if src is a file
+ shutil.rmtree(dst)
+
+ try:
+ if os.path.islink(src):
+ os.symlink(os.readlink(src), dst)
+ elif os.path.isdir(src):
+ if not os.path.exists(dst):
+ os.makedirs(dst)
+ for fname in os.listdir(src):
+ recursive_copy(os.path.join(src, fname),
+ os.path.join(dst, fname))
+ else:
+ shutil.copy2(src, dst)
+ except (IOError, OSError) as err:
+ raise GbpError("Error while copying '%s' to '%s': %s" % (src, dst, err))
+
+def guess_upstream_source(pkg_data, remotes):
+ """Guess the primary upstream source archive."""
+ orig = None
+ name = pkg_data.getVar('PN', True)
+
+ for fetch_data in remotes:
+ if fetch_data.type == 'git':
+ orig = fetch_data
+ else:
+ path = fetch_data.localpath
+ fname = os.path.basename(path)
+ fn_base, archive_fmt, _ = parse_archive_filename(fname)
+ if fn_base.startswith(name) and archive_fmt:
+ # Take an archive that starts with pkg name
+ orig = fetch_data
+ # otherwise we take the first archive
+ elif not orig and archive_fmt:
+ orig = fetch_data
+ # else don't accept
+ return orig
+
+def bb_get_files(pkg_data, tgt_dir, whole_dir=False, download=True):
+ """Get (local) packaging files"""
+ uris = (pkg_data.getVar('SRC_URI', True) or "").split()
+ try:
+ fetch = bb.fetch2.Fetch(uris, pkg_data)
+ if download:
+ gbp.log.info("Fetching sources...")
+ fetch.download()
+ except bb.fetch2.BBFetchException as err:
+ raise GbpError("Failed to fetch packaging files: %s" % err)
+
+ # Copy local files to target directory
+ bb_dir = os.path.dirname(pkg_data.getVar('FILE', True))
+ remote = []
+ local = [path for path in pkg_data.getVar('BBINCLUDED', True).split() if
+ path.startswith(bb_dir) and os.path.exists(path)]
+ for url in fetch.urls:
+ path = fetch.localpath(url)
+ if path.startswith(bb_dir):
+ if not whole_dir:
+ gbp.log.debug("Found local meta file '%s'" % path)
+ local.append(path)
+ else:
+ gbp.log.debug("Found remote file '%s'" % path)
+ remote.append(fetch.ud[url])
+
+ if whole_dir:
+ # Simply copy whole meta dir, if requested
+ recursive_copy(bb_dir, tgt_dir)
+ else:
+ for path in local:
+ relpath = os.path.relpath(path, bb_dir)
+ subdir = os.path.join(tgt_dir, os.path.dirname(relpath))
+ if not os.path.exists(subdir):
+ os.makedirs(subdir)
+ shutil.copy2(path, os.path.join(tgt_dir, relpath))
+
+ return remote
+
+def import_upstream_archive(repo, pkg_data, fetch_data, dirs, options):
+ """Import upstream sources from archive"""
+ # Unpack orig source archive
+ path = fetch_data.localpath
+ sources = RpmUpstreamSource(path)
+ sources = sources.unpack(dirs['origsrc'], options.filters)
+
+ tag_str_fields = dict(pkg_version(pkg_data), vendor=options.vendor.lower())
+ tag = repo.version_to_tag(options.upstream_tag, tag_str_fields)
+ if not repo.has_tag(tag):
+ gbp.log.info("Tag %s not found, importing upstream sources" % tag)
+ branch = options.upstream_branch
+
+ msg = "Upstream version %s" % tag_str_fields['upstreamversion']
+ if options.vcs_tag:
+ parents = [repo.rev_parse("%s^{}" % options.vcs_tag)]
+ else:
+ parents = None
+ commit = repo.commit_dir(sources.unpacked, "Imported %s" % msg,
+ branch, other_parents=parents,
+ create_missing_branch=options.create_missing_branches)
+ repo.create_tag(name=tag, msg=msg, commit=commit,
+ sign=options.sign_tags, keyid=options.keyid)
+
+ if options.pristine_tar:
+ archive_fmt = parse_archive_filename(path)[1]
+ if archive_fmt == 'tar':
+ repo.pristine_tar.commit(path, 'refs/heads/%s' % branch)
+ else:
+ gbp.log.warn('Ignoring pristine-tar, %s archives '
+ 'not supported' % archive_fmt)
+ return repo.rev_parse('%s^0' % tag)
+
+def import_upstream_git(repo, fetch_data, options):
+ """Import upstream sources from Git"""
+ # Fetch from local cached repo
+ for branch in fetch_data.branches.values():
+ repo.fetch(repo=fetch_data.localpath, refspec=branch)
+
+ commit = fetch_data.revision
+ repo.update_ref('refs/heads/' + options.upstream_branch, commit)
+ return commit
+
+def import_upstream_sources(repo, pkg_data, remotes, dirs, options):
+ """Import upstream sources to Git"""
+ fetch_data = guess_upstream_source(pkg_data, remotes)
+ if fetch_data:
+ gbp.log.debug("Using upstream source '%s'" % fetch_data.url)
+ else:
+ gbp.log.info("No orig source archive imported")
+ return
+
+ if not repo.has_branch(options.upstream_branch):
+ if options.create_missing_branches:
+ gbp.log.info("Will create missing branch '%s'" %
+ options.upstream_branch)
+ else:
+ gbp.log.err(no_upstream_branch_msg % options.upstream_branch + "\n"
+ "Also check the --create-missing-branches option.")
+ raise GbpError
+
+ if fetch_data.type == 'git':
+ return import_upstream_git(repo, fetch_data, options)
+ else:
+ return import_upstream_archive(repo, pkg_data, fetch_data, dirs,
+ options)
+
+
+def main(argv):
+ """Main function of the gbp import-bb script"""
+ dirs = dict(top=os.path.abspath(os.curdir))
+ ret = 0
+ skipped = False
+
+ if not bb:
+ return 1
+
+ options, args = parse_args(argv)
+
+ if len(args) == 0 or len(args) > 2:
+ gbp.log.err("Need to give exactly one package to import. Try --help.")
+ return 1
+
+ try:
+ dirs['tmp_base'] = tempfile.mkdtemp(dir=options.tmp_dir,
+ prefix='import-bb')
+ tinfoil = init_tinfoil()
+ pkg_bb = guess_pkg(tinfoil, args[0])
+ dirs['src'] = os.path.abspath(os.path.dirname(pkg_bb))
+ gbp.log.info("Importing '%s' from '%s'" %
+ (os.path.basename(pkg_bb), dirs['src']))
+
+ pkg_data = bb.cache.Cache.loadDataFull(pkg_bb, [], tinfoil.config_data)
+
+ # Determine target repo dir
+ target_dir = ''
+ if len(args) == 2:
+ target_dir = args[1]
+ else:
+ if 'BUILDDIR' in os.environ:
+ target_dir = os.path.join(os.environ['BUILDDIR'], 'devel')
+ target_dir = os.path.join(target_dir, pkg_data.getVar('PN', True))
+
+ # Check the Git repository state
+ repo = init_repo(target_dir)
+ if repo.bare:
+ set_bare_repo_options(options)
+ if repo.is_empty():
+ options.create_missing_branches = True
+ os.chdir(repo.path)
+
+ # Create more tempdirs
+ dirs['origsrc'] = tempfile.mkdtemp(dir=dirs['tmp_base'],
+ prefix='origsrc_')
+ dirs['packaging_base'] = tempfile.mkdtemp(dir=dirs['tmp_base'],
+ prefix='packaging_')
+ dirs['packaging'] = os.path.join(dirs['packaging_base'],
+ options.meta_dir)
+
+ # Copy (local) packaging files to tmp dir
+ remote_srcs = bb_get_files(pkg_data, dirs['packaging'])
+
+ version_dict = pkg_version(pkg_data)
+ tag_str_fields = dict(version_dict, vendor=options.vendor.lower())
+ ver_str = RpmPkgPolicy.compose_full_version(version_dict)
+
+ # Check if the same version of the package is already imported
+ if repo.find_version(options.packaging_tag, tag_str_fields):
+ gbp.log.warn("Version %s already imported." % ver_str)
+ if options.allow_same_version:
+ gbp.log.info("Moving tag of version '%s' since import forced" %
+ ver_str)
+ move_tag_stamp(repo, options.packaging_tag, tag_str_fields)
+ else:
+ raise SkipImport
+
+ # Import upstream sources
+ import_upstream_sources(repo, pkg_data, remote_srcs, dirs, options)
+
+ # Import packaging files
+ gbp.log.info("Importing local meta/packaging files")
+ branch = options.packaging_branch
+ if not repo.has_branch(branch):
+ if options.create_missing_branches:
+ gbp.log.info("Will create missing branch '%s'" % branch)
+ else:
+ gbp.log.err(NO_PACKAGING_BRANCH_MSG % branch + "\n"
+ "Also check the --create-missing-branches "
+ "option.")
+ raise GbpError
+
+ tag = repo.version_to_tag(options.packaging_tag, tag_str_fields)
+ msg = "%s release %s" % (options.vendor, ver_str)
+
+ commit = repo.commit_dir(dirs['packaging_base'],
+ "Imported %s" % msg,
+ branch,
+ create_missing_branch=options.create_missing_branches)
+
+ # Create packaging tag
+ repo.create_tag(name=tag,
+ msg=msg,
+ commit=commit,
+ sign=options.sign_tags,
+ keyid=options.keyid)
+
+ force_to_branch_head(repo, options.packaging_branch)
+
+ except KeyboardInterrupt:
+ ret = 1
+ gbp.log.err("Interrupted. Aborting.")
+ except gbpc.CommandExecFailed:
+ ret = 1
+ except GitRepositoryError as err:
+ gbp.log.err("Git command failed: %s" % err)
+ ret = 1
+ except GbpError as err:
+ if len(err.__str__()):
+ gbp.log.err(err)
+ ret = 1
+ except SkipImport:
+ skipped = True
+ finally:
+ os.chdir(dirs['top'])
+ gbpc.RemoveTree(dirs['tmp_base'])()
+
+ if not ret and not skipped:
+ gbp.log.info("Version '%s' imported under '%s'" %
+ (ver_str, repo.path))
+ return ret
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
+
+# vim:et:ts=4:sw=4:et:sts=4:ai:set list listchars=tab\:»·,trail\:·:
diff --git a/gbp/scripts/import_orig.py b/gbp/scripts/import_orig.py
index 9d183b6d..56b1d9dc 100644
--- a/gbp/scripts/import_orig.py
+++ b/gbp/scripts/import_orig.py
@@ -270,12 +270,6 @@ def main(argv):
source, pkg_name, version, prepare_pristine, options.filters,
options.filter_pristine_tar, None, tmpdir)
- # Prepare sources for importing
- pristine_name = pristine_tarball_name(source, pkg_name, version)
- prepare_pristine = pristine_name if options.pristine_tar else None
- unpacked_orig, pristine_orig = prepare_sources(
- source, pkg_name, version, prepare_pristine, options.filters,
- options.filter_pristine_tar, None, tmpdir)
# Don't mess up our repo with git metadata from an upstream tarball
try:
if os.path.isdir(os.path.join(unpacked_orig, '.git/')):
diff --git a/gbp/scripts/pq_bb.py b/gbp/scripts/pq_bb.py
new file mode 100755
index 00000000..c8247bc8
--- /dev/null
+++ b/gbp/scripts/pq_bb.py
@@ -0,0 +1,427 @@
+# vim: set fileencoding=utf-8 :
+#
+# (C) 2011 Guido Günther <agx@sigxcpu.org>
+# (C) 2012 Intel Corporation <markus.lehtonen@linux.intel.com>
+# 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
+#
+"""manage patches in a patch queue"""
+
+import ConfigParser
+import errno
+import os
+import shutil
+import sys
+
+import gbp.tmpfile as tempfile
+from gbp.config import GbpOptionParserBB
+from gbp.rpm.git import GitRepositoryError, RpmGitRepository
+from gbp.command_wrappers import GitCommand, CommandExecFailed
+from gbp.errors import GbpError
+import gbp.log
+from gbp.patch_series import PatchSeries, Patch
+from gbp.rpm import string_to_int
+from gbp.scripts.common.pq import (is_pq_branch, pq_branch_name, pq_branch_base,
+ apply_and_commit_patch, drop_pq)
+from gbp.scripts.pq_rpm import (generate_patches, safe_patches,
+ import_extra_files)
+from gbp.bb import bb, init_tinfoil, parse_bb, pkg_version
+
+# pylint: disable=bad-continuation
+
+USAGE_STRING = \
+"""%prog [options] action - maintain patches on a patch queue branch
+tions:
+export Export the patch queue / devel branch associated to the
+ current branch into a patch series in and update the recipe file
+import Create a patch queue / devel branch from recipe file
+ and patches in current dir.
+rebase Switch to patch queue / devel branch associated to the current
+ branch and rebase against upstream.
+drop Drop (delete) the patch queue /devel branch associated to
+ the current branch.
+apply Apply a patch
+switch Switch to patch-queue branch and vice versa."""
+
+
+def rm_patch_files(bbfile):
+ """Delete the patch files listed in the pkg meta data."""
+ unlinked = set()
+
+ # Go through local files
+ for path in bbfile.localfiles:
+ if path.endswith('.patch'):
+ gbp.log.debug("Removing patch '%s'" % path)
+ unlinked.add(os.path.basename(path))
+ try:
+ os.unlink(path)
+ except OSError as err:
+ if err.errno != errno.ENOENT:
+ raise GbpError("Failed to remove patch: %s" % err)
+ else:
+ gbp.log.debug("Patch %s does not exist." % path)
+ else:
+ gbp.log.debug("Unlink skipping non-local/non-patch file %s" % path)
+ uris = (bbfile.getVar('SRC_URI', False) or "").split()
+ return [uri for uri in uris if os.path.basename(uri) not in unlinked]
+
+
+def update_patch_series(repo, bbfile, start, end, options):
+ """Export patches to packaging directory and update recipe file"""
+ squash = options.patch_export_squash_until.split(':', 1)
+ if len(squash) == 1:
+ squash.append(None)
+ else:
+ squash[1] += '.diff'
+
+ # Unlink old (local) patch files and generate new patches
+ rm_patch_files(bbfile)
+
+ # Guess patch subdir
+ bb_dir = os.path.dirname(bbfile.getVar('FILE', True))
+ pkg_name = bbfile.getVar('PN', True)
+ pkg_ver = bbfile.getVar('PV', True)
+ subdir = pkg_name + '-' + pkg_ver
+ if not os.path.isdir(os.path.join(bb_dir, subdir)):
+ if os.path.isdir(os.path.join(bb_dir, pkg_name)):
+ subdir = pkg_name
+ elif os.path.isdir(os.path.join(bb_dir, 'files')):
+ subdir = 'files'
+ tgt_dir = os.path.join(bb_dir, subdir)
+
+ patches, _commands = generate_patches(repo, start, squash, end,
+ tgt_dir, options)
+ # TODO: implement commands processing (e.g. topic)
+ new_uris = ['file://' + patch for patch in patches]
+ bbfile.substitute_var_val(bbfile.bb_path, 'SRC_URI', r'file://\S+.\.patch',
+ '')
+ bbfile.append_var_val(bbfile.bb_path, 'SRC_URI', new_uris)
+ return patches
+
+def var_to_str(var, value):
+ """Create a well formatted string buffer for a variable assignment"""
+ indent = ' ' * (len(var) + 3)
+ linebuf = ['%s = "%s \\\n' % (var, value[0])]
+ for val in value[1:]:
+ linebuf.append(indent + ' ' + val + '\\\n')
+ linebuf.append(indent + '"\n')
+ return linebuf
+
+
+def find_upstream_commit(repo, bbfile, upstream_tag):
+ """Find commit corresponding upstream version"""
+ src_rev = bbfile.getVar('SRCREV', True)
+ if src_rev and src_rev != 'INVALID':
+ return bbfile.getVar('SRCREV', True)
+
+ # Find tag
+ upstreamversion = bbfile.getVar('PV', True)
+ tag_str_fields = {'upstreamversion': upstreamversion,
+ 'vendor': 'Upstream'}
+ upstream_commit = repo.find_version(upstream_tag, tag_str_fields)
+ if not upstream_commit:
+ raise GbpError("Couldn't find upstream version %s" % upstreamversion)
+ return upstream_commit
+
+
+def export_patches(cfg, repo, options):
+ """Export patches from the pq branch into a packaging branch"""
+ current = repo.get_branch()
+ if is_pq_branch(current, options):
+ base = pq_branch_base(current, options)
+ gbp.log.info("On branch '%s', switching to '%s'" % (current, base))
+ repo.set_branch(base)
+ bbfile = parse_bb(cfg, options, repo)
+ pq_branch = current
+ else:
+ bbfile = parse_bb(cfg, options, repo)
+ pq_branch = pq_branch_name(current, options, pkg_version(bbfile))
+ upstream_commit = find_upstream_commit(repo, bbfile, options.upstream_tag)
+
+ export_treeish = options.export_rev if options.export_rev else pq_branch
+
+ update_patch_series(repo, bbfile, upstream_commit, export_treeish, options)
+
+ bb_dir = os.path.dirname(bbfile.getVar('FILE', True))
+ GitCommand('status')(['--', bb_dir])
+
+
+def bb_to_patch_series(bbfile):
+ """Get all local patches as a series"""
+ series = PatchSeries()
+ for path in bbfile.localfiles:
+ if path.endswith('.patch'):
+ series.append(Patch(path))
+ return series
+
+
+def import_bb_patches(cfg, repo, options):
+ """Apply a series of patches in a recipe to branch onto a pq branch"""
+ current = repo.get_branch()
+
+ if is_pq_branch(current, options):
+ base = pq_branch_base(current, options)
+ raise GbpError("Already on a patch-queue branch '%s' - doing "
+ "nothing." % current)
+ else:
+ bbfile = parse_bb(cfg, options, repo)
+ base = current
+ upstream_commit = find_upstream_commit(repo, bbfile, options.upstream_tag)
+ pq_branch = pq_branch_name(base, options, pkg_version(bbfile))
+
+ # Create pq-branch
+ if repo.has_branch(pq_branch) and not options.force:
+ raise GbpError("Patch-queue branch '%s' already exists. "
+ "Try 'rebase' instead." % pq_branch)
+ try:
+ if repo.get_branch() == pq_branch:
+ repo.force_head(upstream_commit, hard=True)
+ else:
+ repo.create_branch(pq_branch, upstream_commit, force=True)
+ except GitRepositoryError as err:
+ raise GbpError("Cannot create patch-queue branch '%s': %s" %
+ (pq_branch, err))
+
+ # Put patches in a safe place
+ in_queue = bb_to_patch_series(bbfile)
+ queue = safe_patches(in_queue, options.tmp_dir)
+ # Do import
+ try:
+ gbp.log.info("Switching to branch '%s'" % pq_branch)
+ repo.set_branch(pq_branch)
+ import_extra_files(repo, base, options.import_files)
+
+ if not queue:
+ return
+ gbp.log.info("Trying to apply patches from branch '%s' onto '%s'" %
+ (base, upstream_commit))
+ for patch in queue:
+ gbp.log.debug("Applying %s" % patch.path)
+ apply_and_commit_patch(repo, patch, fallback_author=None)
+ except (GbpError, GitRepositoryError) as err:
+ gbp.log.err('Import failed: %s' % err)
+ repo.force_head('HEAD', hard=True)
+ repo.set_branch(base)
+ repo.delete_branch(pq_branch)
+ raise
+
+ recipe_fn = os.path.basename(bbfile.getVar('FILE', True))
+ gbp.log.info("Patches listed in '%s' imported on '%s'" % (recipe_fn,
+ pq_branch))
+
+
+def rebase_pq(cfg, repo, options):
+ """Rebase pq branch on the correct upstream version"""
+ current = repo.get_branch()
+ if is_pq_branch(current, options):
+ base = pq_branch_base(current, options)
+ bbfile = parse_bb(cfg, options, repo, base)
+ else:
+ base = current
+ bbfile = parse_bb(cfg, options, repo)
+ upstream_commit = find_upstream_commit(repo, bbfile, options.upstream_tag)
+
+ switch_to_pq_branch(cfg, repo, base, options)
+ GitCommand("rebase")([upstream_commit])
+
+
+def switch_pq(cfg, repo, options):
+ """Switch to patch-queue branch if on base branch and vice versa"""
+ current = repo.get_branch()
+ if is_pq_branch(current, options):
+ base = pq_branch_base(current, options)
+ gbp.log.info("Switching to branch '%s'" % base)
+ repo.checkout(base)
+ else:
+ switch_to_pq_branch(cfg, repo, current, options)
+
+
+def drop_pq_bb(cfg, repo, options):
+ """Remove pq branch"""
+ current = repo.get_branch()
+ if is_pq_branch(current, options):
+ base = pq_branch_base(current, options)
+ bbfile = parse_bb(cfg, options, repo, base)
+ else:
+ bbfile = parse_bb(cfg, options, repo)
+ drop_pq(repo, current, options, pkg_version(bbfile))
+
+
+def switch_to_pq_branch(cfg, repo, branch, options):
+ """
+ Switch to patch-queue branch if not already there, create it if it
+ doesn't exist yet
+ """
+ if is_pq_branch(branch, options):
+ return
+
+ bbfile = parse_bb(cfg, options, repo, branch)
+ pq_branch = pq_branch_name(branch, options, pkg_version(bbfile))
+ if not repo.has_branch(pq_branch):
+ raise GbpError("Branch '%s' does not exist" % pq_branch)
+
+ gbp.log.info("Switching to branch '%s'" % pq_branch)
+ repo.set_branch(pq_branch)
+
+def apply_single_patch(cfg, repo, patchfile, options):
+ """Apply a single patch onto the pq branch"""
+ current = repo.get_branch()
+ if not is_pq_branch(current, options):
+ switch_to_pq_branch(cfg, repo, current, options)
+ patch = Patch(patchfile)
+ apply_and_commit_patch(repo, patch, fallback_author=None)
+
+def opt_split_cb(option, opt_str, value, parser):
+ """Split option string into a list"""
+ setattr(parser.values, option.dest, value.split(','))
+
+def build_parser(name):
+ """Create command line argument parser"""
+ try:
+ parser = GbpOptionParserBB(command=os.path.basename(name),
+ prefix='', usage=USAGE_STRING)
+ except ConfigParser.ParsingError as err:
+ gbp.log.err(err)
+ return None
+
+ parser.add_boolean_config_file_option(option_name="patch-numbers",
+ dest="patch_numbers")
+ parser.add_option("-v", "--verbose", action="store_true", dest="verbose",
+ default=False, help="Verbose command execution")
+ parser.add_option("--force", dest="force", action="store_true",
+ default=False,
+ help="In case of import even import if the branch already exists")
+ parser.add_config_file_option(option_name="vendor", action="store",
+ dest="vendor")
+ parser.add_config_file_option(option_name="color", dest="color",
+ type='tristate')
+ parser.add_config_file_option(option_name="color-scheme",
+ dest="color_scheme")
+ parser.add_config_file_option(option_name="tmp-dir", dest="tmp_dir")
+ parser.add_config_file_option(option_name="upstream-tag",
+ dest="upstream_tag")
+ parser.add_config_file_option(option_name="bb-file", dest="bb_file")
+ parser.add_config_file_option(option_name="meta-dir",
+ dest="meta_dir")
+ parser.add_config_file_option(option_name="packaging-branch",
+ dest="packaging_branch",
+ help="Branch the packaging is being maintained on. Only relevant "
+ "if a invariable/single pq-branch is defined, in which case "
+ "this is used as the 'base' branch. Default is "
+ "'%(packaging-branch)s'")
+ parser.add_config_file_option(option_name="pq-branch", dest="pq_branch")
+ parser.add_config_file_option(option_name="import-files",
+ dest="import_files", type="string", action="callback",
+ callback=opt_split_cb)
+ parser.add_option("--export-rev", action="store", dest="export_rev",
+ default="",
+ help="Export patches from treeish object TREEISH instead of head "
+ "of patch-queue branch", metavar="TREEISH")
+ parser.add_config_file_option("patch-export-compress",
+ dest="patch_export_compress")
+ parser.add_config_file_option("patch-export-squash-until",
+ dest="patch_export_squash_until")
+ parser.add_config_file_option("patch-export-ignore-path",
+ dest="patch_export_ignore_path")
+ return parser
+
+def parse_args(argv):
+ """Parse command line arguments"""
+ parser = build_parser(argv[0])
+ if not parser:
+ return None, None
+
+ options, args = parser.parse_args(argv)
+ gbp.log.setup(options.color, options.verbose, options.color_scheme)
+ options.patch_export_compress = string_to_int(options.patch_export_compress)
+
+ return options, args
+
+
+def main(argv):
+ """Main function for the gbp pq-rpm command"""
+ retval = 0
+
+ if not bb:
+ return 1
+
+ options, args = parse_args(argv)
+ if not options:
+ return 1
+
+ if len(args) < 2:
+ gbp.log.err("No action given.")
+ return 1
+ else:
+ action = args[1]
+
+ if args[1] in ["export", "import", "rebase", "drop", "switch"]:
+ pass
+ elif args[1] in ["apply"]:
+ if len(args) != 3:
+ gbp.log.err("No patch name given.")
+ return 1
+ else:
+ patchfile = args[2]
+ else:
+ gbp.log.err("Unknown action '%s'." % args[1])
+ return 1
+
+ try:
+ repo = RpmGitRepository(os.path.curdir)
+ except GitRepositoryError:
+ gbp.log.err("%s is not a git repository" % (os.path.abspath('.')))
+ return 1
+
+ if os.path.abspath('.') != repo.path:
+ gbp.log.warn("Switching to topdir before running commands")
+ os.chdir(repo.path)
+
+ try:
+ # Initialize BitBake
+ tinfoil = init_tinfoil(config_only=True, tracking=True)
+ bb_cfg_data = bb.data.createCopy(tinfoil.config_data)
+
+ # Create base temporary directory for this run
+ options.tmp_dir = tempfile.mkdtemp(dir=options.tmp_dir,
+ prefix='gbp-pq-bb_')
+ if action == "export":
+ export_patches(bb_cfg_data, repo, options)
+ elif action == "import":
+ import_bb_patches(bb_cfg_data, repo, options)
+ elif action == "drop":
+ drop_pq_bb(bb_cfg_data, repo, options)
+ elif action == "rebase":
+ rebase_pq(bb_cfg_data, repo, options)
+ elif action == "apply":
+ apply_single_patch(bb_cfg_data, repo, patchfile, options)
+ elif action == "switch":
+ switch_pq(bb_cfg_data, repo, options)
+ except CommandExecFailed:
+ retval = 1
+ except GitRepositoryError as err:
+ gbp.log.err("Git command failed: %s" % err)
+ retval = 1
+ except GbpError, err:
+ if len(err.__str__()):
+ gbp.log.err(err)
+ retval = 1
+ finally:
+ shutil.rmtree(options.tmp_dir, ignore_errors=True)
+
+ return retval
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
+
diff --git a/gbp/scripts/pq_rpm.py b/gbp/scripts/pq_rpm.py
index e19218a9..32ffadee 100755
--- a/gbp/scripts/pq_rpm.py
+++ b/gbp/scripts/pq_rpm.py
@@ -26,6 +26,7 @@ import sys
import re
import gzip
import bz2
+import tarfile
import subprocess
import gbp.tmpfile as tempfile
from gbp.config import GbpOptionParserRpm
@@ -300,6 +301,9 @@ def safe_patches(queue, tmpdir_base):
gbp.log.debug("Uncompressing '%s'" % os.path.basename(patch.path))
src = uncompressors[comp](patch.path, 'r')
dst_name = os.path.join(tmpdir, os.path.basename(base))
+ if _archive_fmt:
+ tar_name = dst_name
+ dst_name += '.tar'
elif comp:
raise GbpError("Unsupported patch compression '%s', giving up"
% comp)
@@ -311,6 +315,11 @@ def safe_patches(queue, tmpdir_base):
dst.writelines(src)
src.close()
dst.close()
+ if _archive_fmt:
+ t = tarfile.open(dst_name, 'r:')
+ t.extractall(path = tmpdir)
+ t.close()
+ dst_name = tar_name
safequeue.append(patch)
safequeue[-1].path = dst_name
diff --git a/gbp/scripts/submit_bb.py b/gbp/scripts/submit_bb.py
new file mode 100755
index 00000000..7edece5d
--- /dev/null
+++ b/gbp/scripts/submit_bb.py
@@ -0,0 +1,138 @@
+# vim: set fileencoding=utf-8 :
+#
+# (C) 2014 Intel Corporation <markus.lehtonen@linux.intel.com>
+# 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
+#
+"""Create and push submit tag"""
+
+import ConfigParser
+import os
+import sys
+from datetime import datetime
+
+import gbp.log
+from gbp.config import GbpOptionParserBB
+from gbp.errors import GbpError
+from gbp.format import format_msg
+from gbp.git import GitRepository, GitRepositoryError
+
+# pylint: disable=bad-continuation
+
+
+def guess_remote(repo, options):
+ """Guess remote where to push"""
+ if options.remote:
+ return options.remote
+
+ remotes = repo.get_remotes()
+ if not remotes:
+ raise GbpError("Local repo has no remotes configured. Please add one "
+ "or use --remote to define the remote where to push.")
+ elif len(remotes) == 1:
+ return remotes.keys()[0]
+ else:
+ raise GbpError("Local repo has multiple remotes (%s). Don't know which "
+ "one to choose. Use --remote to define where to push." %
+ ', '.join(remotes.keys()))
+
+
+def build_parser(name):
+ """Build command line parser"""
+ usage_str = "%prog [options] - create and push submit tag"
+ try:
+ parser = GbpOptionParserBB(command=os.path.basename(name), prefix='',
+ usage=usage_str)
+ except ConfigParser.ParsingError as err:
+ gbp.log.err(err)
+ return None
+
+ parser.add_option("-v", "--verbose", action="store_true", dest="verbose",
+ help="verbose command execution")
+ parser.add_config_file_option(option_name="color", dest="color",
+ type='tristate')
+ parser.add_config_file_option(option_name="color-scheme",
+ dest="color_scheme")
+ parser.add_option("-m", "--message", dest="message", help="tag message")
+ parser.add_option("-c", "--commit", dest="commit", help="commit to submit",
+ default='HEAD')
+ parser.add_option("-r", "--remote", dest="remote",
+ help="remote where to push")
+ parser.add_config_file_option(option_name="submit-tag", dest="submit_tag")
+ parser.add_config_file_option(option_name="target", dest="target")
+ parser.add_boolean_config_file_option(option_name="sign-tags",
+ dest="sign_tags")
+ parser.add_config_file_option(option_name="keyid", dest="keyid")
+
+ return parser
+
+
+def parse_args(argv):
+ """Parse command line arguments"""
+ parser = build_parser(argv[0])
+ if not parser:
+ return None, None
+ options, args = parser.parse_args(argv)
+
+ gbp.log.setup(options.color, options.verbose, options.color_scheme)
+
+ return (options, args)
+
+
+def main(argv):
+ """Entry point for gbp-submit-bb"""
+ retval = 0
+
+ options, _args = parse_args(argv)
+ if not options:
+ return 1
+
+ try:
+ repo = GitRepository(os.path.curdir)
+ except GitRepositoryError:
+ gbp.log.err("The command must be run under a Git repository")
+ return 1
+
+ try:
+ remote = guess_remote(repo, options)
+
+ tag_fields = {'nowtime': datetime.now().strftime('%Y%m%d.%H%M%S'),
+ 'target': options.target}
+ tag_name = format_msg(options.submit_tag, tag_fields)
+
+ gbp.log.info("Tagging %s" % tag_name)
+ repo.create_tag(tag_name, msg=options.message, commit=options.commit,
+ sign=options.sign_tags, keyid=options.keyid,
+ annotate=True)
+
+ gbp.log.info("Pushing to remote %s" % remote)
+ try:
+ repo.push_tag(remote, tag_name)
+ except GitRepositoryError as err:
+ gbp.log.err(err)
+ gbp.log.info("Removing tag %s" % tag_name)
+ repo.delete_tag(tag_name)
+ raise GbpError("Git push failed!")
+
+ except (GbpError, GitRepositoryError) as err:
+ if len(err.__str__()):
+ gbp.log.err(err)
+ retval = 1
+
+ return retval
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
+