File: bdist_deb.py

package info (click to toggle)
taskcoach 1.4.1-4
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 32,496 kB
  • ctags: 17,810
  • sloc: python: 72,170; makefile: 254; ansic: 120; xml: 29; sh: 16
file content (302 lines) | stat: -rw-r--r-- 12,560 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
'''
Task Coach - Your friendly task manager
Copyright (C) 2004-2014 Task Coach developers <developers@taskcoach.org>

Task Coach is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Task Coach is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
'''

from distutils import log, errors
from distutils.core import Command
from distutils.file_util import copy_file, move_file
import os
import tarfile
import shutil
import textwrap
import time
import glob


class bdist_deb(Command, object):
    # pylint: disable=W0201
    description = 'create a Debian (.deb) package'
    
    user_options = [
        ('sdist=', None, 'Source distribution .tar.gz archive'),
        ('bdist-base=', None, 
         'base directory for creating built distributions [build]'),
        ('dist-dir=', 'd', 'directory to put final deb files in [dist]'),
        ('package=', None, 'package name of the application'),
        ('section=', None, 
         'section of the menu to put the application in [Applications]'),
        ('subsection=', None, 
         'subsection of the menu to put the application in'),
        ('title=', None, 'title of the application'),
        ('description=', None, 
         'brief single line description of the application'),
        ('long-description=', None, 'long description of the application'),
        ('version=', None, 'version of the application'),
        ('package-version=', None, 'version of the package [1]'),
        ('changelog-content=', None, 'Content of the changelog file'),
        ('distribution=', None, 'distribution of the package [UNRELEASED]'),
        ('command=', None, 'command to start the application'),
        ('priority=', None, 'priority of the deb package [optional]'),
        ('urgency=', None, 'urgency of the deb package [low]'),
        ('maintainer=', None, 'maintainer of the deb package [<author>]'),
        ('maintainer-email=', None, 
         'email address of the package maintainer [<author-email>]'),
        ('author=', None, 'author of the application'),
        ('author-email=', None, 'email address of the application author'),
        ('copyright=', None, 'copyright notice of the application'),
        ('license=', None, 'license (title and version) of the application'),
        ('license-abbrev=', None, 'abbreviated license title, e.g. "GPLv3"'),
        ('license-summary=', None, 'summary of the license of the application'),
        ('license-path=', None, 'path of the license on Debian systems'),
        ('wxpythonversion=', None, 'minimal wxPython version needed'),
        ('pythonversion=', None, 'minimal Python version needed'),
        ('twistedversion=', None, 'minimal Twisted version needed'),
        ('sdist-exclude=', None, 
         'dirs and files in the source distribution to exclude from the deb package'),
        ('url=', None, 'url of the application homepage'),
        ('architecture=', None, 'architecure of the deb package [all]')]

    def initialize_options(self):
        self.bdist_base = 'build'
        self.dist_dir = 'dist'
        self.section = 'Applications'
        self.priority = 'optional'
        self.urgency = 'low'
        self.architecture = 'all'
        self.changelog_content = ''
        self.sdist = self.package = self.subsection = self.title = \
            self.description = self.long_description = self.command = \
            self.maintainer = self.maintainer_email = self.author = \
            self.author_email = self.copyright = self.license = \
            self.license_abbrev = self.license_summary = self.license_path = \
            self.url = self.version = self.package_version = \
            self.distribution = self.wxpythonversion = self.pythonversion = \
            self.twistedversion = self.sdist_exclude = None
                    
    def finalize_options(self):
        mandatoryOptions = [\
            ('package', 'the package name'),
            ('subsection', 'a subsection for the menu'),
            ('title', 'a title for the menu'),
            ('description', 'a brief description for the menu'),
            ('long_description', 'a long description of the application'),
            ('version', 'the version of the application'),
            ('copyright', 
             'a copyright description ("Copyright (C) year-year, author")'),
            ('license', 'the title (including version) of the license'),
            ('license_abbrev', 'an abbreviated license title'),
            ('license_summary', 'a summary of the license of the application'),
            ('license_path', 'the path of the license on Debian systems'),
            ('command', 'the command to start the application'),
            ('author', 'the author of the application'),
            ('author_email', 'the email address of the author'),
            ('url', 'the url of the application homepage')]
        for option, description in mandatoryOptions:
            if not getattr(self, option):
                raise errors.DistutilsOptionError, \
                    'you must provide %s (--%s)' % (description, option)
        if not self.maintainer:
            self.maintainer = self.author
            self.maintainer_email = self.author_email
        if not self.package_version:
            self.package_version = '1'
        if not self.distribution:
            self.distribution = 'UNRELEASED'
        if not self.wxpythonversion:
            self.wxpythonversion = '2.8'
        if not self.twistedversion:
            self.twistedversion = '10.0'
        if not self.pythonversion:
            self.pythonversion = '2.6'
        self.sdist_exclude = self.sdist_exclude.split(',') \
            if self.sdist_exclude else []
        self.subsection_lower = self.subsection.lower()
        self.datetime = time.strftime('%a, %d %b %Y %H:%M:%S +0000', 
                                      time.gmtime())
        self.year = time.gmtime()[0]
        self.license_summary = self.wrap_paragraphs(self.license_summary,
                                                    indent='    ')
        self.long_description = self.wrap_paragraphs(self.long_description, 
                                                     indent=' ')
        
    def run(self):
        self.copy_sdist()
        self.untar_sdist()
        self.remove_sdist_excludes()
        self.create_debian_dir()
        self.write_debian_files()
        self.build_debian_package()
        self.move_debian_package_to_distdir()
    
    def copy_sdist(self):
        ''' Copy the source distribution (.tar.gz archive) to the build dir. '''
        (dest_name, dummy_copied) = copy_file(self.sdist, self.bdist_base)
        orig_name = dest_name[:-len('.tar.gz')] + '.orig.tar.gz'
        orig_name = orig_name.lower()
        if os.path.exists(orig_name):
            os.remove(orig_name)
        self.sdist_archive = move_file(dest_name, orig_name)
        
    def untar_sdist(self):
        ''' Unzip and extract the source distribution archive. '''
        expected_extracted_dir = self.sdist_archive[:-len('.orig.tar.gz')] + '/'
        if self.verbose:
            log.info('extracting %s to %s' % (self.sdist_archive, 
                                              expected_extracted_dir))
        archive = tarfile.open(self.sdist_archive)
        extracted_dir = os.path.join(self.bdist_base, archive.getnames()[0])
        archive.extractall(self.bdist_base)
        archive.close()
        if expected_extracted_dir != extracted_dir:
            if os.path.exists(expected_extracted_dir):
                shutil.rmtree(expected_extracted_dir)
            os.rename(extracted_dir, expected_extracted_dir)
        self.extracted_dir = expected_extracted_dir
        
    def remove_sdist_excludes(self):
        ''' Remove directories and files from the source distribution that are 
            not needed for the debian package. '''
        for relative_path in self.sdist_exclude:
            absolute_path = os.path.join(self.extracted_dir, relative_path)
            if self.verbose:
                log.info('removing %s from source distribution' % relative_path)
            if os.path.isdir(absolute_path):
                shutil.rmtree(absolute_path)
            else:
                os.remove(absolute_path)

    def create_debian_dir(self):
        ''' Create the debian dir for package files. '''
        self.debian_dir = os.path.join(self.extracted_dir, 'debian')
        if self.verbose:
            log.info('creating %s' % self.debian_dir)
        os.mkdir(self.debian_dir)

    def write_debian_files(self):
        ''' Create the different package files in the debian folder. '''
        debian_files = dict(rules=rules, compat='5\n', pycompat='2\n',
            menu=menu, control=control, copyright=self.copyright_contents(),
            changelog=changelog)
        for filename, contents in debian_files.iteritems():
            self.write_debian_file(filename, contents % self.__dict__)
               
    def write_debian_file(self, filename, contents):
        filename = os.path.join(self.debian_dir, filename)
        if self.verbose:
            log.info('writing %s' % filename)
        fd = file(filename, 'w')
        fd.write(contents)
        fd.close()
        os.chmod(filename, 0755)
        
    def build_debian_package(self):
        os.system('cd %s; debuild -S; dpkg-buildpackage -rfakeroot; cd ..' % \
                  self.extracted_dir)

    def move_debian_package_to_distdir(self):
        for deb_package in glob.glob('build/*.deb'):
            move_file(deb_package, self.dist_dir)
        
    def copyright_contents(self):
        ''' Create copyright contents. This is a bit complicated because the
            header and footer need to have their variables filled in first 
            before their text can be wrapped. '''
        return self.wrap_paragraphs(copyright_header % self.__dict__) + \
            copyright + self.wrap_paragraphs(copyright_footer % self.__dict__)

    def wrap_paragraphs(self, text, indent=''):
        paragraphs = text.split('\n\n')
        paragraphs = ['\n'.join(textwrap.wrap(paragraph, width=76, 
                      initial_indent=indent, subsequent_indent=indent)) \
                      for paragraph in paragraphs]
        return '\n\n'.join(paragraphs)


rules = '''#!/usr/bin/make -f

DEB_PYTHON_SYSTEM := pysupport

include /usr/share/cdbs/1/rules/simple-patchsys.mk
include /usr/share/cdbs/1/rules/debhelper.mk
include /usr/share/cdbs/1/class/python-distutils.mk

binary-install/taskcoach::
\tdh_desktop
\tdh_icons
'''

menu = '''?package(%(package)s):needs="X11"\\
 section="%(section)s/%(subsection)s"\\
 title="%(title)s"\\
 description="%(description)s"\\
 command="%(command)s"
'''

control = '''Source: %(package)s
Section: %(section)s
Priority: %(priority)s
Maintainer: %(maintainer)s <%(maintainer_email)s>
Build-Depends: cdbs (>= 0.4.43), debhelper (>= 5), python, dpatch
Build-Depends-Indep: python-support (>= 0.5.3)
Standards-Version: 3.7.3
Homepage: %(url)s

Package: %(package)s
Architecture: %(architecture)s
Depends: python (>= %(pythonversion)s), python-wxgtk2.8 (>= %(wxpythonversion)s), python-wxversion, python-twisted (>= %(twistedversion)s), libxss1, ttf-dejavu
Recommends: python-notify, libgnome2-0, libavahi-compat-libdnssd1
Suggests: python-kde4
Description: %(description)s.
%(long_description)s
'''

copyright_header = '''This package was debianized by %(maintainer)s 
<%(maintainer_email)s> on %(datetime)s.
'''

copyright = '''

It was downloaded from %(url)s

Upstream Author: 

    %(author)s <%(author_email)s>

Copyright: 

    %(copyright)s

License:

%(license_summary)s

'''

copyright_footer = '''On Debian systems, the complete text of the %(license)s
can be found in '%(license_path)s'.

The Debian packaging is (C) %(year)s, %(maintainer)s <%(maintainer_email)s> and
is licensed under the %(license_abbrev)s, see above.

'''

changelog = '''%(package)s (%(version)s-%(package_version)s) %(distribution)s; urgency=%(urgency)s

%(changelog_content)s

 -- %(maintainer)s <%(maintainer_email)s>  %(datetime)s
'''