{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE RecursiveDo #-}
{-# LANGUAGE RecordWildCards #-}
module HIE.Bios.Cradle (
      findCradle
    , loadCradle
    , loadImplicitCradle
    , yamlConfig
    , defaultCradle
    , isCabalCradle
    , isStackCradle
    , isDirectCradle
    , isBiosCradle
    , isNoneCradle
    , isMultiCradle
    , isDefaultCradle
    , isOtherCradle
    , getCradle
    , readProcessWithOutputs
    , readProcessWithCwd
    , makeCradleResult
    -- | Cradle project configuration types
    , CradleProjectConfig(..)
    ,
  ) where

import Control.Applicative ((<|>), optional)
import Data.Bifunctor (first)
import Control.DeepSeq
import Control.Exception (handleJust)
import qualified Data.Yaml as Yaml
import Data.Void
import Data.Char (isSpace)
import System.Exit
import System.Directory hiding (findFile)
import Colog.Core (LogAction (..), WithSeverity (..), Severity (..), (<&))
import Control.Monad
import Control.Monad.Extra (unlessM)
import Control.Monad.Trans.Cont
import Control.Monad.Trans.Maybe
import Control.Monad.IO.Class
import Data.Conduit.Process
import qualified Data.Conduit.Combinators as C
import qualified Data.Conduit as C
import qualified Data.Conduit.Text as C
import qualified Data.HashMap.Strict as Map
import Data.Maybe (fromMaybe, maybeToList)
import Data.List
import Data.List.Extra (trimEnd)
import Data.Ord (Down(..))
import qualified Data.Text as T
import System.Environment
import System.FilePath
import System.PosixCompat.Files
import System.Info.Extra (isWindows)
import System.IO (hClose, hGetContents, hSetBuffering, BufferMode(LineBuffering), withFile, IOMode(..))
import System.IO.Error (isPermissionError)
import System.IO.Temp

import HIE.Bios.Config
import HIE.Bios.Environment (getCacheDir)
import HIE.Bios.Types hiding (ActionName(..))
import HIE.Bios.Wrappers
import qualified HIE.Bios.Types as Types
import qualified HIE.Bios.Ghc.Gap as Gap

import GHC.Fingerprint (fingerprintString)
import GHC.ResponseFile (escapeArgs)

import Data.Version
import Data.IORef
import Text.ParserCombinators.ReadP (readP_to_S)

----------------------------------------------------------------

-- | Given @root\/foo\/bar.hs@, return @root\/hie.yaml@, or wherever the yaml file was found.
--
-- Note, 'findCradle' used to **not** work for directories and required a Haskell file.
-- This has been fixed since @0.14.0@.
-- However, 'loadCradle' and 'loadImplicitCradle' still require a Haskell
-- source file and won't work properly with a directory parameter.
findCradle :: FilePath -> IO (Maybe FilePath)
findCradle :: String -> IO (Maybe String)
findCradle String
wfile = do
    String
wdir <- String -> IO Bool
doesDirectoryExist String
wfile forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
      Bool
True ->  forall (f :: * -> *) a. Applicative f => a -> f a
pure String
wfile
      Bool
False -> forall (f :: * -> *) a. Applicative f => a -> f a
pure (String -> String
takeDirectory String
wfile)
    forall (m :: * -> *) a. MaybeT m a -> m (Maybe a)
runMaybeT (String -> MaybeT IO String
yamlConfig String
wdir)

-- | Given root\/hie.yaml load the Cradle.
loadCradle :: LogAction IO (WithSeverity Log) -> FilePath -> IO (Cradle Void)
loadCradle :: LogAction IO (WithSeverity Log) -> String -> IO (Cradle Void)
loadCradle LogAction IO (WithSeverity Log)
l = forall b a.
(FromJSON b, Show a) =>
LogAction IO (WithSeverity Log)
-> (b -> CradleAction a) -> String -> IO (Cradle a)
loadCradleWithOpts LogAction IO (WithSeverity Log)
l forall a. Void -> a
absurd

-- | Given root\/foo\/bar.hs, load an implicit cradle
loadImplicitCradle :: Show a => LogAction IO (WithSeverity Log) -> FilePath -> IO (Cradle a)
loadImplicitCradle :: forall a.
Show a =>
LogAction IO (WithSeverity Log) -> String -> IO (Cradle a)
loadImplicitCradle LogAction IO (WithSeverity Log)
l String
wfile = do
  let wdir :: String
wdir = String -> String
takeDirectory String
wfile
  Maybe (CradleConfig Void, String)
cfg <- forall (m :: * -> *) a. MaybeT m a -> m (Maybe a)
runMaybeT (forall a. String -> MaybeT IO (CradleConfig a, String)
implicitConfig String
wdir)
  case Maybe (CradleConfig Void, String)
cfg of
    Just (CradleConfig Void, String)
bc -> forall a b.
Show a =>
LogAction IO (WithSeverity Log)
-> (b -> CradleAction a)
-> (CradleConfig b, String)
-> IO (Cradle a)
getCradle LogAction IO (WithSeverity Log)
l forall a. Void -> a
absurd (CradleConfig Void, String)
bc
    Maybe (CradleConfig Void, String)
Nothing -> forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a. LogAction IO (WithSeverity Log) -> String -> Cradle a
defaultCradle LogAction IO (WithSeverity Log)
l String
wdir

-- | Finding 'Cradle'.
--   Find a cabal file by tracing ancestor directories.
--   Find a sandbox according to a cabal sandbox config
--   in a cabal directory.
loadCradleWithOpts :: (Yaml.FromJSON b, Show a) => LogAction IO (WithSeverity Log) -> (b -> CradleAction a) -> FilePath -> IO (Cradle a)
loadCradleWithOpts :: forall b a.
(FromJSON b, Show a) =>
LogAction IO (WithSeverity Log)
-> (b -> CradleAction a) -> String -> IO (Cradle a)
loadCradleWithOpts LogAction IO (WithSeverity Log)
l b -> CradleAction a
buildCustomCradle String
wfile = do
    CradleConfig b
cradleConfig <- forall b. FromJSON b => String -> IO (CradleConfig b)
readCradleConfig String
wfile
    forall a b.
Show a =>
LogAction IO (WithSeverity Log)
-> (b -> CradleAction a)
-> (CradleConfig b, String)
-> IO (Cradle a)
getCradle LogAction IO (WithSeverity Log)
l b -> CradleAction a
buildCustomCradle (CradleConfig b
cradleConfig, String -> String
takeDirectory String
wfile)

getCradle :: Show a => LogAction IO (WithSeverity Log) ->  (b -> CradleAction a) -> (CradleConfig b, FilePath) -> IO (Cradle a)
getCradle :: forall a b.
Show a =>
LogAction IO (WithSeverity Log)
-> (b -> CradleAction a)
-> (CradleConfig b, String)
-> IO (Cradle a)
getCradle LogAction IO (WithSeverity Log)
l b -> CradleAction a
buildCustomCradle (CradleConfig b
cc, String
wdir) = do
    [ResolvedCradle b]
rcs <- forall a. String -> [ResolvedCradle a] -> IO [ResolvedCradle a]
canonicalizeResolvedCradles String
wdir [ResolvedCradle b]
cs
    forall a b.
Show a =>
LogAction IO (WithSeverity Log)
-> (b -> CradleAction a)
-> String
-> [ResolvedCradle b]
-> IO (Cradle a)
resolvedCradlesToCradle LogAction IO (WithSeverity Log)
l b -> CradleAction a
buildCustomCradle String
wdir [ResolvedCradle b]
rcs
  where
    cs :: [ResolvedCradle b]
cs = forall a. String -> CradleConfig a -> [ResolvedCradle a]
resolveCradleTree String
wdir CradleConfig b
cc


-- | The actual type of action we will be using to process a file
data ConcreteCradle a
  = ConcreteCabal CabalType
  | ConcreteStack StackType
  | ConcreteBios Callable (Maybe Callable) (Maybe FilePath)
  | ConcreteDirect [String]
  | ConcreteNone
  | ConcreteOther a
  deriving Int -> ConcreteCradle a -> String -> String
forall a. Show a => Int -> ConcreteCradle a -> String -> String
forall a. Show a => [ConcreteCradle a] -> String -> String
forall a. Show a => ConcreteCradle a -> String
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
showList :: [ConcreteCradle a] -> String -> String
$cshowList :: forall a. Show a => [ConcreteCradle a] -> String -> String
show :: ConcreteCradle a -> String
$cshow :: forall a. Show a => ConcreteCradle a -> String
showsPrec :: Int -> ConcreteCradle a -> String -> String
$cshowsPrec :: forall a. Show a => Int -> ConcreteCradle a -> String -> String
Show

-- | ConcreteCradle augmented with information on which file the
-- cradle applies
data ResolvedCradle a
 = ResolvedCradle
 { forall a. ResolvedCradle a -> String
prefix :: FilePath -- ^ the prefix to match files
 , forall a. ResolvedCradle a -> [String]
cradleDeps :: [FilePath] -- ^ accumulated dependencies
 , forall a. ResolvedCradle a -> ConcreteCradle a
concreteCradle :: ConcreteCradle a
 } deriving Int -> ResolvedCradle a -> String -> String
forall a. Show a => Int -> ResolvedCradle a -> String -> String
forall a. Show a => [ResolvedCradle a] -> String -> String
forall a. Show a => ResolvedCradle a -> String
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
showList :: [ResolvedCradle a] -> String -> String
$cshowList :: forall a. Show a => [ResolvedCradle a] -> String -> String
show :: ResolvedCradle a -> String
$cshow :: forall a. Show a => ResolvedCradle a -> String
showsPrec :: Int -> ResolvedCradle a -> String -> String
$cshowsPrec :: forall a. Show a => Int -> ResolvedCradle a -> String -> String
Show

-- | The final cradle config that specifies the cradle for
-- each prefix we know how to handle
data ResolvedCradles a
 = ResolvedCradles
 { forall a. ResolvedCradles a -> String
cradleRoot :: FilePath
 , forall a. ResolvedCradles a -> [ResolvedCradle a]
resolvedCradles :: [ResolvedCradle a] -- ^ In order of decreasing specificity
 , forall a. ResolvedCradles a -> ProgramVersions
cradleProgramVersions :: ProgramVersions
 }

data ProgramVersions =
  ProgramVersions { ProgramVersions -> CachedIO (Maybe Version)
cabalVersion  :: CachedIO (Maybe Version)
                  , ProgramVersions -> CachedIO (Maybe Version)
stackVersion  :: CachedIO (Maybe Version)
                  , ProgramVersions -> CachedIO (Maybe Version)
ghcVersion    :: CachedIO (Maybe Version)
                  }

newtype CachedIO a = CachedIO (IORef (Either (IO a) a))

makeCachedIO :: IO a -> IO (CachedIO a)
makeCachedIO :: forall a. IO a -> IO (CachedIO a)
makeCachedIO IO a
act = forall a. IORef (Either (IO a) a) -> CachedIO a
CachedIO forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. a -> IO (IORef a)
newIORef (forall a b. a -> Either a b
Left IO a
act)

runCachedIO :: CachedIO a -> IO a
runCachedIO :: forall a. CachedIO a -> IO a
runCachedIO (CachedIO IORef (Either (IO a) a)
ref) =
  forall a. IORef a -> IO a
readIORef IORef (Either (IO a) a)
ref forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
    Right a
x -> forall (f :: * -> *) a. Applicative f => a -> f a
pure a
x
    Left IO a
act -> do
      a
x <- IO a
act
      forall a. IORef a -> a -> IO ()
writeIORef IORef (Either (IO a) a)
ref (forall a b. b -> Either a b
Right a
x)
      forall (f :: * -> *) a. Applicative f => a -> f a
pure a
x

makeVersions :: LogAction IO (WithSeverity Log) -> FilePath -> ([String] -> IO (CradleLoadResult String)) -> IO ProgramVersions
makeVersions :: LogAction IO (WithSeverity Log)
-> String
-> ([String] -> IO (CradleLoadResult String))
-> IO ProgramVersions
makeVersions LogAction IO (WithSeverity Log)
l String
wdir [String] -> IO (CradleLoadResult String)
ghc = do
  CachedIO (Maybe Version)
cabalVersion <- forall a. IO a -> IO (CachedIO a)
makeCachedIO forall a b. (a -> b) -> a -> b
$ LogAction IO (WithSeverity Log) -> String -> IO (Maybe Version)
getCabalVersion LogAction IO (WithSeverity Log)
l String
wdir
  CachedIO (Maybe Version)
stackVersion <- forall a. IO a -> IO (CachedIO a)
makeCachedIO forall a b. (a -> b) -> a -> b
$ LogAction IO (WithSeverity Log) -> String -> IO (Maybe Version)
getStackVersion LogAction IO (WithSeverity Log)
l String
wdir
  CachedIO (Maybe Version)
ghcVersion   <- forall a. IO a -> IO (CachedIO a)
makeCachedIO forall a b. (a -> b) -> a -> b
$ ([String] -> IO (CradleLoadResult String)) -> IO (Maybe Version)
getGhcVersion [String] -> IO (CradleLoadResult String)
ghc
  forall (f :: * -> *) a. Applicative f => a -> f a
pure ProgramVersions{CachedIO (Maybe Version)
ghcVersion :: CachedIO (Maybe Version)
stackVersion :: CachedIO (Maybe Version)
cabalVersion :: CachedIO (Maybe Version)
ghcVersion :: CachedIO (Maybe Version)
stackVersion :: CachedIO (Maybe Version)
cabalVersion :: CachedIO (Maybe Version)
..}

getCabalVersion :: LogAction IO (WithSeverity Log) -> FilePath -> IO (Maybe Version)
getCabalVersion :: LogAction IO (WithSeverity Log) -> String -> IO (Maybe Version)
getCabalVersion LogAction IO (WithSeverity Log)
l String
wdir = do
  CradleLoadResult String
res <- LogAction IO (WithSeverity Log)
-> String
-> String
-> [String]
-> String
-> IO (CradleLoadResult String)
readProcessWithCwd LogAction IO (WithSeverity Log)
l String
wdir String
"cabal" [String
"--numeric-version"] String
""
  case CradleLoadResult String
res of
    CradleSuccess String
stdo ->
      forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ String -> Maybe Version
versionMaybe String
stdo
    CradleLoadResult String
_ -> forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a. Maybe a
Nothing

getStackVersion :: LogAction IO (WithSeverity Log) -> FilePath -> IO (Maybe Version)
getStackVersion :: LogAction IO (WithSeverity Log) -> String -> IO (Maybe Version)
getStackVersion LogAction IO (WithSeverity Log)
l String
wdir = do
  CradleLoadResult String
res <- LogAction IO (WithSeverity Log)
-> String
-> String
-> [String]
-> String
-> IO (CradleLoadResult String)
readProcessWithCwd LogAction IO (WithSeverity Log)
l String
wdir String
"stack" [String
"--numeric-version"] String
""
  case CradleLoadResult String
res of
    CradleSuccess String
stdo ->
      forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ String -> Maybe Version
versionMaybe String
stdo
    CradleLoadResult String
_ -> forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a. Maybe a
Nothing

getGhcVersion :: ([String] -> IO (CradleLoadResult String)) -> IO (Maybe Version)
getGhcVersion :: ([String] -> IO (CradleLoadResult String)) -> IO (Maybe Version)
getGhcVersion [String] -> IO (CradleLoadResult String)
ghc = do
  CradleLoadResult String
res <- [String] -> IO (CradleLoadResult String)
ghc [String
"--numeric-version"]
  case CradleLoadResult String
res of
    CradleSuccess String
stdo ->
      forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ String -> Maybe Version
versionMaybe String
stdo
    CradleLoadResult String
_ -> forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a. Maybe a
Nothing

versionMaybe :: String -> Maybe Version
versionMaybe :: String -> Maybe Version
versionMaybe String
xs = case forall a. [a] -> [a]
reverse forall a b. (a -> b) -> a -> b
$ forall a. ReadP a -> ReadS a
readP_to_S ReadP Version
parseVersion String
xs of
  [] -> forall a. Maybe a
Nothing
  ((Version, String)
x:[(Version, String)]
_) -> forall a. a -> Maybe a
Just (forall a b. (a, b) -> a
fst (Version, String)
x)


addActionDeps :: [FilePath] -> CradleLoadResult ComponentOptions -> CradleLoadResult ComponentOptions
addActionDeps :: [String]
-> CradleLoadResult ComponentOptions
-> CradleLoadResult ComponentOptions
addActionDeps [String]
deps =
  forall c r.
c -> (CradleError -> c) -> (r -> c) -> CradleLoadResult r -> c
cradleLoadResult
      forall r. CradleLoadResult r
CradleNone
      (\CradleError
err -> forall r. CradleError -> CradleLoadResult r
CradleFail (CradleError
err { cradleErrorDependencies :: [String]
cradleErrorDependencies = CradleError -> [String]
cradleErrorDependencies CradleError
err forall a. Eq a => [a] -> [a] -> [a]
`union` [String]
deps }))
      (\(ComponentOptions [String]
os' String
dir [String]
ds) -> forall r. r -> CradleLoadResult r
CradleSuccess ([String] -> String -> [String] -> ComponentOptions
ComponentOptions [String]
os' String
dir ([String]
ds forall a. Eq a => [a] -> [a] -> [a]
`union` [String]
deps)))


resolvedCradlesToCradle :: Show a => LogAction IO (WithSeverity Log) -> (b -> CradleAction a) -> FilePath -> [ResolvedCradle b] -> IO (Cradle a)
resolvedCradlesToCradle :: forall a b.
Show a =>
LogAction IO (WithSeverity Log)
-> (b -> CradleAction a)
-> String
-> [ResolvedCradle b]
-> IO (Cradle a)
resolvedCradlesToCradle LogAction IO (WithSeverity Log)
logger b -> CradleAction a
buildCustomCradle String
root [ResolvedCradle b]
cs = mdo
  let run_ghc_cmd :: [String] -> IO (CradleLoadResult String)
run_ghc_cmd [String]
args =
        -- We're being lazy here and just returning the ghc path for the
        -- first non-none cradle. This shouldn't matter in practice: all
        -- sub cradles should be using the same ghc version!
        case forall a. (a -> Bool) -> [a] -> [a]
filter (forall {a}. ActionName a -> Bool
notNoneType forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. CradleAction a -> ActionName a
actionName) forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map forall a b. (a, b) -> b
snd [(ResolvedCradle b, CradleAction a)]
cradleActions of
          [] -> forall (m :: * -> *) a. Monad m => a -> m a
return forall r. CradleLoadResult r
CradleNone
          (CradleAction a
act:[CradleAction a]
_) ->
            forall a.
CradleAction a -> [String] -> IO (CradleLoadResult String)
runGhcCmd
              CradleAction a
act
              [String]
args
  ProgramVersions
versions <- LogAction IO (WithSeverity Log)
-> String
-> ([String] -> IO (CradleLoadResult String))
-> IO ProgramVersions
makeVersions LogAction IO (WithSeverity Log)
logger String
root [String] -> IO (CradleLoadResult String)
run_ghc_cmd
  let rcs :: ResolvedCradles b
rcs = forall a.
String
-> [ResolvedCradle a] -> ProgramVersions -> ResolvedCradles a
ResolvedCradles String
root [ResolvedCradle b]
cs ProgramVersions
versions
      cradleActions :: [(ResolvedCradle b, CradleAction a)]
cradleActions = [ (ResolvedCradle b
c, forall b a.
LogAction IO (WithSeverity Log)
-> (b -> CradleAction a)
-> ResolvedCradles b
-> String
-> ResolvedCradle b
-> CradleAction a
resolveCradleAction LogAction IO (WithSeverity Log)
logger b -> CradleAction a
buildCustomCradle ResolvedCradles b
rcs String
root ResolvedCradle b
c) | ResolvedCradle b
c <- [ResolvedCradle b]
cs ]
      err_msg :: String -> [String]
err_msg String
fp
        = [String
"Multi Cradle: No prefixes matched"
          , String
"pwd: " forall a. [a] -> [a] -> [a]
++ String
root
          , String
"filepath: " forall a. [a] -> [a] -> [a]
++ String
fp
          , String
"prefixes:"
          ] forall a. [a] -> [a] -> [a]
++ [forall a. Show a => a -> String
show (forall a. ResolvedCradle a -> String
prefix ResolvedCradle b
pf, forall a. CradleAction a -> ActionName a
actionName CradleAction a
cc) | (ResolvedCradle b
pf, CradleAction a
cc) <- [(ResolvedCradle b, CradleAction a)]
cradleActions]
  forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ Cradle
    { cradleRootDir :: String
cradleRootDir = String
root
    , cradleLogger :: LogAction IO (WithSeverity Log)
cradleLogger = LogAction IO (WithSeverity Log)
logger
    , cradleOptsProg :: CradleAction a
cradleOptsProg = CradleAction
      { actionName :: ActionName a
actionName = forall {a}. ActionName a
multiActionName
      , runCradle :: String -> [String] -> IO (CradleLoadResult ComponentOptions)
runCradle  = \String
fp [String]
prev -> do
          String
absfp <- String -> IO String
makeAbsolute String
fp
          case forall a. (a -> String) -> String -> [a] -> Maybe a
selectCradle (forall a. ResolvedCradle a -> String
prefix forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a, b) -> a
fst) String
absfp [(ResolvedCradle b, CradleAction a)]
cradleActions of
            Just (ResolvedCradle b
rc, CradleAction a
act) -> do
              [String]
-> CradleLoadResult ComponentOptions
-> CradleLoadResult ComponentOptions
addActionDeps (forall a. ResolvedCradle a -> [String]
cradleDeps ResolvedCradle b
rc) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a.
CradleAction a
-> String -> [String] -> IO (CradleLoadResult ComponentOptions)
runCradle CradleAction a
act String
fp [String]
prev
            Maybe (ResolvedCradle b, CradleAction a)
Nothing -> forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall r. CradleError -> CradleLoadResult r
CradleFail forall a b. (a -> b) -> a -> b
$ [String] -> ExitCode -> [String] -> CradleError
CradleError [] ExitCode
ExitSuccess (String -> [String]
err_msg String
fp)
      , runGhcCmd :: [String] -> IO (CradleLoadResult String)
runGhcCmd = [String] -> IO (CradleLoadResult String)
run_ghc_cmd
      }
    }
  where
    multiActionName :: ActionName a
multiActionName
      | forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (\ResolvedCradle b
c -> forall {a}. ResolvedCradle a -> Bool
isStackCradleConfig ResolvedCradle b
c Bool -> Bool -> Bool
|| forall {a}. ResolvedCradle a -> Bool
isNoneCradleConfig ResolvedCradle b
c) [ResolvedCradle b]
cs
      = forall {a}. ActionName a
Types.Stack
      | forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (\ResolvedCradle b
c -> forall {a}. ResolvedCradle a -> Bool
isCabalCradleConfig ResolvedCradle b
c Bool -> Bool -> Bool
|| forall {a}. ResolvedCradle a -> Bool
isNoneCradleConfig ResolvedCradle b
c) [ResolvedCradle b]
cs
      = forall {a}. ActionName a
Types.Cabal
      | [Bool
True] <- forall a b. (a -> b) -> [a] -> [b]
map forall {a}. ResolvedCradle a -> Bool
isBiosCradleConfig forall a b. (a -> b) -> a -> b
$ forall a. (a -> Bool) -> [a] -> [a]
filter (Bool -> Bool
not forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall {a}. ResolvedCradle a -> Bool
isNoneCradleConfig) [ResolvedCradle b]
cs
      = forall {a}. ActionName a
Types.Bios
      | [Bool
True] <- forall a b. (a -> b) -> [a] -> [b]
map forall {a}. ResolvedCradle a -> Bool
isDirectCradleConfig forall a b. (a -> b) -> a -> b
$ forall a. (a -> Bool) -> [a] -> [a]
filter (Bool -> Bool
not forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall {a}. ResolvedCradle a -> Bool
isNoneCradleConfig) [ResolvedCradle b]
cs
      = forall {a}. ActionName a
Types.Direct
      | Bool
otherwise
      = forall {a}. ActionName a
Types.Multi

    isStackCradleConfig :: ResolvedCradle a -> Bool
isStackCradleConfig ResolvedCradle a
cfg = case forall a. ResolvedCradle a -> ConcreteCradle a
concreteCradle ResolvedCradle a
cfg of
      ConcreteStack{} -> Bool
True
      ConcreteCradle a
_               -> Bool
False

    isCabalCradleConfig :: ResolvedCradle a -> Bool
isCabalCradleConfig ResolvedCradle a
cfg = case forall a. ResolvedCradle a -> ConcreteCradle a
concreteCradle ResolvedCradle a
cfg of
      ConcreteCabal{} -> Bool
True
      ConcreteCradle a
_               -> Bool
False

    isBiosCradleConfig :: ResolvedCradle a -> Bool
isBiosCradleConfig ResolvedCradle a
cfg = case forall a. ResolvedCradle a -> ConcreteCradle a
concreteCradle ResolvedCradle a
cfg of
      ConcreteBios{}  -> Bool
True
      ConcreteCradle a
_               -> Bool
False

    isDirectCradleConfig :: ResolvedCradle a -> Bool
isDirectCradleConfig ResolvedCradle a
cfg = case forall a. ResolvedCradle a -> ConcreteCradle a
concreteCradle ResolvedCradle a
cfg of
      ConcreteDirect{} -> Bool
True
      ConcreteCradle a
_                -> Bool
False

    isNoneCradleConfig :: ResolvedCradle a -> Bool
isNoneCradleConfig ResolvedCradle a
cfg = case forall a. ResolvedCradle a -> ConcreteCradle a
concreteCradle ResolvedCradle a
cfg of
      ConcreteNone{} -> Bool
True
      ConcreteCradle a
_              -> Bool
False

    notNoneType :: ActionName a -> Bool
notNoneType ActionName a
Types.None = Bool
False
    notNoneType ActionName a
_ = Bool
True


resolveCradleAction :: LogAction IO (WithSeverity Log) ->  (b -> CradleAction a) -> ResolvedCradles b -> FilePath -> ResolvedCradle b -> CradleAction a
resolveCradleAction :: forall b a.
LogAction IO (WithSeverity Log)
-> (b -> CradleAction a)
-> ResolvedCradles b
-> String
-> ResolvedCradle b
-> CradleAction a
resolveCradleAction LogAction IO (WithSeverity Log)
l b -> CradleAction a
buildCustomCradle ResolvedCradles b
cs String
root ResolvedCradle b
cradle =
  case forall a. ResolvedCradle a -> ConcreteCradle a
concreteCradle ResolvedCradle b
cradle of
    ConcreteCabal CabalType
t -> forall b a.
LogAction IO (WithSeverity Log)
-> ResolvedCradles b
-> String
-> Maybe String
-> CradleProjectConfig
-> CradleAction a
cabalCradle LogAction IO (WithSeverity Log)
l ResolvedCradles b
cs String
root (CabalType -> Maybe String
cabalComponent CabalType
t) (String -> Maybe String -> CradleProjectConfig
projectConfigFromMaybe String
root (CabalType -> Maybe String
cabalProjectFile CabalType
t))
    ConcreteStack StackType
t -> forall a.
LogAction IO (WithSeverity Log)
-> String -> Maybe String -> CradleProjectConfig -> CradleAction a
stackCradle LogAction IO (WithSeverity Log)
l String
root (StackType -> Maybe String
stackComponent StackType
t) (String -> Maybe String -> CradleProjectConfig
projectConfigFromMaybe String
root (StackType -> Maybe String
stackYaml StackType
t))
    ConcreteBios Callable
bios Maybe Callable
deps Maybe String
mbGhc -> forall a.
LogAction IO (WithSeverity Log)
-> String
-> Callable
-> Maybe Callable
-> Maybe String
-> CradleAction a
biosCradle LogAction IO (WithSeverity Log)
l String
root Callable
bios Maybe Callable
deps Maybe String
mbGhc
    ConcreteDirect [String]
xs -> forall a.
LogAction IO (WithSeverity Log)
-> String -> [String] -> CradleAction a
directCradle LogAction IO (WithSeverity Log)
l String
root [String]
xs
    ConcreteCradle b
ConcreteNone -> forall a. CradleAction a
noneCradle
    ConcreteOther b
a -> b -> CradleAction a
buildCustomCradle b
a

resolveCradleTree :: FilePath -> CradleConfig a -> [ResolvedCradle a]
resolveCradleTree :: forall a. String -> CradleConfig a -> [ResolvedCradle a]
resolveCradleTree String
root (CradleConfig [String]
confDeps CradleTree a
confTree) = forall {a}.
String -> [String] -> CradleTree a -> [ResolvedCradle a]
go String
root [String]
confDeps CradleTree a
confTree
  where
    go :: String -> [String] -> CradleTree a -> [ResolvedCradle a]
go String
pfix [String]
deps CradleTree a
tree = case CradleTree a
tree of
      Cabal CabalType
t              -> [forall a.
String -> [String] -> ConcreteCradle a -> ResolvedCradle a
ResolvedCradle String
pfix [String]
deps (forall a. CabalType -> ConcreteCradle a
ConcreteCabal CabalType
t)]
      Stack StackType
t              -> [forall a.
String -> [String] -> ConcreteCradle a -> ResolvedCradle a
ResolvedCradle String
pfix [String]
deps (forall a. StackType -> ConcreteCradle a
ConcreteStack StackType
t)]
      Bios Callable
bios Maybe Callable
dcmd Maybe String
mbGhc -> [forall a.
String -> [String] -> ConcreteCradle a -> ResolvedCradle a
ResolvedCradle String
pfix [String]
deps (forall a.
Callable -> Maybe Callable -> Maybe String -> ConcreteCradle a
ConcreteBios Callable
bios Maybe Callable
dcmd Maybe String
mbGhc)]
      Direct [String]
xs            -> [forall a.
String -> [String] -> ConcreteCradle a -> ResolvedCradle a
ResolvedCradle String
pfix [String]
deps (forall a. [String] -> ConcreteCradle a
ConcreteDirect [String]
xs)]
      CradleTree a
None                 -> [forall a.
String -> [String] -> ConcreteCradle a -> ResolvedCradle a
ResolvedCradle String
pfix [String]
deps forall a. ConcreteCradle a
ConcreteNone]
      Other a
a Value
_            -> [forall a.
String -> [String] -> ConcreteCradle a -> ResolvedCradle a
ResolvedCradle String
pfix [String]
deps (forall a. a -> ConcreteCradle a
ConcreteOther a
a)]
      CabalMulti CabalType
dc [(String, CabalType)]
xs     -> [forall a.
String -> [String] -> ConcreteCradle a -> ResolvedCradle a
ResolvedCradle String
p    [String]
deps (forall a. CabalType -> ConcreteCradle a
ConcreteCabal (CabalType
dc forall a. Semigroup a => a -> a -> a
<> CabalType
c)) | (String
p, CabalType
c) <- [(String, CabalType)]
xs ]
      StackMulti StackType
dc [(String, StackType)]
xs     -> [forall a.
String -> [String] -> ConcreteCradle a -> ResolvedCradle a
ResolvedCradle String
p    [String]
deps (forall a. StackType -> ConcreteCradle a
ConcreteStack (StackType
dc forall a. Semigroup a => a -> a -> a
<> StackType
c)) | (String
p, StackType
c) <- [(String, StackType)]
xs ]
      Multi [(String, CradleConfig a)]
xs             -> forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [ String -> [String] -> CradleTree a -> [ResolvedCradle a]
go String
pfix' ([String]
deps forall a. [a] -> [a] -> [a]
++ [String]
deps') CradleTree a
tree' | (String
pfix', CradleConfig [String]
deps' CradleTree a
tree') <- [(String, CradleConfig a)]
xs]

-- | Try to infer an appropriate implicit cradle type from stuff we can find in the enclosing directories:
--   * If a .hie-bios file is found, we can treat this as a @Bios@ cradle
--   * If a stack.yaml file is found, we can treat this as a @Stack@ cradle
--   * If a cabal.project or an xyz.cabal file is found, we can treat this as a @Cabal@ cradle
inferCradleTree :: FilePath -> MaybeT IO (CradleTree a, FilePath)
inferCradleTree :: forall a. String -> MaybeT IO (CradleTree a, String)
inferCradleTree String
fp =
       forall {a}. MaybeT IO (CradleTree a, String)
maybeItsBios
   forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> forall {a}. MaybeT IO (CradleTree a, String)
maybeItsStack
   forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> forall {a}. MaybeT IO (CradleTree a, String)
maybeItsCabal
-- <|> maybeItsObelisk
-- <|> maybeItsObelisk

  where
  maybeItsBios :: MaybeT IO (CradleTree a, String)
maybeItsBios = (\String
wdir -> (forall a.
Callable -> Maybe Callable -> Maybe String -> CradleTree a
Bios (String -> Callable
Program forall a b. (a -> b) -> a -> b
$ String
wdir String -> String -> String
</> String
".hie-bios") forall a. Maybe a
Nothing forall a. Maybe a
Nothing, String
wdir)) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> MaybeT IO String
biosWorkDir String
fp

  maybeItsStack :: MaybeT IO (CradleTree a, String)
maybeItsStack = MaybeT IO String
stackExecutable forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> (forall a. StackType -> CradleTree a
Stack forall a b. (a -> b) -> a -> b
$ Maybe String -> Maybe String -> StackType
StackType forall a. Maybe a
Nothing forall a. Maybe a
Nothing,) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> MaybeT IO String
stackWorkDir String
fp

  maybeItsCabal :: MaybeT IO (CradleTree a, String)
maybeItsCabal = (forall a. CabalType -> CradleTree a
Cabal forall a b. (a -> b) -> a -> b
$ Maybe String -> Maybe String -> CabalType
CabalType forall a. Maybe a
Nothing forall a. Maybe a
Nothing,) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> MaybeT IO String
cabalWorkDir String
fp

  -- maybeItsObelisk = (Obelisk,) <$> obeliskWorkDir fp

  -- maybeItsBazel = (Bazel,) <$> rulesHaskellWorkDir fp


-- | Wraps up the cradle inferred by @inferCradleTree@ as a @CradleConfig@ with no dependencies
implicitConfig :: FilePath -> MaybeT IO (CradleConfig a, FilePath)
implicitConfig :: forall a. String -> MaybeT IO (CradleConfig a, String)
implicitConfig = (forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first) (forall a. [String] -> CradleTree a -> CradleConfig a
CradleConfig [String]
noDeps) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. String -> MaybeT IO (CradleTree a, String)
inferCradleTree
  where
  noDeps :: [FilePath]
  noDeps :: [String]
noDeps = []

yamlConfig :: FilePath ->  MaybeT IO FilePath
yamlConfig :: String -> MaybeT IO String
yamlConfig String
fp = do
  String
configDir <- String -> MaybeT IO String
yamlConfigDirectory String
fp
  forall (m :: * -> *) a. Monad m => a -> m a
return (String
configDir String -> String -> String
</> String
configFileName)

yamlConfigDirectory :: FilePath -> MaybeT IO FilePath
yamlConfigDirectory :: String -> MaybeT IO String
yamlConfigDirectory = (String -> Bool) -> String -> MaybeT IO String
findFileUpwards (String
configFileName forall a. Eq a => a -> a -> Bool
==)

readCradleConfig :: Yaml.FromJSON b => FilePath -> IO (CradleConfig b)
readCradleConfig :: forall b. FromJSON b => String -> IO (CradleConfig b)
readCradleConfig String
yamlHie = do
  Config b
cfg  <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. FromJSON a => String -> IO (Config a)
readConfig String
yamlHie
  forall (m :: * -> *) a. Monad m => a -> m a
return (forall a. Config a -> CradleConfig a
cradle Config b
cfg)

configFileName :: FilePath
configFileName :: String
configFileName = String
"hie.yaml"

-- | Pass '-dynamic' flag when GHC is built with dynamic linking.
--
-- Append flag to options of 'defaultCradle' and 'directCradle' if GHC is dynmically linked,
-- because unlike the case of using build tools, which means '-dynamic' can be set via
-- '.cabal' or 'package.yaml', users have to create an explicit hie.yaml to pass this flag.
argDynamic :: [String]
argDynamic :: [String]
argDynamic = [String
"-dynamic" | Bool
Gap.hostIsDynamic ]

---------------------------------------------------------------

isCabalCradle :: Cradle a -> Bool
isCabalCradle :: forall a. Cradle a -> Bool
isCabalCradle Cradle a
crdl = case forall a. CradleAction a -> ActionName a
actionName (forall a. Cradle a -> CradleAction a
cradleOptsProg Cradle a
crdl) of
  ActionName a
Types.Cabal -> Bool
True
  ActionName a
_ -> Bool
False

isStackCradle :: Cradle a -> Bool
isStackCradle :: forall a. Cradle a -> Bool
isStackCradle Cradle a
crdl = case forall a. CradleAction a -> ActionName a
actionName (forall a. Cradle a -> CradleAction a
cradleOptsProg Cradle a
crdl) of
  ActionName a
Types.Stack -> Bool
True
  ActionName a
_ -> Bool
False

isDirectCradle :: Cradle a -> Bool
isDirectCradle :: forall a. Cradle a -> Bool
isDirectCradle Cradle a
crdl = case forall a. CradleAction a -> ActionName a
actionName (forall a. Cradle a -> CradleAction a
cradleOptsProg Cradle a
crdl) of
  ActionName a
Types.Direct -> Bool
True
  ActionName a
_ -> Bool
False

isBiosCradle :: Cradle a -> Bool
isBiosCradle :: forall a. Cradle a -> Bool
isBiosCradle Cradle a
crdl = case forall a. CradleAction a -> ActionName a
actionName (forall a. Cradle a -> CradleAction a
cradleOptsProg Cradle a
crdl) of
  ActionName a
Types.Bios -> Bool
True
  ActionName a
_ -> Bool
False

isMultiCradle :: Cradle a -> Bool
isMultiCradle :: forall a. Cradle a -> Bool
isMultiCradle Cradle a
crdl = case forall a. CradleAction a -> ActionName a
actionName (forall a. Cradle a -> CradleAction a
cradleOptsProg Cradle a
crdl) of
  ActionName a
Types.Multi -> Bool
True
  ActionName a
_ -> Bool
False

isNoneCradle :: Cradle a -> Bool
isNoneCradle :: forall a. Cradle a -> Bool
isNoneCradle Cradle a
crdl = case forall a. CradleAction a -> ActionName a
actionName (forall a. Cradle a -> CradleAction a
cradleOptsProg Cradle a
crdl) of
  ActionName a
Types.None -> Bool
True
  ActionName a
_ -> Bool
False

isDefaultCradle :: Cradle a -> Bool
isDefaultCradle :: forall a. Cradle a -> Bool
isDefaultCradle Cradle a
crdl = case forall a. CradleAction a -> ActionName a
actionName (forall a. Cradle a -> CradleAction a
cradleOptsProg Cradle a
crdl) of
  ActionName a
Types.Default -> Bool
True
  ActionName a
_ -> Bool
False

isOtherCradle :: Cradle a -> Bool
isOtherCradle :: forall a. Cradle a -> Bool
isOtherCradle Cradle a
crdl = case forall a. CradleAction a -> ActionName a
actionName (forall a. Cradle a -> CradleAction a
cradleOptsProg Cradle a
crdl) of
  Types.Other a
_ -> Bool
True
  ActionName a
_ -> Bool
False

---------------------------------------------------------------

-- | Default cradle has no special options, not very useful for loading
-- modules.
defaultCradle :: LogAction IO (WithSeverity Log) -> FilePath -> Cradle a
defaultCradle :: forall a. LogAction IO (WithSeverity Log) -> String -> Cradle a
defaultCradle LogAction IO (WithSeverity Log)
l String
cur_dir =
  Cradle
    { cradleRootDir :: String
cradleRootDir = String
cur_dir
    , cradleLogger :: LogAction IO (WithSeverity Log)
cradleLogger = LogAction IO (WithSeverity Log)
l
    , cradleOptsProg :: CradleAction a
cradleOptsProg = CradleAction
        { actionName :: ActionName a
actionName = forall {a}. ActionName a
Types.Default
        , runCradle :: String -> [String] -> IO (CradleLoadResult ComponentOptions)
runCradle = \String
_ [String]
_ ->
            forall (m :: * -> *) a. Monad m => a -> m a
return (forall r. r -> CradleLoadResult r
CradleSuccess ([String] -> String -> [String] -> ComponentOptions
ComponentOptions [String]
argDynamic String
cur_dir []))
        , runGhcCmd :: [String] -> IO (CradleLoadResult String)
runGhcCmd = LogAction IO (WithSeverity Log)
-> String -> [String] -> IO (CradleLoadResult String)
runGhcCmdOnPath LogAction IO (WithSeverity Log)
l String
cur_dir
        }
    }

---------------------------------------------------------------
-- | The none cradle tells us not to even attempt to load a certain directory

noneCradle :: CradleAction a
noneCradle :: forall a. CradleAction a
noneCradle =
  CradleAction
      { actionName :: ActionName a
actionName = forall {a}. ActionName a
Types.None
      , runCradle :: String -> [String] -> IO (CradleLoadResult ComponentOptions)
runCradle = \String
_ [String]
_ -> forall (m :: * -> *) a. Monad m => a -> m a
return forall r. CradleLoadResult r
CradleNone
      , runGhcCmd :: [String] -> IO (CradleLoadResult String)
runGhcCmd = \[String]
_ -> forall (m :: * -> *) a. Monad m => a -> m a
return forall r. CradleLoadResult r
CradleNone
      }

---------------------------------------------------------------
-- | The multi cradle selects a cradle based on the filepath

-- Canonicalize the relative paths present in the multi-cradle and
-- also order the paths by most specific first. In the cradle selection
-- function we want to choose the most specific cradle possible.
canonicalizeResolvedCradles :: FilePath -> [ResolvedCradle a] -> IO [ResolvedCradle a]
canonicalizeResolvedCradles :: forall a. String -> [ResolvedCradle a] -> IO [ResolvedCradle a]
canonicalizeResolvedCradles String
cur_dir [ResolvedCradle a]
cs =
  forall b a. Ord b => (a -> b) -> [a] -> [a]
sortOn (forall a. a -> Down a
Down forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. ResolvedCradle a -> String
prefix)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM (\ResolvedCradle a
c -> (\String
abs_fp -> ResolvedCradle a
c {prefix :: String
prefix = String
abs_fp}) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> IO String
makeAbsolute (String
cur_dir String -> String -> String
</> forall a. ResolvedCradle a -> String
prefix ResolvedCradle a
c)) [ResolvedCradle a]
cs

selectCradle :: (a -> FilePath) -> FilePath -> [a] -> Maybe a
selectCradle :: forall a. (a -> String) -> String -> [a] -> Maybe a
selectCradle a -> String
_ String
_ [] = forall a. Maybe a
Nothing
selectCradle a -> String
k String
cur_fp (a
c: [a]
css) =
    if a -> String
k a
c forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
cur_fp
      then forall a. a -> Maybe a
Just a
c
      else forall a. (a -> String) -> String -> [a] -> Maybe a
selectCradle a -> String
k String
cur_fp [a]
css


-------------------------------------------------------------------------

directCradle :: LogAction IO (WithSeverity Log) -> FilePath -> [String] -> CradleAction a
directCradle :: forall a.
LogAction IO (WithSeverity Log)
-> String -> [String] -> CradleAction a
directCradle LogAction IO (WithSeverity Log)
l String
wdir [String]
args
  = CradleAction
      { actionName :: ActionName a
actionName = forall {a}. ActionName a
Types.Direct
      , runCradle :: String -> [String] -> IO (CradleLoadResult ComponentOptions)
runCradle = \String
_ [String]
_ ->
          forall (m :: * -> *) a. Monad m => a -> m a
return (forall r. r -> CradleLoadResult r
CradleSuccess ([String] -> String -> [String] -> ComponentOptions
ComponentOptions ([String]
args forall a. [a] -> [a] -> [a]
++ [String]
argDynamic) String
wdir []))
      , runGhcCmd :: [String] -> IO (CradleLoadResult String)
runGhcCmd = LogAction IO (WithSeverity Log)
-> String -> [String] -> IO (CradleLoadResult String)
runGhcCmdOnPath LogAction IO (WithSeverity Log)
l String
wdir
      }


-------------------------------------------------------------------------


-- | Find a cradle by finding an executable `hie-bios` file which will
-- be executed to find the correct GHC options to use.
biosCradle :: LogAction IO (WithSeverity Log) -> FilePath -> Callable -> Maybe Callable -> Maybe FilePath -> CradleAction a
biosCradle :: forall a.
LogAction IO (WithSeverity Log)
-> String
-> Callable
-> Maybe Callable
-> Maybe String
-> CradleAction a
biosCradle LogAction IO (WithSeverity Log)
l String
wdir Callable
biosCall Maybe Callable
biosDepsCall Maybe String
mbGhc
  = CradleAction
      { actionName :: ActionName a
actionName = forall {a}. ActionName a
Types.Bios
      , runCradle :: String -> [String] -> IO (CradleLoadResult ComponentOptions)
runCradle = String
-> Callable
-> Maybe Callable
-> LogAction IO (WithSeverity Log)
-> String
-> [String]
-> IO (CradleLoadResult ComponentOptions)
biosAction String
wdir Callable
biosCall Maybe Callable
biosDepsCall LogAction IO (WithSeverity Log)
l
      , runGhcCmd :: [String] -> IO (CradleLoadResult String)
runGhcCmd = \[String]
args -> LogAction IO (WithSeverity Log)
-> String
-> String
-> [String]
-> String
-> IO (CradleLoadResult String)
readProcessWithCwd LogAction IO (WithSeverity Log)
l String
wdir (forall a. a -> Maybe a -> a
fromMaybe String
"ghc" Maybe String
mbGhc) [String]
args String
""
      }

biosWorkDir :: FilePath -> MaybeT IO FilePath
biosWorkDir :: String -> MaybeT IO String
biosWorkDir = (String -> Bool) -> String -> MaybeT IO String
findFileUpwards (String
".hie-bios" forall a. Eq a => a -> a -> Bool
==)

biosDepsAction :: LogAction IO (WithSeverity Log) -> FilePath -> Maybe Callable -> FilePath -> [FilePath] -> IO [FilePath]
biosDepsAction :: LogAction IO (WithSeverity Log)
-> String -> Maybe Callable -> String -> [String] -> IO [String]
biosDepsAction LogAction IO (WithSeverity Log)
l String
wdir (Just Callable
biosDepsCall) String
fp [String]
_prevs = do
  CreateProcess
biosDeps' <- Callable -> Maybe String -> IO CreateProcess
callableToProcess Callable
biosDepsCall (forall a. a -> Maybe a
Just String
fp) -- TODO multi pass the previous files too
  (ExitCode
ex, [String]
sout, [String]
serr, [(String
_, Maybe [String]
args)]) <- [String]
-> LogAction IO (WithSeverity Log)
-> String
-> CreateProcess
-> IO (ExitCode, [String], [String], [(String, Maybe [String])])
readProcessWithOutputs [String
hie_bios_output] LogAction IO (WithSeverity Log)
l String
wdir CreateProcess
biosDeps'
  case ExitCode
ex of
    ExitFailure Int
_ ->  forall a. HasCallStack => String -> a
error forall a b. (a -> b) -> a -> b
$ forall a. Show a => a -> String
show (ExitCode
ex, [String]
sout, [String]
serr)
    ExitCode
ExitSuccess -> forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a. a -> Maybe a -> a
fromMaybe [] Maybe [String]
args
biosDepsAction LogAction IO (WithSeverity Log)
_ String
_ Maybe Callable
Nothing String
_ [String]
_ = forall (m :: * -> *) a. Monad m => a -> m a
return []

biosAction
  :: FilePath
  -> Callable
  -> Maybe Callable
  -> LogAction IO (WithSeverity Log)
  -> FilePath
  -> [FilePath]
  -> IO (CradleLoadResult ComponentOptions)
biosAction :: String
-> Callable
-> Maybe Callable
-> LogAction IO (WithSeverity Log)
-> String
-> [String]
-> IO (CradleLoadResult ComponentOptions)
biosAction String
wdir Callable
bios Maybe Callable
bios_deps LogAction IO (WithSeverity Log)
l String
fp [String]
fps = do
  CreateProcess
bios' <- Callable -> Maybe String -> IO CreateProcess
callableToProcess Callable
bios (forall a. a -> Maybe a
Just String
fp) -- TODO pass all the files instead of listToMaybe
  (ExitCode
ex, [String]
_stdo, [String]
std, [(String
_, Maybe [String]
res),(String
_, Maybe [String]
mb_deps)]) <-
    [String]
-> LogAction IO (WithSeverity Log)
-> String
-> CreateProcess
-> IO (ExitCode, [String], [String], [(String, Maybe [String])])
readProcessWithOutputs [String
hie_bios_output, String
hie_bios_deps] LogAction IO (WithSeverity Log)
l String
wdir CreateProcess
bios'

  [String]
deps <- case Maybe [String]
mb_deps of
    Just [String]
x  -> forall (m :: * -> *) a. Monad m => a -> m a
return [String]
x
    Maybe [String]
Nothing -> LogAction IO (WithSeverity Log)
-> String -> Maybe Callable -> String -> [String] -> IO [String]
biosDepsAction LogAction IO (WithSeverity Log)
l String
wdir Maybe Callable
bios_deps String
fp [String]
fps
        -- Output from the program should be written to the output file and
        -- delimited by newlines.
        -- Execute the bios action and add dependencies of the cradle.
        -- Removes all duplicates.
  forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ (ExitCode, [String], String, [String])
-> [String] -> CradleLoadResult ComponentOptions
makeCradleResult (ExitCode
ex, [String]
std, String
wdir, forall a. a -> Maybe a -> a
fromMaybe [] Maybe [String]
res) [String]
deps

callableToProcess :: Callable -> Maybe String -> IO CreateProcess
callableToProcess :: Callable -> Maybe String -> IO CreateProcess
callableToProcess (Command String
shellCommand) Maybe String
file = do
  [(String, String)]
old_env <- IO [(String, String)]
getEnvironment
  forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ (String -> CreateProcess
shell String
shellCommand) { env :: Maybe [(String, String)]
env = (forall a. a -> [a] -> [a]
: [(String, String)]
old_env) forall b c a. (b -> c) -> (a -> b) -> a -> c
. (,) String
hie_bios_arg forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe String
file }
callableToProcess (Program String
path) Maybe String
file = do
  String
canon_path <- String -> IO String
canonicalizePath String
path
  forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ String -> [String] -> CreateProcess
proc String
canon_path (forall a. Maybe a -> [a]
maybeToList Maybe String
file)

------------------------------------------------------------------------

projectFileProcessArgs :: CradleProjectConfig -> [String]
projectFileProcessArgs :: CradleProjectConfig -> [String]
projectFileProcessArgs (ExplicitConfig String
prjFile) = [String
"--project-file", String
prjFile]
projectFileProcessArgs CradleProjectConfig
NoExplicitConfig = []

projectLocationOrDefault :: CradleProjectConfig -> [FilePath]
projectLocationOrDefault :: CradleProjectConfig -> [String]
projectLocationOrDefault = \case
  CradleProjectConfig
NoExplicitConfig -> [String
"cabal.project", String
"cabal.project.local"]
  (ExplicitConfig String
prjFile) -> [String
prjFile, String
prjFile String -> String -> String
<.> String
"local"]

-- |Cabal Cradle
-- Works for new-build by invoking `v2-repl`.
cabalCradle :: LogAction IO (WithSeverity Log) -> ResolvedCradles b -> FilePath -> Maybe String -> CradleProjectConfig -> CradleAction a
cabalCradle :: forall b a.
LogAction IO (WithSeverity Log)
-> ResolvedCradles b
-> String
-> Maybe String
-> CradleProjectConfig
-> CradleAction a
cabalCradle LogAction IO (WithSeverity Log)
l ResolvedCradles b
cs String
wdir Maybe String
mc CradleProjectConfig
projectFile
  = CradleAction
    { actionName :: ActionName a
actionName = forall {a}. ActionName a
Types.Cabal
    , runCradle :: String -> [String] -> IO (CradleLoadResult ComponentOptions)
runCradle = \String
fp -> forall (m :: * -> *) a.
CradleLoadResultT m a -> m (CradleLoadResult a)
runCradleResultT forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a.
ResolvedCradles a
-> String
-> Maybe String
-> LogAction IO (WithSeverity Log)
-> CradleProjectConfig
-> String
-> [String]
-> CradleLoadResultT IO ComponentOptions
cabalAction ResolvedCradles b
cs String
wdir Maybe String
mc LogAction IO (WithSeverity Log)
l CradleProjectConfig
projectFile String
fp
    , runGhcCmd :: [String] -> IO (CradleLoadResult String)
runGhcCmd = \[String]
args -> forall (m :: * -> *) a.
CradleLoadResultT m a -> m (CradleLoadResult a)
runCradleResultT forall a b. (a -> b) -> a -> b
$ do
        String
buildDir <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ String -> IO String
cabalBuildDir String
wdir
        -- Workaround for a cabal-install bug on 3.0.0.0:
        -- ./dist-newstyle/tmp/environment.-24811: createDirectory: does not exist (No such file or directory)
        forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ Bool -> String -> IO ()
createDirectoryIfMissing Bool
True (String
buildDir String -> String -> String
</> String
"tmp")
        -- Need to pass -v0 otherwise we get "resolving dependencies..."
        CreateProcess
cabalProc <- LogAction IO (WithSeverity Log)
-> CradleProjectConfig
-> String
-> String
-> [String]
-> CradleLoadResultT IO CreateProcess
cabalProcess LogAction IO (WithSeverity Log)
l CradleProjectConfig
projectFile String
wdir String
"v2-exec" forall a b. (a -> b) -> a -> b
$ [String
"ghc", String
"-v0", String
"--"] forall a. [a] -> [a] -> [a]
++ [String]
args
        LogAction IO (WithSeverity Log)
-> CreateProcess -> String -> CradleLoadResultT IO String
readProcessWithCwd' LogAction IO (WithSeverity Log)
l CreateProcess
cabalProc String
""
    }


-- | Execute a cabal process in our custom cache-build directory configured
-- with the custom ghc executable.
-- The created process has its working directory set to the given working directory.
--
-- Invokes the cabal process in the given directory.
-- Finds the appropriate @ghc@ version as a fallback and provides the path
-- to the custom ghc wrapper via 'hie_bios_ghc' environment variable which
-- the custom ghc wrapper may use as a fallback if it can not respond to certain
-- queries, such as ghc version or location of the libdir.
cabalProcess :: LogAction IO (WithSeverity Log) -> CradleProjectConfig -> FilePath -> String -> [String] -> CradleLoadResultT IO CreateProcess
cabalProcess :: LogAction IO (WithSeverity Log)
-> CradleProjectConfig
-> String
-> String
-> [String]
-> CradleLoadResultT IO CreateProcess
cabalProcess LogAction IO (WithSeverity Log)
l CradleProjectConfig
cabalProject String
workDir String
command [String]
args = do
  (String, String)
ghcDirs <- LogAction IO (WithSeverity Log)
-> CradleProjectConfig
-> String
-> CradleLoadResultT IO (String, String)
cabalGhcDirs LogAction IO (WithSeverity Log)
l CradleProjectConfig
cabalProject String
workDir
  [(String, String)]
newEnvironment <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ (String, String) -> IO [(String, String)]
setupEnvironment (String, String)
ghcDirs
  CreateProcess
cabalProc <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ (String, String) -> IO CreateProcess
setupCabalCommand (String, String)
ghcDirs
  forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ (CreateProcess
cabalProc
      { env :: Maybe [(String, String)]
env = forall a. a -> Maybe a
Just [(String, String)]
newEnvironment
      , cwd :: Maybe String
cwd = forall a. a -> Maybe a
Just String
workDir
      })
  where
    processEnvironment :: (FilePath, FilePath) -> [(String, String)]
    processEnvironment :: (String, String) -> [(String, String)]
processEnvironment (String
ghcBin, String
libdir) =
      [(String
hie_bios_ghc, String
ghcBin), (String
hie_bios_ghc_args,  String
"-B" forall a. [a] -> [a] -> [a]
++ String
libdir)]

    setupEnvironment :: (FilePath, FilePath) -> IO [(String, String)]
    setupEnvironment :: (String, String) -> IO [(String, String)]
setupEnvironment (String, String)
ghcDirs = do
      [(String, String)]
environment <- IO [(String, String)]
getCleanEnvironment
      forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ (String, String) -> [(String, String)]
processEnvironment (String, String)
ghcDirs forall a. [a] -> [a] -> [a]
++ [(String, String)]
environment

    setupCabalCommand :: (FilePath, FilePath) -> IO CreateProcess
    setupCabalCommand :: (String, String) -> IO CreateProcess
setupCabalCommand (String
ghcBin, String
libdir) = do
      String
wrapper_fp <- LogAction IO (WithSeverity Log) -> GhcProc -> String -> IO String
withGhcWrapperTool LogAction IO (WithSeverity Log)
l (String
"ghc", []) String
workDir
      String
buildDir <- String -> IO String
cabalBuildDir String
workDir
      String
ghcPkgPath <- String -> String -> IO String
withGhcPkgTool String
ghcBin String
libdir
      let extraCabalArgs :: [String]
extraCabalArgs =
            [ String
"--builddir=" forall a. Semigroup a => a -> a -> a
<> String
buildDir
            , String
command
            , String
"--with-compiler", String
wrapper_fp
            , String
"--with-hc-pkg", String
ghcPkgPath
            ] forall a. Semigroup a => a -> a -> a
<> CradleProjectConfig -> [String]
projectFileProcessArgs CradleProjectConfig
cabalProject
      forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ String -> [String] -> CreateProcess
proc String
"cabal" ([String]
extraCabalArgs forall a. [a] -> [a] -> [a]
++ [String]
args)

-- | Discovers the location of 'ghc-pkg' given the absolute path to 'ghc'
-- and its '$libdir' (obtainable by running @ghc --print-libdir@).
--
-- @'withGhcPkgTool' ghcPathAbs libdir@ guesses the location by looking at
-- the filename of 'ghcPathAbs' and expects that 'ghc-pkg' is right next to it,
-- which is guaranteed by the ghc build system. Most OS's follow this
-- convention.
--
-- On unix, there is a high-chance that the obtained 'ghc' location is the
-- "unwrapped" executable, e.g. the executable without a shim that specifies
-- the '$libdir' and other important constants.
-- As such, the executable 'ghc-pkg' is similarly without a wrapper shim and
-- is lacking certain constants such as 'global-package-db'. It is, therefore,
-- not suitable to pass in to other consumers, such as 'cabal'.
--
-- Here, we restore the wrapper-shims, if necessary, thus the returned filepath
-- can be passed to 'cabal' without further modifications.
withGhcPkgTool :: FilePath -> FilePath -> IO FilePath
withGhcPkgTool :: String -> String -> IO String
withGhcPkgTool String
ghcPathAbs String
libdir = do
  let ghcName :: String
ghcName = String -> String
takeFileName String
ghcPathAbs
      -- TODO: check for existence
      ghcPkgPath :: String
ghcPkgPath = String -> String
guessGhcPkgFromGhc String
ghcName
  if Bool
isWindows
    then forall (f :: * -> *) a. Applicative f => a -> f a
pure String
ghcPkgPath
    else String -> IO String
withWrapperTool String
ghcPkgPath
  where
    ghcDir :: String
ghcDir = String -> String
takeDirectory String
ghcPathAbs

    guessGhcPkgFromGhc :: String -> String
guessGhcPkgFromGhc String
ghcName =
      let ghcPkgName :: Text
ghcPkgName = Text -> Text -> Text -> Text
T.replace Text
"ghc" Text
"ghc-pkg" (String -> Text
T.pack String
ghcName)
      in String
ghcDir String -> String -> String
</> Text -> String
T.unpack Text
ghcPkgName

    -- Only on unix, creates a wrapper script that's hopefully identical
    -- to the wrapper script 'ghc-pkg' usually comes with.
    --
    -- 'ghc-pkg' needs to know the 'global-package-db' location which is
    -- passed in via a wrapper shim that basically wraps 'ghc-pkg' and
    -- only passes in the correct 'global-package-db'.
    -- For an example on how the wrapper script is supposed to look like, take
    -- a look at @cat $(which ghc-pkg)@, assuming 'ghc-pkg' is on your $PATH.
    --
    -- If we used the raw executable, i.e. not wrapped in a shim, then 'cabal'
    -- can not use the given 'ghc-pkg'.
    withWrapperTool :: String -> IO String
withWrapperTool String
ghcPkg = do
      let globalPackageDb :: String
globalPackageDb = String
libdir String -> String -> String
</> String
"package.conf.d"
          -- This is the same as the wrapper-shims ghc-pkg usually comes with.
          contents :: String
contents = [String] -> String
unlines
            [ String
"#!/bin/sh"
            , [String] -> String
unwords [String
"exec", String -> String
escapeFilePath String
ghcPkg
                      , String
"--global-package-db", String -> String
escapeFilePath String
globalPackageDb
                      , String
"${1+\"$@\"}"
                      ]
            ]
          srcHash :: String
srcHash = forall a. Show a => a -> String
show (String -> Fingerprint
fingerprintString String
contents)
      String -> String -> (String -> IO ()) -> IO String
cacheFile String
"ghc-pkg" String
srcHash forall a b. (a -> b) -> a -> b
$ \String
wrapperFp -> String -> String -> IO ()
writeFile String
wrapperFp String
contents

    -- Escape the filepath and trim excess newlines added by 'escapeArgs'
    escapeFilePath :: String -> String
escapeFilePath String
fp = String -> String
trimEnd forall a b. (a -> b) -> a -> b
$ [String] -> String
escapeArgs [String
fp]

-- | @'cabalCradleDependencies' projectFile rootDir componentDir@.
-- Compute the dependencies of the cabal cradle based
-- on cabal project configuration, the cradle root and the component directory.
--
-- The @projectFile@ and @projectFile <> ".local"@ are always added to the list
-- of dependencies.
--
-- Directory 'componentDir' is a sub-directory where we look for
-- package specific cradle dependencies, such as a '.cabal' file.
--
-- Found dependencies are relative to 'rootDir'.
cabalCradleDependencies :: CradleProjectConfig -> FilePath -> FilePath -> IO [FilePath]
cabalCradleDependencies :: CradleProjectConfig -> String -> String -> IO [String]
cabalCradleDependencies CradleProjectConfig
projectFile String
rootDir String
componentDir = do
    let relFp :: String
relFp = String -> String -> String
makeRelative String
rootDir String
componentDir
    [String]
cabalFiles' <- String -> IO [String]
findCabalFiles String
componentDir
    let cabalFiles :: [String]
cabalFiles = forall a b. (a -> b) -> [a] -> [b]
map (String
relFp String -> String -> String
</>) [String]
cabalFiles'
    forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map String -> String
normalise forall a b. (a -> b) -> a -> b
$ [String]
cabalFiles forall a. [a] -> [a] -> [a]
++ CradleProjectConfig -> [String]
projectLocationOrDefault CradleProjectConfig
projectFile

-- |Find .cabal files in the given directory.
--
-- Might return multiple results,biosAction as we can not know in advance
-- which one is important to the user.
findCabalFiles :: FilePath -> IO [FilePath]
findCabalFiles :: String -> IO [String]
findCabalFiles String
wdir = do
  [String]
dirContent <- String -> IO [String]
listDirectory String
wdir
  forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a. (a -> Bool) -> [a] -> [a]
filter ((forall a. Eq a => a -> a -> Bool
== String
".cabal") forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> String
takeExtension) [String]
dirContent


processCabalWrapperArgs :: [String] -> Maybe (FilePath, [String])
processCabalWrapperArgs :: [String] -> Maybe GhcProc
processCabalWrapperArgs [String]
args =
    case [String]
args of
        (String
dir: [String]
ghc_args) ->
            let final_args :: [String]
final_args =
                    [String] -> [String]
removeVerbosityOpts
                    forall a b. (a -> b) -> a -> b
$ [String] -> [String]
removeRTS
                    forall a b. (a -> b) -> a -> b
$ [String] -> [String]
removeInteractive [String]
ghc_args
            in forall a. a -> Maybe a
Just (String
dir, [String]
final_args)
        [String]
_ -> forall a. Maybe a
Nothing

-- | GHC process information.
-- Consists of the filepath to the ghc executable and
-- arguments to the executable.
type GhcProc = (FilePath, [String])

-- | Generate a fake GHC that can be passed to cabal or stack
-- when run with --interactive, it will print out its
-- command-line arguments and exit
withGhcWrapperTool :: LogAction IO (WithSeverity Log) -> GhcProc -> FilePath -> IO FilePath
withGhcWrapperTool :: LogAction IO (WithSeverity Log) -> GhcProc -> String -> IO String
withGhcWrapperTool LogAction IO (WithSeverity Log)
l (String
mbGhc, [String]
ghcArgs) String
wdir = do
    let wrapperContents :: String
wrapperContents = if Bool
isWindows then String
cabalWrapperHs else String
cabalWrapper
        withExtension :: String -> String
withExtension String
fp = if Bool
isWindows then String
fp String -> String -> String
<.> String
"exe" else String
fp
        srcHash :: String
srcHash = forall a. Show a => a -> String
show (String -> Fingerprint
fingerprintString String
wrapperContents)
    String -> String -> (String -> IO ()) -> IO String
cacheFile (String -> String
withExtension String
"wrapper") String
srcHash forall a b. (a -> b) -> a -> b
$ \String
wrapper_fp ->
      if Bool
isWindows
      then
        forall (m :: * -> *) a.
(MonadIO m, MonadMask m) =>
String -> (String -> m a) -> m a
withSystemTempDirectory String
"hie-bios" forall a b. (a -> b) -> a -> b
$ \ String
tmpDir -> do
          let wrapper_hs :: String
wrapper_hs = String
wrapper_fp String -> String -> String
-<.> String
"hs"
          String -> String -> IO ()
writeFile String
wrapper_hs String
wrapperContents
          let ghcArgsWithExtras :: [String]
ghcArgsWithExtras = [String]
ghcArgs forall a. [a] -> [a] -> [a]
++ [String
"-rtsopts=ignore", String
"-outputdir", String
tmpDir, String
"-o", String
wrapper_fp, String
wrapper_hs]
          let ghcProc :: CreateProcess
ghcProc = (String -> [String] -> CreateProcess
proc String
mbGhc [String]
ghcArgsWithExtras)
                      { cwd :: Maybe String
cwd = forall a. a -> Maybe a
Just String
wdir
                      }
          LogAction IO (WithSeverity Log)
l forall (m :: * -> *) msg. LogAction m msg -> msg -> m ()
<& CreateProcess -> Log
LogCreateProcessRun CreateProcess
ghcProc forall msg. msg -> Severity -> WithSeverity msg
`WithSeverity` Severity
Debug
          CreateProcess -> String -> IO String
readCreateProcess CreateProcess
ghcProc String
"" forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= String -> IO ()
putStr
      else String -> String -> IO ()
writeFile String
wrapper_fp String
wrapperContents

-- | Create and cache a file in hie-bios's cache directory.
--
-- @'cacheFile' fpName srcHash populate@. 'fpName' is the pattern name of the
-- cached file you want to create. 'srcHash' is the hash that is appended to
-- the file pattern and is expected to change whenever you want to invalidate
-- the cache.
--
-- If the cached file's 'srcHash' changes, then a new file is created, but
-- the old cached file name will not be deleted.
--
-- If the file does not exist yet, 'populate' is invoked with cached file
-- location and it is expected that the caller persists the given filepath in
-- the File System.
cacheFile :: FilePath -> String -> (FilePath -> IO ()) -> IO FilePath
cacheFile :: String -> String -> (String -> IO ()) -> IO String
cacheFile String
fpName String
srcHash String -> IO ()
populate = do
  String
cacheDir <- String -> IO String
getCacheDir String
""
  Bool -> String -> IO ()
createDirectoryIfMissing Bool
True String
cacheDir
  let newFpName :: String
newFpName = String
cacheDir String -> String -> String
</> (String -> String
dropExtensions String
fpName forall a. Semigroup a => a -> a -> a
<> String
"-" forall a. Semigroup a => a -> a -> a
<> String
srcHash) String -> String -> String
<.> String -> String
takeExtensions String
fpName
  forall (m :: * -> *). Monad m => m Bool -> m () -> m ()
unlessM (String -> IO Bool
doesFileExist String
newFpName) forall a b. (a -> b) -> a -> b
$ do
    String -> IO ()
populate String
newFpName
    String -> IO ()
setMode String
newFpName
  forall (f :: * -> *) a. Applicative f => a -> f a
pure String
newFpName
  where
    setMode :: String -> IO ()
setMode String
wrapper_fp = String -> FileMode -> IO ()
setFileMode String
wrapper_fp FileMode
accessModes

-- | Given the root directory, get the build dir we are using for cabal
-- In the `hie-bios` cache directory
cabalBuildDir :: FilePath -> IO FilePath
cabalBuildDir :: String -> IO String
cabalBuildDir String
workDir = do
  String
abs_work_dir <- String -> IO String
makeAbsolute String
workDir
  let dirHash :: String
dirHash = forall a. Show a => a -> String
show (String -> Fingerprint
fingerprintString String
abs_work_dir)
  String -> IO String
getCacheDir (String
"dist-" forall a. Semigroup a => a -> a -> a
<> forall a. (a -> Bool) -> [a] -> [a]
filter (Bool -> Bool
not forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Bool
isSpace) (String -> String
takeBaseName String
abs_work_dir)forall a. Semigroup a => a -> a -> a
<>String
"-"forall a. Semigroup a => a -> a -> a
<>String
dirHash)

-- | Discover the location of the ghc binary 'cabal' is going to use together
-- with its libdir location.
-- The ghc executable is an absolute path, but not necessarily canonicalised
-- or normalised. Additionally, the ghc path returned is likely to be the raw
-- executable, i.e. without the usual wrapper shims on non-windows systems.
-- If you want to use the given ghc executable, you should invoke
-- 'withGhcWrapperTool'.
--
-- If cabal can not figure it out, a 'CradleError' is returned.
cabalGhcDirs :: LogAction IO (WithSeverity Log) -> CradleProjectConfig -> FilePath -> CradleLoadResultT IO (FilePath, FilePath)
cabalGhcDirs :: LogAction IO (WithSeverity Log)
-> CradleProjectConfig
-> String
-> CradleLoadResultT IO (String, String)
cabalGhcDirs LogAction IO (WithSeverity Log)
l CradleProjectConfig
cabalProject String
workDir = do
  String
libdir <- LogAction IO (WithSeverity Log)
-> String
-> String
-> [String]
-> String
-> CradleLoadResultT IO String
readProcessWithCwd_ LogAction IO (WithSeverity Log)
l String
workDir String
"cabal"
      ([String
"exec"] forall a. [a] -> [a] -> [a]
++
       [String]
projectFileArgs forall a. [a] -> [a] -> [a]
++
       [String
"-v0", String
"--", String
"ghc", String
"--print-libdir"]
      )
      String
""
  String
exe <- LogAction IO (WithSeverity Log)
-> String
-> String
-> [String]
-> String
-> CradleLoadResultT IO String
readProcessWithCwd_ LogAction IO (WithSeverity Log)
l String
workDir String
"cabal"
      -- DON'T TOUCH THIS CODE
      -- This works with 'NoImplicitPrelude', with 'RebindableSyntax' and other shenanigans.
      -- @-package-env=-@ doesn't work with ghc prior 8.4.x
      ([ String
"exec"] forall a. [a] -> [a] -> [a]
++
       [String]
projectFileArgs forall a. [a] -> [a] -> [a]
++
       [ String
"-v0", String
"--" , String
"ghc", String
"-package-env=-", String
"-ignore-dot-ghci", String
"-e"
       , String
"Control.Monad.join (Control.Monad.fmap System.IO.putStr System.Environment.getExecutablePath)"
       ]
      )
      String
""
  forall (f :: * -> *) a. Applicative f => a -> f a
pure (String -> String
trimEnd String
exe, String -> String
trimEnd String
libdir)
  where
    projectFileArgs :: [String]
projectFileArgs = CradleProjectConfig -> [String]
projectFileProcessArgs CradleProjectConfig
cabalProject


cabalAction
  :: ResolvedCradles a
  -> FilePath
  -> Maybe String
  -> LogAction IO (WithSeverity Log)
  -> CradleProjectConfig
  -> FilePath
  -> [FilePath]
  -> CradleLoadResultT IO ComponentOptions
cabalAction :: forall a.
ResolvedCradles a
-> String
-> Maybe String
-> LogAction IO (WithSeverity Log)
-> CradleProjectConfig
-> String
-> [String]
-> CradleLoadResultT IO ComponentOptions
cabalAction (ResolvedCradles String
root [ResolvedCradle a]
cs ProgramVersions
vs) String
workDir Maybe String
mc LogAction IO (WithSeverity Log)
l CradleProjectConfig
projectFile String
fp [String]
fps = do
  Maybe Version
cabal_version <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. CachedIO a -> IO a
runCachedIO forall a b. (a -> b) -> a -> b
$ ProgramVersions -> CachedIO (Maybe Version)
cabalVersion ProgramVersions
vs
  Maybe Version
ghc_version   <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. CachedIO a -> IO a
runCachedIO forall a b. (a -> b) -> a -> b
$ ProgramVersions -> CachedIO (Maybe Version)
ghcVersion ProgramVersions
vs
  let
    cabalCommand :: String
cabalCommand = String
"v2-repl"
    cabalArgs :: [String]
cabalArgs = case (Maybe Version
cabal_version, Maybe Version
ghc_version) of
      (Just Version
cabal, Just Version
ghc)
        -- Multi-component supported from cabal-install 3.11
        -- and ghc 9.4
        | Version
ghc   forall a. Ord a => a -> a -> Bool
>= [Int] -> Version
makeVersion [Int
9,Int
4]
        , Version
cabal forall a. Ord a => a -> a -> Bool
>= [Int] -> Version
makeVersion [Int
3,Int
11]
        -> case [String]
fps of
          [] -> [forall a. a -> Maybe a -> a
fromMaybe (String -> String
fixTargetPath String
fp) Maybe String
mc]
          -- Start a multi-component session with all the old files
          [String]
_ -> String
"--keep-temp-files"
             forall a. a -> [a] -> [a]
: String
"--enable-multi-repl"
             forall a. a -> [a] -> [a]
: [forall a. a -> Maybe a -> a
fromMaybe (String -> String
fixTargetPath String
fp) Maybe String
mc]
            forall a. [a] -> [a] -> [a]
++ [forall a. a -> Maybe a -> a
fromMaybe (String -> String
fixTargetPath String
old_fp) Maybe String
old_mc
               | String
old_fp <- [String]
fps
               -- Lookup the component for the old file
               , Just (ResolvedCradle{concreteCradle :: forall a. ResolvedCradle a -> ConcreteCradle a
concreteCradle = ConcreteCabal CabalType
ct}) <- [forall a. (a -> String) -> String -> [a] -> Maybe a
selectCradle forall a. ResolvedCradle a -> String
prefix String
old_fp [ResolvedCradle a]
cs]
               -- Only include this file if the old component is in the same project
               , (String -> Maybe String -> CradleProjectConfig
projectConfigFromMaybe String
root (CabalType -> Maybe String
cabalProjectFile CabalType
ct)) forall a. Eq a => a -> a -> Bool
== CradleProjectConfig
projectFile
               , let old_mc :: Maybe String
old_mc = CabalType -> Maybe String
cabalComponent CabalType
ct
               ]
      (Maybe Version, Maybe Version)
_ -> [forall a. a -> Maybe a -> a
fromMaybe (String -> String
fixTargetPath String
fp) Maybe String
mc]

  CreateProcess
cabalProc <- LogAction IO (WithSeverity Log)
-> CradleProjectConfig
-> String
-> String
-> [String]
-> CradleLoadResultT IO CreateProcess
cabalProcess LogAction IO (WithSeverity Log)
l CradleProjectConfig
projectFile String
workDir String
cabalCommand [String]
cabalArgs forall (m :: * -> *) a.
Monad m =>
CradleLoadResultT m a
-> (CradleError -> m CradleError) -> CradleLoadResultT m a
`modCradleError` \CradleError
err -> do
      [String]
deps <- CradleProjectConfig -> String -> String -> IO [String]
cabalCradleDependencies CradleProjectConfig
projectFile String
workDir String
workDir
      forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ CradleError
err { cradleErrorDependencies :: [String]
cradleErrorDependencies = CradleError -> [String]
cradleErrorDependencies CradleError
err forall a. [a] -> [a] -> [a]
++ [String]
deps }

  (ExitCode
ex, [String]
output, [String]
stde, [(String
_, Maybe [String]
maybeArgs)]) <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ [String]
-> LogAction IO (WithSeverity Log)
-> String
-> CreateProcess
-> IO (ExitCode, [String], [String], [(String, Maybe [String])])
readProcessWithOutputs [String
hie_bios_output] LogAction IO (WithSeverity Log)
l String
workDir CreateProcess
cabalProc
  let args :: [String]
args = forall a. a -> Maybe a -> a
fromMaybe [] Maybe [String]
maybeArgs

  let errorDetails :: [String]
errorDetails =
        [String
"Failed command: " forall a. Semigroup a => a -> a -> a
<> CmdSpec -> String
prettyCmdSpec (CreateProcess -> CmdSpec
cmdspec CreateProcess
cabalProc)
        , [String] -> String
unlines [String]
output
        , [String] -> String
unlines [String]
stde
        , [String] -> String
unlines forall a b. (a -> b) -> a -> b
$ [String]
args
        , String
"Process Environment:"]
        forall a. Semigroup a => a -> a -> a
<> CreateProcess -> [String]
prettyProcessEnv CreateProcess
cabalProc

  forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (ExitCode
ex forall a. Eq a => a -> a -> Bool
/= ExitCode
ExitSuccess) forall a b. (a -> b) -> a -> b
$ do
    [String]
deps <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ CradleProjectConfig -> String -> String -> IO [String]
cabalCradleDependencies CradleProjectConfig
projectFile String
workDir String
workDir
    let cmd :: String
cmd = forall a. Show a => a -> String
show ([String
"cabal", String
cabalCommand] forall a. Semigroup a => a -> a -> a
<> [String]
cabalArgs)
    let errorMsg :: String
errorMsg = String
"Failed to run " forall a. Semigroup a => a -> a -> a
<> String
cmd forall a. Semigroup a => a -> a -> a
<> String
" in directory \"" forall a. Semigroup a => a -> a -> a
<> String
workDir forall a. Semigroup a => a -> a -> a
<> String
"\". Consult the logs for full command and error."
    forall (m :: * -> *) a.
Monad m =>
CradleError -> CradleLoadResultT m a
throwCE ([String] -> ExitCode -> [String] -> CradleError
CradleError [String]
deps ExitCode
ex ([String
errorMsg] forall a. Semigroup a => a -> a -> a
<> [String]
errorDetails))

  case [String] -> Maybe GhcProc
processCabalWrapperArgs [String]
args of
    Maybe GhcProc
Nothing -> do
      -- Provide some dependencies an IDE can look for to trigger a reload.
      -- Best effort. Assume the working directory is the
      -- root of the component, so we are right in trivial cases at least.
      [String]
deps <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ CradleProjectConfig -> String -> String -> IO [String]
cabalCradleDependencies CradleProjectConfig
projectFile String
workDir String
workDir
      forall (m :: * -> *) a.
Monad m =>
CradleError -> CradleLoadResultT m a
throwCE ([String] -> ExitCode -> [String] -> CradleError
CradleError [String]
deps ExitCode
ex forall a b. (a -> b) -> a -> b
$
                ([String
"Failed to parse result of calling cabal" ] forall a. Semigroup a => a -> a -> a
<> [String]
errorDetails))
    Just (String
componentDir, [String]
final_args) -> do
      [String]
deps <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ CradleProjectConfig -> String -> String -> IO [String]
cabalCradleDependencies CradleProjectConfig
projectFile String
workDir String
componentDir
      forall (m :: * -> *) a.
m (CradleLoadResult a) -> CradleLoadResultT m a
CradleLoadResultT forall a b. (a -> b) -> a -> b
$ forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ (ExitCode, [String], String, [String])
-> [String] -> CradleLoadResult ComponentOptions
makeCradleResult (ExitCode
ex, [String]
stde, String
componentDir, [String]
final_args) [String]
deps
  where
    -- Need to make relative on Windows, due to a Cabal bug with how it
    -- parses file targets with a C: drive in it
    fixTargetPath :: String -> String
fixTargetPath String
x
      | Bool
isWindows Bool -> Bool -> Bool
&& String -> Bool
hasDrive String
x = String -> String -> String
makeRelative String
workDir String
x
      | Bool
otherwise = String
x

removeInteractive :: [String] -> [String]
removeInteractive :: [String] -> [String]
removeInteractive = forall a. (a -> Bool) -> [a] -> [a]
filter (forall a. Eq a => a -> a -> Bool
/= String
"--interactive")

-- | Strip out any ["+RTS", ..., "-RTS"] sequences in the command string list.
data InRTS = OutsideRTS | InsideRTS

-- | Strip out any ["+RTS", ..., "-RTS"] sequences in the command string list.
--
-- >>> removeRTS ["option1", "+RTS -H32m -RTS", "option2"]
-- ["option1", "option2"]
--
-- >>> removeRTS ["option1", "+RTS", "-H32m", "-RTS", "option2"]
-- ["option1", "option2"]
--
-- >>> removeRTS ["option1", "+RTS -H32m"]
-- ["option1"]
--
-- >>> removeRTS ["option1", "+RTS -H32m", "-RTS", "option2"]
-- ["option1", "option2"]
--
-- >>> removeRTS ["option1", "+RTS -H32m", "-H32m -RTS", "option2"]
-- ["option1", "option2"]
removeRTS :: [String] -> [String]
removeRTS :: [String] -> [String]
removeRTS = InRTS -> [String] -> [String]
go InRTS
OutsideRTS
  where
    go :: InRTS -> [String] -> [String]
    go :: InRTS -> [String] -> [String]
go InRTS
_ [] = []
    go InRTS
OutsideRTS (String
y:[String]
ys)
      | String
"+RTS" forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
y = InRTS -> [String] -> [String]
go (if String
"-RTS" forall a. Eq a => [a] -> [a] -> Bool
`isSuffixOf` String
y then InRTS
OutsideRTS else InRTS
InsideRTS) [String]
ys
      | Bool
otherwise = String
y forall a. a -> [a] -> [a]
: InRTS -> [String] -> [String]
go InRTS
OutsideRTS [String]
ys
    go InRTS
InsideRTS (String
y:[String]
ys) = InRTS -> [String] -> [String]
go (if String
"-RTS" forall a. Eq a => [a] -> [a] -> Bool
`isSuffixOf` String
y then InRTS
OutsideRTS else InRTS
InsideRTS) [String]
ys


removeVerbosityOpts :: [String] -> [String]
removeVerbosityOpts :: [String] -> [String]
removeVerbosityOpts = forall a. (a -> Bool) -> [a] -> [a]
filter (Bool -> Bool -> Bool
(&&) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (forall a. Eq a => a -> a -> Bool
/= String
"-v0") forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> (forall a. Eq a => a -> a -> Bool
/= String
"-w"))


cabalWorkDir :: FilePath -> MaybeT IO FilePath
cabalWorkDir :: String -> MaybeT IO String
cabalWorkDir String
wdir =
      (String -> Bool) -> String -> MaybeT IO String
findFileUpwards (forall a. Eq a => a -> a -> Bool
== String
"cabal.project") String
wdir
  forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> (String -> Bool) -> String -> MaybeT IO String
findFileUpwards (\String
fp -> String -> String
takeExtension String
fp forall a. Eq a => a -> a -> Bool
== String
".cabal") String
wdir


------------------------------------------------------------------------

-- | Explicit data-type for project configuration location.
-- It is basically a 'Maybe' type, but helps to document the API
-- and helps to avoid incorrect usage.
data CradleProjectConfig
  = NoExplicitConfig
  | ExplicitConfig FilePath
  deriving CradleProjectConfig -> CradleProjectConfig -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: CradleProjectConfig -> CradleProjectConfig -> Bool
$c/= :: CradleProjectConfig -> CradleProjectConfig -> Bool
== :: CradleProjectConfig -> CradleProjectConfig -> Bool
$c== :: CradleProjectConfig -> CradleProjectConfig -> Bool
Eq

-- | Create an explicit project configuration. Expects a working directory
-- followed by an optional name of the project configuration.
projectConfigFromMaybe :: FilePath -> Maybe FilePath -> CradleProjectConfig
projectConfigFromMaybe :: String -> Maybe String -> CradleProjectConfig
projectConfigFromMaybe String
_wdir Maybe String
Nothing = CradleProjectConfig
NoExplicitConfig
projectConfigFromMaybe String
wdir (Just String
fp) = String -> CradleProjectConfig
ExplicitConfig (String
wdir String -> String -> String
</> String
fp)

------------------------------------------------------------------------

stackYamlProcessArgs :: CradleProjectConfig -> [String]
stackYamlProcessArgs :: CradleProjectConfig -> [String]
stackYamlProcessArgs (ExplicitConfig String
yaml) = [String
"--stack-yaml", String
yaml]
stackYamlProcessArgs CradleProjectConfig
NoExplicitConfig = []

stackYamlLocationOrDefault :: CradleProjectConfig -> FilePath
stackYamlLocationOrDefault :: CradleProjectConfig -> String
stackYamlLocationOrDefault CradleProjectConfig
NoExplicitConfig = String
"stack.yaml"
stackYamlLocationOrDefault (ExplicitConfig String
yaml) = String
yaml

-- | Stack Cradle
-- Works for by invoking `stack repl` with a wrapper script
stackCradle :: LogAction IO (WithSeverity Log) ->  FilePath -> Maybe String -> CradleProjectConfig -> CradleAction a
stackCradle :: forall a.
LogAction IO (WithSeverity Log)
-> String -> Maybe String -> CradleProjectConfig -> CradleAction a
stackCradle LogAction IO (WithSeverity Log)
l String
wdir Maybe String
mc CradleProjectConfig
syaml =
  CradleAction
    { actionName :: ActionName a
actionName = forall {a}. ActionName a
Types.Stack
    , runCradle :: String -> [String] -> IO (CradleLoadResult ComponentOptions)
runCradle = String
-> Maybe String
-> CradleProjectConfig
-> LogAction IO (WithSeverity Log)
-> String
-> [String]
-> IO (CradleLoadResult ComponentOptions)
stackAction String
wdir Maybe String
mc CradleProjectConfig
syaml LogAction IO (WithSeverity Log)
l
    , runGhcCmd :: [String] -> IO (CradleLoadResult String)
runGhcCmd = \[String]
args -> forall (m :: * -> *) a.
CradleLoadResultT m a -> m (CradleLoadResult a)
runCradleResultT forall a b. (a -> b) -> a -> b
$ do
        -- Setup stack silently, since stack might print stuff to stdout in some cases (e.g. on Win)
        -- Issue 242 from HLS: https://github.com/haskell/haskell-language-server/issues/242
        String
_ <- LogAction IO (WithSeverity Log)
-> String
-> String
-> [String]
-> String
-> CradleLoadResultT IO String
readProcessWithCwd_ LogAction IO (WithSeverity Log)
l String
wdir String
"stack" (CradleProjectConfig -> [String]
stackYamlProcessArgs CradleProjectConfig
syaml forall a. Semigroup a => a -> a -> a
<> [String
"setup", String
"--silent"]) String
""
        LogAction IO (WithSeverity Log)
-> String
-> String
-> [String]
-> String
-> CradleLoadResultT IO String
readProcessWithCwd_ LogAction IO (WithSeverity Log)
l String
wdir String
"stack"
          (CradleProjectConfig -> [String]
stackYamlProcessArgs CradleProjectConfig
syaml forall a. Semigroup a => a -> a -> a
<> [String
"exec", String
"ghc", String
"--"] forall a. Semigroup a => a -> a -> a
<> [String]
args)
          String
""
    }

-- | @'stackCradleDependencies' rootDir componentDir@.
-- Compute the dependencies of the stack cradle based
-- on the cradle root and the component directory.
--
-- Directory 'componentDir' is a sub-directory where we look for
-- package specific cradle dependencies, such as 'package.yaml' and
-- a '.cabal' file.
--
-- Found dependencies are relative to 'rootDir'.
stackCradleDependencies :: FilePath -> FilePath -> CradleProjectConfig -> IO [FilePath]
stackCradleDependencies :: String -> String -> CradleProjectConfig -> IO [String]
stackCradleDependencies String
wdir String
componentDir CradleProjectConfig
syaml = do
  let relFp :: String
relFp = String -> String -> String
makeRelative String
wdir String
componentDir
  [String]
cabalFiles' <- String -> IO [String]
findCabalFiles String
componentDir
  let cabalFiles :: [String]
cabalFiles = forall a b. (a -> b) -> [a] -> [b]
map (String
relFp String -> String -> String
</>) [String]
cabalFiles'
  forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map String -> String
normalise forall a b. (a -> b) -> a -> b
$
    [String]
cabalFiles forall a. [a] -> [a] -> [a]
++ [String
relFp String -> String -> String
</> String
"package.yaml", CradleProjectConfig -> String
stackYamlLocationOrDefault CradleProjectConfig
syaml]

stackAction
  :: FilePath
  -> Maybe String
  -> CradleProjectConfig
  -> LogAction IO (WithSeverity Log)
  -> FilePath
  -> [FilePath]
  -> IO (CradleLoadResult ComponentOptions)
stackAction :: String
-> Maybe String
-> CradleProjectConfig
-> LogAction IO (WithSeverity Log)
-> String
-> [String]
-> IO (CradleLoadResult ComponentOptions)
stackAction String
workDir Maybe String
mc CradleProjectConfig
syaml LogAction IO (WithSeverity Log)
l String
_fp [String]
_fps = do
  let ghcProcArgs :: GhcProc
ghcProcArgs = (String
"stack", CradleProjectConfig -> [String]
stackYamlProcessArgs CradleProjectConfig
syaml forall a. Semigroup a => a -> a -> a
<> [String
"exec", String
"ghc", String
"--"])
  -- Same wrapper works as with cabal
  String
wrapper_fp <- LogAction IO (WithSeverity Log) -> GhcProc -> String -> IO String
withGhcWrapperTool LogAction IO (WithSeverity Log)
l GhcProc
ghcProcArgs String
workDir
  (ExitCode
ex1, [String]
_stdo, [String]
stde, [(String
_, Maybe [String]
maybeArgs)]) <-
    [String]
-> LogAction IO (WithSeverity Log)
-> String
-> CreateProcess
-> IO (ExitCode, [String], [String], [(String, Maybe [String])])
readProcessWithOutputs [String
hie_bios_output] LogAction IO (WithSeverity Log)
l String
workDir
      forall a b. (a -> b) -> a -> b
$ CradleProjectConfig -> [String] -> CreateProcess
stackProcess CradleProjectConfig
syaml
          forall a b. (a -> b) -> a -> b
$  [String
"repl", String
"--no-nix-pure", String
"--with-ghc", String
wrapper_fp]
          forall a. Semigroup a => a -> a -> a
<> [ String
comp | Just String
comp <- [Maybe String
mc] ]

  (ExitCode
ex2, [String]
pkg_args, [String]
stdr, [(String, Maybe [String])]
_) <-
    [String]
-> LogAction IO (WithSeverity Log)
-> String
-> CreateProcess
-> IO (ExitCode, [String], [String], [(String, Maybe [String])])
readProcessWithOutputs [String
hie_bios_output] LogAction IO (WithSeverity Log)
l String
workDir
      forall a b. (a -> b) -> a -> b
$ CradleProjectConfig -> [String] -> CreateProcess
stackProcess CradleProjectConfig
syaml [String
"path", String
"--ghc-package-path"]

  let split_pkgs :: [String]
split_pkgs = forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap String -> [String]
splitSearchPath [String]
pkg_args
      pkg_ghc_args :: [String]
pkg_ghc_args = forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (\String
p -> [String
"-package-db", String
p] ) [String]
split_pkgs
      args :: [String]
args = forall a. a -> Maybe a -> a
fromMaybe [] Maybe [String]
maybeArgs
  case [String] -> Maybe GhcProc
processCabalWrapperArgs [String]
args of
      Maybe GhcProc
Nothing -> do
        -- Best effort. Assume the working directory is the
        -- the root of the component, so we are right in trivial cases at least.
        [String]
deps <- String -> String -> CradleProjectConfig -> IO [String]
stackCradleDependencies String
workDir String
workDir CradleProjectConfig
syaml
        forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ forall r. CradleError -> CradleLoadResult r
CradleFail
                  ([String] -> ExitCode -> [String] -> CradleError
CradleError [String]
deps ExitCode
ex1 forall a b. (a -> b) -> a -> b
$
                    [ String
"Failed to parse result of calling stack" ]
                    forall a. [a] -> [a] -> [a]
++ [String]
stde
                    forall a. [a] -> [a] -> [a]
++ [String]
args
                  )

      Just (String
componentDir, [String]
ghc_args) -> do
        [String]
deps <- String -> String -> CradleProjectConfig -> IO [String]
stackCradleDependencies String
workDir String
componentDir CradleProjectConfig
syaml
        forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ (ExitCode, [String], String, [String])
-> [String] -> CradleLoadResult ComponentOptions
makeCradleResult
                  ( [ExitCode] -> ExitCode
combineExitCodes [ExitCode
ex1, ExitCode
ex2]
                  , [String]
stde forall a. [a] -> [a] -> [a]
++ [String]
stdr, String
componentDir
                  , [String]
ghc_args forall a. [a] -> [a] -> [a]
++ [String]
pkg_ghc_args
                  )
                  [String]
deps

stackProcess :: CradleProjectConfig -> [String] -> CreateProcess
stackProcess :: CradleProjectConfig -> [String] -> CreateProcess
stackProcess CradleProjectConfig
syaml [String]
args = String -> [String] -> CreateProcess
proc String
"stack" forall a b. (a -> b) -> a -> b
$ CradleProjectConfig -> [String]
stackYamlProcessArgs CradleProjectConfig
syaml forall a. Semigroup a => a -> a -> a
<> [String]
args

combineExitCodes :: [ExitCode] -> ExitCode
combineExitCodes :: [ExitCode] -> ExitCode
combineExitCodes = forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr ExitCode -> ExitCode -> ExitCode
go ExitCode
ExitSuccess
  where
    go :: ExitCode -> ExitCode -> ExitCode
go ExitCode
ExitSuccess ExitCode
b = ExitCode
b
    go ExitCode
a ExitCode
_ = ExitCode
a

stackExecutable :: MaybeT IO FilePath
stackExecutable :: MaybeT IO String
stackExecutable = forall (m :: * -> *) a. m (Maybe a) -> MaybeT m a
MaybeT forall a b. (a -> b) -> a -> b
$ String -> IO (Maybe String)
findExecutable String
"stack"

stackWorkDir :: FilePath -> MaybeT IO FilePath
stackWorkDir :: String -> MaybeT IO String
stackWorkDir = (String -> Bool) -> String -> MaybeT IO String
findFileUpwards forall {a}. (Eq a, IsString a) => a -> Bool
isStack
  where
    isStack :: a -> Bool
isStack a
name = a
name forall a. Eq a => a -> a -> Bool
== a
"stack.yaml"

{-
-- Support removed for 0.3 but should be added back in the future
----------------------------------------------------------------------------
-- rules_haskell - Thanks for David Smith for helping with this one.
-- Looks for the directory containing a WORKSPACE file
--
rulesHaskellWorkDir :: FilePath -> MaybeT IO FilePath
rulesHaskellWorkDir fp =
  findFileUpwards (== "WORKSPACE") fp

rulesHaskellCradle :: FilePath -> Cradle
rulesHaskellCradle wdir =
  Cradle
    { cradleRootDir  = wdir
    , cradleOptsProg   = CradleAction
        { actionName = "bazel"
        , runCradle = rulesHaskellAction wdir
        }
    }

rulesHaskellCradleDependencies :: FilePath -> IO [FilePath]
rulesHaskellCradleDependencies _wdir = return ["BUILD.bazel", "WORKSPACE"]

bazelCommand :: String
bazelCommand = $(embedStringFile "wrappers/bazel")

rulesHaskellAction :: FilePath -> FilePath -> IO (CradleLoadResult ComponentOptions)
rulesHaskellAction workDir fp = do
  wrapper_fp <- writeSystemTempFile "wrapper" bazelCommand
  setFileMode wrapper_fp accessModes
  let rel_path = makeRelative workDir fp
  (ex, args, stde) <-
      readProcessWithOutputFile workDir wrapper_fp [rel_path] []
  let args'  = filter (/= '\'') args
  let args'' = filter (/= "\"$GHCI_LOCATION\"") (words args')
  deps <- rulesHaskellCradleDependencies workDir
  return $ makeCradleResult (ex, stde, args'') deps


------------------------------------------------------------------------------
-- Obelisk Cradle
-- Searches for the directory which contains `.obelisk`.

obeliskWorkDir :: FilePath -> MaybeT IO FilePath
obeliskWorkDir fp = do
  -- Find a possible root which will contain the cabal.project
  wdir <- findFileUpwards (== "cabal.project") fp
  -- Check for the ".obelisk" folder in this directory
  check <- liftIO $ doesDirectoryExist (wdir </> ".obelisk")
  unless check (fail "Not obelisk dir")
  return wdir

obeliskCradleDependencies :: FilePath -> IO [FilePath]
obeliskCradleDependencies _wdir = return []

obeliskCradle :: FilePath -> Cradle
obeliskCradle wdir =
  Cradle
    { cradleRootDir  = wdir
    , cradleOptsProg = CradleAction
        { actionName = "obelisk"
        , runCradle = obeliskAction wdir
        }
    }

obeliskAction :: FilePath -> FilePath -> IO (CradleLoadResult ComponentOptions)
obeliskAction workDir _fp = do
  (ex, args, stde) <-
      readProcessWithOutputFile workDir "ob" ["ide-args"] []

  o_deps <- obeliskCradleDependencies workDir
  return (makeCradleResult (ex, stde, words args) o_deps )

-}
------------------------------------------------------------------------------
-- Utilities


-- | Searches upwards for the first directory containing a file to match
-- the predicate.
findFileUpwards :: (FilePath -> Bool) -> FilePath -> MaybeT IO FilePath
findFileUpwards :: (String -> Bool) -> String -> MaybeT IO String
findFileUpwards String -> Bool
p String
dir = do
  [String]
cnts <-
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO
    forall a b. (a -> b) -> a -> b
$ forall e b a.
Exception e =>
(e -> Maybe b) -> (b -> IO a) -> IO a -> IO a
handleJust
        -- Catch permission errors
        (\(IOError
e :: IOError) -> if IOError -> Bool
isPermissionError IOError
e then forall a. a -> Maybe a
Just [] else forall a. Maybe a
Nothing)
        forall (f :: * -> *) a. Applicative f => a -> f a
pure
        ((String -> Bool) -> String -> IO [String]
findFile String -> Bool
p String
dir)

  case [String]
cnts of
    [] | String
dir' forall a. Eq a => a -> a -> Bool
== String
dir -> forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"No cabal files"
            | Bool
otherwise   -> (String -> Bool) -> String -> MaybeT IO String
findFileUpwards String -> Bool
p String
dir'
    String
_ : [String]
_ -> forall (m :: * -> *) a. Monad m => a -> m a
return String
dir
  where dir' :: String
dir' = String -> String
takeDirectory String
dir

-- | Sees if any file in the directory matches the predicate
findFile :: (FilePath -> Bool) -> FilePath -> IO [FilePath]
findFile :: (String -> Bool) -> String -> IO [String]
findFile String -> Bool
p String
dir = do
  Bool
b <- String -> IO Bool
doesDirectoryExist String
dir
  if Bool
b then IO [String]
getFiles forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= forall (m :: * -> *) a.
Applicative m =>
(a -> m Bool) -> [a] -> m [a]
filterM String -> IO Bool
doesPredFileExist else forall (m :: * -> *) a. Monad m => a -> m a
return []
  where
    getFiles :: IO [String]
getFiles = forall a. (a -> Bool) -> [a] -> [a]
filter String -> Bool
p forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> IO [String]
getDirectoryContents String
dir
    doesPredFileExist :: String -> IO Bool
doesPredFileExist String
file = String -> IO Bool
doesFileExist forall a b. (a -> b) -> a -> b
$ String
dir String -> String -> String
</> String
file

-- | Some environments (e.g. stack exec) include GHC_PACKAGE_PATH.
-- Cabal v2 *will* complain, even though or precisely because it ignores them.
-- Unset them from the environment to sidestep this
getCleanEnvironment :: IO [(String, String)]
getCleanEnvironment :: IO [(String, String)]
getCleanEnvironment = do
  forall k v. HashMap k v -> [(k, v)]
Map.toList forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall k v. (Eq k, Hashable k) => k -> HashMap k v -> HashMap k v
Map.delete String
"GHC_PACKAGE_PATH" forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall k v. (Eq k, Hashable k) => [(k, v)] -> HashMap k v
Map.fromList forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO [(String, String)]
getEnvironment

type Outputs = [OutputName]
type OutputName = String

-- | Call a given process with temp files for the process to write to.
-- * The process can discover the temp files paths by reading the environment.
-- * The contents of the temp files are returned by this function, if any.
-- * The logging function is called every time the process emits anything to stdout or stderr.
-- it can be used to report progress of the process to a user.
-- * The process is executed in the given directory.
readProcessWithOutputs
  :: Outputs  -- ^ Names of the outputs produced by this process
  -> LogAction IO (WithSeverity Log) -- ^ Output of the process is emitted as logs.
  -> FilePath -- ^ Working directory. Process is executed in this directory.
  -> CreateProcess -- ^ Parameters for the process to be executed.
  -> IO (ExitCode, [String], [String], [(OutputName, Maybe [String])])
readProcessWithOutputs :: [String]
-> LogAction IO (WithSeverity Log)
-> String
-> CreateProcess
-> IO (ExitCode, [String], [String], [(String, Maybe [String])])
readProcessWithOutputs [String]
outputNames LogAction IO (WithSeverity Log)
l String
workDir CreateProcess
cp = forall a b c. (a -> b -> c) -> b -> a -> c
flip forall {k} (r :: k) (m :: k -> *) a.
ContT r m a -> (a -> m r) -> m r
runContT forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ do
  [(String, String)]
old_env <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO [(String, String)]
getCleanEnvironment
  [(String, String)]
output_files <- forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
traverse (forall a.
[(String, String)] -> String -> ContT a IO (String, String)
withOutput [(String, String)]
old_env) [String]
outputNames

  let process :: CreateProcess
process = CreateProcess
cp { env :: Maybe [(String, String)]
env = forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ [(String, String)]
output_files forall a. [a] -> [a] -> [a]
++ forall a. a -> Maybe a -> a
fromMaybe [(String, String)]
old_env (CreateProcess -> Maybe [(String, String)]
env CreateProcess
cp),
                     cwd :: Maybe String
cwd = forall a. a -> Maybe a
Just String
workDir
                    }

    -- Windows line endings are not converted so you have to filter out `'r` characters
  let loggingConduit :: ConduitT ByteString c IO [String]
loggingConduit = forall (m :: * -> *). MonadThrow m => ConduitT ByteString Text m ()
C.decodeUtf8  forall (m :: * -> *) a b c r.
Monad m =>
ConduitT a b m () -> ConduitT b c m r -> ConduitT a c m r
C..| forall (m :: * -> *). Monad m => ConduitT Text Text m ()
C.lines forall (m :: * -> *) a b c r.
Monad m =>
ConduitT a b m () -> ConduitT b c m r -> ConduitT a c m r
C..| forall seq (m :: * -> *).
(IsSequence seq, Monad m) =>
(Element seq -> Bool) -> ConduitT seq seq m ()
C.filterE (forall a. Eq a => a -> a -> Bool
/= Char
'\r')
        forall (m :: * -> *) a b c r.
Monad m =>
ConduitT a b m () -> ConduitT b c m r -> ConduitT a c m r
C..| forall (m :: * -> *) a b. Monad m => (a -> b) -> ConduitT a b m ()
C.map Text -> String
T.unpack forall (m :: * -> *) a b c r.
Monad m =>
ConduitT a b m () -> ConduitT b c m r -> ConduitT a c m r
C..| forall (m :: * -> *) a. Monad m => (a -> m ()) -> ConduitT a a m ()
C.iterM (\String
msg -> LogAction IO (WithSeverity Log)
l forall (m :: * -> *) msg. LogAction m msg -> msg -> m ()
<& String -> Log
LogProcessOutput String
msg forall msg. msg -> Severity -> WithSeverity msg
`WithSeverity` Severity
Debug) forall (m :: * -> *) a b c r.
Monad m =>
ConduitT a b m () -> ConduitT b c m r -> ConduitT a c m r
C..| forall (m :: * -> *) a o. Monad m => ConduitT a o m [a]
C.sinkList
  forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ LogAction IO (WithSeverity Log)
l forall (m :: * -> *) msg. LogAction m msg -> msg -> m ()
<& CreateProcess -> Log
LogCreateProcessRun CreateProcess
process forall msg. msg -> Severity -> WithSeverity msg
`WithSeverity` Severity
Info
  (ExitCode
ex, [String]
stdo, [String]
stde) <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a b.
MonadUnliftIO m =>
CreateProcess
-> ConduitT () ByteString m ()
-> ConduitT ByteString Void m a
-> ConduitT ByteString Void m b
-> m (ExitCode, a, b)
sourceProcessWithStreams CreateProcess
process forall a. Monoid a => a
mempty forall {c}. ConduitT ByteString c IO [String]
loggingConduit forall {c}. ConduitT ByteString c IO [String]
loggingConduit

  [(String, Maybe [String])]
res <- forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
t a -> (a -> m b) -> m (t b)
forM [(String, String)]
output_files forall a b. (a -> b) -> a -> b
$ \(String
name,String
path) ->
          forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ (String
name,) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> IO (Maybe [String])
readOutput String
path

  forall (m :: * -> *) a. Monad m => a -> m a
return (ExitCode
ex, [String]
stdo, [String]
stde, [(String, Maybe [String])]
res)

    where
      readOutput :: FilePath -> IO (Maybe [String])
      readOutput :: String -> IO (Maybe [String])
readOutput String
path = do
        Bool
haveFile <- String -> IO Bool
doesFileExist String
path
        if Bool
haveFile
          then forall r. String -> IOMode -> (Handle -> IO r) -> IO r
withFile String
path IOMode
ReadMode forall a b. (a -> b) -> a -> b
$ \Handle
handle -> do
            Handle -> BufferMode -> IO ()
hSetBuffering Handle
handle BufferMode
LineBuffering
            !String
res <- forall a. NFData a => a -> a
force forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Handle -> IO String
hGetContents Handle
handle
            forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ String -> [String]
lines forall a b. (a -> b) -> a -> b
$ forall a. (a -> Bool) -> [a] -> [a]
filter (forall a. Eq a => a -> a -> Bool
/= Char
'\r') String
res
          else
            forall (m :: * -> *) a. Monad m => a -> m a
return forall a. Maybe a
Nothing

      withOutput :: [(String,String)] -> OutputName -> ContT a IO (OutputName, String)
      withOutput :: forall a.
[(String, String)] -> String -> ContT a IO (String, String)
withOutput [(String, String)]
env' String
name =
        case forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup String
name [(String, String)]
env' of
          Just file :: String
file@(Char
_:String
_) -> forall {k} (r :: k) (m :: k -> *) a.
((a -> m r) -> m r) -> ContT r m a
ContT forall a b. (a -> b) -> a -> b
$ \(String, String) -> IO a
action -> do
            String -> IO ()
removeFileIfExists String
file
            (String, String) -> IO a
action (String
name, String
file)
          Maybe String
_ -> forall {k} (r :: k) (m :: k -> *) a.
((a -> m r) -> m r) -> ContT r m a
ContT forall a b. (a -> b) -> a -> b
$ \(String, String) -> IO a
action -> forall (m :: * -> *) a.
(MonadIO m, MonadMask m) =>
String -> (String -> Handle -> m a) -> m a
withSystemTempFile String
name forall a b. (a -> b) -> a -> b
$ \ String
file Handle
h -> do
            Handle -> IO ()
hClose Handle
h
            String -> IO ()
removeFileIfExists String
file
            (String, String) -> IO a
action (String
name, String
file)

removeFileIfExists :: FilePath -> IO ()
removeFileIfExists :: String -> IO ()
removeFileIfExists String
f = do
  Bool
yes <- String -> IO Bool
doesFileExist String
f
  forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when Bool
yes (String -> IO ()
removeFile String
f)

makeCradleResult :: (ExitCode, [String], FilePath, [String]) -> [FilePath] -> CradleLoadResult ComponentOptions
makeCradleResult :: (ExitCode, [String], String, [String])
-> [String] -> CradleLoadResult ComponentOptions
makeCradleResult (ExitCode
ex, [String]
err, String
componentDir, [String]
gopts) [String]
deps =
  case ExitCode
ex of
    ExitFailure Int
_ -> forall r. CradleError -> CradleLoadResult r
CradleFail ([String] -> ExitCode -> [String] -> CradleError
CradleError [String]
deps ExitCode
ex [String]
err)
    ExitCode
_ ->
        let compOpts :: ComponentOptions
compOpts = [String] -> String -> [String] -> ComponentOptions
ComponentOptions [String]
gopts String
componentDir [String]
deps
        in forall r. r -> CradleLoadResult r
CradleSuccess ComponentOptions
compOpts

-- | Calls @ghc --print-libdir@, with just whatever's on the PATH.
runGhcCmdOnPath :: LogAction IO (WithSeverity Log) -> FilePath -> [String] -> IO (CradleLoadResult String)
runGhcCmdOnPath :: LogAction IO (WithSeverity Log)
-> String -> [String] -> IO (CradleLoadResult String)
runGhcCmdOnPath LogAction IO (WithSeverity Log)
l String
wdir [String]
args = LogAction IO (WithSeverity Log)
-> String
-> String
-> [String]
-> String
-> IO (CradleLoadResult String)
readProcessWithCwd LogAction IO (WithSeverity Log)
l String
wdir String
"ghc" [String]
args String
""
  -- case mResult of
  --   Nothing

-- | Wrapper around 'readCreateProcess' that sets the working directory and
-- clears the environment, suitable for invoking cabal/stack and raw ghc commands.
readProcessWithCwd :: LogAction IO (WithSeverity Log) -> FilePath -> FilePath -> [String] -> String -> IO (CradleLoadResult String)
readProcessWithCwd :: LogAction IO (WithSeverity Log)
-> String
-> String
-> [String]
-> String
-> IO (CradleLoadResult String)
readProcessWithCwd LogAction IO (WithSeverity Log)
l String
dir String
cmd [String]
args String
stdin = forall (m :: * -> *) a.
CradleLoadResultT m a -> m (CradleLoadResult a)
runCradleResultT forall a b. (a -> b) -> a -> b
$ LogAction IO (WithSeverity Log)
-> String
-> String
-> [String]
-> String
-> CradleLoadResultT IO String
readProcessWithCwd_ LogAction IO (WithSeverity Log)
l String
dir String
cmd [String]
args String
stdin

readProcessWithCwd_ :: LogAction IO (WithSeverity Log) -> FilePath -> FilePath -> [String] -> String -> CradleLoadResultT IO String
readProcessWithCwd_ :: LogAction IO (WithSeverity Log)
-> String
-> String
-> [String]
-> String
-> CradleLoadResultT IO String
readProcessWithCwd_ LogAction IO (WithSeverity Log)
l String
dir String
cmd [String]
args String
stdin = do
  [(String, String)]
cleanEnv <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO [(String, String)]
getCleanEnvironment
  let createdProc' :: CreateProcess
createdProc' = (String -> [String] -> CreateProcess
proc String
cmd [String]
args) { cwd :: Maybe String
cwd = forall a. a -> Maybe a
Just String
dir, env :: Maybe [(String, String)]
env = forall a. a -> Maybe a
Just [(String, String)]
cleanEnv }
  LogAction IO (WithSeverity Log)
-> CreateProcess -> String -> CradleLoadResultT IO String
readProcessWithCwd' LogAction IO (WithSeverity Log)
l CreateProcess
createdProc' String
stdin

-- | Wrapper around 'readCreateProcessWithExitCode', wrapping the result in
-- a 'CradleLoadResult'. Provides better error messages than raw 'readCreateProcess'.
readProcessWithCwd' :: LogAction IO (WithSeverity Log) -> CreateProcess -> String -> CradleLoadResultT IO String
readProcessWithCwd' :: LogAction IO (WithSeverity Log)
-> CreateProcess -> String -> CradleLoadResultT IO String
readProcessWithCwd' LogAction IO (WithSeverity Log)
l CreateProcess
createdProcess String
stdin = do
  Maybe (ExitCode, String, String)
mResult <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall (f :: * -> *) a. Alternative f => f a -> f (Maybe a)
optional forall a b. (a -> b) -> a -> b
$ CreateProcess -> String -> IO (ExitCode, String, String)
readCreateProcessWithExitCode CreateProcess
createdProcess String
stdin
  forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ LogAction IO (WithSeverity Log)
l forall (m :: * -> *) msg. LogAction m msg -> msg -> m ()
<& CreateProcess -> Log
LogCreateProcessRun CreateProcess
createdProcess forall msg. msg -> Severity -> WithSeverity msg
`WithSeverity` Severity
Debug
  let cmdString :: String
cmdString = CmdSpec -> String
prettyCmdSpec forall a b. (a -> b) -> a -> b
$ CreateProcess -> CmdSpec
cmdspec CreateProcess
createdProcess
  case Maybe (ExitCode, String, String)
mResult of
    Just (ExitCode
ExitSuccess, String
stdo, String
_) -> forall (f :: * -> *) a. Applicative f => a -> f a
pure String
stdo
    Just (ExitCode
exitCode, String
stdo, String
stde) -> forall (m :: * -> *) a.
Monad m =>
CradleError -> CradleLoadResultT m a
throwCE forall a b. (a -> b) -> a -> b
$
      [String] -> ExitCode -> [String] -> CradleError
CradleError [] ExitCode
exitCode forall a b. (a -> b) -> a -> b
$
        [String
"Error when calling " forall a. Semigroup a => a -> a -> a
<> String
cmdString, String
stdo, String
stde] forall a. Semigroup a => a -> a -> a
<> CreateProcess -> [String]
prettyProcessEnv CreateProcess
createdProcess
    Maybe (ExitCode, String, String)
Nothing -> forall (m :: * -> *) a.
Monad m =>
CradleError -> CradleLoadResultT m a
throwCE forall a b. (a -> b) -> a -> b
$
      [String] -> ExitCode -> [String] -> CradleError
CradleError [] ExitCode
ExitSuccess forall a b. (a -> b) -> a -> b
$
        [String
"Couldn't execute " forall a. Semigroup a => a -> a -> a
<> String
cmdString] forall a. Semigroup a => a -> a -> a
<> CreateProcess -> [String]
prettyProcessEnv CreateProcess
createdProcess