-
Notifications
You must be signed in to change notification settings - Fork 0
/
cabal-targets.hs
executable file
·138 lines (121 loc) · 3.91 KB
/
cabal-targets.hs
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#!/usr/bin/env cabal
{- cabal:
build-depends:
base, cabal-plan, containers, directory,
optparse-applicative, terminal-size, text
ghc-options: -Wall -Wcompat -Wunused-packages
-}
{-# LANGUAGE ApplicativeDo #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
import Cabal.Plan
import Data.Bool (bool)
import Data.Char (toLower, toUpper)
import Data.List (intercalate, stripPrefix, (\\))
import Data.Maybe (fromMaybe)
import Data.Text (Text)
import Options.Applicative
import System.Directory (doesDirectoryExist)
import System.Exit (die)
import Text.Read (readMaybe)
import qualified Data.Map.Strict as Map
import qualified Data.Text as Text
import qualified Data.Text.IO as Text
import qualified System.Console.Terminal.Size as TS
data Options = Options
{ optProjectDir :: FilePath
, optCompTypes :: [CompType]
}
deriving (Show)
parseArgs :: IO Options
parseArgs = do
cols <- maybe 100 TS.width <$> TS.size
customExecParser
(prefs $ columns cols)
( info
( helper <*> do
project <-
strOption $
short 'p'
<> long "project"
<> metavar "DIR"
<> value "."
<> help "The project directory, or a subdirectory of it"
<> showDefaultWith id
include <-
many . option readType $
short 'i'
<> long "include"
<> metavar "TYPE"
<> help (inExHelp "Include")
exclude <-
many . option readType $
short 'x'
<> long "exclude"
<> metavar "TYPE"
<> help (inExHelp "Exclude")
pure
Options
{ optProjectDir = project
, optCompTypes = (if null include then allCompTypes else include) \\ exclude
}
)
(fullDesc <> header "List the targets in a Cabal project")
)
where
inExHelp op =
op
<> " targets of type TYPE (repeatable; one of: "
<> intercalate ", " (map showType allCompTypes)
<> ")"
readType :: ReadM CompType
readType = maybeReader $ readMaybe . ("CompType" <>) . initial toUpper
showType :: CompType -> String
showType = initial toLower . stripPrefix' "CompType" . show
stripPrefix' p s = fromMaybe s $ stripPrefix p s
initial f (c : s) = f c : s
initial _ s = s
main :: IO ()
main = do
Options{..} <- parseArgs
-- Avoid confusing behaviour from `findProjectRoot`
doesDirectoryExist optProjectDir
>>= bool (die $ "Project directory " <> optProjectDir <> " doesn't exist") (pure ())
root <-
findProjectRoot optProjectDir
>>= maybe (die $ "Can't find project root in " <> optProjectDir) pure
plan <- findAndDecodePlanJson $ ProjectRelativeToDir root
Text.putStr . Text.unlines $
[ dispCompNameTargetFull (pIdName . uPId $ u) c
| u <- Map.elems $ pjUnitsWithType UnitTypeLocal plan
, c <- Map.keys $ uComps u
, compType c `elem` optCompTypes
]
data CompType
= CompTypeLib
| CompTypeFlib
| CompTypeExe
| CompTypeTest
| CompTypeBench
| CompTypeSetup
deriving (Eq, Ord, Enum, Bounded, Show, Read)
compType :: CompName -> CompType
compType = \case
CompNameLib -> CompTypeLib
CompNameSubLib _ -> CompTypeLib
CompNameFLib _ -> CompTypeFlib
CompNameExe _ -> CompTypeExe
CompNameTest _ -> CompTypeTest
CompNameBench _ -> CompTypeBench
CompNameSetup -> CompTypeSetup
allCompTypes :: [CompType]
allCompTypes = [minBound .. maxBound]
dispCompNameTargetFull :: PkgName -> CompName -> Text
dispCompNameTargetFull p c = unPkgName p <> ":" <> dispCompNameTarget p c
pjUnitsWithType :: UnitType -> PlanJson -> Map.Map UnitId Unit
pjUnitsWithType t = Map.filter ((t ==) . uType) . pjUnits
pIdName :: PkgId -> PkgName
pIdName (PkgId name _) = name
unPkgName :: PkgName -> Text
unPkgName (PkgName name) = name