Hello everyone,
I have worked hard with modified version - I have added some option for meson.options
Please make sure when you already downloaded gtk from repository!
Open gtk/meson.options and add end of context!
option('build_static_gtk',
type: 'boolean',
value: false,
description: 'Build GTK as a static library')
Save it! And open gtk/gtk/meson.build and find libgtk_static replace with:
if get_option('build_static_gtk')
libgtk_static = static_library('gtk',
sources: [typefuncs, gtk_sources, gtkmarshal_h, gtkprivatetypebuiltins_h, xdp_dbus_generated],
c_args: gtk_cargs + print_backend_cflags + common_cflags,
include_directories: [confinc, gdkinc, gskinc, gtkinc],
dependencies: gtk_deps + [libgtk_css_dep, libgdk_dep, libgsk_dep, media_deps, print_backend_deps],
link_with: [libgtk_css, libgdk, libgsk ],
install: true,
)
else
libgtk_static = static_library('gtk',
sources: [typefuncs, gtk_sources, gtkmarshal_h, gtkprivatetypebuiltins_h, xdp_dbus_generated],
c_args: gtk_cargs + print_backend_cflags + common_cflags,
include_directories: [confinc, gdkinc, gskinc, gtkinc],
dependencies: gtk_deps + [libgtk_css_dep, libgdk_dep, libgsk_dep, media_deps, print_backend_deps],
link_with: [libgtk_css, libgdk, libgsk ],
)
endif
if get_option('build_static_gtk')
libgtk = static_library('gtk-4',
sources: [typefuncs, gtk_sources, gtkmarshal_h, gtkprivatetypebuiltins_h, xdp_dbus_generated],
c_args: gtk_cargs + print_backend_cflags + common_cflags,
include_directories: [confinc, gdkinc, gskinc, gtkinc],
dependencies: gtk_deps + [libgtk_css_dep, libgdk_dep, libgsk_dep, media_deps, print_backend_deps],
link_with: [libgtk_css, libgdk, libgsk],
install: true,
)
else
libgtk = shared_library('gtk-4',
c_args: gtk_cargs + common_cflags,
include_directories: [confinc, gdkinc, gskinc, gtkinc],
dependencies: gtk_deps + [libgtk_css_dep, libgdk_dep, libgsk_dep],
link_whole: [libgtk_css, libgdk, libgsk],
link_args: common_ldflags,
soversion: gtk_soversion,
version: gtk_library_version,
darwin_versions: darwin_versions,
gnu_symbol_visibility: 'hidden',
)
endif
Save it, open gtk/gtk/css/meson.build and find libgtk_css = static and replace with:
if get_option('build_static_gtk')
libgtk_css = static_library('gtk_css',
sources: [
gtk_css_public_sources,
gtk_css_private_sources,
gtk_css_enums,
gdkversionmacros_h,
gdk_visibility_h,
],
dependencies: gtk_css_deps,
include_directories: [ confinc, ],
c_args: [
'-DGTK_COMPILATION',
'-DG_LOG_DOMAIN="Gtk"',
] + common_cflags,
install: true,
)
else
libgtk_css = static_library('gtk_css',
sources: [
gtk_css_public_sources,
gtk_css_private_sources,
gtk_css_enums,
gdkversionmacros_h,
gdk_visibility_h,
],
dependencies: gtk_css_deps,
include_directories: [ confinc, ],
c_args: [
'-DGTK_COMPILATION',
'-DG_LOG_DOMAIN="Gtk"',
] + common_cflags
)
endif
Save it, open gtk/gsk/meson.build and find libgsk = static and replace with:
if get_option('build_static_gtk')
libgsk = static_library('gsk',
sources: [
gsk_public_sources,
gsk_private_sources,
gsk_enums,
gskresources,
gsk_private_vulkan_shader_headers,
gsk_private_gpu_shader_headers,
],
dependencies: gsk_deps,
include_directories: [ confinc, ],
c_args: [
'-DGTK_COMPILATION',
'-DG_LOG_DOMAIN="Gsk"',
'-DG_LOG_STRUCTURED=1',
] + common_cflags,
link_with: [ libgdk, libgsk_f16c],
install: true,
)
else
libgsk = static_library('gsk',
sources: [
gsk_public_sources,
gsk_private_sources,
gsk_enums,
gskresources,
gsk_private_vulkan_shader_headers,
gsk_private_gpu_shader_headers,
],
dependencies: gsk_deps,
include_directories: [ confinc, ],
c_args: [
'-DGTK_COMPILATION',
'-DG_LOG_DOMAIN="Gsk"',
'-DG_LOG_STRUCTURED=1',
] + common_cflags,
link_with: [ libgdk, libgsk_f16c],
)
endif
Save it, open gtk/gdk/meson.build, find libgdk = static and replace with:
if get_option('build_static_gtk')
libgdk = static_library('gdk',
sources: [gdk_sources, gdk_backends_gen_headers, gdk_gen_headers],
dependencies: gdk_deps + [libgtk_css_dep],
link_with: [libgtk_css],
include_directories: [confinc, gdkx11_inc, wlinc],
c_args: libgdk_c_args + common_cflags,
link_whole: gdk_backends,
install: true,
)
else
libgdk = static_library('gdk',
sources: [gdk_sources, gdk_backends_gen_headers, gdk_gen_headers],
dependencies: gdk_deps + [libgtk_css_dep],
link_with: [libgtk_css],
include_directories: [confinc, gdkx11_inc, wlinc],
c_args: libgdk_c_args + common_cflags,
link_whole: gdk_backends,
)
endif
Save and run meson from python 3 via virtual environment ( venv )
For X11:
meson setup build-gnu-static --prefix=/opt/Gtk4Latest -Ddefault_library=shared -Ddocumentation=false --reconfigure -Dsysprof=disabled -Dwayland-backend=false -Ddebug=false -Dbuild-tests=false -Dbuild-examples=false -Dx11-backend=true -Dbuild-testsuite=false -Dbuild-demos=false -Dbuild_static_gtk=true
For Wayland:
meson setup build-gnu-static --prefix=/opt/Gtk4Latest -Ddefault_library=shared -Ddocumentation=false --reconfigure -Dsysprof=disabled -Dwayland-backend=true -Ddebug=false -Dbuild-tests=false -Dbuild-examples=false -Dx11-backend=false -Dbuild-testsuite=false -Dbuild-demos=false -Dbuild_static_gtk=true
And compile + install
meson compile -C build-gnu-static -j32
meson install -C build-gnu-static
And I tested it with g++ because libicu* is required for c++ that is why it works fine for me.
Example-0 from Getting Started with Gtk4
#include <gtk/gtk.h>
static void
activate (GtkApplication *app,
gpointer user_data)
{
(void)user_data; // <- Need to pass because compiler shows warning message
GtkWidget *window;
window = gtk_application_window_new (app);
gtk_window_set_title (GTK_WINDOW (window), "Window");
gtk_window_set_default_size (GTK_WINDOW (window), 200, 200);
gtk_window_present (GTK_WINDOW (window));
}
int
main (int argc,
char **argv)
{
GtkApplication *app;
int status;
app = gtk_application_new ("org.gtk.example", G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
status = g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app);
return status;
}
And I create Makefile:
# Compiler
CXX = g++
# Directories
SRC_DIR = src
OBJ_DIR = obj
BIN_DIR = bin
# Source and objects
SOURCES = $(wildcard $(SRC_DIR)/*.c)
OBJECTS = $(patsubst $(SRC_DIR)/%.c,$(OBJ_DIR)/%.o,$(SOURCES))
TARGET = $(BIN_DIR)/example-0
# Compilation flags
CFLAGS = -s -Wall -Wextra -O2 \
-Isrc \
-I/opt/Gtk4Latest/include/gtk-4.0 \
-I/opt/Gtk4Latest/include/glib-2.0 \
-I/opt/Gtk4Latest/lib/x86_64-linux-gnu/glib-2.0/include \
-I/opt/Gtk4Latest/include \
-I/opt/Gtk4Latest/include/gio-unix-2.0 \
-I/usr/include/libmount \
-I/usr/include/blkid \
-I/opt/Gtk4Latest/include/gdk-pixbuf-2.0 \
-I/opt/Gtk4Latest/include/pango-1.0 \
-I/opt/Gtk4Latest/include/harfbuzz \
-I/opt/Gtk4Latest/include/fribidi \
-I/opt/Gtk4Latest/include/graphene-1.0 \
-I/opt/Gtk4Latest/lib/x86_64-linux-gnu/graphene-1.0/include \
-I/opt/Gtk4Latest/include/freetype2 \
-I/opt/Gtk4Latest/include/cairo \
-I/opt/Gtk4Latest/include/pixman-1 \
-I/usr/include/libpng16 \
-pthread -mfpmath=sse -msse -msse2 -mavx2
# Linker flags
LDFLAGS = -s -L/opt/Gtk4Latest/lib/x86_64-linux-gnu \
-Wl,-Bstatic \
-static-libgcc -static-libstdc++ \
-lgtk-4 -lrsvg-2 -lcroco-0.6 -lgtk_css -lgtk -lgdk -lgsk -lgdk_pixbuf-2.0 \
-lpangocairo-1.0 -lpangoft2-1.0 -lpango-1.0 \
-lcairo -lpixman-1 -lcairo-gobject -lharfbuzz -lharfbuzz-subset -lcairo-script-interpreter \
-lfribidi -lgraphene-1.0 -lfontconfig -lfreetype -lbz2 -lbrotlidec -lbrotlicommon \
-lpng16 -lz -lexpat -lepoxy -ltiff -ljpeg -lxml2 \
-lgstgl-1.0 -lgstplay-1.0 -lgsttag-1.0 -lgstvideo-1.0 -lgstpbutils-1.0 \
-lgstbase-1.0 -lgstreamer-1.0 -lgstallocators-1.0 -lgstaudio-1.0 \
-lgio-2.0 -lgobject-2.0 -lgmodule-2.0 -lglib-2.0 -lorc-0.4 -lselinux \
-lpcre2-8 -lffi -licuuc -licudata -lxml2 -lz -llzma -lblkid \
-licuuc -licudata -lpthread -ldrm -lmd -lXfixes -lXdamage -lXinerama -lXrandr \
-lXi -lXcursor -lXrender -lXext -lX11 -lX11-xcb -lxcb -lxcb-render -lxcb-shm \
-lXau -lXdmcp -lstdc++ -lgcc \
-Wl,-Bdynamic -lGL -lEGL -lGLdispatch -lmount -pthread -lvulkan \
-Wl,--pie -pie
# Default target
all: $(BIN_DIR) $(OBJ_DIR) $(TARGET)
@echo "Completed with make. You can run: make run"
# Compiling
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
@echo "CXX $@"
@$(CXX) $(CFLAGS) -c $< -o $@
# Linking
$(TARGET): $(OBJECTS)
@echo "Linked: $@"
@$(CXX) $(OBJECTS) $(LDFLAGS) -o $@
# Create directories if not exist
$(BIN_DIR):
mkdir -p $(BIN_DIR)
$(OBJ_DIR):
mkdir -p $(OBJ_DIR)
# Run the program
run: $(TARGET)
@echo "Starting: $(TARGET)"
@./$(TARGET)
# Clean
clean:
rm -rf $(OBJ_DIR)/*.o $(TARGET)
.PHONY: all clean run
That is all - do not worry about dynamically linked Gtk4 runtime - I expect why do they want to get statically linked Gtk4 + Glib-2.0 runtime. I won’t hurt rules.
PS: For Wayland: You need to link wayland-*** to -Wl,-Bdynamic -lwayland-client etc… if you see how does it pkgconfig –libs Gtk-4 if you use Wayland than you will add all into -Wl,-Bdynamic But it is very important for Cairo ,FreeType or other some libraries have to link statically with X11/XCB
I check like ldd example:
ldd bin/example-0
linux-vdso.so.1 (0x00007694bae00000)
libGL.so.1 => /lib/x86_64-linux-gnu/libGL.so.1 (0x00007694bad5d000)
libEGL.so.1 => /lib/x86_64-linux-gnu/libEGL.so.1 (0x00007694b6fee000)
libmount.so.1 => /lib/x86_64-linux-gnu/libmount.so.1 (0x00007694b6fa1000)
libvulkan.so.1 => /lib/x86_64-linux-gnu/libvulkan.so.1 (0x00007694b6f23000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007694b6e3a000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007694b6c00000)
/lib64/ld-linux-x86-64.so.2 (0x00007694bae02000)
libGLdispatch.so.0 => /lib/x86_64-linux-gnu/libGLdispatch.so.0 (0x00007694b6b48000)
libGLX.so.0 => /lib/x86_64-linux-gnu/libGLX.so.0 (0x00007694b6b15000)
libblkid.so.1 => /lib/x86_64-linux-gnu/libblkid.so.1 (0x00007694b6ada000)
libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007694b6aad000)
libX11.so.6 => /lib/x86_64-linux-gnu/libX11.so.6 (0x00007694b6970000)
libpcre2-8.so.0 => /lib/x86_64-linux-gnu/libpcre2-8.so.0 (0x00007694b68d6000)
libxcb.so.1 => /lib/x86_64-linux-gnu/libxcb.so.1 (0x00007694b68ad000)
libXau.so.6 => /lib/x86_64-linux-gnu/libXau.so.6 (0x00007694bad53000)
libXdmcp.so.6 => /lib/x86_64-linux-gnu/libXdmcp.so.6 (0x00007694b6e32000)
libbsd.so.0 => /lib/x86_64-linux-gnu/libbsd.so.0 (0x00007694b6e1c000)
libmd.so.0 => /lib/x86_64-linux-gnu/libmd.so.0 (0x00007694b689e000)
Eh where is Gtk4 runtime + Cairo, Pango and Glib-2.0 ??? That is statically linked as portable linux application.
And check ls -lh:
-rwxrwxr-x 1 deafman1983 deafman1983 62M Oct 16 12:46 bin/example-0
It is okay because it is smaller than AppImage, Flatpak and Snapd.
And I have tested on my laptop with custom glibc version because my laptop is already installed old version of Ubuntu 22.04.x no worries I already copy dynamically linked libraries to custom glibc version like example
I already downloaded, compiled and installed to /opt/GlibcLatest with Glibc 2.39
And copy important dynamically linked or listed libraries ( see ldd bin/example-0 ) and paste to /opt/GlibcLatest/lib/x86_64-linux-gnu
WARNING: Do not use LD_LIBRARY_PATH because statically linked binary shows segfault dumped.
Make sure you use with ld-linux-x86-64.so.2 from /opt/GlibcLatest/lib:
/opt/GlibcLatest/lib/ld-linux-x86-64.so.2 --library-path /opt/GlibcLatest/lib/x86_64-linux-gnu ./example-0
If you see some Glib-Warning - but they need to LC_ALL or FRONTCONFIG - You need configure with export or upgrade sometimes….
Check out Youtube!
Screenshots
That is all. I hope you accept my idea of statically linked Gtk4 runtimes inside binary.
Happy deploying and don’t need to worry about required installation of very latest version of Gtk4 example you have old Gtk4 version on Ubuntu 24.04.3 Oops your Gtk4 is too old and it feels annoying. That is why I want to support for Gtk4 into portable linux application ( shortness: PLA )
Greeting from Germany!

