summaryrefslogtreecommitdiff
path: root/gbp
diff options
context:
space:
mode:
Diffstat (limited to 'gbp')
-rw-r--r--gbp/bb/__init__.py501
-rw-r--r--gbp/config.py230
-rw-r--r--gbp/rpm/__init__.py1
-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
11 files changed, 2344 insertions, 99 deletions
diff --git a/gbp/bb/__init__.py b/gbp/bb/__init__.py
new file mode 100644
index 00000000..1efeb221
--- /dev/null
+++ b/gbp/bb/__init__.py
@@ -0,0 +1,501 @@
+# 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
+"""Bitbake helper functionality"""
+
+import os
+import re
+import shutil
+import string
+import subprocess
+import sys
+import tempfile
+from collections import defaultdict
+
+import gbp.log
+from gbp.errors import GbpError
+from gbp.git.repository import GitRepository, GitRepositoryError
+from gbp.scripts.common.buildpackage import dump_tree
+
+bb = None
+
+# pylint: disable=bad-continuation
+
+
+def import_bb():
+ """Import bitbake lib"""
+ bb_bin = subprocess.Popen(['which', 'bitbake'], stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE).communicate()[0]
+ if bb_bin:
+ bb_lib_path = os.path.dirname(bb_bin) + '/../lib'
+ sys.path.insert(0, bb_lib_path)
+ try:
+ return __import__('bb')
+ except ImportError:
+ print "ERROR: Unable to find bitbake/lib, try initializing build " \
+ "environment with the 'oe-init-build-env' script\n"
+ # Return None instead of raising (ImportError) so that building of
+ # this package succeeds in Debian. Otherwise dpkg-buildpackage fails
+ # because of an import error in epydoc.
+ return None
+
+def init_tinfoil(config_only=False, tracking=False):
+ """Initialize the Bitbake tinfoil module"""
+ import bb.tinfoil
+ try:
+ tinfoil = bb.tinfoil.Tinfoil(tracking=tracking)
+ except (SystemExit, bb.BBHandledException):
+ raise GbpError("Failed to initialize tinfoil")
+ tinfoil.prepare(config_only=config_only)
+ return tinfoil
+
+
+def pkg_version(data):
+ """Get package version as a dict"""
+ return {'upstreamversion': data.getVar('PV', True),
+ 'release': data.getVar('PR', True),
+ 'version': data.getVar('PV', True) + '-' + data.getVar('PR', True)}
+
+
+class BBFile(object):
+ """Class representing .bb meta data"""
+ var_ops = r'\+=|=\+|\?=|\?\?=|:=|='
+ vardef_re = re.compile(
+ r'(^(?P<name>\w+)\s*(?P<op>%s)\s*)(?P<value>\S.*)' % var_ops)
+
+
+ def __init__(self, path, cfg_data=None):
+ self.bb_file = os.path.basename(path)
+ self.bb_dir = os.path.abspath(os.path.dirname(path))
+
+ self._pkg_data = None
+ self._variables = {}
+ self.includes = []
+ self.localfiles = []
+
+ if cfg_data is not None:
+ self.parse_bb(path, cfg_data)
+ else:
+ self.naive_parse_bb(path)
+
+ @property
+ def version(self):
+ """Get version information as a dict"""
+ return {'upstreamversion': self.getVar('PV', True),
+ 'release': self.getVar('PR', True)}
+
+ @property
+ def bb_path(self):
+ """Full path of the bb file"""
+ return os.path.join(self.bb_dir, self.bb_file)
+
+ def parse_bb(self, path, cfg_data):
+ """Parse bb meta file"""
+ self._pkg_data = bb.cache.Cache.loadDataFull(path, [], cfg_data)
+
+ # Determine local packaging files
+ uris = (self.getVar('SRC_URI', True) or "").split()
+ fetcher = bb.fetch2.Fetch(uris, self._pkg_data)
+ bb_dir = os.path.dirname(self.getVar('FILE'))
+ # Also check for file existence as fetcher incorrecly returns some
+ # non-existent .bbclass files under the recipe directory
+ self.includes = [path for path in self.getVar('BBINCLUDED').split() if
+ path.startswith(bb_dir) and os.path.exists(path)]
+ self.localfiles = [path for path in fetcher.localpaths() if
+ path.startswith(bb_dir)]
+
+ def naive_parse_bb(self, path):
+ """Naive parsing of standalone recipes"""
+ # Some variable defaults
+ # e.g. take package name and version directly from recipe file name
+ self._variables['FILE'] = os.path.abspath(path)
+ fn_base, _fn_ext = os.path.splitext(os.path.basename(path))
+ split_base = fn_base.rsplit('_', 1)
+ if len(split_base) == 2:
+ self._variables['PN'] = split_base[0]
+ self._variables['PV'] = split_base[1]
+ else:
+ self._variables['PN'] = fn_base
+ self._variables['PV'] = '1.0'
+ self._variables['PR'] = 'r0'
+
+ def var_parse_cb(lines):
+ """Callback function for parsing variables"""
+ unwrapped = self.unwrap_lines(lines)
+ match = self.vardef_re.match(unwrapped)
+ if match:
+ var = match.groupdict()
+ value = self.unquote_val(var['value'])
+
+ if (var['name'] not in self._variables or
+ var['op'] in ('=', ':=')):
+ self._variables[var['name']] = value
+ elif var['op'] in ('+=', '=+'):
+ self._variables[var['name']] += ' ' + value
+ else:
+ splitted = unwrapped.split(None, 1)
+ if (len(splitted) > 1 and
+ splitted[0] in ('include', 'require')):
+ inc_fname = splitted[1].strip()
+ inc_path = os.path.join(os.path.dirname(path),
+ inc_fname)
+ self.includes.append(os.path.abspath(inc_path))
+ return lines + self.parse_file(inc_path, var_parse_cb)
+ return lines
+
+ # Parse variables from file
+ self.parse_file(path, var_parse_cb)
+
+ # Find local files
+ filedirs = [self.getVar('PN') + '-' + self.getVar('PV'),
+ self.getVar('PN'), 'files']
+ uris = (self.getVar('SRC_URI') or "").split()
+ for uri_str in uris:
+ uri = bb.fetch2.URI(uri_str)
+ if uri.scheme == 'file':
+ found = False
+ for path in [os.path.join(self.bb_dir, dirn, uri.path) for dirn
+ in filedirs]:
+ if os.path.exists(path):
+ self.localfiles.append(path)
+ found = True
+ break
+ if not found:
+ gbp.log.warn("Seemingly local file '%s' not found under "
+ "'%s'" % (uri_str, self.bb_dir))
+
+ def _expand_single(self, match):
+ """Expand single occurrence of a variable reference"""
+ if match.group(1) in self._variables:
+ return self._variables[match.group(1)]
+ return match.group(0)
+
+ def expand_val(self, val, rec=0):
+ """Expand variable"""
+ expanded = re.sub(r'\${(\w+)}', self._expand_single, val)
+ if expanded == val:
+ return expanded
+ elif rec < 20:
+ return self.expand_val(expanded, rec +1)
+ else:
+ raise GbpError("Too many recursions when expanding variable value")
+
+ def getVar(self, var, expand=True):
+ """Get variable"""
+ if self._pkg_data:
+ return self._pkg_data.getVar(var, expand)
+ elif var in self._variables:
+ if expand:
+ return self.expand_val(self._variables[var])
+ else:
+ return self._variables[var]
+ return None
+
+ @staticmethod
+ def unquote_val(val):
+ """Unquote / strip variable value"""
+ return val.strip(string.whitespace + r'"\'\\')
+
+ @staticmethod
+ def unwrap_lines(lines):
+ """Return a joined string of multiple lines"""
+ return ''.join([re.sub(r'\\\s*$', '', line) for line in lines])
+
+ @staticmethod
+ def var_to_str(var, values, oper='+='):
+ """Create a well formatted string buffer containing a multiline variable
+ assignment"""
+ indent = ' ' * (len(var) + 2 + len(oper))
+ linebuf = ['%s %s "%s \\\n' % (var, oper, values[0])]
+ for val in values[1:]:
+ linebuf.append(indent + ' ' + val + '\\\n')
+ linebuf.append(indent + '"\n')
+ return linebuf
+
+ @staticmethod
+ def parse_file(filepath, cb_func):
+ """Parse recipe"""
+ ret_buf = []
+ with open(filepath) as fobj:
+ multiline = []
+ for line in fobj.readlines():
+ stripped = line.rstrip()
+ if not multiline:
+ if not stripped.endswith('\\'):
+ ret_buf.extend(cb_func([line]))
+ else:
+ multiline = [line]
+ else:
+ multiline.append(line)
+ if not stripped.endswith('\\'):
+ ret_buf.extend(cb_func(multiline))
+ multiline = []
+ return ret_buf
+
+ @staticmethod
+ def set_var_val(filepath, var, val):
+ """Set variable value in a recipe"""
+ class _Setter(object):
+ """Class for handling variable injections"""
+ def __init__(self):
+ self.was_set = False
+
+ def set_cb(self, lines):
+ """Parser callback for setting variable value"""
+ unwrapped = BBFile.unwrap_lines(lines)
+ match = BBFile.vardef_re.match(unwrapped)
+ if match and match.group('name') == var:
+ if not self.was_set:
+ self.was_set = True
+ print "Setting value %s = %s" % (var, val)
+ return ['%s = "%s"\n' % (var, val)]
+ else:
+ return []
+ return lines
+
+ # Parse file and set values
+ setter = _Setter()
+ linebuf = BBFile.parse_file(filepath, setter.set_cb)
+
+ # Write file
+ with open(filepath, 'w') as fobj:
+ if not setter.was_set:
+ fobj.write('%s = "%s"\n')
+ fobj.writelines(linebuf)
+
+ @staticmethod
+ def substitute_var_val(filepath, var, pattern, repl):
+ """Update variable in a recipe"""
+ def subst_cb(lines):
+ """Parser callback for substituting variable values"""
+ unwrapped = BBFile.unwrap_lines(lines)
+ match = BBFile.vardef_re.match(unwrapped)
+ if match and match.group('name') == var:
+ filtered = []
+ for line in lines:
+ line = re.sub(pattern, repl, line)
+ # Drop empty lines
+ if not re.match(r'\s*\\\s*', line):
+ filtered.append(line)
+ return filtered
+ return lines
+
+ # Parse file and substitute values
+ linebuf = BBFile.parse_file(filepath, subst_cb)
+
+ # Write file
+ with open(filepath, 'w') as fobj:
+ fobj.writelines(linebuf)
+
+ @staticmethod
+ def append_var_val(filepath, var, new_vals):
+ """Update variable in a recipe"""
+ if not new_vals:
+ return
+
+ class _Finder(object):
+ """Class for recording definitions of variables"""
+ def __init__(self):
+ self.line_ind = 0
+ self.last_occurrence = -1
+
+ def find_last_occurrence_cb(self, lines):
+ """Get the point of insertion for the variable"""
+ unwrapped = BBFile.unwrap_lines(lines)
+ match = BBFile.vardef_re.match(unwrapped)
+ if match and match.group('name') == var:
+ self.last_occurrence = self.line_ind + len(lines) - 1
+ self.line_ind += len(lines)
+ return lines
+
+ finder = _Finder()
+ linebuf = BBFile.parse_file(filepath, finder.find_last_occurrence_cb)
+
+ # Prepare for appending values
+ quote = None
+ if finder.last_occurrence >= 0:
+ last_line = linebuf[finder.last_occurrence].rstrip()
+ # Guess indentation
+ match = BBFile.vardef_re.match(last_line)
+ if match:
+ indent = ' ' * (len(match.group(1)) + 1)
+ else:
+ indent = re.match(r'(\s*)', last_line).group(1)
+
+ # Guess point of insertion for new values and mangle the last line
+ if re.match(r'^\s*$', last_line[:-1]):
+ # Insert before the last line if it's an empty line (with a
+ # quotation character only)
+ insert_ind = finder.last_occurrence
+ indent += ' '
+ else:
+ # Else, remove the quotation character and append after the
+ # last line
+ quote = last_line[-1]
+ last_line = last_line[:-1] + ' \\\n'
+ linebuf[finder.last_occurrence] = last_line
+ insert_ind = finder.last_occurrence + 1
+ else:
+ indent = ' ' * (len(var) + 4)
+
+ # Write file
+ with open(filepath, 'w') as fobj:
+ if finder.last_occurrence > -1:
+ fobj.writelines(linebuf[:insert_ind])
+ for val in new_vals:
+ fobj.write(indent + val + ' \\\n')
+ if quote:
+ fobj.write(indent + quote + '\n')
+ fobj.writelines(linebuf[insert_ind:])
+ else:
+ fobj.writelines(BBFile.var_to_str(var, new_vals, '+='))
+ fobj.writelines(linebuf)
+
+def guess_bb_file(file_list, bbappend):
+ """Guess bb recipe from a list of filenames"""
+ recipes = []
+ file_exts = ['.bb'] if not bbappend else ['.bb', '.bbappend']
+ for ext in file_exts:
+ for filepath in file_list:
+ if filepath.endswith(ext):
+ gbp.log.debug("Found bb recipe file %s" % filepath)
+ recipes.append(filepath)
+ if len(recipes) == 0:
+ raise GbpError("No recipes found.")
+ return sorted(recipes)[-1]
+
+def bb_from_repo(cfg_data, repo, treeish, bb_path):
+ """Get and parse a bb recipe from a Git treeish"""
+ try:
+ tmpdir = tempfile.mkdtemp(prefix='gbp-bb_')
+ # Dump whole bb directory
+ dump_tree(repo, tmpdir, '%s:%s' % (treeish, os.path.dirname(bb_path)),
+ False)
+ fpath = os.path.join(tmpdir, os.path.basename(bb_path))
+ return BBFile(fpath, cfg_data)
+ except GitRepositoryError as err:
+ raise GbpError("Git error: %s" % err)
+ finally:
+ shutil.rmtree(tmpdir)
+
+def guess_bb_path_from_fs(topdir, recursive=True, bbappend=False):
+ """Guess a bitbake recipe file"""
+ file_list = []
+ if not topdir:
+ topdir = '.'
+ for root, dirs, files in os.walk(topdir):
+ file_list.extend([os.path.join(root, fname) for fname in files])
+ if not recursive:
+ del dirs[:]
+ # Skip .git dir in any case
+ if '.git' in dirs:
+ dirs.remove('.git')
+ return guess_bb_file(file_list, bbappend)
+
+def guess_bb_path_from_repo(repo, treeish=None, topdir='', recursive=True,
+ bbappend=False):
+ """Guess a bitbake recipe path from a git repository"""
+ topdir = topdir.rstrip('/') + ('/') if topdir else ''
+ # Search from working copy
+ if not treeish:
+ abspath = guess_bb_path_from_fs(os.path.join(repo.path, topdir),
+ recursive, bbappend)
+ return os.path.relpath(abspath, repo.path)
+
+ # Search from treeish
+ try:
+ file_list = [nam for (mod, typ, sha, nam) in
+ repo.list_tree(treeish, recursive, topdir) if typ == 'blob']
+ except GitRepositoryError as err:
+ raise GbpError("Failed to search bb recipe from treeish %s, "
+ "Git error: %s" % (treeish, err))
+ return guess_bb_file(file_list, bbappend)
+
+def guess_bb_path(options, repo, treeish=None, bbappend=False):
+ """Guess recipe path, relative to repo rootdir"""
+ bb_path = options.bb_file
+ if options.bb_file:
+ if not treeish:
+ path = os.path.join(repo.path, bb_path)
+ if not os.path.exists(path):
+ raise GbpError("'%s' does not exist" % bb_path)
+ else:
+ try:
+ repo.show("%s:%s" % (treeish, bb_path))
+ except GbpError as err:
+ raise GbpError(str(err))
+ else:
+ bb_path = guess_bb_path_from_repo(repo, treeish, options.meta_dir,
+ bbappend=bbappend)
+ return bb_path
+
+def parse_bb(cfg_data, options, repo, treeish=None, bbappend=False):
+ """Find and parse a bb recipe from a repository"""
+ try:
+ bb_path = guess_bb_path(options, repo, treeish, bbappend=bbappend)
+ gbp.log.debug("Using recipe '%s'" % bb_path)
+ options.meta_dir = os.path.dirname(bb_path)
+ if treeish:
+ pkg_data = bb_from_repo(cfg_data, repo, treeish, bb_path)
+ else:
+ full_path = os.path.join(repo.path, bb_path)
+ pkg_data = BBFile(full_path, cfg_data)
+ except GbpError as err:
+ raise GbpError("Can't parse bb recipe: %s" % err)
+ return pkg_data
+
+
+def guess_pkg_from_dir(pkg_dir, tinfoil):
+ """Guess a package from a directory in configured bitbake environment"""
+ abspath = os.path.abspath(pkg_dir)
+ layer_dirs = tinfoil.config_data.getVar('BBLAYERS').split()
+ gbp.log.debug("Checking if %s is in %s" % (abspath, layer_dirs))
+ layer_dir = ''
+ for path in layer_dirs:
+ if abspath.startswith(path):
+ layer_dir = path
+ if not layer_dir:
+ raise GbpError("%s not under configured layers" % abspath)
+
+ bb_files = [path for path in tinfoil.cooker_data.pkg_fn
+ if os.path.dirname(path) == abspath]
+ if len(bb_files):
+ bb_file = bb_files[-1]
+ gbp.log.debug("Found %d recipes in %s, choosing %s" %
+ (len(bb_files), pkg_dir, os.path.basename(bb_file)))
+ else:
+ raise GbpError("No recipes found in %s" % pkg_dir)
+ return bb_file
+
+def guess_pkg(tinfoil, pkg):
+ """Guess package (recipe) from configured bitbake environment"""
+ if pkg in tinfoil.cooker_data.pkg_pn:
+ pkg_bb = tinfoil.cooker_data.pkg_pn[pkg][0]
+ elif not os.path.isdir(pkg):
+ abspath = os.path.abspath(pkg)
+ if abspath in tinfoil.cooker_data.pkg_fn:
+ pkg_bb = abspath
+ else:
+ raise GbpError("Package %s not found in any configured layer" % pkg)
+ elif os.path.exists(pkg):
+ pkg_bb = guess_pkg_from_dir(pkg, tinfoil)
+ else:
+ raise GbpError("Unable to find %s" % pkg)
+ return pkg_bb
+
+
+# Initialize module
+bb = import_bb()
diff --git a/gbp/config.py b/gbp/config.py
index 9fd31b73..267400ce 100644
--- a/gbp/config.py
+++ b/gbp/config.py
@@ -589,101 +589,149 @@ class GbpOptionParserRpm(GbpOptionParser):
Handles commandline options and parsing of config files for rpm tools
"""
defaults = dict(GbpOptionParser.defaults)
+ defaults.update({
+ 'tmp-dir' : '/var/tmp/gbp/',
+ 'vendor' : 'vendor',
+ 'native' : 'auto',
+ 'builder' : 'rpmbuild',
+ 'cleaner' : '/bin/true',
+ 'merge' : 'False',
+ 'packaging-dir' : '',
+ 'packaging-branch' : 'master',
+ 'packaging-tag' : '%(vendor)s/%(version)s',
+ 'upstream-tag' : 'upstream/%(upstreamversion)s',
+ 'pq-branch' : 'development/%(branch)s',
+ 'import-files' : ['.gbp.conf',
+ 'debian/gbp.conf'],
+ 'spec-file' : 'auto',
+
+ 'export-dir' : '../rpmbuild',
+ 'ignore-untracked' : 'False',
+ 'rpmbuild-builddir' : 'BUILD',
+ 'rpmbuild-rpmdir' : 'RPMS',
+ 'rpmbuild-sourcedir' : 'SOURCES',
+ 'rpmbuild-specdir' : 'SPECS',
+ 'rpmbuild-srpmdir' : 'SRPMS',
+ 'rpmbuild-buildrootdir' : 'BUILDROOT',
+ 'patch-export' : 'False',
+ 'patch-export-ignore-path' : '',
+ 'patch-export-compress' : '0',
+ 'patch-export-squash-until' : '',
+ 'pristine-tarball-name' : 'auto',
+ 'orig-prefix' : 'auto',
+ 'patch-import' : 'True',
+ 'spec-vcs-tag' : '',
+
+
+ 'changelog-file' : 'auto',
+ 'changelog-revision' : '',
+ 'spawn-editor' : 'always',
+ 'editor-cmd' : 'vim',
+ 'meta-bts' : '(Close|Closes|Fixes|Fix)',
+ })
+
+ help = dict(GbpOptionParser.help)
+ help.update({
+
+ 'vendor':
+ "Distribution vendor name, default is '%(vendor)s'",
+ 'native':
+ "Treat this package as native, default is '%(native)s'",
+ 'packaging-branch':
+ "Branch the packaging is being maintained on, rpm counterpart "
+ "of the 'debian-branch' option, default is "
+ "'%(packaging-branch)s'",
+ 'packaging-dir':
+ "Subdir for RPM packaging files, default is '%(packaging-dir)s'",
+ 'packaging-tag':
+ "Format string for packaging tags, RPM counterpart of the "
+ "'debian-tag' option, default is '%(packaging-tag)s'",
+ 'pq-branch':
+ "format string for the patch-queue branch name, default is '%(pq-branch)s'",
+ 'import-files':
+ "Comma-separated list of additional file(s) to import from "
+ "packaging branch. These will appear as one monolithic patch "
+ "in the pq/development branch. Default is %(import-files)s",
+ 'spec-file':
+ "Spec file to use, 'auto' makes gbp to guess, other values "
+ "make the packaging-dir option to be ignored, default is "
+ "'%(spec-file)s'",
+
+ 'ignore-untracked':
+ "build with untracked files in the source tree, default is "
+ "'%(ignore-untracked)s'",
+ 'patch-export':
+ "Create patches between upstream and export-treeish, default "
+ "is '%(patch-export)s'",
+ 'patch-export-compress':
+ "Compress (auto-generated) patches larger than given number of "
+ "bytes, 0 never compresses, default is "
+ "'%(patch-export-compress)s'",
+ 'patch-export-ignore-path':
+ "Exclude changes to path(s) matching regex, default is "
+ "'%(patch-export-ignore-path)s'",
+ 'patch-export-squash-until':
+ "Squash commits (from upstream) until given tree-ish into one "
+ "big diff, format is '<commit_ish>[:<filename_base>]'. "
+ "Default is '%(patch-export-squash-until)s'",
+ 'patch-import':
+ "Import patches to the packaging branch, default is "
+ "'%(patch-import)s'",
+ 'spec-vcs-tag':
+ "Set/update the 'VCS:' tag in the spec file, empty value "
+ "removes the tag entirely, default is '%(spec-vcs-tag)s'",
+ 'pristine-tarball-name':
+ "Filename to record to pristine-tar, set to 'auto' to not "
+ "mangle the file name, default is '%(pristine-tarball-name)s'",
+ 'orig-prefix':
+ "Prefix (dir) to be used when generating/importing tarballs, "
+ "default is '%(orig-prefix)s'",
+ 'changelog-file':
+ "Changelog file to be used, default is '%(changelog-file)s'",
+ 'changelog-revision':
+ "Format string for the revision field in the changelog header. "
+ "If empty or not defined the default from packaging policy is "
+ "used.",
+ 'editor-cmd':
+ "Editor command to use",
+ 'git-author':
+ "Use name and email from git-config for the changelog header, "
+ "default is '%(git-author)s'",
+ 'meta-bts':
+ "Meta tags for the bts commands, default is '%(meta-bts)s'",
+ 'tmp-dir':
+ "Base directory under which temporary directories are "
+ "created, default is '%(tmp-dir)s'",
+ })
+
+class GbpOptionParserBB(GbpOptionParserRpm):
+ """Commandline and config file option parser for the -bb commands"""
+ defaults = dict(GbpOptionParserRpm.defaults)
defaults.update( {
- 'vendor' : 'vendor',
- 'native' : 'auto',
- 'builder' : 'rpmbuild',
- 'cleaner' : '/bin/true',
- 'merge' : 'False',
- 'packaging-dir' : '',
- 'packaging-tag' : '%(vendor)s/%(version)s',
- 'upstream-tag' : 'upstream/%(upstreamversion)s',
- 'pq-branch' : 'development/%(branch)s',
- 'import-files' : ['.gbp.conf',
- 'debian/gbp.conf'],
- 'spec-file' : 'auto',
- 'export-dir' : '../rpmbuild',
- 'ignore-untracked' : 'False',
- 'rpmbuild-builddir' : 'BUILD',
- 'rpmbuild-rpmdir' : 'RPMS',
- 'rpmbuild-sourcedir' : 'SOURCES',
- 'rpmbuild-specdir' : 'SPECS',
- 'rpmbuild-srpmdir' : 'SRPMS',
- 'rpmbuild-buildrootdir' : 'BUILDROOT',
- 'patch-export' : 'False',
- 'patch-export-ignore-path' : '',
- 'patch-export-compress' : '0',
- 'patch-export-squash-until' : '',
- 'pristine-tarball-name' : 'auto',
- 'orig-prefix' : 'auto',
- 'patch-import' : 'True',
- 'spec-vcs-tag' : '',
- 'changelog-file' : 'auto',
- 'changelog-revision' : '',
- 'spawn-editor' : 'always',
- 'editor-cmd' : 'vim',
- 'meta-bts' : '(Close|Closes|Fixes|Fix)',
+ 'builder' : 'bitbake',
+ 'export-dir' : '',
+ 'meta-dir' : '',
+ 'bb-file' : '',
+ 'bb-vcs-info' : '',
+ 'submit-tag' : 'submit/%(target)s/%(nowtime)s',
+ 'target' : 'tizen',
} )
- help = dict(GbpOptionParser.help)
+ help = dict(GbpOptionParserRpm.help)
help.update( {
- 'vendor':
- "Distribution vendor name",
- 'native':
- "Treat this package as native, default is '%(native)s'",
- 'packaging-dir':
- "subdir where packaging files are stored, default is '%(packaging-dir)s'",
- 'packaging-tag':
- "format string for packaging tags, rpm counterpart of the 'debian-tag' option, default is '%(packaging-tag)s'",
- 'pq-branch':
- "format string for the patch-queue branch name, default is '%(pq-branch)s'",
- 'import-files':
- ("Comma-separated list of additional file(s) to import "
- "from packaging branch. These will appear as one "
- "monolithic patch in the pq/development branch. "
- "Default is %(import-files)s"),
- 'spec-file':
- "Spec file to use, 'auto' makes gbp to guess, other values make the packaging-dir option to be ignored, default is '%(spec-file)s'",
- 'ignore-untracked':
- "build with untracked files in the source tree, default is '%(ignore-untracked)s'",
- 'patch-export':
- "Create patches between upstream and export-treeish, default is '%(patch-export)s'",
- 'patch-export-ignore-path':
- ("Exclude changes to path(s) matching regex, default "
- "is '%(patch-export-ignore-path)s'"),
- 'patch-export-compress':
- "Compress (auto-generated) patches larger than given number of bytes, 0 never compresses, default is '%(patch-export-compress)s'",
- 'patch-export-squash-until':
- ("Squash commits (from upstream) until given tree-ish "
- "into one big diff, format is "
- "'<commit_ish>[:<filename_base>]'. "
- "Default is '%(patch-export-squash-until)s'"),
- 'pristine-tarball-name':
- "Filename to record to pristine-tar, set to 'auto' to not mangle the file name, default is '%(pristine-tarball-name)s'",
- 'orig-prefix':
- "Prefix (dir) to be used when generating/importing tarballs, default is '%(orig-prefix)s'",
- 'patch-import':
- "Import patches to the packaging branch, default is '%(patch-import)s'",
- 'spec-vcs-tag':
- ("Set/update the 'VCS:' tag in the spec file, empty "
- "value removes the tag entirely, default is "
- "'%(spec-vcs-tag)s'"),
- 'changelog-file':
- ("Changelog file to be used, default is "
- "'%(changelog-file)s'"),
- 'changelog-revision':
- ("Format string for the revision field in the "
- "changelog header. If empty or not defined the "
- "default from packaging policy is used."),
- 'editor-cmd':
- "Editor command to use",
- 'git-author':
- ("Use name and email from git-config for the changelog "
- "header, default is '%(git-author)s'"),
- 'meta-bts':
- ("Meta tags for the bts commands, default is "
- "'%(meta-bts)s'"),
+ 'meta-dir':
+ "Subdir where bitbake meta files are stored, default "
+ "is '%(meta-dir)s'",
+ 'bb-file':
+ "Bitbake recipe file to build",
+ 'bb-vcs-info':
+ "Format string for the VCS information automatically "
+ "set in the recipe file, default is '%(bb-vcs-info)s'",
+ 'submit-tag':
+ "Submit tag format, default is '%(submit-tag)s'",
+ 'target':
+ "Submit target used in submit tag, default is "
+ "'%(target)s'",
} )
-
# vim:et:ts=4:sw=4:et:sts=4:ai:set list listchars=tab\:»·,trail\:·:
diff --git a/gbp/rpm/__init__.py b/gbp/rpm/__init__.py
index 4c5c22bf..d5fa1ee8 100644
--- a/gbp/rpm/__init__.py
+++ b/gbp/rpm/__init__.py
@@ -901,5 +901,4 @@ def string_to_int(val_str):
else:
return int(val_str)
-
# vim:et:ts=4:sw=4:et:sts=4:ai:set list listchars=tab\:»·,trail\:·:
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))
+