Introduction

For those uninitiated, gir is GObject Introspection. A technology from GNOME that allows libraries to expose bindings to any language that can speak to C. It is the reason you can use lots of languages to write your GNOME and GTK applications while Qt5 is mostly limited to C++ and Python.

On Void Linux a high emphasis is placed into stuff being able to be cross compiled and for a long time gir has eluded us, with it's requirement of running stuff on the target system and using raw dumps of C structures.

Now this is a river that is going to be crossed.

crossing the gir

Artifact means any piece of compiled code

Making gir cross compilable is not an easy task, as described above it requires running artifacts created for the target system, the bane of cross compilation.

Most of the time when running artifacts for a package it means generators, which can most of the time be built on the host system and then ran just fine.

Not for gir, it needs specific information from those artifacts that is only pertinent to the target architecture.

gobject-introspection

We start with the beast itself, a collection of tools for generate .gir and .typelib files, g-ir-scanner, g-ir-compiler, g-ir-generate. Also a collection of common bindings like DBus, GLib, etc.

Thankfully for us, the Yocto Project and Buildroot did all of the work patching gir, most of the work required was importing the required parts, discarding what we don't use and gluing it in xbps-src's infrastructure.

One of the first things required was importing a tool that the Yocto Project used on their gir works.

It was easy enough for glibc systems, while we had to fetch a git commit there wasn't much problem getting it compile and run with success.

The problem came when musl appeared, Void Linux developer Enno "Gottox" Boland tackled this problem, and at every corner compatibility problems appeared. Nonetheless he did it, and made prelink-cross available for use in Void Linux

# Template file for 'prelink-cross'
pkgname=prelink-cross
version=20180128
revision=1
_githash=ca213abd9ebfd77a04e3a967bf9f7bc1ef832087
wrksrc="prelink-cross-${_githash}"
build_style=gnu-configure
hostmakedepends="automake libtool"
makedepends="elfutils-devel binutils-devel"
case $XBPS_TARGET_MACHINE in
    *-musl) makedepends+=" argp-standalone"; LDFLAGS+=" -largp" ;;
esac
short_desc="Prelink from the yocto project"
maintainer="maxice8 <thinkabit.ukim@gmail.com>"
license="GPL-2.0-or-later"
homepage="https://git.yoctoproject.org/cgit.cgi/prelink-cross"
distfiles="https://github.com/Gottox/prelink-cross/archive/$_githash.tar.gz"
checksum=b6b0baf60e7e66f62ed8e7117973be24b2d24649490365e6a88e11be0dd2fab4

pre_configure() {
    autoreconf -fi
}

post_install() {
    # We don't want the tools that are on prelink
    # just prelink-rltd
    rm -f ${DESTDIR}/usr/bin/{execstack,prelink}
    rm -rf ${DESTDIR}/usr/share/man
}

To have an idea of the patching required, this is the git additions/deletions of all changes made, to work around glibc specific behaviour:

 configure.ac          |  1 +
 gelfx32/gelfx.h       |  2 +-
 src/Makefile.am       |  2 +-
 src/arch-alpha.c      |  2 +-
 src/arch-arm.c        |  2 +-
 src/arch-cris.c       |  2 +-
 src/arch-i386.c       |  2 +-
 src/arch-ia64.c       |  2 +-
 src/arch-mips.c       |  2 +-
 src/arch-ppc.c        |  2 +-
 src/arch-ppc64.c      |  2 +-
 src/arch-s390.c       |  2 +-
 src/arch-s390x.c      |  2 +-
 src/arch-sh.c         |  2 +-
 src/arch-sparc.c      |  2 +-
 src/arch-sparc64.c    |  2 +-
 src/arch-x86_64.c     |  2 +-
 src/cache.c           |  2 +-
 src/checksum.c        |  2 +-
 src/conflict.c        |  2 +-
 src/cxx.c             |  2 +-
 src/doit.c            |  2 +-
 src/dso.c             |  2 +-
 src/dwarf2.c          |  2 +-
 src/elf.h             |  4 ----
 src/exec.c            |  2 +-
 src/execle_open.c     |  2 +-
 src/execstack.c       |  2 +-
 src/fptr.c            |  2 +-
 src/gather.c          |  6 +++++-
 src/get.c             |  2 +-
 src/layout.c          |  2 +-
 src/main.c            |  4 ++--
 src/md5.h             |  8 ++++----
 src/mdebug.c          |  2 +-
 src/prelink.c         |  2 +-
 src/reloc.c           |  2 +-
 src/rtld/Makefile.am  |  1 +
 src/rtld/dl-load.c    |  2 +-
 src/rtld/dl-lookup.c  |  2 +-
 src/rtld/dl-misc.c    |  2 +-
 src/rtld/dl-object.c  |  2 +-
 src/rtld/dl-version.c |  4 ++--
 src/rtld/rtld.c       |  6 +++---
 src/rtld/rtld.h       |  2 +-
 src/space.c           |  2 +-
 src/stabs.c           |  2 +-
 src/undo.c            |  2 +-
 src/undoall.c         |  2 +-
 src/verify.c          | 11 +++++++++-
 src/wrap-error.c      | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++
 src/wrap-error.h      | 11 ++++++++++
 src/wrap-file.c       |  8 ++++----
 53 files changed, 140 insertions(+), 62 deletions(-)

Regardless, with that out of the way we can go back to gir.


wrapping gobject-introspection

We can now make use of the patches from the Yocto Project, and we need to write our own wrappers based on Yocto's ones.

First we need to wrap g-ir-scanner, easy enough we renamed the executable that is built by the package to g-ir-scanner.wrapped and claim g-ir-scanner for ourselves with this:

#!/usr/bin/env bash
# Check if we are running in an xbps-src environment and run the wrapper if that
# is the case.
if [ -n "$XBPS_CROSS_BASE" -a -n "$XBPS_TARGET_MACHINE" -a -n "$XBPS_VERSION" ]; then
    # This prevents g-ir-scanner from writing cache data to $HOME
    export GI_SCANNER_DISABLE_CACHE=1
    
    exec /usr/bin/g-ir-scanner.wrapped \
                 --use-binary-wrapper=/usr/bin/g-ir-scanner-qemuwrapper \
                 --use-ldd-wrapper=/usr/bin/g-ir-scanner-lddwrapper \
                 --add-include-path=${XBPS_CROSS_BASE}/usr/share/gir-1.0 \
                 --add-include-path=${XBPS_CROSS_BASE}/usr/lib/gir-1.0 \
                 "${@//-I\/usr\/include/-I${XBPS_CROSS_BASE}\/usr\/include}"
fi
    
exec /usr/bin/g-ir-scanner.wrapped "$@"

You may notice the --use-binary-wrapper= and --use-ldd-wrapper=, which point to other wrappers we also need to write, again based on Yocto's but for Void Linux specifically.

Here is g-ir-scanner-qemuwrapper:

#!/bin/sh
# Use a modules directory which doesn't exist so we don't load random things
# which may then get deleted (or their dependencies) and potentially segfault
export GIO_MODULE_DIR=${XBPS_CROSS_BASE}/gio/modules-dummy

case "$XBPS_TARGET_MACHINE" in
    x86_64*) _MACHINE=x86_64 ;;
    i686) _MACHINE=i386 ;;
    aarch64*) _MACHINE=aarch64 ;;
    armv*) _MACHINE=arm ;;
esac

/usr/bin/qemu-${_MACHINE}-static \
            -L ${XBPS_CROSS_BASE} \
            -E LD_LIBRARY_PATH="${XBPS_CROSS_BASE}/usr/lib:.libs:${GIR_EXTRA_LIBS_PATH}" \
            "$@"

You may notice we make use of qemu-user-static to run on the target machine.

And g-ir-scanner-lddwrapper, this is where prelink-cross appears:

#!/bin/sh
/usr/bin/prelink-rtld --root=${XBPS_CROSS_BASE} "$@"

That wraps ups our wrapper for g-ir-scanner, now we need to wrap g-ir-compiler. This one gets more complicated as we need to wrap it in a way it can know to use the g-ir-compiler from the gir if it's being currently built.

#!/bin/sh
#
# Check if we are running in an xbps-src environment and run the wrapper if that
# is the case.
if [ -n "$XBPS_CROSS_BASE" -a -n "$XBPS_TARGET_MACHINE" -a -n "$XBPS_VERSION" ]; then
    # wrapper for g-ir-compiler, which runs the target version of it through qemu.
    # g-ir-compiler writes out the raw content of a C struct to disk, and therefore
    # is architecture dependent.
    if [ -x build/tools/g-ir-compiler ]; then
        # We are compiling gobject-introspection.
        # lets used the one we just compiled.
        gir_bin=build/tools/g-ir-compiler
    elif [ -x ${XBPS_CROSS_BASE}/usr/bin/g-ir-compiler.wrapped ]; then
        # Lets use the g-ir-compiler from the target
        gir_bin=${XBPS_CROSS_BASE}/usr/bin/g-ir-compiler.wrapped
    fi
    exec /usr/bin/g-ir-scanner-qemuwrapper ${gir_bin} "$@"
fi

exec /usr/bin/g-ir-compiler.wrapped "$@"

OK, the wrappers are wrapped, let's make the template install them correctly.

$ sed -n '29,43p' srcpkgs/gobject-introspection/template
# Install our wrappers system-wide, they are required for building all other
# gobject-based packages.
vbin ${FILESDIR}/g-ir-scanner-qemuwrapper
vbin ${FILESDIR}/g-ir-scanner-lddwrapper

# Install g-ir-scanner-wrapper as g-ir-scanner, we need it with that name since
# we can't expect people to just not hardcode /usr/bin/g-ir-scanner, some packages
# like gtk+3 just like **really** much to use /usr/bin/g-ir-scanner and meson with
# find_program is also to blame.
mv ${DESTDIR}/usr/bin/g-ir-scanner{,.wrapped}
vbin ${FILESDIR}/g-ir-scanner-wrapper g-ir-scanner

# Same logic of g-ir-scanner applies here
mv ${DESTDIR}/usr/bin/g-ir-compiler{,.wrapped}
vbin ${FILESDIR}/g-ir-compiler-wrapper g-ir-compiler

Let's also allow our package to be cross compiled by setting the appropriate dependencies and configuration options for it.

$ sed -n '20,26p' srcpkgs/gobject-introspection/template
if [ "$CROSS_BUILD" ]; then
    hostmakedepends+=" gobject-introspection qemu-user-static prelink-cross"
    configure_args+=" -Denable-host-gi=true
     -Denable-gi-cross-wrapper=/usr/bin/g-ir-scanner-qemuwrapper
     -Denable-gi-ldd-wrapper=/usr/bin/g-ir-scanner-lddwrapper
     -Dpkgconfig-sysroot-path=${XBPS_CROSS_BASE}"
fi

pkg-config sysroot dir

Before finishing gir we need to do some final touches on its pkg-config files, which contains some very important variables that packages use like g_ir_scanner which points to the point of the g-ir-scanner binary and typelibdir and girdir which point to the directory where the .typelib and .gir files are to be found.

Packages query for these variables with pkg-config --variable <variable>, and a problem occurs there when cross compiling.

pkg-config can normally be made to return values for the target system by setting the PKG_CONFIG_SYSROOT_DIR variable, when a package queries for CFLAGS with pkg-config --cflags the values returned for the includes (-I) are prefixed with the value of PKG_CONFIG_SYSROOT_DIR.

But, that doesn't happen for values queried with pkg-config --variable, if a package ask for where the .typelib and .gir files are, the values returned are from the host system, not the target one.

Do not despair, not all is lost, we can prefix ${pc_sysrootdir} in the pkg-config files and the value will be inserted as expected, so let's do it in the template:

$ sed -n '45,54p' srcpkgs/gobject-introspection/template
# modify the pkg-config files to respect ${pc_sysrootdir} for variables that are
# meant to be called with 'pkg-config --variable'
vsed -e 's|^g_ir_scanner=.*|g_ir_scanner=${pc_sysrootdir}/${bindir}/g-ir-scanner|g' \
     -e 's|^g_ir_compiler=.*|g_ir_compiler=${pc_sysrootdir}/${bindir}/g-ir-compiler|g' \
     -e 's|^g_ir_generate=.*|g_ir_generate=${pc_sysrootdir}/${bindir}/g-ir-generate|g' \
     -e 's|^gidatadir.*|gidatadir=${pc_sysrootdir}/${datadir}/gobject-introspection-1.0|g' \
     -e 's|^girdir.*|girdir=${pc_sysrootdir}/${datadir}/gir-1.0|g' \
     -e 's|^typelibdir.*|typelibdir=${pc_sysrootdir}/${libdir}/girepository-1.0|g' \
     -i ${DESTDIR}/usr/lib/pkgconfig/gobject-introspection-1.0.pc \
     -i ${DESTDIR}/usr/lib/pkgconfig/gobject-introspection-no-export-1.0.pc

One last thing before we finish the gobject-introspection package itself, let's remove the EXT_SUFFIX from the cpython3 bindings, so that cross compilation is not affected by usage of the EXT_SUFFIX from the host system and not the target one, Reading this article is recommended for further information.

$ sed -n '56,58p' srcpkgs/gobject-introspection/template
# Fix the name of the python3 c bindings
mv ${DESTDIR}/usr/lib/gobject-introspection/giscanner/_giscanner*.so \
   ${DESTDIR}/usr/lib/gobject-introspection/giscanner/_giscanner.so

Let's try and compile it!

$ xbps-src -f -a aarch64 pkg gobject-introspection
=> Registering new packages to /host/binpkgs/gobject-introspection
index: added `gir-freedesktop-1.58.3_2' (aarch64).
index: added `gobject-introspection-1.58.3_2' (aarch64).
index: added `libgirepository-1.58.3_2' (aarch64).
index: added `libgirepository-devel-1.58.3_2' (aarch64).
index: 8 packages registered.

Appears to work, let's spin up an aarch64 machine:

Thanks to Cogitri for lending the machine.

$ uname -m
aarch64
$ python3
>>> import platform
>>> platform.platform()
'Linux-4.14.91_1-aarch64-with-glibc2.17'
>>> import gi
>>> gi.require_version("DBus", "1.0")
>>>

gir build_helper

We can now glue some components from xbps-src to make cross compilation of packages that use gir more convenient.

This time we will use the newly introduced build_helper which, if you haven't heard of them then this article is recommended, here will be written only a very short explanation.

In short build_helper is a system of files that contain shell snippets that set global variables and template specific variables (like hostmakedepends) when sourced, they are a nice way to encapsulate complex logic in a way that we can import them only when necessary.

Here is the new build_helper for helping with gir cross compilation: gir!

$ cat common/build-helper/gir.sh
#
# gir - build-helper for gobject-introspection
#
# This build-helper is used for packages that make use of
# the GObject introspection middleware layer.
# 

# Check if the 'gir' build_option is set or if there is no
# 'gir' build_option.
if [ "$build_option_gir" ] || [[ $build_options != *"gir"* ]]; then
    # Provide the host tooling, g-ir-scanner, g-ir-compiler and its
    # wrappers.
    hostmakedepends+=" gobject-introspection"
    
    if [ "$CROSS_BUILD" ]; then
        # Required for running binaries produced from g-ir-compiler
        # via g-ir-scanner-qemuwrapper
        hostmakedepends+=" qemu-user-static"
    
        # Required for running the g-ir-scanner-lddwrapper
        hostmakedepends+=" prelink-cross"

        # Provide basic .gir types like GLib, GObject, DBus, Gio, cairo
        # and tooling like g-ir-compiler
        makedepends+=" gobject-introspection"
    fi
fi

python-gobject

Now that we got that nice glue, let's make use of it and start by adapting python-gobject to be cross compilable.

Why ? Everyone uses it, here is a list of the packages that depend on either python-gobject or python3-gobject

TwitchNotifier-0.5_1
avahi-discover-0.7_6
blueman-2.0.8_1
caffeine-ng-3.4.2_1
caribou-0.4.21_1
catfish-1.4.7_1
ccsm-0.8.16_4
cdemu-client-3.2.0_2
chrome-gnome-shell-10.1_1
cinnamon-4.0.9_1
cinnamon-screensaver-4.0.3_1
clearine-0.5_1
d-feet-0.3.14_1
devedeng-4.13.0_1
eolie-0.9.51_1
flowblade-1.16_2
gajim-1.1.2_1
gcdemu-3.2.0_1
gnome-3.30.0_3
gnome-music-3.30.2_1
gnome-passwordsafe-3.31.1_2
gnome-tweaks-3.30.2_1
gpodder-3.10.7_1
gpsd-xgps-3.17_4
gramps-5.0.1_1
gscreenshot-2.10.1_1
gst1-python-1.14.4_1
gst1-python3-1.14.4_2
guake-3.4.0_2
gufw-18.10.0_1
ibus-1.5.19_2
ibus-hangul-1.5.1_3
ice-ssb-5.3.4_1
indicator-doom-cpu-1.0.1_1
kupfer-319_2
laditools-1.1.0_2
libgladeui3-3.22.1_1
libpeas-1.22.0_1
libratbag-0.9.904_1
lightdm-gtk-greeter-settings-1.2.2_1
lollypop-0.9.916_1
lutris-0.4.23_1
mate-menu-18.04.3_1
mate-tweak-18.10.2_1
meld-3.20.0_1
menulibre-2.2.0_1
mkchromecast-0.3.8.1_1
mozo-1.20.2_1
mugshot-0.4.1_1
mypaint-1.2.1_5
networkmanager-dmenu-1.1_1
onboard-1.4.1_4
oomox-1.6.2_2
piper-0.2.903_1
pithos-1.4.1_1
pulseaudio-equalizer-ladspa-3.0.1_1
pulseeffects-3.2.3_3
pychess-0.12.4_2
python-atspi-2.30.0_1
python-gobject-devel-3.30.4_2
python3-atspi-2.30.0_1
python3-dbusmock-0.18.1_1
python3-openrazer-2.4.0_1
quodlibet-4.2.1_2
rednotebook-2.8_1
redshift-gtk-1.12_1
sc-controller-0.4.6.1_1
scanmem-0.17_4
sonata-1.7b1_2
soundconverter-3.0.0_3
startup-tools-1.13.4_1
syncthing-gtk-0.9.4.3_1
system-config-printer-1.5.11_4
terminator-1.91_1
tryton-5.0.5_1
uberwriter-12.11.02_1
udiskie-1.7.5_1
variety-0.7.1_1
vimiv-0.9.1_2
virt-manager-tools-2.0.0_2
volctl-0.6.2_1
wpgtk-5.8.7_1
xdot-1.0_1
zeitgeist-1.0.1_1

Quite a lot, eh ?

Here is the python3-gobject template, notable is the usage of the gir build_helper.

# Template file for 'python3-gobject'
pkgname=python3-gobject
version=3.30.4
revision=1
wrksrc="pygobject-${version}"
build_style=meson
build_helper="gir"
configure_args="-Dpython=python${py3_ver}"
pycompile_module="gi pygtkcompat"
hostmakedepends="pkg-config"
makedepends="libglib-devel python3-cairo-devel python3-devel"
depends="gir-freedesktop python3-cairo"
short_desc="Python3 bindings for GObject"
maintainer="Enno Boland <gottox@voidlinux.org>"
license="LGPL-2.1-or-later"
homepage="https://pygobject.readthedocs.io/"
distfiles="${GNOME_SITE}/pygobject/${version%.*}/pygobject-${version}.tar.xz"
checksum=2dc1a1a444b82955e65b81c2a2511ecf8032404beba4ef1d48144168f2f64c43

case "$XBPS_TARGET_MACHINE" in
    x86_64-musl) ;;
    *-musl) broken="Error relocating /usr/aarch64-linux-musl/usr/lib/libz.so.1: unsupported relocation type 1026" ;;
esac

python3-gobject-devel_package() {
    depends="libgirepository-devel python3-cairo-devel
     libglib-devel libffi-devel
     python3-gobject>=${version}_${revision}"
    short_desc+=" - development files"
    pkg_install() {
        vmove usr/include
        vmove usr/lib/pkgconfig
    }
}

Before we can have some fun with it and compile some base software we need to deal with one last issue, meson.

meson

The meson implementation of gir generation unconditionally uses the native pkg-config, it makes sense since nobody (except Yocto, and now us) does cross gir.

Using the native pkg-config means it will run /usr/bin/pkg-config and thus will only search parts of the host system and not of the cross system, which is great when we need to build generators and other binaries that run on the host system to help the build process, but in this case we need the information from the target system.

Let's just patch the meson package to do it. It is a one line change.

diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py
index bf49770..42473cb 100644
--- mesonbuild/modules/gnome.py
+++ mesonbuild/modules/gnome.py
@@ -429,7 +429,7 @@ class GnomeModule(ExtensionModule):
         try:
             gir_dep = self.gir_dep or PkgConfigDependency('gobject-introspection-1.0',
                                                           state.environment,
-                                                          {'native': True})
+                                                          {'native': False})
             pkgargs = gir_dep.get_compile_args()
         except Exception:
             raise MesonException('gobject-introspection dependency was not found, gir cannot be generated.')

OK, all done!

Let's have fun!

Let's have some fun!, let's cross compile gtk+3 with gir!

dep: gdk-pixbuf

First we need to build the dependencies of gtk+3 that it uses for building its own gir files.

diff --git a/srcpkgs/gdk-pixbuf/template b/srcpkgs/gdk-pixbuf/template
index d8c2c6db67..e353c55197 100644
--- a/srcpkgs/gdk-pixbuf/template
+++ b/srcpkgs/gdk-pixbuf/template
@@ -3,10 +3,10 @@ pkgname=gdk-pixbuf
 version=2.38.0
 revision=1
 build_style=meson
+build_helper="gir"
 configure_args="-Dgir=$(vopt_if gir true false) -Djasper=false
  -Dpng=true -Dx11=true -Dinstalled_tests=false"
-hostmakedepends="gettext-devel glib-devel pkg-config
- $(vopt_if gir 'gobject-introspection') libxslt docbook-xsl"
+hostmakedepends="gettext-devel glib-devel pkg-config libxslt docbook-xsl"
 makedepends="libX11-devel libglib-devel libpng-devel tiff-devel
  shared-mime-info"
 short_desc="Image loading library for The GTK+ toolkit (v2)"
@@ -18,12 +18,11 @@ checksum=dd50973c7757bcde15de6bcd3a6d462a445efd552604ae6435a0532fbbadae47
 
 # Package build options
 build_options="gir"
+build_options_default="gir"
 
 # Disable gir for cross builds.
 if [ "$CROSS_BUILD" ]; then
    hostmakedepends+=" gdk-pixbuf-devel"
-else
-	build_options_default="gir"
 fi
 
 pre_configure() {

Now let's build it and check the contents.

$ xbps-src -f -a aarch64 pkg gdk-pixbuf
=> Registering new packages to /host/binpkgs/gobject-introspection
index: added `gdk-pixbuf-2.38.0_1' (aarch64).
index: added `gdk-pixbuf-devel-2.38.0_1' (aarch64).
index: added `gdk-pixbuf-xlib-2.38.0_1' (aarch64).
index: 11 packages registered.
$ env XBPS_TARGET_ARCH=aarch64 xls gdk-pixbuf{,-devel} | grep -E '.(gir|typelib)'
/usr/lib/girepository-1.0/GdkPixbuf-2.0.typelib
/usr/lib/girepository-1.0/GdkPixdata-2.0.typelib
/usr/share/gir-1.0/GdkPixbuf-2.0.gir
/usr/share/gir-1.0/GdkPixdata-2.0.gir

dep: pango

Same thing for pango:

diff --git a/srcpkgs/pango/template b/srcpkgs/pango/template
index 2ba99274b5..98adbd8f1d 100644
--- a/srcpkgs/pango/template
+++ b/srcpkgs/pango/template
@@ -4,8 +4,8 @@ version=1.42.4
 revision=1
 configure_args="$(vopt_enable gir introspection) --disable-gtk-doc"
 build_style=gnu-configure # switch to meson when possible
-hostmakedepends="glib-devel help2man pkg-config
- $(vopt_if gir 'gobject-introspection')"
+build_helper="gir"
+hostmakedepends="glib-devel help2man pkg-config"
 makedepends="fribidi-devel harfbuzz-devel libXft-devel libthai-devel"
 short_desc="Library for layout and rendering of text"
 maintainer="Juan RP <xtraeme@voidlinux.org>"
@@ -16,10 +16,7 @@ checksum=1d2b74cd63e8bd41961f2f8d952355aa0f9be6002b52c8aa7699d9f5da597c9d
 
 # Package build options
 build_options="gir"
-# Disable gir for cross builds.
-if [ -z "$CROSS_BUILD" ]; then
-	build_options_default="gir"
-fi
+build_options_default="gir"
 
 post_install() {
    rm -rf -- "${DESTDIR}"/usr/share/installed-tests

Build and list gir files:

$ xbps-src -f -a aarch64 pkg pango
=> Registering new packages to /host/binpkgs/gobject-introspection
index: added `pango-1.42.4_1' (aarch64).
index: added `pango-devel-1.42.4_1' (aarch64).
index: added `pango-view-1.42.4_1' (aarch64).
index: added `pango-xft-1.42.4_1' (aarch64).
index: 15 packages registered.
$ env XBPS_TARGET_ARCH=aarch64 xls pango{,-devel} | grep -E '.(gir|typelib)'
/usr/lib/girepository-1.0/Pango-1.0.typelib
/usr/lib/girepository-1.0/PangoCairo-1.0.typelib
/usr/lib/girepository-1.0/PangoFT2-1.0.typelib
/usr/share/gir-1.0/Pango-1.0.gir
/usr/share/gir-1.0/PangoCairo-1.0.gir
/usr/share/gir-1.0/PangoFT2-1.0.gir
/usr/share/gir-1.0/PangoXft-1.0.gir

dep: atk

Last one required!

diff --git a/srcpkgs/atk/template b/srcpkgs/atk/template
index 7cb2c212cb..2531e3e127 100644
--- a/srcpkgs/atk/template
+++ b/srcpkgs/atk/template
@@ -3,8 +3,9 @@ pkgname=atk
 version=2.30.0
 revision=1
 build_style=meson
+build_helper="gir"
 configure_args="-Dintrospection=$(vopt_if gir true false)"
-hostmakedepends="pkg-config glib-devel $(vopt_if gir gobject-introspection)"
+hostmakedepends="pkg-config glib-devel"
 makedepends="libglib-devel"
 short_desc="Set of interfaces for accessibility"
 maintainer="Juan RP <xtraeme@voidlinux.org>"
@@ -15,11 +16,7 @@ checksum=dd4d90d4217f2a0c1fee708a555596c2c19d26fef0952e1ead1938ab632c027b
 
 # Package build options
 build_options="gir"
-
-# Disable gir for cross builds.
-if [ -z "$CROSS_BUILD" ]; then
-	build_options_default="gir"
-fi
+build_options_default="gir"
 
 atk-devel_package() {
    depends="${makedepends} atk>=${version}_${revision}"
$ xbps-src -f -a aarch64 pkg atk
=> Registering new packages to /host/binpkgs/gobject-introspection
index: added `atk-2.30.0_1' (aarch64).
index: added `atk-devel-2.30.0_1' (aarch64).
index: 17 packages registered.
$ env XBPS_TARGET_ARCH=aarch64 xls pango{,-devel} | grep -E '.(gir|typelib)'
/usr/lib/girepository-1.0/Atk-1.0.typelib
/usr/share/gir-1.0/Atk-1.0.gir

All done, now for gtk+3 itself!

gtk+3

A very complex template, a maze of conditionals.

diff --git a/srcpkgs/gtk+3/template b/srcpkgs/gtk+3/template
index 1a09502be3..b2315300d0 100644
--- a/srcpkgs/gtk+3/template
+++ b/srcpkgs/gtk+3/template
@@ -4,6 +4,7 @@ version=3.24.4
 revision=1
 wrksrc="gtk+-${version}"
 build_style=gnu-configure
+build_helper="gir"
 #XXX broken configure script: Can't use vopt_enable cloudproviders, configure
 #checks for libcloudproviders when we pass '--disable-cloudproviders' to it!
 configure_args="--disable-schemas-compile
@@ -12,9 +13,8 @@ configure_args="--disable-schemas-compile
  $(vopt_enable wayland wayland-backend) $(vopt_enable x11 x11-backend)
  $(vopt_if cloudproviders '--enable-cloudproviders')"
 conf_files="/etc/gtk-3.0/im-multipress.conf"
-hostmakedepends="gettext-devel glib-devel gobject-introspection gtk-doc
- gtk-update-icon-cache pkg-config perl
- $(vopt_if wayland 'wayland-devel wayland-protocols')"
+hostmakedepends="gettext-devel glib-devel pkg-config perl gtk-doc
+ gtk-update-icon-cache $(vopt_if wayland 'wayland-devel wayland-protocols')"
 makedepends="at-spi2-atk-devel gdk-pixbuf-devel libepoxy-devel pango-devel
  iso-codes $(vopt_if colord 'colord-devel') $(vopt_if cups 'cups-devel')
  $(vopt_if wayland 'libxkbcommon-devel wayland-devel wayland-protocols MesaLib-devel')
@@ -35,12 +35,7 @@ desc_option_broadway="Enable support for the HTML5 Broadway backend"
 desc_option_cloudproviders="Enable integration with cloudproviders, such as Nextcloud"
 
 # Enable all options (other than cloudproviders) by default.
-build_options_default="colord cups broadway wayland x11"
-
-# Enable gir only for native builds.
-if [ -z "$CROSS_BUILD" ]; then
-	build_options_default+=" gir"
-fi
+build_options_default="colord cups broadway wayland x11 gir"
 
 do_check() {
    # Requires xserver running

Seems to be all right.

$ xbps-src -f -a aarch64 pkg gtk+3
=> Registering new packages to /host/binpkgs/gobject-introspection
index: added `gtk+3-3.24.4_1' (aarch64).
index: added `gtk+3-demo-3.24.4_1' (aarch64).
index: added `gtk+3-devel-3.24.4_1' (aarch64).
index: 20 packages registered.
$ env XBPS_TARGET_ARCH=aarch64 xls gtk+3{,-devel} | grep -E '.(gir|typelib)'
/usr/lib/girepository-1.0/Gdk-3.0.typelib
/usr/lib/girepository-1.0/GdkX11-3.0.typelib
/usr/lib/girepository-1.0/Gtk-3.0.typelib
/usr/share/gir-1.0/Gdk-3.0.gir
/usr/share/gir-1.0/GdkX11-3.0.gir
/usr/share/gir-1.0/Gtk-3.0.gir

Ah all nice.

$ python3
>>> import platform
>>> platform.platform()
'Linux-4.14.97_1-aarch64-with-glibc2.17'
>>> import gi
>>> gi.require_version("Gtk", "3.0")
>>>

Loose ends

Not all is rosy when gir and cross compilation meet, but we got very far, regardless there are still fixes to do.

Meson

Meson is still not complete, some packages like gdk-pixbuf and atk compile just fine. Others like libgusb, less so:

diff --git a/srcpkgs/libgusb/template b/srcpkgs/libgusb/template
index c2358c1125..00bcfdb940 100644
--- a/srcpkgs/libgusb/template
+++ b/srcpkgs/libgusb/template
@@ -1,13 +1,14 @@
 # Template file for 'libgusb'
 pkgname=libgusb
 version=0.3.0
-revision=3
+revision=4
 build_style=meson
+build_helper="gir"
 configure_args="-Ddocs=false -Dgir=$(vopt_if gir true false)
  -Dvapi=$(vopt_if vala true false)"
-hostmakedepends="pkg-config $(vopt_if gir gobject-introspection)
- $(vopt_if vala vala)"
-makedepends="libglib-devel libusb-devel libgudev-devel $(vopt_if vala vala-devel) usbutils"
+hostmakedepends="pkg-config $(vopt_if vala vala)"
+makedepends="libglib-devel libusb-devel libgudev-devel $(vopt_if vala vala-devel)
+ usbutils"
 short_desc="GLib wrapper around libusb1"
 maintainer="Juan RP <xtraeme@voidlinux.org>"
 license="LGPL-2.1-or-later"
@@ -16,8 +17,9 @@ distfiles="http://people.freedesktop.org/~hughsient/releases/${pkgname}-${versio
 checksum=d8e7950f99b6ae4c3e9b8c65f3692b9635289e6cff8de40c4af41b2e9b348edc
 
 build_options="gir vala"
+build_options_default="gir"
 if [ -z "$CROSS_BUILD" ]; then
-	build_options_default="gir vala"
+	build_options_default+=" vala"
 fi
 
 libgusb-devel_package() {

Looks good to me.

$ xbps-src -f -a aarch64 pkg libgusb
[8/20] Generating GUsb-1.0.gir with a custom command.
FAILED: gusb/GUsb-1.0.gir
/usr/aarch64-linux-gnu/usr/bin/g-ir-scanner -pthread -I/usr/aarch64-linux-gnu/usr/include/gobject-introspection-1.0 -I/usr/aarch64-linux-gnu/usr/include/glib-2.0 -I/usr/aarch64-linux-gnu/usr/lib/glib-2.0/include --no-libtool --namespace=GUsb --nsversion=1.0 --warn-all --output gusb/GUsb-1.0.gir --c-include=gusb.h -I/builddir/libgusb-0.3.0/gusb -I/builddir/libgusb-0.3.0/build/gusb -I./. -I../. -I./gusb/. -I../gusb/. --filelist=/builddir/libgusb-0.3.0/build/gusb/bf6bc9e@@gusb@sha/GUsb_1.0_gir_filelist -L/builddir/libgusb-0.3.0/build/gusb --extra-library=gusb --include=Gio-2.0 --include=GObject-2.0 --symbol-prefix=g_usb --identifier-prefix=GUsb --pkg-export=gusb --cflags-begin -I./. -I../. -I./gusb/. -I../gusb/. -I/usr/aarch64-linux-gnu/usr/include/libmount -I/usr/aarch64-linux-gnu/usr/include/blkid -I/usr/aarch64-linux-gnu/usr/include/uuid -I/usr/aarch64-linux-gnu/usr/include/glib-2.0 -I/usr/aarch64-linux-gnu/usr/lib/glib-2.0/include -I/usr/aarch64-linux-gnu/usr/include/libusb-1.0 -D_FORTIFY_SOURCE=2 -I/usr/aarch64-linux-gnu/usr/include --cflags-end --library gusb -L/builddir/libgusb-0.3.0/build/gusb -L/usr/aarch64-linux-gnu/usr/lib --extra-library=gio-2.0 --extra-library=gobject-2.0 --extra-library=glib-2.0 --extra-library=usb-1.0
In file included from /builddir/libgusb-0.3.0/build/g-ir-cpp-se2xrgqu.c:4:
/builddir/libgusb-0.3.0/build/gusb/gusb-version.h:31:2: error: #error "Only <gusb.h> can be included directly."
 #error "Only <gusb.h> can be included directly."
  ^~~~~
In file included from /usr/aarch64-linux-gnu/usr/include/bits/libc-header-start.h:33,
                 from /usr/aarch64-linux-gnu/usr/include/limits.h:26,
                 from /usr/lib/gcc/aarch64-linux-gnu/8.2.0/include-fixed/limits.h:194,
                 from /usr/lib/gcc/aarch64-linux-gnu/8.2.0/include-fixed/syslimits.h:7,
                 from /usr/lib/gcc/aarch64-linux-gnu/8.2.0/include-fixed/limits.h:34,
                 from /usr/aarch64-linux-gnu/usr/lib/glib-2.0/include/glibconfig.h:11,
                 from /usr/aarch64-linux-gnu/usr/include/glib-2.0/glib/gtypes.h:32,
                 from /usr/aarch64-linux-gnu/usr/include/glib-2.0/glib/galloca.h:32,
                 from /usr/aarch64-linux-gnu/usr/include/glib-2.0/glib.h:30,
                 from /usr/aarch64-linux-gnu/usr/include/glib-2.0/gobject/gbinding.h:28,
                 from /usr/aarch64-linux-gnu/usr/include/glib-2.0/glib-object.h:23,
                 from /builddir/libgusb-0.3.0/gusb/gusb-context.h:25,
                 from /builddir/libgusb-0.3.0/gusb/gusb-autocleanups.h:24,
                 from /builddir/libgusb-0.3.0/build/g-ir-cpp-se2xrgqu.c:5:
/usr/aarch64-linux-gnu/usr/include/features.h:381:4: warning: #warning _FORTIFY_SOURCE requires compiling with optimization (-O) [-Wcpp]
 #  warning _FORTIFY_SOURCE requires compiling with optimization (-O)
    ^~~~~~~
Traceback (most recent call last):
  File "/usr/lib/python3.6/distutils/unixccompiler.py", line 107, in preprocess
    self.spawn(pp_args)
  File "/usr/lib/python3.6/distutils/ccompiler.py", line 909, in spawn
    spawn(cmd, dry_run=self.dry_run)
  File "/usr/lib/python3.6/distutils/spawn.py", line 36, in spawn
    _spawn_posix(cmd, search_path, dry_run=dry_run)
  File "/usr/lib/python3.6/distutils/spawn.py", line 159, in _spawn_posix
    % (cmd, exit_status))
distutils.errors.DistutilsExecError: command 'aarch64-linux-gnu-cpp' failed with exit status 1

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/bin/g-ir-scanner.wrapped", line 106, in <module>
    sys.exit(scanner_main(sys.argv))
  File "/usr/lib/gobject-introspection/giscanner/scannermain.py", line 555, in scanner_main
    ss = create_source_scanner(options, args)
  File "/usr/lib/gobject-introspection/giscanner/scannermain.py", line 459, in create_source_scanner
    ss.parse_files(filenames)
  File "/usr/lib/gobject-introspection/giscanner/sourcescanner.py", line 262, in parse_files
    self._parse(headers)
  File "/usr/lib/gobject-introspection/giscanner/sourcescanner.py", line 307, in _parse
    self._cpp_options)
  File "/usr/lib/gobject-introspection/giscanner/ccompiler.py", line 211, in preprocess
    extra_postargs=extra_postargs)
  File "/usr/lib/python3.6/distutils/unixccompiler.py", line 109, in preprocess
    raise CompileError(msg)
distutils.errors.CompileError: command 'aarch64-linux-gnu-cpp' failed with exit status 1
ninja: build stopped: subcommand failed.
=> ERROR: libgusb-0.3.0_4: do_build: '${make_cmd} -C ${meson_builddir} ${makejobs} ${make_build_args} ${make_build_target}' exited with 1
=> ERROR:   in do_build() at common/build-style/meson.sh:125

The cause is known, -DGUSB_COMPILATION is lost somewhere along the way. Causing the header to #error out.

musl

musl is very important to Void Linux so it is very nice to have gir be cross compilable to it.

But there are some problems to be debugged and corrected:

$ xbps-src -f -a aarch64-musl pkg gtk+3
FAILED: gdk-pixbuf/GdkPixbuf-2.0.gir
/usr/aarch64-linux-musl/usr/bin/g-ir-scanner -pthread -I/usr/aarch64-linux-musl/usr/include/gobject-introspection-1.0 -I/usr/aarch64-linux-musl/usr/include/glib-2.0 -I/usr/aarch64-linux-musl/usr/lib/glib-2.0/include --no-libtool --namespace=GdkPixbuf --nsversion=2.0 --warn-all --output gdk-pixbuf/GdkPixbuf-2.0.gir --c-include=gdk-pixbuf/gdk-pixbuf.h --quiet -DGDK_PIXBUF_COMPILATION -I/builddir/gdk-pixbuf-2.38.0/gdk-pixbuf -I/builddir/gdk-pixbuf-2.38.0/build/gdk-pixbuf -I./. -I../. -I./gdk-pixbuf/pixops -I../gdk-pixbuf/pixops --filelist=/builddir/gdk-pixbuf-2.38.0/build/gdk-pixbuf/f4c7bd6@@gdk_pixbuf-2.0@sha/GdkPixbuf_2.0_gir_filelist --include=GModule-2.0 --include=Gio-2.0 --symbol-prefix=gdk --identifier-prefix=Gdk --pkg-export=gdk-pixbuf-2.0 --cflags-begin -I./. -I../. -I./gdk-pixbuf/pixops -I../gdk-pixbuf/pixops -I/usr/aarch64-linux-musl/usr/include/glib-2.0 -I/usr/aarch64-linux-musl/usr/lib/glib-2.0/include -I/usr/aarch64-linux-musl/usr/include/libmount -I/usr/aarch64-linux-musl/usr/include/blkid -I/usr/aarch64-linux-musl/usr/include/uuid -D_FORTIFY_SOURCE=2 -I/usr/aarch64-linux-musl/usr/include --cflags-end --library gdk_pixbuf-2.0 -L/builddir/gdk-pixbuf-2.38.0/build/gdk-pixbuf -L/usr/aarch64-linux-musl/usr/lib --extra-library=m --extra-library=gobject-2.0 --extra-library=glib-2.0 --extra-library=gmodule-2.0 --extra-library=gio-2.0
Error relocating /usr/aarch64-linux-musl/usr/lib/libz.so.1: unsupported relocation type 1026
Error relocating /usr/aarch64-linux-musl/usr/lib/libz.so.1: unsupported relocation type 1027
Error relocating /usr/aarch64-linux-musl/usr/lib/libz.so.1: unsupported relocation type 1025
collect2: error: ld returned 127 exit status
linking of temporary binary failed: Command '['aarch64-linux-musl-gcc', '-o', '/builddir/gdk-pixbuf-2.38.0/build/tmp-introspectxv6j_hsz/GdkPixbuf-2.0', '-fstack-clash-protection', '-D_FORTIFY_SOURCE=2', '-O2', '-pipe', '-march=armv8-a', '-I/usr/aarch64-linux-musl/usr/include', '/builddir/gdk-pixbuf-2.38.0/build/tmp-introspectxv6j_hsz/GdkPixbuf-2.0.o', '-L.', '-Wl,-rpath,.', '-Wl,--no-as-needed', '-L/builddir/gdk-pixbuf-2.38.0/build/gdk-pixbuf', '-Wl,-rpath,/builddir/gdk-pixbuf-2.38.0/build/gdk-pixbuf', '-L/usr/aarch64-linux-musl/usr/lib', '-Wl,-rpath,/usr/aarch64-linux-musl/usr/lib', '-lgdk_pixbuf-2.0', '-lm', '-lgobject-2.0', '-lglib-2.0', '-lgmodule-2.0', '-lgio-2.0', '-lgio-2.0', '-lgobject-2.0', '-Wl,--export-dynamic', '-lgmodule-2.0', '-pthread', '-lglib-2.0', '-Wl,-z,relro', '-Wl,-z,now', '-Wl,--as-needed', '-L/usr/aarch64-linux-musl/usr/lib']' returned non-zero exit status 1.
ninja: build stopped: subcommand failed.
=> ERROR: gdk-pixbuf-2.38.0_2: do_build: '${make_cmd} -C ${meson_builddir} ${makejobs} ${make_build_args} ${make_build_target}' exited with 1
=> ERROR:   in do_build() at common/build-style/meson.sh:125

O-oh...

Closing it out

As this post is published on my site i expect the pull request enabling gir cross to be merged, and i will probably be dealing with the inevitable fallout of any big change.

This has been a very nice journey and there is a few groups and people i would like to thank:

Yocto Project and Buildroot

2 Amazing projects dealing with Embedded Linux

Without their patches for the gobject-introspection package, wrappers this project would have taken probably 3 or 4 times as much time as it took.

Their prelink package (which is in Void as prelink-cross) is vital for all this to work.

Enno "Gottox" Boland

Core, long-time Void Linux developer

While musl cross doesn't work at the moment, a first step need to be made and Gottox's work on making prelink-cross compile on musl is much appreciated.

Rasmus "Cogitri" Thomsen

Core Exherbo developer, Void Linux contributor

Gracefully lent his VPS for building packages and ran commands i requested on his native aarch64 machine to test that it works.

If you feel you have helped me along the way with suggestions feel free to contact me on GitHub and i will include you here.

Final Thoughts

I'm satisfied with the current state, all arches that use glibc now have gir and musl is on the works.

GG.