Debugging across GLib and GTK requires a consistent dependency graph. If GTK
loads a development libgtk but resolves GLib, Pango, or GDK Pixbuf from the
host, source lines and runtime types no longer correspond to one coherent
build. An isolated installation prefix provides that consistency without
replacing distribution packages.
Choose the Isolation Boundary #
GNOME recommends Builder for application development and Toolbx for libraries, services, and host-integrated modules. JHBuild remains available for building a dependency graph. Those tools encode more of the graph than a hand-written script and should be preferred for sustained contribution work.
A manual Meson/Ninja prefix remains useful for source inspection or a small, controlled stack. Use it inside a container or disposable development environment when possible. The prefix isolates installed files, while the container isolates build tools and dependencies that still come from the distribution.
GLib, GObject, GModule, GThread, and GIO are built from the same
GLib repository; GObject is not a
separate source checkout. GTK is a separate repository. GTK’s exact dependency
set varies with its enabled Wayland, X11, Vulkan, documentation, and
introspection features, so each checkout’s README.md, meson_options.txt,
and Meson error messages are authoritative for that revision.
Install the distribution’s compiler, Python, Meson, Ninja, pkg-config, and
the development headers needed to bootstrap the graph. Documentation builds
may additionally require tools such as gi-docgen, Graphviz, and Doxygen; they
are not required for every GLib or GTK configuration.
Define One Development Prefix #
Keep source, build directories, and installed artifacts separate:
An activation script makes all discovery paths refer to the same prefix:
#!/usr/bin/env bash
ROOT=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
PREFIX="$ROOT/prefix"
export PREFIX
export PATH="$PREFIX/bin:$PATH"
export PKG_CONFIG_PATH="$PREFIX/lib/pkgconfig:$PREFIX/share/pkgconfig"
export LD_LIBRARY_PATH="$PREFIX/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
export GI_TYPELIB_PATH="$PREFIX/lib/girepository-1.0"
export GIO_EXTRA_MODULES="$PREFIX/lib/gio/modules"
export GSETTINGS_SCHEMA_DIR="$PREFIX/share/glib-2.0/schemas"
export XDG_DATA_DIRS="$PREFIX/share${XDG_DATA_DIRS:+:$XDG_DATA_DIRS}"Setting --libdir=lib during every Meson setup makes these paths independent
of a distribution’s lib64 or multiarch default. PKG_CONFIG_PATH searches
the prefix first but still permits host fallback. After the entire required
dependency closure has been installed into the prefix, strict builds can
replace it with:
export PKG_CONFIG_LIBDIR="$PREFIX/lib/pkgconfig:$PREFIX/share/pkgconfig"
unset PKG_CONFIG_PATHPKG_CONFIG_LIBDIR suppresses pkg-config’s standard search directories.
Enabling it too early also hides bootstrap libraries such as libffi, PCRE2,
FreeType, and Fontconfig, so either build those into the prefix or use a
container with an explicitly documented host baseline.
Do not place this activation in a login profile. LD_LIBRARY_PATH applies to
every process started from the shell and can make unrelated host applications
load ABI-incompatible development libraries.
Build in Dependency Order #
Build leaves before roots because Meson resolves dependencies when configuring each build directory. A GTK stack commonly includes the following groups:
- GLib’s bootstrap dependencies, including libffi and PCRE2.
- GLib, which produces GLib, GObject, and GIO together.
- Graphics and text dependencies such as pixman/Cairo, FreeType, Fontconfig, HarfBuzz, GDK Pixbuf, Pango, Graphene, and libepoxy.
- Backend dependencies, such as Wayland and wayland-protocols or the required X11 libraries.
- GTK, followed by language bindings such as gtkmm when the project needs them.
Dependency edges within the middle groups depend on selected features. A topological module set from JHBuild or GNOME Build Metadata is safer than copying a fixed list across releases.
The same build function can be used for each Meson project:
build_one()
{
source_dir=$1
build_dir=$2
meson setup "$build_dir" "$source_dir" \
--prefix="$PREFIX" \
--libdir=lib \
-Ddebug=true \
-Doptimization=0 \
-Dstrip=false
meson compile -C "$build_dir"
meson test -C "$build_dir" --print-errorlogs
meson install -C "$build_dir"
}Run the function from a shell that has sourced env.sh:
mkdir -p "$PREFIX"
build_one "$PWD/src/glib" "$PWD/src/glib/_build"
build_one "$PWD/src/gtk" "$PWD/src/gtk/_build"The two calls show the endpoints, not a complete GTK build plan. Invoke the same function for every source-built dependency between GLib and GTK in the module set’s topological order.
Meson’s debug build type currently corresponds to debug information with
optimization level 0, but spelling out debug, optimization, and strip
makes the intended debugger behavior visible. Individual projects can add
feature options after inspecting meson configure _build; disabling an
unneeded backend is preferable to accidentally satisfying it from the host.
Installing each completed layer matters because the next project finds its
headers, .pc files, shared objects, schemas, and introspection data under one
prefix. Compile installed schemas after installing a project that provides
them:
glib-compile-schemas "$PREFIX/share/glib-2.0/schemas"Prevent Mixed-System Builds #
Check dependency resolution immediately after Meson setup:
pkg-config --variable=prefix glib-2.0
pkg-config --variable=prefix gtk4
meson introspect src/gtk/_build --dependenciesBoth pkg-config prefix queries should print the development prefix. Meson’s setup log must also show prefix paths for Pango, GDK Pixbuf, Graphene, and other libraries intended to be under source control.
Check the final ELF resolution rather than trusting link-time output:
ldd "$PREFIX/bin/gtk4-demo"
readelf -d "$PREFIX/bin/gtk4-demo" | grep -E 'NEEDED|RPATH|RUNPATH'All libraries in the source-built closure should resolve below $PREFIX.
System C libraries, the dynamic loader, GPU drivers, and deliberately retained
bootstrap dependencies may still resolve from the host. Record that boundary
instead of claiming complete isolation.
An rpath can make binaries usable without LD_LIBRARY_PATH:
export LDFLAGS="-Wl,-rpath,$PREFIX/lib"Use the comma-separated linker form shown above. An rpath only changes runtime
loading; it does not influence which headers or .pc metadata Meson selected.
pkg-config inspection is still required.
Make Updates Repeatable #
Clone each upstream repository once:
git clone https://gitlab.gnome.org/GNOME/glib.git src/glib
git clone https://gitlab.gnome.org/GNOME/gtk.git src/gtkApply the same pattern to source-built dependencies from the selected module set. Subsequent updates should not delete untracked work:
git -C src/glib fetch --prune
git -C src/glib switch main
git -C src/glib pull --ff-only
git -C src/gtk fetch --prune
git -C src/gtk switch main
git -C src/gtk pull --ff-onlyFor each dependency, record the tested commit in a lock file or build log.
“Current source” changes continuously; commit IDs make a successful graph
reproducible. Update leaves first, then rebuild and install dependents. Use
meson setup --reconfigure when only options or detected dependencies changed,
and meson setup --wipe only for a generated build directory when cached
state must be discarded.
Avoid git clean -fdx in update scripts because it destroys untracked source
work. Build directories already provide a replaceable boundary.
Debug the Installed Graph #
Run the debugger from the activated environment:
gdb --args "$PREFIX/bin/gtk4-demo"Useful initial checks are:
(gdb) set breakpoint pending on
(gdb) break g_main_context_dispatch
(gdb) break gtk_widget_snapshot
(gdb) run
(gdb) info sharedlibraryinfo sharedlibrary should show the intended prefix. If a breakpoint resolves
to a system path, inspect LD_DEBUG=libs, ldd, pkg-config prefixes, and
Meson’s dependency report before debugging application logic. Source lookup
can be corrected with GDB’s directory or set substitute-path, but path
substitution cannot repair a binary linked against the wrong library.
The update workflow is therefore deterministic:
- Activate the environment.
- Fetch pinned or selected commits.
- Reconfigure, build, test, and install in dependency order.
- Recompile schemas and any generated loader caches required by the stack.
- Audit pkg-config and ELF resolution.
- Start the debugger only after the graph passes those checks.
Comments
Read or comment on GitHub (sign-in required to comment)