| 1 | # bdist_debian.py |
|---|
| 2 | # |
|---|
| 3 | # Add 'bdist_debian' Debian binary package distribution support to 'distutils'. |
|---|
| 4 | # |
|---|
| 5 | # Written by: Gene Cash <gene.cash@gmail.com> 16-NOV-2007 |
|---|
| 6 | # Extended by: Chris Freeze <cfreeze@users.sourceforge.net> 11/08/2008 |
|---|
| 7 | # * Removed Nokia specific items |
|---|
| 8 | # * Added other keywords that can be in the control file |
|---|
| 9 | |
|---|
| 10 | import os, base64 |
|---|
| 11 | from distutils.core import Command, Distribution |
|---|
| 12 | from distutils.dir_util import remove_tree |
|---|
| 13 | from distutils.util import byte_compile |
|---|
| 14 | |
|---|
| 15 | # make these legal keywords for setup() |
|---|
| 16 | Distribution.icon=None |
|---|
| 17 | Distribution.section=None |
|---|
| 18 | Distribution.depends=None |
|---|
| 19 | Distribution.recommends=None |
|---|
| 20 | Distribution.suggests=None |
|---|
| 21 | Distribution.architecture=None |
|---|
| 22 | Distribution.essential=None |
|---|
| 23 | Distribution.source=None |
|---|
| 24 | |
|---|
| 25 | class ControlFile(object): |
|---|
| 26 | def __init__(self, Installed_Size=0, Long_Description='', Description='', Icon='', **kwargs): |
|---|
| 27 | self.options=kwargs |
|---|
| 28 | self.description=Description |
|---|
| 29 | self.long_description=Long_Description |
|---|
| 30 | self.icon=Icon |
|---|
| 31 | self.installed_size=Installed_Size |
|---|
| 32 | |
|---|
| 33 | def getContent(self): |
|---|
| 34 | content=['%s: %s' % (k, v) for k,v in self.options.iteritems()] |
|---|
| 35 | |
|---|
| 36 | content.append('Installed-Size: %d' % self.installed_size) |
|---|
| 37 | if self.description != 'UNKNOWN': |
|---|
| 38 | content.append('Description: %s' % self.description.strip()) |
|---|
| 39 | if self.long_description != 'UNKNOWN': |
|---|
| 40 | self.long_description=self.long_description.replace('\n', '\n ') |
|---|
| 41 | content.append(' '+self.long_description.strip()) |
|---|
| 42 | |
|---|
| 43 | if self.icon: |
|---|
| 44 | # generate Base64-encoded icon |
|---|
| 45 | s=file(self.icon, 'rb').read() |
|---|
| 46 | x=base64.b64encode(s) |
|---|
| 47 | # wrap width MUST be 76 characters to make application manager happy after install |
|---|
| 48 | lw=76 |
|---|
| 49 | # trailing blank is important, and the XB- is NOT legal |
|---|
| 50 | content.append('XB-Maemo-Icon-26: ') |
|---|
| 51 | for i in range(0, len(x), lw): |
|---|
| 52 | content.append(' '+x[i:i+lw]) |
|---|
| 53 | |
|---|
| 54 | # must have two returns |
|---|
| 55 | return '\n'.join(content)+'\n\n' |
|---|
| 56 | |
|---|
| 57 | class bdist_debian(Command): |
|---|
| 58 | description='' |
|---|
| 59 | # List of option tuples: long name, short name (None if no short name), and help string. |
|---|
| 60 | user_options=[('name=', None, 'Package name'), |
|---|
| 61 | ('section=', None, 'Section (Only "user/*" will display in App Mgr usually) (default=user/other)'), |
|---|
| 62 | ('priority=', None, 'Priority (default=optional)'), |
|---|
| 63 | ('depends=', None, 'Other Debian package dependencies (comma separated) (default=python)'), |
|---|
| 64 | ('recommends=', None, 'Other Debian package you recommend (comma separated) (default=none)'), |
|---|
| 65 | ('suggests=', None, 'Other Debian package you suggest (comma separated) (default=none)'), |
|---|
| 66 | ('essential=', None, 'Whether packge is essential(yes/no) (default=no)'), |
|---|
| 67 | ('architecture=', None, 'Target architecture (default=all)'), |
|---|
| 68 | ('source=', None, 'Source keyword.. (default=package_name)'), |
|---|
| 69 | ('icon=', None, 'Name of icon file to be displayed by App Mgr')] |
|---|
| 70 | |
|---|
| 71 | def initialize_options(self): |
|---|
| 72 | self.section=None |
|---|
| 73 | self.priority=None |
|---|
| 74 | self.depends=None |
|---|
| 75 | self.recommends=None |
|---|
| 76 | self.suggests=None |
|---|
| 77 | self.architecture=None |
|---|
| 78 | self.essential=None |
|---|
| 79 | self.source=None |
|---|
| 80 | self.icon=None |
|---|
| 81 | |
|---|
| 82 | def finalize_options(self): |
|---|
| 83 | if self.section is None: |
|---|
| 84 | self.section='user/other' |
|---|
| 85 | |
|---|
| 86 | if self.priority is None: |
|---|
| 87 | self.priority='optional' |
|---|
| 88 | |
|---|
| 89 | self.maintainer='%s <%s>' % (self.distribution.get_maintainer(), self.distribution.get_maintainer_email()) |
|---|
| 90 | |
|---|
| 91 | if self.depends is None: |
|---|
| 92 | self.depends='python' |
|---|
| 93 | |
|---|
| 94 | self.name=self.distribution.get_name() |
|---|
| 95 | self.description=self.distribution.get_description() |
|---|
| 96 | self.long_description=self.distribution.get_long_description() |
|---|
| 97 | self.version=self.distribution.get_version() |
|---|
| 98 | |
|---|
| 99 | if self.source is None: |
|---|
| 100 | self.source=self.name |
|---|
| 101 | |
|---|
| 102 | # process new keywords |
|---|
| 103 | if self.distribution.icon != None: |
|---|
| 104 | self.icon=self.distribution.icon |
|---|
| 105 | if self.distribution.section != None: |
|---|
| 106 | self.section=self.distribution.section |
|---|
| 107 | if self.distribution.depends != None: |
|---|
| 108 | self.depends=self.distribution.depends |
|---|
| 109 | if self.distribution.recommends != None: |
|---|
| 110 | self.recommends=self.distribution.recommends |
|---|
| 111 | if self.distribution.suggests != None: |
|---|
| 112 | self.suggests=self.distribution.suggests |
|---|
| 113 | if self.distribution.architecture != None: |
|---|
| 114 | self.architecture=self.distribution.architecture |
|---|
| 115 | if self.distribution.essential != None: |
|---|
| 116 | self.essential=self.distribution.essential |
|---|
| 117 | |
|---|
| 118 | def run(self): |
|---|
| 119 | build_dir=os.path.join(self.get_finalized_command('build').build_base, 'debian') |
|---|
| 120 | dist_dir='dist' |
|---|
| 121 | binary_fn='debian-binary' |
|---|
| 122 | control_fn='control' |
|---|
| 123 | data_fn='data' |
|---|
| 124 | tgz_ext='.tar.gz' |
|---|
| 125 | |
|---|
| 126 | # build everything locally |
|---|
| 127 | self.run_command('build') |
|---|
| 128 | install=self.reinitialize_command('install', reinit_subcommands=1) |
|---|
| 129 | install.root=build_dir |
|---|
| 130 | self.run_command('install') |
|---|
| 131 | |
|---|
| 132 | # find out how much space it takes |
|---|
| 133 | installed_size=0 |
|---|
| 134 | for root, dirs, files in os.walk('build'): |
|---|
| 135 | installed_size+=sum(os.path.getsize(os.path.join(root, name)) for name in files) |
|---|
| 136 | |
|---|
| 137 | # make compressed tarball |
|---|
| 138 | self.make_archive(os.path.join(dist_dir, data_fn), 'gztar', root_dir=build_dir) |
|---|
| 139 | |
|---|
| 140 | # remove all the stuff we just built |
|---|
| 141 | remove_tree(build_dir) |
|---|
| 142 | |
|---|
| 143 | # create control file contents |
|---|
| 144 | ctl=ControlFile(Package=self.name, Version=self.version, Section=self.section, Priority=self.priority, |
|---|
| 145 | Installed_Size=installed_size/1024+1, Architecture=self.architecture, Maintainer=self.maintainer, |
|---|
| 146 | Depends=self.depends, Description=self.description, Long_Description=self.long_description, |
|---|
| 147 | Recommends=self.recommends, Suggests=self.suggests, Essential=self.essential, |
|---|
| 148 | Source=self.source, |
|---|
| 149 | Icon=self.icon).getContent() |
|---|
| 150 | |
|---|
| 151 | # grab scripts |
|---|
| 152 | scripts={} |
|---|
| 153 | for fn in ('postinst', 'preinst', 'postrm', 'prerm', 'config'): |
|---|
| 154 | if os.path.exists(fn): |
|---|
| 155 | scripts[fn]=file(fn, 'rb').read() |
|---|
| 156 | |
|---|
| 157 | # now to create the deb package |
|---|
| 158 | os.chdir(dist_dir) |
|---|
| 159 | |
|---|
| 160 | # write control file |
|---|
| 161 | file(control_fn, 'wb').write(ctl) |
|---|
| 162 | |
|---|
| 163 | # write any scripts and chmod a+rx them |
|---|
| 164 | files=[control_fn] |
|---|
| 165 | for fn in scripts: |
|---|
| 166 | files.append(fn) |
|---|
| 167 | file(fn, 'wb').write(scripts[fn]) |
|---|
| 168 | os.chmod(fn, 0555) |
|---|
| 169 | |
|---|
| 170 | # make "control" compressed tarball with control file and any scripts |
|---|
| 171 | self.spawn(['tar', '-czf', control_fn+tgz_ext]+files) |
|---|
| 172 | |
|---|
| 173 | # make debian-binary file |
|---|
| 174 | file(binary_fn, 'wb').write('2.0\n') |
|---|
| 175 | |
|---|
| 176 | # make final archive |
|---|
| 177 | package_filename='%s_%s_all.deb' % (self.name, self.version) |
|---|
| 178 | self.spawn(['ar', '-cr', package_filename, binary_fn, control_fn+tgz_ext, data_fn+tgz_ext]) |
|---|
| 179 | |
|---|
| 180 | # Remove unneeded files |
|---|
| 181 | os.remove(control_fn) |
|---|
| 182 | os.remove(control_fn+tgz_ext) |
|---|
| 183 | os.remove(data_fn+tgz_ext) |
|---|
| 184 | os.remove(binary_fn) |
|---|