cmd_export.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. #!/usr/bin/python -tt
  2. # vim: ai ts=4 sts=4 et sw=4
  3. #
  4. # Copyright (c) 2012 Intel, Inc.
  5. #
  6. # This program is free software; you can redistribute it and/or modify it
  7. # under the terms of the GNU General Public License as published by the Free
  8. # Software Foundation; version 2 of the License
  9. #
  10. # This program is distributed in the hope that it will be useful, but
  11. # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  12. # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  13. # for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License along
  16. # with this program; if not, write to the Free Software Foundation, Inc., 59
  17. # Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  18. """Implementation of subcmd: export
  19. """
  20. import os
  21. import re
  22. import shutil
  23. import glob
  24. import errno
  25. from urlparse import urlparse
  26. from gitbuildsys import utils
  27. from gitbuildsys.conf import configmgr
  28. from gitbuildsys.errors import GbsError, Usage
  29. from gitbuildsys.log import LOGGER as log
  30. from gbp.scripts.buildpackage_rpm import main as gbp_build
  31. from gbp.rpm.git import GitRepositoryError, RpmGitRepository
  32. import gbp.rpm as rpm
  33. from gbp.errors import GbpError
  34. def mkdir_p(path):
  35. """
  36. Create directory as in mkdir -p
  37. """
  38. try:
  39. os.makedirs(path)
  40. except OSError as exc: # Python >2.5
  41. if exc.errno == errno.EEXIST:
  42. pass
  43. else:
  44. raise GbsError('failed to create %s: %s' % (path, exc.strerror))
  45. def is_native_pkg(repo, args):
  46. """
  47. Determine if the package is "native"
  48. """
  49. upstream_branch = configmgr.get_arg_conf(args, 'upstream_branch')
  50. return not repo.has_branch(upstream_branch)
  51. def get_packaging_dir(args):
  52. """
  53. Determine the packaging dir to be used
  54. """
  55. path = configmgr.get_arg_conf(args, 'packaging_dir')
  56. return path.rstrip(os.sep)
  57. def track_export_branches(repo, args):
  58. '''checking export related branches: pristine-tar, upstream.
  59. give warning if pristine-tar/upstream branch exist in remote
  60. but have not been checkout to local
  61. '''
  62. remote_branches = {}
  63. tracked_branches = []
  64. for branch in repo.get_remote_branches():
  65. remote_branches[branch.split('/', 1)[-1]] = branch
  66. upstream_branch = configmgr.get_arg_conf(args, 'upstream_branch')
  67. # track upstream/pristine-tar branch
  68. for branch in [upstream_branch, 'pristine-tar']:
  69. if not repo.has_branch(branch ) and branch in remote_branches:
  70. log.info('tracking branch: %s -> %s' % (remote_branches[branch],
  71. branch))
  72. repo.create_branch(branch, remote_branches[branch])
  73. tracked_branches.append(branch)
  74. return tracked_branches
  75. def untrack_export_branches(repo, branches):
  76. ''' remove local tracking branches, created in track_export_branches()
  77. '''
  78. for branch in branches:
  79. repo.delete_branch(branch)
  80. def create_gbp_export_args(repo, commit, export_dir, tmp_dir, spec, args,
  81. force_native=False, create_tarball=True):
  82. """
  83. Construct the cmdline argument list for git-buildpackage export
  84. """
  85. upstream_branch = configmgr.get_arg_conf(args, 'upstream_branch')
  86. upstream_tag = configmgr.get_arg_conf(args, 'upstream_tag')
  87. # transform variables from shell to python convention ${xxx} -> %(xxx)s
  88. upstream_tag = re.sub(r'\$\{([^}]+)\}', r'%(\1)s', upstream_tag)
  89. log.debug("Using upstream branch: %s" % upstream_branch)
  90. log.debug("Using upstream tag format: '%s'" % upstream_tag)
  91. # Get patch squashing option
  92. squash_patches_until = configmgr.get_arg_conf(args, 'squash_patches_until')
  93. # Determine the remote repourl
  94. reponame = ""
  95. remotes = repo.get_remote_repos()
  96. if remotes:
  97. remotename = 'origin' if 'origin' in remotes else remotes.keys()[0]
  98. # Take the remote repo of current branch, if available
  99. try:
  100. config_remote = repo.get_config('branch.%s.remote' % repo.branch)
  101. except KeyError:
  102. pass
  103. else:
  104. if config_remote in remotes:
  105. remotename = config_remote
  106. elif config_remote != '.':
  107. log.warning("You appear to have non-existent remote '%s' "
  108. "configured for branch '%s'. Check your git config!"
  109. % (config_remote, repo.branch))
  110. reponame = urlparse(remotes[remotename][0]).path.lstrip('/')
  111. packaging_dir = get_packaging_dir(args)
  112. # Now, start constructing the argument list
  113. argv = ["argv[0] placeholder",
  114. "--git-color-scheme=magenta:green:yellow:red",
  115. "--git-ignore-new",
  116. "--git-compression-level=6",
  117. "--git-export-dir=%s" % export_dir,
  118. "--git-tmp-dir=%s" % tmp_dir,
  119. "--git-packaging-dir=%s" % packaging_dir,
  120. "--git-spec-file=%s" % spec,
  121. "--git-export=%s" % commit,
  122. "--git-upstream-branch=%s" % upstream_branch,
  123. "--git-upstream-tag=%s" % upstream_tag,
  124. "--git-spec-vcs-tag=%s#%%(commit)s" % reponame]
  125. if create_tarball:
  126. argv.append("--git-force-create")
  127. else:
  128. argv.append("--git-no-create-orig")
  129. if args.debug:
  130. argv.append("--git-verbose")
  131. if force_native or is_native_pkg(repo, args) or args.no_patch_export:
  132. argv.extend(["--git-no-patch-export",
  133. "--git-upstream-tree=%s" % commit])
  134. else:
  135. argv.extend(["--git-patch-export",
  136. "--git-patch-export-compress=100k",
  137. "--git-patch-export-squash-until=%s" %
  138. squash_patches_until,
  139. "--git-patch-export-ignore-path=^(%s/.*|.gbs.conf)" %
  140. packaging_dir,
  141. ])
  142. if repo.has_branch("pristine-tar"):
  143. argv.extend(["--git-pristine-tar"])
  144. if 'source_rpm' in args and args.source_rpm:
  145. argv.extend(['--git-builder=rpmbuild',
  146. '--git-rpmbuild-builddir=.',
  147. '--git-rpmbuild-builddir=.',
  148. '--git-rpmbuild-rpmdir=.',
  149. '--git-rpmbuild-sourcedir=.',
  150. '--git-rpmbuild-specdir=.',
  151. '--git-rpmbuild-srpmdir=.',
  152. '--git-rpmbuild-buildrootdir=.',
  153. '--short-circuit', '-bs',
  154. ])
  155. else:
  156. argv.extend(["--git-builder=osc", "--git-export-only"])
  157. return argv
  158. def export_sources(repo, commit, export_dir, spec, args, create_tarball=True):
  159. """
  160. Export packaging files using git-buildpackage
  161. """
  162. tmp = utils.Temp(prefix='gbp_', dirn=configmgr.get('tmpdir', 'general'),
  163. directory=True)
  164. gbp_args = create_gbp_export_args(repo, commit, export_dir, tmp.path,
  165. spec, args, create_tarball=create_tarball)
  166. try:
  167. ret = gbp_build(gbp_args)
  168. if ret == 2 and not is_native_pkg(repo, args):
  169. # Try falling back to old logic of one monolithic tarball
  170. log.error("Generating upstream tarball and/or generating patches "
  171. "failed. GBS tried this as you have upstream branch in "
  172. "you git tree. Fix the problem by either:\n"
  173. " 1. Update your upstream branch and/or fix the spec "
  174. "file. Also, check the upstream tag format.\n"
  175. " 2. Remove or rename the upstream branch (change the "
  176. "package to native)\n"
  177. "See https://source.tizen.org/documentation/reference/"
  178. "git-build-system/upstream-package for more details.")
  179. if ret:
  180. raise GbsError("Failed to export packaging files from git tree")
  181. except GitRepositoryError, excobj:
  182. raise GbsError("Repository error: %s" % excobj)
  183. def main(args):
  184. """gbs export entry point."""
  185. if args.commit and args.include_all:
  186. raise Usage("--commit can't be specified together with --include-all")
  187. workdir = args.gitdir
  188. try:
  189. repo = RpmGitRepository(workdir)
  190. except GitRepositoryError, err:
  191. raise GbsError(str(err))
  192. utils.read_localconf(repo.path)
  193. utils.git_status_checker(repo, args)
  194. workdir = repo.path
  195. # Only guess spec filename here, parse later when we have the correct
  196. # spec file at hand
  197. if args.commit:
  198. commit = args.commit
  199. elif args.include_all:
  200. commit = 'WC.UNTRACKED'
  201. else:
  202. commit = 'HEAD'
  203. packaging_dir = get_packaging_dir(args)
  204. main_spec, rest_specs = utils.guess_spec(workdir, packaging_dir,
  205. args.spec, commit)
  206. if args.outdir:
  207. outdir = args.outdir
  208. else:
  209. outdir = os.path.join(workdir, packaging_dir)
  210. outdir = os.path.abspath(outdir)
  211. if os.path.exists(outdir):
  212. if not os.access(outdir, os.W_OK|os.X_OK):
  213. raise GbsError('no write permission to outdir: %s' % outdir)
  214. else:
  215. mkdir_p(outdir)
  216. tmpdir = configmgr.get('tmpdir', 'general')
  217. tempd = utils.Temp(prefix=os.path.join(tmpdir, '.gbs_export_'), \
  218. directory=True)
  219. export_dir = tempd.path
  220. tracked_branches = track_export_branches(repo, args)
  221. with utils.Workdir(workdir):
  222. export_sources(repo, commit, export_dir, main_spec, args)
  223. if rest_specs:
  224. # backup updated spec file
  225. specbakd = utils.Temp(prefix=os.path.join(tmpdir, '.gbs_export_'),
  226. directory=True)
  227. shutil.copy(os.path.join(export_dir,
  228. os.path.basename(main_spec)), specbakd.path)
  229. for spec in rest_specs:
  230. export_sources(repo, commit, export_dir, spec, args,
  231. create_tarball=False)
  232. shutil.copy(os.path.join(export_dir,
  233. os.path.basename(spec)), specbakd.path)
  234. # restore updated spec files
  235. for spec in glob.glob(os.path.join(specbakd.path, "*.spec")):
  236. shutil.copy(spec, export_dir)
  237. # Remove tracked export branches
  238. if tracked_branches:
  239. untrack_export_branches(repo, tracked_branches)
  240. specfile = os.path.basename(main_spec)
  241. try:
  242. spec = rpm.SpecFile(os.path.join(export_dir, specfile))
  243. except GbpError, err:
  244. raise GbsError('%s' % err)
  245. if not spec.name or not spec.version:
  246. raise GbsError('can\'t get correct name or version from spec file.')
  247. else:
  248. outdir = "%s/%s-%s-%s" % (outdir, spec.name, spec.upstreamversion,
  249. spec.release)
  250. if os.path.exists(outdir):
  251. if not os.access(outdir, os.W_OK|os.X_OK):
  252. raise GbsError('no permission to update outdir: %s' % outdir)
  253. shutil.rmtree(outdir, ignore_errors=True)
  254. shutil.move(export_dir, outdir)
  255. if args.source_rpm:
  256. log.info('source rpm generated to:\n %s/%s.src.rpm' % \
  257. (outdir, os.path.basename(outdir)))
  258. log.info('package files have been exported to:\n %s' % outdir)