static-haskell-nix
static-haskell-nix copied to clipboard
cannot build Haskell programs that depend on gtk+3 statically
I'm trying to build a Haskell executable (Termonad) statically that uses gi-gtk, which is built using haskell-gi and depends on gtk+3
.
It appears to be failing when building systemd
.
Termonad depdends on gi-gtk
, which depends on gtk+3
, which depends on at-spi2-atk
, which depends on dbus
, which depends on systemd
.
I can include the output from the failed systemd
build if it would help.
Alternatively, here is the derivation I am trying to build:
let
survey = import ./survey { normalPkgs = import ./nixpkgs {}; };
termonad-pkg =
{ mkDerivation, adjunctions, base, Cabal, cabal-doctest
, classy-prelude, colour, constraints, containers, data-default
, directory, distributive, doctest, dyre, filepath, focuslist
, genvalidity-containers, genvalidity-hspec, gi-gdk, gi-gio
, gi-glib, gi-gtk, gi-pango, gi-vte, gtk3, haskell-gi-base
, hedgehog, inline-c, lens, libpcre2, mono-traversable
, pretty-simple, QuickCheck, singletons, stdenv, tasty
, tasty-hedgehog, tasty-hspec, template-haskell, text, vte_291
, xml-conduit, xml-html-qq
}:
mkDerivation {
pname = "termonad";
version = "2.0.0.0";
sha256 = "0rprqn5vcvhbqqg0grrmz0ijjpkrprza88la4mbdg6skb34fjsp0";
isLibrary = true;
isExecutable = true;
enableSeparateDataOutput = true;
setupHaskellDepends = [ base Cabal cabal-doctest ];
libraryHaskellDepends = [
adjunctions base classy-prelude colour constraints containers
data-default directory distributive dyre filepath focuslist gi-gdk
gi-gio gi-glib gi-gtk gi-pango gi-vte haskell-gi-base inline-c lens
mono-traversable pretty-simple QuickCheck singletons text
xml-conduit xml-html-qq
];
libraryPkgconfigDepends = [ gtk3 libpcre2 vte_291 ];
executableHaskellDepends = [ base ];
testHaskellDepends = [
base doctest genvalidity-containers genvalidity-hspec hedgehog lens
QuickCheck tasty tasty-hedgehog tasty-hspec template-haskell
];
homepage = "https://github.com/cdepillabout/termonad";
description = "Terminal emulator configurable in Haskell";
license = stdenv.lib.licenses.bsd3;
};
myHaskellPackages =
survey.haskellPackages.override {
overrides = self: super: {
termonad = self.callPackage termonad-pkg {
libpcre2 = survey.pkgs.pcre2;
vte_291 = survey.pkgs.gnome3.vte;
};
gi-gtk = survey.pkgs.haskell.lib.unmarkBroken super.gi-gtk;
gi-vte = survey.pkgs.haskell.lib.unmarkBroken super.gi-vte;
};
};
in
myHaskellPackages.termonad
Note the following:
-
termonad-pkg
is generated usingcabal2nix
. The current version oftermonad
in nixpkgs would probably work too. -
gi-gtk
andgi-vte
(Haskell dependencies of Termonad) are marked broken, probably because they don't compile with the latest version of GTK+3. I'm not worried about this, since this also occurs when building with dynamic linking. (I'm mostly concerned with getting GTK+3 built statically, so that I can work on creating static builds of Termonad). - I'm using the latest HEAD of
static-haskell-nix
, which is commit 761f34bb4b09dd5838f82782c5e56ebfac4039fb.
It is possible this should just be an issue on the nixpkgs
repo. If this is the case, I will move it there.
I'm not super familiar with cross-compiling support in nixpkgs, and I don't really know the relationship between static-haskell-nix
and the pkgsStatic
overlay, but I also tried to build systemd
with pkgsStatic
and pkgsMusl
(where ./nixpkgs/
below is the nixpkgs
submodule in this repo, currently at commit b577340eb5b):
$ nix-build ./nixpkgs -A pkgsMusl.systemd
...
$ nix-build ./nixpkgs -A pkgsStatic.systemd
...
Neither of these worked. All the dependencies for pkgsMusl.systemd
were able to be built, but systemd
itself failed. A bunch of the dependencies for pkgsStatic.systemd
were not able to be built.
Oh, hmm, I found https://github.com/NixOS/nixpkgs/issues/61580, which makes it sound like systemd
can't be built with musl.
I made some progress with this. I was able to get everything building except for one system dependency, VTE:
$ nix-build build-termonad.nix
these derivations will be built:
/nix/store/j4kwsxxzcx34c99zqxi7b9aaw01cm31d-vte-0.56.3.drv
/nix/store/rjjaia4f6fajgd0szail634gdfbf8cwh-gi-gtk-3.0.27.drv
/nix/store/rq0922i46774q7mx7d3zfxfyn7mzziqn-gi-vte-2.91.19.drv
/nix/store/x1wmi30vyzjg7kq5fsj842nrx1i5qqm4-termonad-2.0.0.0.drv
building '/nix/store/j4kwsxxzcx34c99zqxi7b9aaw01cm31d-vte-0.56.3.drv'...
...
vte.cc:3587:53: warning: unused variable ‘wp_str’ [-Wunused-variable]
char const* wp_str = g_unichar_isprint(c) ? c_buf : _vte_debug_sequence_to_string(c_buf, -1);
^~~~~~
vte.cc: In member function ‘void vte::terminal::Terminal::expand_rectangle(cairo_rectangle_int_t&) const’:
vte.cc:8994:31: warning: variable ‘old_rect’ set but not used [-Wunused-but-set-variable]
cairo_rectangle_int_t old_rect = rect;
^~~~~~~~
CXX libvte_2_91_la-vtedraw.lo
vte.cc: In member function ‘void vte::terminal::Terminal::insert_char(gunichar, bool, bool)’:
vte.cc:2908:40: warning: missed loop optimization, the loop counter may overflow [-Wunsafe-loop-optimizations]
while (cell && cell->attr.fragment() && col > 0)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~
vte.cc: In member function ‘GString* vte::terminal::Terminal::get_text(vte::grid::row_t, vte::grid::column_t, vte::grid::row_t, vte::grid::column_t, bool, bool, GArray*)’:
vte.cc:6171:82: warning: missed loop optimization, the loop counter may overflow [-Wunsafe-loop-optimizations]
while ((pcell = _vte_row_data_get (row_data, col))) {
^
CXX libvte_2_91_la-vtegtk.lo
CXX libvte_2_91_la-vteregex.lo
vteregex.cc:109:32: warning: unknown option after ‘#pragma GCC diagnostic’ kind [-Wpragmas]
#pragma GCC diagnostic ignored "-Wcast-function-type"
^~~~~~~~~~~~~~~~~~~~~~
CXX libvte_2_91_la-vterowdata.lo
CXX libvte_2_91_la-vtespawn.lo
CXX libvte_2_91_la-vteseq.lo
CXX libvte_2_91_la-vtestream.lo
CXX libvte_2_91_la-vtetypes.lo
CXX libvte_2_91_la-vteunistr.lo
CXX libvte_2_91_la-vteutils.lo
CXX libvte_2_91_la-widget.lo
widget.cc: In member function ‘void vte::platform::Widget::dispose()’:
widget.cc:134:30: error: ‘W_EXITCODE’ was not declared in this scope
int status = W_EXITCODE(0, SIGKILL);
^~~~~~~~~~
widget.cc:134:30: note: suggested alternative: ‘WEXITED’
int status = W_EXITCODE(0, SIGKILL);
^~~~~~~~~~
WEXITED
make[4]: *** [Makefile:1810: libvte_2_91_la-widget.lo] Error 1
make[4]: *** Waiting for unfinished jobs....
make[4]: Leaving directory '/build/vte-0.56.3/src'
make[3]: *** [Makefile:2133: all-recursive] Error 1
make[3]: Leaving directory '/build/vte-0.56.3/src'
make[2]: *** [Makefile:1279: all] Error 2
make[2]: Leaving directory '/build/vte-0.56.3/src'
make[1]: *** [Makefile:578: all-recursive] Error 1
make[1]: Leaving directory '/build/vte-0.56.3'
make: *** [Makefile:485: all] Error 2
builder for '/nix/store/j4kwsxxzcx34c99zqxi7b9aaw01cm31d-vte-0.56.3.drv' failed with exit code 2
Here's the nix file I am using. It is basically the same as the above, with some additional overrides:
let
gtk3NoCupsOverlay = self: super: {
gtk3 = super.gtk3.override {
cups = null;
cupsSupport = false;
};
};
dbusNoSystemdOverlay = self: super: {
dbus = super.dbus.override {
systemd = null;
};
};
survey = import ./survey {
normalPkgs = import ./nixpkgs {
overlays = [
dbusNoSystemdOverlay
gtk3NoCupsOverlay
];
};
};
termonad-pkg =
{ mkDerivation, adjunctions, base, Cabal, cabal-doctest
, classy-prelude, colour, constraints, containers, data-default
, directory, distributive, doctest, dyre, filepath, focuslist
, genvalidity-containers, genvalidity-hspec, gi-gdk, gi-gio
, gi-glib, gi-gtk, gi-pango, gi-vte, gtk3, haskell-gi-base
, hedgehog, inline-c, lens, libpcre2, mono-traversable
, pretty-simple, QuickCheck, singletons, stdenv, tasty
, tasty-hedgehog, tasty-hspec, template-haskell, text, vte_291
, xml-conduit, xml-html-qq
}:
mkDerivation {
pname = "termonad";
version = "2.0.0.0";
sha256 = "0rprqn5vcvhbqqg0grrmz0ijjpkrprza88la4mbdg6skb34fjsp0";
isLibrary = true;
isExecutable = true;
enableSeparateDataOutput = true;
setupHaskellDepends = [ base Cabal cabal-doctest ];
libraryHaskellDepends = [
adjunctions base classy-prelude colour constraints containers
data-default directory distributive dyre filepath focuslist gi-gdk
gi-gio gi-glib gi-gtk gi-pango gi-vte haskell-gi-base inline-c lens
mono-traversable pretty-simple QuickCheck singletons text
xml-conduit xml-html-qq
];
libraryPkgconfigDepends = [ gtk3 libpcre2 vte_291 ];
executableHaskellDepends = [ base ];
testHaskellDepends = [
base doctest genvalidity-containers genvalidity-hspec hedgehog lens
QuickCheck tasty tasty-hedgehog tasty-hspec template-haskell
];
homepage = "https://github.com/cdepillabout/termonad";
description = "Terminal emulator configurable in Haskell";
license = stdenv.lib.licenses.bsd3;
};
myHaskellPackages =
survey.haskellPackages.override {
overrides = self: super: {
termonad = self.callPackage termonad-pkg {
libpcre2 = survey.pkgs.pcre2;
vte_291 = survey.pkgs.gnome3.vte;
};
gi-gtk = survey.pkgs.haskell.lib.unmarkBroken super.gi-gtk;
gi-vte = survey.pkgs.haskell.lib.unmarkBroken super.gi-vte;
gtk3 = survey.pkgs.gtk3;
};
};
in
myHaskellPackages.termonad
This is pretty exciting! I'm surprised it was this easy to get gtk3 compiled statically!
@nh2 I'd be interested if you had any suggestions on how to get VTE compiled statically as well.
Oh, looks like I was able to get VTE building as well with a small patch:
let
gtk3NoCupsOverlay = self: super: {
gtk3 = super.gtk3.override {
cups = null;
cupsSupport = false;
};
};
dbusNoSystemdOverlay = self: super: {
dbus = super.dbus.override {
systemd = null;
};
};
vteMuslPatchOverlay = self: super: {
vteForMusl = super.gnome3.vte.overrideAttrs (oldAttrs: {
patches = (oldAttrs.patches or []) ++ [
(self.fetchpatch {
name = "0001-Add-W_EXITCODE-macro-for-non-glibc-systems.patch";
url = "https://gitlab.gnome.org/GNOME/vte/uploads/c334f767f5d605e0f30ecaa2a0e4d226/0001-Add-W_EXITCODE-macro-for-non-glibc-systems.patch";
sha256 = "1ii9db9i5l3fy2alxz7bjfsgjs3lappnlx339dvxbi2141zknf5r";
})
];
});
};
survey = import ./survey {
normalPkgs = import ./nixpkgs {
overlays = [
dbusNoSystemdOverlay
gtk3NoCupsOverlay
vteMuslPatchOverlay
];
};
};
termonad-pkg =
{ mkDerivation, adjunctions, base, Cabal, cabal-doctest
, classy-prelude, colour, constraints, containers, data-default
, directory, distributive, doctest, dyre, filepath, focuslist
, genvalidity-containers, genvalidity-hspec, gi-gdk, gi-gio
, gi-glib, gi-gtk, gi-pango, gi-vte, gtk3, haskell-gi-base
, hedgehog, inline-c, lens, libpcre2, mono-traversable
, pretty-simple, QuickCheck, singletons, stdenv, tasty
, tasty-hedgehog, tasty-hspec, template-haskell, text, vte_291
, xml-conduit, xml-html-qq
}:
mkDerivation {
pname = "termonad";
version = "2.0.0.0";
sha256 = "0rprqn5vcvhbqqg0grrmz0ijjpkrprza88la4mbdg6skb34fjsp0";
isLibrary = true;
isExecutable = true;
enableSeparateDataOutput = true;
setupHaskellDepends = [ base Cabal cabal-doctest ];
libraryHaskellDepends = [
adjunctions base classy-prelude colour constraints containers
data-default directory distributive dyre filepath focuslist gi-gdk
gi-gio gi-glib gi-gtk gi-pango gi-vte haskell-gi-base inline-c lens
mono-traversable pretty-simple QuickCheck singletons text
xml-conduit xml-html-qq
];
libraryPkgconfigDepends = [ gtk3 libpcre2 vte_291 ];
executableHaskellDepends = [ base ];
testHaskellDepends = [
base doctest genvalidity-containers genvalidity-hspec hedgehog lens
QuickCheck tasty tasty-hedgehog tasty-hspec template-haskell
];
homepage = "https://github.com/cdepillabout/termonad";
description = "Terminal emulator configurable in Haskell";
license = stdenv.lib.licenses.bsd3;
};
myHaskellPackages =
survey.haskellPackages.override {
overrides = self: super: {
termonad = self.callPackage termonad-pkg {
libpcre2 = survey.pkgs.pcre2;
vte_291 = survey.pkgs.vteForMusl;
gtk3 = survey.pkgs.gtk3;
};
gi-gtk = survey.pkgs.haskell.lib.unmarkBroken super.gi-gtk;
gi-vte =
(survey.pkgs.haskell.lib.unmarkBroken super.gi-vte).override {
vte_291 = survey.pkgs.vteForMusl;
};
};
};
in
myHaskellPackages.termonad
Running it:
$ nix-build build-termonad.nix
these derivations will be built:
/nix/store/rjjaia4f6fajgd0szail634gdfbf8cwh-gi-gtk-3.0.27.drv
/nix/store/njrrx5fxn17q37fmxjv5x6fqvfc8kpyc-gi-vte-2.91.19.drv
/nix/store/a310ambi2z9xcr2d7lc4lml823jd9h7i-termonad-2.0.0.0.drv
building '/nix/store/rjjaia4f6fajgd0szail634gdfbf8cwh-gi-gtk-3.0.27.drv'...
gi-gtk
and gi-vte
aren't able to be built, but I am pretty sure this is also a problem when building dynamically:
$ cd nixpkgs/ # this is the nixpkgs submodule in this repo, currently at commit b577340eb5b
$ nix-build -A haskellPackages.gi-gtk
error: Package ‘gi-gtk-3.0.27’ in /home/illabout/git/static-haskell-nix/nixpkgs/pkgs/development/haskell-modules/hackage-packages.nix:93780 is marked as broken, refusing to evaluate.
a) For `nixos-rebuild` you can set
{ nixpkgs.config.allowBroken = true; }
in configuration.nix to override this.
b) For `nix-env`, `nix-build`, `nix-shell` or any other Nix command you can add
{ allowBroken = true; }
to ~/.config/nixpkgs/config.nix.
(use '--show-trace' to show detailed location information)
However it looks like gi-gtk
and gi-vte
are no longer marked as broken in current nixpkgs master
, so once the nixpkgs
submodule in this repo is updated to the current version of nixpkgs, I imagine gi-gtk
and gi-vte
will be able to be built statically as well! I am hopeful that termonad
will then be easy to build statically!
@nh2, I guess this issue can be closed, since I figured out most of the questions I had.
Thanks for the detailed writeup!
I also want to try building static GUI applications soon, so I'm very grateful for this.
However it looks like
gi-gtk
andgi-vte
are no longer marked as broken in current nixpkgsmaster
, so once thenixpkgs
submodule in this repo is updated to the current version of nixpkgs, I imaginegi-gtk
andgi-vte
will be able to be built statically as well!
That should be after #61 is done.
looks like I was able to get VTE building as well with a small patch:
@cdepillabout Would you mind PRing this into nixpkgs
, directly into the vte
package?
You can check for musl using the bool stdenv.hostPlatform.isMusl
.
@nh2 I've sent 4 PRs to nixpkgs that are needed to get vte
(and gtk3
) to compile with pkgsMusl
. Would you be able to do a review of these?
I wasn't sure exactly what was the best way to do the check for pkgsMusl
.
- dbus: enable building with musl #71560
- libusb1: enable compiling with musl #71563
- cups: enable compiling with musl #71575
- vte: enable compiling with musl #71577
Awesome, very appreciated!
@cdepillabout There is another obstacle we have to take on:
When moving from autotools to Meson, gtk3 lost the ability to build static libraries:
https://gitlab.gnome.org/GNOME/gtk/issues/2248
CC @flokli
When moving from autotools to Meson, gtk3 lost the ability to build static libraries:
My PR to fix it for gtk4 got merged: https://gitlab.gnome.org/GNOME/gtk/merge_requests/1172#note_648846
Now we should probably backport it to gtk3 upstream.
Now we should probably backport it to gtk3 upstream.
I've done it in https://gitlab.gnome.org/GNOME/gtk/merge_requests/1196.
I've done it in https://gitlab.gnome.org/GNOME/gtk/merge_requests/1196.
Merged.
Another upstream PR: https://gitlab.gnome.org/GNOME/at-spi2-atk/merge_requests/19
Also very relevant: meson 0.52.0
https://github.com/NixOS/nixpkgs/pull/70650#issuecomment-558421025
Behold tens of hours of effort:
(Edit: For a fancy button example scroll down.)
ldd /nix/store/bv21vj7xa6az6fhkg385523rv96xdmnh-meson-tutorial-gtk-0.0.1/bin/demo-gtk
not a dynamic executable
This is a static GTK app written in C built with meson.
Some first sizes evaluation (plain strip
, gzip -9
, xz -6
):
21M demo-gtk-unstripped
18M demo-gtk-stripped
8.2M demo-gtk-unstripped.gz
7.3M demo-gtk-stripped.gz
6.2M demo-gtk-unstripped.xz
5.6M demo-gtk-stripped.xz
nm -g
shows that all gtk functions seem to be present, so this doesn't seem to do any unused-code elimination yet.
@nh2 Great work!
I'm really impressed how much work went in to this (including upstreaming all the required fixes)!
This is a static GTK app written in C built with meson.
For some more reproducibility, I just checked that this still works, and pushed the following branches:
-
static-haskell-nix
branchc-static-gtk3-apps
(commit 19a1b4ac) which has as nixpkgs submodule my branchgtk3-static
I also made the demo app way cooler, it now has a button that actually does something to show that it can do more than rendering empty windows:
Binaries: https://github.com/nh2/static-haskell-nix/releases/tag/c-static-gtk3-apps-button-example-2020-11-23
geekosaur
on #haskell
IRC gave me a tip that building UIs with glade
and loading the corresponding XML file might cause GTK to dlopen()
stuff, I should check how that behaves in static exes.
might cause GTK to
dlopen()
stuff
Indeed, loading glade files does not work yet; I made a demo glade app glade-example-main.c
(must be run from its directory to find the .glade
file) and it errors with:
(demo-glade:8872): GModule-CRITICAL **: 22:19:02.953: g_module_symbol: assertion 'module != NULL' failed
(demo-glade:8872): GModule-CRITICAL **: 22:19:02.986: g_module_close: assertion 'module != NULL' failed
Dynamic loading not supported
Failed to load module: /nix/store/88gpkpcfjbgihn3fl8b8vk5ggfs8wn73-dconf-0.36.0-lib/lib/gio/modules/libdconfsettings.so
Dynamic loading not supported
Failed to load module: /nix/store/d6l7xwbdm23xgds5vafzibw57790zw71-glib-networking-2.64.3/lib/gio/modules/libgiolibproxy.so
Dynamic loading not supported
Failed to load module: /nix/store/d6l7xwbdm23xgds5vafzibw57790zw71-glib-networking-2.64.3/lib/gio/modules/libgiognutls.so
Dynamic loading not supported
Failed to load module: /nix/store/d6l7xwbdm23xgds5vafzibw57790zw71-glib-networking-2.64.3/lib/gio/modules/libgiognomeproxy.so
Dynamic loading not supported
Failed to load module: /nix/store/bkjpypri81svkgq5rdfd4mdn33ic1pja-gvfs-1.44.1/lib/gio/modules/libgioremote-volume-monitor.so
Dynamic loading not supported
Failed to load module: /nix/store/bkjpypri81svkgq5rdfd4mdn33ic1pja-gvfs-1.44.1/lib/gio/modules/libgvfsdbus.so
(demo-glade:8872): GModule-CRITICAL **: 22:19:03.183: g_module_symbol: assertion 'module != NULL' failed
(demo-glade:8872): Gtk-ERROR **: 22:19:03.186: gtk_builder_connect_signals() requires working GModule
Need to check whether those disappear if you specify those libs as direct build dependencies of the static exe.