summaryrefslogtreecommitdiffstats
path: root/dh_python3
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-xdh_python3292
-rw-r--r--dh_python3.rst238
2 files changed, 530 insertions, 0 deletions
diff --git a/dh_python3 b/dh_python3
new file mode 100755
index 0000000..561ade7
--- /dev/null
+++ b/dh_python3
@@ -0,0 +1,292 @@
+#! /usr/bin/python3
+# vim: et ts=4 sw=4
+
+# Copyright © 2010-2013 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
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+import logging
+import os
+import sys
+from argparse import ArgumentParser, SUPPRESS
+from os.path import exists, join
+from shutil import copy as fcopy
+from dhpython.debhelper import DebHelper
+from dhpython.depends import Dependencies
+from dhpython.interpreter import Interpreter, EXTFILE_RE
+from dhpython.version import supported, default, Version, VersionRange
+from dhpython.pydist import validate as validate_pydist
+from dhpython.fs import fix_locations, Scan
+from dhpython.option import compiled_regex
+from dhpython.tools import pyinstall, pyremove
+
+# initialize script
+logging.basicConfig(format='%(levelname).1s: dh_python3 '
+ '%(module)s:%(lineno)d: %(message)s')
+log = logging.getLogger('dhpython')
+os.umask(0o22)
+DEFAULT = default('cpython3')
+SUPPORTED = supported('cpython3')
+
+
+class Scanner(Scan):
+ def handle_ext(self, fpath):
+ path, fname = fpath.rsplit('/', 1)
+ tagver = EXTFILE_RE.search(fname)
+ if tagver is None:
+ # yeah, python3.1 is not covered, but we don't want to
+ # mess with non-Python libraries, don't we?
+ return
+ tagver = tagver.groupdict()['ver']
+ if tagver is None:
+ return
+ tagver = Version("%s.%s" % (tagver[0], tagver[1:]))
+ return tagver
+
+
+def main():
+ parser = ArgumentParser()
+ parser.add_argument(
+ '--version', action='version', version='%(prog)s DEVELV')
+ parser.add_argument(
+ '--no-guessing-deps', action='store_false', dest='guess_deps',
+ help='disable guessing dependencies')
+ parser.add_argument(
+ '--skip-private', action='store_true',
+ help="don't check private directories")
+ parser.add_argument(
+ '-v', '--verbose', action='store_true',
+ default=os.environ.get('DH_VERBOSE') == '1',
+ help='turn verbose mode on')
+ # arch=False->arch:all only, arch=True->arch:any only, None->all of them
+ parser.add_argument(
+ '-i', '--indep', action='store_false', dest='arch', default=None,
+ help='act on architecture independent packages')
+ parser.add_argument(
+ '-a', '-s', '--arch', action='store_true', dest='arch',
+ help='act on architecture dependent packages')
+ parser.add_argument(
+ '-q', '--quiet', action='store_false', dest='verbose', help='be quiet')
+ parser.add_argument(
+ '-p', '--package', action='append', metavar='PACKAGE',
+ help='act on the package named PACKAGE')
+ parser.add_argument(
+ '-N', '--no-package', action='append', metavar='PACKAGE',
+ help='do not act on the specified package')
+ parser.add_argument(
+ '--remaining-packages', action='store_true',
+ help='Do not act on the packages which have already been acted on by '
+ 'this debhelper command earlier')
+ parser.add_argument(
+ '--compile-all', action='store_true',
+ help='compile all files from given private directory in postinst, not '
+ 'just the ones provided by the package')
+ parser.add_argument(
+ '-V', type=VersionRange, dest='vrange', metavar='[X.Y][-][A.B]',
+ help='specify list of supported Python versions. See py3compile(1) for '
+ 'examples')
+ parser.add_argument(
+ '-X', '--exclude', action='append', dest='regexpr', type=compiled_regex,
+ metavar='REGEXPR',
+ help='exclude items that match given REGEXPR. You may use this option '
+ 'multiple times to build up a list of things to exclude.')
+ parser.add_argument(
+ '--accept-upstream-versions', action='store_true',
+ help='accept upstream versions while translating Python dependencies '
+ 'into Debian ones')
+ parser.add_argument(
+ '--depends', action='append', metavar='REQ',
+ help='translate given requirements into Debian dependencies and add '
+ 'them to ${python3:Depends}. Use it for missing items in '
+ 'requires.txt.')
+ parser.add_argument(
+ '--depends-section', action='append', metavar='SECTION',
+ help='translate requirements from given section into Debian '
+ 'dependencies and add them to ${python3:Depends}')
+ parser.add_argument(
+ '--recommends', action='append', metavar='REQ',
+ help='translate given requirements into Debian dependencies and add '
+ 'them to ${python3:Recommends}')
+ parser.add_argument(
+ '--recommends-section', action='append', metavar='SECTION',
+ help='translate requirements from given section into Debian '
+ 'dependencies and add them to ${python3:Recommends}')
+ parser.add_argument(
+ '--suggests', action='append', metavar='REQ',
+ help='translate given requirements into Debian dependencies and add '
+ 'them to ${python3:Suggests}')
+ parser.add_argument(
+ '--suggests-section', action='append', metavar='SECTION',
+ help='translate requirements from given section into Debian '
+ 'dependencies and add them to ${python3:Suggests}')
+ parser.add_argument(
+ '--requires', action='append', metavar='FILE',
+ help='translate requirements from given file into Debian dependencies '
+ 'and add them to ${python3:Depends}')
+ parser.add_argument(
+ '--shebang', metavar='COMMAND',
+ help='use given command as shebang in scripts')
+ parser.add_argument(
+ '--ignore-shebangs', action='store_true',
+ help='do not translate shebangs into Debian dependencies')
+ parser.add_argument(
+ '--no-dbg-cleaning', action='store_false', dest='clean_dbg_pkg',
+ help='do not remove files from debug packages')
+ parser.add_argument(
+ '--no-ext-rename', action='store_true',
+ help='do not add magic tags nor multiarch tuples to extension file '
+ 'names)')
+ parser.add_argument(
+ '--no-shebang-rewrite', action='store_true',
+ help='do not rewrite shebangs')
+ parser.add_argument('private_dir', nargs='?',
+ help='Private directory containing Python modules (optional)')
+ # debhelper options:
+ parser.add_argument('-O', action='append', help=SUPPRESS)
+
+ options = parser.parse_args(os.environ.get('DH_OPTIONS', '').split()
+ + sys.argv[1:])
+ if options.O:
+ parser.parse_known_args(options.O, options)
+
+ private_dir = options.private_dir
+ if private_dir:
+ if not private_dir.startswith('/'):
+ # handle usr/share/foo dirs (without leading slash)
+ private_dir = '/' + private_dir
+ # TODO: support more than one private dir at the same time (see :meth:scan)
+ if options.skip_private:
+ private_dir = False
+
+ if options.verbose:
+ log.setLevel(logging.DEBUG)
+ log.debug('version: DEVELV')
+ log.debug('argv: %s', sys.argv)
+ log.debug('options: %s', options)
+ log.debug('supported Python versions: %s (default=%s)',
+ ','.join(str(v) for v in SUPPORTED), DEFAULT)
+ else:
+ log.setLevel(logging.INFO)
+
+ options.write_log = False
+ if os.environ.get('DH_INTERNAL_OVERRIDE', ''):
+ options.write_log = True
+
+ try:
+ dh = DebHelper(options, impl='cpython3')
+ except Exception as e:
+ log.error('cannot initialize DebHelper: %s', e)
+ exit(2)
+ if not dh.packages:
+ log.error('no package to act on (python3-foo or one with ${python3:Depends} in Depends)')
+ # exit(7)
+ if not options.vrange and dh.python_version:
+ options.vrange = VersionRange(dh.python_version)
+
+ interpreter = Interpreter('python3')
+ for package, pdetails in dh.packages.items():
+ log.debug('processing package %s...', package)
+ interpreter.debug = package.endswith('-dbg')
+
+ if not private_dir:
+ try:
+ pyinstall(interpreter, package, options.vrange)
+ except Exception as err:
+ log.error("%s.pyinstall: %s", package, err)
+ exit(4)
+ try:
+ pyremove(interpreter, package, options.vrange)
+ except Exception as err:
+ log.error("%s.pyremove: %s", package, err)
+ exit(5)
+ fix_locations(package, interpreter, SUPPORTED, options)
+ stats = Scanner(interpreter, package, private_dir, options).result
+
+ dependencies = Dependencies(package, 'cpython3', dh.build_depends)
+ dependencies.parse(stats, options)
+
+ pyclean_added = False # invoke pyclean only once in maintainer script
+ if stats['compile']:
+ args = ''
+ if options.vrange:
+ args += "-V %s" % options.vrange
+ dh.autoscript(package, 'postinst', 'postinst-py3compile', args)
+ dh.autoscript(package, 'prerm', 'prerm-py3clean', '')
+ pyclean_added = True
+ for pdir, details in sorted(stats['private_dirs'].items()):
+ if not details.get('compile'):
+ continue
+ if not pyclean_added:
+ dh.autoscript(package, 'prerm', 'prerm-py3clean', '')
+ pyclean_added = True
+
+ args = pdir
+
+ ext_for = details.get('ext_vers')
+ ext_no_version = details.get('ext_no_version')
+ if ext_for is None and not ext_no_version: # no extension
+ shebang_versions = list(i.version for i in details.get('shebangs', [])
+ if i.version and i.version.minor)
+ if not options.ignore_shebangs and len(shebang_versions) == 1:
+ # only one version from shebang
+ args += " -V %s" % shebang_versions[0]
+ elif options.vrange and options.vrange != (None, None):
+ args += " -V %s" % options.vrange
+ elif ext_no_version:
+ # at least one extension's version not detected
+ if options.vrange and '-' not in str(options.vrange):
+ ver = str(options.vrange)
+ else: # try shebang or default Python version
+ ver = (list(i.version for i in details.get('shebangs', [])
+ if i.version and i.version.minor) or [None])[0] or DEFAULT
+ dependencies.depend("python%s" % ver)
+ args += " -V %s" % ver
+ else:
+ extensions = sorted(ext_for)
+ vr = VersionRange(minver=extensions[0], maxver=extensions[-1])
+ args += " -V %s" % vr
+
+ for regex in options.regexpr or []:
+ args += " -X '%s'" % regex.pattern.replace("'", r"'\''")
+
+ dh.autoscript(package, 'postinst', 'postinst-py3compile', args)
+
+ dependencies.export_to(dh)
+
+ pydist_file = join('debian', "%s.pydist" % package)
+ if exists(pydist_file):
+ if not validate_pydist(pydist_file):
+ log.warning("%s.pydist file is invalid", package)
+ else:
+ dstdir = join('debian', package, 'usr/share/python3/dist/')
+ if not exists(dstdir):
+ os.makedirs(dstdir)
+ fcopy(pydist_file, join(dstdir, package))
+ bcep_file = join('debian', "%s.bcep" % package)
+ if exists(bcep_file):
+ dstdir = join('debian', package, 'usr/share/python3/bcep/')
+ if not exists(dstdir):
+ os.makedirs(dstdir)
+ fcopy(bcep_file, join(dstdir, package))
+
+ dh.save()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/dh_python3.rst b/dh_python3.rst
new file mode 100644
index 0000000..1c69634
--- /dev/null
+++ b/dh_python3.rst
@@ -0,0 +1,238 @@
+============
+ dh_python3
+============
+
+-----------------------------------------------------------------------------------
+calculates Python dependencies, adds maintainer scripts to byte compile files, etc.
+-----------------------------------------------------------------------------------
+
+:Manual section: 1
+:Author: Piotr Ożarowski, 2012-2013
+
+SYNOPSIS
+========
+ dh_python3 -p PACKAGE [-V [X.Y][-][A.B]] DIR [-X REGEXPR]
+
+DESCRIPTION
+===========
+
+QUICK GUIDE FOR MAINTAINERS
+---------------------------
+
+ * build depend on dh-python
+ * build-depend on python3 (Python application) or python3-all (Python module)
+ or python3-all-dev (Python extension),
+ * if necessary, describe supported Python 3 versions via X-Python3-Version field
+ in debian/control,
+ * build module/application using its standard build system (pybuild wrapper
+ recommended, see pybuild.1 for more details), remember to build extensions
+ for all supported Python 3 versions (loop over ``py3versions -vr``),
+ * install files to the *standard* locations, add `--install-layout=deb` to
+ setup.py's install command if your package is using distutils,
+ * add `python3` to dh's --with option, or:
+ * `include /usr/share/cdbs/1/class/python-distutils.mk` in debian/rules and
+ depend on `cdbs (>= 0.4.90)`, or:
+ * call ``dh_python3`` in the `binary-*` target,
+ * add `${python3:Depends}` to Depends
+
+NOTES
+-----
+
+dependencies
+~~~~~~~~~~~~
+dh_python3 tries to translate Python dependencies from `Requires-Dist`
+entries in `dist-info` or `requires.txt` contents in `egg-info` to
+Debian dependencies.
+In many cases, this works without any additional configuration because
+dh_python3 comes with a build-in mapping of Python module names to
+Debian packages that is periodically regenerated from the Debian
+archive. By default, the version information in the Python dependencies is
+discarded. If you want dh_python3 to generate more strict dependencies (e.g. to
+avoid ABI problems), or if the automatic mapping does not work correctly for
+your package, you have to provide dh_python3 with additional rules for the
+translation of Python module to Debian package dependencies.
+
+For a package *python3-foo* that depends on a package *python3-bar*, there are
+two files that may provide such rules:
+
+#. If the *python3-foo* source package ships with a
+ `debian/py3dist-overrides` file, this file is used by dh_python3
+ during the build of *python3-foo*.
+
+#. If the *python3-bar* source package ships with a
+ `debian/python3-bar.pydist` file (and uses dh_python3), this file
+ will be included in the binary package as
+ `/usr/share/dh-python/dist/cpython3/python3-bar`. During the build
+ of *python3-foo*, dh_python3 will then find and use the file.
+
+Both files have the same format described in
+`/usr/share/doc/dh-python/README.PyDist`. If all you want is to generate
+versioned dependencies (and assuming that the *python3-bar* package provides
+the *pybar* Python module), in most cases it will be sufficient to put the line
+``pybar python3-bar; PEP386`` into either of the above files.
+
+private dirs
+~~~~~~~~~~~~
+`/usr/share/foo`, `/usr/share/games/foo`, `/usr/lib/foo` and
+`/usr/lib/games/foo` private directories are scanned for Python files
+by default (where `foo` is binary package name). If your package ships
+Python files in some other directory, add another dh_python3 call in
+debian/rules with directory name as an argument - you can use different set of
+options in this call. If you need to change options (f.e. a list of supported
+Python 3 versions) for a private directory that is checked by default, invoke
+dh_python3 with --skip-private option and add another call with a path to this
+directory and new options.
+
+debug packages
+~~~~~~~~~~~~~~
+In binary packages which name ends with `-dbg`, all files in
+`/usr/lib/python3/dist-packages/` directory
+that have extensions different than `so` or `h` are removed by default.
+Use --no-dbg-cleaning option to disable this feature.
+
+pyinstall files
+~~~~~~~~~~~~~~~
+Files listed in debian/pkg.pyinstall file will be installed as public modules
+(i.e. into .../dist-packages/ directory) for all requested Python versions.
+
+Syntax: ``path/to/file [NAMESPACE] [VERSION_RANGE]``
+
+debian directory is automatically removed from the path, so you can place your
+files in debian/ directory and install them from this location (if you want to
+install them in "debian" namespace, set NAMESPACE to debian). If NAMESPACE is
+set, all listed files will be installed in .../dist-packages/NAMESPACE/
+directory.
+
+Examples:
+ * ``foo.py`` installs .../dist-packages/foo.py for all supported Python versions
+ * ``foo/bar.py 3.3-`` installs .../dist-packages/foo/bar.py for versions >= 3.3
+ * ``foo/bar.py spam`` installs .../dist-packages/spam/bar.py
+ * ``debian/*.py spam.egg 3.2`` installs .../python3.2/dist-packages/spam/egg/\*.py
+ files
+
+pyremove files
+~~~~~~~~~~~~~~
+If you want to remove some public modules (i.e. files in .../dist-packages/
+directory) installed by build system (from all supported Python versions or
+only from a subset of these versions), add them to debian/pkg.pyremove file.
+
+Examples:
+ * ``*.pth`` removes .pth files from .../dist-packages/
+ * ``bar/baz.py 3.2`` removes .../python3.2/dist-packages/bar/baz.py
+
+bcep files
+~~~~~~~~~~
+Byte-compilation exception patterns can be described in these files. Use it if
+you want py3compile to skip specific files. This is the only way to skip .py
+files in …/dist-packages/ directory (as `--exclude` passed to py3compile in
+postinst is not used in rtupdate scripts and thus this option cannot be used
+for non-private modules).
+
+``re|-3.6|/usr/lib/python3/dist-packages/jinja2|.*/async(foo|bar).py``
+will skip byte-compilation of `asyncfoo.py` and `asyncbar.py` in
+`/usr/lib/python3/dist-packages/jinja2/` directory for each interpreter that
+doesn't support `async` keyword (introduced in Python 3.6).
+
+If you want to skip byte-compilation in a subdirectory for all interpreters, use:
+``dir|-4.0|/usr/lib/python3/dist-packages/foo/tests/``.
+VERSION_RANGE (`-4.0` in the example) is described in `README.PyDist` file.
+
+`debian/python3-foo.bcep` file from source package will be included in the
+binary package as `/usr/share/python3/bcep/python3-foo.bcep`
+
+overriding supported / default Python versions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+If you want to override system's list of supported Python versions or the
+default one (f.e. to build a package that includes symlinks for older version
+of Python or compile .py files only for given interpreter version), you can do
+that via `DEBPYTHON3_SUPPORTED` and/or `DEBPYTHON3_DEFAULT` env. variables.
+
+Example: ``3.2,3.3`` limits the list of supported Python versions to Python 3.2
+and Python 3.3.
+
+
+OPTIONS
+=======
+--version show program's version number and exit
+
+-h, --help show help message and exit
+
+--no-guessing-deps disable guessing dependencies
+
+--no-dbg-cleaning do not remove any files from debug packages
+
+--no-ext-rename do not add magic tags nor multiarch tuples to extension file names
+
+--no-shebang-rewrite do not rewrite shebangs
+
+--skip-private don't check private directories
+
+-v, --verbose turn verbose mode on
+
+-i, --indep act on architecture independent packages
+
+-a, --arch act on architecture dependent packages
+
+-q, --quiet be quiet
+
+-p PACKAGE, --package=PACKAGE act on the package named PACKAGE
+
+-N NO_PACKAGE, --no-package=NO_PACKAGE do not act on the specified package
+
+--remaining-packages Do not act on the packages which have already
+ been acted on by this command earlier in the same override.
+
+-V VERSION_RANGE specify list of supported Python 3 versions. See
+ py3compile(1) for examples
+
+-X REGEXPR, --exclude=REGEXPR exclude items that match given REGEXPR. You may
+ use this option multiple times to build up a list of things to exclude from
+ byte-compilation in private dirs. See also `bcep files`.
+
+--compile-all compile all files from given private directory in postinst/rtupdate
+ not just the ones provided by the package (i.e. do not pass the --package
+ parameter to py3compile/py3clean)
+
+--accept-upstream-versions accept upstream versions while translating
+ Python dependencies into Debian ones
+
+--depends=DEPENDS translate given requirements into Debian dependencies
+ and add them to ${python3:Depends}. Use it for missing items in
+ `requires.txt` / `Requires-Dist`.
+
+--depends-section=SECTION translate requirements from given extra
+ sections of `requres.txt` / `Requires-Dist` into Debian dependencies
+ and add them to ${python3:Depends}. May be repeated for multiple
+ sections.
+
+--recommends=RECOMMENDS translate given requirements into Debian dependencies
+ and add them to ${python3:Recommends}
+
+--recommends-section=SECTION translate requirements from given extra
+ sections of `requires.txt` / `Requires-Dist` into Debian dependencies
+ and add them to ${python3:Recommends}. May be repeated for multiple
+ sections.
+
+--suggests=SUGGESTS translate given requirements into Debian dependencies
+ and add them to ${python3:Suggests}
+
+--suggests-section=SECTION translate requirements from given extra
+ sections of `requires.txt` / `Requires-Dist` into Debian dependencies
+ and add them to ${python3:Suggests}. May be repeated for multiple
+ sections.
+
+--requires=FILENAME translate requirements from given file(s) into Debian
+ dependencies and add them to ${python3:Depends}
+
+--shebang=COMMAND use given command as shebang in scripts
+
+--ignore-shebangs do not translate shebangs into Debian dependencies
+
+SEE ALSO
+========
+* /usr/share/doc/python3/python-policy.txt.gz
+* /usr/share/doc/dh-python/README.PyDist
+* pybuild(1)
+* py3compile(1), py3clean(1)
+* pycompile(1), pyclean(1)
+* http://deb.li/dhp3 - most recent version of this document