I'm not sure if this is known or not.
If I add -static and -pthread to ld-options in my project.cabal file and try to build with nix-build (full steps below) I will get a failure. It looks like ld cannot find certain libraries.
I've created a repository that will allow you to clone and nix-build run.nix to see the issue. The repository is here: https://github.com/aaronlevin/nixpkgsissue
Here is an overview of the steps:
project.cabal file:ld-options:
-static
-pthread
project.nix: cabal2Nix . > project.nix# run.nix
with (import <nixpkgs> {});
haskellPackages.callPackage (import ./project.nix) {}
nix-build run.nixOne should see errors similar to this output: http://lpaste.net/126997 and the build will fail.
@aaronlevin, thank you very much for the detailed bug report. I really appreciate the effort you've put into setting up an easily reproducible test case!
The problems you're experiencing aren't caused by the use of ld-options per se, but these linker errors are the result of specifying -static in that stanza. Building a statically linked executable with Cabal isn't quite so easy, because Haskell distinguishes between Haskell libraries and system libraries, and it's possible to link either one of those statically but not the other. By default, Cabal uses shared libraries for everything. Now, when you add -static to the linker flags, the linker will try to link everything statically, but Cabal isn't aware of the fact that you're trying to do this and it hasn't configured the build accordingly. As a resullt, system libraries will be linked statically (because the linker knows how to do that), but Haskell libraries cannot be linked at all (because Cabal didn't set them up).
Now, the proper way to build statically linked Haskell libraries with Cabal is to configure the build with the flag --disable-executable-dynamic. If you do this, you'll get a binary with statically linked Haskell libraries and dynamically linked system libraries. If you specify --disable-executable-dynamic --ghc-option=-optl=-static --ghc-option=-optl=-pthread, then you'll get an all-statically linked binary.
Generally speaking, I'd recommend against hard-coding these kind of flags in the Cabal file and to leave those choices to the user running cabal configure. If you really want to, however, then you can remedy the code in nixpkgsissue by adding the attribute
enableSharedExecutables = false;
the the nixpkgsissue.nix file to configure the build with --disable-executable-dynamic.
@peti thanks for the detailed (and enlightening) response! One note I wanted to make:
I was able to hop into a nix-shell with haskellPackages.cabalInstall on my PATH and successfully built a static executable by running cabal build. I don't think this contradicts anything you wrote, but I was surprised that nix-build wasn't reproducing the same behaviour of cabal build with respect to this. This could be because of some side-effects happening (perhaps some settings in ~/.cabal? I did blow that directory away and experienced the same).
Thanks again for your notes.
PS - the fact that Nix provides some safe reproducibility and because we can make a shell.nix, all bug reports should have reproducible test cases ;) Although, it is a bit of work hehe :)
Our builder doesn't use cabal-install. We build by running runhaskell Setup.hs configure && runhaskell Setup.hs build. Maybe that makes a difference?
Sorry for a bit of an offtopic.
Do you know if this cabal facility of building shared libraries with statically linked haskell libraries can be used by Stack?
@AleXoundOS as with the release of Stack 1.7.1 statically linked Linux bindists are no longer available, due to difficulty with GHC 8.2.2 on Alpine Linux. Source and more information on the Haskell Cafe Mailing List.
It looks like this is a problem also affecting Stack 1.6, relevant Issue here. @kindaro has done an excellent brief report about the current situation. You may avoid this problem on Windows or OSX, but I鈥榤 unsure if this is really true.
I鈥榤 not sure if the now unavailable feature would solve your problem. If it does, a downgrade may help.
@emanuelbuholzer, thank you for the info.
I PR's a flag to Cabal to make building static binaries easier: https://github.com/haskell/cabal/pull/5446
To answer the original question
How to build statically linked Haskell binaries?
https://github.com/nh2/static-haskell-nix shows how you can do that with the musl support that has landed in nixpkgs master in the last days.
It can build many Haskell exes already, and I wouldn't be surprised if with nixpkgs 18.09 all of it is easily available.
(Most of the background work has been done by others such as @dtzWill and @Ericson2314)
See https://github.com/NixOS/nixpkgs/issues/43795 for an overview issue
Most helpful comment
@aaronlevin, thank you very much for the detailed bug report. I really appreciate the effort you've put into setting up an easily reproducible test case!
The problems you're experiencing aren't caused by the use of
ld-optionsper se, but these linker errors are the result of specifying-staticin that stanza. Building a statically linked executable with Cabal isn't quite so easy, because Haskell distinguishes between Haskell libraries and system libraries, and it's possible to link either one of those statically but not the other. By default, Cabal uses shared libraries for everything. Now, when you add-staticto the linker flags, the linker will try to link everything statically, but Cabal isn't aware of the fact that you're trying to do this and it hasn't configured the build accordingly. As a resullt, system libraries will be linked statically (because the linker knows how to do that), but Haskell libraries cannot be linked at all (because Cabal didn't set them up).Now, the proper way to build statically linked Haskell libraries with Cabal is to configure the build with the flag
--disable-executable-dynamic. If you do this, you'll get a binary with statically linked Haskell libraries and dynamically linked system libraries. If you specify--disable-executable-dynamic --ghc-option=-optl=-static --ghc-option=-optl=-pthread, then you'll get an all-statically linked binary.Generally speaking, I'd recommend against hard-coding these kind of flags in the Cabal file and to leave those choices to the user running
cabal configure. If you really want to, however, then you can remedy the code innixpkgsissueby adding the attributethe the
nixpkgsissue.nixfile to configure the build with--disable-executable-dynamic.