diff --git a/doc/languages-frameworks/haskell.section.md b/doc/languages-frameworks/haskell.section.md index dde55c329a4ac..3843f3a36eeef 100644 --- a/doc/languages-frameworks/haskell.section.md +++ b/doc/languages-frameworks/haskell.section.md @@ -923,14 +923,62 @@ for this to work. `justStaticExecutables drv` : Only build and install the executables produced by `drv`, removing everything -that may refer to other Haskell packages' store paths (like libraries and -documentation). This dramatically reduces the closure size of the resulting -derivation. Note that the executables are only statically linked against their -Haskell dependencies, but will still link dynamically against libc, GMP and -other system library dependencies. If dependencies use their Cabal-generated -`Paths_*` module, this may not work as well if GHC's dead code elimination -is unable to remove the references to the dependency's store path that module -contains. + that may refer to other Haskell packages' store paths (like libraries and + documentation). This dramatically reduces the closure size of the resulting + derivation. Note that the executables are only statically linked against their + Haskell dependencies, but will still link dynamically against libc, GMP and + other system library dependencies. + + If the library being built or its dependencies use their Cabal-generated + `Paths_*` module, this may not work as well if GHC's dead code elimination is + unable to remove the references to the dependency's store path that module + contains. (See [nixpkgs#164630][164630] for more information.) + + Nixpkgs unstable and releases from 24.11 onwards will verify that such + references are eliminated and fail the build if they are not. + You can opt in to this new behavior as follows: + + ```nix + pkgs.haskell.lib.overrideCabal + (pkgs.haskell.lib.justStaticExecutables my-haskell-package) + (drv: { + disallowGhcReference = true; + }) + ``` + + on the package you are statically linking with `justStaticExecutables`. + After doing so, importing the `Paths_*` module may cause builds to fail with this message: + + ``` + error: output '/nix/store/64k8iw0ryz76qpijsnl9v87fb26v28z8-my-haskell-package-1.0.0.0' is not allowed to refer to the following paths: + /nix/store/5q5s4a07gaz50h04zpfbda8xjs8wrnhg-ghc-9.6.3 + ``` + + If that happens, temporarily disable the check for GHC references and rebuild the + derivation. Then use `strings` to determine which libraries are responsible: + + ``` + $ nix-build ... + $ strings result/bin/my-haskell-binary | grep /nix/store/ + ... + /nix/store/n7ciwdlg8yyxdhbrgd6yc2d8ypnwpmgq-hs-opentelemetry-sdk-0.0.3.6/bin + ... + ``` + + Finally, use `remove-references-to` to delete those store paths from the produced output: + + ```nix + pkgs.haskell.lib.overrideCabal + (pkgs.haskell.lib.justStaticExecutables my-haskell-package) + (drv: { + postInstall = '' + ${drv.postInstall or ""} + remove-references-to -t ${pkgs.haskellPackages.hs-opentelemetry-sdk} + ''; + }) + ``` + +[164630]: https://github.com/NixOS/nixpkgs/issues/164630 `enableSeparateBinOutput drv` : Install executables produced by `drv` to a separate `bin` output. This diff --git a/pkgs/development/haskell-modules/generic-builder.nix b/pkgs/development/haskell-modules/generic-builder.nix index f9acdd0f4a27b..e383af0b42281 100644 --- a/pkgs/development/haskell-modules/generic-builder.nix +++ b/pkgs/development/haskell-modules/generic-builder.nix @@ -110,6 +110,29 @@ in # build products from that prior build as a starting point for accelerating # this build , previousIntermediates ? null + # References to these store paths are forbidden in the produced output. +, disallowedRequisites ? [] + # Whether to allow the produced output to refer to `ghc`. + # + # This can be used in conjunction with `haskell.lib.justStaticExecutables` + # to help prevent static Haskell binaries from having erroneous dependencies + # on GHC. + # + # Generated `Paths_*` modules include paths for the runtime library + # directory (and similar) of the package being built. If the `Paths_*` + # module is imported, this creates a dependency from the static binary + # being built to the _library_ being built (which is dynamically linked + # and depends on the GHC used to build it). + # + # To avoid this: + # 1. Build the impacted derivation. + # 2. Run `strings` on the built binary of the impacted derivation to + # locate the store paths it depends on. + # 3. Add `remove-references-to -t ${bad-store-path-in-binary}` to the + # impacted derivation's `postInstall`. + # + # See: https://github.com/NixOS/nixpkgs/issues/164630 +, disallowGhcReference ? false , # Cabal 3.8 which is shipped by default for GHC >= 9.3 always calls # `pkg-config --libs --static` as part of the configure step. This requires # Requires.private dependencies of pkg-config dependencies to be present in @@ -647,7 +670,7 @@ stdenv.mkDerivation ({ passthru = passthru // rec { - inherit pname version; + inherit pname version disallowGhcReference; compiler = ghc; @@ -808,10 +831,19 @@ stdenv.mkDerivation ({ // optionalAttrs (args ? dontStrip) { inherit dontStrip; } // optionalAttrs (postPhases != []) { inherit postPhases; } // optionalAttrs (stdenv.buildPlatform.libc == "glibc"){ LOCALE_ARCHIVE = "${glibcLocales}/lib/locale/locale-archive"; } +// optionalAttrs (disallowedRequisites != [] || disallowGhcReference) { + disallowedRequisites = + disallowedRequisites + ++ ( + if disallowGhcReference + then [ghc] + else [] + ); +} # Implicit pointer to integer conversions are errors by default since clang 15. # Works around https://gitlab.haskell.org/ghc/ghc/-/issues/23456. -// lib.optionalAttrs (stdenv.hasCC && stdenv.cc.isClang) { +// optionalAttrs (stdenv.hasCC && stdenv.cc.isClang) { NIX_CFLAGS_COMPILE = "-Wno-error=int-conversion"; } ) diff --git a/pkgs/development/haskell-modules/lib/compose.nix b/pkgs/development/haskell-modules/lib/compose.nix index 09cee08b91c1f..3717ed98d4a1d 100644 --- a/pkgs/development/haskell-modules/lib/compose.nix +++ b/pkgs/development/haskell-modules/lib/compose.nix @@ -290,7 +290,7 @@ rec { /* link executables statically against haskell libs to reduce closure size */ - justStaticExecutables = overrideCabal (drv: { + justStaticExecutables = overrideCabal (drv: { enableSharedExecutables = false; enableLibraryProfiling = false; isLibrary = false; @@ -300,6 +300,8 @@ rec { # Remove every directory which could have links to other store paths. rm -rf $out/lib $out/nix-support $out/share/doc ''; + # We don't do this yet in 24.05. Unstable has it, and 24.11 will have it. + # disallowGhcReference = true; }); /* Build a source distribution tarball instead of using the source files