Admin message

Due to a large amount of spam we do not allow new users to create repositories, they are "external" users. If you are a new user and want to create a repository, for example for forking GHC, open a new issue on ghc/ghc using the "get-verified" issue template

nightly bindist for Windows contains hard-coded path to Clang
## Summary Nightly bindist for Windows contains hard-coded path to Clang and other tools. Making it practically unusable. ## Steps to reproduce I encountered this issue when testing my library on GHA's Windows runner against a nightly GHC ([log here](https://github.com/minoki/haskell-simdy/actions/runs/31392795530/job/93468330043)). It is locally reproducible: ``` # I ran these commands on WSL2: $ curl -Lo archive.zip https://gitlab.haskell.org/ghc/ghc/-/jobs/2612371/artifacts/download?file_type=archive $ unzip archive.zip $ tar xf ghc-x86_64-windows-validate.tar.xz # and this one on Windows: > ghc-10.1.20260810-x86_64-unknown-mingw32\bin\ghc.exe Hello.hs [1 of 2] Compiling Main ( Hello.hs, Hello.o ) <no location info>: error: Assembler: could not execute: C:/GitLabRunner/builds/0/2612371/_build/bindist/ghc-10.1.20260810-x86_64-unknown-mingw32/mingw//bin/clang.exe ``` ## Expected behavior The path should be relative to `$topdir` or something. ## Environment * GHC version used: ghc-10.1.20260810 * Operating System: Windows * System Architecture: x86-64 ## Analysis by Claude <details> <summary>Not (yet) verified by human </summary> ### Windows bindists ship `default.target` with hard-coded CI toolchain paths #### Summary On `master`, the Windows binary distributions (including the nightly bindists that ghcup consumes) ship a `lib/targets/default.target` in which every toolchain `prgPath` is an **absolute path into the GitLab runner's build directory**, e.g. ``` C:/GitLab-Runner/builds/0/2603709/_build/bindist/ghc-10.1.20260803-x86_64-unknown-mingw32/mingw//bin/clang.exe ``` instead of the expected `$topdir/../mingw/bin/clang.exe`. Windows bindists are relocatable and are not `configure`d by the user — ghcup simply unpacks the tarball — so the installed compiler ends up pointing its C compiler, C++ compiler, CPP, Cmm CPP, linker, `ranlib`, `llc`, `opt`, `llvm-windres` and `llvm-dlltool` at paths that only ever existed on the CI machine. Anything that needs the toolchain (compiling a `.c` file, linking, `hsc2hs`, TH, …) fails. The `$tooldir` substitution that is supposed to rewrite those paths silently fails; the two root causes are described below. Both were introduced by 7d378476c3 (*"Read Toolchain.Target files rather than 'settings'"*, 2025-07-08), which is on `master` only. `ghc-9.14` and `ghc-9.12` do not contain `m4/subst_tooldir.m4` at all and are unaffected. #### Evidence `distrib/configure.ac.in` prints `default.target` right after applying the substitution, so the failure is visible in every Windows CI job log. From the nightly build of `10.1.20260803` (job 2603709, `https://gitlab.haskell.org/ghc/ghc/-/jobs/2603709/raw`): ``` sed: -e expression #1, char 2: extra characters after command ... config.status: creating default.target Applied tooldir substitution to default.target: Target { tgtArchOs = ArchOS {archOS_arch = ArchX86_64, archOS_OS = OSMinGW32} ... , tgtCCompiler = Cc {ccProgram = Program {prgPath = "C:/GitLab-Runner/builds/0/2603709/_build/bindist/ghc-10.1.20260803-x86_64-unknown-mingw32/mingw//bin/clang.exe", prgFlags = ["--target=x86_64-unknown-windows-gnu","--rtlib=compiler-rt","-D_UCRT","-IC:/GitLab-Runner/builds/0/2603709/_build/bindist/ghc-10.1.20260803-x86_64-unknown-mingw32/mingw//include","-Qunused-arguments"]}} , tgtCxxCompiler = Cxx {cxxProgram = Program {prgPath = "C:/GitLab-Runner/builds/0/2603709/.../mingw//bin/clang++.exe", ...}} , tgtCCompilerLink = CcLink { ccLinkProgram = Program {prgPath = "C:/GitLab-Runner/builds/0/2603709/.../mingw//bin/clang.exe", ...} ... ``` The `sed` error and the unchanged output show that `SUBST_TOOLDIR` did nothing. `hadrian/bindist/Makefile` then just copies the file into place, so this is exactly what lands in the tarball: ```make lib/targets/default.target : config.mk default.target @rm -f $@ @echo "Copying the bindist-configured default.target to lib/targets/default.target" cp default.target $@ ``` #### Root cause 1: `sed` argument order in `SUBST_TOOLDIR` `m4/subst_tooldir.m4:44` ```sh sed -i.bkp $1 's%'"$mingw_prefix"'%'"$mingw_install_prefix"'%g' ``` With `-i`, `sed` takes the *script* first and the *files* afterwards. Here the filename is passed as the script, so `sed` tries to parse `default.target` as a command list, fails with ``` sed: -e expression #1, char 2: extra characters after command ``` and exits 4 without touching the file. `configure` does not run under `set -e`, so the failure is not fatal and the build proceeds. Up to `ghc-9.14`, `SUBST_TOOLDIR` took a *shell variable name*, not a filename: ```sh $1=`echo "$$1" | sed 's%'"$mingw_prefix"'%'"$mingw_install_prefix"'%g'` ``` and was invoked as `SUBST_TOOLDIR([SettingsCCompilerCommand])`. When 7d378476c3 changed it to operate on `default.target` directly, `$1` stayed in the position it had before, which is now the script position. #### Root cause 2: `$$topdir` is no longer halved by `make` `distrib/configure.ac.in:106` ``` FP_SETUP_WINDOWS_TOOLCHAIN([$hardtop/mingw/], [\$\$topdir/../mingw/]) ``` `\$\$topdir` yields a literal `$$topdir` (two dollars) in `mingw_install_prefix`. That was correct while the paths flowed through `hadrian/bindist/Makefile`: ```make @echo ',("C compiler command", "$(SettingsCCompilerCommand)")' >> $@ ``` where `make` expands `$$` to `$`. Since 7d378476c3 the target file is `cp`'d verbatim, so nothing halves the `$$` any more. `expandPathVar` (`libraries/ghc-boot/GHC/BaseDir.hs:41`) only recognises `$topdir`: ```haskell expandPathVar var value str | Just str' <- stripPrefix ('$':var) str , maybe True isPathSeparator (listToMaybe str') = value ++ expandPathVar var value str' expandPathVar var value (x:xs) = x : expandPathVar var value xs expandPathVar _ _ [] = [] ``` so `$$topdir/../mingw/bin/clang.exe` would expand to `$C:/ghcup/ghc/<ver>/lib/../mingw/bin/clang.exe` — note the leading `$`. In other words, fixing root cause 1 alone is not enough; it would replace one broken path with another. #### Proposed fix ```diff --- a/m4/subst_tooldir.m4 +++ b/m4/subst_tooldir.m4 @@ -41,5 +41,5 @@ AC_DEFUN([SUBST_TOOLDIR], [ - sed -i.bkp $1 's%'"$mingw_prefix"'%'"$mingw_install_prefix"'%g' + sed -i.bkp 's%'"$mingw_prefix"'%'"$mingw_install_prefix"'%g' $1 ]) --- a/distrib/configure.ac.in +++ b/distrib/configure.ac.in @@ -103,7 +103,7 @@ if test "$HostOS" = "mingw32" -a "$EnableDistroToolchain" = "NO"; then - FP_SETUP_WINDOWS_TOOLCHAIN([$hardtop/mingw/], [\$\$topdir/../mingw/]) + FP_SETUP_WINDOWS_TOOLCHAIN([$hardtop/mingw], [\$topdir/../mingw]) fi ``` The trailing slash is dropped as well: `FP_SETUP_WINDOWS_TOOLCHAIN` builds `mingwbin="$mingw_prefix/bin/"` (`m4/fp_setup_windows_toolchain.m4:92`), which is why the current paths contain `mingw//bin`. `configure.ac:313` already passes `$hardtop/inplace/mingw` without a trailing slash, so this also makes the two call sites consistent. I verified the fix by extracting the equivalent m4 into a standalone `configure.ac`, running `autoconf`, and applying it to the exact `default.target` emitted by job 2603709. Result: ``` , tgtCCompiler = Cc {ccProgram = Program {prgPath = "$topdir/../mingw/bin/clang.exe", prgFlags = ["-I$topdir/../mingw/include", ...]}} ``` which `expandTopDir` resolves correctly at GHC startup. #### Suggested follow-up `SUBST_TOOLDIR` failing is invisible today. Since the resulting bindist is unusable, it would be worth making the substitution failure fatal, e.g. ```sh sed -i.bkp 's%'"$mingw_prefix"'%'"$mingw_install_prefix"'%g' $1 \ || AC_MSG_ERROR([failed to apply tooldir substitution to $1]) ``` and/or having the bindist `configure` assert that no occurrence of `$hardtop` remains in `default.target`. #### Not affected * `--enable-distro-toolchain` bindists — the basename rewriting loop in `distrib/configure.ac.in` is a separate, working code path. * `ghc-9.14` / `ghc-9.12` and earlier — `m4/subst_tooldir.m4` does not exist there and the settings file is still produced by `hadrian/bindist/Makefile` from `$(Settings*)` variables. </details>
issue