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

Contents of /trunk/tools/dtar/dtar

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2369 - (show annotations) (download)
Mon Jan 31 09:00:44 2011 UTC (13 years, 4 months ago) by jpye
File size: 11738 byte(s)
Working to refactor ida.c, which is too big and scary.
1 #!/usr/bin/env python
2 # dtar - Tool to produce .deb package from source tarball
3 # Copyright (C) 2008 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 print "Parallel build with",parallel
91
92 startingcwd = os.getcwd()
93
94 f = args[0]
95
96 fmeta = None
97 if len(args)==2:
98 # debian.tar.* meta-file tarball, optional
99 fmeta = args[1]
100 fmetafull = os.path.abspath(fmeta)
101
102 if not os.path.exists(fmeta):
103 raise RuntimeError("Debian metadata tarball '%s' was not found" % fmeta)
104
105 ffull = os.path.abspath(f)
106
107 # get the first filename in the tarball
108 t = tarfile.open(ffull)
109 M = t.getmembers()
110 print M[0].name
111 # split the top-level directory name from that path
112
113 for i in [0,1]:
114 print "name =",M[i].name
115 h,path = os.path.split(M[i].name)
116 print "h =",h
117 print "path =",path
118 if h:
119 break
120 if not h:
121 raise RuntimeError("Tarball appears not contain a top-level directory")
122
123 while h:
124 head = h
125 h,path = os.path.split(path)
126
127 print "Head directory = ",head
128
129 r = re.compile(r"^%s/debian/([^/]*)$" % re.escape(head))
130 r2 = re.compile(r"^debian/([^/]*)$")
131
132 debfiles = {}
133 # this is a dictionary of sequences, first element 'm' or 's' depending
134 # on whether the debian file comes from the 'meta' tarball (if given), or the
135 # source tarball.
136
137 # search for debian/* files inside the original tarball
138
139 for m in M:
140 if r.match(m.name):
141 h,p = os.path.split(m.name)
142 debfiles[p] = [t,m]
143
144 # search for debian/* files inside the meta tarball
145 tmeta = None
146 if fmeta:
147 tmeta = tarfile.open(fmetafull)
148 Mmeta = tmeta.getmembers()
149 for m in Mmeta:
150 print "meta file contains '%s'" % m.name
151 if r2.match(m.name):
152 h,p = os.path.split(m.name)
153 debfiles[p] = [tmeta,m]
154
155 def getdebfile(name):
156 _t,_f = debfiles[name]
157 return _t.extractfile(_f)
158
159 mandatorydebfiles = ['control','changelog','rules']
160
161 missingdebfiles = []
162 for _f in mandatorydebfiles:
163 if _f not in debfiles:
164 missingdebfiles.append(_f)
165 if missingdebfiles:
166 s = "Tarball(s) missing required debian/* files: %s" %missingdebfiles
167 raise RuntimeError(s)
168
169 fc = getdebfile('control')
170
171 # display mandatory fields from 'debian/control'
172
173 controlmandatory = ['Source','Maintainer']
174
175 control = deb822.Deb822(fc.read())
176 for _c in controlmandatory:
177 print "%s: %s" % (_c, control[_c])
178
179 # check that debian/changelog is current
180
181 fchange = getdebfile('changelog')
182
183 changes = changelog.Changelog(file=fchange.read())
184
185 debianname = "%s-%s" % (control['Source'],changes.upstream_version)
186
187 need_tarball_reheaded = False
188 if debianname != head:
189 need_tarball_reheaded = True
190 #raise RuntimeError("Debian files not up to date: debian/changelog refers to version '%s' but tarball head directory is '%s'"
191 # % (debianname,head));
192
193 # check for build-time dependencies
194
195 dependencies = ['build-essential','fakeroot','debhelper']
196
197 if 'Build-Depends' in control:
198 #print "Build-Depends:",control['Build-Depends']
199 c = apt.Cache()
200 splitre = re.compile(r"\s*,\s+")
201 dependencies += splitre.split(control['Build-Depends'])
202
203 print "DEPS = ",dependencies
204
205
206 print "Checking build dependencies..."
207 depsmissing = []
208 depre = re.compile(r"^(\S+)(\s*\(\s*([<>=]+\s*\S+)\))?$") # \s*\(\s*({<<|<=|=|>=|>>})\s*([^\)]+)\)\s*\[([^\]])\]$
209 import apt_pkg
210 for d in dependencies:
211 mm = apt_pkg.ParseDepends(d)[0]
212 ok = 0
213 for m in mm:
214 try:
215 i = c[m[0]]
216 except KeyError:
217 continue
218
219 if not i.isInstalled:
220 # if it's not installed, it's not OK
221 continue
222
223 if m[1] and m[2]:
224 # if version number is specified
225 cmp = apt_pkg.VersionCompare(c[m[0]].installed.version,m[1])
226 if m[2]=="<<" and cmp<0:
227 ok = 1
228 elif m[2]=="<=" and cmp<=0:
229 ok = 1
230 elif m[2]=="=" and cmp==0:
231 ok = 1
232 elif m[2]==">=" and cmp>=0:
233 ok = 1
234 elif m[2]==">>" and cmp>0:
235 ok = 1
236 else:
237 # no version number specified
238 ok = 1
239 if not ok:
240 depsmissing.append(d)
241
242 if depsmissing:
243 raise RuntimeError("Unable to proceed. Missing buildtime dependencies: %s" % (depsmissing))
244
245 # extract tarball to tmp dir
246
247 maindir = "/tmp/dtar-%s" % os.getpid()
248
249 if os.path.exists(maindir):
250 raise RuntimeError("Temp path '%s' already exists!"%maindir)
251
252 os.mkdir(maindir)
253
254 try:
255 # extract source code tarball
256
257 print "Extracting tarball to '%s'..." % maindir
258 os.chdir(maindir)
259 t.extractall()
260 assert(os.path.exists(head))
261 print "Files extracted to '%s'" % os.getcwd()
262
263 # extract meta-file tarball if provided
264
265 if tmeta:
266 print "Extracting meta tarball to '%s'..." % maindir
267 os.chdir(os.path.join(maindir,head))
268 tmeta.extractall()
269 os.chdir(maindir)
270
271 assert(os.path.exists(os.path.join(head,"debian/control")))
272
273 # apply patches so that dpkg-buildpackage can come and detect them
274
275 print "LOOKING FOR PATCHES"
276 os.chdir(os.path.join(maindir,head))
277 if os.path.exists("debian/patches"):
278 print "PATCH DIR FOUND"
279 patches = glob.glob("debian/patches/*.patch")
280 for p in patches:
281 print "PATCH %s" % p
282 pf = open(p,"r")
283 po = subprocess.Popen(["patch","-p1"],stdin = pf,executable="/usr/bin/patch")
284 po.communicate()
285 pf.close()
286 if 0==po.returncode:
287 print "PATCH %s APPLIED OK" % p
288 os.unlink(p)
289 else:
290 raise RuntimeError("Error applying patch '%s': is it in 'patch -p1' format?" % p)
291 os.rmdir("debian/patches")
292 os.chdir(maindir)
293
294 # copy original tarball (recompressing to .gz/reheading if required?)
295
296 unzip = None
297 ext = None
298 if f[-7:] == ".tar.gz" or f[-4:] == ".tgz":
299 print "Original tarball is in gzip format"
300 unzip = ["gzip","-d"]
301 exe = "/bin/gzip"
302 ext = "gz"
303 elif f[-8:] == ".tar.bz2":
304 print "Original tarball is in bzip2 format"
305 unzip = ["bzip2","-d"]
306 exe = "/bin/bzip2"
307 ext = "bz2"
308
309 if not unzip:
310 raise RuntimeError("Unrecognised input format for '%s'" % f)
311
312 if need_tarball_reheaded:
313 print "Extracting tarball for re-heading..."
314 tar1 = tarfile.open(ffull)
315 tar1.extractall()
316 tar1.close()
317 print "Renaming head directory from '%s' to '%s'..." % (head,debianname)
318 assert(os.path.exists(head))
319 os.rename(head,debianname)
320 assert(os.path.exists(debianname))
321 print "Re-zipping in .tar.gz format..."
322 temptar = "%s_%s.orig.tar" % (control['Source'],changes.upstream_version)
323 temptz = "%s.%s"%(temptar,ext)
324 tar = tarfile.open(temptz, "w:gz")
325 tar.add(debianname, recursive=True)
326 tar.close()
327 assert(os.path.exists(temptz))
328
329 elif ext != "gz" and ext != "bz2":
330 print "Source tarball needs to be re-zipped to gzip format."
331 temptar = "%s_%s.orig.tar" % (control['Source'],changes.upstream_version)
332 temptz = "%s.%s"%(temptar,ext)
333 print "Creating tarball copy '%s'" % temptz
334 shutil.copyfile(ffull,temptz)
335 print "Extracting with %s..." % unzip[0]
336 subprocess.call(unzip + [temptz], executable=exe)
337 assert(os.path.exists(temptar))
338 print "Recompressing with gzip..."
339 subprocess.call(['gzip',temptar])
340
341 else:
342 origname = "%s_%s.orig.tar.%s" % (control['Source'],changes.upstream_version,ext)
343 print "Original tarball is in %s format" % unzip[0]
344 shutil.copyfile(ffull, origname)
345
346 print "Checking for correct original tarball name..."
347 origname = "%s_%s.orig.tar.%s" % (control['Source'],changes.upstream_version,ext)
348 assert(os.path.exists(origname))
349
350 t1 = t
351
352 # build the source package
353 #print "Building source package..."
354 #res = subprocess.call(["dpkg-source","-b",head,ffull],executable="/usr/bin/dpkg-source")
355
356 #if res:
357 # raise RuntimeError("dpkg-source returned error code %d" % res)
358
359 # build the binary package
360 print "Entering directory '%s'" % debianname
361 os.chdir(debianname)
362 if buildsource:
363 print "Building source package..."
364 _cmd = ["dpkg-buildpackage","-i","-S","-rfakeroot","-sa"]
365 res = subprocess.call(_cmd)
366 else:
367 print "Building binary package..."
368 _cmd = ["dpkg-buildpackage","-i","-rfakeroot","-sa"]
369 if parallel:
370 _cmd += [parallel]
371 res = subprocess.call(_cmd)
372
373 if res:
374 raise RuntimeError("dpkg-buildpackage returned error code %s" % res)
375
376 except Exception, e:
377 print "ERROR IN DTAR: %s"% str(e)
378 print "Temporary files can be inspected in %s" % maindir
379 sys.exit(1)
380
381 print "Found files:"
382 os.chdir(maindir)
383 resultfiles = glob.glob("*.dsc") + glob.glob("*.deb") + glob.glob("*.gz") + glob.glob("*.changes")
384 print resultfiles
385
386 # move found files to current directory
387 failedtomovefiles = []
388 for _f in resultfiles:
389 _p2 = os.path.join(startingcwd,_f)
390
391 if not overwrite and os.path.exists(_p2):
392 failedtomovefiles.append(_f)
393 shutil.move(_f, _p2)
394
395 if failedtomovefiles:
396 raise RuntimeError("Failed to move the following files (check --nooverwrite): %s",failedtomovefiles)
397
398 #clean up files
399
400 if clean:
401 print "Cleaning up files from '%s'..." % maindir
402 assert maindir[:10]=="/tmp/dtar-"
403 for _root,_dirs,_files in os.walk(maindir,topdown=False):
404 for _f in _files:
405 os.remove(os.path.join(_root,_f))
406 for _d in _dirs:
407 os.rmdir(os.path.join(_root,_d))
408 os.rmdir(maindir)
409 else:
410 print "Leaving all temporary files in '%s'..." % maindir
411
412 print "All done, exiting"

Properties

Name Value
svn:executable *

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