-
-
Notifications
You must be signed in to change notification settings - Fork 14.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fully static Haskell executables - overview issue #43795
Comments
Related:
|
CC @vaibhavsagar whose blog post got me into working on this |
I know NixOS is careful about licenses and distribution. I'm thinking about libgmp, which is LGPL: https://www.gnu.org/licenses/gpl-faq.en.html#LGPLStaticVsDynamic
Can we ensure either one of these if we enable caching of static Haskell executables? |
@puffnfresh: Or you could use a version of GHC with |
@Gabriel439 yeah, is that what we'll do for redistributing static Haskell executables? |
This is great, thanks so much for working on this and creating this issue @nh2! |
Before doing more of this #43524 -style fixing of individual package builds, could someone give a try of https://github.com/endrazine/wcc (which has been packaged in #43014) in converting shared objects to static objects? |
@puffnfresh Every Haskell package already has a The best solution seems to be to traverse the licenses of all things linked and ensure that Hydra does not build statically linked exes where one of the dependencies is LGPL or stronger (unless the final project itself is also LGPL or stronger anyway). For executables blocked that way, a fallback should happen where it's checked if the issue goes away if |
@dezgeg It sounds interesting and may be worth a try but there may be the independent questions whether I want "binary black magic" (as wcc calls it) in my production binaries. Nix is already pretty custom in its build process, I wouldn't want an upstream library author reject my bug report about a segfault with "well you use totally custom tools, if you just used our normally built static libs we would be more eager to look into that". It's a good feature if nix's builds still essentially are a series of "normal" build steps (compiler and linker invocations). I haven't looked into wcc in detail so I cannot judge its safety, but my intuition tells me I'd rather sponsor Hydra some build servers and HDs than turn dynamic libs into static ones and have the risk of unexpected behaviour. |
We already provide fully static builds of pandoc with each release, using docker and alpine. |
Thank you for CCing me into this. Could Aura be added to the list of projects above? I haven't yet been able to decipher the instructions for testing it myself, apologies. |
I just want to thank you @nh2 this is excellent work :) |
@fosskers Does Currently most executables I'm trying are already in nixpkgs and then I just override them with
Conceptually very simple, for example for and then built with
For some packages it works immediately, for others I have to make some small overrides of their dependencies (see a bit further up in the file), usually to give static libraries for system dependencies. |
Having discussed it on IRC, here's a way of building all Haskell executables: Just nix-build that file. This includes broken packages by default, because if you want to see which new packages your change breaks, you need to know which ones were broken already (so you should build this once before your change and once after, then compare). |
I have incorporated (a large part of) that into https://github.com/nh2/static-haskell-nix/blob/09d0eaa605111ea516dfaa0e7341a71ff1a63042/survey/default.nix#L47-L124 now. So now we can build If some binary doesn't build because of dependent libraries making problems, those libraries are supposed to be patched here. Contributors are welcome to help make as many of those build as possible. |
@nh2 Aura isn't yet connected to Nix infrastructure in any way. I suppose I'll pull up my old notes about getting a project set up with Nix and try to build Aura that way at first. |
I understand the concern, yes.
The problem isn't the disk space or resource usage but rather the effort of fixing packages one-by-one to support static linking and the cost of maintaining that. But maybe Haskell packages don't use too many native dependencies. |
I have given a first run at building all executables on Stackage, statically. See this post for full build outputs. It took around 3 hours to get there (I built with 5 machines). The final status line, The program didn't terminate; right now it's
Insights:
|
@dezgeg Yes, I think that is accurate. From what I posted a just above, it looks like most of Stackage's executables will be buildable as long as a set of 15 native libs (13 above and |
Great work! My rule of thumb for static vs shared is:
We pretty much need to support both in Nixpkgs. I can see some of the benefits of always building statically but I think the advantages to shared linking is much greater. |
I've found and PRd a fix for another cabal issue that needs to be merged to make static linking reasonable: This passes I've also added it to the overview in the issue description. |
This makes sense.
Yes. One of my goals is that nixpkgs becomes the building environment which makes it really easy to build any program so that it works on any Linux distribution, forever. Other Linux distributions make it really hard to build things statically.
I guess I agree in general but there are some exceptions / other points, like
I'd find it very cool if somebody could build a typical NixOS with only static exes and compare what the size difference (and perhaps resident memory difference) is. |
This adds a new `dhall-static` target that builds a fully static `dhall` executable that can be run on any Linux machine (i.e. it is a relocatable executable that is completely dependency free). That in turns implies that even though it is built with Nix it doesn't require that the user installs Nix to run it (i.e. no dependency on the `/nix/store` or a Nix installer). Just copy the standalone executable to any Linux machine and it's good to go. This based on the following work of @nh2: * NixOS/nixpkgs#43795 * dhall-lang/dhall-lang#192 (comment) This also bumps the version of `nixpkgs` used for the normal (non-static) Dhall build to be the closest revision on `nixpkgs` `master` as the one used by @nh2 in his work. Once that work is merged into `nixpkgs` `master` then both builds can use the same revision from `nixpkgs` `master`.
I have created @NixOS/static team for people interested in static builds in I have started started to invite some people that I think are interested: https://github.com/orgs/NixOS/teams/static/members Sonebody please tell me if it is OK to add non-committers to such teams, and whether the Edit: I now found the nixpkgs-committers team, so other teams do NOT necessarily get commit access, and it's safe to add anyone to the |
Maybe a good opportunity to note some recent movement in upstream nixpkgs:
This is quite a step in nixpkgs, as it is now possible to compile non-trivial applications like The implementation and investigation was done by @rnhmjoj, thank you! |
@sternenseemann @rnhmjoj I received a question today whether it would make sense to move all It sounds good to me, but would there be any problems with having to list |
Doing that would effectively break static linking as it works today, since we assume that the libraries are to be found in I assume the idea is to install static archives in In general our notion of platforms is pretty strict — it's either dynamic or static (of course some language toolchains like to link their dependencies statically like Go and Haskell); Eventually I feel like we may be forced to devise a way to have both, for cases like Haskell which may want to load dynamic libraries (in TH) despite linking statically. I guess to respond properly I would need to understand better what this idea envisions exactly? |
I always though the standard package set is supposed to contain only dynamically linked binaries and shared objects. So, static libraries could go either to a See also: #164141 |
just for curiosity, even if each individual statically linked executable is smaller as each statically linked executable contains all its dependencies won't they eventually be bigger than dynamically linked ones as in that case you install the dependency only once? if i am right how many would be the threshold? |
Statically linked Haskell executables are smaller than the sum of their parts – or rather dependencies. My guess would be that it is to a degree where it would take a lot of installed packages for the deduplication effect outweighing the save, but that would require some testing. Other factors are that the store paths of libraries tend to be quite big:
These factors mean that dynamic linking is atrocious and only really feasible in cases where you need all the dependencies and GHC anyways (e.g. HLS). There is still some optimization potential in splitting the derivations up more, into smaller derivations (for GHC if the build system allows it eventually) and into smaller outputs for ordinary packages that don't reference each other which is the trouble so far. |
Update:
|
Thanks for that! I keep forgetting these details, it’s good to have them written down. Is it worth adding “If the package you care about builds statically using nixpkgs’s |
I think that's probably the case. There might be minor differences, e.g. |
|
Is there an established interface for mixing |
Isn't |
@ShamrockLee you are right, and I wasn't precise, I mean
|
You can get any combination you want by using crossSystem |
You can also just do this: |
Note: Currently static GHC 9.8.1 builds are broken due to #275304 . |
Update:
|
If you just want to build static Haskell executables right now, follow these instructions.
This issue collects/links all issues and ongoing work to make fully-static building of Haskell executables an excellently-working feature in nixpkgs.
I will update here regularly the progress we are making, and sub-goals we encounter on the way that need to be fulfilled to tick off the parent goal.
Obviously contributions to this list are welcome.
stack
based projects with their ownresolver
should be really easystack
based example should be added (here)AllMost Haskell executables in nixpkgs succeed to build staticallysurvey
directory of https://github.com/nh2/static-haskell-nixnixpkgsStackageconfiguration-hackage2nix.yaml
to be able to filter for Stackage packages (thanks @domenkozar for the tip)stack
stack
builds withcrossSystem = { config = "x86_64-unknown-linux-musl"; };
stack
builds withpkgsMusl
stack
builds with its own Stackage snapshot viastack2nix
stack
1.9 official release uses this toolchain for its static buildintray-web-server
(which includes, amongst others,servant
) (CC @NorfairKing)dhall
(CC @Gabriel439)cachix
(CC @domenkozar)xmonad
libgmp.so.3
errorlibgmp
error is gone since Fully static Haskell executables - overview issue #43795 (comment)pandoc
(CC @jgm)hslua
test case segfault might mean some functionality will crashdarcs
libcurl
which itself has many dependencies, and had to fix a cabal issue for it, but it's working now.aura
static-haskell-nix
CI build that tracks nixpkgsmaster
to find breakage early (done)crossSystem = { config = "x86_64-unknown-linux-musl"; };
support is working (done by @dtzWill)pkgsMusl
support has landed in nixpkgs master (done by @dtzWill)master
Argument list too long
when linkingCabal
--enable-executable-static
feature is available in nixpkgshaskellPackages
where all exes are statically linkedCabal
release is available that includes itCabal
--ld-option
GHC passthrough feature is available in nixpkgs.a
files) only at the linker stageCabal
release is available that includes it.a
files)ncurses
.a
andtinfo
fix has been merged tostaging
(done by (@shlevy).a
andtinfo
fix has been merged tomaster
(this is a mass-rebuild PR) (merged)staging
has landed inmaster
(only.a
fix, nottinfo
part, because it's not a mass-rebuild PRpkgsStatic
- a fully static overlaypkgsStatic
to nixpkgsmaster
(@matthewbauer)static-haskell-nix
on top of it (move system dependency overrides that add static libs there)pkgsStatic
can be used as a base.pkgsStatic
(Provide middle-ground overlay between pkgsMusl and pkgsStatic #61575), then drop commitpkgsStatic: Don't disable .so files.
from forked nixpkgsmaster
curl-config
when cross-compiling. #61552pkgsStatic: Don't disable static on curl for git #66416pkgsStatic: Add libffi override #66417callPackage
#66503--disable-static
#66759static-haskell-nix
's Haskell functionality over as well-ffunction-sections
can make final executables even smaller (results)integer-simple
instead ofinteger-gmp
, aslibgmp
is the biggest LGPL dependency of all Haskell programs (see here)integer-openssl
to be ready for use (CC @ch1bo)Why is static linking desirable?
Static linking means your executable depends on as few things running on the target system as possible.
For Linux, it means that an executable you build will work on any Linux distribution, with essentially infinite forwards-compatibility (because Linux in general does not change its system call interface).
Statically linked executables do not depend on the target system's
libc
.Statically linked executable are incredibly easy to deploy (many Go executables are statically linked, which gave them the reputation of being easy to deploy in the devops world).
Statically linked executables can start faster than dynamically linked ones where dynamic linking has to be performed at startup (of course the startup time depends on the number of libraries to link and the amount of symbols contained within).
Statically linked executables are smaller (up to 4x in our mesurements), mostly because GHC's
-split-sections
has way more effects with them (for unknown reason for now).Who is working on or contributing to this
Feel free to add yourself!
The text was updated successfully, but these errors were encountered: