Nixpkgs: How to build statically linked Haskell binaries?

Created on 17 Mar 2015  路  8Comments  路  Source: NixOS/nixpkgs

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.

Steps to Recreate

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:

  • add the following options to the executable region of a haskell project's project.cabal file:
ld-options:
  -static
  -pthread
  • create project.nix: cabal2Nix . > project.nix
  • create run.nix:
# run.nix

with (import <nixpkgs> {});
haskellPackages.callPackage (import ./project.nix) {}
  • nix-build run.nix

One should see errors similar to this output: http://lpaste.net/126997 and the build will fail.

question haskell

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-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.

All 8 comments

@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)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

yawnt picture yawnt  路  3Comments

copumpkin picture copumpkin  路  3Comments

edolstra picture edolstra  路  3Comments

sid-kap picture sid-kap  路  3Comments

lverns picture lverns  路  3Comments