Statically linked Gtk4 - without AppImage, Flatpak or Snap :)

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!

1 Like

Updated in gdk/meson.build

You should add if else statement in gdk_f16c too because gtk4 latest is 4.21.1 and statical linked whole gtk4 runtime and 3 dynamically linked for OpenGL and I will add improvement for Vulkan loader without Glib-Warning if you use in Dotnet NativeAot but it is really impossible to fix that’s why I need to create library for loading Vulkan library.

Called gvk ( Gnome Vulkan ) and I packed statical linked gtk4 runtime with required deps into Dotnet = it has size over 73Mb.

No problem for everything! If you use gcc or clang then you should use -std=c11 if icu libraries already packed in portable linux application.

I am working hard on Gtk4, Glib-2.0, Cairo,Pango and any deps.

Warning don’t use -static-pie if you open portable linux application replies

Segfault dumped.

Please use -Wl,-Bstatic … -Wl,-Bdynamic.

thanks for understanding me!

Hello everyone, that is proof because I would like to show why I am working with statically linked runtime ( Gtk 4, Glib-2.0, GdkPixBuf-2, Pango-1 and Cairo with hidden dependencies like FreeType, XML2, and etc )

Please allow me to make portable linux applications for stressed users if they have big problem with build instructions and installation if some distros don’t have prepared versions. That is why :slight_smile:

I would love to teach how do you want to get statically linked runtime.
I made with Dotnet 10.x RC 1 and Gtk4, Glib2, GdkPixBuf, Cairo and Pango full statically and dynamically linked in custom directory /opt/Gtk4Latest if I run for testing like JIT and statically linked as NativeAOT.

Thanks for understanding me!

I mean, nobody is going to stop you. As long as you follow the terms of the license, you’re free to recompile and redistribute GTK applications with statically linked libraries.
So, you can create your own website where you offer such a thing for download.

That being said:
Its unlikely that GNOME itself will do anything into this direction.

I mean, this is in many ways similar to AppImages and shares their problems. Like how do you maintain the applications and ensure updates? Or the fact that libraries will not be de-duplicated, when every application carries their own statically linked libraries with them. Or the fact that “portable” applications rarely integrate well into the system.

Add to that the fact that GNOME itself generally only releases the source code tarballs, no pre-build binaries. While for the binaries of the Flatpaks the GNOME team serves as official maintainers, in most other cases the final compilation and distribution of the binaries is done by, well, the distributions, not GNOME.

1 Like