1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
module Main where
import Control.Monad (forM_, when)
import qualified Data.Binary as B
import Data.Function (on)
import Data.List (intercalate, groupBy, sortBy)
import Data.Maybe (catMaybes, fromJust, isJust)
import Data.Ord (comparing)
import qualified Distribution.Types.PackageDescription as PD
import Distribution.Types.PackageDescription (PackageDescription)
import Distribution.Types.BuildType (BuildType(Simple))
import Options.Applicative (execParser)
import System.Directory (createDirectoryIfMissing, removeDirectoryRecursive)
import System.Exit (die)
import System.FilePath ((</>))
import Coolbal.CabalPlan
import Coolbal.Configure (configure)
import Coolbal.FindRoot
import Coolbal.Options
import Coolbal.Target
import Coolbal.Target.Executable (makeExeTarget)
checkCompatibleSpec :: PackageDescription -> IO ()
checkCompatibleSpec pd
| PD.buildType pd /= Simple = die ("Only build type Simple is supported; package uses " ++ show (PD.buildType pd))
| not (null (PD.setupBuildInfo pd)) = die "Custom setup build info unsupported"
| otherwise = return ()
compatibleTargets :: PackageDescription -> CabalPlan -> [AnyTarget]
compatibleTargets pd plan =
catMaybes (map (\e -> AnyTargetExe <$> makeExeTarget e plan) (PD.executables pd))
parseCabalPlan' :: FoundCabalSpec -> IO CabalPlan
parseCabalPlan' foundSpec =
parseCabalPlan (foundcsRootDir foundSpec </> "dist-newstyle/cache/plan.json")
>>= maybe (die "Cabal plan.json not found; make sure to run 'cabal build' beforehand")
return
readCachedTargets :: (AnyTarget -> Bool) -> IO (FilePath, [AnyTarget], [(String, [AnyTarget])])
readCachedTargets predicate = do
FoundRootDir rootdir <- findDist
allTargets <- B.decodeFile (rootdir </> "dist-coolbal/targets.bin")
let targets = filter predicate allTargets
targetsForName = map ((,) <$> fst . head <*> map snd)
$ groupBy ((==) `on` fst)
$ sortBy (comparing fst)
$ concat
[[(useAnyTarget targetName t, t), (useAnyTarget targetNameQualified t, t)]
| t <- targets]
return (rootdir, targets, targetsForName)
targetsToBuild :: [(String, [AnyTarget])] -> String -> IO [AnyTarget]
targetsToBuild targetsForName name =
case lookup name targetsForName of
Nothing -> die ("Target not found: '" ++ name ++ "'")
Just [t] -> return [t]
Just _ -> die ("Ambiguous target name: '" ++ name ++ "'")
doConfigure :: IO ()
doConfigure = do
foundSpec <- findCabalSpec
pd <- configure (foundcsCabal foundSpec)
checkCompatibleSpec pd
plan <- parseCabalPlan' foundSpec
-- print pd
let targets = compatibleTargets pd plan
names = map (useAnyTarget (\t -> targetName t ++ " (" ++ targetNameQualified t ++ ")")) targets
putStrLn ("Supported targets: " ++ intercalate ", " names)
createDirectoryIfMissing True (foundcsRootDir foundSpec </> "dist-coolbal")
B.encodeFile (foundcsRootDir foundSpec </> "dist-coolbal/targets.bin") targets
doClean :: IO ()
doClean = do
foundSpec <- findCabalSpec
removeDirectoryRecursive (foundcsRootDir foundSpec </> "dist-coolbal")
doBuild :: BuildOptions -> IO ()
doBuild (BuildOptions mtarget) = do
(rootdir, targets, targetsForName) <- readCachedTargets (const True)
toBuild <- maybe (return targets) (targetsToBuild targetsForName) mtarget
forM_ toBuild $ useAnyTarget $ \tg -> do
old <- targetCheckOld rootdir tg
when old $ targetBuild rootdir tg
doRun :: RunOptions -> IO ()
doRun (RunOptions mtarget args) = do
(rootdir, targets, targetsForName) <- readCachedTargets (isJust . useAnyTarget targetExecute)
toBuild <- maybe (return targets) (targetsToBuild targetsForName) mtarget
case toBuild of
[tg] -> do
useAnyTarget (\tg' -> do old <- targetCheckOld rootdir tg'
when old $ targetBuild rootdir tg'
-- when (not old) $ putStrLn "Up to date"
fromJust (targetExecute tg') rootdir args)
tg
_ -> do
die "Cannot determine which target to run, multiple executables found"
main :: IO ()
main = do
options <- execParser optionParser
case options of
Configure -> doConfigure
Clean -> doClean
Build opts -> doBuild opts
Run opts -> doRun opts
|