Packaging rofimoji

rofimoji is a character picker for rofi. It is written in Python and it’s compatible with PEP 517.

If you want to follow along, run this:

rm -r srcpkgs/rofimoji

Gathering info

Repository used for gathering the info: https://github.com/fdw/rofimoji

Python’s packaging systems

Python is known for its complicated build systems. Explaining them is beyond the scope of this tutorial (and beyond the scope of my comprehension to be honest).

But thankfully xbps-src’s build systems do most of the job. It provides the following build styles: python2-module, python3-module and python3-pep517 The -module styles use setup.py which is kinda deprecated (I recommend you read this article for more info) and the python3-pep517 (as the name implies) uses PEP 517.

rofimoji uses python3-pep517.

Creating the template

xnew rofimoji
# Template file for 'rofimoji'
pkgname=rofimoji
version=
revision=1
#archs="i686 x86_64"
#build_wrksrc=
build_style=gnu-configure
#configure_args=""
#make_build_args=""
#make_install_args=""
#conf_files=""
#make_dirs="/var/log/dir 0755 root root"
hostmakedepends=""
makedepends=""
depends=""
short_desc=""
maintainer="meator <meator.dev@gmail.com>"
license="GPL-3.0-or-later"
homepage=""
#changelog=""
distfiles=""
checksum=badbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadb

Let’s fill it out:

# Template file for 'rofimoji'
pkgname=rofimoji
version=6.2.0
revision=1
build_style=python3-pep517
hostmakedepends=""
makedepends=""
depends=""
short_desc="Emoji, unicode and general character picker for rofi and rofi-likes"
maintainer="meator <meator.dev@gmail.com>"
license="MIT"
homepage="https://github.com/fdw/rofimoji"
changelog="https://raw.githubusercontent.com/fdw/rofimoji/main/CHANGELOG.md"
distfiles="https://github.com/fdw/rofimoji/archive/refs/tags/6.2.0.tar.gz"
checksum=badbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadb
xgensum -i rofimoji
# Template file for 'rofimoji'
pkgname=rofimoji
version=6.2.0
revision=1
build_style=python3-pep517
hostmakedepends=""
makedepends=""
depends=""
short_desc="Emoji, unicode and general character picker for rofi and rofi-likes"
maintainer="meator <meator.dev@gmail.com>"
license="MIT"
homepage="https://github.com/fdw/rofimoji"
changelog="https://raw.githubusercontent.com/fdw/rofimoji/main/CHANGELOG.md"
distfiles="https://github.com/fdw/rofimoji/archive/refs/tags/6.2.0.tar.gz"
checksum=21da8e6879ac16d774f3ce6dfcd1783cec891ad172617eead2c10597f477d9a9

Let’s do some housekeeping for good measure:

./xbps-src clean
git pull --rebase upstream master

Now, we can build:

> ./xbps-src pkg rofimoji
=> xbps-src: updating repositories for host (x86_64)...
[*] Updating repository `https://repo-default.voidlinux.org/current/bootstrap/x86_64-repodata' ...
[*] Updating repository `https://repo-default.voidlinux.org/current/x86_64-repodata' ...
[*] Updating repository `https://repo-default.voidlinux.org/current/nonfree/x86_64-repodata' ...
[*] Updating repository `https://repo-default.voidlinux.org/current/debug/x86_64-repodata' ...
[*] Updating repository `https://repo-default.voidlinux.org/current/multilib/bootstrap/x86_64-repodata' ...
[*] Updating repository `https://repo-default.voidlinux.org/current/multilib/x86_64-repodata' ...
[*] Updating repository `https://repo-default.voidlinux.org/current/multilib/nonfree/x86_64-repodata' ...
=> xbps-src: updating software in / masterdir...
=> xbps-src: cleaning up / masterdir...
=> rofimoji-6.2.0_1: removing autodeps, please wait...
=> rofimoji-6.2.0_1: building with [python3-pep517] [python3] for x86_64...
   [host] python3-build-1.0.3_2: found (https://repo-default.voidlinux.org/current)
   [host] python3-installer-0.7.0_2: found (https://repo-default.voidlinux.org/current)
=> rofimoji-6.2.0_1: installing host dependencies: python3-build-1.0.3_2 python3-installer-0.7.0_2 ...
=> rofimoji-6.2.0_1: running do-fetch hook: 00-distfiles ...
=> rofimoji-6.2.0_1: running do-extract hook: 00-distfiles ...
=> rofimoji-6.2.0_1: extracting distfile(s), please wait...
=> rofimoji-6.2.0_1: running do-patch hook: 00-patches ...
=> rofimoji-6.2.0_1: running pre-configure hook: 00-gnu-configure-asneeded ...
=> rofimoji-6.2.0_1: running pre-configure hook: 01-override-config ...
=> rofimoji-6.2.0_1: running pre-configure hook: 02-script-wrapper ...
=> rofimoji-6.2.0_1: running pre-build hook: 02-script-wrapper ...
=> rofimoji-6.2.0_1: running do_build ...
* Getting build dependencies for wheel...

Traceback (most recent call last):
  File "/usr/lib/python3.12/site-packages/pyproject_hooks/_impl.py", line 321, in _call_hook
    raise BackendUnavailable(data.get('traceback', ''))
pyproject_hooks._impl.BackendUnavailable: Traceback (most recent call last):
  File "/usr/lib/python3.12/site-packages/pyproject_hooks/_in_process/_in_process.py", line 77, in _build_backend
    obj = import_module(mod_path)
          ^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/importlib/__init__.py", line 90, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen importlib._bootstrap>", line 1387, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1310, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
  File "<frozen importlib._bootstrap>", line 1387, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1310, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
  File "<frozen importlib._bootstrap>", line 1387, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1310, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
  File "<frozen importlib._bootstrap>", line 1387, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1324, in _find_and_load_unlocked
ModuleNotFoundError: No module named 'poetry'

ERROR Backend 'poetry.core.masonry.api' is not available.
=> ERROR: rofimoji-6.2.0_1: do_build: 'python3 -m build --no-isolation --wheel ${make_build_args} ${make_build_target}' exited with 1
=> ERROR:   in do_build() at common/build-style/python3-pep517.sh:17

ModuleNotFoundError: No module named 'poetry' poetry is missing. xbps-query shows us this:

> xbps-query -Rs poetry
[-] python3-poetry-core-1.5.0_2 Poetry PEP 517 Build Backend & Core Utilities

This is our dependency. But I should talk about dependencies more.

Dependencies for interpreted packages

As I’ve mentioned in the cross-compilation section of packaging j4-dmenu-desktop, cross-compilation isn’t really a thing for interpreted languages like Python.

Note

This is true for pure Python. Some Python code can have C or C++ extensions (usually handled by Cython). If that is the case, the rules of cross-compilation described earlier still hold.

Because of this, a lot of things that require close attention for compiled packages can be ignored for interpreted ones.

For example, you usually don’t have to distinguish hostmakedepends and makedepends. All dependencies which should be present in the masterdir should be defined in hostmakedepends. makedepends isn’t usually used.

One exception to this rule is Python compiled extensions. These usually have

makedepends="python3-devel"

One disadvantage of interpreted packages is that Shlib detection doesn’t exist for them. This means that you have to manually add all dependencies to depends.

Hunting for dependencies

As you may know, the main package manager for Python is pip. It uses the PyPI repository.

Because Python projects already integrate with a package manager, they have to be aware of their dependencies. This can greatly simplify dependency hunting.

To quote the Manual

Python packages that rely on python3-setuptools should generally map setup_requires dependencies in setup.py to hostmakedepends in the template and install_requires dependencies to depends in the template; include python3 in depends if there are no other python dependencies.

This advice applies to PEP 517 too, you’ll just have to look for the dependencies in different places.

Let’s look at rofimoji’s pyproject.toml:

[tool.poetry]
name = "rofimoji"
version = "6.2.0"

...

[tool.poetry.dependencies]
python = "^3.8"
ConfigArgParse = "^1.3"

...

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

...

Here we see that rofimoji depends on Python (this isn’t much of a surprise) and it depends on ConfigArgParse.

It also depends on its build system, poetry-core.

Upstream’s build system will help you figure out the dependencies, but it won’t find them for you. The dependencies will likely have a different name in PyPI than on Void. They also might not be packaged or they might be packaged differently compared to the PyPI version (but this is unlikely).

xbps-query -Rs is usually enough to figure out dependencies:

> xbps-query -Rs poetry-core
[-] python3-poetry-core-1.5.0_2 Poetry PEP 517 Build Backend & Core Utilities
> xbps-query -Rs ConfigArgParse
[-] python3-ConfigArgParse-1.7_1 Drop-in replacement for argparse

Let’s fix the template:

# Template file for 'rofimoji'
pkgname=rofimoji
version=6.2.0
revision=1
build_style=python3-pep517
hostmakedepends="python3 python3-poetry-core"
depends="python3-ConfigArgParse"
short_desc="Emoji, unicode and general character picker for rofi and rofi-likes"
maintainer="meator <meator.dev@gmail.com>"
license="MIT"
homepage="https://github.com/fdw/rofimoji"
changelog="https://raw.githubusercontent.com/fdw/rofimoji/main/CHANGELOG.md"
distfiles="https://github.com/fdw/rofimoji/archive/refs/tags/6.2.0.tar.gz"
checksum=21da8e6879ac16d774f3ce6dfcd1783cec891ad172617eead2c10597f477d9a9

And that’s it. The template builds without issues now.

Let’s run xlint:

> xlint rofimoji
rofimoji:10: license 'MIT', but no use of vlicense

That’s a simple fix:

# Template file for 'rofimoji'
pkgname=rofimoji
version=6.2.0
revision=1
build_style=python3-pep517
hostmakedepends="python3 python3-poetry-core"
depends="python3-ConfigArgParse"
short_desc="Emoji, unicode and general character picker for rofi and rofi-likes"
maintainer="meator <meator.dev@gmail.com>"
license="MIT"
homepage="https://github.com/fdw/rofimoji"
changelog="https://raw.githubusercontent.com/fdw/rofimoji/main/CHANGELOG.md"
distfiles="https://github.com/fdw/rofimoji/archive/refs/tags/6.2.0.tar.gz"
checksum=21da8e6879ac16d774f3ce6dfcd1783cec891ad172617eead2c10597f477d9a9

post_install() {
	vlicense LICENSE
}

Comparing with the upstream template

This is our template:

# Template file for 'rofimoji'
pkgname=rofimoji
version=6.2.0
revision=1
build_style=python3-pep517
hostmakedepends="python3 python3-poetry-core"
depends="python3-ConfigArgParse"
short_desc="Emoji, unicode and general character picker for rofi and rofi-likes"
maintainer="meator <meator.dev@gmail.com>"
license="MIT"
homepage="https://github.com/fdw/rofimoji"
changelog="https://raw.githubusercontent.com/fdw/rofimoji/main/CHANGELOG.md"
distfiles="https://github.com/fdw/rofimoji/archive/refs/tags/6.2.0.tar.gz"
checksum=21da8e6879ac16d774f3ce6dfcd1783cec891ad172617eead2c10597f477d9a9

post_install() {
	vlicense LICENSE
}

This is the official template:

# Template file for 'rofimoji'
pkgname=rofimoji
version=6.2.0
revision=1
build_style=python3-pep517
hostmakedepends="python3-poetry-core"
depends="python3-ConfigArgParse"
short_desc="Simple character picker using rofi"
maintainer="Marcin Puc <tranzystorek.io@protonmail.com>"
license="MIT"
homepage="https://github.com/fdw/rofimoji"
changelog="https://raw.githubusercontent.com/fdw/rofimoji/main/CHANGELOG.md"
distfiles="https://github.com/fdw/rofimoji/archive/refs/tags/${version}.tar.gz"
checksum=21da8e6879ac16d774f3ce6dfcd1783cec891ad172617eead2c10597f477d9a9

post_install() {
	vlicense LICENSE
	vman src/picker/docs/rofimoji.1
}

These two templates are almost identical, which is a good sign.

The official template’s short_desc is more concise than ours. It also installs the manpage, which is useful.

The end?

Well, you’ve made it. This is the end of the practical part of this tutorial. You should now be able to package your own programs, libraries or other things using xbps-src. You now know the many helpful features of xbps-src that should simplify packaging.

This tutorial doesn’t cover the entirety of xbps-src and it doesn’t try to do so. Look in the Manual for that. Now that you know the basics, you should be able to comprehend it well.

Knowing the basics is also good when asking for help. I have often seen people in #voidlinux trying to solve some problem with their template while not knowing the basics of xbps-src. This can complicate troubleshooting for both parties.

You should now have working package(s). You might want to contribute them. If yes, continue reading:

Contributing