Switching to a touch selector for the addressbook selector
[gc-dialer] / support / py2deb.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 ##
4 ##    Copyright (C) 2009 manatlan manatlan[at]gmail(dot)com
5 ##
6 ## This program is free software; you can redistribute it and/or modify
7 ## it under the terms of the GNU General Public License as published
8 ## by the Free Software Foundation; version 2 only.
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 """
16 Known limitations :
17 - don't sign package (-us -uc)
18 - no distinctions between author and maintainer(packager)
19
20 depends on :
21 - dpkg-dev (dpkg-buildpackage)
22 - alien
23 - python
24 - fakeroot
25
26 changelog
27  - ??? 9/14/2009 (By epage)
28     - PEP8
29     - added recommends
30     - fixed bug where it couldn't handle the contents of the pre/post scripts being specified
31     - Added customization based on the targeted policy for sections (Maemo support)
32     - Added maemo specific tarball, dsc, changes file generation support (including icon support)
33  - 0.5 05/09/2009
34     - pre/post install/remove scripts enabled
35     - deb package install py2deb in dist-packages for py2.6
36  - 0.4 14/10/2008
37     - use os.environ USERNAME or USER (debian way)
38     - install on py 2.(4,5,6) (*FIX* do better here)
39
40 """
41
42 import os
43 import hashlib
44 import sys
45 import shutil
46 import time
47 import string
48 import StringIO
49 import stat
50 import commands
51 import base64
52 from tarfile import TarFile
53 from glob import glob
54 from datetime import datetime
55 import socket # gethostname()
56 from subprocess import Popen, PIPE
57
58 #~ __version__ = "0.4"
59 __version__ = "0.5"
60 __author__ = "manatlan"
61 __mail__ = "manatlan@gmail.com"
62
63
64 PERMS_URW_GRW_OR = stat.S_IRUSR | stat.S_IWUSR | \
65                    stat.S_IRGRP | stat.S_IWGRP | \
66                    stat.S_IROTH
67
68 UID_ROOT = 0
69 GID_ROOT = 0
70
71
72 def run(cmds):
73     p = Popen(cmds, shell=False, stdout=PIPE, stderr=PIPE)
74     time.sleep(0.01)    # to avoid "IOError: [Errno 4] Interrupted system call"
75     out = string.join(p.stdout.readlines()).strip()
76     outerr = string.join(p.stderr.readlines()).strip()
77     return out
78
79
80 def deb2rpm(file):
81     txt=run(['alien', '-r', file])
82     return txt.split(" generated")[0]
83
84
85 def py2src(TEMP, name):
86     l=glob("%(TEMP)s/%(name)s*.tar.gz"%locals())
87     if len(l) != 1:
88         raise Py2debException("don't find source package tar.gz")
89
90     tar = os.path.basename(l[0])
91     shutil.move(l[0], tar)
92
93     return tar
94
95
96 def md5sum(filename):
97     f = open(filename, "r")
98     try:
99         return hashlib.md5(f.read()).hexdigest()
100     finally:
101         f.close()
102
103
104 class Py2changes(object):
105
106     def __init__(self, ChangedBy, description, changes, files, category, repository, **kwargs):
107       self.options = kwargs # TODO: Is order important?
108       self.description = description
109       self.changes=changes
110       self.files=files
111       self.category=category
112       self.repository=repository
113       self.ChangedBy=ChangedBy
114
115     def getContent(self):
116         content = ["%s: %s" % (k, v)
117                    for k,v in self.options.iteritems()]
118
119         if self.description:
120             description=self.description.replace("\n","\n ")
121             content.append('Description: ')
122             content.append(' %s' % description)
123         if self.changes:
124             changes=self.changes.replace("\n","\n ")
125             content.append('Changes: ')
126             content.append(' %s' % changes)
127         if self.ChangedBy:
128             content.append("Changed-By: %s" % self.ChangedBy)
129
130         content.append('Files:')
131
132         for onefile in self.files:
133             md5 = md5sum(onefile)
134             size = os.stat(onefile).st_size.__str__()
135             content.append(' ' + md5 + ' ' + size + ' ' + self.category +' '+self.repository+' '+os.path.basename(onefile))
136
137         return "\n".join(content) + "\n\n"
138
139
140 def py2changes(params):
141     changescontent = Py2changes(
142                     "%(author)s <%(mail)s>" % params,
143                     "%(description)s" % params,
144                     "%(changelog)s" % params,
145                     (
146                            "%(TEMP)s/%(name)s_%(version)s.tar.gz" % params,
147                            "%(TEMP)s/%(name)s_%(version)s.dsc" % params,
148                     ),
149                     "%(section)s" % params,
150                     "", #"%(repository)s" % params,
151                     Format='1.7',
152                     Date=time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime()),
153                     Source="%(name)s" % params,
154                     Architecture="%(arch)s" % params,
155                     Version="%(version)s" % params,
156                     Distribution="", #"%(distribution)s" % params,
157                     Urgency="", #"%(urgency)s" % params,
158                     Maintainer="%(author)s <%(mail)s>" % params
159                     )
160     f = open("%(TEMP)s/%(name)s_%(version)s.changes" % params,"wb")
161     f.write(changescontent.getContent())
162     f.close()
163
164     fileHandle = open('/tmp/py2deb2.tmp', 'w')
165     fileHandle.write('#!/bin/sh\n')
166     fileHandle.write("cd " +os.getcwd()+ "\n")
167     fileHandle.write("gpg --local-user %(mail)s --clearsign %(TEMP)s/%(name)s_%(version)s.changes\n" % params)
168     fileHandle.write("mv %(TEMP)s/%(name)s_%(version)s.changes.asc %(TEMP)s/%(name)s_%(version)s.changes\n" % params)
169     fileHandle.write('\nexit')
170     fileHandle.close()
171     commands.getoutput("chmod 777 /tmp/py2deb2.tmp")
172     commands.getoutput("/tmp/py2deb2.tmp")
173
174     ret = []
175
176     l=glob("%(TEMP)s/%(name)s*.tar.gz" % params)
177     if len(l)!=1:
178         raise Py2debException("don't find source package tar.gz")
179     tar = os.path.basename(l[0])
180     shutil.move(l[0],tar)
181     ret.append(tar)
182
183     l=glob("%(TEMP)s/%(name)s*.dsc" % params)
184     if len(l)!=1:
185         raise Py2debException("don't find source package dsc")
186     tar = os.path.basename(l[0])
187     shutil.move(l[0],tar)
188     ret.append(tar)
189
190     l=glob("%(TEMP)s/%(name)s*.changes" % params)
191     if len(l)!=1:
192         raise Py2debException("don't find source package changes")
193     tar = os.path.basename(l[0])
194     shutil.move(l[0],tar)
195     ret.append(tar)
196
197     return ret
198
199
200 class Py2dsc(object):
201
202     def __init__(self, StandardsVersion, BuildDepends, files, **kwargs):
203       self.options = kwargs # TODO: Is order important?
204       self.StandardsVersion = StandardsVersion
205       self.BuildDepends=BuildDepends
206       self.files=files
207
208     @property
209     def content(self):
210         content = ["%s: %s" % (k, v)
211                    for k,v in self.options.iteritems()]
212
213         if self.BuildDepends:
214             content.append("Build-Depends: %s" % self.BuildDepends)
215         if self.StandardsVersion:
216             content.append("Standards-Version: %s" % self.StandardsVersion)
217
218         content.append('Files:')
219
220         for onefile in self.files:
221             print onefile
222             md5 = md5sum(onefile)
223             size = os.stat(onefile).st_size.__str__()
224             content.append(' '+md5 + ' ' + size +' '+os.path.basename(onefile))
225
226         return "\n".join(content)+"\n\n"
227
228
229 def py2dsc(TEMP, name, version, depends, author, mail, arch):
230     dsccontent = Py2dsc("%(version)s" % locals(),
231              "%(depends)s" % locals(),
232              ("%(TEMP)s/%(name)s_%(version)s.tar.gz" % locals(),),
233              Format='1.0',
234              Source="%(name)s" % locals(),
235              Version="%(version)s" % locals(),
236              Maintainer="%(author)s <%(mail)s>" % locals(),
237              Architecture="%(arch)s" % locals(),
238     )
239
240     filename = "%(TEMP)s/%(name)s_%(version)s.dsc" % locals()
241
242     f = open(filename, "wb")
243     try:
244         f.write(dsccontent.content)
245     finally:
246         f.close()
247
248     fileHandle = open('/tmp/py2deb.tmp', 'w')
249     try:
250         fileHandle.write('#!/bin/sh\n')
251         fileHandle.write("cd " + os.getcwd() + "\n")
252         fileHandle.write("gpg --local-user %(mail)s --clearsign %(TEMP)s/%(name)s_%(version)s.dsc\n" % locals())
253         fileHandle.write("mv %(TEMP)s/%(name)s_%(version)s.dsc.asc %(filename)s\n" % locals())
254         fileHandle.write('\nexit')
255         fileHandle.close()
256     finally:
257         f.close()
258
259     commands.getoutput("chmod 777 /tmp/py2deb.tmp")
260     commands.getoutput("/tmp/py2deb.tmp")
261
262     return filename
263
264
265 class Py2tar(object):
266
267     def __init__(self, dataDirectoryPath):
268         self._dataDirectoryPath = dataDirectoryPath
269
270     def packed(self):
271         return self._getSourcesFiles()
272
273     def _getSourcesFiles(self):
274         directoryPath = self._dataDirectoryPath
275
276         outputFileObj = StringIO.StringIO() # TODO: Do more transparently?
277
278         tarOutput = TarFile.open('sources',
279                                  mode = "w:gz",
280                                  fileobj = outputFileObj)
281
282         # Note: We can't use this because we need to fiddle permissions:
283         #       tarOutput.add(directoryPath, arcname = "")
284
285         for root, dirs, files in os.walk(directoryPath):
286             archiveRoot = root[len(directoryPath):]
287
288             tarinfo = tarOutput.gettarinfo(root, archiveRoot)
289             # TODO: Make configurable?
290             tarinfo.uid = UID_ROOT
291             tarinfo.gid = GID_ROOT
292             tarinfo.uname = ""
293             tarinfo.gname = ""
294             tarOutput.addfile(tarinfo)
295
296             for f in  files:
297                 tarinfo = tarOutput.gettarinfo(os.path.join(root, f),
298                                                os.path.join(archiveRoot, f))
299                 tarinfo.uid = UID_ROOT
300                 tarinfo.gid = GID_ROOT
301                 tarinfo.uname = ""
302                 tarinfo.gname = ""
303                 tarOutput.addfile(tarinfo, file(os.path.join(root, f)))
304
305         tarOutput.close()
306
307         data_tar_gz = outputFileObj.getvalue()
308
309         return data_tar_gz
310
311
312 def py2tar(DEST, TEMP, name, version):
313     tarcontent = Py2tar("%(DEST)s" % locals())
314     filename = "%(TEMP)s/%(name)s_%(version)s.tar.gz" % locals()
315     f = open(filename, "wb")
316     try:
317         f.write(tarcontent.packed())
318     finally:
319         f.close()
320     return filename
321
322
323 class Py2debException(Exception):
324     pass
325
326
327 SECTIONS_BY_POLICY = {
328     # http://www.debian.org/doc/debian-policy/ch-archive.html#s-subsections
329     "debian": "admin, base, comm, contrib, devel, doc, editors, electronics, embedded, games, gnome, graphics, hamradio, interpreters, kde, libs, libdevel, mail, math, misc, net, news, non-free, oldlibs, otherosfs, perl, python, science, shells, sound, tex, text, utils, web, x11",
330     # http://maemo.org/forrest-images/pdf/maemo-policy.pdf
331     "chinook": "accessories, communication, games, multimedia, office, other, programming, support, themes, tools",
332     # http://wiki.maemo.org/Task:Package_categories
333     "diablo": "user/desktop, user/development, user/education, user/games, user/graphics, user/multimedia, user/navigation, user/network, user/office, user/science, user/system, user/utilities",
334     # http://wiki.maemo.org/Task:Fremantle_application_categories
335     "mer": "user/desktop, user/development, user/education, user/games, user/graphics, user/multimedia, user/navigation, user/network, user/office, user/science, user/system, user/utilities",
336     # http://wiki.maemo.org/Task:Fremantle_application_categories
337     "fremantle": "user/desktop, user/development, user/education, user/games, user/graphics, user/multimedia, user/navigation, user/network, user/office, user/science, user/system, user/utilities",
338 }
339
340
341 class Py2deb(object):
342     """
343     heavily based on technic described here :
344     http://wiki.showmedo.com/index.php?title=LinuxJensMakingDeb
345     """
346     ## STATICS
347     clear=False  # clear build folder after py2debianization
348
349     SECTIONS = SECTIONS_BY_POLICY["debian"]
350
351     #http://www.debian.org/doc/debian-policy/footnotes.html#f69
352     ARCHS="all i386 ia64 alpha amd64 armeb arm hppa m32r m68k mips mipsel powerpc ppc64 s390 s390x sh3 sh3eb sh4 sh4eb sparc darwin-i386 darwin-ia64 darwin-alpha darwin-amd64 darwin-armeb darwin-arm darwin-hppa darwin-m32r darwin-m68k darwin-mips darwin-mipsel darwin-powerpc darwin-ppc64 darwin-s390 darwin-s390x darwin-sh3 darwin-sh3eb darwin-sh4 darwin-sh4eb darwin-sparc freebsd-i386 freebsd-ia64 freebsd-alpha freebsd-amd64 freebsd-armeb freebsd-arm freebsd-hppa freebsd-m32r freebsd-m68k freebsd-mips freebsd-mipsel freebsd-powerpc freebsd-ppc64 freebsd-s390 freebsd-s390x freebsd-sh3 freebsd-sh3eb freebsd-sh4 freebsd-sh4eb freebsd-sparc kfreebsd-i386 kfreebsd-ia64 kfreebsd-alpha kfreebsd-amd64 kfreebsd-armeb kfreebsd-arm kfreebsd-hppa kfreebsd-m32r kfreebsd-m68k kfreebsd-mips kfreebsd-mipsel kfreebsd-powerpc kfreebsd-ppc64 kfreebsd-s390 kfreebsd-s390x kfreebsd-sh3 kfreebsd-sh3eb kfreebsd-sh4 kfreebsd-sh4eb kfreebsd-sparc knetbsd-i386 knetbsd-ia64 knetbsd-alpha knetbsd-amd64 knetbsd-armeb knetbsd-arm knetbsd-hppa knetbsd-m32r knetbsd-m68k knetbsd-mips knetbsd-mipsel knetbsd-powerpc knetbsd-ppc64 knetbsd-s390 knetbsd-s390x knetbsd-sh3 knetbsd-sh3eb knetbsd-sh4 knetbsd-sh4eb knetbsd-sparc netbsd-i386 netbsd-ia64 netbsd-alpha netbsd-amd64 netbsd-armeb netbsd-arm netbsd-hppa netbsd-m32r netbsd-m68k netbsd-mips netbsd-mipsel netbsd-powerpc netbsd-ppc64 netbsd-s390 netbsd-s390x netbsd-sh3 netbsd-sh3eb netbsd-sh4 netbsd-sh4eb netbsd-sparc openbsd-i386 openbsd-ia64 openbsd-alpha openbsd-amd64 openbsd-armeb openbsd-arm openbsd-hppa openbsd-m32r openbsd-m68k openbsd-mips openbsd-mipsel openbsd-powerpc openbsd-ppc64 openbsd-s390 openbsd-s390x openbsd-sh3 openbsd-sh3eb openbsd-sh4 openbsd-sh4eb openbsd-sparc hurd-i386 hurd-ia64 hurd-alpha hurd-amd64 hurd-armeb hurd-arm hurd-hppa hurd-m32r hurd-m68k hurd-mips hurd-mipsel hurd-powerpc hurd-ppc64 hurd-s390 hurd-s390x hurd-sh3 hurd-sh3eb hurd-sh4 hurd-sh4eb hurd-sparc".split(" ")
353
354     # license terms taken from dh_make
355     LICENSES=["gpl", "lgpl", "bsd", "artistic"]
356
357     def __setitem__(self, path, files):
358
359         if not type(files)==list:
360             raise Py2debException("value of key path '%s' is not a list"%path)
361         if not files:
362             raise Py2debException("value of key path '%s' should'nt be empty"%path)
363         if not path.startswith("/"):
364             raise Py2debException("key path '%s' malformed (don't start with '/')"%path)
365         if path.endswith("/"):
366             raise Py2debException("key path '%s' malformed (shouldn't ends with '/')"%path)
367
368         nfiles=[]
369         for file in files:
370
371             if ".." in file:
372                 raise Py2debException("file '%s' contains '..', please avoid that!"%file)
373
374
375             if "|" in file:
376                 if file.count("|")!=1:
377                     raise Py2debException("file '%s' is incorrect (more than one pipe)"%file)
378
379                 file, nfile = file.split("|")
380             else:
381                 nfile=file  # same localisation
382
383             if os.path.isdir(file):
384                 raise Py2debException("file '%s' is a folder, and py2deb refuse folders !"%file)
385
386             if not os.path.isfile(file):
387                 raise Py2debException("file '%s' doesn't exist"%file)
388
389             if file.startswith("/"):    # if an absolute file is defined
390                 if file==nfile:         # and not renamed (pipe trick)
391                     nfile=os.path.basename(file)   # it's simply copied to 'path'
392
393             nfiles.append((file, nfile))
394
395         nfiles.sort(lambda a, b: cmp(a[1], b[1]))    #sort according new name (nfile)
396
397         self.__files[path]=nfiles
398
399     def __delitem__(self, k):
400         del self.__files[k]
401
402     def __init__(self,
403                     name,
404                     description="no description",
405                     license="gpl",
406                     depends="",
407                     section="utils",
408                     arch="all",
409
410                     url="",
411                     author = None,
412                     mail = None,
413
414                     preinstall = None,
415                     postinstall = None,
416                     preremove = None,
417                     postremove = None
418                 ):
419
420         if author is None:
421             author = ("USERNAME" in os.environ) and os.environ["USERNAME"] or None
422             if author is None:
423                 author = ("USER" in os.environ) and os.environ["USER"] or "unknown"
424
425         if mail is None:
426             mail = author+"@"+socket.gethostname()
427
428         self.name = name
429         self.description = description
430         self.license = license
431         self.depends = depends
432         self.recommends = ""
433         self.section = section
434         self.arch = arch
435         self.url = url
436         self.author = author
437         self.mail = mail
438         self.icon = ""
439
440         self.preinstall = preinstall
441         self.postinstall = postinstall
442         self.preremove = preremove
443         self.postremove = postremove
444
445         self.__files={}
446
447     def __repr__(self):
448         name = self.name
449         license = self.license
450         description = self.description
451         depends = self.depends
452         recommends = self.recommends
453         section = self.section
454         arch = self.arch
455         url = self.url
456         author = self.author
457         mail = self.mail
458
459         preinstall = self.preinstall
460         postinstall = self.postinstall
461         preremove = self.preremove
462         postremove = self.postremove
463
464         paths=self.__files.keys()
465         paths.sort()
466         files=[]
467         for path in paths:
468             for file, nfile in self.__files[path]:
469                 #~ rfile=os.path.normpath(os.path.join(path, nfile))
470                 rfile=os.path.join(path, nfile)
471                 if nfile==file:
472                     files.append(rfile)
473                 else:
474                     files.append(rfile + " (%s)"%file)
475
476         files.sort()
477         files = "\n".join(files)
478
479
480         lscripts = [    preinstall and "preinst",
481                         postinstall and "postinst",
482                         preremove and "prerm",
483                         postremove and "postrm",
484                     ]
485         scripts = lscripts and ", ".join([i for i in lscripts if i]) or "None"
486         return """
487 ----------------------------------------------------------------------
488 NAME        : %(name)s
489 ----------------------------------------------------------------------
490 LICENSE     : %(license)s
491 URL         : %(url)s
492 AUTHOR      : %(author)s
493 MAIL        : %(mail)s
494 ----------------------------------------------------------------------
495 DEPENDS     : %(depends)s
496 RECOMMENDS  : %(recommends)s
497 ARCH        : %(arch)s
498 SECTION     : %(section)s
499 ----------------------------------------------------------------------
500 DESCRIPTION :
501 %(description)s
502 ----------------------------------------------------------------------
503 SCRIPTS : %(scripts)s
504 ----------------------------------------------------------------------
505 FILES :
506 %(files)s
507 """ % locals()
508
509     def generate(self, version, changelog="", rpm=False, src=False, build=True, tar=False, changes=False, dsc=False):
510         """ generate a deb of version 'version', with or without 'changelog', with or without a rpm
511             (in the current folder)
512             return a list of generated files
513         """
514         if not sum([len(i) for i in self.__files.values()])>0:
515             raise Py2debException("no files are defined")
516
517         if not changelog:
518             changelog="* no changelog"
519
520         name = self.name
521         description = self.description
522         license = self.license
523         depends = self.depends
524         recommends = self.recommends
525         section = self.section
526         arch = self.arch
527         url = self.url
528         author = self.author
529         mail = self.mail
530         files = self.__files
531         preinstall = self.preinstall
532         postinstall = self.postinstall
533         preremove = self.preremove
534         postremove = self.postremove
535
536         if section not in Py2deb.SECTIONS:
537             raise Py2debException("section '%s' is unknown (%s)" % (section, str(Py2deb.SECTIONS)))
538
539         if arch not in Py2deb.ARCHS:
540             raise Py2debException("arch '%s' is unknown (%s)"% (arch, str(Py2deb.ARCHS)))
541
542         if license not in Py2deb.LICENSES:
543             raise Py2debException("License '%s' is unknown (%s)" % (license, str(Py2deb.LICENSES)))
544
545         # create dates (buildDate, buildDateYear)
546         d=datetime.now()
547         buildDate=d.strftime("%a, %d %b %Y %H:%M:%S +0000")
548         buildDateYear=str(d.year)
549
550
551         #clean description (add a space before each next lines)
552         description=description.replace("\r", "").strip()
553         description = "\n ".join(description.split("\n"))
554
555         #clean changelog (add 2 spaces before each next lines)
556         changelog=changelog.replace("\r", "").strip()
557         changelog = "\n  ".join(changelog.split("\n"))
558
559
560         TEMP = ".py2deb_build_folder"
561         DEST = os.path.join(TEMP, name)
562         DEBIAN = os.path.join(DEST, "debian")
563
564         # let's start the process
565         try:
566             shutil.rmtree(TEMP)
567         except:
568             pass
569
570         os.makedirs(DEBIAN)
571         try:
572             rules=[]
573             dirs=[]
574             for path in files:
575                 for ofile, nfile in files[path]:
576                     if os.path.isfile(ofile):
577                         # it's a file
578
579                         if ofile.startswith("/"): # if absolute path
580                             # we need to change dest
581                             dest=os.path.join(DEST, nfile)
582                         else:
583                             dest=os.path.join(DEST, ofile)
584
585                         # copy file to be packaged
586                         destDir = os.path.dirname(dest)
587                         if not os.path.isdir(destDir):
588                             os.makedirs(destDir)
589
590                         shutil.copy2(ofile, dest)
591
592                         ndir = os.path.join(path, os.path.dirname(nfile))
593                         nname = os.path.basename(nfile)
594
595                         # make a line RULES to be sure the destination folder is created
596                         # and one for copying the file
597                         fpath = "/".join(["$(CURDIR)", "debian", name+ndir])
598                         rules.append('mkdir -p "%s"' % fpath)
599                         rules.append('cp -a "%s" "%s"' % (ofile, os.path.join(fpath, nname)))
600
601                         # append a dir
602                         dirs.append(ndir)
603
604                     else:
605                         raise Py2debException("unknown file '' "%ofile) # shouldn't be raised (because controlled before)
606
607             # make rules right
608             rules= "\n\t".join(rules) + "\n"
609
610             # make dirs right
611             dirs= [i[1:] for i in set(dirs)]
612             dirs.sort()
613
614             #==========================================================================
615             # CREATE debian/dirs
616             #==========================================================================
617             open(os.path.join(DEBIAN, "dirs"), "w").write("\n".join(dirs))
618
619             #==========================================================================
620             # CREATE debian/changelog
621             #==========================================================================
622             clog="""%(name)s (%(version)s) stable; urgency=low
623
624   %(changelog)s
625
626  -- %(author)s <%(mail)s>  %(buildDate)s
627 """ % locals()
628
629             open(os.path.join(DEBIAN, "changelog"), "w").write(clog)
630
631             #==========================================================================
632             #Create pre/post install/remove
633             #==========================================================================
634             def mkscript(name, dest):
635                 if name and name.strip()!="":
636                     if os.path.isfile(name):    # it's a file
637                         content = file(name).read()
638                     else:   # it's a script
639                         content = name
640                     open(os.path.join(DEBIAN, dest), "w").write(content)
641
642             mkscript(preinstall, "preinst")
643             mkscript(postinstall, "postinst")
644             mkscript(preremove, "prerm")
645             mkscript(postremove, "postrm")
646
647
648             #==========================================================================
649             # CREATE debian/compat
650             #==========================================================================
651             open(os.path.join(DEBIAN, "compat"), "w").write("5\n")
652
653             #==========================================================================
654             # CREATE debian/control
655             #==========================================================================
656             txt="""Source: %(name)s
657 Section: %(section)s
658 Priority: extra
659 Maintainer: %(author)s <%(mail)s>
660 Build-Depends: debhelper (>= 5)
661 Standards-Version: 3.7.2
662
663 Package: %(name)s
664 Architecture: %(arch)s
665 Depends: %(depends)s
666 Recommends: %(recommends)s
667 Description: %(description)s""" % locals()
668             if self.icon:
669                 f = open(self.icon, "rb")
670                 try:
671                     rawIcon = f.read()
672                 finally:
673                     f.close()
674                 uueIcon = base64.b64encode(rawIcon)
675                 uueIconLines = []
676                 for i, c in enumerate(uueIcon):
677                     if i % 60 == 0:
678                         uueIconLines.append("")
679                     uueIconLines[-1] += c
680                 uueIconLines[0:0] = ("XB-Maemo-Icon-26:", )
681                 txt = "\n".join((txt, "\n  ".join(uueIconLines), ""))
682             open(os.path.join(DEBIAN, "control"), "w").write(txt)
683
684             #==========================================================================
685             # CREATE debian/copyright
686             #==========================================================================
687             copy={}
688             copy["gpl"]="""
689     This package is free software; you can redistribute it and/or modify
690     it under the terms of the GNU General Public License as published by
691     the Free Software Foundation; either version 2 of the License, or
692     (at your option) any later version.
693
694     This package is distributed in the hope that it will be useful,
695     but WITHOUT ANY WARRANTY; without even the implied warranty of
696     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
697     GNU General Public License for more details.
698
699     You should have received a copy of the GNU General Public License
700     along with this package; if not, write to the Free Software
701     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
702
703 On Debian systems, the complete text of the GNU General
704 Public License can be found in `/usr/share/common-licenses/GPL'.
705 """
706             copy["lgpl"]="""
707     This package is free software; you can redistribute it and/or
708     modify it under the terms of the GNU Lesser General Public
709     License as published by the Free Software Foundation; either
710     version 2 of the License, or (at your option) any later version.
711
712     This package is distributed in the hope that it will be useful,
713     but WITHOUT ANY WARRANTY; without even the implied warranty of
714     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
715     Lesser General Public License for more details.
716
717     You should have received a copy of the GNU Lesser General Public
718     License along with this package; if not, write to the Free Software
719     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
720
721 On Debian systems, the complete text of the GNU Lesser General
722 Public License can be found in `/usr/share/common-licenses/LGPL'.
723 """
724             copy["bsd"]="""
725     Redistribution and use in source and binary forms, with or without
726     modification, are permitted under the terms of the BSD License.
727
728     THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
729     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
730     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
731     ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
732     FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
733     DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
734     OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
735     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
736     LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
737     OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
738     SUCH DAMAGE.
739
740 On Debian systems, the complete text of the BSD License can be
741 found in `/usr/share/common-licenses/BSD'.
742 """
743             copy["artistic"]="""
744     This program is free software; you can redistribute it and/or modify it
745     under the terms of the "Artistic License" which comes with Debian.
746
747     THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
748     WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES
749     OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
750
751 On Debian systems, the complete text of the Artistic License
752 can be found in `/usr/share/common-licenses/Artistic'.
753 """
754
755             txtLicense = copy[license]
756             pv=__version__
757             txt="""This package was py2debianized(%(pv)s) by %(author)s <%(mail)s> on
758 %(buildDate)s.
759
760 It was downloaded from %(url)s
761
762 Upstream Author: %(author)s <%(mail)s>
763
764 Copyright: %(buildDateYear)s by %(author)s
765
766 License:
767
768 %(txtLicense)s
769
770 The Debian packaging is (C) %(buildDateYear)s, %(author)s <%(mail)s> and
771 is licensed under the GPL, see above.
772
773
774 # Please also look if there are files or directories which have a
775 # different copyright/license attached and list them here.
776 """ % locals()
777             open(os.path.join(DEBIAN, "copyright"), "w").write(txt)
778
779             #==========================================================================
780             # CREATE debian/rules
781             #==========================================================================
782             txt="""#!/usr/bin/make -f
783 # -*- makefile -*-
784 # Sample debian/rules that uses debhelper.
785 # This file was originally written by Joey Hess and Craig Small.
786 # As a special exception, when this file is copied by dh-make into a
787 # dh-make output file, you may use that output file without restriction.
788 # This special exception was added by Craig Small in version 0.37 of dh-make.
789
790 # Uncomment this to turn on verbose mode.
791 #export DH_VERBOSE=1
792
793
794
795
796 CFLAGS = -Wall -g
797
798 ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
799         CFLAGS += -O0
800 else
801         CFLAGS += -O2
802 endif
803
804 configure: configure-stamp
805 configure-stamp:
806         dh_testdir
807         # Add here commands to configure the package.
808
809         touch configure-stamp
810
811
812 build: build-stamp
813
814 build-stamp: configure-stamp
815         dh_testdir
816         touch build-stamp
817
818 clean:
819         dh_testdir
820         dh_testroot
821         rm -f build-stamp configure-stamp
822         dh_clean
823
824 install: build
825         dh_testdir
826         dh_testroot
827         dh_clean -k
828         dh_installdirs
829
830         # ======================================================
831         #$(MAKE) DESTDIR="$(CURDIR)/debian/%(name)s" install
832         mkdir -p "$(CURDIR)/debian/%(name)s"
833
834         %(rules)s
835         # ======================================================
836
837 # Build architecture-independent files here.
838 binary-indep: build install
839 # We have nothing to do by default.
840
841 # Build architecture-dependent files here.
842 binary-arch: build install
843         dh_testdir
844         dh_testroot
845         dh_installchangelogs debian/changelog
846         dh_installdocs
847         dh_installexamples
848 #       dh_install
849 #       dh_installmenu
850 #       dh_installdebconf
851 #       dh_installlogrotate
852 #       dh_installemacsen
853 #       dh_installpam
854 #       dh_installmime
855 #       dh_python
856 #       dh_installinit
857 #       dh_installcron
858 #       dh_installinfo
859         dh_installman
860         dh_link
861         dh_strip
862         dh_compress
863         dh_fixperms
864 #       dh_perl
865 #       dh_makeshlibs
866         dh_installdeb
867         dh_shlibdeps
868         dh_gencontrol
869         dh_md5sums
870         dh_builddeb
871
872 binary: binary-indep binary-arch
873 .PHONY: build clean binary-indep binary-arch binary install configure
874 """ % locals()
875             open(os.path.join(DEBIAN, "rules"), "w").write(txt)
876             os.chmod(os.path.join(DEBIAN, "rules"), 0755)
877
878             ###########################################################################
879             ###########################################################################
880             ###########################################################################
881
882             generatedFiles = []
883
884             if build:
885                 #http://www.debian.org/doc/manuals/maint-guide/ch-build.fr.html
886                 ret=os.system('cd "%(DEST)s"; dpkg-buildpackage -tc -rfakeroot -us -uc' % locals())
887                 if ret != 0:
888                     raise Py2debException("buildpackage failed (see output)")
889
890                 l=glob("%(TEMP)s/%(name)s*.deb"%locals())
891                 if len(l) != 1:
892                     raise Py2debException("didn't find builded deb")
893
894                 tdeb = l[0]
895                 deb = os.path.basename(tdeb)
896                 shutil.move(tdeb, deb)
897
898                 generatedFiles = [deb, ]
899
900                 if rpm:
901                     rpmFilename = deb2rpm(deb)
902                     generatedFiles.append(rpmFilename)
903
904                 if src:
905                     tarFilename = py2src(TEMP, name)
906                     generatedFiles.append(tarFilename)
907
908             if tar:
909                 tarFilename = py2tar(DEST, TEMP, name, version)
910                 generatedFiles.append(tarFilename)
911
912             if dsc:
913                 dscFilename = py2dsc(TEMP, name, version, depends, author, mail, arch)
914                 generatedFiles.append(dscFilename)
915
916             if changes:
917                 changesFilenames = py2changes(locals())
918                 generatedFiles.extend(changesFilenames)
919
920             return generatedFiles
921
922         #~ except Exception,m:
923             #~ raise Py2debException("build error :"+str(m))
924
925         finally:
926             if Py2deb.clear:
927                 shutil.rmtree(TEMP)
928
929
930 if __name__ == "__main__":
931     try:
932         os.chdir(os.path.dirname(sys.argv[0]))
933     except:
934         pass
935
936     p=Py2deb("python-py2deb")
937     p.description="Generate simple deb(/rpm/tgz) from python (2.4, 2.5 and 2.6)"
938     p.url = "http://www.manatlan.com/page/py2deb"
939     p.author=__author__
940     p.mail=__mail__
941     p.depends = "dpkg-dev, fakeroot, alien, python"
942     p.section="python"
943     p["/usr/lib/python2.6/dist-packages"] = ["py2deb.py", ]
944     p["/usr/lib/python2.5/site-packages"] = ["py2deb.py", ]
945     p["/usr/lib/python2.4/site-packages"] = ["py2deb.py", ]
946     #~ p.postinstall = "s.py"
947     #~ p.preinstall = "s.py"
948     #~ p.postremove = "s.py"
949     #~ p.preremove = "s.py"
950     print p
951     print p.generate(__version__, changelog = __doc__, src=True)