jbi-0.2.0.0: lib/System/JBI/Commands.hs
{-# LANGUAGE FlexibleInstances, GADTs, OverloadedStrings, RankNTypes,
StandaloneDeriving #-}
{- |
Module : System.JBI.Commands
Description : Running a specific build tool
Copyright : (c) Ivan Lazar Miljenovic
License : MIT
Maintainer : Ivan.Miljenovic@gmail.com
-}
module System.JBI.Commands
( WrappedTool (..)
, Valid
, toolName
, toolInformation
, checkValidity
, alreadyUsed
, infoProjectDir
-- * Commands
, prepare
, targets
, build
, repl
, clean
, test
, bench
, exec
, run
, update
) where
import System.JBI.Commands.BuildTool
import System.JBI.Commands.Tool
import System.JBI.Environment
import System.JBI.Tagged
import Control.Monad (forM)
import Data.Aeson (ToJSON(toJSON))
import Data.Function (on)
import Data.Proxy (Proxy(..))
import System.Directory (withCurrentDirectory)
import System.Exit (ExitCode(ExitSuccess), die)
--------------------------------------------------------------------------------
data WrappedTool proxy where
Wrapped :: (NamedTool bt) => proxy bt -> WrappedTool proxy
-- | Not made polymorphic as there might be extra data contained
-- within.
instance Eq (WrappedTool Proxy) where
(==) = (==) `on` toolName
-- | Not really a valid instance as it doesn't produce Haskell code.
instance Show (WrappedTool Proxy) where
show = toolName
deriving instance Show (WrappedTool ToolInformation)
deriving instance Show (WrappedTool Valid)
instance ToJSON (WrappedTool ToolInformation) where
toJSON = withWrapped toJSON
withWrapped :: (forall bt. (NamedTool bt) => proxy bt -> res)
-> WrappedTool proxy -> res
withWrapped f (Wrapped bt) = f bt
toolName :: WrappedTool proxy -> String
toolName = withWrapped prettyName
toolInformation :: Env -> WrappedTool proxy -> IO (WrappedTool ToolInformation)
toolInformation env (Wrapped pr) = Wrapped <$> commandToolInformation env pr
--------------------------------------------------------------------------------
data Valid bt = Valid
{ command :: !(Tagged bt CommandPath)
-- ^ @since 0.2.0.0
, projectDir :: !(Tagged bt ProjectRoot)
, hasArtifacts :: !Bool
-- ^ Only to be used with 'ensurePrepared', 'prepare', 'unprepared'.
} deriving (Eq, Ord, Show, Read)
alreadyUsed :: WrappedTool Valid -> Bool
alreadyUsed = withWrapped hasArtifacts
infoProjectDir :: WrappedTool Valid -> ProjectRoot
infoProjectDir = withWrapped (stripTag . projectDir)
-- This is pretty ugly; one way to clean it up would be to use MaybeT.
checkValidity :: Env -> WrappedTool proxy -> IO (Maybe (WrappedTool Valid))
checkValidity env (Wrapped p) = fmap Wrapped <$> check p
where
check :: (BuildTool bt) => proxy' bt -> IO (Maybe (Valid bt))
check _ = do
mcp <- commandPath
case mcp of
Nothing -> return Nothing
Just cp -> do
usbl <- canUseCommand env cp
if not usbl
then return Nothing
else do mroot <- commandProjectRoot cp
forM mroot $ \root ->
Valid cp root <$> hasBuildArtifacts root
runInProject :: (forall bt. (BuildTool bt) => Tagged bt CommandPath -> IO res)
-> WrappedTool Valid -> IO res
runInProject f (Wrapped val) = withCurrentDirectory (stripTag (projectDir val))
(f (command val))
prepareWrapped :: Env -> WrappedTool Valid -> IO (WrappedTool Valid)
prepareWrapped env wt@(Wrapped val) = do
ec <- runInProject (commandPrepare env) wt
case ec of
ExitSuccess -> do
hasArt <- canUseCommand env (command val)
if hasArt
then return (Wrapped (val { hasArtifacts = True }))
else die "Preparation failed"
_ -> die "Could not prepare"
runPrepared :: (forall bt. (BuildTool bt) => Env -> Tagged bt CommandPath -> IO res)
-> Env -> WrappedTool Valid -> IO res
runPrepared f env wv = do
wv' <- if not (alreadyUsed wv)
then prepareWrapped env wv
else return wv
runInProject (f env) wv'
--------------------------------------------------------------------------------
-- This mimics the actual command-level portion of BuildTool
prepare :: Env -> WrappedTool Valid -> IO ExitCode
prepare env wv = prepareWrapped env wv >> return ExitSuccess
-- Explicitly prepare.
targets :: Env -> WrappedTool Valid -> IO [ProjectTarget]
targets = runPrepared ((fmap stripTags .) . commandTargets . envConfig)
build :: Maybe ProjectTarget -> Env -> WrappedTool Valid -> IO ExitCode
build targ = runPrepared (\env cp -> commandBuild env cp (tagInner (tag targ)))
repl :: Args -> Maybe ProjectTarget -> Env -> WrappedTool Valid -> IO ExitCode
repl rargs targ = runPrepared (\env cp -> commandRepl env cp (Tagged rargs) (tagInner (tag targ)))
clean :: Env -> WrappedTool Valid -> IO ExitCode
clean = runPrepared commandClean
test :: Env -> WrappedTool Valid -> IO ExitCode
test = runPrepared commandTest
bench :: Env -> WrappedTool Valid -> IO ExitCode
bench = runPrepared commandBench
exec :: String -> Args -> Env -> WrappedTool Valid -> IO ExitCode
exec cmd args = runPrepared (\env cp -> commandExec env cp cmd args)
run :: ProjectTarget -> Args -> Env -> WrappedTool Valid -> IO ExitCode
run targ args = runPrepared (\env cp -> commandRun env cp (tag targ) args)
update :: Env -> WrappedTool Valid -> IO ExitCode
update = runPrepared commandUpdate