Merge lp:~mitya57/ubuntu/raring/python3-defaults/resync into lp:ubuntu/raring/python3-defaults

Proposed by Dmitry Shachnev
Status: Merged
Merged at revision: 60
Proposed branch: lp:~mitya57/ubuntu/raring/python3-defaults/resync
Merge into: lp:ubuntu/raring/python3-defaults
Diff against target: 2122 lines (+777/-492)
28 files modified
Makefile (+7/-7)
debian/changelog (+46/-3)
debian/control (+30/-0)
debian/control.in (+30/-0)
debian/python3.manpages (+1/-0)
debian/rules (+5/-2)
debian/tests/control (+2/-0)
debian/tests/dh_python3 (+12/-0)
debpython/build/__init__.py (+2/-2)
debpython/build/base.py (+31/-27)
debpython/build/plugin_distutils.py (+6/-6)
debpython/interpreter.py (+0/-360)
debpython/tools.py (+52/-0)
debpython/version.py (+4/-1)
dh/pybuild.pm (+172/-0)
dh_python3 (+4/-1)
dh_python3.rst (+1/-0)
pybuild (+194/-73)
pybuild.rst (+165/-0)
tests/t1/debian/compat (+1/-1)
tests/t1/debian/control (+1/-2)
tests/t1/debian/rules (+2/-1)
tests/t2/debian/compat (+1/-1)
tests/t2/debian/rules (+2/-1)
tests/t3/debian/compat (+1/-1)
tests/t3/debian/rules (+2/-1)
tests/t4/debian/compat (+1/-1)
tests/t4/debian/rules (+2/-1)
To merge this branch: bzr merge lp:~mitya57/ubuntu/raring/python3-defaults/resync
Reviewer Review Type Date Requested Status
Barry Warsaw (community) Approve
Piotr Ożarowski (community) Needs Fixing
Martin Pitt Approve
Matthias Klose Pending
Review via email: mp+143697@code.launchpad.net

Description of the change

Resynchronized the package with Debian packaging bzr (3.3.0-2 is in NEW: http://ftp-master.debian.org/new/python3-defaults_3.3.0-2.html). New version includes pybuild (a new tool to build python packages) and DEP-8 tests.

Note that I am not able to verify that the tests pass in a clean environment (lp:auto-package-testing) (because I'm currently using my old laptop that kvm refuses to run on), so I will be happy if someone does that for me before merging.

To post a comment you must log in.
Revision history for this message
Martin Pitt (pitti) wrote :

I tried running "run-adt-test -b lp:~mitya57/ubuntu/raring/python3-defaults/resync", but the package currently fails to build:

mkdir -p debian/libpython3-dev/usr/share/man/man1
ln -sf x86_64-linux-gnu-python3.3m-config.1.gz \
  debian/libpython3-dev/usr/share/man/man1/x86_64-linux-gnu-python3m-config.1.gz
ln -sf x86_64-linux-gnu-python3.3-config.1.gz \
  debian/libpython3-dev/usr/share/man/man1/x86_64-linux-gnu-python3-config.1.gz
ln -sf -python3.3m-config \
  debian/libpython3-dev/usr/bin/-python3m-config
ln: invalid option -- 'p'
Try 'ln --help' for more information.
make: *** [binary-arch] Error 1

Revision history for this message
Martin Pitt (pitti) :
review: Needs Fixing
63. By Dmitry Shachnev

Include architecture.mk in debian/rules instead of defining DEB_HOST_* manually

64. By Dmitry Shachnev

Add a build-dependency on dpkg-dev (>= 1.16.1~), as suggested by lintian

65. By Dmitry Shachnev

tests/t1: Build-depend on python3-all instead of python-all.

66. By Dmitry Shachnev

Refactor tests to make them work without python-support installed.

Revision history for this message
Martin Pitt (pitti) wrote :

Tests pass now, good work!

review: Approve
67. By Dmitry Shachnev

debian/tests/control: Add a dependency on libjs-jquery (test1 contains
a symlink to /usr/share/javascript/jquery/jquery.js).

68. By Dmitry Shachnev

Fix debian/changelog (remove change that is actually applied in Debian)

Revision history for this message
Barry Warsaw (barry) wrote :

I can't tell if this is success or failure ;)

...
make[1]: Leaving directory `/tmp/tmp.hLZxoncJhj/ubtree0t-dh_python3-testtmp/adttmp/tests/t4'
adt-run: trace: & ubtree0t-dh_python3: ----------------------------------------]
adt-run: trace1: ** needs_reset, previously=False
adt-run: trace: @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ tests done.
adt-run: trace: ** stop
adt-run: trace: ** close, scratch=tb-scratch~/tmp/tmp.hLZxoncJhj:-/|/tmp/tmp.hLZxoncJhj/!
Warning: Permanently added '[localhost]:54323' (ECDSA) to the list of known hosts.
find: `./.autopkgtest/gpg': Permission denied
rm: cannot remove ‘./resync/tests/t4/setup.py’: Permission denied
rm: cannot remove ‘./resync/tests/t2/lib/bar.c’: Permission denied
rm: cannot remove ‘./resync/tests/t2/lib/__init__.py’: Permission denied
rm: cannot remove ‘./resync/.bzr/checkout/views’: Permission denied
rm: cannot remove ‘./resync/debpython/__init__.py’: Permission denied
Connection to localhost closed.
2013-01-18 01:48:17 PM: Info: Cleaning up

Revision history for this message
Barry Warsaw (barry) wrote :

It looks good to me too, but I'd like to give Matthias a chance to weigh in. I will happily sponsor this if he doesn't object.

review: Approve
Revision history for this message
Piotr Ożarowski (piotr) wrote :

can you update to what's in bzr on alioth?

I added "args" to one line too many, fortunately I noticed it before releasing, so

  dh.autoscript(package, 'prerm', 'prerm-py3clean', args)

is back to:

  dh.autoscript(package, 'prerm', 'prerm-py3clean', '')

review: Needs Fixing
69. By Dmitry Shachnev

* Update to upstream r259
* Revert dependency on dpkg-dev (>= 1.16.1~), precise has that version

Revision history for this message
Dmitry Shachnev (mitya57) wrote :

Updated to r259, thanks Piotr!

70. By Dmitry Shachnev

Bump the timestamp

Revision history for this message
Barry Warsaw (barry) wrote :

I'm going to upload this now, thanks for the great work!

(I was going to mention the following to you on IRC, but you're offline atm :)

You mentioned on IRC that you were going to announce pybuild on ubuntu-devel (yay!). It would also be great to start documenting pybuild on http://wiki.debian.org/Python/LibraryStyleGuide (and the parallel application guide). Even though we can't use this on Debian yet, let's start promoting this in preparation for Jessy (which of course, in limited cases we can start to play with in Raring).

We might need to reorganize the pages a bit. Hit me up on IRC and we can discuss details.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Makefile'
2--- Makefile 2012-12-12 10:45:17 +0000
3+++ Makefile 2013-01-24 11:51:20 +0000
4@@ -1,7 +1,7 @@
5 #!/usr/bin/make -f
6 INSTALL ?= install
7 PREFIX ?= /usr/local
8-MANPAGES ?= dh_python3.1 py3compile.1 py3clean.1
9+MANPAGES ?= dh_python3.1 py3compile.1 py3clean.1 pybuild.1
10 VERSION=$(shell dpkg-parsechangelog | sed -rne 's,^Version: (.+),\1,p')
11
12 clean:
13@@ -15,18 +15,21 @@
14 $(INSTALL) -m 755 -d $(DESTDIR)$(PREFIX)/bin \
15 $(DESTDIR)$(PREFIX)/share/python3/runtime.d \
16 $(DESTDIR)$(PREFIX)/share/debhelper/autoscripts/ \
17- $(DESTDIR)$(PREFIX)/share/perl5/Debian/Debhelper/Sequence/
18+ $(DESTDIR)$(PREFIX)/share/perl5/Debian/Debhelper/Sequence/ \
19+ $(DESTDIR)$(PREFIX)/share/perl5/Debian/Debhelper/Buildsystem/
20 $(INSTALL) -m 755 runtime.d/* $(DESTDIR)$(PREFIX)/share/python3/runtime.d/
21 $(INSTALL) -m 644 autoscripts/* $(DESTDIR)$(PREFIX)/share/debhelper/autoscripts/
22 $(INSTALL) -m 755 dh_python3 $(DESTDIR)$(PREFIX)/bin/
23 sed -i -e 's/DEVELV/$(VERSION)/' $(DESTDIR)$(PREFIX)/bin/dh_python3
24- $(INSTALL) -m 644 python3.pm $(DESTDIR)$(PREFIX)/share/perl5/Debian/Debhelper/Sequence/
25+ $(INSTALL) -m 644 dh/python3.pm $(DESTDIR)$(PREFIX)/share/perl5/Debian/Debhelper/Sequence/
26+ $(INSTALL) -m 644 dh/pybuild.pm $(DESTDIR)$(PREFIX)/share/perl5/Debian/Debhelper/Buildsystem/
27 $(INSTALL) -m 755 pybuild $(DESTDIR)$(PREFIX)/bin/
28 sed -i -e 's/DEVELV/$(VERSION)/' $(DESTDIR)$(PREFIX)/bin/pybuild
29
30 install-runtime:
31- $(INSTALL) -m 755 -d $(DESTDIR)$(PREFIX)/share/python3/debpython $(DESTDIR)$(PREFIX)/bin
32+ $(INSTALL) -m 755 -d $(DESTDIR)$(PREFIX)/share/python3/debpython/build $(DESTDIR)$(PREFIX)/bin
33 $(INSTALL) -m 644 debpython/*.py $(DESTDIR)$(PREFIX)/share/python3/debpython/
34+ $(INSTALL) -m 644 debpython/build/*.py $(DESTDIR)$(PREFIX)/share/python3/debpython/build/
35 $(INSTALL) -m 755 py3compile $(DESTDIR)$(PREFIX)/bin/
36 sed -i -e 's/DEVELV/$(VERSION)/' $(DESTDIR)$(PREFIX)/bin/py3compile
37 $(INSTALL) -m 755 py3clean $(DESTDIR)$(PREFIX)/bin/
38@@ -37,9 +40,6 @@
39 %.1: %.rst
40 rst2man $< > $@
41
42-pybuild.1: pybuild
43- help2man -N --version-string=version ./pybuild > $@
44-
45 manpages: $(MANPAGES)
46
47 dist_fallback:
48
49=== modified file 'debian/changelog'
50--- debian/changelog 2012-12-14 22:05:46 +0000
51+++ debian/changelog 2013-01-24 11:51:20 +0000
52@@ -1,3 +1,28 @@
53+python3-defaults (3.3.0-2ubuntu5) raring; urgency=low
54+
55+ [ Piotr Ożarowski ]
56+ * pybuild:
57+ - add --ext-dest-dir (PYBUILD_EXT_DESTDIR) and --ext-pattern
58+ (PYBUILD_EXT_PATTERN) options to move files after install step
59+ - save before/after command logs if quiet mode is on
60+ - do not warn about limiting versions if there's only one
61+ * dh_python3: add version range to py3compile in maintainer scripts
62+
63+ [ Dmitry Shachnev ]
64+ * Merge with Debian packaging bzr r259. Remaining changes:
65+ - Drop python3.2 as a supported python3 version.
66+ - Make python3-{all,all-dev,all-dbg} Multi-Arch: allowed.
67+ - Move debiandoc-sgml and lsb-release from Build-Depends-Indep to
68+ Build-Depends.
69+ * Include architecture.mk in debian/rules instead of defining
70+ DEB_HOST_* manually.
71+ * tests/t1: Build-depend on python3-all instead of python-all.
72+ * Make tests work without python-support installed.
73+ * debian/tests/control: Add a dependency on libjs-jquery (test1 contains
74+ a symlink to /usr/share/javascript/jquery/jquery.js).
75+
76+ -- Dmitry Shachnev <mitya57@ubuntu.com> Thu, 24 Jan 2013 15:48:55 +0400
77+
78 python3-defaults (3.3.0-2ubuntu4) raring; urgency=low
79
80 [ Piotr Ożarowski ]
81@@ -32,15 +57,33 @@
82 * dh_python3:
83 - add multiarch support (rename extensions in dist-packages)
84 - rename foomodule.so to foo.so in dist-packages (for Python >= 3.3)
85+ * pybuild:
86+ - add --{before,after}-{clean,configure,build,install,test}
87+ command line options and many more PYBUILD_FOO environment vars
88+ (f.e. PYBUILD_DESTDIR, PYBUILD_DESTDIR_python3,
89+ PYBUILD_DESTDIR_python3.3-dbg)
90+ - add --disable command line option (to disable step, interpreter, version
91+ or any mix of them); drop --no-clean and --no-tests arguments
92+ (replaced by --disable clean, --disable test or --disable 'clean test')
93+ - add new build system to dh sequencer (dh --buildsystem=pybuild)
94+ - add manpage
95+ * Move python-docutils to Build-Depends (python3 package is now arch:any)
96
97 [ Matthias Klose ]
98 * Build dependency packages as architecture dependent packages.
99 * Make python3, python3-{minimal,dev,dbg} Multi-Arch: allowed.
100 * Don't use the `u' abi flag when defaulting to 3.3.
101- * Build Multi-Arch: any packages libpython3-{dev,dbg} packages providing
102+ * Build Multi-Arch: same packages libpython3-{dev,dbg} packages providing
103 $(DEB_HOST_MULTIARCH)-python3-config symlinks.
104-
105- -- Matthias Klose <doko@debian.org> Wed, 12 Dec 2012 00:28:52 +0100
106+ * Add Multi-Arch: same packages libpython3-all-{dev,dbg} (not yet built,
107+ python3.2 doesn't have support for multiarch).
108+
109+ [ Dmitry Shachnev ]
110+ * debpython/interpreter.py: fix failing doctest
111+ (architecture no longer hardcoded)
112+ * Added DEP-8 tests (Closes: #698002).
113+
114+ -- Piotr Ożarowski <piotr@debian.org> Sat, 12 Jan 2013 17:13:14 +0100
115
116 python3-defaults (3.3.0-1) experimental; urgency=low
117
118
119=== modified file 'debian/control'
120--- debian/control 2012-12-12 11:09:30 +0000
121+++ debian/control 2013-01-24 11:51:20 +0000
122@@ -10,6 +10,7 @@
123 Homepage: http://www.python.org/
124 Vcs-Bzr: http://alioth.debian.org/anonscm/bzr/pkg-python/python3-defaults-debian
125 Vcs-Browser: http://alioth.debian.org/scm/loggerhead/pkg-python/python3-defaults-debian/files
126+XS-Testsuite: autopkgtest
127
128 Package: python3
129 Architecture: any
130@@ -170,3 +171,32 @@
131 .
132 This package is a dependency package used as a build dependency for other
133 packages to avoid hardcoded dependencies on specific Python 3 debug packages.
134+
135+#Package: libpython3-all-dev
136+#Architecture: any
137+#Multi-Arch: same
138+#Depends: libpython3-dev (= ${binary:Version}), ${misc:Depends},
139+# libpython3.3-dev
140+#Description: package depending on all supported Python 3 development packages
141+# The package currently depends on libpython3.3-dev, in the
142+# future, dependencies on jython (Python for a JVM) and ironpython (Python
143+# for Mono) may be added.
144+# .
145+# This package is a dependency package used as a build dependency for other
146+# packages to avoid hardcoded dependencies on specific Python 3 development
147+# packages.
148+
149+#Package: libpython3-all-dbg
150+#Architecture: any
151+#Multi-Arch: same
152+#Section: debug
153+#Priority: extra
154+#Depends: libpython3-dbg (= ${binary:Version}), ${misc:Depends},
155+# libpython3.3-dbg
156+#Description: package depending on all supported Python 3 debugging packages
157+# The package currently depends on libpython3.3-dbg, in the
158+# future, dependencies on jython (Python for a JVM) and ironpython (Python
159+# for Mono) may be added.
160+# .
161+# This package is a dependency package used as a build dependency for other
162+# packages to avoid hardcoded dependencies on specific Python 3 debug packages.
163
164=== modified file 'debian/control.in'
165--- debian/control.in 2012-12-12 11:09:30 +0000
166+++ debian/control.in 2013-01-24 11:51:20 +0000
167@@ -9,6 +9,7 @@
168 Homepage: http://www.python.org/
169 Vcs-Bzr: http://alioth.debian.org/anonscm/bzr/pkg-python/python3-defaults-debian
170 Vcs-Browser: http://alioth.debian.org/scm/loggerhead/pkg-python/python3-defaults-debian/files
171+XS-Testsuite: autopkgtest
172
173 Package: python3
174 Architecture: any
175@@ -169,3 +170,32 @@
176 .
177 This package is a dependency package used as a build dependency for other
178 packages to avoid hardcoded dependencies on specific Python 3 debug packages.
179+
180+#Package: libpython3-all-dev
181+#Architecture: any
182+#Multi-Arch: same
183+#Depends: libpython3-dev (= ${binary:Version}), ${misc:Depends},
184+# libpython3.3-dev
185+#Description: package depending on all supported Python 3 development packages
186+# The package currently depends on libpython3.3-dev, in the
187+# future, dependencies on jython (Python for a JVM) and ironpython (Python
188+# for Mono) may be added.
189+# .
190+# This package is a dependency package used as a build dependency for other
191+# packages to avoid hardcoded dependencies on specific Python 3 development
192+# packages.
193+
194+#Package: libpython3-all-dbg
195+#Architecture: any
196+#Multi-Arch: same
197+#Section: debug
198+#Priority: extra
199+#Depends: libpython3-dbg (= ${binary:Version}), ${misc:Depends},
200+# libpython3.3-dbg
201+#Description: package depending on all supported Python 3 debugging packages
202+# The package currently depends on libpython3.3-dbg, in the
203+# future, dependencies on jython (Python for a JVM) and ironpython (Python
204+# for Mono) may be added.
205+# .
206+# This package is a dependency package used as a build dependency for other
207+# packages to avoid hardcoded dependencies on specific Python 3 debug packages.
208
209=== modified file 'debian/python3.manpages'
210--- debian/python3.manpages 2011-04-14 21:44:29 +0000
211+++ debian/python3.manpages 2013-01-24 11:51:20 +0000
212@@ -1,3 +1,4 @@
213 py3clean.1
214 py3compile.1
215 dh_python3.1
216+pybuild.1
217
218=== modified file 'debian/rules'
219--- debian/rules 2012-12-12 10:45:17 +0000
220+++ debian/rules 2013-01-24 11:51:20 +0000
221@@ -6,8 +6,7 @@
222 # Uncomment this to turn on verbose mode.
223 #export DH_VERBOSE=1
224
225-DEB_HOST_ARCH := $(shell dpkg-architecture -qDEB_HOST_ARCH)
226-DEB_HOST_MULTIARCH := $(shell dpkg-architecture -qDEB_HOST_MULTIARCH)
227+include /usr/share/dpkg/architecture.mk
228
229 changelog_values := $(shell dpkg-parsechangelog \
230 | awk '/^(Version|Source):/ {print $$2}')
231@@ -382,6 +381,10 @@
232 rm -rf debian/$$p/usr/share/doc/$$p; \
233 ln -sf python3 debian/$$p/usr/share/doc/$$p; \
234 done
235+# rm -rf debian/libpython3-all-dev/usr/share/doc/libpython3-all-dev
236+# ln -sf libpython3-dev debian/libython3-all-dev/usr/share/doc/libpython3-all-dev
237+# rm -rf debian/libpython3-all-dbg/usr/share/doc/libpython3-all-dev
238+# ln -sf libpython3-dbg debian/libpython3-all-dbg/usr/share/doc/libpython3-all-dbg
239
240 dh_compress -a $(NOPKGS)
241 dh_fixperms -a $(NOPKGS)
242
243=== added directory 'debian/tests'
244=== added file 'debian/tests/control'
245--- debian/tests/control 1970-01-01 00:00:00 +0000
246+++ debian/tests/control 2013-01-24 11:51:20 +0000
247@@ -0,0 +1,2 @@
248+Tests: dh_python3
249+Depends: python3-all-dev, debhelper, libjs-jquery
250
251=== added file 'debian/tests/dh_python3'
252--- debian/tests/dh_python3 1970-01-01 00:00:00 +0000
253+++ debian/tests/dh_python3 2013-01-24 11:51:20 +0000
254@@ -0,0 +1,12 @@
255+#!/bin/sh
256+
257+set -eu
258+cp -r tests "$ADTTMP"
259+cd "$ADTTMP/tests"
260+sed -i "s/..\/..\/debian/\/usr\/share\/python3/g" common.mk
261+for t in 1 2 3 4; do
262+ sed -i "s/PYTHONPATH=..\/..\//PYTHONPATH=\/usr\/share\/python3/g" t$t/debian/rules
263+ sed -i "s/..\/..\/debian\///g" t$t/debian/rules
264+ sed -i "s/..\/..\///g" t$t/debian/rules
265+ make test$t 2>&1
266+done
267
268=== modified file 'debpython/build/__init__.py'
269--- debpython/build/__init__.py 2012-11-26 09:20:46 +0000
270+++ debpython/build/__init__.py 2013-01-24 11:51:20 +0000
271@@ -33,6 +33,6 @@
272 plugins[i] = module.BuildSystem
273 except Exception as err:
274 if log.level < logging.WARN:
275- log.warn("cannot initialize '%s'", i, exc_info=True)
276+ log.info("cannot initialize '%s'", i, exc_info=True)
277 else:
278- log.warn("cannot initialize '%s': %s", i, err)
279+ log.info("cannot initialize '%s': %s", i, err)
280
281=== modified file 'debpython/build/base.py'
282--- debpython/build/base.py 2012-12-12 10:45:17 +0000
283+++ debpython/build/base.py 2013-01-24 11:51:20 +0000
284@@ -19,11 +19,13 @@
285 # THE SOFTWARE.
286
287 import logging
288+from functools import wraps
289 from glob import glob1
290 from os import remove, walk
291-from os.path import exists, join
292+from os.path import join
293 from subprocess import Popen, PIPE
294 from shutil import rmtree
295+from debpython.tools import execute
296
297 log = logging.getLogger('debpython')
298
299@@ -80,7 +82,7 @@
300 for tpl in self.REQUIRED_FILES:
301 found = False
302 for ftpl in tpl.split('|'):
303- res = glob1(self.cfg.dir, ftpl)
304+ res = glob1(context['dir'], ftpl)
305 if res:
306 found = True
307 self.DETECTED_REQUIRED_FILES.setdefault(tpl, []).extend(res)
308@@ -92,7 +94,7 @@
309
310 self.DETECTED_OPTIONAL_FILES = {}
311 for ftpl, score in self.OPTIONAL_FILES.items():
312- res = glob1(self.cfg.dir, ftpl)
313+ res = glob1(context['dir'], ftpl)
314 if res:
315 result += score
316 self.DETECTED_OPTIONAL_FILES.setdefault(ftpl, []).extend(res)
317@@ -101,7 +103,7 @@
318 return result
319
320 def clean(self, context, args):
321- for root, dirs, file_names in walk(self.cfg.dir):
322+ for root, dirs, file_names in walk(context['dir']):
323 for name in dirs:
324 if name == '__pycache__':
325 dpath = join(root, name)
326@@ -131,11 +133,24 @@
327 raise NotImplementedError("build method not implemented in %s" % self.NAME)
328
329 def test(self, context, args):
330- pass
331+ if args['version'] == '2.7' or args['version'] >> '3.1':
332+ return 'cd {build_dir}; {interpreter} -m unittest discover -v {args}'
333+
334+ def execute(self, context, args, command, log_file=None):
335+ if log_file is False and self.cfg.really_quiet:
336+ log_file = None
337+ command = command.format(**args)
338+ if 'PYTHONPATH' in args:
339+ env = dict(context['ENV'])
340+ env['PYTHONPATH'] = args['PYTHONPATH']
341+ else:
342+ env = context['ENV']
343+ return execute(command, context['dir'], env, log_file)
344
345
346 def shell_command(func):
347
348+ @wraps(func)
349 def wrapped_func(self, context, args, *oargs, **kwargs):
350 command = kwargs.pop('command', None)
351 if not command:
352@@ -146,30 +161,19 @@
353 log.warn('missing command (plugin=%s, method=%s, version=%s)',
354 self.NAME, func.__name__, args.get('version'))
355 return command
356- command = command.format(**args)
357- log.debug('invoking: %s', command)
358-
359- opath = join(args['home_dir'], '{}_cmd.log'.format(func.__name__))
360- i = 0
361- while exists(opath):
362- i += 1
363- opath = join(args['home_dir'], '{}_cmd_{}.log'.format(
364- func.__name__, i))
365-
366- output = None
367- command_args = dict(shell=True, cwd=self.cfg.dir, env=context['ENV'])
368+
369 if self.cfg.quiet:
370- output = open(opath, 'w')
371- command_args.update(dict(stdout=output, stderr=output))
372+ log_file = join(args['home_dir'], '{}_cmd.log'.format(func.__name__))
373+ else:
374+ log_file = False
375
376- with Popen(command, **command_args) as process:
377- stdout, stderr = process.communicate()
378- if process.returncode != 0:
379- msg = 'exit code={}: {}'.format(process.returncode, command)
380- if output:
381- msg += '\nfull command log is available in {}'.format(opath)
382- raise Exception(msg)
383+ command = command.format(**args)
384+ output = self.execute(context, args, command, log_file)
385+ if output['returncode'] != 0:
386+ msg = 'exit code={}: {}'.format(output['returncode'], command)
387+ if log_file:
388+ msg += '\nfull command log is available in {}'.format(log_file)
389+ raise Exception(msg)
390 return True
391
392- wrapped_func.__name__ = func.__name__
393 return wrapped_func
394
395=== modified file 'debpython/build/plugin_distutils.py'
396--- debpython/build/plugin_distutils.py 2012-12-12 10:45:17 +0000
397+++ debpython/build/plugin_distutils.py 2013-01-24 11:51:20 +0000
398@@ -48,8 +48,7 @@
399 '[install]\n',
400 'install-layout=deb\n',
401 'install-lib={}\n'.format(args['install_dir']),
402- 'root={}\n'.format(args['dest_dir']),
403- 'single-version-externally-managed=1\n',
404+ 'root={}\n'.format(args['destdir']),
405 'skip-build=1\n'])
406 context['ENV']['HOME'] = args['home_dir']
407 return func(self, context, args, *oargs, **kwargs)
408@@ -81,8 +80,8 @@
409 @create_pydistutils_cfg
410 def clean(self, context, args):
411 super(BuildSystem, self).clean(context, args)
412- for fname in glob1(self.cfg.dir, '*.egg-info'):
413- fpath = join(self.cfg.dir, fname)
414+ for fname in glob1(context['dir'], '*.egg-info'):
415+ fpath = join(context['dir'], fname)
416 rmtree(fpath) if isdir(fpath) else remove(fpath)
417 return '{interpreter} {setup_py} clean {args}'
418
419@@ -106,6 +105,7 @@
420 def test(self, context, args):
421 fpath = join(args['dir'], args['setup_py'])
422 with open(fpath) as fp:
423- if fp.read().find('test_suite') > 0: # TODO: is that enough to detect if test target is available?
424+ if fp.read().find('test_suite') > 0:
425+ # TODO: is that enough to detect if test target is available?
426 return '{interpreter} {setup_py} test {args}'
427- return super(BuildSystem, self).test(context, args) # try nosetests if setup.py test is not available
428+ return super(BuildSystem, self).test(context, args)
429
430=== added file 'debpython/interpreter.py'
431--- debpython/interpreter.py 1970-01-01 00:00:00 +0000
432+++ debpython/interpreter.py 2013-01-24 11:51:20 +0000
433@@ -0,0 +1,363 @@
434+# Copyright © 2012 Piotr Ożarowski <piotr@debian.org>
435+#
436+# Permission is hereby granted, free of charge, to any person obtaining a copy
437+# of this software and associated documentation files (the "Software"), to deal
438+# in the Software without restriction, including without limitation the rights
439+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
440+# copies of the Software, and to permit persons to whom the Software is
441+# furnished to do so, subject to the following conditions:
442+#
443+# The above copyright notice and this permission notice shall be included in
444+# all copies or substantial portions of the Software.
445+#
446+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
447+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
448+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
449+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
450+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
451+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
452+# THE SOFTWARE.
453+
454+import logging
455+import re
456+from os.path import join, split
457+from debpython.version import Version
458+from debpython.tools import execute
459+
460+SHEBANG_RE = re.compile(r'''
461+ (?:\#!\s*){0,1} # shebang prefix
462+ (?P<path>
463+ .*?/bin/.*?)?
464+ (?P<name>
465+ python|pypy)
466+ (?P<version>
467+ \d[\.\d]*)?
468+ (?P<debug>
469+ -dbg)?
470+ (?P<options>.*)
471+ ''', re.VERBOSE)
472+EXTFILE_RE = re.compile(r'''
473+ (?P<name>.*?)
474+ (?:\.
475+ (?P<stableabi>abi\d+)
476+ |(?:\.
477+ (?P<soabi>
478+ (?P<impl>cpython|pypy)
479+ -
480+ (?P<ver>\d{2})
481+ (?P<flags>[a-z]*?)
482+ )
483+ (?:
484+ -(?P<multiarch>[^/]*?)
485+ )?
486+ ))?
487+ (?P<debug>_d)?
488+ \.so$''', re.VERBOSE)
489+log = logging.getLogger(__name__)
490+
491+
492+class Interpreter:
493+ path = None
494+ name = 'python'
495+ version = None
496+ debug = False
497+ impl = 'cpython' # implementation
498+ options = ()
499+ _cache = {}
500+
501+ def __init__(self, value=None, path=None, name=None, version=None,
502+ debug=None, impl=None, options=None):
503+ params = locals()
504+ del params['self']
505+ del params['value']
506+
507+ if isinstance(value, Interpreter):
508+ for key in params.keys():
509+ if params[key] is None:
510+ params[key] = getattr(value, key)
511+ elif value:
512+ if value.replace('.', '').isdigit() and not version:
513+ params['version'] = Version(value)
514+ else:
515+ for key, val in self.parse(value).items():
516+ if params[key] is None:
517+ params[key] = val
518+
519+ for key, val in params.items():
520+ setattr(self, key, val)
521+
522+ def __setattr__(self, name, value):
523+ if name == 'name' and value:
524+ if value.startswith('python'):
525+ self.__dict__['impl'] = 'cpython'
526+ elif value.startswith('pypy'):
527+ self.__dict__['impl'] = 'pypy'
528+ if '-dbg' in value:
529+ self.__dict__['debug'] = True
530+ elif name == 'version' and value is not None:
531+ value = Version(value)
532+ if name in ('name', 'impl', 'debug', 'options') and value is None:
533+ # get the class default instead
534+ self.__dict__[name] = getattr(Interpreter, name)
535+ else:
536+ self.__dict__[name] = value
537+
538+ def __repr__(self):
539+ result = self.path or ''
540+ result += self._vstr(self.version)
541+ if self.options:
542+ result += ' ' + ' '.join(self.options)
543+ return result
544+
545+ def __str__(self):
546+ return self._vstr(self.version)
547+
548+ def _vstr(self, version):
549+ if self.impl == 'pypy':
550+ # TODO: will Debian support more than one PyPy version?
551+ return self.name
552+ if version and str(version) not in self.name:
553+ if self.debug:
554+ return 'python{}-dbg'.format(version)
555+ return self.name + str(version)
556+ return self.name
557+
558+ @staticmethod
559+ def parse(shebang):
560+ """Return dict with parsed shebang
561+
562+ >>> sorted(Interpreter.parse('pypy').items())
563+ [('debug', None), ('name', 'pypy'), ('options', ()), ('path', None), ('version', None)]
564+ >>> sorted(Interpreter.parse('/usr/bin/python3.3-dbg').items())
565+ [('debug', '-dbg'), ('name', 'python'), ('options', ()), ('path', '/usr/bin/'), ('version', '3.3')]
566+ >>> sorted(Interpreter.parse('#! /usr/bin/pypy --foo').items())
567+ [('debug', None), ('name', 'pypy'), ('options', ('--foo',)), ('path', '/usr/bin/'), ('version', None)]
568+ >>> sorted(Interpreter.parse('#! /usr/bin/python3.2').items())
569+ [('debug', None), ('name', 'python'), ('options', ()), ('path', '/usr/bin/'), ('version', '3.2')]
570+ >>> sorted(Interpreter.parse('/usr/bin/python3.2-dbg --foo --bar').items())
571+ [('debug', '-dbg'), ('name', 'python'), ('options', ('--foo', '--bar')),\
572+ ('path', '/usr/bin/'), ('version', '3.2')]
573+ """
574+ result = SHEBANG_RE.search(shebang)
575+ if not result:
576+ return {}
577+ result = result.groupdict()
578+ if 'options' in result:
579+ # TODO: do we need "--key value" here?
580+ result['options'] = tuple(result['options'].split())
581+ return result
582+
583+ def sitedir(self, gdb=False, package=None, version=None):
584+ """Return path to site-packages directory.
585+
586+ Note that returned path is not the final location of .py files
587+
588+ >>> i = Interpreter('python')
589+ >>> i.sitedir(version='3.1')
590+ '/usr/lib/python3/dist-packages/'
591+ >>> i.sitedir(version='2.5')
592+ '/usr/lib/python2.5/site-packages/'
593+ >>> i.sitedir(version=Version('2.7'))
594+ '/usr/lib/python2.7/dist-packages/'
595+ >>> i.sitedir(version='3.1', gdb=True, package='python3-foo')
596+ 'debian/python3-foo/usr/lib/debug/usr/lib/python3/dist-packages/'
597+ >>> i.sitedir(version=Version('3.2'))
598+ '/usr/lib/python3/dist-packages/'
599+ >>> Interpreter('pypy').sitedir(version='2.0')
600+ '/usr/lib/pypy/dist-packages/'
601+ """
602+ version = Version(version or self.version)
603+ #if not version:
604+ # version = Version(DEFAULT)
605+ if self.impl == 'pypy':
606+ path = '/usr/lib/pypy/dist-packages/'
607+ elif version << Version('2.6'):
608+ path = "/usr/lib/python%s/site-packages/" % version
609+ elif version << Version('3.0'):
610+ path = "/usr/lib/python%s/dist-packages/" % version
611+ else:
612+ path = '/usr/lib/python3/dist-packages/'
613+
614+ if gdb:
615+ path = "/usr/lib/debug%s" % path
616+ if package:
617+ path = "debian/%s%s" % (package, path)
618+
619+ return path
620+
621+ def cache_file(self, fpath, version=None):
622+ """Given path to a .py file, return path to its .pyc/.pyo file.
623+
624+ This function is inspired by Python 3.2's imp.cache_from_source.
625+
626+ :param fpath: path to file name
627+ :param version: Python version
628+
629+ >>> i = Interpreter('python')
630+ >>> i.cache_file('foo.py', Version('3.1'))
631+ 'foo.pyc'
632+ >>> i.cache_file('bar/foo.py', '3.2')
633+ 'bar/__pycache__/foo.cpython-32.pyc'
634+ """
635+ version = Version(version or self.version)
636+ last_char = 'o' if '-O' in self.options else 'c'
637+ if version <= Version('3.1'):
638+ return fpath + last_char
639+
640+ fdir, fname = split(fpath)
641+ if not fname.endswith('.py'):
642+ fname += '.py'
643+ return join(fdir, '__pycache__', "%s.%s.py%s" %
644+ (fname[:-3], self.magic_tag(version), last_char))
645+
646+ def ext_file(self, name, version=None):
647+ """Return extension name with soname and multiarch tags."""
648+ version = Version(version or self.version)
649+ soabi, multiarch = self._get_config(version)
650+ result = name.split('.', 1)[0]
651+ if soabi:
652+ result += '.{}'.format(soabi)
653+ if multiarch:
654+ result += '-{}'.format(multiarch)
655+ if self.debug and self.impl == 'cpython'\
656+ and version << Version('3'):
657+ result += '_d'
658+ return '{}.so'.format(result)
659+
660+ def magic_number(self, version=None):
661+ """Return magic number."""
662+ version = Version(version or self.version)
663+ if self.impl == 'cpython' and version << Version('3'):
664+ return ''
665+ result = self._execute('import imp; print(imp.get_magic())', version)
666+ return eval(result)
667+
668+ def magic_tag(self, version=None):
669+ """Return Python magic tag (used in __pycache__ dir to tag files).
670+
671+ >>> i = Interpreter('python')
672+ >>> i.magic_tag(version='3.2')
673+ 'cpython-32'
674+ """
675+ version = Version(version or self.version)
676+ if self.impl == 'cpython' and version << Version('3.2'):
677+ return ''
678+ return self._execute('import imp; print(imp.get_tag())', version)
679+
680+ def multiarch(self, version=None):
681+ """Return multiarch tag."""
682+ version = Version(version or self.version)
683+ try:
684+ soabi, multiarch = self._get_config(version)
685+ except Exception:
686+ log.debug('cannot get multiarch', exc_info=True)
687+ # interpreter without multiach support
688+ return ''
689+ return multiarch
690+
691+ def stableabi(self, version=None):
692+ version = Version(version or self.version)
693+ # stable ABI was introduced in Python 3.3
694+ if self.impl == 'cpython' and version >> Version('3.2'):
695+ return 'abi{}'.format(version.major)
696+
697+ def soabi(self, version=None):
698+ """Return SOABI flag (used to in .so files).
699+
700+ >>> i = Interpreter('python')
701+ >>> i.soabi(version=Version('3.3'))
702+ 'cpython-33m'
703+ """
704+ version = Version(version or self.version)
705+ # NOTE: it's not the same as magic_tag
706+ try:
707+ soabi, multiarch = self._get_config(version)
708+ except Exception:
709+ log.debug('cannot get soabi', exc_info=True)
710+ # interpreter without soabi support
711+ return ''
712+ return soabi
713+
714+ def check_extname(self, fname, version=None):
715+ """Return extension file name if file can be renamed.
716+
717+ >>> i = Interpreter('python')
718+ >>> i.check_extname('foo.so', version='3.3') # doctest: +ELLIPSIS
719+ 'foo.cpython-33m-....so'
720+ >>> i.check_extname('foo.abi3.so', version='3.3')
721+ >>> i.check_extname('foo/bar/bazmodule.so', version='3.3') # doctest: +ELLIPSIS
722+ 'foo/bar/baz.cpython-33m-....so'
723+ """
724+ version = Version(version or self.version)
725+
726+ if '/' in fname:
727+ fdir, fname = fname.rsplit('/', 1) # in case full path was passed
728+ else:
729+ fdir = ''
730+
731+ info = EXTFILE_RE.search(fname)
732+ if not info:
733+ return
734+ info = info.groupdict()
735+
736+ if info['stableabi']:
737+ # files with stable ABI in name don't need changes
738+ return
739+
740+ i = Interpreter(self, version=version)
741+ if info['ver']:
742+ i.version = "{}.{}".format(info['ver'][0], info['ver'][1])
743+ if not i.debug and (info['debug'] or 'd' in (info['flags'] or '')):
744+ i.debug = True
745+ try:
746+ soabi, multiarch = i._get_config()
747+ except Exception:
748+ log.debug('cannot get soabi/multiarch', exc_info=True)
749+ return
750+ result = info['name']
751+ if i.impl == 'cpython' and i.version >> '3.2' and result.endswith('module'):
752+ result = result[:-6]
753+ if info['soabi'] or soabi:
754+ result = "{}.{}".format(result, info['soabi'] or soabi)
755+ if info['multiarch'] or multiarch:
756+ result = "{}-{}".format(result, info['multiarch'] or multiarch)
757+
758+ result += '.so'
759+ if fname == result:
760+ return
761+ return join(fdir, result)
762+
763+ def _get_config(self, version=None):
764+ version = Version(version or self.version)
765+ # sysconfig module is available since Python 3.2
766+ # (also backported to Python 2.7)
767+ if self.impl == 'pypy' or self.impl == 'cpython' and (
768+ version >> '2.6' and version << '3'
769+ or version >> '3.1' or version == '3'):
770+ cmd = 'import sysconfig as s;'
771+ else:
772+ cmd = 'from distutils import sysconfig as s;'
773+ cmd += 'print("__SEP__".join(i or "" ' \
774+ 'for i in s.get_config_vars("SOABI", "MULTIARCH")))'
775+ return self._execute(cmd, version).split('__SEP__')
776+
777+ def _execute(self, command, version=None, cache=True):
778+ version = Version(version or self.version)
779+ command = "{} -c '{}'".format(self._vstr(version), command.replace("'", "\'"))
780+ if cache and command in self.__class__._cache:
781+ return self.__class__._cache[command]
782+
783+ output = execute(command)
784+ if output['returncode'] != 0:
785+ log.debug(output['stderr'])
786+ raise Exception('{} failed with status code {}'.format(command, output['returncode']))
787+
788+ result = output['stdout'].splitlines()
789+
790+ if len(result) == 1:
791+ result = result[0]
792+
793+ if cache:
794+ self.__class__._cache[command] = result
795+
796+ return result
797
798=== removed file 'debpython/interpreter.py'
799--- debpython/interpreter.py 2012-12-14 22:05:46 +0000
800+++ debpython/interpreter.py 1970-01-01 00:00:00 +0000
801@@ -1,360 +0,0 @@
802-# Copyright © 2012 Piotr Ożarowski <piotr@debian.org>
803-#
804-# Permission is hereby granted, free of charge, to any person obtaining a copy
805-# of this software and associated documentation files (the "Software"), to deal
806-# in the Software without restriction, including without limitation the rights
807-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
808-# copies of the Software, and to permit persons to whom the Software is
809-# furnished to do so, subject to the following conditions:
810-#
811-# The above copyright notice and this permission notice shall be included in
812-# all copies or substantial portions of the Software.
813-#
814-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
815-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
816-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
817-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
818-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
819-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
820-# THE SOFTWARE.
821-
822-import logging
823-import re
824-from os.path import join, split
825-from subprocess import PIPE, Popen
826-from debpython.version import Version
827-
828-SHEBANG_RE = re.compile(r'''
829- (?:\#!\s*){0,1} # shebang prefix
830- (?P<path>
831- .*?/bin/.*?)?
832- (?P<name>
833- python|pypy)
834- (?P<version>
835- \d[\.\d]*)?
836- (?P<debug>
837- -dbg)?
838- (?P<options>.*)
839- ''', re.VERBOSE)
840-EXTFILE_RE = re.compile(r'''
841- (?P<name>.*?)
842- (?:\.
843- (?P<stableabi>abi\d+)
844- |(?:\.
845- (?P<soabi>
846- (?P<impl>cpython|pypy)
847- -
848- (?P<ver>\d{2})
849- (?P<flags>[a-z]*?)
850- )
851- (?:
852- -(?P<multiarch>[^/]*?)
853- )?
854- ))?
855- (?P<debug>_d)?
856- \.so$''', re.VERBOSE)
857-log = logging.getLogger(__name__)
858-
859-
860-class Interpreter:
861- path = None
862- name = 'python'
863- version = None
864- debug = False
865- impl = 'cpython' # implementation
866- options = ()
867- _cache = {}
868-
869- def __init__(self, value=None, path=None, name=None, version=None,
870- debug=None, impl=None, options=None):
871- params = locals()
872- del params['self']
873- del params['value']
874-
875- if isinstance(value, Interpreter):
876- for key in params.keys():
877- if params[key] is None:
878- params[key] = getattr(value, key)
879- elif value:
880- if value.replace('.', '').isdigit() and not version:
881- params['version'] = Version(value)
882- else:
883- for key, val in self.parse(value).items():
884- if params[key] is None:
885- params[key] = val
886-
887- for key, val in params.items():
888- setattr(self, key, val)
889-
890- def __setattr__(self, name, value):
891- if name == 'name' and value:
892- if value.startswith('python'):
893- self.__dict__['impl'] = 'cpython'
894- elif value.startswith('pypy'):
895- self.__dict__['impl'] = 'pypy'
896- if '-dbg' in value:
897- self.__dict__['debug'] = True
898- elif name == 'version' and value is not None:
899- value = Version(value)
900- if name in ('name', 'impl', 'debug', 'options') and value is None:
901- # get the class default instead
902- self.__dict__[name] = getattr(Interpreter, name)
903- else:
904- self.__dict__[name] = value
905-
906- def __repr__(self):
907- result = self.path or ''
908- result += self._vstr(self.version)
909- if self.options:
910- result += ' ' + ' '.join(self.options)
911- return result
912-
913- def __str__(self):
914- return self._vstr(self.version)
915-
916- def _vstr(self, version):
917- if self.impl == 'pypy':
918- # TODO: will Debian support more than one PyPy version?
919- return self.name
920- if version and str(version) not in self.name:
921- if self.debug:
922- return 'python{}-dbg'.format(version)
923- return self.name + str(version)
924- return self.name
925-
926- @staticmethod
927- def parse(shebang):
928- """Return dict with parsed shebang
929-
930- >>> sorted(Interpreter.parse('pypy').items())
931- [('debug', None), ('name', 'pypy'), ('options', ()), ('path', None), ('version', None)]
932- >>> sorted(Interpreter.parse('/usr/bin/python3.3-dbg').items())
933- [('debug', '-dbg'), ('name', 'python'), ('options', ()), ('path', '/usr/bin/'), ('version', '3.3')]
934- >>> sorted(Interpreter.parse('#! /usr/bin/pypy --foo').items())
935- [('debug', None), ('name', 'pypy'), ('options', ('--foo',)), ('path', '/usr/bin/'), ('version', None)]
936- >>> sorted(Interpreter.parse('#! /usr/bin/python3.2').items())
937- [('debug', None), ('name', 'python'), ('options', ()), ('path', '/usr/bin/'), ('version', '3.2')]
938- >>> sorted(Interpreter.parse('/usr/bin/python3.2-dbg --foo --bar').items())
939- [('debug', '-dbg'), ('name', 'python'), ('options', ('--foo', '--bar')),\
940- ('path', '/usr/bin/'), ('version', '3.2')]
941- """
942- result = SHEBANG_RE.search(shebang)
943- if not result:
944- return {}
945- result = result.groupdict()
946- if 'options' in result:
947- # TODO: do we need "--key value" here?
948- result['options'] = tuple(result['options'].split())
949- return result
950-
951- def sitedir(self, gdb=False, package=None, version=None):
952- """Return path to site-packages directory.
953-
954- Note that returned path is not the final location of .py files
955-
956- >>> i = Interpreter('python')
957- >>> i.sitedir(version='3.1')
958- '/usr/lib/python3/dist-packages/'
959- >>> i.sitedir(version='2.5')
960- '/usr/lib/python2.5/site-packages/'
961- >>> i.sitedir(version=Version('2.7'))
962- '/usr/lib/python2.7/dist-packages/'
963- >>> i.sitedir(version='3.1', gdb=True, package='python3-foo')
964- 'debian/python3-foo/usr/lib/debug/usr/lib/python3/dist-packages/'
965- >>> i.sitedir(version=Version('3.2'))
966- '/usr/lib/python3/dist-packages/'
967- """
968- version = Version(version or self.version)
969- #if not version:
970- # version = Version(DEFAULT)
971-
972- if version << Version('2.6'):
973- path = "/usr/lib/python%s/site-packages/" % version
974- elif version << Version('3.0'):
975- path = "/usr/lib/python%s/dist-packages/" % version
976- else:
977- path = '/usr/lib/python3/dist-packages/'
978-
979- if gdb:
980- path = "/usr/lib/debug%s" % path
981- if package:
982- path = "debian/%s%s" % (package, path)
983-
984- return path
985-
986- def cache_file(self, fpath, version=None):
987- """Given path to a .py file, return path to its .pyc/.pyo file.
988-
989- This function is inspired by Python 3.2's imp.cache_from_source.
990-
991- :param fpath: path to file name
992- :param version: Python version
993-
994- >>> i = Interpreter('python')
995- >>> i.cache_file('foo.py', Version('3.1'))
996- 'foo.pyc'
997- >>> i.cache_file('bar/foo.py', '3.2')
998- 'bar/__pycache__/foo.cpython-32.pyc'
999- """
1000- version = Version(version or self.version)
1001- last_char = 'o' if '-O' in self.options else 'c'
1002- if version <= Version('3.1'):
1003- return fpath + last_char
1004-
1005- fdir, fname = split(fpath)
1006- if not fname.endswith('.py'):
1007- fname += '.py'
1008- return join(fdir, '__pycache__', "%s.%s.py%s" %
1009- (fname[:-3], self.magic_tag(version), last_char))
1010-
1011- def ext_file(self, name, version=None):
1012- """Return extension name with soname and multiarch tags."""
1013- version = Version(version or self.version)
1014- soabi, multiarch = self._get_config(version)
1015- result = name.split('.', 1)[0]
1016- if soabi:
1017- result += '.{}'.format(soabi)
1018- if multiarch:
1019- result += '-{}'.format(multiarch)
1020- if self.debug and self.impl == 'cpython'\
1021- and version << Version('3'):
1022- result += '_d'
1023- return '{}.so'.format(result)
1024-
1025- def magic_number(self, version=None):
1026- """Return magic number."""
1027- version = Version(version or self.version)
1028- if self.impl == 'cpython' and version << Version('3'):
1029- return ''
1030- result = self._call('import imp; print(imp.get_magic())', version)
1031- return eval(result)
1032-
1033- def magic_tag(self, version=None):
1034- """Return Python magic tag (used in __pycache__ dir to tag files).
1035-
1036- >>> i = Interpreter('python')
1037- >>> i.magic_tag(version='3.2')
1038- 'cpython-32'
1039- """
1040- version = Version(version or self.version)
1041- if self.impl == 'cpython' and version << Version('3.2'):
1042- return ''
1043- return self._call('import imp; print(imp.get_tag())', version)
1044-
1045- def multiarch(self, version=None):
1046- """Return multiarch tag."""
1047- version = Version(version or self.version)
1048- try:
1049- soabi, multiarch = self._get_config(version)
1050- except Exception:
1051- # interpreter without multiach support
1052- return ''
1053- return multiarch
1054-
1055- def stableabi(self, version=None):
1056- version = Version(version or self.version)
1057- # stable ABI was introduced in Python 3.3
1058- if self.impl == 'cpython' and version >> Version('3.2'):
1059- return 'abi{}'.format(version.major)
1060-
1061- def soabi(self, version=None):
1062- """Return SOABI flag (used to in .so files).
1063-
1064- >>> i = Interpreter('python')
1065- >>> i.soabi(version=Version('3.3'))
1066- 'cpython-33m'
1067- """
1068- version = Version(version or self.version)
1069- # NOTE: it's not the same as magic_tag
1070- try:
1071- soabi, multiarch = self._get_config(version)
1072- except Exception:
1073- # interpreter without soabi support
1074- return ''
1075- return soabi
1076-
1077- def check_extname(self, fname, version=None):
1078- """Return extension file name if file can be renamed.
1079-
1080- >>> i = Interpreter('python')
1081- >>> i.check_extname('foo.so', version='3.3')
1082- 'foo.cpython-33m-x86_64-linux-gnu.so'
1083- >>> i.check_extname('foo.abi3.so', version='3.3')
1084- >>> i.check_extname('foo/bar/bazmodule.so', version='3.3')
1085- 'foo/bar/baz.cpython-33m-x86_64-linux-gnu.so'
1086- """
1087- version = Version(version or self.version)
1088-
1089- if '/' in fname:
1090- fdir, fname = fname.rsplit('/', 1) # in case full path was passed
1091- else:
1092- fdir = ''
1093-
1094- info = EXTFILE_RE.search(fname)
1095- if not info:
1096- return
1097- info = info.groupdict()
1098-
1099- if info['stableabi']:
1100- # files with stable ABI in name don't need changes
1101- return
1102-
1103- i = Interpreter(self, version=version)
1104- if info['ver']:
1105- i.version = "{}.{}".format(info['ver'][0], info['ver'][1])
1106- if not i.debug and (info['debug'] or 'd' in (info['flags'] or '')):
1107- i.debug = True
1108- try:
1109- soabi, multiarch = i._get_config()
1110- except Exception:
1111- return
1112- result = info['name']
1113- if i.impl == 'cpython' and i.version >> '3.2' and result.endswith('module'):
1114- result = result[:-6]
1115- if info['soabi'] or soabi:
1116- result = "{}.{}".format(result, info['soabi'] or soabi)
1117- if info['multiarch'] or multiarch:
1118- result = "{}-{}".format(result, info['multiarch'] or multiarch)
1119-
1120- result += '.so'
1121- if fname == result:
1122- return
1123- return join(fdir, result)
1124-
1125- def _get_config(self, version=None):
1126- version = Version(version or self.version)
1127- # sysconfig module is available since Python 3.2
1128- # (also backported to Python 2.7)
1129- if self.impl == 'pypy' or self.impl == 'cpython' and (
1130- version >> '2.6' and version << '3'
1131- or version >> '3.1' or version == '3'):
1132- cmd = 'import sysconfig as s;'
1133- else:
1134- cmd = 'from distutils import sysconfig as s;'
1135- cmd += 'print("__SEP__".join(i or "" ' \
1136- 'for i in s.get_config_vars("SOABI", "MULTIARCH")))'
1137- return self._call(cmd, version).split('__SEP__')
1138-
1139- def _call(self, command, version=None, cache=True):
1140- version = Version(version or self.version)
1141- command = "{} -c '{}'".format(self._vstr(version), command.replace("'", "\'"))
1142- if cache and command in self.__class__._cache:
1143- return self.__class__._cache[command]
1144-
1145- log.debug("invoking %s", command)
1146- p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE)
1147- status = p.wait()
1148- if status != 0:
1149- log.debug(p.stderr.read())
1150- raise Exception('{} failed with status code {}'.format(command, status))
1151- result = []
1152- for line in p.stdout:
1153- result.append(str(line, 'utf-8').rstrip('\r\n'))
1154-
1155- if len(result) == 1:
1156- result = result[0]
1157-
1158- if cache:
1159- self.__class__._cache[command] = result
1160-
1161- return result
1162
1163=== modified file 'debpython/tools.py'
1164--- debpython/tools.py 2012-12-12 10:45:17 +0000
1165+++ debpython/tools.py 2013-01-24 11:51:20 +0000
1166@@ -22,8 +22,10 @@
1167 import logging
1168 import os
1169 import re
1170+from datetime import datetime
1171 from pickle import dumps
1172 from os.path import isdir, islink, join, split
1173+from subprocess import Popen, PIPE
1174 from debpython.version import getver
1175
1176 log = logging.getLogger(__name__)
1177@@ -64,6 +66,22 @@
1178 os.rename(fpath, dstdir)
1179
1180
1181+def move_matching_files(src, dst, pattern):
1182+ """Move files (preserving path) that match given pattern.
1183+
1184+ move_files('foo/bar/', 'foo/baz/', 'spam/.*\.so$')
1185+ will move foo/bar/a/b/c/spam/file.so to foo/baz/a/b/c/spam/file.so
1186+ """
1187+ match = re.compile(pattern).search
1188+ for root, dirs, filenames in os.walk(src):
1189+ for fn in filenames:
1190+ spath = join(root, fn)
1191+ if match(spath):
1192+ dpath = join(dst, spath.lstrip(src).lstrip('/'))
1193+ os.renames(spath, dpath)
1194+
1195+
1196+
1197 def fix_shebang(fpath, replacement=None):
1198 """Normalize file's shebang.
1199
1200@@ -140,6 +158,40 @@
1201 return name
1202
1203
1204+def execute(command, cwd=None, env=None, log_output=None):
1205+ """Execute external shell commad.
1206+
1207+ :param cdw: currennt working directory
1208+ :param env: environment
1209+ :param log_output:
1210+ * opened log file or path to this file, or
1211+ * None if output should be included in the returned dict, or
1212+ * False if output should be redirectored to stdout/stderr
1213+ """
1214+ args = {'shell': True, 'cwd': cwd, 'env': env}
1215+ close = False
1216+ if log_output is False:
1217+ pass
1218+ elif log_output is None:
1219+ args.update(stdout=PIPE, stderr=PIPE)
1220+ elif log_output:
1221+ if isinstance(log_output, str):
1222+ close = True
1223+ log_output = open(log_output, 'a')
1224+ log_output.write('\n# command executed on {}'.format(datetime.now().isoformat()))
1225+ log_output.write('\n$ {}\n'.format(command))
1226+ log_output.flush()
1227+ args.update(stdout=log_output, stderr=log_output)
1228+
1229+ log.debug('invoking: %s', command)
1230+ with Popen(command, **args) as process:
1231+ stdout, stderr = process.communicate()
1232+ close and log_output.close()
1233+ return dict(returncode=process.returncode,
1234+ stdout=stdout and str(stdout, 'utf-8'),
1235+ stderr=stderr and str(stderr, 'utf-8'))
1236+
1237+
1238 class memoize:
1239 def __init__(self, func):
1240 self.func = func
1241
1242=== modified file 'debpython/version.py'
1243--- debpython/version.py 2012-12-12 10:45:17 +0000
1244+++ debpython/version.py 2013-01-24 11:51:20 +0000
1245@@ -100,6 +100,9 @@
1246 result += '.{}'.format(self.minor)
1247 return result
1248
1249+ def __hash__(self):
1250+ return hash(repr(self))
1251+
1252 def __repr__(self):
1253 """Return full version string.
1254
1255@@ -248,7 +251,7 @@
1256 :type available: bool
1257
1258 >>> sorted(get_requested_versions([(3, 0), None]))
1259- [(3, 2)]
1260+ [(3, 2), (3, 3)]
1261 >>> sorted(get_requested_versions('')) == sorted(SUPPORTED)
1262 True
1263 >>> sorted(get_requested_versions([None, None])) == sorted(SUPPORTED)
1264
1265=== added directory 'dh'
1266=== added file 'dh/pybuild.pm'
1267--- dh/pybuild.pm 1970-01-01 00:00:00 +0000
1268+++ dh/pybuild.pm 2013-01-24 11:51:20 +0000
1269@@ -0,0 +1,172 @@
1270+# A debhelper build system class for building Python libraries
1271+#
1272+# Copyright: © 2012-2013 Piotr Ożarowski
1273+# License: GPL-2+
1274+
1275+# TODO:
1276+# * save check_auto_buildable's result and pass it later via --system to speed things up
1277+# * support for dh --parallel
1278+
1279+package Debian::Debhelper::Buildsystem::pybuild;
1280+
1281+use strict;
1282+use Cwd ();
1283+use Debian::Debhelper::Dh_Lib qw(error);
1284+use base 'Debian::Debhelper::Buildsystem';
1285+
1286+sub DESCRIPTION {
1287+ "Python pybuild"
1288+}
1289+
1290+sub check_auto_buildable {
1291+ my $this=shift;
1292+ return $this->doit_in_sourcedir('pybuild', '--detect', '--really-quiet');
1293+}
1294+
1295+sub new {
1296+ my $class=shift;
1297+ my $this=$class->SUPER::new(@_);
1298+ $this->enforce_in_source_building();
1299+
1300+ if (!$ENV{'PYBUILD_INTERPRETERS'}) {
1301+ $this->{pydef} = `pyversions -vd 2>/dev/null`;
1302+ $this->{pydef} =~ s/\s+$//;
1303+ $this->{pyvers} = `pyversions -vr 2>/dev/null`;
1304+ $this->{pyvers} =~ s/\s+$//;
1305+ $this->{py3def} = `py3versions -vd 2>/dev/null`;
1306+ $this->{py3def} =~ s/\s+$//;
1307+ $this->{py3vers} = `py3versions -vr 2>/dev/null`;
1308+ $this->{py3vers} =~ s/\s+$//;
1309+ $this->{pypydef} = `pypy -c 'from sys import pypy_version_info as i; print("%s.%s" % (i.major, i.minor))' 2>/dev/null`;
1310+ $this->{pypydef} =~ s/\s+$//;
1311+ }
1312+
1313+ return $this;
1314+}
1315+
1316+sub configure {
1317+ my $this=shift;
1318+ foreach my $command ($this->pybuild_commands('configure', @_)) {
1319+ $this->doit_in_sourcedir($command);
1320+ }
1321+}
1322+
1323+sub build {
1324+ my $this=shift;
1325+ foreach my $command ($this->pybuild_commands('build', @_)) {
1326+ $this->doit_in_sourcedir($command);
1327+ }
1328+}
1329+
1330+sub install {
1331+ my $this=shift;
1332+ my $destdir=shift;
1333+ foreach my $command ($this->pybuild_commands('install', @_)) {
1334+ $this->doit_in_sourcedir("$command --dest-dir '$destdir'");
1335+ }
1336+}
1337+
1338+sub test {
1339+ my $this=shift;
1340+ foreach my $command ($this->pybuild_commands('test', @_)) {
1341+ $this->doit_in_sourcedir($command);
1342+ }
1343+}
1344+
1345+sub clean {
1346+ my $this=shift;
1347+ foreach my $command ($this->pybuild_commands('clean', @_)) {
1348+ $this->doit_in_sourcedir($command);
1349+ }
1350+ $this->doit_in_sourcedir('rm', '-rf', '.pybuild/');
1351+}
1352+
1353+sub pybuild_commands {
1354+ my $this=shift;
1355+ my $step=shift;
1356+ my @options = @_;
1357+ my @result;
1358+
1359+ if ($ENV{'PYBUILD_INTERPRETERS'}) {
1360+ push @result, "pybuild --$step @options";
1361+ }
1362+ else {
1363+ # get interpreter packages from Build-Depends{,-Indep}:
1364+ # NOTE: possible problems with alternative/versioned dependencies
1365+ my @deps = $this->python_build_dependencies();
1366+
1367+ my $pyall = 0;
1368+ my $pyalldbg = 0;
1369+ my $py3all = 0;
1370+ my $py3alldbg = 0;
1371+
1372+ my $i = 'python{version}';
1373+
1374+ # Python
1375+ if ($this->{pyvers}) {
1376+ if (grep {$_ eq 'python-all' or $_ eq 'python-all-dev'} @deps) {
1377+ $pyall = 1;
1378+ push @result, "pybuild --$step -i $i -p '$this->{pyvers}' @options";
1379+ }
1380+ if (grep {$_ eq 'python-all-dbg'} @deps) {
1381+ $pyalldbg = 1;
1382+ push @result, "pybuild --$step -i $i-dbg -p '$this->{pyvers}' @options";
1383+ }
1384+ }
1385+ if ($this->{pydef}) {
1386+ if (not $pyall and grep {$_ eq 'python' or $_ eq 'python-dev'} @deps) {
1387+ push @result, "pybuild --$step -i $i -p '$this->{pydef}' @options";
1388+ }
1389+ if (not $pyalldbg and grep {$_ eq 'python-dbg'} @deps) {
1390+ push @result, "pybuild --$step -i $i-dbg -p '$this->{pydef}' @options";
1391+ }
1392+ }
1393+
1394+ # Python 3
1395+ if ($this->{py3vers}) {
1396+ my $i = 'python{version}';
1397+ if (grep {$_ eq 'python3-all' or $_ eq 'python3-all-dev'} @deps) {
1398+ $py3all = 1;
1399+ push @result, "pybuild --$step -i $i -p '$this->{py3vers}' @options";
1400+ }
1401+ if (grep {$_ eq 'python3-all-dbg'} @deps) {
1402+ $py3alldbg = 1;
1403+ push @result, "pybuild --$step -i $i-dbg -p'$this->{py3vers}' @options";
1404+ }
1405+ }
1406+ if ($this->{py3def}) {
1407+ if (not $pyall and grep {$_ eq 'python3' or $_ eq 'python3-dev'} @deps) {
1408+ # TODO: "python3" case: should X-Python3-Version header in debian/control be also required here?
1409+ push @result, "pybuild --$step -i $i -p '$this->{py3def}' @options";
1410+ }
1411+ if (not $pyalldbg and grep {$_ eq 'python3-dbg'} @deps) {
1412+ push @result, "pybuild --$step -i $i-dbg -p '$this->{py3def}' @options";
1413+ }
1414+ }
1415+ # TODO: pythonX.Y → `pybuild -i python{version} -p X.Y`
1416+
1417+ # PyPy
1418+ if ($this->{pypydef} and grep {$_ eq 'pypy'} @deps) {
1419+ push @result, "pybuild --$step -i pypy -p '$this->{pypydef}' @options";
1420+ }
1421+ }
1422+ return @result;
1423+}
1424+
1425+sub python_build_dependencies {
1426+ my $this=shift;
1427+
1428+ my @result;
1429+ open (CONTROL, 'debian/control') || error("cannot read debian/control: $!\n");
1430+ foreach my $builddeps (join('', <CONTROL>) =~
1431+ /^Build-Depends[^:]*:.*\n(?:^[^\w\n#].*\n)*/gmi) {
1432+ while ($builddeps =~ /[\s,](pypy|python[0-9\.]*(-all)?((-dev)|(-dbg))?)[\s,]|$/g) {
1433+ if ($1) {push @result, $1};
1434+ }
1435+ }
1436+
1437+ close CONTROL;
1438+ return @result;
1439+}
1440+
1441+1
1442
1443=== renamed file 'python3.pm' => 'dh/python3.pm'
1444=== modified file 'dh_python3'
1445--- dh_python3 2012-12-12 10:45:17 +0000
1446+++ dh_python3 2013-01-24 11:51:20 +0000
1447@@ -450,7 +450,10 @@
1448
1449 pyclean_added = False # invoke pyclean only once in maintainer script
1450 if stats['compile']:
1451- dh.autoscript(package, 'postinst', 'postinst-py3compile', '')
1452+ args = ''
1453+ if options.vrange and options.vrange != (None, None):
1454+ args += "-V %s" % vrange_str(options.vrange)
1455+ dh.autoscript(package, 'postinst', 'postinst-py3compile', args)
1456 dh.autoscript(package, 'prerm', 'prerm-py3clean', '')
1457 pyclean_added = True
1458 for pdir, details in stats['private_dirs'].items():
1459
1460=== modified file 'dh_python3.rst'
1461--- dh_python3.rst 2012-11-26 09:20:46 +0000
1462+++ dh_python3.rst 2013-01-24 11:51:20 +0000
1463@@ -129,3 +129,4 @@
1464 * /usr/share/doc/python3-doc/README.PyDist (python3-doc package)
1465 * py3compile(1), py3clean(1)
1466 * dh_python2(1), pycompile(1), pyclean(1)
1467+* http://deb.li/dhp3 - most recent version of this document
1468
1469=== modified file 'pybuild' (properties changed: +x to -x)
1470--- pybuild 2012-12-12 10:45:17 +0000
1471+++ pybuild 2013-01-24 11:51:20 +0000
1472@@ -38,7 +38,8 @@
1473 except ImportError:
1474 sys.path.append('/usr/share/python3/')
1475 from debpython import build # NOQA
1476- from debpython.version import debsorted, get_requested_versions, vrepr
1477+ from debpython.version import Version, debsorted, get_requested_versions
1478+ from debpython.tools import execute, move_matching_files
1479
1480 if cfg.list_systems:
1481 for name, Plugin in sorted(build.plugins.items()):
1482@@ -52,6 +53,7 @@
1483 log.error('unrecognized build system: %s', cfg.system)
1484 exit(10)
1485 plugin = Plugin(cfg)
1486+ context = {'ENV': environ.copy(), 'args': {}, 'dir': cfg.dir}
1487 else:
1488 plugin, certainty, context = None, 0, None
1489 for Plugin in build.plugins.values():
1490@@ -61,7 +63,7 @@
1491 log.warn('cannot initialize %s plugin: %s', Plugin.NAME,
1492 err, exc_info=cfg.verbose)
1493 continue
1494- tmp_context = {'ENV': environ.copy(), 'args': {}}
1495+ tmp_context = {'ENV': environ.copy(), 'args': {}, 'dir': cfg.dir}
1496 tmp_context['ENV'].update({'LC_ALL': 'C',
1497 'http_proxy': 'http://127.0.0.1:9/'})
1498 tmp_certainty = tmp_plugin.detect(tmp_context)
1499@@ -69,7 +71,8 @@
1500 plugin, certainty, context = tmp_plugin, tmp_certainty, tmp_context
1501 del Plugin
1502 if not plugin:
1503- log.error('cannot detect build system')
1504+ log.error('cannot detect build system, please use --system option'
1505+ ' or set PYBUILD_SYSTEM env. variable')
1506 exit(11)
1507 for interpreter in cfg.interpreter:
1508 if plugin.SUPPORTED_INTERPRETERS is not True and interpreter not in plugin.SUPPORTED_INTERPRETERS:
1509@@ -83,41 +86,122 @@
1510 exit(0)
1511
1512 # reversed so that default Python version will be last
1513- versions = cfg.versions # TODO: default should be last
1514+ versions = cfg.versions
1515 if not versions:
1516 versions = list(reversed(debsorted(get_requested_versions(available=True))))
1517- # TODO: defalt interpreter{version} → interpreter
1518+ versions = [Version(v) for v in versions]
1519+
1520+ def get_option(name, interpreter=None, version=None, default=None):
1521+ if interpreter:
1522+ # try PYBUILD_NAME_python3.3-dbg (or hardcoded interpreter)
1523+ i = interpreter.format(version=version or '')
1524+ opt = "PYBUILD_{}_{}".format(name.upper(), i)
1525+ if opt in environ:
1526+ return environ[opt]
1527+ # try PYBUILD_NAME_python3-dbg (if not checked above)
1528+ if '{version}' in interpreter and version:
1529+ i = interpreter.format(version=version.major)
1530+ opt = "PYBUILD_{}_{}".format(name.upper(), i)
1531+ if opt in environ:
1532+ return environ[opt]
1533+ # try PYBUILD_NAME
1534+ opt = "PYBUILD_{}".format(name.upper())
1535+ if opt in environ:
1536+ return environ[opt]
1537+ # try command line args
1538+ return getattr(cfg, name, default) or default
1539
1540 def get_args(context, step, version, interpreter):
1541- v = vrepr(version)
1542- i = interpreter.format(version=v)
1543- home_dir = '.pybuild/{}_{}'.format(interpreter.format(version='X.Y'), v)
1544- build_dir = join(home_dir, 'build')
1545+ i = interpreter.format(version=version)
1546+ home_dir = '.pybuild/{}_{}'.format(interpreter.format(version='X.Y'), version)
1547+ build_dir = get_option('build_dir', interpreter, version,
1548+ default=join(home_dir, 'build'))
1549 args = dict(context['args'])
1550 args.update({
1551 'interpreter': i,
1552- 'args': getattr(cfg, "%s_args" % step),
1553- 'dir': cfg.dir.format(version=v),
1554- 'dest_dir': (join(cfg.dir, cfg.dest_dir)).format(
1555- version=v, interpreter=i),
1556- 'home_dir': (join(cfg.dir, home_dir)).format(
1557- version=v, interpreter=i),
1558- 'build_dir': (join(cfg.dir, build_dir)).format(
1559- version=v, interpreter=i),
1560- 'install_dir': cfg.install_dir.format(
1561- version=v, interpreter=i),
1562- 'version': v})
1563+ 'args': get_option("%s_args" % step, interpreter, version, ''),
1564+ 'dir': context['dir'].format(version=version, interpreter=i),
1565+ 'destdir': (join(context['dir'], context['destdir'])).format(
1566+ version=version, interpreter=i),
1567+ 'home_dir': (join(context['dir'], home_dir)).format(
1568+ version=version, interpreter=i),
1569+ 'build_dir': (join(context['dir'], build_dir)).format(
1570+ version=version, interpreter=i),
1571+ # versioned dist-packages even for Python 3.X - dh_python3 will fix it later
1572+ # (and will have a chance to compare files)
1573+ 'install_dir': get_option('install_dir', interpreter, version,
1574+ '/usr/lib/python{version}/dist-packages'
1575+ ).format(version=version, interpreter=i),
1576+ 'version': version})
1577+ if interpreter == 'pypy':
1578+ args['install_dir'] = '/usr/lib/pypy/dist-packages/'
1579 if step == 'test':
1580- args['test_dir'] = join(args['dest_dir'], args['install_dir'].lstrip('/'))
1581 pp = context['ENV'].get('PYTHONPATH', '')
1582+ args['test_dir'] = join(args['destdir'], args['install_dir'].lstrip('/'))
1583 if args['test_dir'] not in pp.split(':'):
1584- context['ENV']['PYTHONPATH'] = "{}:{}".format(pp, args['test_dir']).lstrip(':')
1585+ pp = "{}:{}".format(pp, args['test_dir']).lstrip(':')
1586+ if args['build_dir'] not in pp.split(':'):
1587+ pp = "{}:{}".format(pp, args['build_dir']).lstrip(':')
1588+ args['PYTHONPATH'] = pp
1589
1590 if not exists(args['build_dir']):
1591 makedirs(args['build_dir'])
1592
1593 return args
1594
1595+ def is_disabled(step, interpreter, version):
1596+ i = interpreter
1597+ prefix = "{}/".format(step)
1598+ disabled = (get_option('disable', i, version) or '').split()
1599+ for item in disabled:
1600+ if item in (step, '1'):
1601+ log.debug('disabling {} step for {} {}'.format(step, i, version))
1602+ return True
1603+ if item.startswith(prefix):
1604+ disabled.append(item[len(prefix):])
1605+ if i in disabled or str(version) in disabled or \
1606+ i.format(version=version) in disabled or \
1607+ i.format(version=version.major) in disabled:
1608+ log.debug('disabling {} step for {} {}'.format(step, i, version))
1609+ return True
1610+ return False
1611+
1612+ def run(func, interpreter, version, context):
1613+ step = func.__func__.__name__
1614+ args = get_args(context, step, version, interpreter)
1615+ if 'PYTHONPATH' in args:
1616+ env = dict(context['ENV'])
1617+ env['PYTHONPATH'] = args['PYTHONPATH']
1618+ else:
1619+ env = context['ENV']
1620+
1621+ before_cmd = get_option('before_{}'.format(step), interpreter, version)
1622+ if before_cmd:
1623+ if cfg.quiet:
1624+ log_file = join(args['home_dir'], 'before_{}_cmd.log'.format(step))
1625+ else:
1626+ log_file = False
1627+ command = before_cmd.format(**args)
1628+ output = execute(command, context['dir'], env, log_file)
1629+ if output['returncode'] != 0:
1630+ msg = 'exit code={}: {}'.format(output['returncode'], command)
1631+ raise Exception(msg)
1632+
1633+ result = func(context, args)
1634+
1635+ after_cmd = get_option('after_{}'.format(step), interpreter, version)
1636+ if after_cmd:
1637+ if cfg.quiet:
1638+ log_file = join(args['home_dir'], 'after_{}_cmd.log'.format(step))
1639+ else:
1640+ log_file = False
1641+ command = after_cmd.format(**args)
1642+ output = execute(command, context['dir'], env, log_file)
1643+ if output['returncode'] != 0:
1644+ msg = 'exit code={}: {}'.format(output['returncode'], command)
1645+ raise Exception(msg)
1646+ return result
1647+
1648 func = None
1649 if cfg.clean_only:
1650 func = plugin.clean
1651@@ -129,44 +213,68 @@
1652 func = plugin.install
1653 elif cfg.test_only:
1654 func = plugin.test
1655+
1656+ ### one function for each interpreter at a time mode ###
1657 if func:
1658 step = func.__func__.__name__
1659 for interpreter in cfg.interpreter:
1660 iversions = versions
1661- if '{version}' not in interpreter:
1662+ if '{version}' not in interpreter and len(versions) > 1:
1663 log.info('limiting Python versions to %s due to missing {version}'
1664- 'in interpreter string', vrepr(versions[0]))
1665+ ' in interpreter string', str(versions[0]))
1666 iversions = versions[:1] # just the default or closest to default
1667 for version in iversions:
1668+ if is_disabled(step, interpreter, version):
1669+ continue
1670 c = dict(context)
1671+ c['dir'] = get_option('dir', interpreter, version, cfg.dir)
1672+ c['destdir'] = get_option('destdir', interpreter, version, cfg.destdir)
1673 try:
1674- func(c, get_args(c, step, version, interpreter))
1675+ run(func, interpreter, version, c)
1676 except Exception as err:
1677 log.error('%s: plugin %s failed with: %s',
1678 step, plugin.NAME, err, exc_info=cfg.verbose)
1679 exit(13)
1680+ if step == 'install':
1681+ ext_destdir = get_option('ext_destdir', interpreter, version)
1682+ if ext_destdir:
1683+ move_matching_files(c['destdir'], ext_destdir,
1684+ get_option('ext_pattern', interpreter, version))
1685 exit(0)
1686
1687+ ### all functions for interpreters in batches mode ###
1688 try:
1689 context_map = {}
1690 for i in cfg.interpreter:
1691 iversions = versions
1692- if '{version}' not in i:
1693+ if '{version}' not in i and len(versions) > 1:
1694 log.info('limiting Python versions to %s due to missing {version}'
1695- 'in interpreter string', vrepr(versions[0]))
1696+ ' in interpreter string', str(versions[0]))
1697 iversions = versions[:1] # just the default or closest to default
1698 for version in iversions:
1699 key = (i, version)
1700- if key not in context_map:
1701- context_map[key] = dict(context)
1702- c = context_map[key]
1703- if not cfg.no_clean:
1704- plugin.clean(c, get_args(c, 'clean', version, i))
1705- plugin.configure(c, get_args(c, 'configure', version, i))
1706- plugin.build(c, get_args(c, 'build', version, i))
1707- plugin.install(c, get_args(c, 'install', version, i))
1708- if not cfg.no_tests:
1709- plugin.test(c, get_args(c, 'test', version, i))
1710+ if key in context_map:
1711+ c = context_map[key]
1712+ else:
1713+ c = dict(context)
1714+ c['dir'] = get_option('dir', i, version, cfg.dir)
1715+ c['destdir'] = get_option('destdir', i, version, cfg.destdir)
1716+ context_map[key] = c
1717+
1718+ if not is_disabled('clean', i, version):
1719+ run(plugin.clean, i, version, c)
1720+ if not is_disabled('configure', i, version):
1721+ run(plugin.configure, i, version, c)
1722+ if not is_disabled('build', i, version):
1723+ run(plugin.build, i, version, c)
1724+ if not is_disabled('install', i, version):
1725+ run(plugin.install, i, version, c)
1726+ ext_destdir = get_option('ext_destdir', i, version)
1727+ if ext_destdir:
1728+ move_matching_files(c['destdir'], ext_destdir,
1729+ get_option('ext_pattern', i, version))
1730+ if not is_disabled('test', i, version):
1731+ run(plugin.test, i, version, c)
1732 except Exception as err:
1733 log.error('plugin %s failed: %s', plugin.NAME, err,
1734 exc_info=cfg.verbose)
1735@@ -204,63 +312,75 @@
1736 help='invoke install step for all requested Python versions')
1737 action.add_argument('--test', action='store_true', dest='test_only',
1738 help='invoke tests for auto-detected build system')
1739+ action.add_argument('--list-systems', action='store_true',
1740+ help='list available build systems and exit')
1741
1742 arguments = parser.add_argument_group('BUILD SYSTEM ARGS', '''
1743 Additional arguments passed to the build system.
1744- {version} will be replaced with current Python version.
1745 --system=custom requires complete command.''')
1746- arguments.add_argument('--clean-args', metavar='ARGS',
1747- default=environ.get('PYBUILD_CLEAN_ARGS', ''))
1748- arguments.add_argument('--configure-args', metavar='ARGS',
1749- default=environ.get('PYBUILD_CONFIGURE_ARGS', ''))
1750- arguments.add_argument('--build-args', metavar='ARGS',
1751- default=environ.get('PYBUILD_BUILD_ARGS', ''))
1752- arguments.add_argument('--install-args', metavar='ARGS',
1753- default=environ.get('PYBUILD_INSTALL_ARGS', ''))
1754- arguments.add_argument('--test-args', metavar='ARGS',
1755- default=environ.get('PYBUILD_TEST_ARGS', ''))
1756-
1757- dirs = parser.add_argument_group('DIRECTORIES', '''
1758- {version} will be replaced with current Python version;
1759- {interpreter} will be replaced with selected interpreter''')
1760+ arguments.add_argument('--before-clean', metavar='CMD',
1761+ help='invoked before the clean command')
1762+ arguments.add_argument('--clean-args', metavar='ARGS')
1763+ arguments.add_argument('--after-clean', metavar='CMD',
1764+ help='invoked after the clean command')
1765+
1766+ arguments.add_argument('--before-configure', metavar='CMD',
1767+ help='invoked before the configure command')
1768+ arguments.add_argument('--configure-args', metavar='ARGS')
1769+ arguments.add_argument('--after-configure', metavar='CMD',
1770+ help='invoked after the configure command')
1771+
1772+ arguments.add_argument('--before-build', metavar='CMD',
1773+ help='invoked before the build command')
1774+ arguments.add_argument('--build-args', metavar='ARGS')
1775+ arguments.add_argument('--after-build', metavar='CMD',
1776+ help='invoked after the build command')
1777+
1778+ arguments.add_argument('--before-install', metavar='CMD',
1779+ help='invoked before the install command')
1780+ arguments.add_argument('--install-args', metavar='ARGS')
1781+ arguments.add_argument('--after-install', metavar='CMD',
1782+ help='invoked after the install command')
1783+
1784+ arguments.add_argument('--before-test', metavar='CMD',
1785+ help='invoked before the test command')
1786+ arguments.add_argument('--test-args', metavar='ARGS')
1787+ arguments.add_argument('--after-test', metavar='CMD',
1788+ help='invoked after the test command')
1789+
1790+ dirs = parser.add_argument_group('DIRECTORIES')
1791 dirs.add_argument('-d', '--dir', action='store', metavar='DIR',
1792- default=environ.get('PYBUILD_DIR', getcwd()),
1793+ default=getcwd(),
1794 help='source files directory - base for other relative dirs [default: CWD]')
1795- dirs.add_argument('--dest-dir', action='store', metavar='DIR',
1796+ dirs.add_argument('--dest-dir', action='store', metavar='DIR', dest='destdir',
1797 default=environ.get('DESTDIR', 'debian/tmp'),
1798 help='destination directory [default: debian/tmp]')
1799+ dirs.add_argument('--ext-dest-dir', action='store', metavar='DIR', dest='ext_destdir',
1800+ default=environ.get('EXT_DESTDIR'),
1801+ help='destination directory for .so files')
1802+ dirs.add_argument('--ext-pattern', action='store', metavar='PATTERN',
1803+ default=environ.get('EXT_PATTERN', r'\.so(\.[^/]*)?$'),
1804+ help='regular expression for files that should be moved'
1805+ ' if --ext-destdir is set [default: .so files]')
1806 dirs.add_argument('--install-dir', action='store', metavar='DIR',
1807- default=environ.get('PYBUILD_INSTALL_DIR',
1808- # versioned dist-packages even for Python 3.X -
1809- # dh_python3 will fix it later (and will have a chance
1810- # to compare files)
1811- '/usr/lib/python{version}/dist-packages'),
1812 help='installation directory [default: .../dist-packages]')
1813
1814 limit = parser.add_argument_group('LIMITATIONS')
1815 limit.add_argument('-s', '--system',
1816 default=environ.get('PYBUILD_SYSTEM'),
1817- help='selected build system [default: auto-detection]')
1818+ help='select a build system [default: auto-detection]')
1819 limit.add_argument('-p', '--pyver', action='append', dest='versions',
1820 help='''build for Python VERSION.
1821- This option can be used multiple times''')
1822+ This option can be used multiple times
1823+ [default: all supported Python 3.X versions]''')
1824 limit.add_argument('-i', '--interpreter', action='append',
1825 help='change interpreter [default: python{version}]')
1826-
1827- limit.add_argument('--no-tests', action='store_true',
1828- default=environ.get('PYBUILD_NO_TESTS') == '1',
1829- help='skip tests step after building module/extension')
1830- limit.add_argument('--no-clean', action='store_false',
1831- default=environ.get('PYBUILD_NO_CLEAN') == '1',
1832- help='skip the clean step before building for next Python version')
1833-
1834- other = parser.add_argument_group('OTHER')
1835- other.add_argument('--list-systems', action='store_true',
1836- help='list available build systems and exit')
1837+ limit.add_argument('--disable', metavar='ITEMS',
1838+ help='disable action, interpreter or version')
1839
1840 args = parser.parse_args()
1841 if not args.interpreter:
1842- args.interpreter = [environ.get('PYBUILD_INTERPRETER', 'python{version}')]
1843+ args.interpreter = environ.get('PYBUILD_INTERPRETERS', 'python{version}').split()
1844 if not args.versions:
1845 args.versions = environ.get('PYBUILD_VERSIONS', '').split()
1846 else:
1847@@ -281,6 +401,7 @@
1848 log.setLevel(logging.DEBUG)
1849 else:
1850 log.setLevel(logging.WARNING)
1851+ log.debug(sys.argv)
1852 main(cfg)
1853 # let dh/cdbs clean the .pybuild dir
1854 # rmtree(join(cfg.dir, '.pybuild'))
1855
1856=== added file 'pybuild.rst'
1857--- pybuild.rst 1970-01-01 00:00:00 +0000
1858+++ pybuild.rst 2013-01-24 11:51:20 +0000
1859@@ -0,0 +1,165 @@
1860+=========
1861+ pybuild
1862+=========
1863+
1864+----------------------------------------------------------------------------------------------------
1865+invokes various build systems for requested Python versions in order to build modules and extensions
1866+----------------------------------------------------------------------------------------------------
1867+
1868+SYNOPSIS
1869+========
1870+ pybuild [ACTION] [BUILD SYSTEM ARGUMENTS] [DIRECTORIES] [OPTIONS]
1871+
1872+OPTIONS
1873+=======
1874+ Most options can be set (in addition to command line) via environment
1875+ variables. PyBuild will check:
1876+
1877+ * PYBUILD_OPTION_VERSIONED_INTERPRETER (f.e. PYBUILD_CLEAN_ARGS_python3.2)
1878+ * PYBUILD_OPTION_INTERPRETER (f.e. PYBUILD_CONFIGURE_ARGS_python3-dbg)
1879+ * PYBUILD_OPTION (f.e. PYBUILD_INSTALL_ARGS)
1880+
1881+optional arguments
1882+------------------
1883+ -h, --help show this help message and exit
1884+ -v, --verbose turn verbose mode on
1885+ -q, --quiet doesn't show external command's output
1886+ -qq, --really-quiet be quiet
1887+ --version show program's version number and exit
1888+
1889+ACTION
1890+------
1891+ The default is to build, install and test the library using detected build
1892+ system version by version. Selecting one of following actions, will invoke
1893+ given action for all versions - one by one - which (contrary to the default
1894+ action) in some build systems can overwrite previous results.
1895+
1896+ --detect
1897+ return the name of detected build system
1898+ --clean
1899+ clean files using auto-detected build system specific methods
1900+ --configure
1901+ invoke configure step for all requested Python versions
1902+ --build
1903+ invoke build step for all requested Python versions
1904+ --install
1905+ invoke install step for all requested Python versions
1906+ --test
1907+ invoke tests for auto-detected build system
1908+ --list-systems
1909+ list available build systems and exit
1910+
1911+BUILD SYSTEM ARGUMENTS
1912+----------------------
1913+ Additional arguments passed to the build system.
1914+ --system=custom requires complete command.
1915+
1916+ --before-clean COMMAND
1917+ invoked before the clean command
1918+ --clean-args ARGUMENTS
1919+ arguments added to clean command generated by build system plugin
1920+ --after-clean COMMAND
1921+ invoked after the clean command
1922+ --before-configure COMMAND
1923+ invoked before the configure command
1924+ --configure-args ARGUMENTS
1925+ arguments added to configure command generated by build system plugin
1926+ --after-configure COMMAND
1927+ invoked after the configure command
1928+ --before-build COMMAND
1929+ invoked before the build command
1930+ --build-args ARGUMENTS
1931+ arguments added to build command generated by build system plugin
1932+ --after-build COMMAND
1933+ invoked after the build command
1934+ --before-install COMMAND
1935+ invoked before the install command
1936+ --install-args ARGUMENTS
1937+ arguments added to install command generated by build system plugin
1938+ --after-install COMMAND
1939+ invoked after the install command
1940+ --before-test COMMAND
1941+ invoked before the test command
1942+ --test-args ARGUMENTS
1943+ arguments added to test command generated by build system plugin
1944+ --after-test COMMAND
1945+ invoked after the test command
1946+
1947+variables that can be used in `ARGUMENTS` and `COMMAND`
1948+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1949+* `{version}` will be replaced with current Python version,
1950+* `{interpreter}` will be replaced with current interpreter,
1951+* `{dir}` will be replaced with sources directory,
1952+* `{destdir}` will be replaced with destination directory,
1953+* `{home_dir}` will be replaced with temporary HOME directory,
1954+ where plugins can keep their data
1955+ (.pybuild/interpreter_version/ by default),
1956+* `{build_dir}` will be replaced with build directory
1957+* `{install_dir}` will be replaced with install directory.
1958+
1959+DIRECTORIES
1960+-----------
1961+ -d DIR, --dir DIR
1962+ set source files directory - base for other relative dirs
1963+ [by default: current working directory]
1964+ --dest-dir DIR
1965+ set destination directory [default: debian/tmp]
1966+ --ext-dest-dir DIR
1967+ set destination directory for .so files
1968+ --ext-pattern PATTERN
1969+ regular expression for files that should be moved if --ext-destdir is set
1970+ [default: `\.so(\.[^/]*)?$`]
1971+ --install-dir DIR
1972+ set installation directory [default: .../dist-packages]
1973+
1974+variables that can be used in `DIR`
1975+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1976+* `{version}` will be replaced with current Python version,
1977+* `{interpreter}` will be replaced with selected interpreter.
1978+
1979+LIMITATIONS
1980+-----------
1981+ -s SYSTEM, --system SYSTEM
1982+ select a build system [default: auto-detection]
1983+ -p VERSIONS, --pyver VERSIONS
1984+ build for Python VERSIONS. This option can be used multiple times.
1985+ Versions can be separated by space character.
1986+ The default is all Python 3.X supported versions.
1987+ -i INTERPRETER, --interpreter INTERPRETER
1988+ change interpreter [default: python{version}]
1989+ --disable ITEMS
1990+ disable action, interpreter, version or any mix of them.
1991+
1992+disable examples
1993+~~~~~~~~~~~~~~~~
1994+* `--disable test/python2.5-dbg` - disables tests for python2.5-dbg
1995+* `--disable '2.4 2.7'` - disables all actions for version 2.4 and 2.7
1996+* `PYBUILD_DISABLE_python2=1` - disables all actions for Python 2.X
1997+* `PYBUILD_DISABLE_python3.3=test` - disables tests for Python 3.3
1998+* `PYBUILD_DISABLE=test/python3.3` - same as above
1999+* `PYBUILD_DISABLE='configure/python3 2.4 pypy'` - disables configure
2000+ action for all python3 interpreters, all actions for version 2.4, and
2001+ all actions for pypy
2002+
2003+DEBHELPER COMMAND SEQUENCER INTEGRATION
2004+=======================================
2005+* build depend on python3 (>= 3.3.0-2)
2006+* add "--buildsystem=pybuild" to dh's arguments in debian/rules
2007+* if more than one binary package is build:
2008+ add debian/python-foo.install files, or
2009+ export PYBUILD_DESTDIR env. variables in debian/rules
2010+
2011+debian/rules file example::
2012+
2013+ export PYBUILD_DESTDIR_python2=debian/python-foo/
2014+ export PYBUILD_DESTDIR_python2-dbg=debian/python-foo-dbg/
2015+ export PYBUILD_DESTDIR_python3=debian/python3-foo/
2016+ export PYBUILD_DESTDIR_python3-dbg=debian/python3-foo-dbg/
2017+ %:
2018+ dh $@ --with python2,python3 --buildsystem=pybuild
2019+
2020+SEE ALSO
2021+========
2022+* dh_python2(1)
2023+* dh_python3(1)
2024+* http://deb.li/pybuild - most recent version of this document
2025
2026=== modified file 'tests/t1/debian/compat'
2027--- tests/t1/debian/compat 2010-06-18 00:10:21 +0000
2028+++ tests/t1/debian/compat 2013-01-24 11:51:20 +0000
2029@@ -1,1 +1,1 @@
2030-7
2031+9
2032
2033=== modified file 'tests/t1/debian/control'
2034--- tests/t1/debian/control 2012-10-21 21:29:45 +0000
2035+++ tests/t1/debian/control 2013-01-24 11:51:20 +0000
2036@@ -2,8 +2,7 @@
2037 Section: python
2038 Priority: optional
2039 Maintainer: Piotr Ożarowski <piotr@debian.org>
2040-Build-Depends: debhelper (>= 7.0.50~)
2041-Build-Depends-Indep: python-all
2042+Build-Depends: debhelper (>= 7.0.50~), python3-all
2043 Standards-Version: 3.9.0
2044 X-Python3-Version: >= 3.1, << 3.4
2045
2046
2047=== modified file 'tests/t1/debian/rules'
2048--- tests/t1/debian/rules 2011-04-03 19:02:43 +0000
2049+++ tests/t1/debian/rules 2013-01-24 11:51:20 +0000
2050@@ -5,7 +5,8 @@
2051 override_dh_auto_install:
2052 python3 setup.py install --root=debian/python3-foo/
2053
2054-override_dh_pysupport:
2055+override_dh_install:
2056+ dh_install
2057 find debian/ -name jquery.js -exec \
2058 ln -fs /usr/share/javascript/jquery/jquery.js '{}' \;
2059 DH_VERBOSE=1 ../../dh_python3\
2060
2061=== modified file 'tests/t2/debian/compat'
2062--- tests/t2/debian/compat 2011-04-03 19:02:43 +0000
2063+++ tests/t2/debian/compat 2013-01-24 11:51:20 +0000
2064@@ -1,1 +1,1 @@
2065-7
2066+9
2067
2068=== modified file 'tests/t2/debian/rules'
2069--- tests/t2/debian/rules 2012-12-12 10:45:17 +0000
2070+++ tests/t2/debian/rules 2013-01-24 11:51:20 +0000
2071@@ -5,7 +5,8 @@
2072 %:
2073 dh $@ --buildsystem=python_distutils
2074
2075-override_dh_pysupport:
2076+override_dh_install:
2077+ dh_install
2078 # install also as private extension
2079 dh_install debian/python3-foo/usr/local/lib/python3*/dist-packages/foo/bar*.so \
2080 /usr/lib/python3-foo/
2081
2082=== modified file 'tests/t3/debian/compat'
2083--- tests/t3/debian/compat 2011-04-03 19:02:43 +0000
2084+++ tests/t3/debian/compat 2013-01-24 11:51:20 +0000
2085@@ -1,1 +1,1 @@
2086-7
2087+9
2088
2089=== modified file 'tests/t3/debian/rules'
2090--- tests/t3/debian/rules 2011-04-03 19:02:43 +0000
2091+++ tests/t3/debian/rules 2013-01-24 11:51:20 +0000
2092@@ -5,7 +5,8 @@
2093 %:
2094 dh $@
2095
2096-override_dh_pysupport:
2097+override_dh_install:
2098+ dh_install
2099 DH_VERBOSE=1 ../../dh_python3
2100
2101 override_dh_auto_build:
2102
2103=== modified file 'tests/t4/debian/compat'
2104--- tests/t4/debian/compat 2012-06-30 15:20:39 +0000
2105+++ tests/t4/debian/compat 2013-01-24 11:51:20 +0000
2106@@ -1,1 +1,1 @@
2107-8
2108+9
2109
2110=== modified file 'tests/t4/debian/rules'
2111--- tests/t4/debian/rules 2012-06-30 15:20:39 +0000
2112+++ tests/t4/debian/rules 2013-01-24 11:51:20 +0000
2113@@ -2,7 +2,8 @@
2114 %:
2115 dh $@ --buildsystem=python_distutils
2116
2117-override_dh_pysupport:
2118+override_dh_install:
2119+ dh_install
2120 DH_VERBOSE=1 ../../dh_python3
2121 DH_VERBOSE=1 ../../dh_python3 /usr/share/bar
2122 DH_VERBOSE=1 ../../dh_python3 /usr/share/baz32 --shebang '/usr/bin/python3.2 -OO'

Subscribers

People subscribed via source and target branches