porting code to python3.x with os patch
[tools/git-buildpackage.git] / gbp / scripts / common / buildpackage.py
1 # vim: set fileencoding=utf-8 :
2 #
3 # (C) 2006-2011 Guido Guenther <agx@sigxcpu.org>
4 # (C) 2012 Intel Corporation <markus.lehtonen@linux.intel.com>
5 #    This program is free software; you can redistribute it and/or modify
6 #    it under the terms of the GNU General Public License as published by
7 #    the Free Software Foundation; either version 2 of the License, or
8 #    (at your option) any later version.
9 #
10 #    This program is distributed in the hope that it will be useful,
11 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
12 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 #    GNU General Public License for more details.
14 #
15 #    You should have received a copy of the GNU General Public License
16 #    along with this program; if not, write to the Free Software
17 #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 #
19 """Common functionality for Debian and RPM buildpackage scripts"""
20
21 import os, os.path
22 import subprocess
23 import shutil
24
25 import gbp.tmpfile as tempfile
26 from gbp.command_wrappers import (CatenateTarArchive, CatenateZipArchive)
27 from gbp.git.repository import GitRepository, GitRepositoryError
28 from gbp.errors import GbpError
29 import gbp.log
30
31 # when we want to reference the index in a treeish context we call it:
32 index_name = "INDEX"
33 # when we want to reference the working copy in treeish context we call it:
34 wc_names = {'WC':           {'force': True, 'untracked': True},
35             'WC.TRACKED':   {'force': False, 'untracked': False},
36             'WC.UNTRACKED': {'force': False, 'untracked': True},
37             'WC.IGNORED':   {'force': True, 'untracked': True}}
38
39
40 def sanitize_prefix(prefix):
41     """
42     Sanitize the prefix used for generating source archives
43
44     >>> sanitize_prefix('')
45     '/'
46     >>> sanitize_prefix('foo/')
47     'foo/'
48     >>> sanitize_prefix('/foo/bar')
49     'foo/bar/'
50     """
51     if prefix:
52         return prefix.strip('/') + '/'
53     return '/'
54
55
56 def compress(cmd, options, output, input_data=None):
57     """
58     Filter data through a compressor cmd.
59
60     For better performance input_data should feed data in bigger chunks.
61     """
62     stdin = subprocess.PIPE if input_data else None
63     try:
64       with open(output, 'w') as fobj:
65             popen = subprocess.Popen([cmd] + options, stdin=stdin, stdout=fobj)
66             if stdin:
67                 for chunk in input_data:
68                     popen.stdin.write(chunk)
69                 popen.stdin.close()
70             if popen.wait():
71                 raise GbpError("Error creating %s: running '%s' failed" %
72                                 (output, ' '.join([cmd] + options)))
73     except (OSError, IOError) as err:
74         raise GbpError("Error creating %s: %s" % (output, err))
75
76 def git_archive_submodules(repo, treeish, output, tmpdir_base, prefix,
77                            comp_type, comp_level, comp_opts, format='tar'):
78     """
79     Create a source tree archive with submodules.
80
81     Since git-archive always writes an end of tarfile trailer we concatenate
82     the generated archives using tar and compress the result.
83
84     Exception handling is left to the caller.
85     """
86     prefix = sanitize_prefix(prefix)
87     tempdir = tempfile.mkdtemp(dir=tmpdir_base, prefix='git-archive_')
88     main_archive = os.path.join(tempdir, "main.%s" % format)
89     submodule_archive = os.path.join(tempdir, "submodule.%s" % format)
90     try:
91         # generate main (tmp) archive
92         repo.archive(format=format, prefix=prefix,
93                      output=main_archive, treeish=treeish)
94
95         # generate each submodule's arhive and append it to the main archive
96         for (subdir, commit) in repo.get_submodules(treeish):
97             tarpath = [subdir, subdir[2:]][subdir.startswith("./")]
98             subrepo = GitRepository(os.path.join(repo.path, subdir))
99
100             gbp.log.debug("Processing submodule %s (%s)" % (subdir, commit[0:8]))
101             subrepo.archive(format=format, prefix='%s%s/' % (prefix, tarpath),
102                             output=submodule_archive, treeish=commit)
103             if format == 'tar':
104                 CatenateTarArchive(main_archive)(submodule_archive)
105             elif format == 'zip':
106                 CatenateZipArchive(main_archive)(submodule_archive)
107
108         # compress the output
109         if comp_type:
110             compress(comp_type, ['--stdout', '-%s' % comp_level] + comp_opts +
111                      [main_archive], output)
112         else:
113             shutil.move(main_archive, output)
114     finally:
115         shutil.rmtree(tempdir)
116
117
118 def git_archive_single(repo, treeish, output, prefix, comp_type, comp_level,
119                        comp_opts, format='tar'):
120     """
121     Create an archive without submodules
122
123     Exception handling is left to the caller.
124     """
125     prefix = sanitize_prefix(prefix)
126     if comp_type:
127         cmd = comp_type
128         opts = ['--stdout', '-%s' % comp_level] + comp_opts
129     else:
130         cmd= 'cat'
131         opts = []
132     input_data = repo.archive(format, prefix, None, treeish)
133     compress(cmd, opts, output, input_data)
134
135 def untar_data(outdir, data):
136     """Extract tar provided as an iterable"""
137     popen = subprocess.Popen(['tar', '-C', outdir, '-x'],
138                              stdin=subprocess.PIPE)
139     for chunk in data:
140         popen.stdin.write(chunk)
141     popen.stdin.close()
142     if popen.wait():
143         raise GbpError("Error extracting tar to %s" % outdir)
144
145 #{ Functions to handle export-dir
146 def dump_tree(repo, export_dir, treeish, with_submodules, recursive=True):
147     """Dump a git tree-ish to output_dir"""
148     if not os.path.exists(export_dir):
149         os.makedirs(export_dir)
150     if recursive:
151         paths = ''
152     else:
153         paths = ["'%s'" % nam.decode() for _mod, typ, _sha, nam in
154                  repo.list_tree(treeish) if typ == 'blob']
155
156     try:
157         data = repo.archive('tar', '', None, treeish, paths)
158         untar_data(export_dir, data)
159         if recursive and with_submodules and repo.has_submodules():
160             repo.update_submodules()
161             for (subdir, commit) in repo.get_submodules(treeish):
162                 gbp.log.info("Processing submodule %s (%s)" % (subdir,
163                                                                commit[0:8]))
164                 subrepo = GitRepository(os.path.join(repo.path, subdir))
165                 prefix = [subdir, subdir[2:]][subdir.startswith("./")] + '/'
166                 data = subrepo.archive('tar', prefix, None, treeish=commit)
167                 untar_data(export_dir, data)
168     except GitRepositoryError as err:
169         gbp.log.err("Git error when dumping tree: %s" % err)
170         return False
171     return True
172
173
174 def wc_index(repo):
175     """Get path of the temporary index file used for exporting working copy"""
176     return os.path.join(repo.git_dir, "gbp_index")
177
178 def write_wc(repo, force=True, untracked=True):
179     """write out the current working copy as a treeish object"""
180     clone_index(repo)
181     repo.add_files(repo.path, force=force, untracked=untracked, index_file=wc_index(repo))
182     tree = repo.write_tree(index_file=wc_index(repo))
183     return tree
184
185
186 def drop_index(repo):
187     """drop our custom index"""
188     if os.path.exists(wc_index(repo)):
189         os.unlink(wc_index(repo))
190
191 def clone_index(repo):
192     """Copy the current index file to our custom index file"""
193     indexfn = os.path.join(repo.git_dir, "index")
194     if os.path.exists(indexfn):
195         shutil.copy2(indexfn, wc_index(repo))