Skip to content
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

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

Open
wolfgangwalther opened this issue Feb 4, 2024 · 1 comment

Comments

@wolfgangwalther
Copy link
Contributor

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.

wolfgangwalther added a commit to wolfgangwalther/nixpkgs that referenced this issue Feb 4, 2024
This works around NixOS#286285 by passing --enable-split-sections instead of ghc-options.
wolfgangwalther added a commit to wolfgangwalther/postgrest that referenced this issue Feb 4, 2024
…aller again

This works around NixOS/nixpkgs#286285 to use -split-sections
in a cross-compiling scenario. This will reduce the size of the static executable and also
remove the remaining references to /nix/store/.. reducing closure size dramatically.

This also fixes the docker image blowing up in size since we switched to pkgsStatic.
wolfgangwalther added a commit to PostgREST/postgrest that referenced this issue Feb 6, 2024
…aller again

This works around NixOS/nixpkgs#286285 to use -split-sections
in a cross-compiling scenario. This will reduce the size of the static executable and also
remove the remaining references to /nix/store/.. reducing closure size dramatically.

This also fixes the docker image blowing up in size since we switched to pkgsStatic.
wolfgangwalther added a commit to wolfgangwalther/nixpkgs that referenced this issue Feb 7, 2024
This works around NixOS#286285 by passing --enable-split-sections instead of ghc-options.
wolfgangwalther added a commit to wolfgangwalther/nixpkgs that referenced this issue Feb 8, 2024
This works around NixOS#286285 by passing --enable-split-sections instead of ghc-options.
@wolfgangwalther
Copy link
Contributor Author

Was able to reproduce this with plain cabal in haskell/cabal#10069.

wolfgangwalther added a commit to wolfgangwalther/nixpkgs that referenced this issue Jun 4, 2024
…ling

The following sequence of operations looses ghc-options:
  Setup.hs configure --ghc-options
  Setup.hs build --with-ghc=...

This is described in [1].

The fix is simple: Don't pass --with-ghc in the build phase. The values
are taken from the configure step anyway. This seems to have been
introduced all the way back in 64ec4dd
about 8 years ago for unknown reasons.

Resolves NixOS#286285

[1]: haskell/cabal#10069
wolfgangwalther added a commit to wolfgangwalther/nixpkgs that referenced this issue Jun 4, 2024
…ling

The following sequence of operations loses ghc-options:
  Setup.hs configure --ghc-options
  Setup.hs build --with-ghc=...

This is described in [1].

The fix is simple: Don't pass --with-ghc in the build phase. The values
are taken from the configure step anyway. This seems to have been
introduced all the way back in 64ec4dd
about 8 years ago for unknown reasons.

Resolves NixOS#286285

[1]: haskell/cabal#10069
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant