{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE LambdaCase        #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards   #-}

-- Types and functions related to Stack's @script@ command.

module Stack.Script
  ( ScriptOpts (..)
  , ScriptExecute (..)
  , ShouldRun (..)
  , scriptCmd
  ) where

import           Data.ByteString.Builder ( toLazyByteString )
import qualified Data.ByteString.Char8 as S8
import qualified Data.Conduit.List as CL
import           Data.List.Split ( splitWhen )
import qualified Data.Map.Strict as Map
import qualified Data.Set as Set
import           Distribution.Compiler ( CompilerFlavor (..) )
import           Distribution.ModuleName ( ModuleName )
import qualified Distribution.PackageDescription as PD
import qualified Distribution.Types.CondTree as C
import           Distribution.Types.ModuleReexport ( moduleReexportName )
import           Distribution.Types.PackageName ( mkPackageName )
import           Distribution.Types.VersionRange ( withinRange )
import           Distribution.System ( Platform (..) )
import qualified Pantry.SHA256 as SHA256
import           Path
                   ( (</>), filename, fromAbsDir, fromAbsFile, fromRelFile
                   , parent, parseRelDir, replaceExtension, splitExtension
                   )
import           Path.IO ( getModificationTime, resolveFile' )
import qualified RIO.Directory as Dir
import           RIO.Process ( exec, proc, readProcessStdout_, withWorkingDir )
import qualified RIO.Text as T
import           Stack.Build ( build )
import           Stack.Build.Installed ( getInstalled, toInstallMap )
import           Stack.Constants ( osIsWindows, relDirScripts )
import           Stack.Prelude
import           Stack.Runners
                   ( ShouldReexec (..), withConfig, withDefaultEnvConfig )
import           Stack.Setup ( withNewLocalBuildTargets )
import           Stack.SourceMap ( getCompilerInfo, immutableLocSha )
import           Stack.Types.Compiler ( ActualCompiler (..) )
import           Stack.Types.CompilerPaths
                   ( CompilerPaths (..), GhcPkgExe (..), HasCompiler (..) )
import           Stack.Types.Config ( Config (..), HasConfig (..), stackRootL )
import           Stack.Types.ConfigMonoid ( ConfigMonoid (..) )
import           Stack.Types.DumpPackage ( DumpPackage (..) )
import           Stack.Types.EnvConfig
                   ( EnvConfig (..), HasEnvConfig (..), actualCompilerVersionL
                   , appropriateGhcColorFlag
                   )
import           Stack.Types.EnvSettings ( defaultEnvSettings )
import           Stack.Types.GlobalOpts ( GlobalOpts (..) )
import           Stack.Types.Platform ( HasPlatform (..) )
import           Stack.Types.Runner ( Runner, globalOptsL )
import           Stack.Types.SourceMap
                   ( CommonPackage (..), DepPackage (..), SourceMap (..) )
import           Stack.Types.StackYamlLoc ( StackYamlLoc (..) )
import           System.FilePath ( splitDrive )

-- | Type representing exceptions thrown by functions exported by the

-- "Stack.Script" module.

data ScriptException
  = MutableDependenciesForScript [PackageName]
  | AmbiguousModuleName ModuleName [PackageName]
  | ArgumentsWithNoRunInvalid
  | NoRunWithoutCompilationInvalid
  | FailedToParseScriptFileAsDirBug (Path Rel File)
  | FailedToParseFileAsDirBug (Path Abs Dir)
  deriving (Int -> ScriptException -> ShowS
[ScriptException] -> ShowS
ScriptException -> String
(Int -> ScriptException -> ShowS)
-> (ScriptException -> String)
-> ([ScriptException] -> ShowS)
-> Show ScriptException
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ScriptException -> ShowS
showsPrec :: Int -> ScriptException -> ShowS
$cshow :: ScriptException -> String
show :: ScriptException -> String
$cshowList :: [ScriptException] -> ShowS
showList :: [ScriptException] -> ShowS
Show, Typeable)

instance Exception ScriptException where
  displayException :: ScriptException -> String
displayException (MutableDependenciesForScript [PackageName]
names) = [String] -> String
unlines
    ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ String
"Error: [S-4994]"
    String -> [String] -> [String]
forall a. a -> [a] -> [a]
: String
"No mutable packages are allowed in the 'script' command. Mutable \
      \packages found:"
    String -> [String] -> [String]
forall a. a -> [a] -> [a]
: (PackageName -> String) -> [PackageName] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (\PackageName
name -> String
"- " String -> ShowS
forall a. [a] -> [a] -> [a]
++ PackageName -> String
packageNameString PackageName
name) [PackageName]
names
  displayException (AmbiguousModuleName ModuleName
mname [PackageName]
pkgs) = [String] -> String
unlines
    ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ String
"Error: [S-1691]"
    String -> [String] -> [String]
forall a. a -> [a] -> [a]
: (  String
"Module "
      String -> ShowS
forall a. [a] -> [a] -> [a]
++ ModuleName -> String
moduleNameString ModuleName
mname
      String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" appears in multiple packages: "
      )
    String -> [String] -> [String]
forall a. a -> [a] -> [a]
: [ [String] -> String
unwords ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ (PackageName -> String) -> [PackageName] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map PackageName -> String
packageNameString [PackageName]
pkgs ]
  displayException ScriptException
ArgumentsWithNoRunInvalid =
    String
"Error: [S-5067]\n"
    String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"'--no-run' incompatible with arguments."
  displayException ScriptException
NoRunWithoutCompilationInvalid =
    String
"Error: [S-9469]\n"
    String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"'--no-run' requires either '--compile' or '--optimize'."
  displayException (FailedToParseScriptFileAsDirBug Path Rel File
fp) = String -> ShowS
bugReport String
"[S-5055]" ShowS -> ShowS
forall a b. (a -> b) -> a -> b
$
       String
"Failed to parse script file name as directory:\n"
    String -> ShowS
forall a. Semigroup a => a -> a -> a
<> Path Rel File -> String
fromRelFile Path Rel File
fp String -> ShowS
forall a. Semigroup a => a -> a -> a
<> String
"\n"
  displayException (FailedToParseFileAsDirBug Path Abs Dir
p) = String -> ShowS
bugReport String
"[S-9464]" ShowS -> ShowS
forall a b. (a -> b) -> a -> b
$
       String
"Failed to parse path to script file as directory:\n"
    String -> ShowS
forall a. Semigroup a => a -> a -> a
<> Path Abs Dir -> String
fromAbsDir Path Abs Dir
p String -> ShowS
forall a. Semigroup a => a -> a -> a
<> String
"\n"

-- | Type representing choices of interpreting, compiling (without optimisation)

-- and compiling (with optimisation).

data ScriptExecute
  = SEInterpret
  | SECompile
    -- ^ Without optimisation.

  | SEOptimize
    -- ^ Compile with optimisation.

  deriving Int -> ScriptExecute -> ShowS
[ScriptExecute] -> ShowS
ScriptExecute -> String
(Int -> ScriptExecute -> ShowS)
-> (ScriptExecute -> String)
-> ([ScriptExecute] -> ShowS)
-> Show ScriptExecute
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ScriptExecute -> ShowS
showsPrec :: Int -> ScriptExecute -> ShowS
$cshow :: ScriptExecute -> String
show :: ScriptExecute -> String
$cshowList :: [ScriptExecute] -> ShowS
showList :: [ScriptExecute] -> ShowS
Show

-- | Type representing choices of whether to run or not.

data ShouldRun
  = YesRun
    -- ^ Run.

  | NoRun
    -- ^ Do not run.

  deriving Int -> ShouldRun -> ShowS
[ShouldRun] -> ShowS
ShouldRun -> String
(Int -> ShouldRun -> ShowS)
-> (ShouldRun -> String)
-> ([ShouldRun] -> ShowS)
-> Show ShouldRun
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ShouldRun -> ShowS
showsPrec :: Int -> ShouldRun -> ShowS
$cshow :: ShouldRun -> String
show :: ShouldRun -> String
$cshowList :: [ShouldRun] -> ShowS
showList :: [ShouldRun] -> ShowS
Show

-- | Type representing command line options for the @stack script@ command.

data ScriptOpts = ScriptOpts
  { ScriptOpts -> [String]
soPackages :: ![String]
  , ScriptOpts -> String
soFile :: !FilePath
  , ScriptOpts -> [String]
soArgs :: ![String]
  , ScriptOpts -> ScriptExecute
soCompile :: !ScriptExecute
  , ScriptOpts -> Bool
soUseRoot :: !Bool
  , ScriptOpts -> [String]
soGhcOptions :: ![String]
  , ScriptOpts -> [PackageIdentifierRevision]
soScriptExtraDeps :: ![PackageIdentifierRevision]
  , ScriptOpts -> ShouldRun
soShouldRun :: !ShouldRun
  }
  deriving Int -> ScriptOpts -> ShowS
[ScriptOpts] -> ShowS
ScriptOpts -> String
(Int -> ScriptOpts -> ShowS)
-> (ScriptOpts -> String)
-> ([ScriptOpts] -> ShowS)
-> Show ScriptOpts
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ScriptOpts -> ShowS
showsPrec :: Int -> ScriptOpts -> ShowS
$cshow :: ScriptOpts -> String
show :: ScriptOpts -> String
$cshowList :: [ScriptOpts] -> ShowS
showList :: [ScriptOpts] -> ShowS
Show

-- | Run a Stack Script

scriptCmd :: ScriptOpts -> RIO Runner ()
scriptCmd :: ScriptOpts -> RIO Runner ()
scriptCmd ScriptOpts
opts = do
  -- Some warnings in case the user somehow tries to set a stack.yaml location.

  -- Note that in this functions we use logError instead of logWarn because,

  -- when using the interpreter mode, only error messages are shown. See:

  -- https://github.com/commercialhaskell/stack/issues/3007

  Getting StackYamlLoc Runner StackYamlLoc -> RIO Runner StackYamlLoc
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view ((GlobalOpts -> Const StackYamlLoc GlobalOpts)
-> Runner -> Const StackYamlLoc Runner
forall env. HasRunner env => Lens' env GlobalOpts
Lens' Runner GlobalOpts
globalOptsL((GlobalOpts -> Const StackYamlLoc GlobalOpts)
 -> Runner -> Const StackYamlLoc Runner)
-> ((StackYamlLoc -> Const StackYamlLoc StackYamlLoc)
    -> GlobalOpts -> Const StackYamlLoc GlobalOpts)
-> Getting StackYamlLoc Runner StackYamlLoc
forall b c a. (b -> c) -> (a -> b) -> a -> c
.(GlobalOpts -> StackYamlLoc)
-> SimpleGetter GlobalOpts StackYamlLoc
forall s a. (s -> a) -> SimpleGetter s a
to GlobalOpts -> StackYamlLoc
globalStackYaml) RIO Runner StackYamlLoc
-> (StackYamlLoc -> RIO Runner ()) -> RIO Runner ()
forall a b. RIO Runner a -> (a -> RIO Runner b) -> RIO Runner b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
    SYLOverride Path Abs File
fp -> Utf8Builder -> RIO Runner ()
forall (m :: * -> *) env.
(MonadIO m, MonadReader env m, HasLogFunc env, HasCallStack) =>
Utf8Builder -> m ()
logError (Utf8Builder -> RIO Runner ()) -> Utf8Builder -> RIO Runner ()
forall a b. (a -> b) -> a -> b
$
         Utf8Builder
"Ignoring override stack.yaml file for script command: "
      Utf8Builder -> Utf8Builder -> Utf8Builder
forall a. Semigroup a => a -> a -> a
<> String -> Utf8Builder
forall a. IsString a => String -> a
fromString (Path Abs File -> String
forall b t. Path b t -> String
toFilePath Path Abs File
fp)
    StackYamlLoc
SYLGlobalProject -> Utf8Builder -> RIO Runner ()
forall (m :: * -> *) env.
(MonadIO m, MonadReader env m, HasLogFunc env, HasCallStack) =>
Utf8Builder -> m ()
logError Utf8Builder
"Ignoring SYLGlobalProject for script command"
    StackYamlLoc
SYLDefault -> () -> RIO Runner ()
forall a. a -> RIO Runner a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
    SYLNoProject [PackageIdentifierRevision]
_ -> Bool -> RIO Runner () -> RIO Runner ()
forall a. HasCallStack => Bool -> a -> a
assert Bool
False (() -> RIO Runner ()
forall a. a -> RIO Runner a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ())

  Path Abs File
file <- String -> RIO Runner (Path Abs File)
forall (m :: * -> *). MonadIO m => String -> m (Path Abs File)
resolveFile' (String -> RIO Runner (Path Abs File))
-> String -> RIO Runner (Path Abs File)
forall a b. (a -> b) -> a -> b
$ ScriptOpts -> String
soFile ScriptOpts
opts
  let scriptFile :: Path Rel File
scriptFile = Path Abs File -> Path Rel File
forall b. Path b File -> Path Rel File
filename Path Abs File
file

  Bool
isNoRunCompile <- FirstFalse -> Bool
fromFirstFalse (FirstFalse -> Bool)
-> (ConfigMonoid -> FirstFalse) -> ConfigMonoid -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ConfigMonoid -> FirstFalse
configMonoidNoRunCompile (ConfigMonoid -> Bool)
-> RIO Runner ConfigMonoid -> RIO Runner Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$>
                           Getting ConfigMonoid Runner ConfigMonoid -> RIO Runner ConfigMonoid
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view ((GlobalOpts -> Const ConfigMonoid GlobalOpts)
-> Runner -> Const ConfigMonoid Runner
forall env. HasRunner env => Lens' env GlobalOpts
Lens' Runner GlobalOpts
globalOptsL((GlobalOpts -> Const ConfigMonoid GlobalOpts)
 -> Runner -> Const ConfigMonoid Runner)
-> ((ConfigMonoid -> Const ConfigMonoid ConfigMonoid)
    -> GlobalOpts -> Const ConfigMonoid GlobalOpts)
-> Getting ConfigMonoid Runner ConfigMonoid
forall b c a. (b -> c) -> (a -> b) -> a -> c
.(GlobalOpts -> ConfigMonoid)
-> SimpleGetter GlobalOpts ConfigMonoid
forall s a. (s -> a) -> SimpleGetter s a
to GlobalOpts -> ConfigMonoid
globalConfigMonoid)

  let scriptDir :: Path Abs Dir
scriptDir = Path Abs File -> Path Abs Dir
forall b t. Path b t -> Path b Dir
parent Path Abs File
file
      modifyGO :: GlobalOpts -> GlobalOpts
modifyGO GlobalOpts
go = GlobalOpts
go
        { globalConfigMonoid :: ConfigMonoid
globalConfigMonoid = (GlobalOpts -> ConfigMonoid
globalConfigMonoid GlobalOpts
go)
            { configMonoidInstallGHC :: FirstTrue
configMonoidInstallGHC = Maybe Bool -> FirstTrue
FirstTrue (Maybe Bool -> FirstTrue) -> Maybe Bool -> FirstTrue
forall a b. (a -> b) -> a -> b
$ Bool -> Maybe Bool
forall a. a -> Maybe a
Just Bool
True
            }
        , globalStackYaml :: StackYamlLoc
globalStackYaml = [PackageIdentifierRevision] -> StackYamlLoc
SYLNoProject ([PackageIdentifierRevision] -> StackYamlLoc)
-> [PackageIdentifierRevision] -> StackYamlLoc
forall a b. (a -> b) -> a -> b
$ ScriptOpts -> [PackageIdentifierRevision]
soScriptExtraDeps ScriptOpts
opts
        }
      (ShouldRun
shouldRun, ScriptExecute
shouldCompile) = if Bool
isNoRunCompile
        then (ShouldRun
NoRun, ScriptExecute
SECompile)
        else (ScriptOpts -> ShouldRun
soShouldRun ScriptOpts
opts, ScriptOpts -> ScriptExecute
soCompile ScriptOpts
opts)

  Path Abs Dir
root <- ShouldReexec
-> RIO Config (Path Abs Dir) -> RIO Runner (Path Abs Dir)
forall a. ShouldReexec -> RIO Config a -> RIO Runner a
withConfig ShouldReexec
NoReexec (RIO Config (Path Abs Dir) -> RIO Runner (Path Abs Dir))
-> RIO Config (Path Abs Dir) -> RIO Runner (Path Abs Dir)
forall a b. (a -> b) -> a -> b
$ Getting (Path Abs Dir) Config (Path Abs Dir)
-> RIO Config (Path Abs Dir)
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting (Path Abs Dir) Config (Path Abs Dir)
forall s. HasConfig s => Lens' s (Path Abs Dir)
Lens' Config (Path Abs Dir)
stackRootL
  Path Abs Dir
outputDir <- if ScriptOpts -> Bool
soUseRoot ScriptOpts
opts
    then do
      Path Rel Dir
scriptFileAsDir <- RIO Runner (Path Rel Dir)
-> (Path Rel Dir -> RIO Runner (Path Rel Dir))
-> Maybe (Path Rel Dir)
-> RIO Runner (Path Rel Dir)
forall b a. b -> (a -> b) -> Maybe a -> b
maybe
        (ScriptException -> RIO Runner (Path Rel Dir)
forall (m :: * -> *) e a. (MonadIO m, Exception e) => e -> m a
throwIO (ScriptException -> RIO Runner (Path Rel Dir))
-> ScriptException -> RIO Runner (Path Rel Dir)
forall a b. (a -> b) -> a -> b
$ Path Rel File -> ScriptException
FailedToParseScriptFileAsDirBug Path Rel File
scriptFile)
        Path Rel Dir -> RIO Runner (Path Rel Dir)
forall a. a -> RIO Runner a
forall (f :: * -> *) a. Applicative f => a -> f a
pure
        (String -> Maybe (Path Rel Dir)
forall (m :: * -> *). MonadThrow m => String -> m (Path Rel Dir)
parseRelDir (String -> Maybe (Path Rel Dir)) -> String -> Maybe (Path Rel Dir)
forall a b. (a -> b) -> a -> b
$ Path Rel File -> String
fromRelFile Path Rel File
scriptFile)
      let fileAsDir :: Path Abs Dir
fileAsDir = Path Abs Dir
scriptDir Path Abs Dir -> Path Rel Dir -> Path Abs Dir
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel Dir
scriptFileAsDir
          -- We drop the information about the drive. On Windows, in principle,

          -- the drive could distinguish between two otherwise identical

          -- fileAsDir (eg C:\MyScript.hs\ D:\MyScript.hs\). In pactice, we

          -- tolerate that possibility as being unlikely.

          (String
_, String
escaped) = String -> (String, String)
splitDrive (Path Abs Dir -> String
fromAbsDir Path Abs Dir
fileAsDir)
      Path Rel Dir
escapedRelDir <- RIO Runner (Path Rel Dir)
-> (Path Rel Dir -> RIO Runner (Path Rel Dir))
-> Maybe (Path Rel Dir)
-> RIO Runner (Path Rel Dir)
forall b a. b -> (a -> b) -> Maybe a -> b
maybe
        (ScriptException -> RIO Runner (Path Rel Dir)
forall (m :: * -> *) e a. (MonadIO m, Exception e) => e -> m a
throwIO (ScriptException -> RIO Runner (Path Rel Dir))
-> ScriptException -> RIO Runner (Path Rel Dir)
forall a b. (a -> b) -> a -> b
$ Path Abs Dir -> ScriptException
FailedToParseFileAsDirBug Path Abs Dir
fileAsDir)
        Path Rel Dir -> RIO Runner (Path Rel Dir)
forall a. a -> RIO Runner a
forall (f :: * -> *) a. Applicative f => a -> f a
pure
        (String -> Maybe (Path Rel Dir)
forall (m :: * -> *). MonadThrow m => String -> m (Path Rel Dir)
parseRelDir String
escaped)
      Path Abs Dir -> RIO Runner (Path Abs Dir)
forall a. a -> RIO Runner a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Path Abs Dir -> RIO Runner (Path Abs Dir))
-> Path Abs Dir -> RIO Runner (Path Abs Dir)
forall a b. (a -> b) -> a -> b
$ Path Abs Dir
root Path Abs Dir -> Path Rel Dir -> Path Abs Dir
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel Dir
relDirScripts Path Rel Dir -> Path Rel Dir -> Path Rel Dir
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel Dir
escapedRelDir
    else Path Abs Dir -> RIO Runner (Path Abs Dir)
forall a. a -> RIO Runner a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Path Abs Dir
scriptDir

  -- path does not necessarily end with an extension.

  let dropExtension :: Path b File -> f (Path b File)
dropExtension Path b File
path = Path b File -> f (Path b File)
forall a. a -> f a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Path b File -> f (Path b File)) -> Path b File -> f (Path b File)
forall a b. (a -> b) -> a -> b
$ Path b File
-> ((Path b File, String) -> Path b File)
-> Maybe (Path b File, String)
-> Path b File
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Path b File
path (Path b File, String) -> Path b File
forall a b. (a, b) -> a
fst (Maybe (Path b File, String) -> Path b File)
-> Maybe (Path b File, String) -> Path b File
forall a b. (a -> b) -> a -> b
$ Path b File -> Maybe (Path b File, String)
forall (m :: * -> *) b.
MonadThrow m =>
Path b File -> m (Path b File, String)
splitExtension Path b File
path

  Path Abs File
exe <- if Bool
osIsWindows
    then String -> Path Abs File -> RIO Runner (Path Abs File)
forall (m :: * -> *) b.
MonadThrow m =>
String -> Path b File -> m (Path b File)
replaceExtension String
".exe" (Path Abs Dir
outputDir Path Abs Dir -> Path Rel File -> Path Abs File
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel File
scriptFile)
    else Path Abs File -> RIO Runner (Path Abs File)
forall {f :: * -> *} {b}.
Applicative f =>
Path b File -> f (Path b File)
dropExtension (Path Abs Dir
outputDir Path Abs Dir -> Path Rel File -> Path Abs File
forall b t. Path b Dir -> Path Rel t -> Path b t
</> Path Rel File
scriptFile)

  case ShouldRun
shouldRun of
    ShouldRun
YesRun -> () -> RIO Runner ()
forall a. a -> RIO Runner a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
    ShouldRun
NoRun -> do
      Bool -> RIO Runner () -> RIO Runner ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless ([String] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([String] -> Bool) -> [String] -> Bool
forall a b. (a -> b) -> a -> b
$ ScriptOpts -> [String]
soArgs ScriptOpts
opts) (RIO Runner () -> RIO Runner ()) -> RIO Runner () -> RIO Runner ()
forall a b. (a -> b) -> a -> b
$ ScriptException -> RIO Runner ()
forall (m :: * -> *) e a. (MonadIO m, Exception e) => e -> m a
throwIO ScriptException
ArgumentsWithNoRunInvalid
      case ScriptExecute
shouldCompile of
        ScriptExecute
SEInterpret -> ScriptException -> RIO Runner ()
forall (m :: * -> *) e a. (MonadIO m, Exception e) => e -> m a
throwIO ScriptException
NoRunWithoutCompilationInvalid
        ScriptExecute
SECompile -> () -> RIO Runner ()
forall a. a -> RIO Runner a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
        ScriptExecute
SEOptimize -> () -> RIO Runner ()
forall a. a -> RIO Runner a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()

  -- Optimization: if we're compiling, and the executable is newer than the

  -- source file, run it immediately.

  (Runner -> Runner) -> RIO Runner () -> RIO Runner ()
forall a. (Runner -> Runner) -> RIO Runner a -> RIO Runner a
forall r (m :: * -> *) a. MonadReader r m => (r -> r) -> m a -> m a
local (ASetter Runner Runner GlobalOpts GlobalOpts
-> (GlobalOpts -> GlobalOpts) -> Runner -> Runner
forall s t a b. ASetter s t a b -> (a -> b) -> s -> t
over ASetter Runner Runner GlobalOpts GlobalOpts
forall env. HasRunner env => Lens' env GlobalOpts
Lens' Runner GlobalOpts
globalOptsL GlobalOpts -> GlobalOpts
modifyGO) (RIO Runner () -> RIO Runner ()) -> RIO Runner () -> RIO Runner ()
forall a b. (a -> b) -> a -> b
$
    case ScriptExecute
shouldCompile of
      ScriptExecute
SEInterpret -> ShouldRun
-> ScriptExecute -> Path Abs File -> Path Abs File -> RIO Runner ()
forall {t}.
ShouldRun
-> ScriptExecute -> Path Abs t -> Path Abs File -> RIO Runner ()
longWay ShouldRun
shouldRun ScriptExecute
shouldCompile Path Abs File
file Path Abs File
exe
      ScriptExecute
SECompile -> ShouldRun
-> ScriptExecute -> Path Abs File -> Path Abs File -> RIO Runner ()
forall {t}.
ShouldRun
-> ScriptExecute -> Path Abs t -> Path Abs File -> RIO Runner ()
shortCut ShouldRun
shouldRun ScriptExecute
shouldCompile Path Abs File
file Path Abs File
exe
      ScriptExecute
SEOptimize -> ShouldRun
-> ScriptExecute -> Path Abs File -> Path Abs File -> RIO Runner ()
forall {t}.
ShouldRun
-> ScriptExecute -> Path Abs t -> Path Abs File -> RIO Runner ()
shortCut ShouldRun
shouldRun ScriptExecute
shouldCompile Path Abs File
file Path Abs File
exe

 where
  runCompiled :: ShouldRun -> Path Abs File -> RIO env ()
runCompiled ShouldRun
shouldRun Path Abs File
exe = do
    case ShouldRun
shouldRun of
      ShouldRun
YesRun -> String -> [String] -> RIO env ()
forall env b.
(HasProcessContext env, HasLogFunc env) =>
String -> [String] -> RIO env b
exec (Path Abs File -> String
fromAbsFile Path Abs File
exe) (ScriptOpts -> [String]
soArgs ScriptOpts
opts)
      ShouldRun
NoRun -> [StyleDoc] -> RIO env ()
forall env (m :: * -> *).
(HasCallStack, HasTerm env, MonadReader env m, MonadIO m) =>
[StyleDoc] -> m ()
prettyInfoL
        [ String -> StyleDoc
flow String
"Compilation finished, executable available at"
        , Style -> StyleDoc -> StyleDoc
style Style
File (String -> StyleDoc
forall a. IsString a => String -> a
fromString (Path Abs File -> String
fromAbsFile Path Abs File
exe)) StyleDoc -> StyleDoc -> StyleDoc
forall a. Semigroup a => a -> a -> a
<> StyleDoc
"."
        ]

  shortCut :: ShouldRun
-> ScriptExecute -> Path Abs t -> Path Abs File -> RIO Runner ()
shortCut ShouldRun
shouldRun ScriptExecute
shouldCompile Path Abs t
file Path Abs File
exe =
    (IOException -> RIO Runner ()) -> RIO Runner () -> RIO Runner ()
forall (m :: * -> *) a.
MonadUnliftIO m =>
(IOException -> m a) -> m a -> m a
handleIO (RIO Runner () -> IOException -> RIO Runner ()
forall a b. a -> b -> a
const (RIO Runner () -> IOException -> RIO Runner ())
-> RIO Runner () -> IOException -> RIO Runner ()
forall a b. (a -> b) -> a -> b
$ ShouldRun
-> ScriptExecute -> Path Abs t -> Path Abs File -> RIO Runner ()
forall {t}.
ShouldRun
-> ScriptExecute -> Path Abs t -> Path Abs File -> RIO Runner ()
longWay ShouldRun
shouldRun ScriptExecute
shouldCompile Path Abs t
file Path Abs File
exe) (RIO Runner () -> RIO Runner ()) -> RIO Runner () -> RIO Runner ()
forall a b. (a -> b) -> a -> b
$ do
      UTCTime
srcMod <- Path Abs t -> RIO Runner UTCTime
forall (m :: * -> *) b t. MonadIO m => Path b t -> m UTCTime
getModificationTime Path Abs t
file
      UTCTime
exeMod <- String -> RIO Runner UTCTime
forall (m :: * -> *). MonadIO m => String -> m UTCTime
Dir.getModificationTime (Path Abs File -> String
fromAbsFile Path Abs File
exe)
      if UTCTime
srcMod UTCTime -> UTCTime -> Bool
forall a. Ord a => a -> a -> Bool
< UTCTime
exeMod
        then ShouldRun -> Path Abs File -> RIO Runner ()
forall {env}.
(HasTerm env, HasProcessContext env) =>
ShouldRun -> Path Abs File -> RIO env ()
runCompiled ShouldRun
shouldRun Path Abs File
exe
        else ShouldRun
-> ScriptExecute -> Path Abs t -> Path Abs File -> RIO Runner ()
forall {t}.
ShouldRun
-> ScriptExecute -> Path Abs t -> Path Abs File -> RIO Runner ()
longWay ShouldRun
shouldRun ScriptExecute
shouldCompile Path Abs t
file Path Abs File
exe

  longWay :: ShouldRun
-> ScriptExecute -> Path Abs t -> Path Abs File -> RIO Runner ()
longWay ShouldRun
shouldRun ScriptExecute
shouldCompile Path Abs t
file Path Abs File
exe =
    ShouldReexec -> RIO Config () -> RIO Runner ()
forall a. ShouldReexec -> RIO Config a -> RIO Runner a
withConfig ShouldReexec
YesReexec (RIO Config () -> RIO Runner ()) -> RIO Config () -> RIO Runner ()
forall a b. (a -> b) -> a -> b
$
    RIO EnvConfig () -> RIO Config ()
forall a. RIO EnvConfig a -> RIO Config a
withDefaultEnvConfig (RIO EnvConfig () -> RIO Config ())
-> RIO EnvConfig () -> RIO Config ()
forall a b. (a -> b) -> a -> b
$ do
      Config
config <- Getting Config EnvConfig Config -> RIO EnvConfig Config
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting Config EnvConfig Config
forall env. HasConfig env => Lens' env Config
Lens' EnvConfig Config
configL
      ProcessContext
menv <- IO ProcessContext -> RIO EnvConfig ProcessContext
forall a. IO a -> RIO EnvConfig a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO ProcessContext -> RIO EnvConfig ProcessContext)
-> IO ProcessContext -> RIO EnvConfig ProcessContext
forall a b. (a -> b) -> a -> b
$ Config -> EnvSettings -> IO ProcessContext
configProcessContextSettings Config
config EnvSettings
defaultEnvSettings
      ProcessContext -> RIO EnvConfig () -> RIO EnvConfig ()
forall env a.
HasProcessContext env =>
ProcessContext -> RIO env a -> RIO env a
withProcessContext ProcessContext
menv (RIO EnvConfig () -> RIO EnvConfig ())
-> RIO EnvConfig () -> RIO EnvConfig ()
forall a b. (a -> b) -> a -> b
$ do
        Maybe String
colorFlag <- RIO EnvConfig (Maybe String)
forall env.
(HasEnvConfig env, HasRunner env) =>
RIO env (Maybe String)
appropriateGhcColorFlag

        Set PackageName
targetsSet <-
          case ScriptOpts -> [String]
soPackages ScriptOpts
opts of
            [] -> String -> RIO EnvConfig (Set PackageName)
getPackagesFromImports (ScriptOpts -> String
soFile ScriptOpts
opts) -- Using the import parser

            [String]
packages -> do
              let targets :: [String]
targets = (String -> [String]) -> [String] -> [String]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap String -> [String]
wordsComma [String]
packages
              [PackageName]
targets' <- (String -> RIO EnvConfig PackageName)
-> [String] -> RIO EnvConfig [PackageName]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
mapM String -> RIO EnvConfig PackageName
forall (m :: * -> *). MonadThrow m => String -> m PackageName
parsePackageNameThrowing [String]
targets
              Set PackageName -> RIO EnvConfig (Set PackageName)
forall a. a -> RIO EnvConfig a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Set PackageName -> RIO EnvConfig (Set PackageName))
-> Set PackageName -> RIO EnvConfig (Set PackageName)
forall a b. (a -> b) -> a -> b
$ [PackageName] -> Set PackageName
forall a. Ord a => [a] -> Set a
Set.fromList [PackageName]
targets'

        Bool -> RIO EnvConfig () -> RIO EnvConfig ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (Set PackageName -> Bool
forall a. Set a -> Bool
Set.null Set PackageName
targetsSet) (RIO EnvConfig () -> RIO EnvConfig ())
-> RIO EnvConfig () -> RIO EnvConfig ()
forall a b. (a -> b) -> a -> b
$ do
          -- Optimization: use the relatively cheap ghc-pkg list --simple-output

          -- to check which packages are installed already. If all needed

          -- packages are available, we can skip the (rather expensive) build

          -- call below.

          GhcPkgExe Path Abs File
pkg <- Getting GhcPkgExe EnvConfig GhcPkgExe -> RIO EnvConfig GhcPkgExe
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view (Getting GhcPkgExe EnvConfig GhcPkgExe -> RIO EnvConfig GhcPkgExe)
-> Getting GhcPkgExe EnvConfig GhcPkgExe -> RIO EnvConfig GhcPkgExe
forall a b. (a -> b) -> a -> b
$ Getting GhcPkgExe EnvConfig CompilerPaths
forall env. HasCompiler env => SimpleGetter env CompilerPaths
SimpleGetter EnvConfig CompilerPaths
compilerPathsLGetting GhcPkgExe EnvConfig CompilerPaths
-> ((GhcPkgExe -> Const GhcPkgExe GhcPkgExe)
    -> CompilerPaths -> Const GhcPkgExe CompilerPaths)
-> Getting GhcPkgExe EnvConfig GhcPkgExe
forall b c a. (b -> c) -> (a -> b) -> a -> c
.(CompilerPaths -> GhcPkgExe)
-> SimpleGetter CompilerPaths GhcPkgExe
forall s a. (s -> a) -> SimpleGetter s a
to CompilerPaths -> GhcPkgExe
cpPkg
          -- https://github.com/haskell/process/issues/251

          [ByteString]
bss <- ((), [ByteString]) -> [ByteString]
forall a b. (a, b) -> b
snd (((), [ByteString]) -> [ByteString])
-> RIO EnvConfig ((), [ByteString]) -> RIO EnvConfig [ByteString]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String
-> [String]
-> ConduitM ByteString Void (RIO EnvConfig) ()
-> ConduitM ByteString Void (RIO EnvConfig) [ByteString]
-> RIO EnvConfig ((), [ByteString])
forall e o env.
(HasProcessContext env, HasLogFunc env, HasCallStack) =>
String
-> [String]
-> ConduitM ByteString Void (RIO env) e
-> ConduitM ByteString Void (RIO env) o
-> RIO env (e, o)
sinkProcessStderrStdout (Path Abs File -> String
forall b t. Path b t -> String
toFilePath Path Abs File
pkg)
              [String
"list", String
"--simple-output"] ConduitM ByteString Void (RIO EnvConfig) ()
forall (m :: * -> *) i o. Monad m => ConduitT i o m ()
CL.sinkNull ConduitM ByteString Void (RIO EnvConfig) [ByteString]
forall (m :: * -> *) a o. Monad m => ConduitT a o m [a]
CL.consume -- FIXME use the package info from envConfigPackages, or is that crazy?

          let installed :: Set String
installed = [String] -> Set String
forall a. Ord a => [a] -> Set a
Set.fromList
                        ([String] -> Set String) -> [String] -> Set String
forall a b. (a -> b) -> a -> b
$ ShowS -> [String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map ShowS
toPackageName
                        ([String] -> [String]) -> [String] -> [String]
forall a b. (a -> b) -> a -> b
$ String -> [String]
words
                        (String -> [String]) -> String -> [String]
forall a b. (a -> b) -> a -> b
$ ByteString -> String
S8.unpack
                        (ByteString -> String) -> ByteString -> String
forall a b. (a -> b) -> a -> b
$ [ByteString] -> ByteString
S8.concat [ByteString]
bss
          if Set String -> Bool
forall a. Set a -> Bool
Set.null (Set String -> Bool) -> Set String -> Bool
forall a b. (a -> b) -> a -> b
$ Set String -> Set String -> Set String
forall a. Ord a => Set a -> Set a -> Set a
Set.difference ((PackageName -> String) -> Set PackageName -> Set String
forall b a. Ord b => (a -> b) -> Set a -> Set b
Set.map PackageName -> String
packageNameString Set PackageName
targetsSet) Set String
installed
            then Utf8Builder -> RIO EnvConfig ()
forall (m :: * -> *) env.
(MonadIO m, MonadReader env m, HasLogFunc env, HasCallStack) =>
Utf8Builder -> m ()
logDebug Utf8Builder
"All packages already installed"
            else do
              Utf8Builder -> RIO EnvConfig ()
forall (m :: * -> *) env.
(MonadIO m, MonadReader env m, HasLogFunc env, HasCallStack) =>
Utf8Builder -> m ()
logDebug Utf8Builder
"Missing packages, performing installation"
              let targets :: [Text]
targets =
                    (PackageName -> Text) -> [PackageName] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map (String -> Text
T.pack (String -> Text) -> (PackageName -> String) -> PackageName -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PackageName -> String
packageNameString) ([PackageName] -> [Text]) -> [PackageName] -> [Text]
forall a b. (a -> b) -> a -> b
$ Set PackageName -> [PackageName]
forall a. Set a -> [a]
Set.toList Set PackageName
targetsSet
              [Text] -> RIO EnvConfig () -> RIO EnvConfig ()
forall env a. HasEnvConfig env => [Text] -> RIO env a -> RIO env a
withNewLocalBuildTargets [Text]
targets (RIO EnvConfig () -> RIO EnvConfig ())
-> RIO EnvConfig () -> RIO EnvConfig ()
forall a b. (a -> b) -> a -> b
$ Maybe (Set (Path Abs File) -> IO ()) -> RIO EnvConfig ()
forall env.
HasEnvConfig env =>
Maybe (Set (Path Abs File) -> IO ()) -> RIO env ()
build Maybe (Set (Path Abs File) -> IO ())
forall a. Maybe a
Nothing

        let ghcArgs :: [String]
ghcArgs = [[String]] -> [String]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat
              [ [String
"-i", String
"-i" String -> ShowS
forall a. [a] -> [a] -> [a]
++ Path Abs Dir -> String
fromAbsDir (Path Abs t -> Path Abs Dir
forall b t. Path b t -> Path b Dir
parent Path Abs t
file)]
              , [String
"-hide-all-packages"]
              , Maybe String -> [String]
forall a. Maybe a -> [a]
maybeToList Maybe String
colorFlag
              , ShowS -> [String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (String
"-package" ++)
                  ([String] -> [String]) -> [String] -> [String]
forall a b. (a -> b) -> a -> b
$ Set String -> [String]
forall a. Set a -> [a]
Set.toList
                  (Set String -> [String]) -> Set String -> [String]
forall a b. (a -> b) -> a -> b
$ String -> Set String -> Set String
forall a. Ord a => a -> Set a -> Set a
Set.insert String
"base"
                  (Set String -> Set String) -> Set String -> Set String
forall a b. (a -> b) -> a -> b
$ (PackageName -> String) -> Set PackageName -> Set String
forall b a. Ord b => (a -> b) -> Set a -> Set b
Set.map PackageName -> String
packageNameString Set PackageName
targetsSet
              , case ScriptExecute
shouldCompile of
                  ScriptExecute
SEInterpret -> []
                  ScriptExecute
SECompile -> []
                  ScriptExecute
SEOptimize -> [String
"-O2"]
              , ScriptOpts -> [String]
soGhcOptions ScriptOpts
opts
              , if ScriptOpts -> Bool
soUseRoot ScriptOpts
opts
                  then
                    [ String
"-outputdir=" String -> ShowS
forall a. [a] -> [a] -> [a]
++ Path Abs Dir -> String
fromAbsDir (Path Abs File -> Path Abs Dir
forall b t. Path b t -> Path b Dir
parent Path Abs File
exe)
                    , String
"-o", Path Abs File -> String
fromAbsFile Path Abs File
exe
                    ]
                  else []
              ]
        case ScriptExecute
shouldCompile of
          ScriptExecute
SEInterpret -> do
            Path Abs File
interpret <- Getting (Path Abs File) EnvConfig (Path Abs File)
-> RIO EnvConfig (Path Abs File)
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view (Getting (Path Abs File) EnvConfig (Path Abs File)
 -> RIO EnvConfig (Path Abs File))
-> Getting (Path Abs File) EnvConfig (Path Abs File)
-> RIO EnvConfig (Path Abs File)
forall a b. (a -> b) -> a -> b
$ Getting (Path Abs File) EnvConfig CompilerPaths
forall env. HasCompiler env => SimpleGetter env CompilerPaths
SimpleGetter EnvConfig CompilerPaths
compilerPathsLGetting (Path Abs File) EnvConfig CompilerPaths
-> ((Path Abs File -> Const (Path Abs File) (Path Abs File))
    -> CompilerPaths -> Const (Path Abs File) CompilerPaths)
-> Getting (Path Abs File) EnvConfig (Path Abs File)
forall b c a. (b -> c) -> (a -> b) -> a -> c
.(CompilerPaths -> Path Abs File)
-> SimpleGetter CompilerPaths (Path Abs File)
forall s a. (s -> a) -> SimpleGetter s a
to CompilerPaths -> Path Abs File
cpInterpreter
            String -> [String] -> RIO EnvConfig ()
forall env b.
(HasProcessContext env, HasLogFunc env) =>
String -> [String] -> RIO env b
exec (Path Abs File -> String
forall b t. Path b t -> String
toFilePath Path Abs File
interpret)
                ([String]
ghcArgs [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ Path Abs t -> String
forall b t. Path b t -> String
toFilePath Path Abs t
file String -> [String] -> [String]
forall a. a -> [a] -> [a]
: ScriptOpts -> [String]
soArgs ScriptOpts
opts)
          ScriptExecute
_ -> do
            -- Use readProcessStdout_ so that (1) if GHC does send any output

            -- to stdout, we capture it and stop it from being sent to our

            -- stdout, which could break scripts, and (2) if there's an

            -- exception, the standard output we did capture will be reported

            -- to the user.

            IO () -> RIO EnvConfig ()
forall a. IO a -> RIO EnvConfig a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> RIO EnvConfig ()) -> IO () -> RIO EnvConfig ()
forall a b. (a -> b) -> a -> b
$ Bool -> String -> IO ()
forall (m :: * -> *). MonadIO m => Bool -> String -> m ()
Dir.createDirectoryIfMissing Bool
True (Path Abs Dir -> String
fromAbsDir (Path Abs File -> Path Abs Dir
forall b t. Path b t -> Path b Dir
parent Path Abs File
exe))
            String
compilerExeName <- Getting String EnvConfig String -> RIO EnvConfig String
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view (Getting String EnvConfig String -> RIO EnvConfig String)
-> Getting String EnvConfig String -> RIO EnvConfig String
forall a b. (a -> b) -> a -> b
$ Getting String EnvConfig CompilerPaths
forall env. HasCompiler env => SimpleGetter env CompilerPaths
SimpleGetter EnvConfig CompilerPaths
compilerPathsLGetting String EnvConfig CompilerPaths
-> ((String -> Const String String)
    -> CompilerPaths -> Const String CompilerPaths)
-> Getting String EnvConfig String
forall b c a. (b -> c) -> (a -> b) -> a -> c
.(CompilerPaths -> Path Abs File)
-> SimpleGetter CompilerPaths (Path Abs File)
forall s a. (s -> a) -> SimpleGetter s a
to CompilerPaths -> Path Abs File
cpCompilerGetting String CompilerPaths (Path Abs File)
-> ((String -> Const String String)
    -> Path Abs File -> Const String (Path Abs File))
-> (String -> Const String String)
-> CompilerPaths
-> Const String CompilerPaths
forall b c a. (b -> c) -> (a -> b) -> a -> c
.(Path Abs File -> String) -> SimpleGetter (Path Abs File) String
forall s a. (s -> a) -> SimpleGetter s a
to Path Abs File -> String
forall b t. Path b t -> String
toFilePath
            String -> RIO EnvConfig () -> RIO EnvConfig ()
forall env (m :: * -> *) a.
(HasProcessContext env, MonadReader env m, MonadIO m) =>
String -> m a -> m a
withWorkingDir (Path Abs Dir -> String
fromAbsDir (Path Abs t -> Path Abs Dir
forall b t. Path b t -> Path b Dir
parent Path Abs t
file)) (RIO EnvConfig () -> RIO EnvConfig ())
-> RIO EnvConfig () -> RIO EnvConfig ()
forall a b. (a -> b) -> a -> b
$ String
-> [String]
-> (ProcessConfig () () () -> RIO EnvConfig ())
-> RIO EnvConfig ()
forall env (m :: * -> *) a.
(HasProcessContext env, HasLogFunc env, MonadReader env m,
 MonadIO m, HasCallStack) =>
String -> [String] -> (ProcessConfig () () () -> m a) -> m a
proc
              String
compilerExeName
              ([String]
ghcArgs [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [Path Abs t -> String
forall b t. Path b t -> String
toFilePath Path Abs t
file])
              (RIO EnvConfig ByteString -> RIO EnvConfig ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (RIO EnvConfig ByteString -> RIO EnvConfig ())
-> (ProcessConfig () () () -> RIO EnvConfig ByteString)
-> ProcessConfig () () ()
-> RIO EnvConfig ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ProcessConfig () () () -> RIO EnvConfig ByteString
forall (m :: * -> *) stdin stdoutIgnored stderr.
MonadIO m =>
ProcessConfig stdin stdoutIgnored stderr -> m ByteString
readProcessStdout_)
            ShouldRun -> Path Abs File -> RIO EnvConfig ()
forall {env}.
(HasTerm env, HasProcessContext env) =>
ShouldRun -> Path Abs File -> RIO env ()
runCompiled ShouldRun
shouldRun Path Abs File
exe

  toPackageName :: ShowS
toPackageName = ShowS
forall a. [a] -> [a]
reverse ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> ShowS
forall a. Int -> [a] -> [a]
drop Int
1 ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char -> Bool) -> ShowS
forall a. (a -> Bool) -> [a] -> [a]
dropWhile (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
'-') ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ShowS
forall a. [a] -> [a]
reverse

  -- Like words, but splits on both commas and spaces

  wordsComma :: String -> [String]
wordsComma = (Char -> Bool) -> String -> [String]
forall a. (a -> Bool) -> [a] -> [[a]]
splitWhen (\Char
c -> Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
' ' Bool -> Bool -> Bool
|| Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
',')

getPackagesFromImports ::
     FilePath -- ^ script filename

  -> RIO EnvConfig (Set PackageName)
getPackagesFromImports :: String -> RIO EnvConfig (Set PackageName)
getPackagesFromImports String
scriptFP = do
  (Set PackageName
pns, Set ModuleName
mns) <- IO (Set PackageName, Set ModuleName)
-> RIO EnvConfig (Set PackageName, Set ModuleName)
forall a. IO a -> RIO EnvConfig a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Set PackageName, Set ModuleName)
 -> RIO EnvConfig (Set PackageName, Set ModuleName))
-> IO (Set PackageName, Set ModuleName)
-> RIO EnvConfig (Set PackageName, Set ModuleName)
forall a b. (a -> b) -> a -> b
$ ByteString -> (Set PackageName, Set ModuleName)
parseImports (ByteString -> (Set PackageName, Set ModuleName))
-> IO ByteString -> IO (Set PackageName, Set ModuleName)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> IO ByteString
S8.readFile String
scriptFP
  if Set ModuleName -> Bool
forall a. Set a -> Bool
Set.null Set ModuleName
mns
    then Set PackageName -> RIO EnvConfig (Set PackageName)
forall a. a -> RIO EnvConfig a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Set PackageName
pns
    else Set PackageName -> Set PackageName -> Set PackageName
forall a. Ord a => Set a -> Set a -> Set a
Set.union Set PackageName
pns (Set PackageName -> Set PackageName)
-> RIO EnvConfig (Set PackageName)
-> RIO EnvConfig (Set PackageName)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Set ModuleName -> RIO EnvConfig (Set PackageName)
getPackagesFromModuleNames Set ModuleName
mns

getPackagesFromModuleNames ::
     Set ModuleName
  -> RIO EnvConfig (Set PackageName)
getPackagesFromModuleNames :: Set ModuleName -> RIO EnvConfig (Set PackageName)
getPackagesFromModuleNames Set ModuleName
mns = do
  SnapshotCacheHash
hash <- RIO EnvConfig SnapshotCacheHash
hashSnapshot
  SnapshotCacheHash
-> RIO EnvConfig (Map PackageName (Set ModuleName))
-> ((ModuleName -> RIO EnvConfig [PackageName])
    -> RIO EnvConfig (Set PackageName))
-> RIO EnvConfig (Set PackageName)
forall env a.
(HasPantryConfig env, HasLogFunc env) =>
SnapshotCacheHash
-> RIO env (Map PackageName (Set ModuleName))
-> ((ModuleName -> RIO env [PackageName]) -> RIO env a)
-> RIO env a
withSnapshotCache SnapshotCacheHash
hash RIO EnvConfig (Map PackageName (Set ModuleName))
mapSnapshotPackageModules (((ModuleName -> RIO EnvConfig [PackageName])
  -> RIO EnvConfig (Set PackageName))
 -> RIO EnvConfig (Set PackageName))
-> ((ModuleName -> RIO EnvConfig [PackageName])
    -> RIO EnvConfig (Set PackageName))
-> RIO EnvConfig (Set PackageName)
forall a b. (a -> b) -> a -> b
$ \ModuleName -> RIO EnvConfig [PackageName]
getModulePackages -> do
    [Set PackageName]
pns <- [ModuleName]
-> (ModuleName -> RIO EnvConfig (Set PackageName))
-> RIO EnvConfig [Set PackageName]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
t a -> (a -> m b) -> m (t b)
forM (Set ModuleName -> [ModuleName]
forall a. Set a -> [a]
Set.toList Set ModuleName
mns) ((ModuleName -> RIO EnvConfig (Set PackageName))
 -> RIO EnvConfig [Set PackageName])
-> (ModuleName -> RIO EnvConfig (Set PackageName))
-> RIO EnvConfig [Set PackageName]
forall a b. (a -> b) -> a -> b
$ \ModuleName
mn -> do
      [PackageName]
pkgs <- ModuleName -> RIO EnvConfig [PackageName]
getModulePackages ModuleName
mn
      case [PackageName]
pkgs of
        [] -> Set PackageName -> RIO EnvConfig (Set PackageName)
forall a. a -> RIO EnvConfig a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Set PackageName
forall a. Set a
Set.empty
        [PackageName
pn] -> Set PackageName -> RIO EnvConfig (Set PackageName)
forall a. a -> RIO EnvConfig a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Set PackageName -> RIO EnvConfig (Set PackageName))
-> Set PackageName -> RIO EnvConfig (Set PackageName)
forall a b. (a -> b) -> a -> b
$ PackageName -> Set PackageName
forall a. a -> Set a
Set.singleton PackageName
pn
        [PackageName]
_ -> ScriptException -> RIO EnvConfig (Set PackageName)
forall e a. Exception e => e -> RIO EnvConfig a
forall (m :: * -> *) e a. (MonadThrow m, Exception e) => e -> m a
throwM (ScriptException -> RIO EnvConfig (Set PackageName))
-> ScriptException -> RIO EnvConfig (Set PackageName)
forall a b. (a -> b) -> a -> b
$ ModuleName -> [PackageName] -> ScriptException
AmbiguousModuleName ModuleName
mn [PackageName]
pkgs
    Set PackageName -> RIO EnvConfig (Set PackageName)
forall a. a -> RIO EnvConfig a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Set PackageName -> RIO EnvConfig (Set PackageName))
-> Set PackageName -> RIO EnvConfig (Set PackageName)
forall a b. (a -> b) -> a -> b
$ [Set PackageName] -> Set PackageName
forall (f :: * -> *) a. (Foldable f, Ord a) => f (Set a) -> Set a
Set.unions [Set PackageName]
pns Set PackageName -> Set PackageName -> Set PackageName
forall a. Ord a => Set a -> Set a -> Set a
`Set.difference` Set PackageName
blacklist

hashSnapshot :: RIO EnvConfig SnapshotCacheHash
hashSnapshot :: RIO EnvConfig SnapshotCacheHash
hashSnapshot = do
  SourceMap
sourceMap <- Getting SourceMap EnvConfig SourceMap -> RIO EnvConfig SourceMap
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view (Getting SourceMap EnvConfig SourceMap -> RIO EnvConfig SourceMap)
-> Getting SourceMap EnvConfig SourceMap -> RIO EnvConfig SourceMap
forall a b. (a -> b) -> a -> b
$ (EnvConfig -> Const SourceMap EnvConfig)
-> EnvConfig -> Const SourceMap EnvConfig
forall env. HasEnvConfig env => Lens' env EnvConfig
Lens' EnvConfig EnvConfig
envConfigL ((EnvConfig -> Const SourceMap EnvConfig)
 -> EnvConfig -> Const SourceMap EnvConfig)
-> Getting SourceMap EnvConfig SourceMap
-> Getting SourceMap EnvConfig SourceMap
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (EnvConfig -> SourceMap) -> SimpleGetter EnvConfig SourceMap
forall s a. (s -> a) -> SimpleGetter s a
to EnvConfig -> SourceMap
envConfigSourceMap
  Builder
compilerInfo <- RIO EnvConfig Builder
forall env. (HasConfig env, HasCompiler env) => RIO env Builder
getCompilerInfo
  let eitherPliHash :: (a, DepPackage) -> Either a Builder
eitherPliHash (a
pn, DepPackage
dep)
        | PLImmutable PackageLocationImmutable
pli <- DepPackage -> PackageLocation
dpLocation DepPackage
dep = Builder -> Either a Builder
forall a b. b -> Either a b
Right (Builder -> Either a Builder) -> Builder -> Either a Builder
forall a b. (a -> b) -> a -> b
$ PackageLocationImmutable -> Builder
immutableLocSha PackageLocationImmutable
pli
        | Bool
otherwise = a -> Either a Builder
forall a b. a -> Either a b
Left a
pn
      deps :: [(PackageName, DepPackage)]
deps = Map PackageName DepPackage -> [(PackageName, DepPackage)]
forall k a. Map k a -> [(k, a)]
Map.toList (SourceMap -> Map PackageName DepPackage
smDeps SourceMap
sourceMap)
  case [Either PackageName Builder] -> ([PackageName], [Builder])
forall a b. [Either a b] -> ([a], [b])
partitionEithers (((PackageName, DepPackage) -> Either PackageName Builder)
-> [(PackageName, DepPackage)] -> [Either PackageName Builder]
forall a b. (a -> b) -> [a] -> [b]
map (PackageName, DepPackage) -> Either PackageName Builder
forall {a}. (a, DepPackage) -> Either a Builder
eitherPliHash [(PackageName, DepPackage)]
deps) of
    ([], [Builder]
pliHashes) -> do
      let hashedContent :: Builder
hashedContent = [Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat ([Builder] -> Builder) -> [Builder] -> Builder
forall a b. (a -> b) -> a -> b
$ Builder
compilerInfo Builder -> [Builder] -> [Builder]
forall a. a -> [a] -> [a]
: [Builder]
pliHashes
      SnapshotCacheHash -> RIO EnvConfig SnapshotCacheHash
forall a. a -> RIO EnvConfig a
forall (f :: * -> *) a. Applicative f => a -> f a
pure
        (SnapshotCacheHash -> RIO EnvConfig SnapshotCacheHash)
-> SnapshotCacheHash -> RIO EnvConfig SnapshotCacheHash
forall a b. (a -> b) -> a -> b
$ SHA256 -> SnapshotCacheHash
SnapshotCacheHash (ByteString -> SHA256
SHA256.hashLazyBytes
        (ByteString -> SHA256) -> ByteString -> SHA256
forall a b. (a -> b) -> a -> b
$ Builder -> ByteString
toLazyByteString Builder
hashedContent)
    ([PackageName]
mutables, [Builder]
_) -> ScriptException -> RIO EnvConfig SnapshotCacheHash
forall e a. Exception e => e -> RIO EnvConfig a
forall (m :: * -> *) e a. (MonadThrow m, Exception e) => e -> m a
throwM (ScriptException -> RIO EnvConfig SnapshotCacheHash)
-> ScriptException -> RIO EnvConfig SnapshotCacheHash
forall a b. (a -> b) -> a -> b
$ [PackageName] -> ScriptException
MutableDependenciesForScript [PackageName]
mutables

mapSnapshotPackageModules :: RIO EnvConfig (Map PackageName (Set ModuleName))
mapSnapshotPackageModules :: RIO EnvConfig (Map PackageName (Set ModuleName))
mapSnapshotPackageModules = do
  SourceMap
sourceMap <- Getting SourceMap EnvConfig SourceMap -> RIO EnvConfig SourceMap
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view (Getting SourceMap EnvConfig SourceMap -> RIO EnvConfig SourceMap)
-> Getting SourceMap EnvConfig SourceMap -> RIO EnvConfig SourceMap
forall a b. (a -> b) -> a -> b
$ (EnvConfig -> Const SourceMap EnvConfig)
-> EnvConfig -> Const SourceMap EnvConfig
forall env. HasEnvConfig env => Lens' env EnvConfig
Lens' EnvConfig EnvConfig
envConfigL ((EnvConfig -> Const SourceMap EnvConfig)
 -> EnvConfig -> Const SourceMap EnvConfig)
-> Getting SourceMap EnvConfig SourceMap
-> Getting SourceMap EnvConfig SourceMap
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (EnvConfig -> SourceMap) -> SimpleGetter EnvConfig SourceMap
forall s a. (s -> a) -> SimpleGetter s a
to EnvConfig -> SourceMap
envConfigSourceMap
  InstallMap
installMap <- SourceMap -> RIO EnvConfig InstallMap
forall (m :: * -> *). MonadIO m => SourceMap -> m InstallMap
toInstallMap SourceMap
sourceMap
  (InstalledMap
_installedMap, [DumpPackage]
globalDumpPkgs, [DumpPackage]
snapshotDumpPkgs, [DumpPackage]
_localDumpPkgs) <-
    InstallMap
-> RIO
     EnvConfig
     (InstalledMap, [DumpPackage], [DumpPackage], [DumpPackage])
forall env.
HasEnvConfig env =>
InstallMap
-> RIO
     env (InstalledMap, [DumpPackage], [DumpPackage], [DumpPackage])
getInstalled InstallMap
installMap
  let globals :: Map PackageName (Set ModuleName)
globals = Map PackageName GlobalPackage
-> [DumpPackage] -> Map PackageName (Set ModuleName)
forall a.
Map PackageName a
-> [DumpPackage] -> Map PackageName (Set ModuleName)
dumpedPackageModules (SourceMap -> Map PackageName GlobalPackage
smGlobal SourceMap
sourceMap) [DumpPackage]
globalDumpPkgs
      notHidden :: Map k DepPackage -> Map k DepPackage
notHidden = (DepPackage -> Bool) -> Map k DepPackage -> Map k DepPackage
forall a k. (a -> Bool) -> Map k a -> Map k a
Map.filter (Bool -> Bool
not (Bool -> Bool) -> (DepPackage -> Bool) -> DepPackage -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. DepPackage -> Bool
dpHidden)
      notHiddenDeps :: Map PackageName DepPackage
notHiddenDeps = Map PackageName DepPackage -> Map PackageName DepPackage
forall {k}. Map k DepPackage -> Map k DepPackage
notHidden (Map PackageName DepPackage -> Map PackageName DepPackage)
-> Map PackageName DepPackage -> Map PackageName DepPackage
forall a b. (a -> b) -> a -> b
$ SourceMap -> Map PackageName DepPackage
smDeps SourceMap
sourceMap
      installedDeps :: Map PackageName (Set ModuleName)
installedDeps = Map PackageName DepPackage
-> [DumpPackage] -> Map PackageName (Set ModuleName)
forall a.
Map PackageName a
-> [DumpPackage] -> Map PackageName (Set ModuleName)
dumpedPackageModules Map PackageName DepPackage
notHiddenDeps [DumpPackage]
snapshotDumpPkgs
      dumpPkgs :: Set PackageName
dumpPkgs = [PackageName] -> Set PackageName
forall a. Ord a => [a] -> Set a
Set.fromList ([PackageName] -> Set PackageName)
-> [PackageName] -> Set PackageName
forall a b. (a -> b) -> a -> b
$ (DumpPackage -> PackageName) -> [DumpPackage] -> [PackageName]
forall a b. (a -> b) -> [a] -> [b]
map (PackageIdentifier -> PackageName
pkgName (PackageIdentifier -> PackageName)
-> (DumpPackage -> PackageIdentifier) -> DumpPackage -> PackageName
forall b c a. (b -> c) -> (a -> b) -> a -> c
. DumpPackage -> PackageIdentifier
dpPackageIdent) [DumpPackage]
snapshotDumpPkgs
      notInstalledDeps :: Map PackageName DepPackage
notInstalledDeps = Map PackageName DepPackage
-> Set PackageName -> Map PackageName DepPackage
forall k a. Ord k => Map k a -> Set k -> Map k a
Map.withoutKeys Map PackageName DepPackage
notHiddenDeps Set PackageName
dumpPkgs
  Map PackageName (Set ModuleName)
otherDeps <- Map PackageName DepPackage
-> (DepPackage -> RIO EnvConfig (Set ModuleName))
-> RIO EnvConfig (Map PackageName (Set ModuleName))
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
t a -> (a -> f b) -> f (t b)
for Map PackageName DepPackage
notInstalledDeps ((DepPackage -> RIO EnvConfig (Set ModuleName))
 -> RIO EnvConfig (Map PackageName (Set ModuleName)))
-> (DepPackage -> RIO EnvConfig (Set ModuleName))
-> RIO EnvConfig (Map PackageName (Set ModuleName))
forall a b. (a -> b) -> a -> b
$ \DepPackage
dep -> do
    GenericPackageDescription
gpd <- IO GenericPackageDescription
-> RIO EnvConfig GenericPackageDescription
forall a. IO a -> RIO EnvConfig a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO GenericPackageDescription
 -> RIO EnvConfig GenericPackageDescription)
-> IO GenericPackageDescription
-> RIO EnvConfig GenericPackageDescription
forall a b. (a -> b) -> a -> b
$ CommonPackage -> IO GenericPackageDescription
cpGPD (DepPackage -> CommonPackage
dpCommon DepPackage
dep)
    [ModuleName] -> Set ModuleName
forall a. Ord a => [a] -> Set a
Set.fromList ([ModuleName] -> Set ModuleName)
-> RIO EnvConfig [ModuleName] -> RIO EnvConfig (Set ModuleName)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GenericPackageDescription -> RIO EnvConfig [ModuleName]
allExposedModules GenericPackageDescription
gpd
  -- source map construction process should guarantee unique package names in

  -- these maps

  Map PackageName (Set ModuleName)
-> RIO EnvConfig (Map PackageName (Set ModuleName))
forall a. a -> RIO EnvConfig a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Map PackageName (Set ModuleName)
 -> RIO EnvConfig (Map PackageName (Set ModuleName)))
-> Map PackageName (Set ModuleName)
-> RIO EnvConfig (Map PackageName (Set ModuleName))
forall a b. (a -> b) -> a -> b
$ Map PackageName (Set ModuleName)
globals Map PackageName (Set ModuleName)
-> Map PackageName (Set ModuleName)
-> Map PackageName (Set ModuleName)
forall a. Semigroup a => a -> a -> a
<> Map PackageName (Set ModuleName)
installedDeps Map PackageName (Set ModuleName)
-> Map PackageName (Set ModuleName)
-> Map PackageName (Set ModuleName)
forall a. Semigroup a => a -> a -> a
<> Map PackageName (Set ModuleName)
otherDeps

dumpedPackageModules ::
     Map PackageName a
  -> [DumpPackage]
  -> Map PackageName (Set ModuleName)
dumpedPackageModules :: forall a.
Map PackageName a
-> [DumpPackage] -> Map PackageName (Set ModuleName)
dumpedPackageModules Map PackageName a
pkgs [DumpPackage]
dumpPkgs =
  let pnames :: Set PackageName
pnames = Map PackageName a -> Set PackageName
forall k a. Map k a -> Set k
Map.keysSet Map PackageName a
pkgs Set PackageName -> Set PackageName -> Set PackageName
forall a. Ord a => Set a -> Set a -> Set a
`Set.difference` Set PackageName
blacklist
  in  [(PackageName, Set ModuleName)] -> Map PackageName (Set ModuleName)
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList
        [ (PackageName
pn, Set ModuleName
dpExposedModules)
        | DumpPackage {Bool
[String]
[Text]
[GhcPkgId]
Maybe String
Maybe License
Maybe PackageIdentifier
Set ModuleName
PackageIdentifier
GhcPkgId
dpPackageIdent :: DumpPackage -> PackageIdentifier
dpExposedModules :: Set ModuleName
dpGhcPkgId :: GhcPkgId
dpPackageIdent :: PackageIdentifier
dpParentLibIdent :: Maybe PackageIdentifier
dpLicense :: Maybe License
dpLibDirs :: [String]
dpLibraries :: [Text]
dpHasExposedModules :: Bool
dpDepends :: [GhcPkgId]
dpHaddockInterfaces :: [String]
dpHaddockHtml :: Maybe String
dpIsExposed :: Bool
dpGhcPkgId :: DumpPackage -> GhcPkgId
dpParentLibIdent :: DumpPackage -> Maybe PackageIdentifier
dpLicense :: DumpPackage -> Maybe License
dpLibDirs :: DumpPackage -> [String]
dpLibraries :: DumpPackage -> [Text]
dpHasExposedModules :: DumpPackage -> Bool
dpExposedModules :: DumpPackage -> Set ModuleName
dpDepends :: DumpPackage -> [GhcPkgId]
dpHaddockInterfaces :: DumpPackage -> [String]
dpHaddockHtml :: DumpPackage -> Maybe String
dpIsExposed :: DumpPackage -> Bool
..} <- [DumpPackage]
dumpPkgs
        , let PackageIdentifier PackageName
pn Version
_ = PackageIdentifier
dpPackageIdent
        , PackageName
pn PackageName -> Set PackageName -> Bool
forall a. Ord a => a -> Set a -> Bool
`Set.member` Set PackageName
pnames
        ]

allExposedModules :: PD.GenericPackageDescription -> RIO EnvConfig [ModuleName]
allExposedModules :: GenericPackageDescription -> RIO EnvConfig [ModuleName]
allExposedModules GenericPackageDescription
gpd = do
  Platform Arch
curArch OS
curOs <- Getting Platform EnvConfig Platform -> RIO EnvConfig Platform
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting Platform EnvConfig Platform
forall env. HasPlatform env => Lens' env Platform
Lens' EnvConfig Platform
platformL
  ActualCompiler
curCompiler <- Getting ActualCompiler EnvConfig ActualCompiler
-> RIO EnvConfig ActualCompiler
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting ActualCompiler EnvConfig ActualCompiler
forall env. HasSourceMap env => SimpleGetter env ActualCompiler
SimpleGetter EnvConfig ActualCompiler
actualCompilerVersionL
  let checkCond :: ConfVar -> Either ConfVar Bool
checkCond (PD.OS OS
os) = Bool -> Either ConfVar Bool
forall a. a -> Either ConfVar a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Bool -> Either ConfVar Bool) -> Bool -> Either ConfVar Bool
forall a b. (a -> b) -> a -> b
$ OS
os OS -> OS -> Bool
forall a. Eq a => a -> a -> Bool
== OS
curOs
      checkCond (PD.Arch Arch
arch) = Bool -> Either ConfVar Bool
forall a. a -> Either ConfVar a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Bool -> Either ConfVar Bool) -> Bool -> Either ConfVar Bool
forall a b. (a -> b) -> a -> b
$ Arch
arch Arch -> Arch -> Bool
forall a. Eq a => a -> a -> Bool
== Arch
curArch
      checkCond (PD.Impl CompilerFlavor
compiler VersionRange
range) = case ActualCompiler
curCompiler of
        ACGhc Version
version ->
          Bool -> Either ConfVar Bool
forall a. a -> Either ConfVar a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Bool -> Either ConfVar Bool) -> Bool -> Either ConfVar Bool
forall a b. (a -> b) -> a -> b
$ CompilerFlavor
compiler CompilerFlavor -> CompilerFlavor -> Bool
forall a. Eq a => a -> a -> Bool
== CompilerFlavor
GHC Bool -> Bool -> Bool
&& Version
version Version -> VersionRange -> Bool
`withinRange` VersionRange
range
        ACGhcGit {} ->
          Bool -> Either ConfVar Bool
forall a. a -> Either ConfVar a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Bool -> Either ConfVar Bool) -> Bool -> Either ConfVar Bool
forall a b. (a -> b) -> a -> b
$ CompilerFlavor
compiler CompilerFlavor -> CompilerFlavor -> Bool
forall a. Eq a => a -> a -> Bool
== CompilerFlavor
GHC
      -- currently we don't do flag checking here

      checkCond ConfVar
other = ConfVar -> Either ConfVar Bool
forall a b. a -> Either a b
Left ConfVar
other
      mlibrary :: Maybe Library
mlibrary = ([Dependency], Library) -> Library
forall a b. (a, b) -> b
snd (([Dependency], Library) -> Library)
-> (CondTree ConfVar [Dependency] Library
    -> ([Dependency], Library))
-> CondTree ConfVar [Dependency] Library
-> Library
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ConfVar -> Either ConfVar Bool)
-> CondTree ConfVar [Dependency] Library -> ([Dependency], Library)
forall a d v.
(Semigroup a, Semigroup d) =>
(v -> Either v Bool) -> CondTree v d a -> (d, a)
C.simplifyCondTree ConfVar -> Either ConfVar Bool
checkCond (CondTree ConfVar [Dependency] Library -> Library)
-> Maybe (CondTree ConfVar [Dependency] Library) -> Maybe Library
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> GenericPackageDescription
-> Maybe (CondTree ConfVar [Dependency] Library)
PD.condLibrary GenericPackageDescription
gpd
  [ModuleName] -> RIO EnvConfig [ModuleName]
forall a. a -> RIO EnvConfig a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([ModuleName] -> RIO EnvConfig [ModuleName])
-> [ModuleName] -> RIO EnvConfig [ModuleName]
forall a b. (a -> b) -> a -> b
$ case Maybe Library
mlibrary  of
    Just Library
lib -> Library -> [ModuleName]
PD.exposedModules Library
lib [ModuleName] -> [ModuleName] -> [ModuleName]
forall a. [a] -> [a] -> [a]
++
                (ModuleReexport -> ModuleName) -> [ModuleReexport] -> [ModuleName]
forall a b. (a -> b) -> [a] -> [b]
map ModuleReexport -> ModuleName
moduleReexportName (Library -> [ModuleReexport]
PD.reexportedModules Library
lib)
    Maybe Library
Nothing  -> [ModuleName]
forall a. Monoid a => a
mempty

-- | The Stackage project introduced the concept of hidden packages, to deal

-- with conflicting module names. However, this is a relatively recent addition

-- (at time of writing). See:

-- http://www.snoyman.com/blog/2017/01/conflicting-module-names. To kick this

-- thing off a bit better, we're included a blacklist of packages that should

-- never be auto-parsed in.

blacklist :: Set PackageName
blacklist :: Set PackageName
blacklist = [PackageName] -> Set PackageName
forall a. Ord a => [a] -> Set a
Set.fromList
  [ String -> PackageName
mkPackageName String
"Glob"
  , String -> PackageName
mkPackageName String
"HTF"
  , String -> PackageName
mkPackageName String
"async-dejafu"
  , String -> PackageName
mkPackageName String
"binary-ieee754"
  , String -> PackageName
mkPackageName String
"cipher-aes"
  , String -> PackageName
mkPackageName String
"cipher-blowfish"
  , String -> PackageName
mkPackageName String
"cipher-camellia"
  , String -> PackageName
mkPackageName String
"cipher-des"
  , String -> PackageName
mkPackageName String
"cipher-rc4"
  , String -> PackageName
mkPackageName String
"control-monad-free"
  , String -> PackageName
mkPackageName String
"courier"
  , String -> PackageName
mkPackageName String
"crypto-api"
  , String -> PackageName
mkPackageName String
"crypto-cipher-types"
  , String -> PackageName
mkPackageName String
"crypto-numbers"
  , String -> PackageName
mkPackageName String
"crypto-pubkey"
  , String -> PackageName
mkPackageName String
"crypto-random"
  , String -> PackageName
mkPackageName String
"cryptohash"
  , String -> PackageName
mkPackageName String
"cryptohash-conduit"
  , String -> PackageName
mkPackageName String
"cryptohash-md5"
  , String -> PackageName
mkPackageName String
"cryptohash-sha1"
  , String -> PackageName
mkPackageName String
"cryptohash-sha256"
  , String -> PackageName
mkPackageName String
"fay-base"
  , String -> PackageName
mkPackageName String
"gl"
  , String -> PackageName
mkPackageName String
"gtk3"
  , String -> PackageName
mkPackageName String
"hashmap"
  , String -> PackageName
mkPackageName String
"hledger-web"
  , String -> PackageName
mkPackageName String
"hxt-unicode"
  , String -> PackageName
mkPackageName String
"kawhi"
  , String -> PackageName
mkPackageName String
"language-c"
  , String -> PackageName
mkPackageName String
"log"
  , String -> PackageName
mkPackageName String
"monad-extras"
  , String -> PackageName
mkPackageName String
"monads-tf"
  , String -> PackageName
mkPackageName String
"nanospec"
  , String -> PackageName
mkPackageName String
"newtype-generics"
  , String -> PackageName
mkPackageName String
"objective"
  , String -> PackageName
mkPackageName String
"plot-gtk3"
  , String -> PackageName
mkPackageName String
"prompt"
  , String -> PackageName
mkPackageName String
"regex-compat-tdfa"
  , String -> PackageName
mkPackageName String
"regex-pcre-builtin"
  , String -> PackageName
mkPackageName String
"rerebase"
  , String -> PackageName
mkPackageName String
"svg-tree"
  , String -> PackageName
mkPackageName String
"zip"
  ]

parseImports :: ByteString -> (Set PackageName, Set ModuleName)
parseImports :: ByteString -> (Set PackageName, Set ModuleName)
parseImports =
  [(Set PackageName, Set ModuleName)]
-> (Set PackageName, Set ModuleName)
forall a. Monoid a => [a] -> a
forall (t :: * -> *) m. (Foldable t, Monoid m) => t m -> m
fold ([(Set PackageName, Set ModuleName)]
 -> (Set PackageName, Set ModuleName))
-> (ByteString -> [(Set PackageName, Set ModuleName)])
-> ByteString
-> (Set PackageName, Set ModuleName)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ByteString -> Maybe (Set PackageName, Set ModuleName))
-> [ByteString] -> [(Set PackageName, Set ModuleName)]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe (ByteString -> Maybe (Set PackageName, Set ModuleName)
forall {a}.
IsString a =>
ByteString -> Maybe (Set PackageName, Set a)
parseLine (ByteString -> Maybe (Set PackageName, Set ModuleName))
-> (ByteString -> ByteString)
-> ByteString
-> Maybe (Set PackageName, Set ModuleName)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString
stripCR') ([ByteString] -> [(Set PackageName, Set ModuleName)])
-> (ByteString -> [ByteString])
-> ByteString
-> [(Set PackageName, Set ModuleName)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> [ByteString]
S8.lines
 where
  -- Remove any carriage pure character present at the end, to support

  -- Windows-style line endings (CRLF)

  stripCR' :: ByteString -> ByteString
stripCR' ByteString
bs
    | ByteString -> Bool
S8.null ByteString
bs = ByteString
bs
    | ByteString -> Char
S8.last ByteString
bs Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'\r' = HasCallStack => ByteString -> ByteString
ByteString -> ByteString
S8.init ByteString
bs
    | Bool
otherwise = ByteString
bs

  stripPrefix :: ByteString -> ByteString -> Maybe ByteString
stripPrefix ByteString
x ByteString
y
    | ByteString
x ByteString -> ByteString -> Bool
`S8.isPrefixOf` ByteString
y = ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just (ByteString -> Maybe ByteString) -> ByteString -> Maybe ByteString
forall a b. (a -> b) -> a -> b
$ Int -> ByteString -> ByteString
S8.drop (ByteString -> Int
S8.length ByteString
x) ByteString
y
    | Bool
otherwise = Maybe ByteString
forall a. Maybe a
Nothing

  parseLine :: ByteString -> Maybe (Set PackageName, Set a)
parseLine ByteString
bs0 = do
    ByteString
bs1 <- ByteString -> ByteString -> Maybe ByteString
stripPrefix ByteString
"import " ByteString
bs0
    let bs2 :: ByteString
bs2 = (Char -> Bool) -> ByteString -> ByteString
S8.dropWhile (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
' ') ByteString
bs1
        bs3 :: ByteString
bs3 = ByteString -> Maybe ByteString -> ByteString
forall a. a -> Maybe a -> a
fromMaybe ByteString
bs2 (Maybe ByteString -> ByteString) -> Maybe ByteString -> ByteString
forall a b. (a -> b) -> a -> b
$ ByteString -> ByteString -> Maybe ByteString
stripPrefix ByteString
"qualified " ByteString
bs2
    case ByteString -> ByteString -> Maybe ByteString
stripPrefix ByteString
"\"" ByteString
bs3 of
      Just ByteString
bs4 -> do
        PackageName
pn <- String -> Maybe PackageName
forall (m :: * -> *). MonadThrow m => String -> m PackageName
parsePackageNameThrowing (String -> Maybe PackageName) -> String -> Maybe PackageName
forall a b. (a -> b) -> a -> b
$ ByteString -> String
S8.unpack (ByteString -> String) -> ByteString -> String
forall a b. (a -> b) -> a -> b
$ (Char -> Bool) -> ByteString -> ByteString
S8.takeWhile (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
'"') ByteString
bs4
        (Set PackageName, Set a) -> Maybe (Set PackageName, Set a)
forall a. a -> Maybe a
Just (PackageName -> Set PackageName
forall a. a -> Set a
Set.singleton PackageName
pn, Set a
forall a. Set a
Set.empty)
      Maybe ByteString
Nothing -> (Set PackageName, Set a) -> Maybe (Set PackageName, Set a)
forall a. a -> Maybe a
Just
        ( Set PackageName
forall a. Set a
Set.empty
        , a -> Set a
forall a. a -> Set a
Set.singleton
            (a -> Set a) -> a -> Set a
forall a b. (a -> b) -> a -> b
$ String -> a
forall a. IsString a => String -> a
fromString
            (String -> a) -> String -> a
forall a b. (a -> b) -> a -> b
$ Text -> String
T.unpack
            (Text -> String) -> Text -> String
forall a b. (a -> b) -> a -> b
$ OnDecodeError -> ByteString -> Text
decodeUtf8With OnDecodeError
lenientDecode
            (ByteString -> Text) -> ByteString -> Text
forall a b. (a -> b) -> a -> b
$ (Char -> Bool) -> ByteString -> ByteString
S8.takeWhile (\Char
c -> Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
' ' Bool -> Bool -> Bool
&& Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
'(') ByteString
bs3
        )