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

Contents of /trunk/tools/dtar/dtar

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2128 - (show annotations) (download)
Thu Dec 17 05:35:38 2009 UTC (10 years, 6 months ago) by jpye
File size: 10786 byte(s)
Fix version checking comparison.
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 -k
52 --keep
53 Keep temporary files at end of build, for checking/debugging
54 purposes. These will be in /tmp/dtar-*, and you will have to
55 manually delete them yourself if you use this option. Normally
56 all the files are deleted if the build completes successfully.
57 """
58
59 opts,args = getopt.getopt(sys.argv[1:],"nhsk",["help","source","nooverwrite","keep"])
60
61 # do we just want a source package?
62 buildsource = False
63 overwrite = True
64 clean = True
65
66 for _o,_a in opts:
67 if _o in ['-h','--help']:
68 usage()
69 sys.exit()
70 elif _o in ['-s','--source']:
71 buildsource = True
72 elif _o in ['-n','--nooverwrite']:
73 overwrite = False
74 elif _o in ['-k','--keep']:
75 clean = False
76 else:
77 assert False,"unhandled option"
78
79 if len(args)<1 or len(args)>2:
80 raise RuntimeError("One or two tarballs must be supplied to dtar! (see dtar --help)")
81
82 # the tarball is the argument to this script
83
84 startingcwd = os.getcwd()
85
86 f = args[0]
87
88 fmeta = None
89 if len(args)==2:
90 # debian.tar.* meta-file tarball, optional
91 fmeta = args[1]
92 fmetafull = os.path.abspath(fmeta)
93
94 if not os.path.exists(fmeta):
95 raise RuntimeError("Debian metadata tarball '%s' was not found" % fmeta)
96
97 ffull = os.path.abspath(f)
98
99 # get the first filename in the tarball
100 t = tarfile.open(ffull)
101 M = t.getmembers()
102 print M[0].name
103 # split the top-level directory name from that path
104
105 for i in [0,1]:
106 print "name =",M[i].name
107 h,path = os.path.split(M[i].name)
108 print "h =",h
109 print "path =",path
110 if h:
111 break
112 if not h:
113 raise RuntimeError("Tarball appears not contain a top-level directory")
114
115 while h:
116 head = h
117 h,path = os.path.split(path)
118
119 print "Head directory = ",head
120
121 r = re.compile(r"^%s/debian/([^/]*)$" % re.escape(head))
122 r2 = re.compile(r"^debian/([^/]*)$")
123
124 debfiles = {}
125 # this is a dictionary of sequences, first element 'm' or 's' depending
126 # on whether the debian file comes from the 'meta' tarball (if given), or the
127 # source tarball.
128
129 # search for debian/* files inside the original tarball
130
131 for m in M:
132 if r.match(m.name):
133 h,p = os.path.split(m.name)
134 debfiles[p] = [t,m]
135
136 # search for debian/* files inside the meta tarball
137 tmeta = None
138 if fmeta:
139 tmeta = tarfile.open(fmetafull)
140 Mmeta = tmeta.getmembers()
141 for m in Mmeta:
142 print "meta file contains '%s'" % m.name
143 if r2.match(m.name):
144 h,p = os.path.split(m.name)
145 debfiles[p] = [tmeta,m]
146
147 def getdebfile(name):
148 _t,_f = debfiles[name]
149 return _t.extractfile(_f)
150
151 mandatorydebfiles = ['control','changelog','rules']
152
153 missingdebfiles = []
154 for _f in mandatorydebfiles:
155 if _f not in debfiles:
156 missingdebfiles.append(_f)
157 if missingdebfiles:
158 s = "Tarball(s) missing required debian/* files: %s" %missingdebfiles
159 raise RuntimeError(s)
160
161 fc = getdebfile('control')
162
163 # display mandatory fields from 'debian/control'
164
165 controlmandatory = ['Source','Maintainer']
166
167 control = deb822.Deb822(fc.read())
168 for _c in controlmandatory:
169 print "%s: %s" % (_c, control[_c])
170
171 # check that debian/changelog is current
172
173 fchange = getdebfile('changelog')
174
175 changes = changelog.Changelog(file=fchange.read())
176
177 debianname = "%s-%s" % (control['Source'],changes.upstream_version)
178
179 need_tarball_reheaded = False
180 if debianname != head:
181 need_tarball_reheaded = True
182 #raise RuntimeError("Debian files not up to date: debian/changelog refers to version '%s' but tarball head directory is '%s'"
183 # % (debianname,head));
184
185 # check for build-time dependencies
186
187 dependencies = ['build-essential','fakeroot','debhelper']
188
189 if 'Build-Depends' in control:
190 #print "Build-Depends:",control['Build-Depends']
191 c = apt.Cache()
192 splitre = re.compile(r"\s*,\s+")
193 dependencies += splitre.split(control['Build-Depends'])
194
195 print "DEPS = ",dependencies
196
197
198 print "Checking build dependencies..."
199 depsmissing = []
200 depre = re.compile(r"^(\S+)(\s*\(\s*([<>=]+\s*\S+)\))?$") # \s*\(\s*({<<|<=|=|>=|>>})\s*([^\)]+)\)\s*\[([^\]])\]$
201 import apt_pkg
202 for d in dependencies:
203 mm = apt_pkg.ParseDepends(d)[0]
204 ok = 0
205 for m in mm:
206 try:
207 i = c[m[0]]
208 except KeyError:
209 continue
210
211 if not i.isInstalled:
212 # if it's not installed, it's not OK
213 continue
214
215 if m[1] and m[2]:
216 # if version number is specified
217 cmp = apt_pkg.VersionCompare(c[m[0]].installed.version,m[1])
218 if m[2]=="<<" and cmp<0:
219 ok = 1
220 elif m[2]=="<=" and cmp<=0:
221 ok = 1
222 elif m[2]=="=" and cmp==0:
223 ok = 1
224 elif m[2]==">=" and cmp>=0:
225 ok = 1
226 elif m[2]==">>" and cmp>0:
227 ok = 1
228 else:
229 # no version number specified
230 ok = 1
231 if not ok:
232 depsmissing.append(d)
233
234 if depsmissing:
235 raise RuntimeError("Unable to proceed. Missing buildtime dependencies: %s" % (depsmissing))
236
237 # extract tarball to tmp dir
238
239 maindir = "/tmp/dtar-%s" % os.getpid()
240
241 if os.path.exists(maindir):
242 raise RuntimeError("Temp path '%s' already exists!"%maindir)
243
244 os.mkdir(maindir)
245
246 try:
247 # extract source code tarball
248
249 print "Extracting tarball to '%s'..." % maindir
250 os.chdir(maindir)
251 t.extractall()
252 assert(os.path.exists(head))
253 print "Files extracted to '%s'" % os.getcwd()
254
255 # extract meta-file tarball if provided
256
257 if tmeta:
258 print "Extracting meta tarball to '%s'..." % maindir
259 os.chdir(os.path.join(maindir,head))
260 tmeta.extractall()
261 os.chdir(maindir)
262
263 assert(os.path.exists(os.path.join(head,"debian/control")))
264
265 # copy original tarball (recompressing to .gz/reheading if required?)
266
267 unzip = None
268 ext = None
269 if f[-7:] == ".tar.gz" or f[-4:] == ".tgz":
270 print "Original tarball is in gzip format"
271 unzip = ["gzip","-d"]
272 exe = "/bin/gzip"
273 ext = "gz"
274 elif f[-8:] == ".tar.bz2":
275 print "Original tarball is in bzip2 format"
276 unzip = ["bzip2","-d"]
277 exe = "/bin/bzip2"
278 ext = "bz2"
279
280 if not unzip:
281 raise RuntimeError("Unrecognised input format for '%s'" % f)
282
283 if need_tarball_reheaded:
284 print "Extracting tarball for re-heading..."
285 tar1 = tarfile.open(ffull)
286 tar1.extractall()
287 tar1.close()
288 print "Renaming head directory from '%s' to '%s'..." % (head,debianname)
289 assert(os.path.exists(head))
290 os.rename(head,debianname)
291 assert(os.path.exists(debianname))
292 print "Re-zipping in .tar.gz format..."
293 temptar = "%s_%s.orig.tar" % (control['Source'],changes.upstream_version)
294 temptz = "%s.%s"%(temptar,ext)
295 tar = tarfile.open(temptz, "w:gz")
296 tar.add(debianname, recursive=True)
297 tar.close()
298 assert(os.path.exists(temptz))
299
300 elif ext != "gz":
301 print "Source tarball needs to be re-zipped to gzip format."
302 temptar = "%s_%s.orig.tar" % (control['Source'],changes.upstream_version)
303 temptz = "%s.%s"%(temptar,ext)
304 print "Creating tarball copy '%s'" % temptz
305 shutil.copyfile(ffull,temptz)
306 print "Extracting with %s..." % unzip[0]
307 subprocess.call(unzip + [temptz], executable=exe)
308 assert(os.path.exists(temptar))
309 print "Recompressing with gzip..."
310 subprocess.call(['gzip',temptar])
311
312 else:
313 origname = "%s_%s.orig.tar.gz" % (control['Source'],changes.upstream_version)
314 print "Original tarball is in gzip format"
315 shutil.copyfile(ffull, origname)
316
317 print "Checking for correct original tarball name..."
318 origname = "%s_%s.orig.tar.gz" % (control['Source'],changes.upstream_version)
319 assert(os.path.exists(origname))
320
321 t1 = t
322
323 # build the source package
324 #print "Building source package..."
325 #res = subprocess.call(["dpkg-source","-b",head,ffull],executable="/usr/bin/dpkg-source")
326
327 #if res:
328 # raise RuntimeError("dpkg-source returned error code %d" % res)
329
330 # build the binary package
331 print "Entering directory '%s'" % debianname
332 os.chdir(debianname)
333 if buildsource:
334 print "Building source package..."
335 res = subprocess.call(["debuild","-rfakeroot","-S","-sa"])
336 else:
337 print "Building binary package..."
338 res = subprocess.call(["dpkg-buildpackage","-rfakeroot","-sa"])
339
340 if res:
341 raise RuntimeError("dpkg-buildpackage returned error code %s" % res)
342
343 except Exception, e:
344 print "ERROR DURING DPKG-BUILDPACKAGE: %s"% str(e)
345 print "Temporary files can be inspected in %s" % maindir
346 sys.exit(1)
347
348 print "Found files:"
349 os.chdir(maindir)
350 resultfiles = glob.glob("*.dsc") + glob.glob("*.deb") + glob.glob("*.gz") + glob.glob("*.changes")
351 print resultfiles
352
353 # move found files to current directory
354 failedtomovefiles = []
355 for _f in resultfiles:
356 _p2 = os.path.join(startingcwd,_f)
357
358 if not overwrite and os.path.exists(_p2):
359 failedtomovefiles.append(_f)
360 shutil.move(_f, _p2)
361
362 if failedtomovefiles:
363 raise RuntimeError("Failed to move the following files (check --nooverwrite): %s",failedtomovefiles)
364
365 #clean up files
366
367 if clean:
368 print "Cleaning up files from '%s'..." % maindir
369 assert maindir[:10]=="/tmp/dtar-"
370 for _root,_dirs,_files in os.walk(maindir,topdown=False):
371 for _f in _files:
372 os.remove(os.path.join(_root,_f))
373 for _d in _dirs:
374 os.rmdir(os.path.join(_root,_d))
375 os.rmdir(maindir)
376 else:
377 print "Leaving all temporary files in '%s'..." % maindir
378
379 print "All done, exiting"

Properties

Name Value
svn:executable *

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