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()!