NOTE: This package is currently not under active development.
Convert PyPI packages to Arch Linux packages, inspired from pip2arch.
Handles packages of all sizes, from the simplest (pure Python, no dependencies) to the most complex (C-level dependencies, external C libraries, etc., e.g. most of the scientific Python stack, or GUI toolkits such as PyGObject and wxPython) [1].
[1] | ... with a bit of help. |
pypi2pkgbuild.py
depends on the Arch Linux packages namcap, pkgfile, and
python [2].
[2] | Officially, only the latest releases packaged by Arch Linux are
supported. In practice, the hard requirements that I am aware of are
pacman≥5.1 (which changed the behavior of makepkg --printsrcinfo ) and a
recent enough Python so that python -mvenv creates a virtual environment
with pip≥10 (which changed the default format of pip --list ) and
setuptools. |
The script can be installed with pip install [--user] .
, or can also be run
directly.
One can even run pypi2pkgbuild.py
on itself to create a proper Arch package
(pypi2pkgbuild.py git+https://github.com/anntzer/pypi2pkgbuild
).
A minimal test suite (checking that pypi2pkgbuild.py
can indeed package
itself) can by run with unittest (or pytest).
pypi2pkgbuild.py PYPINAME
creates a PKGBUILD for the latest version of the
given PyPI package and the current version of the Python interpreter (Python 3
only). Prereleases are considered if the --pre
flag is passed. Because
PyPI's dependency information is somewhat unreliable, it installs the package
in a venv to figure out the dependencies. Note that thanks to pip
's wheel
cache, the build is later reused; i.e. the procedure entails very little extra
work.
A -git
package can be built with pypi2pkgbuild.py git+https://...
.
The package is then built and verified with namcap
.
The goal is to make this tool as automated as possible: if all the information to build a package is (reasonably) accessible, this tool should be able to build it.
In order to provide additional information to makepkg
, edit
PKGBUILD_EXTRAS
(which can also be done with the --pkgbuild-extras
flag). This file is sourced at the end of PKGBUILD
. For ease of
patching, the build
, package
, and, where applicable, pkgver
functions are defined by forwarding to _build
, _package
, and
_pkgver
. A _check
function is also available, but not used (due to the
lack of standard testing CLI). Some useful examples of PKGBUILD_EXTRAS
are
listed in the pkgbuild-extras
directory.
- It is suggested to create an alias with standard options set, e.g.
alias pypi2pkgbuild.py='PKGEXT=.pkg.tar pypi2pkgbuild.py -g cython -b /tmp/pypi2pkgbuild/ -f'
By default, the
pkgrel
of (standard) packages is set to00
. This allows automatic upgrading into official packages (and AUR ones, if an AUR helper is used) whenever the repositories are updated. Additionally, the use of00
rather than0
serves as a (weak) marker that the package was automatically generated by this tool. In order to prevent such an upgrade, one can use the--pkgrel
flag to setpkgrel
to, e.g.,99
.If one wishes to completely bypass AUR Python packages while maintaining the use of an AUR helper for non-Python packages, one can define a shell function that excludes
pypi2pkgbuild.py
-generated packages that do not appear in the official repositories, e.g., forpacaur
:pacaur() { if [[ "$1" = "-Syu" ]]; then # Update, in case some packages moved in or out of the official repos. sudo pacman -Sy # Upgrade everything except python packages with pkgver=00 or 99. PKGEXT=.pkg.tar command pacaur -Su --ignore \ "$(pacman -Qm | grep '^python-.*-\(00\|99\)$' | cut -d' ' -f1 | paste -sd,)" else command pacaur "$@" fi }
This function will not bypass Python packages explicitly installed from the AUR, as the user may have done so to bypass some incorrect packaging by
pypi2pkgbuild.py
. It is recommended to use the-i
flag to calls topypi2pkgbuild.py
(e.g. in an alias) to exclude packages that are mishandled bypypi2pkgbuild.py
(see mispackaged packages). The-i
flag can be passed multiple times; passing an empty argument to it will clear the ignore list defined so far.In order to package a locally available git repository, use
$ pypi2pkgbuild.py git+file://$absolute_path_to_repo # (e.g. file:///home/...)
In order to package a locally available sdist or wheel, use
$ pypi2pkgbuild.py file://$absolute_path_to_file # (e.g. file:///home/...)
Note that in both cases absolute paths are necessary.
Building packages from local repos or wheels needs to be done in topological order of the dependencies (so that
pypi2pkgbuild.py
can find that the dependencies are actually present), or by passing the-d
flag ("do not build dependencies"); if it is used, the Arch package may not use the correct dependency names (if they are not of the formpython-pep503-normalized-name
).By default,
pypi2pkgbuild.py
ignorespip
config files such as~/.config/pip/pip.conf
. An explicitly setPIP_CONFIG_FILE
will be respected, but may causepypi2pkgbuild.py
to fail as somepip
calls will be unexpectedly modified.Likewise, user-site packages are ignored unless
PYTHONNOUSERSITE
is explicitly set to an empty value.
pypi2pkgbuild.py
attempts to guess whether Cython
and SWIG
are
build-time dependencies by checking for the presence of .pyx
and .i
files, respectively. If this is not desired, set the --guess-makedepends
option accordingly.
pypi2pkgbuild.py
guesses whether numpy
is a build-time dependency by
attempting a build without numpy
, then, in case of failure, a build with
numpy
.
Additional Python build-time dependencies (i.e., setup_requires
) can be
specified (as PyPI names) using the --setup-requires
flag, or just as
normal entries using --pkgbuild-extras
(they will be installed into the
build virtualenv).
Additional non-Python build-time dependencies can be set as makedepends
using --pkgbuild-extras
; they will be installed before
pypi2pkgbuild.py
attempts to build a wheel for the package.
Some Arch packages (e.g. ipython
) include a number of smaller PyPI
packages.
Because it is not possible to assign a meaningful version automatically,
pypi2pkgbuild.py
instead creates an independent Arch package for each of
the PyPI packages (with two dashes in the name, to prevent name conflicts) and
a master package that depends on all of them. The pkgrel
of the master
package is set to $official_pkgrel.99
, so that the package appears more
recent than the current official version but older than any future official
version. All these packages conflict
with all versions of the official
package (except the newly created package), so upgrading should work fine when
the official package is actually updated.
However, dependencies are still expressed using the master package (to avoid breakage on upgrade into an official package), so internal dependencies will appear be circular.
All the packages are placed in a subfolder named meta:$pkgname
, so one can
easily install everything by cd
'ing there and running
$ sudo pacman -U --asdeps **/*.xz
$ sudo pacman -D --asexplicit $pkgname/$pkgname.tar.xz
When the Python minor version (x
in 3.x
) is upgraded, it is necessary
to regenerate all self-built packages. This can be done e.g. with
$ pypi2pkgbuild.py $(
ls /usr/lib/python3.$oldver/site-packages |
grep -Po '.*(?=-.*.dist-info)'
)
Other similar tools include pip2arch, pip2pkgbuild, and fpm. To the best of my knowledge, the features below are unique to PyPI2PKGBUILD; please let me know if this is incorrect.
- Supports wheels (the default is to prefer
any
-platform wheels, thensdist
s, thenmanylinux1
wheels, but this can be changed using--pkgtypes
). - Resolves Python dependencies via installation in a temporary virtualenv, and also creates PKGBUILDs for those that are not available as official packages.
- Resolves binary dependencies via
namcap
and adds them to thedepends
array if they are installed (thus, it is suggested to first install them as--asdeps
and then let the generated PKGBUILD pick them up as dependencies). Note that some packages are distributed with a copy of the required libraries; in this case,pypi2pkgbuild.py
’s behavior will depend on whether the package defaults to using the system-wide library or its own copy. - Automatically tries to fetch a missing license file from Github, if applicable.
- Automatically builds the package (with options given in
--makepkg=...
) and runnamcap
. - Automatically builds all outdated dependencies via
-u
.