stack-0.1.0.0: src/main/Main.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ViewPatterns #-}
-- | Main stack tool entry point.
module Main where
import Control.Exception
import Control.Monad
import Control.Monad.IO.Class
import Control.Monad.Logger
import Control.Monad.Reader (ask,asks)
import Data.Char (toLower)
import Data.List
import qualified Data.List as List
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Maybe
import Data.Monoid
import qualified Data.Set as Set
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Text.IO as T
import Data.Traversable
import Network.HTTP.Client
import Network.HTTP.Client.Conduit (getHttpManager)
import Options.Applicative.Args
import Options.Applicative.Builder.Extra
import Options.Applicative.Simple
import Options.Applicative.Types (readerAsk)
import Path
import qualified Paths_stack as Meta
import Plugins
import Prelude hiding (pi)
import Stack.Build
import Stack.Build.Types
import Stack.Config
import Stack.Constants
import qualified Stack.Docker as Docker
import Stack.Exec
import Stack.Fetch
import Stack.Init
import Stack.New
import qualified Stack.PackageIndex
import Stack.Repl
import Stack.Setup
import Stack.Solver (solveExtraDeps)
import Stack.Types
import Stack.Types.Internal
import Stack.Types.StackT
import qualified Stack.Upload as Upload
import System.Environment (getArgs, getProgName)
import System.Exit
import System.FilePath (searchPathSeparator)
import System.IO (stderr)
import System.Process.Read
-- | Commandline dispatcher.
main :: IO ()
main =
do when False $ do -- https://github.com/commercialhaskell/stack/issues/322
plugins <- findPlugins (T.pack stackProgName)
tryRunPlugin plugins
progName <- getProgName
args <- getArgs
execExtraHelp args
dockerHelpOptName
(Docker.dockerOptsParser True)
("Only showing --" ++ Docker.dockerCmdName ++ "* options.")
let versionString' = $(simpleVersion Meta.version)
(level,run) <-
simpleOptions
versionString'
"stack - The Haskell Tool Stack"
""
(extraHelpOption progName (Docker.dockerCmdName ++ "*") dockerHelpOptName <*> globalOpts)
(do addCommand "build"
"Build the project(s) in this directory/configuration"
(buildCmd DoNothing)
(buildOpts False)
addCommand "install"
"Build executables and install to a user path"
installCmd
(buildOpts False)
addCommand "test"
"Build and test the project(s) in this directory/configuration"
(buildCmd DoTests)
(buildOpts False)
addCommand "bench"
"Build and benchmark the project(s) in this directory/configuration"
(buildCmd DoBenchmarks)
(buildOpts False)
addCommand "haddock"
"Generate haddocks for the project(s) in this directory/configuration"
(buildCmd DoNothing)
(buildOpts True)
addCommand "new"
"Create a brand new project"
newCmd
initOptsParser
addCommand "init"
"Initialize a stack project based on one or more cabal packages"
initCmd
initOptsParser
addCommand "solver"
"Use a dependency solver to try and determine missing extra-deps"
solverCmd
solverOptsParser
addCommand "setup"
"Get the appropriate ghc for your project"
setupCmd
setupParser
addCommand "path"
"Print out handy path information"
pathCmd
(fmap
catMaybes
(sequenceA
(map
(\(desc,name,_) ->
flag Nothing
(Just name)
(long (T.unpack name) <>
help desc))
paths)))
addCommand "unpack"
"Unpack one or more packages locally"
unpackCmd
(some $ strArgument $ metavar "PACKAGE")
addCommand "update"
"Update the package index"
updateCmd
(pure ())
addCommand "upload"
"Upload a package to Hackage"
uploadCmd
(many $ strArgument $ metavar "TARBALL/DIR")
addCommand "exec"
"Execute a command"
execCmd
((,)
<$> strArgument (metavar "CMD")
<*> many (strArgument (metavar "-- ARGS (e.g. stack exec -- ghc --version)")))
addCommand "ghc"
"Run ghc"
execCmd
((,)
<$> pure "ghc"
<*> many (strArgument (metavar "-- ARGS (e.g. stack ghc -- X.hs -o x)")))
addCommand "ghci"
"Run ghci in the context of project(s)"
replCmd
((,,) <$>
fmap (map T.pack)
(many (strArgument
(metavar "TARGET" <>
help "If none specified, use all packages defined in current directory"))) <*>
many (strOption (long "ghc-options" <>
metavar "OPTION" <>
help "Additional options passed to GHCi")) <*>
fmap (fromMaybe "ghc")
(optional (strOption (long "with-ghc" <>
metavar "GHC" <>
help "Use this command for the GHC to run"))))
addCommand "runghc"
"Run runghc"
execCmd
((,)
<$> pure "runghc"
<*> many (strArgument (metavar "-- ARGS (e.g. stack runghc -- X.hs)")))
addCommand "clean"
"Clean the local packages"
cleanCmd
(pure ())
addSubCommands
Docker.dockerCmdName
"Subcommands specific to Docker use"
(do addCommand Docker.dockerPullCmdName
"Pull latest version of Docker image from registry"
dockerPullCmd
(pure ())
addCommand "reset"
"Reset the Docker sandbox"
dockerResetCmd
(flag False True (long "keep-home" <>
help "Do not delete sandbox's home directory"))
addCommand Docker.dockerCleanupCmdName
"Clean up Docker images and containers"
dockerCleanupCmd
dockerCleanupOpts
addCommand "exec"
"Execute a command in a Docker container without setting up Haskell environment first"
dockerExecCmd
((,) <$> strArgument (metavar "[--] CMD")
<*> many (strArgument (metavar "ARGS"))))
)
-- commandsFromPlugins plugins pluginShouldHaveRun) https://github.com/commercialhaskell/stack/issues/322
when (globalLogLevel level == LevelDebug) $ putStrLn versionString'
run level `catch` \e -> do
-- This special handler stops "stack: " from being printed before the
-- exception
case fromException e of
Just ec -> exitWith ec
Nothing -> do
print e
exitFailure
where
dockerHelpOptName = Docker.dockerCmdName ++ "-help"
-- Try to run a plugin
tryRunPlugin :: Plugins -> IO ()
tryRunPlugin plugins = do
args <- getArgs
case dropWhile (List.isPrefixOf "-") args of
((T.pack -> name):args')
| isJust (lookupPlugin plugins name) -> do
callPlugin plugins name args' `catch` onPluginErr
exitSuccess
_ -> return ()
-- TODO(danburton): use logger
onPluginErr :: PluginException -> IO ()
onPluginErr (PluginNotFound _ name) = do
T.hPutStr stderr $ "Stack plugin not found: " <> name
exitFailure
onPluginErr (PluginExitFailure _ i) = do
exitWith (ExitFailure i)
-- TODO(danburton): improve this, although it should never happen
pluginShouldHaveRun :: Plugin -> GlobalOpts -> IO ()
pluginShouldHaveRun _plugin _globalOpts = do
fail "Plugin should have run"
-- | Print out useful path information in a human-readable format (and
-- support others later).
pathCmd :: [Text] -> GlobalOpts -> IO ()
pathCmd keys go =
withBuildConfig
go
ExecStrategy
(do env <- ask
let cfg = envConfig env
bc = envConfigBuildConfig cfg
menv <- getMinimalEnvOverride
snap <- packageDatabaseDeps
local <- packageDatabaseLocal
snaproot <- installationRootDeps
localroot <- installationRootLocal
distDir <- distRelativeDir
forM_
(filter
(\(_,key,_) ->
null keys || elem key keys)
paths)
(\(_,key,path) ->
$logInfo
(key <> ": " <>
path
(PathInfo
bc
menv
snap
local
snaproot
localroot
distDir))))
-- | Passed to all the path printers as a source of info.
data PathInfo = PathInfo
{piBuildConfig :: BuildConfig
,piEnvOverride :: EnvOverride
,piSnapDb :: Path Abs Dir
,piLocalDb :: Path Abs Dir
,piSnapRoot :: Path Abs Dir
,piLocalRoot :: Path Abs Dir
,piDistDir :: Path Rel Dir
}
-- | The paths of interest to a user. The first tuple string is used
-- for a description that the optparse flag uses, and the second
-- string as a machine-readable key and also for @--foo@ flags. The user
-- can choose a specific path to list like @--global-stack-root@. But
-- really it's mainly for the documentation aspect.
--
-- When printing output we generate @PathInfo@ and pass it to the
-- function to generate an appropriate string.
paths :: [(String, Text, PathInfo -> Text)]
paths =
[ ( "Global stack root directory"
, "global-stack-root"
, \pi ->
T.pack (toFilePath (configStackRoot (bcConfig (piBuildConfig pi)))))
, ( "Project root (derived from stack.yaml file)"
, "project-root"
, \pi ->
T.pack (toFilePath (bcRoot (piBuildConfig pi))))
, ( "Configuration location (where the stack.yaml file is)"
, "config-location"
, \pi ->
T.pack (toFilePath (bcStackYaml (piBuildConfig pi))))
, ( "PATH environment variable"
, "bin-path"
, \pi ->
T.pack (intercalate ":" (eoPath (piEnvOverride pi))))
, ( "Installed GHCs (unpacked and archives)"
, "ghc-paths"
, \pi ->
T.pack (toFilePath (configLocalPrograms (bcConfig (piBuildConfig pi)))))
, ( "Local bin path where stack installs executables"
, "local-bin-path"
, \pi ->
T.pack (toFilePath (configLocalBin (bcConfig (piBuildConfig pi)))))
, ( "Extra include directories"
, "extra-include-dirs"
, \pi ->
T.intercalate
", "
(Set.elems (configExtraIncludeDirs (bcConfig (piBuildConfig pi)))))
, ( "Extra library directories"
, "extra-library-dirs"
, \pi ->
T.intercalate ", " (Set.elems (configExtraLibDirs (bcConfig (piBuildConfig pi)))))
, ( "Snapshot package database"
, "snapshot-pkg-db"
, \pi ->
T.pack (toFilePath (piSnapDb pi)))
, ( "Local project package database"
, "local-pkg-db"
, \pi ->
T.pack (toFilePath (piLocalDb pi)))
, ( "Snapshot installation root"
, "snapshot-install-root"
, \pi ->
T.pack (toFilePath (piSnapRoot pi)))
, ( "Local project installation root"
, "local-install-root"
, \pi ->
T.pack (toFilePath (piLocalRoot pi)))
, ( "Dist work directory"
, "dist-dir"
, \pi ->
T.pack (toFilePath (piDistDir pi)))]
data SetupCmdOpts = SetupCmdOpts
{ scoGhcVersion :: !(Maybe Version)
, scoForceReinstall :: !Bool
}
setupParser :: Parser SetupCmdOpts
setupParser = SetupCmdOpts
<$> (optional $ argument readVersion (metavar "VERSION"))
<*> boolFlags False
"reinstall"
"Reinstall GHC, even if available (implies no-system-ghc)"
idm
where
readVersion = do
s <- readerAsk
case parseVersionFromString s of
Nothing -> readerError $ "Invalid version: " ++ s
Just x -> return x
setupCmd :: SetupCmdOpts -> GlobalOpts -> IO ()
setupCmd SetupCmdOpts{..} go@GlobalOpts{..} = do
(manager,lc) <- loadConfigWithOpts go
runStackT manager globalLogLevel (lcConfig lc) globalTerminal $
Docker.rerunWithOptionalContainer
(lcProjectRoot lc)
(runStackLoggingT manager globalLogLevel globalTerminal $ do
(ghc, mstack) <-
case scoGhcVersion of
Just v -> return (v, Nothing)
Nothing -> do
bc <- lcLoadBuildConfig lc globalResolver ExecStrategy
return (bcGhcVersionExpected bc, Just $ bcStackYaml bc)
mpaths <- runStackT manager globalLogLevel (lcConfig lc) globalTerminal $ ensureGHC SetupOpts
{ soptsInstallIfMissing = True
, soptsUseSystem =
(configSystemGHC $ lcConfig lc)
&& not scoForceReinstall
, soptsExpected = ghc
, soptsStackYaml = mstack
, soptsForceReinstall = scoForceReinstall
, soptsSanityCheck = True
}
case mpaths of
Nothing -> $logInfo "GHC on PATH would be used"
Just ps -> $logInfo $ "Would add the following to PATH: "
<> T.pack (intercalate [searchPathSeparator] ps)
)
withBuildConfig :: GlobalOpts
-> NoBuildConfigStrategy
-> StackT EnvConfig IO ()
-> IO ()
withBuildConfig go@GlobalOpts{..} strat inner = do
(manager, lc) <- loadConfigWithOpts go
runStackT manager globalLogLevel (lcConfig lc) globalTerminal $
Docker.rerunWithOptionalContainer (lcProjectRoot lc) $ do
bconfig <- runStackLoggingT manager globalLogLevel globalTerminal $
lcLoadBuildConfig lc globalResolver strat
envConfig <-
runStackT
manager globalLogLevel bconfig globalTerminal
setupEnv
runStackT
manager
globalLogLevel
envConfig
globalTerminal
inner
cleanCmd :: () -> GlobalOpts -> IO ()
cleanCmd () go = withBuildConfig go ThrowException clean
-- | Parser for package names
readPackageName :: ReadM PackageName
readPackageName = do
s <- readerAsk
case parsePackageNameFromString s of
Nothing -> readerError $ "Invalid package name: " ++ s
Just x -> return x
-- | Parser for package:[-]flag
readFlag :: ReadM (Map PackageName (Map FlagName Bool))
readFlag = do
s <- readerAsk
case break (== ':') s of
(pn, ':':mflag) -> do
pn' <-
case parsePackageNameFromString pn of
Nothing -> readerError $ "Invalid package name: " ++ pn
Just x -> return x
let (b, flagS) =
case mflag of
'-':x -> (False, x)
_ -> (True, mflag)
flagN <-
case parseFlagNameFromString flagS of
Nothing -> readerError $ "Invalid flag name: " ++ flagS
Just x -> return x
return $ Map.singleton pn' $ Map.singleton flagN b
_ -> readerError "Must have a colon"
-- | Build the project.
buildCmd :: FinalAction -> BuildOpts -> GlobalOpts -> IO ()
buildCmd finalAction opts go@GlobalOpts{..} = withBuildConfig go ThrowException $
Stack.Build.build opts { boptsFinalAction = finalAction }
-- | Install
installCmd :: BuildOpts -> GlobalOpts -> IO ()
installCmd opts go@GlobalOpts{..} = withBuildConfig go ExecStrategy $
Stack.Build.build opts { boptsInstallExes = True }
-- | Unpack packages to the filesystem
unpackCmd :: [String] -> GlobalOpts -> IO ()
unpackCmd names go@GlobalOpts{..} = do
(manager,lc) <- loadConfigWithOpts go
runStackT manager globalLogLevel (lcConfig lc) globalTerminal $
Docker.rerunWithOptionalContainer (lcProjectRoot lc) $
runStackT manager globalLogLevel (lcConfig lc) globalTerminal $ do
menv <- getMinimalEnvOverride
Stack.Fetch.unpackPackages menv "." names
-- | Update the package index
updateCmd :: () -> GlobalOpts -> IO ()
updateCmd () go@GlobalOpts{..} = do
(manager,lc) <- loadConfigWithOpts go
runStackT manager globalLogLevel (lcConfig lc) globalTerminal $
Docker.rerunWithOptionalContainer (lcProjectRoot lc) $
runStackT manager globalLogLevel (lcConfig lc) globalTerminal $
getMinimalEnvOverride >>= Stack.PackageIndex.updateAllIndices
-- | Upload to Hackage
uploadCmd :: [String] -> GlobalOpts -> IO ()
uploadCmd args0 go = withBuildConfig go ExecStrategy $ do
let args = if null args0 then ["."] else args0
config <- asks getConfig
manager <- asks getHttpManager
menv <- getMinimalEnvOverride
runghc <- join $ System.Process.Read.findExecutable menv "runghc"
liftIO $ do
uploader <- Upload.mkUploader
(toFilePath runghc)
config
$ Upload.setGetManager (return manager)
Upload.defaultUploadSettings
mapM_ (Upload.upload uploader) args
-- | Execute a command.
execCmd :: (String, [String]) -> GlobalOpts -> IO ()
execCmd (cmd,args) go@GlobalOpts{..} =
withBuildConfig go ExecStrategy $
exec cmd args
-- | Run the REPL in the context of a project, with
replCmd :: ([Text], [String], FilePath) -> GlobalOpts -> IO ()
replCmd (targets,args,path) go@GlobalOpts{..} = withBuildConfig go ExecStrategy $ do
repl targets args path
-- | Pull the current Docker image.
dockerPullCmd :: () -> GlobalOpts -> IO ()
dockerPullCmd _ go@GlobalOpts{..} = do
(manager,lc) <- liftIO $ loadConfigWithOpts go
runStackT manager globalLogLevel (lcConfig lc) globalTerminal $
Docker.preventInContainer Docker.pull
-- | Reset the Docker sandbox.
dockerResetCmd :: Bool -> GlobalOpts -> IO ()
dockerResetCmd keepHome go@GlobalOpts{..} = do
(manager,lc) <- liftIO (loadConfigWithOpts go)
runStackLoggingT manager globalLogLevel globalTerminal$ Docker.preventInContainer $
Docker.reset (lcProjectRoot lc) keepHome
-- | Cleanup Docker images and containers.
dockerCleanupCmd :: Docker.CleanupOpts -> GlobalOpts -> IO ()
dockerCleanupCmd cleanupOpts go@GlobalOpts{..} = do
(manager,lc) <- liftIO $ loadConfigWithOpts go
runStackT manager globalLogLevel (lcConfig lc) globalTerminal $
Docker.preventInContainer $
Docker.cleanup cleanupOpts
-- | Execute a command
dockerExecCmd :: (String, [String]) -> GlobalOpts -> IO ()
dockerExecCmd (cmd,args) go@GlobalOpts{..} = do
(manager,lc) <- liftIO $ loadConfigWithOpts go
runStackT manager globalLogLevel (lcConfig lc) globalTerminal $
Docker.preventInContainer $
Docker.rerunCmdWithRequiredContainer (lcProjectRoot lc)
(return (cmd,args,id))
-- | Parser for build arguments.
buildOpts :: Bool -> Parser BuildOpts
buildOpts forHaddock =
BuildOpts <$> target <*> libProfiling <*> exeProfiling <*>
optimize <*> haddock <*> haddockDeps <*> finalAction <*> dryRun <*> ghcOpts <*>
flags <*> installExes <*> preFetch <*> testArgs <*> onlySnapshot
where optimize =
maybeBoolFlags "optimizations" "optimizations for TARGETs and all its dependencies" idm
target =
fmap (map T.pack)
(many (strArgument
(metavar "TARGET" <>
help "If none specified, use all packages defined in current directory")))
libProfiling =
boolFlags False
"library-profiling"
"library profiling for TARGETs and all its dependencies"
idm
exeProfiling =
boolFlags False
"executable-profiling"
"library profiling for TARGETs and all its dependencies"
idm
haddock =
boolFlags forHaddock
"haddock"
"building Haddocks"
idm
haddockDeps =
maybeBoolFlags
"haddock-deps"
"building Haddocks for dependencies"
idm
finalAction = pure DoNothing
installExes = pure False
dryRun = flag False True (long "dry-run" <>
help "Don't build anything, just prepare to")
ghcOpts = (++)
<$> flag [] ["-Wall", "-Werror"]
( long "pedantic"
<> help "Turn on -Wall and -Werror (note: option name may change in the future"
)
<*> many (fmap T.pack
(strOption (long "ghc-options" <>
metavar "OPTION" <>
help "Additional options passed to GHC")))
flags =
fmap (Map.unionsWith Map.union) $ many
(option readFlag
( long "flag"
<> metavar "PACKAGE:[-]FLAG"
<> help "Override flags set in stack.yaml (applies to local packages and extra-deps)"
))
preFetch = flag False True
(long "prefetch" <>
help "Fetch packages necessary for the build immediately, useful with --dry-run")
testArgs =
fmap (fromMaybe [])
(optional
(argsOption
(long "test-arguments" <> metavar "TEST_ARGS" <>
help "Arguments passed in to the test suite program")))
onlySnapshot = flag False True
(long "only-snapshot" <>
help "Only build packages for the snapshot database, not the local database")
-- | Parser for docker cleanup arguments.
dockerCleanupOpts :: Parser Docker.CleanupOpts
dockerCleanupOpts =
Docker.CleanupOpts <$>
(flag' Docker.CleanupInteractive
(short 'i' <>
long "interactive" <>
help "Show cleanup plan in editor and allow changes (default)") <|>
flag' Docker.CleanupImmediate
(short 'y' <>
long "immediate" <>
help "Immediately execute cleanup plan") <|>
flag' Docker.CleanupDryRun
(short 'n' <>
long "dry-run" <>
help "Display cleanup plan but do not execute") <|>
pure Docker.CleanupInteractive) <*>
opt (Just 14) "known-images" "LAST-USED" <*>
opt Nothing "unknown-images" "CREATED" <*>
opt (Just 0) "dangling-images" "CREATED" <*>
opt Nothing "stopped-containers" "CREATED" <*>
opt Nothing "running-containers" "CREATED"
where opt def' name mv =
fmap Just
(option auto
(long name <>
metavar (mv ++ "-DAYS-AGO") <>
help ("Remove " ++
toDescr name ++
" " ++
map toLower (toDescr mv) ++
" N days ago" ++
case def' of
Just n -> " (default " ++ show n ++ ")"
Nothing -> ""))) <|>
flag' Nothing
(long ("no-" ++ name) <>
help ("Do not remove " ++
toDescr name ++
case def' of
Just _ -> ""
Nothing -> " (default)")) <|>
pure def'
toDescr = map (\c -> if c == '-' then ' ' else c)
-- | Parser for global command-line options.
globalOpts :: Parser GlobalOpts
globalOpts =
GlobalOpts <$> logLevelOpt <*>
configOptsParser False <*>
optional resolverParser <*>
flag
True
False
(long "no-terminal" <>
help
"Override terminal detection in the case of running in a false terminal")
-- | Parse for a logging level.
logLevelOpt :: Parser LogLevel
logLevelOpt =
fmap parse
(strOption (long "verbosity" <>
metavar "VERBOSITY" <>
help "Verbosity: silent, error, warn, info, debug")) <|>
flag defaultLogLevel
verboseLevel
(short 'v' <> long "verbose" <>
help ("Enable verbose mode: verbosity level \"" <> showLevel verboseLevel <> "\""))
where verboseLevel = LevelDebug
showLevel l =
case l of
LevelDebug -> "debug"
LevelInfo -> "info"
LevelWarn -> "warn"
LevelError -> "error"
LevelOther x -> T.unpack x
parse s =
case s of
"debug" -> LevelDebug
"info" -> LevelInfo
"warn" -> LevelWarn
"error" -> LevelError
_ -> LevelOther (T.pack s)
resolverParser :: Parser Resolver
resolverParser =
option readResolver
(long "resolver" <>
metavar "RESOLVER" <>
help "Override resolver in project file")
-- | Default logging level should be something useful but not crazy.
defaultLogLevel :: LogLevel
defaultLogLevel = LevelInfo
-- | Parsed global command-line options.
data GlobalOpts = GlobalOpts
{ globalLogLevel :: LogLevel -- ^ Log level
, globalConfigMonoid :: ConfigMonoid -- ^ Config monoid, for passing into 'loadConfig'
, globalResolver :: Maybe Resolver -- ^ Resolver override
, globalTerminal :: Bool -- ^ We're in a terminal?
} deriving (Show)
-- | Load the configuration with a manager. Convenience function used
-- throughout this module.
loadConfigWithOpts :: GlobalOpts -> IO (Manager,LoadConfig (StackLoggingT IO))
loadConfigWithOpts GlobalOpts{..} = do
manager <- newTLSManager
lc <- runStackLoggingT
manager
globalLogLevel
globalTerminal
(loadConfig globalConfigMonoid)
return (manager,lc)
-- | Project initialization
initCmd :: InitOpts -> GlobalOpts -> IO ()
initCmd initOpts go@GlobalOpts{..} = do
(manager,lc) <- loadConfigWithOpts go
runStackT manager globalLogLevel (lcConfig lc) globalTerminal $
Docker.rerunWithOptionalContainer (lcProjectRoot lc) $
runStackT manager globalLogLevel (lcConfig lc) globalTerminal $
initProject initOpts
-- | Project creation
newCmd :: InitOpts -> GlobalOpts -> IO ()
newCmd initOpts go@GlobalOpts{..} = do
(manager,lc) <- loadConfigWithOpts go
runStackT manager globalLogLevel (lcConfig lc) globalTerminal $
Docker.rerunWithOptionalContainer (lcProjectRoot lc) $
runStackT manager globalLogLevel (lcConfig lc) globalTerminal $ do
newProject
initProject initOpts
-- | Fix up extra-deps for a project
solverCmd :: Bool -- ^ modify stack.yaml automatically?
-> GlobalOpts
-> IO ()
solverCmd fixStackYaml go =
withBuildConfig go ThrowException (solveExtraDeps fixStackYaml)
-- | Parser for @solverCmd@
solverOptsParser :: Parser Bool
solverOptsParser = boolFlags False
"modify-stack-yaml"
"Automatically modify stack.yaml with the solver's recommendations"
idm