diff --git a/.github/scripts/test.sh b/.github/scripts/test.sh index ce5ae26..687a1ce 100644 --- a/.github/scripts/test.sh +++ b/.github/scripts/test.sh @@ -39,6 +39,12 @@ rm "ghcup-test${ext}" "ghcup-test-optparse${ext}" eghcup --numeric-version +# test PATH on windows wrt msys2 +# https://github.com/haskell/ghcup-hs/pull/992/checks +if [ "${OS}" = "Windows" ] ; then + eghcup run -m -- sh -c 'echo $PATH' | sed 's/:/\n/' | grep '^/mingw64/bin$' +fi + eghcup install ghc "${GHC_VER}" eghcup unset ghc "${GHC_VER}" ls -lah "$(eghcup whereis -d ghc "${GHC_VER}")" diff --git a/.github/workflows/bootstrap.yaml b/.github/workflows/bootstrap.yaml index dd84cef..37ae677 100644 --- a/.github/workflows/bootstrap.yaml +++ b/.github/workflows/bootstrap.yaml @@ -52,7 +52,9 @@ jobs: - if: runner.os == 'Windows' name: Run bootstrap run: | + $ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop $curDir = Get-Location Write-Host "Current Working Directory: $curDir" - ./scripts/bootstrap/bootstrap-haskell.ps1 -InstallDir ${GITHUB_WORKSPACE} -BootstrapUrl ("{0}/scripts/bootstrap/bootstrap-haskell" -f $curDir) -InBash + ./scripts/bootstrap/bootstrap-haskell.ps1 -InstallDir ${GITHUB_WORKSPACE} -BootstrapUrl ("{0}/scripts/bootstrap/bootstrap-haskell" -f $curDir) -InBash -Msys2Env "MINGW64" shell: pwsh + diff --git a/docs/guide.md b/docs/guide.md index 99ef557..9dd121e 100644 --- a/docs/guide.md +++ b/docs/guide.md @@ -105,6 +105,7 @@ This is the complete list of env variables that change GHCup behavior: On windows, there's additionally: * `GHCUP_MSYS2`: Has to point to the root of an existing MSYS2 installation (when installed by GHCup, that's e.g. `C:\ghcup\msys64`). GHCup bootstrap takes care of this usually. +* `GHCUP_MSYS2_ENV`: The [MSYS2 environment](https://www.msys2.org/docs/environments/) to use when executing e.g. `ghcup run --mingw-path`. Possible values are `MSYS`, `UCRT64`, `CLANG64`, `CLANGARM64`, `CLANG32`, `MINGW64`, `MINGW32`. Defaults to `MINGW64`, `MINGW32` or `CLANGARM64`, depending on the architecture. `MSYS` is always added as the last component. If you change this value after running the bootstrap script, you may need to make sure that the cabal config reflects this change, more specifically `extra-prog-path`, `extra-include-dirs` and `extra-lib-dirs`. (**NOTE: specifying anything other than the default is considered experimental**) ### XDG support @@ -508,7 +509,7 @@ See `ghcup compile ghc --help` for further information. Since ghcup version 0.1.20.0, we provide cross bindists for GHC JS and WASM. These can be installed conveniently. However, these are intended as a developer preview only. By using these GHC variants, you are implicitly signing up to participate in GHC development! -If you run into bugs or missing behavior, join the dev chat at https://matrix.to/#/#GHC:matrix.org. +If you run into bugs or missing behavior, join the dev chat at https://matrix.to/#/#GHC:matrix.org. First, add the cross release channel: diff --git a/lib/GHCup/Prelude/Process/Windows.hs b/lib/GHCup/Prelude/Process/Windows.hs index 22ef529..23290f3 100644 --- a/lib/GHCup/Prelude/Process/Windows.hs +++ b/lib/GHCup/Prelude/Process/Windows.hs @@ -263,12 +263,9 @@ createProcessWithMingwPath :: MonadIO m => CreateProcess -> m CreateProcess createProcessWithMingwPath cp = do - msys2Dir <- liftIO ghcupMsys2Dir cEnv <- Map.fromList <$> maybe (liftIO getEnvironment) pure (env cp) - let mingWPaths = [msys2Dir "mingw64" "bin" - ,msys2Dir "usr" "bin" - ] - paths = ["PATH", "Path"] + mingWPaths <- liftIO ghcupMsys2BinDirs' + let paths = ["PATH", "Path"] curPaths = (\x -> maybe [] splitSearchPath (Map.lookup x cEnv)) =<< paths newPath = intercalate [searchPathSeparator] (mingWPaths ++ curPaths) envWithoutPath = foldr (\x y -> Map.delete x y) cEnv paths @@ -276,11 +273,4 @@ createProcessWithMingwPath cp = do liftIO $ setEnv "Path" newPath pure $ cp { env = Just $ Map.toList envWithNewPath } -ghcupMsys2Dir :: IO FilePath -ghcupMsys2Dir = - lookupEnv "GHCUP_MSYS2" >>= \case - Just fp -> pure fp - Nothing -> do - baseDir <- liftIO ghcupBaseDir - pure (fromGHCupPath baseDir "msys64") diff --git a/lib/GHCup/Types.hs b/lib/GHCup/Types.hs index 6f00deb..3817115 100644 --- a/lib/GHCup/Types.hs +++ b/lib/GHCup/Types.hs @@ -544,11 +544,23 @@ data Dirs = Dirs , dbDir :: GHCupPath , recycleDir :: GHCupPath -- mainly used on windows , tmpDir :: GHCupPath + , msys2Dir :: FilePath } deriving (Show, GHC.Generic) instance NFData Dirs +data MSYS2Env = MSYS + | UCRT64 + | CLANG64 + | CLANGARM64 + | CLANG32 + | MINGW64 + | MINGW32 + deriving (Eq, Show, Ord, GHC.Generic, Read) + +instance NFData MSYS2Env + data KeepDirs = Always | Errors | Never diff --git a/lib/GHCup/Utils.hs b/lib/GHCup/Utils.hs index ddc61e4..903d8fc 100644 --- a/lib/GHCup/Utils.hs +++ b/lib/GHCup/Utils.hs @@ -1156,7 +1156,7 @@ ensureShimGen -- | Ensure ghcup directory structure exists. ensureDirectories :: Dirs -> IO () -ensureDirectories (Dirs baseDir binDir cacheDir logsDir confDir trashDir dbDir tmpDir) = do +ensureDirectories (Dirs baseDir binDir cacheDir logsDir confDir trashDir dbDir tmpDir _) = do createDirRecursive' (fromGHCupPath baseDir) createDirRecursive' (fromGHCupPath baseDir "ghc") createDirRecursive' (fromGHCupPath baseDir "hls") diff --git a/lib/GHCup/Utils/Dirs.hs b/lib/GHCup/Utils/Dirs.hs index 838341e..3dd13f0 100644 --- a/lib/GHCup/Utils/Dirs.hs +++ b/lib/GHCup/Utils/Dirs.hs @@ -32,6 +32,8 @@ module GHCup.Utils.Dirs , getConfigFilePath' , useXDG , cleanupTrash + , ghcupMsys2BinDirs + , ghcupMsys2BinDirs' , GHCupPath , appendGHCupPath @@ -136,6 +138,7 @@ import GHC.IO.Exception ( IOErrorType(NoSuchThing) ) import Haskus.Utils.Variant.Excepts import Optics hiding ( uncons ) import Safe +import System.Info import System.Directory hiding ( removeDirectory , removeDirectoryRecursive , removePathForcibly @@ -338,6 +341,48 @@ ghcupTMPDir else ghcupBaseDir <&> (\(GHCupPath gp) -> GHCupPath (gp "tmp")) +ghcupMsys2Dir :: IO FilePath +ghcupMsys2Dir = + lookupEnv "GHCUP_MSYS2" >>= \case + Just fp -> pure fp + Nothing -> do + baseDir <- liftIO ghcupBaseDir + pure (fromGHCupPath baseDir "msys64") + +ghcupMsys2BinDirs :: (MonadFail m, MonadIO m, MonadReader env m, HasDirs env) => m [FilePath] +ghcupMsys2BinDirs = do + Dirs{..} <- getDirs + liftIO $ ghcupMsys2BinDirs_ msys2Dir + +ghcupMsys2BinDirs' :: IO [FilePath] +ghcupMsys2BinDirs' = do + msys2Dir <- ghcupMsys2Dir + ghcupMsys2BinDirs_ msys2Dir + +ghcupMsys2BinDirs_ :: FilePath -> IO [FilePath] +ghcupMsys2BinDirs_ msys2Dir' = do + env <- liftIO (lookupEnv "GHCUP_MSYS2_ENV") >>= \case + Just env -> maybe (fail parseFailMsg) pure $ readMay @MSYS2Env env + Nothing + | "x86_64" <- arch -> pure MINGW64 + | "i386" <- arch -> pure MINGW32 + | "aarch64" <- arch -> pure CLANGARM64 + | otherwise -> fail "No compatible architecture for msys2" + pure [msys2Dir' toEnvDir env "bin", msys2Dir' toEnvDir MSYS "bin"] + where + -- https://www.msys2.org/docs/environments/ + toEnvDir :: MSYS2Env -> FilePath + toEnvDir MSYS = "usr" + toEnvDir UCRT64 = "ucrt64" + toEnvDir CLANG64 = "clang64" + toEnvDir CLANGARM64 = "clangarm64" + toEnvDir CLANG32 = "clang32" + toEnvDir MINGW64 = "mingw64" + toEnvDir MINGW32 = "mingw32" + + parseFailMsg = "Invalid value for GHCUP_MSYS2_ENV. Valid values are: MSYS, UCRT64, CLANG64, CLANGARM64, CLANG32, MINGW64, MINGW32" + + getAllDirs :: IO Dirs getAllDirs = do baseDir <- ghcupBaseDir @@ -348,6 +393,7 @@ getAllDirs = do recycleDir <- ghcupRecycleDir tmpDir <- ghcupTMPDir dbDir <- ghcupDbDir + msys2Dir <- ghcupMsys2Dir pure Dirs { .. } diff --git a/scripts/bootstrap/bootstrap-haskell b/scripts/bootstrap/bootstrap-haskell index d044440..5c6a2d8 100755 --- a/scripts/bootstrap/bootstrap-haskell +++ b/scripts/bootstrap/bootstrap-haskell @@ -20,6 +20,7 @@ # * BOOTSTRAP_HASKELL_ADJUST_CABAL_CONFIG - whether to adjust mingw paths in cabal.config on windows # * BOOTSTRAP_HASKELL_DOWNLOADER - which downloader to use (default: curl) # * GHCUP_BASE_URL - the base url for ghcup binary download (use this to overwrite https://downloads.haskell.org/~ghcup with a mirror) +# * GHCUP_MSYS2_ENV - the msys2 environment to use on windows, see https://www.msys2.org/docs/environments/ (defauts to MINGW64, MINGW32 or CLANGARM64, depending on the architecture) # License: LGPL-3.0 @@ -27,6 +28,15 @@ # safety subshell to avoid executing anything in case this script is not downloaded properly ( +die() { + if [ -n "${NO_COLOR}" ] ; then + (>&2 printf "%s\\n" "$1") + else + (>&2 printf "\\033[0;31m%s\\033[0m\\n" "$1") + fi + exit 2 +} + plat="$(uname -s)" arch=$(uname -m) ghver="0.1.20.0" @@ -55,18 +65,40 @@ case "${plat}" in ;; esac +case "${GHCUP_MSYS2_ENV}" in + "") + case "${arch}" in + x86_64|amd64) + GHCUP_MSYS2_ENV_DIR="mingw64" ;; + i*86) + GHCUP_MSYS2_ENV_DIR="mingw32" ;; + aarch64|arm64) + GHCUP_MSYS2_ENV_DIR="clangarm64" ;; + *) die "Unknown architecture: ${arch}" ;; + esac + ;; + MSYS) + GHCUP_MSYS2_ENV_DIR="usr" ;; + UCRT64) + GHCUP_MSYS2_ENV_DIR="ucrt64" ;; + CLANG64) + GHCUP_MSYS2_ENV_DIR="clang64" ;; + CLANGARM64) + GHCUP_MSYS2_ENV_DIR="clangarm64" ;; + CLANG32) + GHCUP_MSYS2_ENV_DIR="clang32" ;; + MINGW64) + GHCUP_MSYS2_ENV_DIR="mingw64" ;; + MINGW32) + GHCUP_MSYS2_ENV_DIR="mingw32" ;; + *) + die "Invalid value for GHCUP_MSYS2_ENV. Valid values are: MSYS, UCRT64, CLANG64, CLANGARM64, CLANG32, MINGW64, MINGW32" ;; +esac + : "${BOOTSTRAP_HASKELL_GHC_VERSION:=recommended}" : "${BOOTSTRAP_HASKELL_CABAL_VERSION:=recommended}" -die() { - if [ -n "${NO_COLOR}" ] ; then - (>&2 printf "%s\\n" "$1") - else - (>&2 printf "\\033[0;31m%s\\033[0m\\n" "$1") - fi - exit 2 -} warn() { if [ -n "${NO_COLOR}" ] ; then @@ -595,7 +627,7 @@ adjust_cabal_config() { else cabal_bin="$HOME/AppData/Roaming/cabal/bin" fi - ecabal user-config -a "extra-prog-path: $(cygpath -w "$GHCUP_BIN"), $(cygpath -w "$cabal_bin"), $(cygpath -w "$GHCUP_MSYS2"/mingw64/bin), $(cygpath -w "$GHCUP_MSYS2"/usr/bin)" -a "extra-include-dirs: $(cygpath -w "$GHCUP_MSYS2"/mingw64/include)" -a "extra-lib-dirs: $(cygpath -w "$GHCUP_MSYS2"/mingw64/lib)" -f init + ecabal user-config -a "extra-prog-path: $(cygpath -w "$GHCUP_BIN"), $(cygpath -w "$cabal_bin"), $(cygpath -w "$GHCUP_MSYS2"/${GHCUP_MSYS2_ENV_DIR}/bin), $(cygpath -w "$GHCUP_MSYS2"/usr/bin)" -a "extra-include-dirs: $(cygpath -w "$GHCUP_MSYS2"/${GHCUP_MSYS2_ENV_DIR}/include)" -a "extra-lib-dirs: $(cygpath -w "$GHCUP_MSYS2"/${GHCUP_MSYS2_ENV_DIR}/lib)" -f init } ask_cabal_config_init() { diff --git a/scripts/bootstrap/bootstrap-haskell.ps1 b/scripts/bootstrap/bootstrap-haskell.ps1 index d31f79d..c300b74 100644 --- a/scripts/bootstrap/bootstrap-haskell.ps1 +++ b/scripts/bootstrap/bootstrap-haskell.ps1 @@ -46,7 +46,9 @@ param ( # Whether to disable creation of several desktop shortcuts [switch]$DontWriteDesktopShortcuts, # Whether to disable adjusting bashrc (in msys2 env) with PATH - [switch]$DontAdjustBashRc + [switch]$DontAdjustBashRc, + # The msys2 environment to use, see https://www.msys2.org/docs/environments/ (defauts to MINGW64, MINGW32 or CLANGARM64, depending on the architecture) + [string]$Msys2Env ) $DefaultMsys2Version = "20221216" @@ -194,6 +196,36 @@ if (!$SupportedArchitectures.contains($env:PROCESSOR_ARCHITECTURE)) { Exit 1 } +# set default Msys2Env if not set +if (!$Msys2Env) { + if ($env:PROCESSOR_ARCHITECTURE -eq 'x86') { + $Msys2Env = 'MINGW32' + } elseif ($env:PROCESSOR_ARCHITECTURE -eq 'AMD64') { + $Msys2Env = 'MINGW64' + } +} + +# parse Msys2Env and set the corresponding variables +if ($Msys2Env -eq 'MINGW32') { + $ShellType = '-mingw32' + $PkgConf = 'mingw-w64-i686-pkgconf' +} elseif ($Msys2Env -eq 'MINGW64') { + $ShellType = '-mingw64' + $PkgConf = 'mingw-w64-x86_64-pkgconf' +} elseif ($Msys2Env -eq 'MSYS') { + $ShellType = '-msys2' + $PkgConf = 'pkgconf' +} elseif ($Msys2Env -eq 'UCRT64') { + $ShellType = '-ucrt64' + $PkgConf = 'mingw-w64-ucrt-x86_64-pkgconf' +} elseif ($Msys2Env -eq 'CLANG64') { + $ShellType = '-clang64' + $PkgConf = 'mingw-w64-clang-x86_64-pkgconf' +} else { + Print-Msg -color Red -msg ("Unsupported Msys2 environment: {0}. Supported environments are: MINGW64, MINGW32, MSYS, UCRT64, CLANG64" -f $Msys2Env) + Exit 1 +} + $ErrorActionPreference = 'Stop' $GhcupBasePrefixEnv = [System.Environment]::GetEnvironmentVariable('GHCUP_INSTALL_BASE_PREFIX', 'user') @@ -501,7 +533,7 @@ if (!(Test-Path -Path ('{0}' -f $MsysDir))) { Exec "$Bash" '-lc' 'pacman --noconfirm -Syuu' Print-Msg -msg 'Installing Dependencies...' - Exec "$Bash" '-lc' 'pacman --noconfirm -S --needed curl autoconf mingw-w64-x86_64-pkgconf' + Exec "$Bash" '-lc' ('pacman --noconfirm -S --needed curl autoconf {0}' -f $PkgConf) Print-Msg -msg 'Updating SSL root certificate authorities...' Exec "$Bash" '-lc' 'pacman --noconfirm -S ca-certificates' @@ -609,9 +641,9 @@ if ($Host.Name -eq "ConsoleHost") } '@ - $GhcInstArgs = '-mingw64 -mintty -c "pacman --noconfirm -S --needed base-devel gettext autoconf make libtool automake python p7zip patch unzip"' + $GhcInstArgs = ('{0} -mintty -c "pacman --noconfirm -S --needed base-devel gettext autoconf make libtool automake python p7zip patch unzip"' -f $ShellType) Create-Shortcut -SourceExe ('{0}\msys2_shell.cmd' -f $MsysDir) -ArgumentsToSourceExe $GhcInstArgs -DestinationPath 'Install GHC dev dependencies.lnk' -TempPath $GhcupDir - Create-Shortcut -SourceExe ('{0}\msys2_shell.cmd' -f $MsysDir) -ArgumentsToSourceExe '-mingw64' -DestinationPath 'Mingw haskell shell.lnk' -TempPath $GhcupDir + Create-Shortcut -SourceExe ('{0}\msys2_shell.cmd' -f $MsysDir) -ArgumentsToSourceExe $ShellType -DestinationPath 'Mingw haskell shell.lnk' -TempPath $GhcupDir Create-Shortcut -SourceExe 'https://www.msys2.org/docs/package-management' -ArgumentsToSourceExe '' -DestinationPath 'Mingw package management docs.url' -TempPath $GhcupDir $DesktopDir = [Environment]::GetFolderPath("Desktop") $null = New-Item -Path $DesktopDir -Name "Uninstall Haskell.ps1" -ItemType "file" -Force -Value $uninstallShortCut @@ -660,10 +692,15 @@ if (!($DontAdjustBashRc)) { $AdjustBashRcExport = 'export BOOTSTRAP_HASKELL_ADJUST_BASHRC=1 ;' } +# set msys2 env export for the shell bootstrap script +$Msys2EnvExport = ('export GHCUP_MSYS2_ENV={0} ;' -f $Msys2Env) +# export GHCUP_MSYS2_ENV +$null = [Environment]::SetEnvironmentVariable("GHCUP_MSYS2_ENV", $Msys2Env, [System.EnvironmentVariableTarget]::User) + if ((Get-Process -ID $PID).ProcessName.StartsWith("bootstrap-haskell") -Or $InBash) { - Exec "$Bash" '-lc' ('{4} {6} {7} {8} {9} {10} {12} [ -n ''{1}'' ] && export GHCUP_MSYS2=$(cygpath -m ''{1}'') ; [ -n ''{2}'' ] && export GHCUP_INSTALL_BASE_PREFIX=$(cygpath -m ''{2}/'') ; export PATH=$(cygpath -u ''{3}/bin''):$PATH ; export CABAL_DIR=''{5}'' ; [[ ''{0}'' = https* ]] && {11} {0} | bash || cat $(cygpath -m ''{0}'') | bash' -f $BootstrapUrl, $MsysDir, $GhcupBasePrefix, $GhcupDir, $SilentExport, $CabalDirFull, $StackInstallExport, $HLSInstallExport, $AdjustCabalConfigExport, $MinimalExport, $BootstrapDownloader, $DownloadScript, $AdjustBashRcExport) + Exec "$Bash" '-lc' ('{4} {6} {7} {8} {9} {10} {12} {13} [ -n ''{1}'' ] && export GHCUP_MSYS2=$(cygpath -m ''{1}'') ; [ -n ''{2}'' ] && export GHCUP_INSTALL_BASE_PREFIX=$(cygpath -m ''{2}/'') ; export PATH=$(cygpath -u ''{3}/bin''):$PATH ; export CABAL_DIR=''{5}'' ; [[ ''{0}'' = https* ]] && {11} {0} | bash || cat $(cygpath -m ''{0}'') | bash' -f $BootstrapUrl, $MsysDir, $GhcupBasePrefix, $GhcupDir, $SilentExport, $CabalDirFull, $StackInstallExport, $HLSInstallExport, $AdjustCabalConfigExport, $MinimalExport, $BootstrapDownloader, $DownloadScript, $AdjustBashRcExport, $Msys2EnvExport) } else { - Exec "$Msys2Shell" '-mingw64' '-mintty' '-shell' 'bash' '-c' ('{4} {6} {7} {8} {9} {10} {12} [ -n ''{1}'' ] && export GHCUP_MSYS2=$(cygpath -m ''{1}'') ; [ -n ''{2}'' ] && export GHCUP_INSTALL_BASE_PREFIX=$(cygpath -m ''{2}/'') ; export PATH=$(cygpath -u ''{3}/bin''):$PATH ; export CABAL_DIR=''{5}'' ; trap ''echo Press any key to exit && read -n 1 && exit'' 2 ; [[ ''{0}'' = https* ]] && {11} {0} | bash || cat $(cygpath -m ''{0}'') | bash ; echo ''Press any key to exit'' && read -n 1' -f $BootstrapUrl, $MsysDir, $GhcupBasePrefix, $GhcupDir, $SilentExport, $CabalDirFull, $StackInstallExport, $HLSInstallExport, $AdjustCabalConfigExport, $MinimalExport, $BootstrapDownloader, $DownloadScript, $AdjustBashRcExport) + Exec "$Msys2Shell" $ShellType '-mintty' '-shell' 'bash' '-c' ('{4} {6} {7} {8} {9} {10} {12} {13} [ -n ''{1}'' ] && export GHCUP_MSYS2=$(cygpath -m ''{1}'') ; [ -n ''{2}'' ] && export GHCUP_INSTALL_BASE_PREFIX=$(cygpath -m ''{2}/'') ; export PATH=$(cygpath -u ''{3}/bin''):$PATH ; export CABAL_DIR=''{5}'' ; trap ''echo Press any key to exit && read -n 1 && exit'' 2 ; [[ ''{0}'' = https* ]] && {11} {0} | bash || cat $(cygpath -m ''{0}'') | bash ; echo ''Press any key to exit'' && read -n 1' -f $BootstrapUrl, $MsysDir, $GhcupBasePrefix, $GhcupDir, $SilentExport, $CabalDirFull, $StackInstallExport, $HLSInstallExport, $AdjustCabalConfigExport, $MinimalExport, $BootstrapDownloader, $DownloadScript, $AdjustBashRcExport, $Msys2EnvExport) }