ensureGHC :: (MonadIO m, MonadMask m, MonadLogger m, MonadReader env m, HasConfig env, HasHttpManager env, MonadBaseControl IO m)
          => SetupOpts
          -> m (Maybe [FilePath])
ensureGHC sopts = do
    case soptsWantedCompiler sopts of
        GhcVersion v | v < $(mkVersion "7.8") -> do
            $logWarn "stack will almost certainly fail with GHC below version 7.8"
            $logWarn "Valiantly attempting to run anyway, but I know this is doomed"
            $logWarn "For more information, see: https://github.com/commercialhaskell/stack/issues/648"
            $logWarn ""
        _ -> return ()

    -- Check the available GHCs
    menv0 <- getMinimalEnvOverride

    msystem <-
        if soptsUseSystem sopts
            then getSystemGHC menv0
            else return Nothing

    Platform expectedArch _ <- asks getPlatform

    let needLocal = case msystem of
            Nothing -> True
            Just _ | soptsSkipGhcCheck sopts -> False
            Just (system, arch) ->
                not (isWanted system) ||
                arch /= expectedArch
        isWanted = isWantedCompiler (soptsCompilerCheck sopts) (soptsWantedCompiler sopts) . GhcVersion

    -- If we need to install a GHC, try to do so
    mpaths <- if needLocal
        then do
            -- Avoid having to load it twice
            siRef <- liftIO $ newIORef Nothing
            manager <- asks getHttpManager
            let getSetupInfo' = liftIO $ do
                    msi <- readIORef siRef
                    case msi of
                        Just si -> return si
                        Nothing -> do
                            si <- getSetupInfo manager
                            writeIORef siRef $ Just si
                            return si

            config <- asks getConfig
            installed <- runReaderT listInstalled config

            -- Install GHC
            ghcIdent <- case getInstalledTool installed $(mkPackageName "ghc") isWanted of
                Just ident -> return ident
                Nothing
                    | soptsInstallIfMissing sopts -> do
                        si <- getSetupInfo'
                        downloadAndInstallGHC menv0 si (soptsWantedCompiler sopts) (soptsCompilerCheck sopts)
                    | otherwise -> do
                        Platform arch _ <- asks getPlatform
                        throwM $ GHCVersionMismatch
                            msystem
                            (soptsWantedCompiler sopts, arch)
                            (soptsCompilerCheck sopts)
                            (soptsStackYaml sopts)
                            (fromMaybe
                                "Try running stack setup to locally install the correct GHC"
                                $ soptsResolveMissingGHC sopts)

            -- Install git on windows, if necessary
            mgitIdent <- case configPlatform config of
                Platform _ os | isWindows os && not (soptsSkipMsys sopts) ->
                    case getInstalledTool installed $(mkPackageName "git") (const True) of
                        Just ident -> return (Just ident)
                        Nothing
                            | soptsInstallIfMissing sopts -> do
                                si <- getSetupInfo'
                                let VersionedDownloadInfo version info = siPortableGit si
                                Just <$> downloadAndInstallTool si info $(mkPackageName "git") version installGitWindows
                            | otherwise -> do
                                $logWarn "Continuing despite missing tool: git"
                                return Nothing
                _ -> return Nothing

            let idents = catMaybes [Just ghcIdent, mgitIdent]
            paths <- runReaderT (mapM binDirs idents) config
            return $ Just $ map toFilePathNoTrailingSlash $ concat paths
        else return Nothing

    menv <-
        case mpaths of
            Nothing -> return menv0
            Just paths -> do
                config <- asks getConfig
                let m0 = unEnvOverride menv0
                    path0 = Map.lookup "PATH" m0
                    path = augmentPath paths path0
                    m = Map.insert "PATH" path m0
                mkEnvOverride (configPlatform config) (removeHaskellEnvVars m)

    when (soptsUpgradeCabal sopts) $ do
        unless needLocal $ do
            $logWarn "Trying to upgrade Cabal library on a GHC not installed by stack."
            $logWarn "This may fail, caveat emptor!"

        upgradeCabal menv

    when (soptsSanityCheck sopts) $ sanityCheck menv

    return mpaths

-- | Install the newest version of Cabal globally
ensureGHC :: (MonadIO m, MonadMask m, MonadLogger m, MonadReader env m, HasConfig env, HasHttpManager env, MonadBaseControl IO m)
          => SetupOpts
          -> m (Maybe [FilePath])
ensureGHC sopts = do
    case soptsWantedCompiler sopts of
        GhcVersion v | v < $(mkVersion "7.8") -> do
            $logWarn "stack will almost certainly fail with GHC below version 7.8"
            $logWarn "Valiantly attempting to run anyway, but I know this is doomed"
            $logWarn "For more information, see: https://github.com/commercialhaskell/stack/issues/648"
            $logWarn ""
        _ -> return ()

    -- Check the available GHCs
    menv0 <- getMinimalEnvOverride

    msystem <-
        if soptsUseSystem sopts
            then getSystemGHC menv0
            else return Nothing

    Platform expectedArch _ <- asks getPlatform

    let needLocal = case msystem of
            Nothing -> True
            Just _ | soptsSkipGhcCheck sopts -> False
            Just (system, arch) ->
                not (isWanted system) ||
                arch /= expectedArch
        isWanted = isWantedCompiler (soptsCompilerCheck sopts) (soptsWantedCompiler sopts) . GhcVersion

    -- If we need to install a GHC, try to do so
    mpaths <- if needLocal
        then do
            -- Avoid having to load it twice
            siRef <- liftIO $ newIORef Nothing
            manager <- asks getHttpManager
            let getSetupInfo' = liftIO $ do
                    msi <- readIORef siRef
                    case msi of
                        Just si -> return si
                        Nothing -> do
                            si <- getSetupInfo manager
                            writeIORef siRef $ Just si
                            return si

            config <- asks getConfig
            installed <- runReaderT listInstalled config

            -- Install GHC
            ghcIdent <- case getInstalledTool installed $(mkPackageName "ghc") isWanted of
                Just ident -> return ident
                Nothing
                    | soptsInstallIfMissing sopts -> do
                        si <- getSetupInfo'
                        downloadAndInstallGHC menv0 si (soptsWantedCompiler sopts) (soptsCompilerCheck sopts)
                    | otherwise -> do
                        Platform arch _ <- asks getPlatform
                        throwM $ GHCVersionMismatch
                            msystem
                            (soptsWantedCompiler sopts, arch)
                            (soptsCompilerCheck sopts)
                            (soptsStackYaml sopts)
                            (fromMaybe
                                "Try running stack setup to locally install the correct GHC"
                                $ soptsResolveMissingGHC sopts)

            -- Install git on windows, if necessary
            mgitIdent <- case configPlatform config of
                Platform _ os | isWindows os && not (soptsSkipMsys sopts) ->
                    case getInstalledTool installed $(mkPackageName "git") (const True) of
                        Just ident -> return (Just ident)
                        Nothing
                            | soptsInstallIfMissing sopts -> do
                                si <- getSetupInfo'
                                let VersionedDownloadInfo version info = siPortableGit si
                                Just <$> downloadAndInstallTool si info $(mkPackageName "git") version installGitWindows
                            | otherwise -> do
                                $logWarn "Continuing despite missing tool: git"
                                return Nothing
                _ -> return Nothing

            let idents = catMaybes [Just ghcIdent, mgitIdent]
            paths <- runReaderT (mapM binDirs idents) config
            return $ Just $ map toFilePathNoTrailingSlash $ concat paths
        else return Nothing

    menv <-
        case mpaths of
            Nothing -> return menv0
            Just paths -> do
                config <- asks getConfig
                let m0 = unEnvOverride menv0
                    path0 = Map.lookup "PATH" m0
                    path = augmentPath paths path0
                    m = Map.insert "PATH" path m0
                mkEnvOverride (configPlatform config) (removeHaskellEnvVars m)

    when (soptsUpgradeCabal sopts) $ do
        unless needLocal $ do
            $logWarn "Trying to upgrade Cabal library on a GHC not installed by stack."
            $logWarn "This may fail, caveat emptor!"

        upgradeCabal menv

    when (soptsSanityCheck sopts) $ sanityCheck menv

    return mpaths

-- | Install the newest version of Cabal globally
ensureGHC sopts = do
    case soptsWantedCompiler sopts of
        GhcVersion v | v < $(mkVersion "7.8") -> do
            $logWarn "stack will almost certainly fail with GHC below version 7.8"
            $logWarn "Valiantly attempting to run anyway, but I know this is doomed"
            $logWarn "For more information, see: https://github.com/commercialhaskell/stack/issues/648"
            $logWarn ""
        _ -> return ()

    -- Check the available GHCs
    menv0 <- getMinimalEnvOverride

    msystem <-
        if soptsUseSystem sopts
            then getSystemGHC menv0
            else return Nothing

    Platform expectedArch _ <- asks getPlatform

    let needLocal = case msystem of
            Nothing -> True
            Just _ | soptsSkipGhcCheck sopts -> False
            Just (system, arch) ->
                not (isWanted system) ||
                arch /= expectedArch
        isWanted = isWantedCompiler (soptsCompilerCheck sopts) (soptsWantedCompiler sopts) . GhcVersion

    -- If we need to install a GHC, try to do so
    mpaths <- if needLocal
        then do
            -- Avoid having to load it twice
            siRef <- liftIO $ newIORef Nothing
            manager <- asks getHttpManager
            let getSetupInfo' = liftIO $ do
                    msi <- readIORef siRef
                    case msi of
                        Just si -> return si
                        Nothing -> do
                            si <- getSetupInfo manager
                            writeIORef siRef $ Just si
                            return si

            config <- asks getConfig
            installed <- runReaderT listInstalled config

            -- Install GHC
            ghcIdent <- case getInstalledTool installed $(mkPackageName "ghc") isWanted of
                Just ident -> return ident
                Nothing
                    | soptsInstallIfMissing sopts -> do
                        si <- getSetupInfo'
                        downloadAndInstallGHC menv0 si (soptsWantedCompiler sopts) (soptsCompilerCheck sopts)
                    | otherwise -> do
                        Platform arch _ <- asks getPlatform
                        throwM $ GHCVersionMismatch
                            msystem
                            (soptsWantedCompiler sopts, arch)
                            (soptsCompilerCheck sopts)
                            (soptsStackYaml sopts)
                            (fromMaybe
                                "Try running stack setup to locally install the correct GHC"
                                $ soptsResolveMissingGHC sopts)

            -- Install git on windows, if necessary
            mgitIdent <- case configPlatform config of
                Platform _ os | isWindows os && not (soptsSkipMsys sopts) ->
                    case getInstalledTool installed $(mkPackageName "git") (const True) of
                        Just ident -> return (Just ident)
                        Nothing
                            | soptsInstallIfMissing sopts -> do
                                si <- getSetupInfo'
                                let VersionedDownloadInfo version info = siPortableGit si
                                Just <$> downloadAndInstallTool si info $(mkPackageName "git") version installGitWindows
                            | otherwise -> do
                                $logWarn "Continuing despite missing tool: git"
                                return Nothing
                _ -> return Nothing

            let idents = catMaybes [Just ghcIdent, mgitIdent]
            paths <- runReaderT (mapM binDirs idents) config
            return $ Just $ map toFilePathNoTrailingSlash $ concat paths
        else return Nothing

    menv <-
        case mpaths of
            Nothing -> return menv0
            Just paths -> do
                config <- asks getConfig
                let m0 = unEnvOverride menv0
                    path0 = Map.lookup "PATH" m0
                    path = augmentPath paths path0
                    m = Map.insert "PATH" path m0
                mkEnvOverride (configPlatform config) (removeHaskellEnvVars m)

    when (soptsUpgradeCabal sopts) $ do
        unless needLocal $ do
            $logWarn "Trying to upgrade Cabal library on a GHC not installed by stack."
            $logWarn "This may fail, caveat emptor!"

        upgradeCabal menv

    when (soptsSanityCheck sopts) $ sanityCheck menv

    return mpaths

-- | Install the newest version of Cabal globally