APKBUILD examples:Python

From Alpine Linux
Revision as of 14:58, 12 April 2019 by Ncopa (talk | contribs) (→‎Multiversion package without executables: do the install in package() and clean up py2 deps vs py3 deps)

A lot of Python packages use the setuptools or distutils framework. This mean that the build() and the package() section looks a bit different compared to an application which uses make.


Considerations

Python 2 vs. Python 3

Python 3 should be the default option. If the project support both Python 2 and Python 3, then the Alpine package may provide both variants as py2- and py3- subpackages. However, add py2 variant only when you really need it. Python 2 is legacy and will be deprecated soon. You may omit py2 variant, but never omit py3 variant if it's supported by the project.

pkgname

Package name for a Python library must be prefixed with py-, py2-, or py3-.

py-
Use if the project supports both Python 2 and Python 3, even if it does not support Python 3 yet, but may in future.
py2-
TODO
py3-
Use if the project does not support Python 2.

There’s no exact rule if the prefix should be used for tools and applications written in Python, it varies.

arch

noarch
Use for pure Python packages (i.e. without compiled code). Also add python2, or python3 to depends=.
all (and others)
Use for packages with native extensions (i.e. with compiled code). Do not add python2/python3 to depends= (it's auto-detected via dynamic linking to python library).

source

Basically all Python are published in PyPI (the Python Package Index). If the package has a source tarball available in PyPI (that’s true for most packages), you should reference it in source= as:

https://files.pythonhosted.org/packages/source/${_pyname%${_pyname#?}}/$_pyname/$_pyname-$pkgver.tar.gz

where _pyname is a real name of the Python package.


Examples

Comments marked with ## should not be copied into final APKBUILD!


Package for single Python version

If the project supports only Python 3 (or 2), then the APKBUILD is very simple:

pkgname="py3-foo"
_pyname="foo"
...
depends="python3"
makedepends="python3-dev"
...

build() {
	cd "$builddir"
	python3 setup.py build
}

check() {
	cd "$builddir"
	python3 setup.py test
}

package() {
	cd "$builddir"
	python3 setup.py install --prefix=/usr --root="$pkgdir"
}


Multiversion package without executables

Use this variant if the package does not install any files outside of /usr/lib/pythonX.Y/site-packages. If the build modifies source code, e.g. using 2to3, read the last section!

pkgname="py-foo"
_pyname="PyFoo"
...
_py2deps="python2 py2-bar py2-baz"
_py3deps="python3 py3-bar py3-baz"
makedepends="python2-dev python3-dev"
subpackages="py2-${pkgname#py-}:_py2 py3-${pkgname#py-}:_py3"
...

build() {
	cd "$builddir"
	python2 setup.py build
	python3 setup.py build
}

check() {
	cd "$builddir"
	python2 setup.py test
	python3 setup.py test
}

package() {
	cd "$builddir"
	python2 setup.py install --prefix=/usr --root="$pkgdir"
	python3 setup.py install --prefix=/usr --root="$pkgdir"
}

_py() {
	local python="$1"
	pkgdesc="$pkgdesc (for $python)"
	install_if="$pkgname=$pkgver-r$pkgrel $python"

	mkdir -p "$subpkgdir"/usr/lib/
	mv "$pkgdir"/usr/lib/$python* "$subpkgdir"
}

_py2() {
	depends="$_py2deps"
	_py python2
}

_py3() {
	depends="$_py3deps"
	_py python3
}

Multiversion package with executables

Use this variant if the packages installs some executables into /usr/bin. If the build modifies source code, e.g. using 2to3, read the last section!

Note: This is just mere workaround that will not be needed in future once we add needed features to apk.

pkgname="py-foo"
_pyname="PyFoo"
...
depends="py-bar py-baz"
makedepends="python2-dev python3-dev py-setuptools"
subpackages="py2-${pkgname#py-}:_py2 py3-${pkgname#py-}:_py3"
...

build() {
	cd "$builddir"
	python2 setup.py build
	python3 setup.py build
}

package() {
	mkdir -p "$pkgdir"/usr/bin

	# Create unsuffixed symlinks for executables from py3 subpackage.
	local name; for name in foo bar baz; do
		ln -s $name-3 "$pkgdir"/usr/bin/$name
	done
}

_py2() {
	replaces="$pkgname"             ## remove if you're writing a new aport (and always remove this comment!)
	depends="${depends//py-/py2-}"  ## remove if there are no common Python dependencies, i.e. above depends= is empty/undefined (and always remove this comment!)
	_py python2
}

_py3() {
	depends="${depends//py-/py3-}"  ## remove if there are no common Python dependencies, i.e. above depends= is empty/undefined (and always remove this comment!)
	_py python3
}

_py() {
	local python="$1"
	local pyver="${1:6:1}"
	pkgdesc="$pkgdesc (for $python)"
	depends="$depends $python"  ## remove if arch isn't noarch (and always remove this comment!)
	install_if="$pkgname=$pkgver-r$pkgrel $python"

	cd "$builddir"
	$python setup.py install --prefix=/usr --root="$subpkgdir"

	# Add version suffix to executable files.
	local path; for path in "$subpkgdir"/usr/bin/*; do
		mv "$path" "$path-$pyver"
	done
}


Multiversion package for project using 2to3

Some Python projects use 2to3 (or similar tools) during build phase to make the code-base compatible with Python 3. Since it modifies sources during build, you cannot run setup.py build with python2 and python3 one after another with the same source files. You must duplicate the buildir and run them separately.

prepare() {
	default_prepare

	# Soure files are modified during build with 2to3 tool, so we cannot
	# build it for both Python versions in the same location.
	local python; for python in python2 python3; do
		cp -r "$builddir" "$builddir-$python"
	done
}

build() {
	local python; for python in python2 python3; do
		cd "$builddir"-$python
		$python setup.py build
	done
}

_py() {
	...

	cd "$builddir"-$python
	$python setup.py install --prefix=/usr --root="$subpkgdir"
}

Generating documentation

To generate documentation:

makedepends="py-sphinx"
subpackages="$pkgname-doc"

package() {
	...
        cd "$builddir"/doc
        install -d "$pkgdir"/usr/share/html/$pkgname "$pkgdir"/usr/share/man/man1
        PYTHONPATH="$builddir/src:$PYTHONPATH" make html man
        install -t "$pkgdir"/usr/share/man/man1 build/man/*
        mv build/html/* "$pkgdir"/usr/share/html/$pkgname
}