From 67a247f6d9697e64db0e2ab0133cebd3fc330259 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20O=C5=BCarowski?= <piotr@debian.org> Date: Sun, 10 Jun 2012 14:32:29 +0200 Subject: [PATCH] =?UTF-8?q?*=20dh=5Fpython2:=20=20=20-=20rewrite=20shebang?= =?UTF-8?q?s=20by=20default=20(disable=20via=20--no-shebang-rewrite),=20?= =?UTF-8?q?=20=20=20=20examples:=20=20=20=20=20=20+=20"/usr/bin/env=20pyth?= =?UTF-8?q?on*"=20=E2=86=92=20"/usr/bin/python*"=20=20=20=20=20=20+=20"/us?= =?UTF-8?q?r/local/bin/python=20foo"=20=E2=86=92=20"/usr/bin/python=20foo"?= =?UTF-8?q?=20=20=20=20=20=20+=20"/usr/bin/python2"=20=E2=86=92=20"/usr/bi?= =?UTF-8?q?n/python"=20=20=20-=20new=20--shebang=20option=20to=20replace?= =?UTF-8?q?=20all=20shebangs=20in=20bin=20dirs=20=20=20=20=20(example:=20-?= =?UTF-8?q?-shebang=20/usr/bin/python2.6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- debian/changelog | 12 ++++++++-- debpython/depends.py | 10 +++----- debpython/tools.py | 45 ++++++++++++++++++++++++++++++----- dh_python2 | 45 ++++++++++++++++++++++------------- dh_python2.rst | 4 ++++ tests/Makefile | 2 +- tests/t7/Makefile | 24 +++++++++++++++++++ tests/t7/bar.py | 2 ++ tests/t7/baz.py | 2 ++ tests/t7/debian/changelog | 5 ++++ tests/t7/debian/compat | 1 + tests/t7/debian/control | 13 ++++++++++ tests/t7/debian/copyright | 2 ++ tests/t7/debian/install | 7 ++++++ tests/t7/debian/rules | 11 +++++++++ tests/t7/debian/source/format | 1 + tests/t7/foo.py | 2 ++ tests/t7/setup.py | 0 tests/t7/spam.py | 1 + 19 files changed, 156 insertions(+), 33 deletions(-) create mode 100644 tests/t7/Makefile create mode 100755 tests/t7/bar.py create mode 100755 tests/t7/baz.py create mode 100644 tests/t7/debian/changelog create mode 100644 tests/t7/debian/compat create mode 100644 tests/t7/debian/control create mode 100644 tests/t7/debian/copyright create mode 100644 tests/t7/debian/install create mode 100755 tests/t7/debian/rules create mode 100644 tests/t7/debian/source/format create mode 100755 tests/t7/foo.py create mode 100644 tests/t7/setup.py create mode 100644 tests/t7/spam.py diff --git a/debian/changelog b/debian/changelog index 17f10e6..540b484 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,7 +1,15 @@ python-defaults (2.7.3~rc2-2) unstable; urgency=low - * dh_python2: remove even more \.so.* dangling symlinks, thanks to Stefano - Rivera for providing a test case + * dh_python2: + - remove even more \.so.* dangling symlinks, thanks to Stefano + Rivera for providing a test case + - rewrite shebangs by default (disable via --no-shebang-rewrite), + examples: + + "/usr/bin/env python*" → "/usr/bin/python*" + + "/usr/local/bin/python foo" → "/usr/bin/python foo" + + "/usr/bin/python2" → "/usr/bin/python" + - new --shebang option to replace all shebangs in bin dirs + (example: --shebang /usr/bin/python2.6) -- Piotr Ożarowski <piotr@debian.org> Sun, 10 Jun 2012 13:52:38 +0200 diff --git a/debpython/depends.py b/debpython/depends.py index ec286b7..cf146d1 100644 --- a/debpython/depends.py +++ b/debpython/depends.py @@ -112,15 +112,11 @@ class Dependencies(object): if stats['compile']: self.depend(MINPYCDEP) - if not options.ignore_shebangs: - for interpreter, version in stats['shebangs']: - self.depend(interpreter) + for interpreter, version in stats['shebangs']: + self.depend(interpreter) for private_dir, details in stats['private_dirs'].iteritems(): - if options.ignore_shebangs: - versions = [] - else: - versions = list(v for i, v in details.get('shebangs', []) if v) + versions = list(v for i, v in details.get('shebangs', []) if v) for v in versions: if v in SUPPORTED: diff --git a/debpython/tools.py b/debpython/tools.py index 78979f6..7b90f95 100644 --- a/debpython/tools.py +++ b/debpython/tools.py @@ -1,5 +1,5 @@ # -*- coding: UTF-8 -*- -# Copyright © 2010 Piotr Ożarowski <piotr@debian.org> +# Copyright © 2010-2012 Piotr Ożarowski <piotr@debian.org> # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -33,7 +33,7 @@ from debpython.version import RANGE_PATTERN, getver, get_requested_versions log = logging.getLogger(__name__) EGGnPTH_RE = re.compile(r'(.*?)(-py\d\.\d(?:-[^.]*)?)?(\.egg-info|\.pth)$') -SHEBANG_RE = re.compile(r'^#!\s*/usr/bin/(?:env\s+)?(python(\d+\.\d+)?(?:-dbg)?).*') +SHEBANG_RE = re.compile(r'^#!\s*(.*?/bin/.*?)(python(\d+\.\d+)?(?:-dbg)?)(?:\s(.*))?') SHAREDLIB_RE = re.compile(r'NEEDED.*libpython(\d\.\d)') INSTALL_RE = re.compile(r""" (?P<pattern>.+?) # file pattern @@ -92,6 +92,39 @@ def relative_symlink(target, link): return os.symlink(relpath(target, link), link) +def fix_shebang(fpath, replacement=None): + """Normalize file's shebang. + + :param replacement: new shebang command (path to interpreter and options) + """ + try: + with open(fpath) as fp: + fcontent = fp.readlines() + except IOError: + log.error('cannot open %s', fpath) + return False + + match = SHEBANG_RE.match(fcontent[0]) + if not match: + return None + if not replacement: + path, interpreter, version, argv = match.groups() + if path != '/usr/bin': # f.e. /usr/local/* or */bin/env + replacement = "/usr/bin/%s" % interpreter + if interpreter == 'python2': + replacement = '/usr/bin/python' + if replacement and argv: + replacement += " %s" % argv + if replacement: + log.info('replacing shebang in %s (%s)', fpath, fcontent[0]) + # do not catch IOError here, the file is zeroed at this stage so it's + # better to fail dh_python2 + with open(fpath, 'w') as fp: + fp.write("#! %s\n" % replacement) + fp.writelines(fcontent[1:]) + return True + + def shebang2pyver(fpath): """Check file's shebang. @@ -105,10 +138,10 @@ def shebang2pyver(fpath): if not match: return None res = match.groups() - if res != (None, None): - if res[1]: - res = res[0], getver(res[1]) - return res + if res[1:3] != (None, None): + if res[2]: + return res[1], getver(res[2]) + return res[1], None except IOError: log.error('cannot open %s', fpath) diff --git a/dh_python2 b/dh_python2 index 95e3467..c625ceb 100755 --- a/dh_python2 +++ b/dh_python2 @@ -42,8 +42,8 @@ import debpython.namespace as ns from debpython.pydist import validate as validate_pydist, \ PUBLIC_DIR_RE from debpython.tools import sitedir, relative_symlink, \ - shebang2pyver, so2pyver, \ - clean_egg_name, \ + fix_shebang, shebang2pyver, \ + so2pyver, clean_egg_name, \ pyinstall, pyremove from debpython.option import Option @@ -299,7 +299,7 @@ def share_2x(dir1, dir2, dc=None): ### PACKAGE DETAILS ############################################ -def scan(package, dname=None, clean_dbg_pkg=True): +def scan(package, dname=None, options=None): """Gather statistics about Python files in given package.""" r = {'requires.txt': set(), 'nsp.txt': set(), @@ -358,7 +358,7 @@ def scan(package, dname=None, clean_dbg_pkg=True): # handle some EGG related data (.egg-info dirs) for name in dirs: if name.endswith('.egg-info'): - if dbg_package and clean_dbg_pkg: + if dbg_package and options.clean_dbg_pkg: rmtree(join(root, name)) dirs.remove(name) continue @@ -404,18 +404,22 @@ def scan(package, dname=None, clean_dbg_pkg=True): os.remove(lpath) log.info('renaming %s to %s', dstfpath, fn) os.rename(dstfpath, fpath) - if dbg_package and clean_dbg_pkg and fext not in ('so', 'h'): + if dbg_package and options.clean_dbg_pkg and \ + fext not in ('so', 'h'): os.remove(join(root, fn)) continue elif private_dir: - if exists(join(root, fn)): - mode = os.stat(join(root, fn))[ST_MODE] + if exists(fpath): + mode = os.stat(fpath)[ST_MODE] if mode & S_IXUSR or mode & S_IXGRP or mode & S_IXOTH: - res = shebang2pyver(join(root, fn)) - if res: - r['private_dirs'].setdefault(private_dir, {})\ - .setdefault('shebangs', set()).add(res) + if (options.no_shebang_rewrite or \ + fix_shebang(fpath, options.shebang)) and \ + not options.ignore_shebangs: + res = shebang2pyver(fpath) + if res: + r['private_dirs'].setdefault(private_dir, {})\ + .setdefault('shebangs', set()).add(res) if public_dir or private_dir: if fext == 'so': @@ -452,11 +456,14 @@ def scan(package, dname=None, clean_dbg_pkg=True): continue # search for scripts in bin dirs if bin_dir: - res = shebang2pyver(fpath) - if res: - r['shebangs'].add(res) - - if dbg_package and clean_dbg_pkg: + if (options.no_shebang_rewrite or \ + fix_shebang(fpath, options.shebang)) and \ + not options.ignore_shebangs: + res = shebang2pyver(fpath) + if res: + r['shebangs'].add(res) + + if dbg_package and options.clean_dbg_pkg: # remove empty directories in -dbg packages proot = proot + '/usr/lib' for root, dirs, file_names in os.walk(proot, topdown=False): @@ -519,6 +526,8 @@ def main(): help='recreate __init__.py files for given namespaces at install time') parser.add_option('--clean-pycentral', action='store_true', default=False, help='generate maintainer script that will remove pycentral files') + parser.add_option('--shebang', + help='use given command as shebang in scripts') parser.add_option('--ignore-shebangs', action='store_true', default=False, help='do not translate shebangs into Debian dependencies') parser.add_option('--ignore-namespace', action='store_true', default=False, @@ -526,6 +535,8 @@ def main(): parser.add_option('--no-dbg-cleaning', action='store_false', dest='clean_dbg_pkg', default=True, help='do not remove files from debug packages') + parser.add_option('--no-shebang-rewrite', action='store_true', + default=False, help='do not rewrite shebangs') # ignore some debhelper options: parser.add_option('-O', help=SUPPRESS_HELP) @@ -593,7 +604,7 @@ def main(): if not pyremove(package, options.vrange): exit(5) fix_locations(package) - stats = scan(package, private_dir, options.clean_dbg_pkg) + stats = scan(package, private_dir, options) if not private_dir: share(package, stats, options) pyshared_dir = "debian/%s/usr/share/pyshared/" % package diff --git a/dh_python2.rst b/dh_python2.rst index 234f621..0b44a7d 100644 --- a/dh_python2.rst +++ b/dh_python2.rst @@ -112,6 +112,8 @@ OPTIONS --no-dbg-cleaning do not remove any files from debug packages +--no-shebang-rewrite do not rewrite shebangs + --skip-private don't check private directories -v, --verbose turn verbose mode on @@ -156,6 +158,8 @@ OPTIONS --clean-pycentral generate maintainer script that will remove byte code generated by python-central helper +--shebang=COMMAND use given command as shebang in scripts + --ignore-shebangs do not translate shebangs into Debian dependencies SEE ALSO diff --git a/tests/Makefile b/tests/Makefile index 8cade20..224dca2 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,7 +1,7 @@ #!/usr/bin/make -f # enable or disable tests here: -TESTS := test1 test2 test3 test4 test5 test6 +TESTS := test1 test2 test3 test4 test5 test6 test7 all: $(TESTS) diff --git a/tests/t7/Makefile b/tests/t7/Makefile new file mode 100644 index 0000000..a2b7fe8 --- /dev/null +++ b/tests/t7/Makefile @@ -0,0 +1,24 @@ +#!/usr/bin/make -f + +all: run check + + +run: clean + dpkg-buildpackage -b -us -uc + +check: + # python2.4 hardcoded via `dh_python2 -shebang ...python2.4` + grep -q '\-V 2.4 /usr/share/baz24' debian/foo/usr/share/python/runtime.d/foo.rtupdate + grep -q '/usr/share/baz24 \-V 2.4' debian/foo/DEBIAN/postinst + grep -q '#! /usr/bin/python2.4 -OO' debian/foo/usr/share/baz24/baz.py + # python2.6 hardcoded via shebang + grep -q '\-V 2.6 /usr/share/foo' debian/foo/usr/share/python/runtime.d/foo.rtupdate + grep -q '/usr/share/foo \-V 2.6' debian/foo/DEBIAN/postinst + # /env removed from shebang + grep -q '#! /usr/bin/python' debian/foo/usr/share/bar/bar.py + # /local removed from shebang + grep -q '#! /usr/bin/python' debian/foo/usr/share/foo/baz.py + grep -q '#! /usr/bin/python2.6' debian/foo/usr/share/foo/foo.py + +clean: + ./debian/rules clean diff --git a/tests/t7/bar.py b/tests/t7/bar.py new file mode 100755 index 0000000..42a88cc --- /dev/null +++ b/tests/t7/bar.py @@ -0,0 +1,2 @@ +#!/usr/bin/env python +"env in shebang" diff --git a/tests/t7/baz.py b/tests/t7/baz.py new file mode 100755 index 0000000..4dc3658 --- /dev/null +++ b/tests/t7/baz.py @@ -0,0 +1,2 @@ +#!/usr/local/bin/python +"/usr/local in shebang" diff --git a/tests/t7/debian/changelog b/tests/t7/debian/changelog new file mode 100644 index 0000000..c1ed13c --- /dev/null +++ b/tests/t7/debian/changelog @@ -0,0 +1,5 @@ +foo (1.0) unstable; urgency=low + + * Initial release + + -- Piotr Ożarowski <piotr@debian.org> Sun, 10 Jun 2012 14:09:45 +0200 diff --git a/tests/t7/debian/compat b/tests/t7/debian/compat new file mode 100644 index 0000000..45a4fb7 --- /dev/null +++ b/tests/t7/debian/compat @@ -0,0 +1 @@ +8 diff --git a/tests/t7/debian/control b/tests/t7/debian/control new file mode 100644 index 0000000..d14acc7 --- /dev/null +++ b/tests/t7/debian/control @@ -0,0 +1,13 @@ +Source: foo +Section: misc +Priority: optional +Maintainer: Piotr Ożarowski <piotr@debian.org> +Build-Depends: debhelper (>= 7.0.50~) +Build-Depends-Indep: python +Standards-Version: 3.9.3 + +Package: foo +Architecture: all +Depends: ${python:Depends}, ${misc:Depends} +Description: example 7 - shebangs + exemple package #7 - shebang related tests diff --git a/tests/t7/debian/copyright b/tests/t7/debian/copyright new file mode 100644 index 0000000..bf78fd0 --- /dev/null +++ b/tests/t7/debian/copyright @@ -0,0 +1,2 @@ +The Debian packaging is © 2012, Piotr Ożarowski <piotr@debian.org> and +is licensed under the MIT License. diff --git a/tests/t7/debian/install b/tests/t7/debian/install new file mode 100644 index 0000000..ff65342 --- /dev/null +++ b/tests/t7/debian/install @@ -0,0 +1,7 @@ +foo.py /usr/share/foo/ +baz.py /usr/share/foo/ +spam.py /usr/share/foo/ +bar.py /usr/share/bar/ +spam.py /usr/share/bar/ +baz.py /usr/share/baz24/ +spam.py /usr/share/baz24/ diff --git a/tests/t7/debian/rules b/tests/t7/debian/rules new file mode 100755 index 0000000..551e9c9 --- /dev/null +++ b/tests/t7/debian/rules @@ -0,0 +1,11 @@ +#!/usr/bin/make -f +%: + dh $@ --buildsystem=python_distutils + +override_dh_pysupport: + DH_VERBOSE=1 ../../dh_python2 + DH_VERBOSE=1 ../../dh_python2 /usr/share/bar + DH_VERBOSE=1 ../../dh_python2 /usr/share/baz24 --shebang '/usr/bin/python2.4 -OO' + +clean: + dh_clean diff --git a/tests/t7/debian/source/format b/tests/t7/debian/source/format new file mode 100644 index 0000000..89ae9db --- /dev/null +++ b/tests/t7/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/tests/t7/foo.py b/tests/t7/foo.py new file mode 100755 index 0000000..6ea2fec --- /dev/null +++ b/tests/t7/foo.py @@ -0,0 +1,2 @@ +#!/usr/local/bin/python2.6 +"/usr/local/bin/python2.6 hardcoded in shebang" diff --git a/tests/t7/setup.py b/tests/t7/setup.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/t7/spam.py b/tests/t7/spam.py new file mode 100644 index 0000000..c8b0fd9 --- /dev/null +++ b/tests/t7/spam.py @@ -0,0 +1 @@ +print('spam') -- GitLab