jbi-0.1.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 :: GlobalEnv -> WrappedTool proxy -> IO (WrappedTool ToolInformation)
toolInformation env (Wrapped pr) = Wrapped <$> commandToolInformation env pr
--------------------------------------------------------------------------------
data Valid bt = Valid
{ command :: !(Installed bt)
, 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 :: GlobalEnv -> 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
mInst <- commandInformation
case mInst of
Nothing -> return Nothing
Just inst -> do
usbl <- canUseCommand env inst
if not usbl
then return Nothing
else do mroot <- commandProjectRoot (path inst)
forM mroot $ \root ->
Valid inst 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 (path (command val)))
prepareWrapped :: GlobalEnv -> 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) => GlobalEnv -> Tagged bt CommandPath -> IO res)
-> GlobalEnv -> 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 :: GlobalEnv -> WrappedTool Valid -> IO ExitCode
prepare env wv = prepareWrapped env wv >> return ExitSuccess
-- Explicitly prepare.
targets :: GlobalEnv -> WrappedTool Valid -> IO [ProjectTarget]
targets = runPrepared (const (fmap stripTags . commandTargets))
build :: Maybe ProjectTarget -> GlobalEnv -> WrappedTool Valid -> IO ExitCode
build targ = runPrepared (\env cp -> commandBuild env cp (tagInner (tag targ)))
repl :: Maybe ProjectTarget -> GlobalEnv -> WrappedTool Valid -> IO ExitCode
repl targ = runPrepared (\env cp -> commandRepl env cp (tagInner (tag targ)))
clean :: GlobalEnv -> WrappedTool Valid -> IO ExitCode
clean = runPrepared commandClean
test :: GlobalEnv -> WrappedTool Valid -> IO ExitCode
test = runPrepared commandTest
bench :: GlobalEnv -> WrappedTool Valid -> IO ExitCode
bench = runPrepared commandBench
exec :: String -> Args -> GlobalEnv -> WrappedTool Valid -> IO ExitCode
exec cmd args = runPrepared (\env cp -> commandExec env cp cmd args)
run :: ProjectTarget -> Args -> GlobalEnv -> WrappedTool Valid -> IO ExitCode
run targ args = runPrepared (\env cp -> commandRun env cp (tag targ) args)
update :: GlobalEnv -> WrappedTool Valid -> IO ExitCode
update = runPrepared commandUpdate