/[ascend]/trunk/tools/dtar/dtar
ViewVC logotype

Contents of /trunk/tools/dtar/dtar

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3204 - (show annotations) (download)
Mon Jun 19 08:33:47 2017 UTC (17 months ago) by jpye
File size: 11915 byte(s)
version updated for 16.04

1 #!/usr/bin/env python
2 # dtar - Tool to produce .deb package from source tarball
3 # Copyright (C) 2008-2012 John Pye <john@curioussymbols.com>
4 #
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, or (at your option)
8 # 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,
18 # Boston, MA 02111-1307, USA.
19
20 import sys, re, tarfile, os.path, subprocess, shutil, getopt, glob
21 import apt
22 from debian_bundle import deb822, changelog
23
24 def usage():
25 print """%s: create debian package directly from source code tarball
26 %s [-s] originaltarball.tar.* [debian.tar.bz2|debian.tar.gz]
27
28 originaltarball.tar.*
29 The tarball containing the source code for the package
30 or packages you are going to create
31 debian.tar.*
32 OPTIONAL tarball containing the debian files
33 for your package. Only use this file if you have excluded
34 the debian/* files from your original source code tarball.
35 It is probably best to use a separate debian* tarball
36 for these files if you are likely to run into build
37 problems, eg when uploading packages to a build farm such
38 as Ubuntu PPA or openSUSE Build Service.
39
40 -s
41 --source Create source package only, no binary
42
43 -h
44 -help Print this message.
45
46 -n
47 --nooverwrite
48 Do not overwrite package files in current directory at
49 the end of the build.
50
51 -jN
52 Parallel build using N processors.
53 -k
54 --keep
55 Keep temporary files at end of build, for checking/debugging
56 purposes. These will be in /tmp/dtar-*, and you will have to
57 manually delete them yourself if you use this option. Normally
58 all the files are deleted if the build completes successfully.
59 """
60
61 opts,args = getopt.getopt(sys.argv[1:],"nhskj:",["help","source","nooverwrite","keep","parallel="])
62
63 # do we just want a source package?
64 buildsource = False
65 overwrite = True
66 clean = True
67 parallel = ""
68
69 for _o,_a in opts:
70 if _o in ['-h','--help']:
71 usage()
72 sys.exit()
73 elif _o in ['-s','--source']:
74 buildsource = True
75 elif _o in ['-n','--nooverwrite']:
76 overwrite = False
77 elif _o in ['-k','--keep']:
78 clean = False
79 elif _o in ['-j','--parallel']:
80 if int(_a) > 1:
81 parallel = "-j%s" % _a
82 else:
83 assert False,"unhandled option"
84
85 if len(args)<1 or len(args)>2:
86 raise RuntimeError("One or two tarballs must be supplied to dtar! (see dtar --help)")
87
88 # the tarball is the argument to this script
89
90 if parallel:
91 print "Parallel build with",parallel
92
93 startingcwd = os.getcwd()
94
95 f = args[0]
96
97 fmeta = None
98 if len(args)==2:
99 # debian.tar.* meta-file tarball, optional
100 fmeta = args[1]
101 fmetafull = os.path.abspath(fmeta)
102
103 if not os.path.exists(fmeta):
104 raise RuntimeError("Debian metadata tarball '%s' was not found" % fmeta)
105
106 ffull = os.path.abspath(f)
107
108 # get the first filename in the tarball
109 t = tarfile.open(ffull)
110 M = t.getmembers()
111 print M[0].name
112 # split the top-level directory name from that path
113
114 for i in [0,1]:
115 print "name =",M[i].name
116 h,path = os.path.split(M[i].name)
117 print "h =",h
118 print "path =",path
119 if h:
120 break
121 if not h:
122 raise RuntimeError("Tarball appears not contain a top-level directory")
123
124 while h:
125 head = h
126 h,path = os.path.split(path)
127
128 print "Head directory = ",head
129
130 r = re.compile(r"^%s/debian/([^/]*)$" % re.escape(head))
131 r2 = re.compile(r"^debian/([^/]*)$")
132
133 debfiles = {}
134 # this is a dictionary of sequences, first element 'm' or 's' depending
135 # on whether the debian file comes from the 'meta' tarball (if given), or the
136 # source tarball.
137
138 # search for debian/* files inside the original tarball
139
140 for m in M:
141 if r.match(m.name):
142 h,p = os.path.split(m.name)
143 debfiles[p] = [t,m]
144
145 # search for debian/* files inside the meta tarball
146 tmeta = None
147 if fmeta:
148 tmeta = tarfile.open(fmetafull)
149 Mmeta = tmeta.getmembers()
150 for m in Mmeta:
151 print "meta file contains '%s'" % m.name
152 if r2.match(m.name):
153 h,p = os.path.split(m.name)
154 debfiles[p] = [tmeta,m]
155
156 def getdebfile(name):
157 _t,_f = debfiles[name]
158 return _t.extractfile(_f)
159
160 mandatorydebfiles = ['control','changelog','rules']
161
162 missingdebfiles = []
163 for _f in mandatorydebfiles:
164 if _f not in debfiles:
165 missingdebfiles.append(_f)
166 if missingdebfiles:
167 s = "Tarball(s) missing required debian/* files: %s" %missingdebfiles
168 raise RuntimeError(s)
169
170 fc = getdebfile('control')
171
172 # display mandatory fields from 'debian/control'
173
174 controlmandatory = ['Source','Maintainer']
175
176 control = deb822.Deb822(fc.read())
177 for _c in controlmandatory:
178 print "%s: %s" % (_c, control[_c])
179
180 # check that debian/changelog is current
181
182 fchange = getdebfile('changelog')
183
184 changes = changelog.Changelog()
185 changes.parse_changelog(file=fchange.read(),strict=1)
186
187 if not changes:
188 s = "Failed to parse Debian changelog file."
189 raise RuntimeError(s)
190
191 debianname = "%s-%s" % (control['Source'],changes.upstream_version)
192
193 print "DEBIAN NAME =",debianname
194
195 need_tarball_reheaded = False
196 if debianname != head:
197 need_tarball_reheaded = True
198 #raise RuntimeError("Debian files not up to date: debian/changelog refers to version '%s' but tarball head directory is '%s'"
199 # % (debianname,head));
200
201 # check for build-time dependencies
202
203 dependencies = ['build-essential','fakeroot','debhelper']
204
205 if 'Build-Depends' in control:
206 #print "Build-Depends:",control['Build-Depends']
207 c = apt.Cache()
208 splitre = re.compile(r"\s*,\s+")
209 dependencies += splitre.split(control['Build-Depends'])
210
211 print "DEPS = ",dependencies
212
213
214 print "Checking build dependencies..."
215 depsmissing = []
216 depre = re.compile(r"^(\S+)(\s*\(\s*([<>=]+\s*\S+)\))?$") # \s*\(\s*({<<|<=|=|>=|>>})\s*([^\)]+)\)\s*\[([^\]])\]$
217 import apt_pkg
218 for d in dependencies:
219 mm = apt_pkg.parse_depends(d)[0]
220 ok = 0
221 for m in mm:
222 try:
223 i = c[m[0]]
224 except KeyError:
225 continue
226
227 if not i.is_installed:
228 # if it's not installed, it's not OK
229 continue
230
231 if m[1] and m[2]:
232 # if version number is specified
233 cmp = apt_pkg.version_compare(c[m[0]].installed.version,m[1])
234 if m[2]=="<<" and cmp<0:
235 ok = 1
236 elif m[2]=="<=" and cmp<=0:
237 ok = 1
238 elif m[2]=="=" and cmp==0:
239 ok = 1
240 elif m[2]==">=" and cmp>=0:
241 ok = 1
242 elif m[2]==">>" and cmp>0:
243 ok = 1
244 else:
245 # no version number specified
246 ok = 1
247 if not ok:
248 depsmissing.append(d)
249
250 if depsmissing:
251 raise RuntimeError("Unable to proceed. Missing buildtime dependencies: %s" % (depsmissing))
252
253 # extract tarball to tmp dir
254
255 maindir = "/tmp/dtar-%s" % os.getpid()
256
257 if os.path.exists(maindir):
258 raise RuntimeError("Temp path '%s' already exists!"%maindir)
259
260 os.mkdir(maindir)
261
262 try:
263 # extract source code tarball
264
265 print "Extracting tarball to '%s'..." % maindir
266 os.chdir(maindir)
267 t.extractall()
268 assert(os.path.exists(head))
269 print "Files extracted to '%s'" % os.getcwd()
270
271 # extract meta-file tarball if provided
272
273 if tmeta:
274 print "Extracting meta tarball to '%s'..." % maindir
275 os.chdir(os.path.join(maindir,head))
276 tmeta.extractall()
277 os.chdir(maindir)
278
279 assert(os.path.exists(os.path.join(head,"debian/control")))
280
281 # apply patches so that dpkg-buildpackage can come and detect them
282
283 print "LOOKING FOR PATCHES"
284 os.chdir(os.path.join(maindir,head))
285 if os.path.exists("debian/patches"):
286 print "PATCH DIR FOUND"
287 patches = glob.glob("debian/patches/*.patch")
288 for p in patches:
289 print "PATCH %s" % p
290 pf = open(p,"r")
291 po = subprocess.Popen(["patch","-p1"],stdin = pf,executable="/usr/bin/patch")
292 po.communicate()
293 pf.close()
294 if 0==po.returncode:
295 print "PATCH %s APPLIED OK" % p
296 os.unlink(p)
297 else:
298 raise RuntimeError("Error applying patch '%s': is it in 'patch -p1' format?" % p)
299 os.rmdir("debian/patches")
300 os.chdir(maindir)
301
302 # copy original tarball (recompressing to .gz/reheading if required?)
303
304 unzip = None
305 ext = None
306 if f[-7:] == ".tar.gz" or f[-4:] == ".tgz":
307 print "Original tarball is in gzip format"
308 unzip = ["gzip","-d"]
309 exe = "/bin/gzip"
310 ext = "gz"
311 elif f[-8:] == ".tar.bz2":
312 print "Original tarball is in bzip2 format"
313 unzip = ["bzip2","-d"]
314 exe = "/bin/bzip2"
315 ext = "bz2"
316
317 if not unzip:
318 raise RuntimeError("Unrecognised input format for '%s'" % f)
319
320 if need_tarball_reheaded:
321 print "Extracting tarball for re-heading..."
322 tar1 = tarfile.open(ffull)
323 tar1.extractall()
324 tar1.close()
325 print "Renaming head directory from '%s' to '%s'..." % (head,debianname)
326 assert(os.path.exists(head))
327 os.rename(head,debianname)
328 assert(os.path.exists(debianname))
329 print "Re-zipping in .tar.gz format..."
330 temptar = "%s_%s.orig.tar" % (control['Source'],changes.upstream_version)
331 temptz = "%s.%s"%(temptar,ext)
332 tar = tarfile.open(temptz, "w:gz")
333 tar.add(debianname, recursive=True)
334 tar.close()
335 assert(os.path.exists(temptz))
336
337 elif ext != "gz" and ext != "bz2":
338 print "Source tarball needs to be re-zipped to gzip format."
339 temptar = "%s_%s.orig.tar" % (control['Source'],changes.upstream_version)
340 temptz = "%s.%s"%(temptar,ext)
341 print "Creating tarball copy '%s'" % temptz
342 shutil.copyfile(ffull,temptz)
343 print "Extracting with %s..." % unzip[0]
344 subprocess.call(unzip + [temptz], executable=exe)
345 assert(os.path.exists(temptar))
346 print "Recompressing with gzip..."
347 subprocess.call(['gzip',temptar])
348
349 else:
350 origname = "%s_%s.orig.tar.%s" % (control['Source'],changes.upstream_version,ext)
351 print "Original tarball is in %s format" % unzip[0]
352 shutil.copyfile(ffull, origname)
353
354 print "Checking for correct original tarball name..."
355 origname = "%s_%s.orig.tar.%s" % (control['Source'],changes.upstream_version,ext)
356 assert(os.path.exists(origname))
357
358 t1 = t
359
360 # build the source package
361 #print "Building source package..."
362 #res = subprocess.call(["dpkg-source","-b",head,ffull],executable="/usr/bin/dpkg-source")
363
364 #if res:
365 # raise RuntimeError("dpkg-source returned error code %d" % res)
366
367 # build the binary package
368 print "Entering directory '%s'" % debianname
369 os.chdir(debianname)
370 if buildsource:
371 print "Building source package..."
372 _cmd = ["dpkg-buildpackage","-i","-S","-rfakeroot","-sa"]
373 res = subprocess.call(_cmd)
374 else:
375 print "Building binary package..."
376 _cmd = ["dpkg-buildpackage","-i","-rfakeroot","-sa"]
377 if parallel:
378 _cmd += [parallel]
379 res = subprocess.call(_cmd)
380
381 if res:
382 raise RuntimeError("dpkg-buildpackage returned error code %s" % res)
383
384 except Exception, e:
385 print "ERROR IN DTAR: %s"% str(e)
386 print "Temporary files can be inspected in %s" % maindir
387 sys.exit(1)
388
389 print "Found files:"
390 os.chdir(maindir)
391 resultfiles = glob.glob("*.dsc") + glob.glob("*.deb") + glob.glob("*.gz") + glob.glob("*.changes")
392 print resultfiles
393
394 # move found files to current directory
395 failedtomovefiles = []
396 for _f in resultfiles:
397 _p2 = os.path.join(startingcwd,_f)
398
399 if not overwrite and os.path.exists(_p2):
400 failedtomovefiles.append(_f)
401 shutil.move(_f, _p2)
402
403 if failedtomovefiles:
404 raise RuntimeError("Failed to move the following files (check --nooverwrite): %s",failedtomovefiles)
405
406 #clean up files
407
408 if clean:
409 print "Cleaning up files from '%s'..." % maindir
410 assert maindir[:10]=="/tmp/dtar-"
411 for _root,_dirs,_files in os.walk(maindir,topdown=False):
412 for _f in _files:
413 os.remove(os.path.join(_root,_f))
414 for _d in _dirs:
415 os.rmdir(os.path.join(_root,_d))
416 os.rmdir(maindir)
417 else:
418 print "Leaving all temporary files in '%s'..." % maindir
419
420 print "All done, exiting"

Properties

Name Value
svn:executable *

john.pye@anu.edu.au
ViewVC Help
Powered by ViewVC 1.1.22