diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a20b2a1..ead4a24 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -182,12 +182,12 @@ variables: - export HOMEBREW_CHANGE_ARCH_TO_ARM=1 # make sure to not pollute the machine with temp files etc - - mkdir -p $CI_PROJECT_DIR/.brew_cache - - export HOMEBREW_CACHE=$CI_PROJECT_DIR/.brew_cache - - mkdir -p $CI_PROJECT_DIR/.brew_logs - - export HOMEBREW_LOGS=$CI_PROJECT_DIR/.brew_logs - - mkdir -p $CI_PROJECT_DIR/.brew_tmp - - export HOMEBREW_TEMP=$CI_PROJECT_DIR/.brew_tmp + - mkdir -p $CI_PROJECT_DIR/.bc + - export HOMEBREW_CACHE=$CI_PROJECT_DIR/.bc + - mkdir -p $CI_PROJECT_DIR/.bl + - export HOMEBREW_LOGS=$CI_PROJECT_DIR/.bl + - mkdir -p $CI_PROJECT_DIR/.bt + - export HOMEBREW_TEMP=$CI_PROJECT_DIR/.bt # update and install packages - brew update @@ -541,12 +541,12 @@ release:darwin:aarch64: - export HOMEBREW_CHANGE_ARCH_TO_ARM=1 # make sure to not pollute the machine with temp files etc - - mkdir -p $CI_PROJECT_DIR/.brew_cache - - export HOMEBREW_CACHE=$CI_PROJECT_DIR/.brew_cache - - mkdir -p $CI_PROJECT_DIR/.brew_logs - - export HOMEBREW_LOGS=$CI_PROJECT_DIR/.brew_logs - - mkdir -p $CI_PROJECT_DIR/.brew_tmp - - export HOMEBREW_TEMP=$CI_PROJECT_DIR/.brew_tmp + - mkdir -p $CI_PROJECT_DIR/.bc + - export HOMEBREW_CACHE=$CI_PROJECT_DIR/.bc + - mkdir -p $CI_PROJECT_DIR/.bl + - export HOMEBREW_LOGS=$CI_PROJECT_DIR/.bl + - mkdir -p $CI_PROJECT_DIR/.bt + - export HOMEBREW_TEMP=$CI_PROJECT_DIR/.bt # update and install packages - brew update diff --git a/cabal.project b/cabal.project index 99ea20b..0c156a4 100644 --- a/cabal.project +++ b/cabal.project @@ -12,6 +12,11 @@ constraints: http-io-streams -brotli, any.Cabal ==3.6.2.0, any.aeson >= 2.0.1.0 +source-repository-package + type: git + location: https://github.com/input-output-hk/optparse-applicative + tag: 7497a29cb998721a9068d5725d49461f2bba0e7a + package libarchive flags: -system-libarchive diff --git a/ghcup.cabal b/ghcup.cabal index 97cafda..c1e2ee7 100644 --- a/ghcup.cabal +++ b/ghcup.cabal @@ -51,6 +51,14 @@ flag no-exe library exposed-modules: GHCup + GHCup.Data.Versions + GHCup.GHC + GHCup.GHC.Rm + GHCup.GHC.Unset + GHCup.GHC.Set + GHCup.GHC.Compile + GHCup.GHC.Common + GHCup.GHC.Install GHCup.Download GHCup.Download.Common GHCup.Errors @@ -233,7 +241,7 @@ executable ghcup , libarchive ^>=3.0.3.0 , megaparsec >=8.0.0 && <9.1 , mtl ^>=2.2 - , optparse-applicative >=0.15.1.0 && <0.17 + , optparse-applicative-fork >=0.15.1.0 && <0.17 , pretty ^>=1.1.3.1 , pretty-terminal ^>=0.1.0.0 , resourcet ^>=1.2.2 diff --git a/lib/GHCup.hs b/lib/GHCup.hs index bfc976e..5ee0eea 100644 --- a/lib/GHCup.hs +++ b/lib/GHCup.hs @@ -23,9 +23,14 @@ and so on. These are the entry points. -} -module GHCup where +module GHCup + ( module GHCup.GHC + , module GHCup + ) + where +import GHCup.GHC import GHCup.Download import GHCup.Errors import GHCup.Platform @@ -171,242 +176,6 @@ fetchGHCSrc v mfp = do ------------------------- --- | Like 'installGHCBin', except takes the 'DownloadInfo' as --- argument instead of looking it up from 'GHCupDownloads'. -installGHCBindist :: ( MonadFail m - , MonadMask m - , MonadCatch m - , MonadReader env m - , HasDirs env - , HasSettings env - , HasPlatformReq env - , HasLog env - , MonadResource m - , MonadIO m - , MonadUnliftIO m - ) - => DownloadInfo -- ^ where/how to download - -> Version -- ^ the version to install - -> Maybe FilePath -- ^ isolated filepath if user passed any - -> Bool -- ^ Force install - -> Excepts - '[ AlreadyInstalled - , BuildFailed - , DigestError - , GPGError - , DownloadFailed - , NoDownload - , NotInstalled - , UnknownArchive - , TarDirDoesNotExist - , DirNotEmpty - , ArchiveResult - , ProcessError - ] - m - () -installGHCBindist dlinfo ver isoFilepath forceInstall = do - let tver = mkTVer ver - - lift $ logDebug $ "Requested to install GHC with " <> prettyVer ver - - regularGHCInstalled <- lift $ checkIfToolInstalled GHC ver - - if - | not forceInstall - , regularGHCInstalled - , Nothing <- isoFilepath -> do - throwE $ AlreadyInstalled GHC ver - - | forceInstall - , regularGHCInstalled - , Nothing <- isoFilepath -> do - lift $ logInfo "Removing the currently installed GHC version first!" - liftE $ rmGHCVer tver - - | otherwise -> pure () - - -- download (or use cached version) - dl <- liftE $ downloadCached dlinfo Nothing - - -- prepare paths - ghcdir <- lift $ ghcupGHCDir tver - - toolchainSanityChecks - - case isoFilepath of - Just isoDir -> do -- isolated install - lift $ logInfo $ "isolated installing GHC to " <> T.pack isoDir - liftE $ installPackedGHC dl (view dlSubdir dlinfo) isoDir ver forceInstall - Nothing -> do -- regular install - liftE $ installPackedGHC dl (view dlSubdir dlinfo) ghcdir ver forceInstall - - -- make symlinks & stuff when regular install, - liftE $ postGHCInstall tver - - where - toolchainSanityChecks = do - r <- forM ["CC", "LD"] (liftIO . lookupEnv) - case catMaybes r of - [] -> pure () - _ -> do - lift $ logWarn $ "CC/LD environment variable is set. This will change the compiler/linker" - <> "\n" <> "GHC uses internally and can cause defunct GHC in some cases (e.g. in Anaconda" - <> "\n" <> "environments). If you encounter problems, unset CC and LD and reinstall." - - --- | Install a packed GHC distribution. This only deals with unpacking and the GHC --- build system and nothing else. -installPackedGHC :: ( MonadMask m - , MonadCatch m - , MonadReader env m - , HasDirs env - , HasPlatformReq env - , HasSettings env - , MonadThrow m - , HasLog env - , MonadIO m - , MonadUnliftIO m - , MonadFail m - ) - => FilePath -- ^ Path to the packed GHC bindist - -> Maybe TarDir -- ^ Subdir of the archive - -> FilePath -- ^ Path to install to - -> Version -- ^ The GHC version - -> Bool -- ^ Force install - -> Excepts - '[ BuildFailed - , UnknownArchive - , TarDirDoesNotExist - , DirNotEmpty - , ArchiveResult - , ProcessError - ] m () -installPackedGHC dl msubdir inst ver forceInstall = do - PlatformRequest {..} <- lift getPlatformReq - - unless forceInstall - (liftE $ installDestSanityCheck inst) - - -- unpack - tmpUnpack <- lift mkGhcupTmpDir - liftE $ cleanUpOnError tmpUnpack (unpackToDir tmpUnpack dl) - liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform tmpUnpack - - -- the subdir of the archive where we do the work - workdir <- maybe (pure tmpUnpack) - (liftE . intoSubdir tmpUnpack) - msubdir - - liftE $ runBuildAction tmpUnpack - (Just inst) - (installUnpackedGHC workdir inst ver) - where - -- | Does basic checks for isolated installs - -- Isolated Directory: - -- 1. if it doesn't exist -> proceed - -- 2. if it exists and is empty -> proceed - -- 3. if it exists and is non-empty -> panic and leave the house - installDestSanityCheck :: ( MonadIO m - , MonadCatch m - ) => - FilePath -> - Excepts '[DirNotEmpty] m () - installDestSanityCheck isoDir = do - hideErrorDef [doesNotExistErrorType] () $ do - contents <- liftIO $ getDirectoryContentsRecursive isoDir - unless (null contents) (throwE $ DirNotEmpty isoDir) - - - --- | Install an unpacked GHC distribution. This only deals with the GHC --- build system and nothing else. -installUnpackedGHC :: ( MonadReader env m - , HasPlatformReq env - , HasDirs env - , HasSettings env - , MonadThrow m - , HasLog env - , MonadIO m - , MonadUnliftIO m - , MonadMask m - ) - => FilePath -- ^ Path to the unpacked GHC bindist (where the configure script resides) - -> FilePath -- ^ Path to install to - -> Version -- ^ The GHC version - -> Excepts '[ProcessError] m () -installUnpackedGHC path inst ver - | isWindows = do - lift $ logInfo "Installing GHC (this may take a while)" - -- Windows bindists are relocatable and don't need - -- to run configure. - -- We also must make sure to preserve mtime to not confuse ghc-pkg. - lift $ withRunInIO $ \run -> flip onException (run $ recyclePathForcibly inst) $ copyDirectoryRecursive path inst $ \source dest -> do - mtime <- getModificationTime source - moveFilePortable source dest - setModificationTime dest mtime - | otherwise = do - PlatformRequest {..} <- lift getPlatformReq - - let alpineArgs - | ver >= [vver|8.2.2|], Linux Alpine <- _rPlatform - = ["--disable-ld-override"] - | otherwise - = [] - - lift $ logInfo "Installing GHC (this may take a while)" - lEM $ execLogged "sh" - ("./configure" : ("--prefix=" <> inst) - : alpineArgs - ) - (Just path) - "ghc-configure" - Nothing - lEM $ make ["install"] (Just path) - pure () - - --- | Installs GHC into @~\/.ghcup\/ghc/\@ and places the --- following symlinks in @~\/.ghcup\/bin@: --- --- * @ghc-x.y.z -> ..\/ghc\/x.y.z\/bin/ghc@ --- * @ghc-x.y -> ..\/ghc\/x.y.z\/bin/ghc@ (if x.y.z is the latest x.y version) -installGHCBin :: ( MonadFail m - , MonadMask m - , MonadCatch m - , MonadReader env m - , HasPlatformReq env - , HasGHCupInfo env - , HasDirs env - , HasSettings env - , HasLog env - , MonadResource m - , MonadIO m - , MonadUnliftIO m - ) - => Version -- ^ the version to install - -> Maybe FilePath -- ^ isolated install filepath, if user passed any - -> Bool -- ^ force install - -> Excepts - '[ AlreadyInstalled - , BuildFailed - , DigestError - , GPGError - , DownloadFailed - , NoDownload - , NotInstalled - , UnknownArchive - , TarDirDoesNotExist - , DirNotEmpty - , ArchiveResult - , ProcessError - ] - m - () -installGHCBin ver isoFilepath forceInstall = do - dlinfo <- liftE $ getDownloadInfo GHC ver - liftE $ installGHCBindist dlinfo ver isoFilepath forceInstall - -- | Like 'installCabalBin', except takes the 'DownloadInfo' as -- argument instead of looking it up from 'GHCupDownloads'. @@ -1049,124 +818,6 @@ installStackUnpacked path inst mver' forceInstall = do --- | Set GHC symlinks in @~\/.ghcup\/bin@ for the requested GHC version. The behavior depends --- on `SetGHC`: --- --- * SetGHCOnly: @~\/.ghcup\/bin\/ghc -> ~\/.ghcup\/ghc\/\\/bin\/ghc@ --- * SetGHC_XY: @~\/.ghcup\/bin\/ghc-X.Y -> ~\/.ghcup\/ghc\/\\/bin\/ghc@ --- * SetGHC_XYZ: @~\/.ghcup\/bin\/ghc-\ -> ~\/.ghcup\/ghc\/\\/bin\/ghc@ --- --- Additionally creates a @~\/.ghcup\/share -> ~\/.ghcup\/ghc\/\\/share symlink@ --- for 'SetGHCOnly' constructor. -setGHC :: ( MonadReader env m - , HasDirs env - , HasLog env - , MonadThrow m - , MonadFail m - , MonadIO m - , MonadCatch m - , MonadMask m - , MonadUnliftIO m - ) - => GHCTargetVersion - -> SetGHC - -> Excepts '[NotInstalled] m GHCTargetVersion -setGHC ver sghc = do - let verS = T.unpack $ prettyVer (_tvVersion ver) - ghcdir <- lift $ ghcupGHCDir ver - - whenM (lift $ not <$> ghcInstalled ver) (throwE (NotInstalled GHC ver)) - - -- symlink destination - Dirs {..} <- lift getDirs - - -- first delete the old symlinks (this fixes compatibility issues - -- with old ghcup) - case sghc of - SetGHCOnly -> liftE $ rmPlain (_tvTarget ver) - SetGHC_XY -> liftE $ rmMajorSymlinks ver - SetGHC_XYZ -> liftE $ rmMinorSymlinks ver - - -- for ghc tools (ghc, ghci, haddock, ...) - verfiles <- ghcToolFiles ver - forM_ verfiles $ \file -> do - mTargetFile <- case sghc of - SetGHCOnly -> pure $ Just file - SetGHC_XY -> do - handle - (\(e :: ParseError) -> lift $ logWarn (T.pack $ displayException e) >> pure Nothing) - $ do - (mj, mi) <- getMajorMinorV (_tvVersion ver) - let major' = intToText mj <> "." <> intToText mi - pure $ Just (file <> "-" <> T.unpack major') - SetGHC_XYZ -> - pure $ Just (file <> "-" <> verS) - - -- create symlink - forM mTargetFile $ \targetFile -> do - let fullF = binDir targetFile <> exeExt - fileWithExt = file <> exeExt - destL <- lift $ ghcLinkDestination fileWithExt ver - lift $ createLink destL fullF - - -- create symlink for share dir - when (isNothing . _tvTarget $ ver) $ lift $ symlinkShareDir ghcdir verS - - when (sghc == SetGHCOnly) $ lift warnAboutHlsCompatibility - - pure ver - - where - - symlinkShareDir :: ( MonadReader env m - , HasDirs env - , MonadIO m - , HasLog env - , MonadCatch m - , MonadMask m - ) - => FilePath - -> String - -> m () - symlinkShareDir ghcdir ver' = do - Dirs {..} <- getDirs - let destdir = baseDir - case sghc of - SetGHCOnly -> do - let sharedir = "share" - let fullsharedir = ghcdir sharedir - logDebug $ "Checking for sharedir existence: " <> T.pack fullsharedir - whenM (liftIO $ doesDirectoryExist fullsharedir) $ do - let fullF = destdir sharedir - let targetF = "." "ghc" ver' sharedir - logDebug $ "rm -f " <> T.pack fullF - hideError doesNotExistErrorType $ rmDirectoryLink fullF - logDebug $ "ln -s " <> T.pack targetF <> " " <> T.pack fullF - - if isWindows - then liftIO - -- On windows we need to be more permissive - -- in case symlinks can't be created, be just - -- give up here. This symlink isn't strictly necessary. - $ hideError permissionErrorType - $ hideError illegalOperationErrorType - $ createDirectoryLink targetF fullF - else liftIO - $ createDirectoryLink targetF fullF - _ -> pure () - -unsetGHC :: ( MonadReader env m - , HasDirs env - , HasLog env - , MonadThrow m - , MonadFail m - , MonadIO m - , MonadMask m - ) - => Maybe Text - -> Excepts '[NotInstalled] m () -unsetGHC = rmPlain - -- | Set the @~\/.ghcup\/bin\/cabal@ symlink. setCabal :: ( MonadMask m @@ -1303,31 +954,6 @@ unsetStack = do hideError doesNotExistErrorType $ rmLink stackbin --- | Warn if the installed and set HLS is not compatible with the installed and --- set GHC version. -warnAboutHlsCompatibility :: ( MonadReader env m - , HasDirs env - , HasLog env - , MonadThrow m - , MonadCatch m - , MonadIO m - ) - => m () -warnAboutHlsCompatibility = do - supportedGHC <- hlsGHCVersions - currentGHC <- fmap _tvVersion <$> ghcSet Nothing - currentHLS <- hlsSet - - case (currentGHC, currentHLS) of - (Just gv, Just hv) | gv `notElem` supportedGHC -> do - logWarn $ - "GHC " <> T.pack (prettyShow gv) <> " is not compatible with " <> - "Haskell Language Server " <> T.pack (prettyShow hv) <> "." <> "\n" <> - "Haskell IDE support may not work until this is fixed." <> "\n" <> - "Install a different HLS version, or install and set one of the following GHCs:" <> "\n" <> - T.pack (prettyShow supportedGHC) - - _ -> return () ------------------ --[ List tools ]-- @@ -1689,56 +1315,6 @@ listVersions lt' criteria = do -------------------- --- | Delete a ghc version and all its symlinks. --- --- This may leave GHCup without a "set" version. --- Will try to fix the ghc-x.y symlink after removal (e.g. to an --- older version). -rmGHCVer :: ( MonadReader env m - , HasDirs env - , MonadThrow m - , HasLog env - , MonadIO m - , MonadFail m - , MonadCatch m - , MonadMask m - , MonadUnliftIO m - ) - => GHCTargetVersion - -> Excepts '[NotInstalled] m () -rmGHCVer ver = do - isSetGHC <- lift $ fmap (== Just ver) $ ghcSet (_tvTarget ver) - - whenM (lift $ fmap not $ ghcInstalled ver) (throwE (NotInstalled GHC ver)) - dir <- lift $ ghcupGHCDir ver - - -- this isn't atomic, order matters - when isSetGHC $ do - lift $ logInfo "Removing ghc symlinks" - liftE $ rmPlain (_tvTarget ver) - - lift $ logInfo "Removing ghc-x.y.z symlinks" - liftE $ rmMinorSymlinks ver - - lift $ logInfo "Removing/rewiring ghc-x.y symlinks" - -- first remove - handle (\(_ :: ParseError) -> pure ()) $ liftE $ rmMajorSymlinks ver - -- then fix them (e.g. with an earlier version) - - lift $ logInfo $ "Removing directory recursively: " <> T.pack dir - lift $ recyclePathForcibly dir - - v' <- - handle - (\(e :: ParseError) -> lift $ logWarn (T.pack $ displayException e) >> pure Nothing) - $ fmap Just - $ getMajorMinorV (_tvVersion ver) - forM_ v' $ \(mj, mi) -> lift (getGHCForPVP (PVP (fromIntegral mj :| [fromIntegral mi])) (_tvTarget ver)) - >>= mapM_ (\v -> liftE $ setGHC v SetGHC_XY) - - Dirs {..} <- lift getDirs - - lift $ hideError doesNotExistErrorType $ rmDirectoryLink (baseDir "share") -- | Delete a cabal version. Will try to fix the @cabal@ symlink @@ -2066,476 +1642,6 @@ getDebugInfo = do - --------------- - --[ Compile ]-- - --------------- - - --- | Compile a GHC from source. This behaves wrt symlinks and installation --- the same as 'installGHCBin'. -compileGHC :: ( MonadMask m - , MonadReader env m - , HasDirs env - , HasPlatformReq env - , HasGHCupInfo env - , HasSettings env - , MonadThrow m - , MonadResource m - , HasLog env - , MonadIO m - , MonadUnliftIO m - , MonadFail m - ) - => Either GHCTargetVersion GitBranch -- ^ version to install - -> Maybe Version -- ^ overwrite version - -> Either Version FilePath -- ^ version to bootstrap with - -> Maybe Int -- ^ jobs - -> Maybe FilePath -- ^ build config - -> Maybe FilePath -- ^ patch directory - -> [Text] -- ^ additional args to ./configure - -> Maybe String -- ^ build flavour - -> Bool - -> Maybe FilePath -- ^ isolate dir - -> Excepts - '[ AlreadyInstalled - , BuildFailed - , DigestError - , GPGError - , DownloadFailed - , GHCupSetError - , NoDownload - , NotFoundInPATH - , PatchFailed - , UnknownArchive - , TarDirDoesNotExist - , NotInstalled - , DirNotEmpty - , ArchiveResult - , FileDoesNotExistError - , HadrianNotFound - , InvalidBuildConfig - , ProcessError - , CopyError - , BuildFailed - ] - m - GHCTargetVersion -compileGHC targetGhc ov bstrap jobs mbuildConfig patchdir aargs buildFlavour hadrian isolateDir - = do - PlatformRequest { .. } <- lift getPlatformReq - GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo - - (workdir, tmpUnpack, tver) <- case targetGhc of - -- unpack from version tarball - Left tver -> do - lift $ logDebug $ "Requested to compile: " <> tVerToText tver <> " with " <> either prettyVer T.pack bstrap - - -- download source tarball - dlInfo <- - preview (ix GHC % ix (tver ^. tvVersion) % viSourceDL % _Just) dls - ?? NoDownload - dl <- liftE $ downloadCached dlInfo Nothing - - -- unpack - tmpUnpack <- lift mkGhcupTmpDir - liftE $ cleanUpOnError tmpUnpack (unpackToDir tmpUnpack dl) - liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform tmpUnpack - - workdir <- maybe (pure tmpUnpack) - (liftE . intoSubdir tmpUnpack) - (view dlSubdir dlInfo) - forM_ patchdir (\dir -> liftE $ applyPatches dir workdir) - - pure (workdir, tmpUnpack, tver) - - -- clone from git - Right GitBranch{..} -> do - tmpUnpack <- lift mkGhcupTmpDir - let git args = execLogged "git" ("--no-pager":args) (Just tmpUnpack) "git" Nothing - tver <- reThrowAll @_ @'[PatchFailed, ProcessError, NotFoundInPATH] DownloadFailed $ do - let rep = fromMaybe "https://gitlab.haskell.org/ghc/ghc.git" repo - lift $ logInfo $ "Fetching git repo " <> T.pack rep <> " at ref " <> T.pack ref <> " (this may take a while)" - lEM $ git [ "init" ] - lEM $ git [ "remote" - , "add" - , "origin" - , fromString rep ] - - let fetch_args = - [ "fetch" - , "--depth" - , "1" - , "--quiet" - , "origin" - , fromString ref ] - lEM $ git fetch_args - - lEM $ git [ "checkout", "FETCH_HEAD" ] - lEM $ git [ "submodule", "update", "--init", "--depth", "1" ] - forM_ patchdir (\dir -> liftE $ applyPatches dir tmpUnpack) - lEM $ execWithGhcEnv "python3" ["./boot"] (Just tmpUnpack) "ghc-bootstrap" - lEM $ execWithGhcEnv "sh" ["./configure"] (Just tmpUnpack) "ghc-bootstrap" - CapturedProcess {..} <- lift $ makeOut - ["show!", "--quiet", "VALUE=ProjectVersion" ] (Just tmpUnpack) - case _exitCode of - ExitSuccess -> throwEither . MP.parse ghcProjectVersion "" . decUTF8Safe' $ _stdOut - ExitFailure c -> fail ("Could not figure out GHC project version. Exit code was: " <> show c <> ". Error was: " <> T.unpack (decUTF8Safe' _stdErr)) - - liftE $ catchWarn $ lEM @_ @'[ProcessError] $ darwinNotarization _rPlatform tmpUnpack - lift $ logInfo $ "Git version " <> T.pack ref <> " corresponds to GHC version " <> prettyVer tver - - pure (tmpUnpack, tmpUnpack, GHCTargetVersion Nothing tver) - -- the version that's installed may differ from the - -- compiled version, so the user can overwrite it - let installVer = maybe tver (\ov' -> tver { _tvVersion = ov' }) ov - - alreadyInstalled <- lift $ ghcInstalled installVer - alreadySet <- fmap (== Just installVer) $ lift $ ghcSet (_tvTarget installVer) - - when alreadyInstalled $ do - case isolateDir of - Just isoDir -> - lift $ logWarn $ "GHC " <> T.pack (prettyShow installVer) <> " already installed. Isolate installing to " <> T.pack isoDir - Nothing -> - lift $ logWarn $ "GHC " <> T.pack (prettyShow installVer) <> " already installed. Will overwrite existing version." - lift $ logWarn - "...waiting for 10 seconds before continuing, you can still abort..." - liftIO $ threadDelay 10000000 -- give the user a sec to intervene - - ghcdir <- case isolateDir of - Just isoDir -> pure isoDir - Nothing -> lift $ ghcupGHCDir installVer - - (mBindist, bmk) <- liftE $ runBuildAction - tmpUnpack - Nothing - (do - b <- if hadrian - then compileHadrianBindist tver workdir ghcdir - else compileMakeBindist tver workdir ghcdir - bmk <- liftIO $ handleIO (\_ -> pure "") $ B.readFile (build_mk workdir) - pure (b, bmk) - ) - - case isolateDir of - Nothing -> - -- only remove old ghc in regular installs - when alreadyInstalled $ do - lift $ logInfo "Deleting existing installation" - liftE $ rmGHCVer installVer - - _ -> pure () - - forM_ mBindist $ \bindist -> do - liftE $ installPackedGHC bindist - (Just $ RegexDir "ghc-.*") - ghcdir - (installVer ^. tvVersion) - False -- not a force install, since we already overwrite when compiling. - - liftIO $ B.writeFile (ghcdir ghcUpSrcBuiltFile) bmk - - case isolateDir of - -- set and make symlinks for regular (non-isolated) installs - Nothing -> do - reThrowAll GHCupSetError $ postGHCInstall installVer - -- restore - when alreadySet $ liftE $ void $ setGHC installVer SetGHCOnly - - _ -> pure () - - pure installVer - - where - defaultConf = - let cross_mk = $(LitE . StringL <$> (qAddDependentFile "data/build_mk/cross" >> runIO (readFile "data/build_mk/cross"))) - default_mk = $(LitE . StringL <$> (qAddDependentFile "data/build_mk/default" >> runIO (readFile "data/build_mk/default"))) - in case targetGhc of - Left (GHCTargetVersion (Just _) _) -> cross_mk - _ -> default_mk - - compileHadrianBindist :: ( MonadReader env m - , HasDirs env - , HasSettings env - , HasPlatformReq env - , MonadThrow m - , MonadCatch m - , HasLog env - , MonadIO m - , MonadFail m - ) - => GHCTargetVersion - -> FilePath - -> FilePath - -> Excepts - '[ FileDoesNotExistError - , HadrianNotFound - , InvalidBuildConfig - , PatchFailed - , ProcessError - , NotFoundInPATH - , CopyError] - m - (Maybe FilePath) -- ^ output path of bindist, None for cross - compileHadrianBindist tver workdir ghcdir = do - lEM $ execWithGhcEnv "python3" ["./boot"] (Just workdir) "ghc-bootstrap" - - liftE $ configureBindist tver workdir ghcdir - - lift $ logInfo "Building (this may take a while)..." - hadrian_build <- liftE $ findHadrianFile workdir - lEM $ execWithGhcEnv hadrian_build - ( maybe [] (\j -> ["-j" <> show j] ) jobs - ++ maybe [] (\bf -> ["--flavour=" <> bf]) buildFlavour - ++ ["binary-dist"] - ) - (Just workdir) "ghc-make" - [tar] <- liftIO $ findFiles - (workdir "_build" "bindist") - (makeRegexOpts compExtended - execBlank - ([s|^ghc-.*\.tar\..*$|] :: ByteString) - ) - liftE $ fmap Just $ copyBindist tver tar (workdir "_build" "bindist") - - findHadrianFile :: (MonadIO m) - => FilePath - -> Excepts - '[HadrianNotFound] - m - FilePath - findHadrianFile workdir = do - let possible_files = if isWindows - then ((workdir "hadrian") ) <$> ["build.bat"] - else ((workdir "hadrian") ) <$> ["build", "build.sh"] - exsists <- forM possible_files (\f -> liftIO (doesFileExist f) <&> (,f)) - case filter fst exsists of - [] -> throwE HadrianNotFound - ((_, x):_) -> pure x - - compileMakeBindist :: ( MonadReader env m - , HasDirs env - , HasSettings env - , HasPlatformReq env - , MonadThrow m - , MonadCatch m - , HasLog env - , MonadIO m - , MonadFail m - ) - => GHCTargetVersion - -> FilePath - -> FilePath - -> Excepts - '[ FileDoesNotExistError - , HadrianNotFound - , InvalidBuildConfig - , PatchFailed - , ProcessError - , NotFoundInPATH - , CopyError] - m - (Maybe FilePath) -- ^ output path of bindist, None for cross - compileMakeBindist tver workdir ghcdir = do - liftE $ configureBindist tver workdir ghcdir - - case mbuildConfig of - Just bc -> liftIOException - doesNotExistErrorType - (FileDoesNotExistError bc) - (liftIO $ copyFile bc (build_mk workdir)) - Nothing -> - liftIO $ T.writeFile (build_mk workdir) (addBuildFlavourToConf defaultConf) - - liftE $ checkBuildConfig (build_mk workdir) - - lift $ logInfo "Building (this may take a while)..." - lEM $ make (maybe [] (\j -> ["-j" <> fS (show j)]) jobs) (Just workdir) - - if | isCross tver -> do - lift $ logInfo "Installing cross toolchain..." - lEM $ make ["install"] (Just workdir) - pure Nothing - | otherwise -> do - lift $ logInfo "Creating bindist..." - lEM $ make ["binary-dist"] (Just workdir) - [tar] <- liftIO $ findFiles - workdir - (makeRegexOpts compExtended - execBlank - ([s|^ghc-.*\.tar\..*$|] :: ByteString) - ) - liftE $ fmap Just $ copyBindist tver tar workdir - - build_mk workdir = workdir "mk" "build.mk" - - copyBindist :: ( MonadReader env m - , HasDirs env - , HasSettings env - , HasPlatformReq env - , MonadIO m - , MonadThrow m - , MonadCatch m - , HasLog env - ) - => GHCTargetVersion - -> FilePath -- ^ tar file - -> FilePath -- ^ workdir - -> Excepts - '[CopyError] - m - FilePath - copyBindist tver tar workdir = do - Dirs {..} <- lift getDirs - pfreq <- lift getPlatformReq - c <- liftIO $ BL.readFile (workdir tar) - cDigest <- - fmap (T.take 8) - . lift - . throwEither - . E.decodeUtf8' - . B16.encode - . SHA256.hashlazy - $ c - cTime <- liftIO getCurrentTime - let tarName = makeValid ("ghc-" - <> T.unpack (tVerToText tver) - <> "-" - <> pfReqToString pfreq - <> "-" - <> iso8601Show cTime - <> "-" - <> T.unpack cDigest - <> ".tar" - <> takeExtension tar) - let tarPath = cacheDir tarName - copyFileE (workdir tar) - tarPath - lift $ logInfo $ "Copied bindist to " <> T.pack tarPath - pure tarPath - - checkBuildConfig :: (MonadReader env m, MonadCatch m, MonadIO m, HasLog env) - => FilePath - -> Excepts - '[FileDoesNotExistError, InvalidBuildConfig] - m - () - checkBuildConfig bc = do - c <- liftIOException - doesNotExistErrorType - (FileDoesNotExistError bc) - (liftIO $ B.readFile bc) - let lines' = fmap T.strip . T.lines $ decUTF8Safe c - - -- for cross, we need Stage1Only - case targetGhc of - Left (GHCTargetVersion (Just _) _) -> when ("Stage1Only = YES" `notElem` lines') $ throwE - (InvalidBuildConfig - [s|Cross compiling needs to be a Stage1 build, add "Stage1Only = YES" to your config!|] - ) - _ -> pure () - - forM_ buildFlavour $ \bf -> - when (T.pack ("BuildFlavour = " <> bf) `notElem` lines') $ do - lift $ logWarn $ "Customly specified build config overwrites --flavour=" <> T.pack bf <> " switch! Waiting 5 seconds..." - liftIO $ threadDelay 5000000 - - addBuildFlavourToConf bc = case buildFlavour of - Just bf -> "BuildFlavour = " <> T.pack bf <> "\n" <> bc - Nothing -> bc - - isCross :: GHCTargetVersion -> Bool - isCross = isJust . _tvTarget - - - configureBindist :: ( MonadReader env m - , HasDirs env - , HasSettings env - , HasPlatformReq env - , MonadThrow m - , MonadCatch m - , HasLog env - , MonadIO m - , MonadFail m - ) - => GHCTargetVersion - -> FilePath - -> FilePath - -> Excepts - '[ FileDoesNotExistError - , InvalidBuildConfig - , PatchFailed - , ProcessError - , NotFoundInPATH - , CopyError - ] - m - () - configureBindist tver workdir ghcdir = do - lift $ logInfo [s|configuring build|] - - if | _tvVersion tver >= [vver|8.8.0|] -> do - lEM $ execWithGhcEnv - "sh" - ("./configure" : maybe mempty - (\x -> ["--target=" <> T.unpack x]) - (_tvTarget tver) - ++ ["--prefix=" <> ghcdir] - ++ (if isWindows then ["--enable-tarballs-autodownload"] else []) - ++ fmap T.unpack aargs - ) - (Just workdir) - "ghc-conf" - | otherwise -> do - lEM $ execLogged - "sh" - ( [ "./configure", "--with-ghc=" <> either id id bghc - ] - ++ maybe mempty - (\x -> ["--target=" <> T.unpack x]) - (_tvTarget tver) - ++ ["--prefix=" <> ghcdir] - ++ (if isWindows then ["--enable-tarballs-autodownload"] else []) - ++ fmap T.unpack aargs - ) - (Just workdir) - "ghc-conf" - Nothing - pure () - - execWithGhcEnv :: ( MonadReader env m - , HasSettings env - , HasDirs env - , MonadIO m - , MonadThrow m) - => FilePath -- ^ thing to execute - -> [String] -- ^ args for the thing - -> Maybe FilePath -- ^ optionally chdir into this - -> FilePath -- ^ log filename (opened in append mode) - -> m (Either ProcessError ()) - execWithGhcEnv fp args dir logf = do - env <- ghcEnv - execLogged fp args dir logf (Just env) - - bghc = case bstrap of - Right g -> Right g - Left bver -> Left ("ghc-" <> (T.unpack . prettyVer $ bver) <> exeExt) - - ghcEnv :: (MonadThrow m, MonadIO m) => m [(String, String)] - ghcEnv = do - cEnv <- liftIO getEnvironment - bghcPath <- case bghc of - Right ghc' -> pure ghc' - Left bver -> do - spaths <- liftIO getSearchPath - throwMaybeM (NotFoundInPATH bver) $ liftIO (searchPath spaths bver) - pure (("GHC", bghcPath) : cEnv) - - - - - - --------------------- --[ Upgrade GHCup ]-- --------------------- @@ -2618,31 +1724,6 @@ upgradeGHCup mtarget force' = do --- | Creates @ghc-x.y.z@ and @ghc-x.y@ symlinks. This is used for --- both installing from source and bindist. -postGHCInstall :: ( MonadReader env m - , HasDirs env - , HasLog env - , MonadThrow m - , MonadFail m - , MonadIO m - , MonadCatch m - , MonadMask m - , MonadUnliftIO m - ) - => GHCTargetVersion - -> Excepts '[NotInstalled] m () -postGHCInstall ver@GHCTargetVersion {..} = do - void $ liftE $ setGHC ver SetGHC_XYZ - - -- Create ghc-x.y symlinks. This may not be the current - -- version, create it regardless. - v' <- - handle (\(e :: ParseError) -> lift $ logWarn (T.pack $ displayException e) >> pure Nothing) - $ fmap Just - $ getMajorMinorV _tvVersion - forM_ v' $ \(mj, mi) -> lift (getGHCForPVP (PVP (fromIntegral mj :| [fromIntegral mi])) _tvTarget) - >>= mapM_ (\v -> liftE $ setGHC v SetGHC_XY) -- | Reports the binary location of a given tool: @@ -2846,3 +1927,9 @@ rmTmp = do let p = tmpdir f logDebug $ "rm -rf " <> T.pack p rmPathForcibly p + + + + + + diff --git a/lib/GHCup/Utils.hs b/lib/GHCup/Utils.hs index 14fe832..8ee6d2e 100644 --- a/lib/GHCup/Utils.hs +++ b/lib/GHCup/Utils.hs @@ -36,6 +36,9 @@ import GHCup.System.Console.Windows #else import GHCup.System.Console.Posix #endif +import {-# SOURCE #-} GHCup.GHC.Common +import {-# SOURCE #-} GHCup.GHC.Set +import GHCup.Data.Versions import GHCup.Download import GHCup.Errors import GHCup.Types @@ -78,6 +81,7 @@ import System.FilePath import System.IO.Error import Text.Regex.Posix import URI.ByteString +import Text.PrettyPrint.HughesPJClass ( prettyShow ) import qualified Codec.Compression.BZip as BZip import qualified Codec.Compression.GZip as GZip @@ -121,161 +125,6 @@ import qualified Data.List.NonEmpty as NE - ------------------------ - --[ Symlink handling ]-- - ------------------------ - - --- | The symlink destination of a ghc tool. -ghcLinkDestination :: ( MonadReader env m - , HasDirs env - , MonadThrow m, MonadIO m) - => FilePath -- ^ the tool, such as 'ghc', 'haddock' etc. - -> GHCTargetVersion - -> m FilePath -ghcLinkDestination tool ver = do - Dirs {..} <- getDirs - ghcd <- ghcupGHCDir ver - pure (relativeSymlink binDir (ghcd "bin" tool)) - - --- | Removes the minor GHC symlinks, e.g. ghc-8.6.5. -rmMinorSymlinks :: ( MonadReader env m - , HasDirs env - , MonadIO m - , HasLog env - , MonadThrow m - , MonadFail m - , MonadMask m - ) - => GHCTargetVersion - -> Excepts '[NotInstalled] m () -rmMinorSymlinks tv@GHCTargetVersion{..} = do - Dirs {..} <- lift getDirs - - files <- liftE $ ghcToolFiles tv - forM_ files $ \f -> do - let f_xyz = f <> "-" <> T.unpack (prettyVer _tvVersion) <> exeExt - let fullF = binDir f_xyz - lift $ logDebug ("rm -f " <> T.pack fullF) - lift $ hideError doesNotExistErrorType $ rmLink fullF - - --- | Removes the set ghc version for the given target, if any. -rmPlain :: ( MonadReader env m - , HasDirs env - , HasLog env - , MonadThrow m - , MonadFail m - , MonadIO m - , MonadMask m - ) - => Maybe Text -- ^ target - -> Excepts '[NotInstalled] m () -rmPlain target = do - Dirs {..} <- lift getDirs - mtv <- lift $ ghcSet target - forM_ mtv $ \tv -> do - files <- liftE $ ghcToolFiles tv - forM_ files $ \f -> do - let fullF = binDir f <> exeExt - lift $ logDebug ("rm -f " <> T.pack fullF) - lift $ hideError doesNotExistErrorType $ rmLink fullF - -- old ghcup - let hdc_file = binDir "haddock-ghc" <> exeExt - lift $ logDebug ("rm -f " <> T.pack hdc_file) - lift $ hideError doesNotExistErrorType $ rmLink hdc_file - - --- | Remove the major GHC symlink, e.g. ghc-8.6. -rmMajorSymlinks :: ( MonadReader env m - , HasDirs env - , MonadIO m - , HasLog env - , MonadThrow m - , MonadFail m - , MonadMask m - ) - => GHCTargetVersion - -> Excepts '[NotInstalled] m () -rmMajorSymlinks tv@GHCTargetVersion{..} = do - Dirs {..} <- lift getDirs - (mj, mi) <- getMajorMinorV _tvVersion - let v' = intToText mj <> "." <> intToText mi - - files <- liftE $ ghcToolFiles tv - forM_ files $ \f -> do - let f_xy = f <> "-" <> T.unpack v' <> exeExt - let fullF = binDir f_xy - lift $ logDebug ("rm -f " <> T.pack fullF) - lift $ hideError doesNotExistErrorType $ rmLink fullF - - - - - ----------------------------------- - --[ Set/Installed introspection ]-- - ----------------------------------- - - --- | Whether the given GHC versin is installed. -ghcInstalled :: (MonadIO m, MonadReader env m, HasDirs env, MonadThrow m) => GHCTargetVersion -> m Bool -ghcInstalled ver = do - ghcdir <- ghcupGHCDir ver - liftIO $ doesDirectoryExist ghcdir - - --- | Whether the given GHC version is installed from source. -ghcSrcInstalled :: (MonadIO m, MonadReader env m, HasDirs env, MonadThrow m) => GHCTargetVersion -> m Bool -ghcSrcInstalled ver = do - ghcdir <- ghcupGHCDir ver - liftIO $ doesFileExist (ghcdir ghcUpSrcBuiltFile) - - --- | Whether the given GHC version is set as the current. -ghcSet :: (MonadReader env m, HasDirs env, MonadThrow m, MonadIO m) - => Maybe Text -- ^ the target of the GHC version, if any - -- (e.g. armv7-unknown-linux-gnueabihf) - -> m (Maybe GHCTargetVersion) -ghcSet mtarget = do - Dirs {..} <- getDirs - let ghc = maybe "ghc" (\t -> T.unpack t <> "-ghc") mtarget - let ghcBin = binDir ghc <> exeExt - - -- link destination is of the form ../ghc//bin/ghc - -- for old ghcup, it is ../ghc//bin/ghc- - liftIO $ handleIO' NoSuchThing (\_ -> pure Nothing) $ do - link <- liftIO $ getLinkTarget ghcBin - Just <$> ghcLinkVersion link - where - ghcLinkVersion :: MonadThrow m => FilePath -> m GHCTargetVersion - ghcLinkVersion (T.pack . dropSuffix exeExt -> t) = throwEither $ MP.parse parser "ghcLinkVersion" t - where - parser = - (do - _ <- parseUntil1 ghcSubPath - _ <- ghcSubPath - r <- parseUntil1 pathSep - rest <- MP.getInput - MP.setInput r - x <- ghcTargetVerP - MP.setInput rest - pure x - ) - <* pathSep - <* MP.takeRest - <* MP.eof - ghcSubPath = pathSep <* MP.chunk "ghc" *> pathSep - --- | Get all installed GHCs by reading ~/.ghcup/ghc/. --- If a dir cannot be parsed, returns left. -getInstalledGHCs :: (MonadReader env m, HasDirs env, MonadIO m) => m [Either FilePath GHCTargetVersion] -getInstalledGHCs = do - ghcdir <- ghcupGHCBaseDir - fs <- liftIO $ hideErrorDef [NoSuchThing] [] $ listDirectory ghcdir - forM fs $ \f -> case parseGHCupGHCDir f of - Right r -> pure $ Right r - Left _ -> pure $ Left f -- | Get all installed cabals, by matching on @~\/.ghcup\/bin/cabal-*@. @@ -589,79 +438,6 @@ hlsSymlinks = do - ----------------------------------------- - --[ Major version introspection (X.Y) ]-- - ----------------------------------------- - - --- | Extract (major, minor) from any version. -getMajorMinorV :: MonadThrow m => Version -> m (Int, Int) -getMajorMinorV Version {..} = case _vChunks of - ((Digits x :| []) :| ((Digits y :| []):_)) -> pure (fromIntegral x, fromIntegral y) - _ -> throwM $ ParseError "Could not parse X.Y from version" - - -matchMajor :: Version -> Int -> Int -> Bool -matchMajor v' major' minor' = case getMajorMinorV v' of - Just (x, y) -> x == major' && y == minor' - Nothing -> False - --- | Match PVP prefix. --- --- >>> matchPVPrefix [pver|8.8|] [pver|8.8.4|] --- True --- >>> matchPVPrefix [pver|8|] [pver|8.8.4|] --- True --- >>> matchPVPrefix [pver|8.10|] [pver|8.8.4|] --- False --- >>> matchPVPrefix [pver|8.10|] [pver|8.10.7|] --- True -matchPVPrefix :: PVP -> PVP -> Bool -matchPVPrefix (toL -> prefix) (toL -> full) = and $ zipWith (==) prefix full - -toL :: PVP -> [Int] -toL (PVP inner) = fmap fromIntegral $ NE.toList inner - - --- | Get the latest installed full GHC version that satisfies the given (possibly partial) --- PVP version. -getGHCForPVP :: (MonadReader env m, HasDirs env, MonadIO m, MonadThrow m) - => PVP - -> Maybe Text -- ^ the target triple - -> m (Maybe GHCTargetVersion) -getGHCForPVP pvpIn mt = do - ghcs <- rights <$> getInstalledGHCs - -- we're permissive here... failed parse just means we have no match anyway - let ghcs' = catMaybes $ flip fmap ghcs $ \GHCTargetVersion{..} -> do - (pvp_, rest) <- versionToPVP _tvVersion - pure (pvp_, rest, _tvTarget) - - getGHCForPVP' pvpIn ghcs' mt - --- | Like 'getGHCForPVP', except with explicit input parameter. --- --- >>> getGHCForPVP' [pver|8|] installedVersions Nothing --- Just (GHCTargetVersion {_tvTarget = Nothing, _tvVersion = Version {_vEpoch = Nothing, _vChunks = (Digits 8 :| []) :| [Digits 10 :| [],Digits 7 :| []], _vRel = [Str "debug" :| []], _vMeta = Just "lol"}}) --- >>> fmap prettyShow $ getGHCForPVP' [pver|8.8|] installedVersions Nothing --- "Just 8.8.4" --- >>> fmap prettyShow $ getGHCForPVP' [pver|8.10.4|] installedVersions Nothing --- "Just 8.10.4" -getGHCForPVP' :: MonadThrow m - => PVP - -> [(PVP, Text, Maybe Text)] -- ^ installed GHCs - -> Maybe Text -- ^ the target triple - -> m (Maybe GHCTargetVersion) -getGHCForPVP' pvpIn ghcs' mt = do - let mResult = lastMay - . sortBy (\(x, _, _) (y, _, _) -> compare x y) - . filter - (\(pvp_, _, target) -> - target == mt && matchPVPrefix pvp_ pvpIn - ) - $ ghcs' - forM mResult $ \(pvp_, rest, target) -> do - ver' <- pvpToVersion pvp_ rest - pure (GHCTargetVersion target ver') -- | Get the latest available ghc for the given PVP version, which @@ -811,39 +587,6 @@ getLatestBaseVersion av pvpVer = ------------- --- | Get tool files from @~\/.ghcup\/bin\/ghc\/\\/bin\/\*@ --- while ignoring @*-\@ symlinks and accounting for cross triple prefix. --- --- Returns unversioned relative files without extension, e.g.: --- --- - @["hsc2hs","haddock","hpc","runhaskell","ghc","ghc-pkg","ghci","runghc","hp2ps"]@ -ghcToolFiles :: (MonadReader env m, HasDirs env, MonadThrow m, MonadFail m, MonadIO m) - => GHCTargetVersion - -> Excepts '[NotInstalled] m [FilePath] -ghcToolFiles ver = do - ghcdir <- lift $ ghcupGHCDir ver - let bindir = ghcdir "bin" - - -- fail if ghc is not installed - whenM (fmap not $ liftIO $ doesDirectoryExist ghcdir) - (throwE (NotInstalled GHC ver)) - - files <- liftIO (listDirectory bindir >>= filterM (doesFileExist . (bindir ))) - pure (getUniqueTools . groupToolFiles . fmap (dropSuffix exeExt) $ files) - - where - - groupToolFiles :: [FilePath] -> [[(FilePath, String)]] - groupToolFiles = groupBy (\(a, _) (b, _) -> a == b) . fmap (splitOnPVP "-") - - getUniqueTools :: [[(FilePath, String)]] -> [String] - getUniqueTools = filter (isNotAnyInfix blackListedTools) . nub . fmap fst . filter ((== "") . snd) . concat - - blackListedTools :: [String] - blackListedTools = ["haddock-ghc"] - - isNotAnyInfix :: [String] -> String -> Bool - isNotAnyInfix xs t = foldr (\a b -> not (a `isInfixOf` t) && b) True xs -- | This file, when residing in @~\/.ghcup\/ghc\/\\/@ signals that @@ -1142,3 +885,33 @@ ensureDirectories (Dirs baseDir binDir cacheDir logsDir confDir trashDir) = do ghcBinaryName :: GHCTargetVersion -> String ghcBinaryName (GHCTargetVersion (Just t) v') = T.unpack (t <> "-ghc-" <> prettyVer v' <> T.pack exeExt) ghcBinaryName (GHCTargetVersion Nothing v') = T.unpack ("ghc-" <> prettyVer v' <> T.pack exeExt) + + + +-- | Warn if the installed and set HLS is not compatible with the installed and +-- set GHC version. +warnAboutHlsCompatibility :: ( MonadReader env m + , HasDirs env + , HasLog env + , MonadThrow m + , MonadCatch m + , MonadIO m + ) + => m () +warnAboutHlsCompatibility = do + supportedGHC <- hlsGHCVersions + currentGHC <- fmap _tvVersion <$> ghcSet Nothing + currentHLS <- hlsSet + + case (currentGHC, currentHLS) of + (Just gv, Just hv) | gv `notElem` supportedGHC -> do + logWarn $ + "GHC " <> T.pack (prettyShow gv) <> " is not compatible with " <> + "Haskell Language Server " <> T.pack (prettyShow hv) <> "." <> "\n" <> + "Haskell IDE support may not work until this is fixed." <> "\n" <> + "Install a different HLS version, or install and set one of the following GHCs:" <> "\n" <> + T.pack (prettyShow supportedGHC) + + _ -> return () + +