4
# Copyright © 2013 Piotr Ożarowski <piotr@debian.org>
6
# Permission is hereby granted, free of charge, to any person obtaining a copy
7
# of this software and associated documentation files (the "Software"), to deal
8
# in the Software without restriction, including without limitation the rights
9
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
# copies of the Software, and to permit persons to whom the Software is
11
# furnished to do so, subject to the following conditions:
13
# The above copyright notice and this permission notice shall be included in
14
# all copies or substantial portions of the Software.
16
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28
from optparse import OptionParser, SUPPRESS_HELP
29
from os.path import exists, join
30
from shutil import copy as fcopy
31
from dhpython.debhelper import DebHelper
32
from dhpython.depends import Dependencies
33
from dhpython.interpreter import Interpreter, EXTFILE_RE
34
from dhpython.version import supported, default, Version, VersionRange
35
from dhpython.pydist import validate as validate_pydist
36
from dhpython.fs import fix_locations, Scan
37
from dhpython.option import Option
40
logging.basicConfig(format='%(levelname).1s: dh_pypy '
41
'%(module)s:%(lineno)d: %(message)s')
42
log = logging.getLogger('dhpython')
44
DEFAULT = default('pypy')
45
SUPPORTED = supported('pypy')
49
def handle_ext(self, fpath):
50
path, fname = fpath.rsplit('/', 1)
51
tagver = EXTFILE_RE.search(fname)
54
tagver = tagver.groupdict()['ver']
57
tagver = Version("%s.%s" % (tagver[0], tagver[1]))
62
usage = '%prog -p PACKAGE [-V [X.Y][-][A.B]] DIR [-X REGEXPR]\n'
63
parser = OptionParser(usage, version='%prog DEVELV', option_class=Option)
64
parser.add_option('--no-guessing-deps', action='store_false',
65
dest='guess_deps', default=True,
66
help='disable guessing dependencies')
67
parser.add_option('--skip-private', action='store_true', default=False,
68
help='don\'t check private directories')
69
parser.add_option('-v', '--verbose', action='store_true', default=False,
70
help='turn verbose mode on')
71
# arch=False->arch:all only, arch=True->arch:any only, None->all of them
72
parser.add_option('-i', '--indep', action='store_false',
73
dest='arch', default=None,
74
help='act on architecture independent packages')
75
parser.add_option('-a', '-s', '--arch', action='store_true',
76
dest='arch', help='act on architecture dependent packages')
77
parser.add_option('-q', '--quiet', action='store_false', dest='verbose',
79
parser.add_option('-p', '--package', action='append',
80
help='act on the package named PACKAGE')
81
parser.add_option('-N', '--no-package', action='append',
82
help='do not act on the specified package')
83
parser.add_option('--compile-all', action='store_true', default=False,
84
help='compile all files from given private directory '
85
'in postinst, not just the ones provided by the '
87
parser.add_option('-V', type='version_range', dest='vrange',
88
#help='specify list of supported PyPy versions. ' +
89
# 'See pypycompile(1) for examples',
91
parser.add_option('-X', '--exclude', action='append', dest='regexpr',
92
help='exclude items that match given REGEXPR. You may '
93
'use this option multiple times to build up a list'
94
' of things to exclude.')
95
parser.add_option('--depends', action='append',
96
help='translate given requirements into Debian '
97
'dependencies and add them to ${pypy:Depends}. '
98
'Use it for missing items in requires.txt.')
99
parser.add_option('--recommends', action='append',
100
help='translate given requirements into Debian '
101
'dependencies and add them to ${pypy:Recommends}')
102
parser.add_option('--suggests', action='append',
103
help='translate given requirements into Debian '
104
'dependencies and add them to ${pypy:Suggests}')
105
parser.add_option('--shebang',
106
help='use given command as shebang in scripts')
107
parser.add_option('--ignore-shebangs', action='store_true', default=False,
108
help='do not translate shebangs into Debian dependencies')
109
parser.add_option('--no-dbg-cleaning', action='store_false',
110
dest='clean_dbg_pkg', default=True,
111
help='do not remove files from debug packages')
112
parser.add_option('--no-shebang-rewrite', action='store_true',
113
default=False, help='do not rewrite shebangs')
114
# ignore some debhelper options:
115
parser.add_option('-O', help=SUPPRESS_HELP)
117
options, args = parser.parse_args(sys.argv[1:] +
118
os.environ.get('DH_OPTIONS', '').split())
119
# regexpr option type is not used so lets check patterns here
120
for pattern in options.regexpr or []:
121
# fail now rather than at runtime
123
pattern = re.compile(pattern)
125
log.error('regular expression is not valid: %s', pattern)
131
private_dir = args[0]
132
if not private_dir.startswith('/'):
133
# handle usr/share/foo dirs (without leading slash)
134
private_dir = '/' + private_dir
135
# TODO: support more than one private dir at the same time (see :meth:scan)
136
if options.skip_private:
139
if options.verbose or os.environ.get('DH_VERBOSE') == '1':
140
log.setLevel(logging.DEBUG)
141
log.debug('argv: %s', sys.argv)
142
log.debug('options: %s', options)
143
log.debug('args: %s', args)
144
log.debug('supported PyPy versions: %s (default=%s)',
145
','.join(str(v) for v in SUPPORTED), DEFAULT)
147
log.setLevel(logging.INFO)
150
dh = DebHelper(options, impl='pypy')
151
except Exception as e:
152
log.error('cannot initialize DebHelper: %s', e)
154
if not options.vrange and dh.python_version:
155
options.vrange = VersionRange(dh.python_version)
157
interpreter = Interpreter('pypy')
158
for package, pdetails in dh.packages.items():
159
if options.arch is False and pdetails['arch'] != 'all' or \
160
options.arch is True and pdetails['arch'] == 'all':
162
log.debug('processing package %s...', package)
163
if package.endswith('-dbg'):
164
interpreter.debug = True
167
fix_locations(package, interpreter, SUPPORTED)
168
stats = Scanner(interpreter, package, private_dir, options).result
170
dependencies = Dependencies(package, 'pypy')
171
dependencies.parse(stats, options)
173
if stats['ext_vers']:
174
dh.addsubstvar(package, 'pypy:Versions',
175
', '.join(str(v) for v in sorted(stats['ext_vers'])))
176
ps = package.split('-', 1)
177
if len(ps) > 1 and ps[0] == 'pypy':
178
dh.addsubstvar(package, 'pypy:Provides',
179
', '.join("pypy%s-%s" % (i, ps[1])
180
for i in sorted(stats['ext_vers'])))
182
pypyclean_added = False # invoke pypyclean only once in maintainer script
186
args += "-V %s" % options.vrange
187
dh.autoscript(package, 'postinst', 'postinst-pypycompile', args)
188
dh.autoscript(package, 'prerm', 'prerm-pypyclean', '')
189
pypyclean_added = True
190
for pdir, details in stats['private_dirs'].items():
191
if not details.get('compile'):
193
if not pypyclean_added:
194
dh.autoscript(package, 'prerm', 'prerm-pypyclean', '')
195
pypyclean_added = True
199
ext_for = details.get('ext_vers')
200
ext_no_version = details.get('ext_no_version')
201
if ext_for is None and not ext_no_version: # no extension
202
shebang_versions = list(i.version for i in details.get('shebangs', [])
203
if i.version and i.version.minor)
204
if not options.ignore_shebangs and len(shebang_versions) == 1:
205
# only one version from shebang
206
args += " -V %s" % shebang_versions[0]
207
elif options.vrange and options.vrange != (None, None):
208
args += " -V %s" % options.vrange
210
# at least one extension's version not detected
211
if options.vrange and '-' not in str(options.vrange):
212
ver = str(options.vrange)
213
else: # try shebang or default PyPy version
214
ver = (list(i.version for i in details.get('shebangs', [])
215
if i.version) or [None])[0] or DEFAULT
216
dependencies.depend("pypy%s" % ver)
217
args += " -V %s" % ver
219
extensions = sorted(ext_for)
220
vr = VersionRange(minver=extensions[0], maxver=extensions[-1])
221
args += " -V %s" % vr
223
for pattern in options.regexpr or []:
224
args += " -X '%s'" % pattern.replace("'", r"'\''")
226
dh.autoscript(package, 'postinst', 'postinst-pypycompile', args)
228
dependencies.export_to(dh)
230
pydist_file = join('debian', "%s.pydist" % package)
231
if exists(pydist_file):
232
if not validate_pydist(pydist_file):
233
log.warning("%s.pydist file is invalid", package)
235
dstdir = join('debian', package, 'usr/share/pypy/dist/')
236
if not exists(dstdir):
238
fcopy(pydist_file, join(dstdir, package))
242
if __name__ == '__main__':