Skip to content

Haskell: ghc-options are lost when cross-compiling (including pkgsStatic) #286285

@wolfgangwalther

Description

@wolfgangwalther

Describe the bug

When cross-compiling haskell packages custom ghc-options are not passed to the compiler. This includes those passed in:

(optionalString (enableSharedExecutables && stdenv.isLinux) "--ghc-option=-optl=-Wl,-rpath=$out/${ghcLibdir}/${pname}-${version}")
(optionalString (enableSharedExecutables && stdenv.isDarwin) "--ghc-option=-optl=-Wl,-headerpad_max_install_names")
(optionalString enableParallelBuilding "--ghc-options=${parallelBuildingFlags}")
(optionalString useCpphs "--with-cpphs=${cpphs}/bin/cpphs --ghc-options=-cpp --ghc-options=-pgmP${cpphs}/bin/cpphs --ghc-options=-optP--cpp")

] ++ optionals (enableDeadCodeElimination && (lib.versionOlder "8.0.1" ghc.version)) [
"--ghc-option=-split-sections"

] ++ optionals (doHaddockInterfaces && isLibrary) [
"--ghc-options=-haddock"

Steps To Reproduce

Run the following to see the invocation of GHC when cross-compiling to musl64:

nix-build -E 'with import ./default.nix  {}; haskell.lib.overrideCabal pkgsCross.musl64.haskellPackages.hello { buildFlags = [ "-v" ]; }'

The configureFlags line will look like this:

configureFlags: --verbose --prefix=/nix/store/ddsc15fcakx45v3c86cx9xrdwlclhb7g-hello-x86_64-unknown-linux-musl-1.0.0.2
--libdir=$prefix/lib/$compiler/lib --libsubdir=$abi/$libname --with-gcc=x86_64-unknown-linux-musl-gcc
--package-db=/build/tmp.W3Zovu64mu/package.conf.d --ghc-options=-j16 +RTS -A64M -RTS --disable-split-objs
--enable-library-profiling --profiling-detail=exported-functions --disable-profiling --enable-shared --disable-coverage
--enable-static --disable-executable-dynamic --disable-tests --disable-benchmarks --enable-library-vanilla --disable-library-for-ghci
--ghc-option=-split-sections --configure-option=--host=x86_64-unknown-linux-musl --with-ghc=x86_64-unknown-linux-musl-ghc
--with-ghc-pkg=x86_64-unknown-linux-musl-ghc-pkg --with-gcc=x86_64-unknown-linux-musl-gcc
--with-ld=x86_64-unknown-linux-musl-ld --with-ar=x86_64-unknown-linux-musl-ar
--with-hsc2hs=x86_64-unknown-linux-musl-hsc2hs --with-strip=x86_64-unknown-linux-musl-strip
--hsc2hs-option=--cross-compile
--extra-lib-dirs=/nix/store/1v2iayibpfkyq4gk9vwz5l19m599n9vd-ncurses-x86_64-unknown-linux-musl-6.4/lib
--extra-lib-dirs=/nix/store/lc6kgi9shf14lgxj08cwbzxfqfl3wlgb-libffi-x86_64-unknown-linux-musl-3.4.4/lib
--extra-lib-dirs=/nix/store/wgzbmp2xfs5rfmdbqa4xjz860276027v-elfutils-x86_64-unknown-linux-musl-0.190/lib
--extra-lib-dirs=/nix/store/1f5r748az7lsd4fdgbrf7z4qw93rx916-gmp-with-cxx-x86_64-unknown-linux-musl-6.3.0/lib
--extra-include-dirs=/nix/store/dkv5hjqr1v3ff3rwr00bmb0q5xzfj3bd-musl-iconv-1.2.3/include

Note the --ghc-options=-j16 and --ghc-option=-split-sections.

The call to GHC:

Running: /nix/store/pzpcvgfj7hagiwfdb5ckyp299vmh68p3-x86_64-unknown-linux-musl-ghc-9.6.4/bin/x86_64-unknown-linux-musl-ghc
--make -no-link -fbuilding-cabal-package -O -static -outputdir dist/build/hello/hello-tmp -odir dist/build/hello/hello-tmp
-hidir dist/build/hello/hello-tmp -stubdir dist/build/hello/hello-tmp -i -idist/build/hello/hello-tmp -isrc -idist/build/hello/autogen
-idist/build/global-autogen -Idist/build/hello/autogen -Idist/build/global-autogen -Idist/build/hello/hello-tmp
-I/nix/store/dkv5hjqr1v3ff3rwr00bmb0q5xzfj3bd-musl-iconv-1.2.3/include -optP-include
-optPdist/build/hello/autogen/cabal_macros.h -hide-all-packages -Wmissing-home-modules -no-user-package-db
-package-db /build/tmp.W3Zovu64mu/package.conf.d -package-db dist/package.conf.inplace -package-id base-4.18.2.0
-XHaskell98 src/hello.hs

None of the two options are passed to GHC.

Expected behavior

Run the same without cross-compiling for pkgsMusl:

nix-build -E 'with import ./default.nix  {}; haskell.lib.overrideCabal pkgsMusl.haskellPackages.hello { buildFlags = [ "-v" ]; }'

The configure flags are similar, but the GHC invocation looks like this:

Running: /nix/store/pp40hbfhzcy6cf4lrcg2jyyj3m05hgzp-ghc-musl-9.6.4/bin/ghc --make -no-link -fbuilding-cabal-package
-O -static -outputdir dist/build/hello/hello-tmp -odir dist/build/hello/hello-tmp -hidir dist/build/hello/hello-tmp 
-stubdir dist/build/hello/hello-tmp -i -idist/build/hello/hello-tmp -isrc -idist/build/hello/autogen -idist/build/global-autogen
-Idist/build/hello/autogen -Idist/build/global-autogen -Idist/build/hello/hello-tmp
-I/nix/store/vjc0iaa0wa4dbza5vp9dwiv8hn40g81c-musl-iconv-1.2.3/include -optP-include
-optPdist/build/hello/autogen/cabal_macros.h -hide-all-packages -Wmissing-home-modules -no-user-package-db
-package-db /build/tmp.FCMFDXPgoS/package.conf.d -package-db dist/package.conf.inplace -package-id base-4.18.2.0
-XHaskell98 src/hello.hs -j16 -split-sections

Note the -j16 -split-sections at the end.

Additional context

I assume this is a bug in cabal somehow, but I was not able to reproduce this with plain cabal outside nixpkgs, yet. I tried many variations of configureFlags, but the only thing that actually made a difference is pkgsCross / pkgsStatic.

This affects pkgsStatic and results in all statically built haskell executables being larger than they should be, because dead code elimination is not happening. This was already observed in static-haskell-nix (@nh2):

https://github.com/nh2/static-haskell-nix/blob/88f1e2d57e3f4cd6d980eb3d8f99d5e60040ad54/survey/default.nix#L15-L17

It also means that static executables can't strip auto-generated Paths_xxx modules, which are never required at run-time, I think. But they contain references to /nix/store/... paths of their own module. When trying to create a minimal docker image, with just a single static executable, this will then pull in all of those modules and their dependencies, resulting in a huge image. This was observed after switching PostgREST's build from static-haskell-nix to pkgsStatic.

Dead code elimination / -split-sections can be worked around by passing --enable-split-sections to cabal instead. This will pass on -split-sections correctly, even when cross compiling. However, that doesn't solve the problem for other ghc options.

Notify maintainers

@cdepillabout @expipiplus1 @maralorn @ncfavier @sternenseemann


Add a 👍 reaction to issues you find important.

Metadata

Metadata

Assignees

No one assigned

    Labels

    0.kind: bugSomething is broken6.topic: haskellGeneral-purpose, statically typed, purely functional programming language

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions