NixOS, posix_spawn: Argument list too long

New to NixOS, I thought I would see if I could use Stack to build Stack. However, GHC fails and I wondered if I was hitting a known problem on NixOS and, if so, is there a solution?

GHC 9.10.3 fails at the linking stage with (reformatted):

error: 
  gcc: 
    fatal error: 
      cannot execute ‘/nix/store/qxaq7jz61a6zkr2mq49i0zvqip2m2jj8-gcc-15.2.0/libexec/gcc/x86_64-unknown-linux-gnu/15.2.0/collect2’:
        posix_spawn: Argument list too long

and explains:

Running: /nix/store/k493jzz83044mqayvlb6247l35780kxy-ghc-9.10.3/bin/hsc2hs-ghc-9.10.3 '@/tmp/nix-shell-4558-1683970142/hsc2hs-response4979-0.txt'
[warn] linking .stack-work/dist/x86_64-linux-nix/ghc-9.10.3/build/System/Terminal_hsc_make.o failed (exit code 1)
[warn] rsp file was: ".stack-work/dist/x86_64-linux-nix/ghc-9.10.3/build/System/hsc2hscall4986-2.rsp"
[warn] command was: /nix/store/788mx070y81zjlg5ipcl0cra3afviw9k-gcc-wrapper-15.2.0/bin/cc <very long list of arguments omitted here>

The very long list of arguments (see further below) is about 284,115 characters long. However:

$ getconf ARG_MAX 
2097152 

If I understand correctly, even 284,115 characters cannot be hitting the constraints of the Linux kernel. It must be hitting something else.

The command that Stack was trying to execute was short (reformatted):

Run process within /home/mpilgrem/Code/GitHub/commercialhaskell/stack/: 
  /home/mpilgrem/Code/GitHub/commercialhaskell/stack/.stack-work/dist/x86_64-linux-nix/ghc-9.10.3/setup/setup 
    --verbose=2 
    --builddir=.stack-work/dist/x86_64-linux-nix/ghc-9.10.3 
    build lib:stack exe:stack 
    --ghc-options ""

The command that Cabal (the library) was trying to execute was (reformatted):

Running: /nix/store/k493jzz83044mqayvlb6247l35780kxy-ghc-9.10.3/bin/hsc2hs-ghc-9.10.3 '@/tmp/nix-shell-4558-1683970142/hsc2hs-response4979-0.txt'

The very long list of arguments to cc appears to include, for each dependency of stack, something that looks like this (using mustache-2.4.3.1 as an example):

-L/home/mpilgrem/.stack/snapshots/x86_64-linux-nix/9166b319527d9e0ed673bb86918f609f422429a81f3b93a3c9fb87aadd0c1261/9.10.3/lib/x86_64-linux-ghc-9.10.3-21af/mustache-2.4.3.1-5E6ETcjbN8JLxPInIFj7hG
-L/nix/store/1m05k7xgfnw6jc21xxk5681ni3ar97wf-pkg-config-wrapper-0.29.2/lib
-L/nix/store/mhw4sjc1rz4zmb3vkdinvfm7ym96fazx-unzip-6.0/lib
-L/nix/store/61a1nwx3w6rqyaisj5rn1sal1981apm7-zlib-1.3.2/lib
-L/nix/store/k493jzz83044mqayvlb6247l35780kxy-ghc-9.10.3/lib
-L/nix/store/bcnisk3ydfgv26v2gw3zlky24g00yww2-git-2.54.0/lib
-L/nix/store/788mx070y81zjlg5ipcl0cra3afviw9k-gcc-wrapper-15.2.0/lib
-L/nix/store/y56aqb1m3y5davn6hsv4jfbml78qw4mv-gmp-with-cxx-6.3.0/lib
-L/nix/store/6snfrwakqanqp7c21nnzmz183fkhnzv1-nss-cacert-3.123/lib
-Wl,-R,/home/mpilgrem/.stack/snapshots/x86_64-linux-nix/9166b319527d9e0ed673bb86918f609f422429a81f3b93a3c9fb87aadd0c1261/9.10.3/lib/x86_64-linux-ghc-9.10.3-21af/mustache-2.4.3.1-5E6ETcjbN8JLxPInIFj7hG
-Wl,-R,/nix/store/1m05k7xgfnw6jc21xxk5681ni3ar97wf-pkg-config-wrapper-0.29.2/lib
-Wl,-R,/nix/store/mhw4sjc1rz4zmb3vkdinvfm7ym96fazx-unzip-6.0/lib
-Wl,-R,/nix/store/61a1nwx3w6rqyaisj5rn1sal1981apm7-zlib-1.3.2/lib
-Wl,-R,/nix/store/k493jzz83044mqayvlb6247l35780kxy-ghc-9.10.3/lib
-Wl,-R,/nix/store/bcnisk3ydfgv26v2gw3zlky24g00yww2-git-2.54.0/lib
-Wl,-R,/nix/store/788mx070y81zjlg5ipcl0cra3afviw9k-gcc-wrapper-15.2.0/lib
-Wl,-R,/nix/store/y56aqb1m3y5davn6hsv4jfbml78qw4mv-gmp-with-cxx-6.3.0/lib
-Wl,-R,/nix/store/6snfrwakqanqp7c21nnzmz183fkhnzv1-nss-cacert-3.123/lib

That is, the following is repeated for each dependency (about 1,087 characters), which explains 284,115 characters overall:

-L/nix/store/1m05k7xgfnw6jc21xxk5681ni3ar97wf-pkg-config-wrapper-0.29.2/lib
-L/nix/store/mhw4sjc1rz4zmb3vkdinvfm7ym96fazx-unzip-6.0/lib
-L/nix/store/61a1nwx3w6rqyaisj5rn1sal1981apm7-zlib-1.3.2/lib
-L/nix/store/k493jzz83044mqayvlb6247l35780kxy-ghc-9.10.3/lib
-L/nix/store/bcnisk3ydfgv26v2gw3zlky24g00yww2-git-2.54.0/lib
-L/nix/store/788mx070y81zjlg5ipcl0cra3afviw9k-gcc-wrapper-15.2.0/lib
-L/nix/store/y56aqb1m3y5davn6hsv4jfbml78qw4mv-gmp-with-cxx-6.3.0/lib
-L/nix/store/6snfrwakqanqp7c21nnzmz183fkhnzv1-nss-cacert-3.123/lib
-Wl,-R,/nix/store/1m05k7xgfnw6jc21xxk5681ni3ar97wf-pkg-config-wrapper-0.29.2/lib
-Wl,-R,/nix/store/mhw4sjc1rz4zmb3vkdinvfm7ym96fazx-unzip-6.0/lib
-Wl,-R,/nix/store/61a1nwx3w6rqyaisj5rn1sal1981apm7-zlib-1.3.2/lib
-Wl,-R,/nix/store/k493jzz83044mqayvlb6247l35780kxy-ghc-9.10.3/lib
-Wl,-R,/nix/store/bcnisk3ydfgv26v2gw3zlky24g00yww2-git-2.54.0/lib
-Wl,-R,/nix/store/788mx070y81zjlg5ipcl0cra3afviw9k-gcc-wrapper-15.2.0/lib
-Wl,-R,/nix/store/y56aqb1m3y5davn6hsv4jfbml78qw4mv-gmp-with-cxx-6.3.0/lib
-Wl,-R,/nix/store/6snfrwakqanqp7c21nnzmz183fkhnzv1-nss-cacert-3.123/lib

I think GHC’s own code relating to the error reporting (at hsc2hs/src/Common.hs) :

rawSystemL :: FilePath -> FilePath -> String -> Bool -> FilePath -> [String] -> IO ()
rawSystemL outDir outBase action flg prog args = withResponseFile outDir outBase args $ \rspFile -> do
  let cmdLine = prog++" "++unwords args
  when flg $ hPutStrLn stderr ("Executing: (@" ++ rspFile ++ ") " ++ cmdLine)
  (_ ,_ ,progerr ,ph) <- createProcess (proc prog ['@':rspFile])
  -- Because of the response files being written and removed after the process
  -- terminates we now need to use process jobs here to correctly wait for all
  -- child processes to terminate.  Not doing so would causes a race condition
  -- between the last child dieing and not holding a lock on the response file
  -- and the response file getting deleted.
    { std_err = CreatePipe
#if MIN_VERSION_process(1,5,0)
    , use_process_jobs = True
#endif
    }
  errdata <- maybeReadHandle progerr
  exitStatus <- waitForProcess ph
  case exitStatus of
    ExitFailure exitCode ->
      do die $ action ++ " failed "
                      ++ "(exit code "    ++ show exitCode ++ ")\n"
                      ++ "rsp file was: " ++ show rspFile ++ "\n"
                      ++ "command was: "  ++ cmdLine ++ "\n"
                      ++ "error: "        ++ errdata ++ "\n"
    _                    -> return ()

is predicated on the assumption that reported cmdLine is faithful to the actual contents of rspFile. I don’t know if that assumption is a good one. EDIT: It is a good one, because of how withResponseFile works.

Looks like hackage-doc-builder-config/cabal-hsc2hs-args-patch.diff at master · haskell-infra/hackage-doc-builder-config · GitHub (also perhaps check Nix files around).

It was fixed in Cabal HEAD, but I do not remember if it was released already.

Indeed: