Assuming gir is nocross
Introduction
A while back i wrote an article about making gir be cross compilable on Void Linux, called crossing the gir.
This article post is a small rant about a thankfully uncommon problem but that sadly still annoys me greatly, it is an anti-pattern born out of an outdated assumption that at best cause unhelpful error messages and at worst silently fail much later on.
This is the story of assuming gir is not cross compilable.
atk
We start like i started, the first package i had this problem.
Template
Let's take a look at the template to see if it all correct, here is a checklist of what we need:
- [ ] gir build_helper set
- [ ] gir build_option declared
- [ ] gir in the build_options_default
- [ ] vmove /usr/share/gir-1.0 to -devel package
# Template file for 'atk' pkgname=atk version=2.30.0 revision=2 build_style=meson build_helper="gir" configure_args="-Dintrospection=$(vopt_if gir true false)" hostmakedepends="pkg-config glib-devel" makedepends="libglib-devel" short_desc="Set of interfaces for accessibility" maintainer="Juan RP <xtraeme@voidlinux.org>" license="LGPL-2.1-or-later" homepage="http://www.gtk.org/" distfiles="${GNOME_SITE}/atk/${version%.*}/atk-${version}.tar.xz" checksum=dd4d90d4217f2a0c1fee708a555596c2c19d26fef0952e1ead1938ab632c027b # Package build options build_options="gir" case "$XBPS_TARGET_MACHINE" in x86_64-musl) build_options_default+=" gir" ;; *-musl) ;; *) build_options_default+=" gir" ;; esac atk-devel_package() { depends="${makedepends} atk>=${version}_${revision}" short_desc+=" - development files" pkg_install() { vmove usr/include vmove usr/lib/pkgconfig vmove "usr/lib/*.so" if [ "$build_option_gir" ]; then vmove "usr/share/gir-*" fi } }
- [x] gir build_helper set
build_style=meson build_helper="gir" configure_args="-Dintrospection=$(vopt_if gir true false)"
- [x] gir build_option declared
# Package build options build_options="gir"
- [x] gir in the build_options_default
case "$XBPS_TARGET_MACHINE" in x86_64-musl) build_options_default+=" gir" ;; *-musl) ;; *) build_options_default+=" gir" ;; esac
The above covers all arches that we support gir in, all except cross musl ones.
- [x] vmove /usr/share/gir-1.0 to -devel package
if [ "$build_option_gir" ]; then vmove "usr/share/gir-*" fi
Error
Alright we are all set!, Let's build it!
$ xbps-src -f -a aarch64 pkg atk => atk-devel-2.30.0_2: running pkg_install ... mv: cannot stat '/destdir/aarch64-linux-gnu/atk-2.30.0/usr/share/gir-*': No such file or directory => ERROR: atk-devel-2.30.0_2: pkg_install: 'mv ${_destdir}/$files ${_pkgdestdir}/${_targetdir}' exited with 1 => ERROR: in _vmove() at common/environment/setup/install.sh:231 => ERROR: in _noglob_helper() at common/environment/setup/install.sh:12 => ERROR: in pkg_install() at srcpkgs/atk-devel/template:34
Wait, what ?, the files aren't there!, why? It works on native compilations...
Inspecting
I'll save the time i ran around like a headless chicken looking at everything, here is the problem, this is atk's meson.build file, the section shown is the part that generates gir.
if not meson.is_cross_build() and get_option('introspection') gnome.generate_gir(libatk, sources: atk_sources + atk_headers + [ atk_enum_h ] + [ atk_version_h ], namespace: 'Atk', nsversion: atk_api_version, identifier_prefix: 'Atk', symbol_prefix: 'atk', export_packages: 'atk', includes: [ 'GObject-2.0' ], install: true, extra_args: [ '--quiet', '--c-include=atk/atk.h', '-DATK_COMPILATION', ]) endif
See it ? Let's enhance!
if not meson.is_cross_build() and get_option('introspection')
That is_cross_build check! It guts the gir generation and we left with a super unhelpful message, not even from meson and the project, but from xbps-src, telling us that we don't have the gir directory.
Fixing
Let's remove it and try again:
diff --git a/atk/meson.build b/atk/meson.build index 616a3e6..941ded8 100644 --- atk/meson.build +++ atk/meson.build @@ -137,7 +137,7 @@ libatk_dep = declare_dependency(link_with: libatk, dependencies: gobject_dep, sources: atk_enum_h) -if not meson.is_cross_build() and get_option('introspection') +if get_option('introspection') gnome.generate_gir(libatk, sources: atk_sources + atk_headers + [ atk_enum_h ] + [ atk_version_h ], namespace: 'Atk',
$ xbps-src -f -a aarch64 pkg atk => Registering new packages to /host/binpkgs/atk index: added `atk-2.30.0_2' (aarch64). index: added `atk-devel-2.30.0_2' (aarch64). index: 2 packages registered.
All good, let's send it to upstream, i filled a merge request on atk's gitlab.
libgxps
And we continue like i continued, this is the second package i ran across that had this kind of problem. Let's apply all the same methodology that we applied to atk.
Template
Here is your usual checklist
- [ ] gir build_helper set
- [ ] gir build_option declared
- [ ] gir in the build_options_default
- [ ] vmove /usr/share/gir-1.0 to -devel package
# Template file for 'libgxps' pkgname=libgxps version=0.3.1 revision=2 build_style=meson build_helper="gir" configure_args="-Denable-test=false -Ddisable-introspection=$(vopt_if gir false true)" hostmakedepends="pkg-config" makedepends="cairo-devel libglib-devel libjpeg-turbo-devel libpng-devel tiff-devel lcms2-devel libarchive-devel freetype-devel" short_desc="GObject base library for XPS documents" maintainer="Rasmus Thomsen <rasmus.thomsen@protonmail.com>" license="LGPL-2.1-or-later" homepage="https://wiki.gnome.org/Projects/libgxps" distfiles="${GNOME_SITE}/${pkgname}/${version%.*}/${pkgname}-${version}.tar.xz" checksum=1a939fc8fcea9471b7eca46b1ac90cff89a30d26f65c7c9a375a4bf91223fa94 # Package build options build_options="gir" case "$XBPS_TARGET_MACHINE" in x86_64-musl) build_options_default+=" gir" ;; *-musl) ;; *) build_options_default+=" gir" ;; esac libgxps-devel_package() { depends="cairo-devel libglib-devel libarchive-devel ${sourcepkg}-${version}_${revision}" short_desc+=" - development files" pkg_install() { vmove usr/include vmove "usr/lib/*.so" vmove usr/lib/pkgconfig if [ "$build_option_gir" ]; then vmove usr/share/gir-1.0 fi } }
- [x] gir build_helper set
build_style=meson build_helper="gir" configure_args="-Denable-test=false
- [x] gir build_option declared
# Package build options build_options="gir"
- [x] gir in the build_options_default
case "$XBPS_TARGET_MACHINE" in x86_64-musl) build_options_default+=" gir" ;; *-musl) ;; *) build_options_default+=" gir" ;; esac
- [x] vmove /usr/share/gir-1.0 to -devel package
if [ "$build_option_gir" ]; then vmove usr/share/gir-1.0 fi
Looks all fine to me
Error
Let's build it
$ xbps-src -f -a aarch64 pkg libgxps => libgxps-devel-0.3.1_2: running pkg_install ... mv: cannot stat '/destdir/aarch64-linux-gnu/libgxps-0.3.1/usr/share/gir-1.0': No such file or directory => ERROR: libgxps-devel-0.3.1_2: pkg_install: 'mv ${_destdir}/$files ${_pkgdestdir}/${_targetdir}' exited with 1 => ERROR: in _vmove() at common/environment/setup/install.sh:231 => ERROR: in _noglob_helper() at common/environment/setup/install.sh:12 => ERROR: in pkg_install() at srcpkgs/libgxps-devel/template:37
Ah, it is even the same error!, missing directory!
Inspecting
Let's not beat around the bush, find that cross build!
gir = find_program('g-ir-scanner', required: false) build_gir = gir.found() and not meson.is_cross_build() and not get_option('disable-introspection')
Found it, again dreams of a cross gir world are gutted by an assumption.
Fixing
Let's patch it out
diff --git a/meson.build b/meson.build index a34a616..2d6eb1e 100644 --- meson.build +++ meson.build @@ -131,7 +131,7 @@ libm_dep = cc.find_library('m', required: false) gnome = import('gnome') gir = find_program('g-ir-scanner', required: false) -build_gir = gir.found() and not meson.is_cross_build() and not get_option('disable-introspection') +build_gir = gir.found() and not get_option('disable-introspection') configure_file(output: 'config.h', configuration: cdata)
And build it:
$ xbps-src -f -a aarch64 pkg libgxps => Registering new packages to /host/binpkgs/libgxps index: added `libgxps-0.3.1_2' (aarch64). index: added `libgxps-devel-0.3.1_2' (aarch64). index: 2 packages registered.
All good again, a merge request has been made in libgxps' gitlab.
libgit2-glib
This is the most interesting one, and the one that drove me over the edge into writing this article, it is the one that provides the most helpful yet useless debug message and it is all because of the same assumption as above, mixed with vala.
Template
Never forget your handy checklist:
- [ ] gir build_helper set
- [ ] gir build_option declared
- [ ] gir in the build_options_default
- [ ] vmove /usr/share/gir-1.0 to -devel package
# Template file for 'libgit2-glib' pkgname=libgit2-glib version=0.27.7 revision=1 build_style=meson build_helper="gir" configure_args="-Dintrospection=$(vopt_if gir true false) -Dvapi=$(vopt_if vala true false)" hostmakedepends="pkg-config glib-devel $(vopt_if vala vala)" makedepends="libglib-devel libgit2-devel python-gobject-devel" short_desc="Glib wrapper library around libgit2" maintainer="Juan RP <xtraeme@voidlinux.org>" license="LGPL-2.1-or-later" homepage="https://github.com/GNOME/${pkgname}" distfiles="${homepage}/archive/v${version}.tar.gz" checksum=224a0bc1d902729b42ca9424db545c460ff8aaeb08addc2b0a01bf235b4c9d6e build_options="gir vala" case "$XBPS_TARGET_MACHINE" in x86_64-musl) build_options_default+=" gir vala" ;; *-musl) ;; *) build_options_default+=" gir vala" ;; esac libgit2-glib-devel_package() { depends="${sourcepkg}>=${version}_${revision} libglib-devel libgit2-devel" short_desc+=" - development files" pkg_install() { vmove "usr/lib/*.so" vmove usr/include vmove usr/lib/pkgconfig if [ "$build_option_vala" ]; then vmove usr/share/vala fi if [ "$build_option_gir" ]; then vmove usr/share/gir-1.0 fi } }
- [x] gir build_helper set
build_style=meson build_helper="gir" configure_args="-Dintrospection=$(vopt_if gir true false)
- [x] gir build_option declared
build_options="gir vala"
- [x] gir in the build_options_default
case "$XBPS_TARGET_MACHINE" in x86_64-musl) build_options_default+=" gir vala" ;; *-musl) ;; *) build_options_default+=" gir vala" ;; esac
- [x] vmove /usr/share/gir-1.0 to -devel package
if [ "$build_option_gir" ]; then vmove usr/share/gir-1.0 fi
Looks fine as always.
Error
OK, let's build it!
$ xbps-src -f -a aarch64 pkg libgit2-glib
meson.build:132:2: ERROR: Assert failed: vapi support was requested, but introspection support is mandatory.
Inspecting
That looks different. More helpful, let's go to that line in the meson.build!
assert(enable_gir, 'vapi support was requested, but introspection support is mandatory.')
The code above will error out if enable_gir is false, let's find out where this enable_gir is set up, Lucky for up it was in the code block above:
enable_gir = get_option('introspection') if enable_gir # XXX: Not nice, but probably our best option enable_gir = find_program('g-ir-scanner', required: false).found() and not meson.is_cross_build() endif
Yet again, is_cross_build checks cause problems, enable_gir is always false when cross compiling because of it.
Fixing
Let's patch it.
diff --git a/meson.build b/meson.build index e1e9fde..63b958a 100644 --- meson.build +++ meson.build @@ -124,7 +124,7 @@ libgit2_dep = dependency('libgit2', version: '>=' + git2_req) enable_gir = get_option('introspection') if enable_gir # XXX: Not nice, but probably our best option - enable_gir = find_program('g-ir-scanner', required: false).found() and not meson.is_cross_build() + enable_gir = find_program('g-ir-scanner', required: false).found() endif enable_vapi = get_option('vapi')
And build it:
$ xbps-src -f -a aarch64 pkg libgit2-glib => Registering new packages to /host/binpkgs/libgit2-glib index: added `libgit2-glib-0.27.7_1' (aarch64). index: added `libgit2-glib-devel-0.27.7_1' (aarch64). index: 2 packages registered.<Paste>
And it works, as expected, and also it is expected that a merge request is filled in libgit2-glib's gitlab.
Conclusion
All these errors would not happen if it were not for developers making an assumption that might have been true at their time. I say might because it is entirely possible they made that assumption after buildroot/yocto added support to cross compiling gir. In that case the assumption is just plain wrong.
The end result is:
Silent errors
The errors of atk and libgxps wouldn't be caught without a package building system like xbps-src, if it weren't for xbps-src trying to move usr/share/gir-* and /usr/share/gir-1.0, on atk and libgxps respectively, it would never fail and the package would be silently generated without the gir objects.
Unhelpful messages
The message from libgit2-glib was only helpful because in the back of my mind this sentence was running:
It is that cross_build check thingie, isn't it ? it is right ?!!!
It took a grand total of a few seconds from opening the meson.build file to fixing it.
PSA
GIR IS CROSS COMPILABLE DON'T GATEKEEP GIR GENERATION BEHIND is_cross_build()!