summaryrefslogtreecommitdiff
path: root/utils/hpc/HpcFlags.hs
blob: 68bd86135349d3b9a1f6418a230c8e4f009787bc (plain)
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
-- (c) 2007 Andy Gill

module HpcFlags where

import System.Console.GetOpt
import Data.Maybe ( fromMaybe )
import qualified HpcSet as Set
import Data.Char
import Trace.Hpc.Tix
import Trace.Hpc.Mix
import System.Exit

data Flags = Flags 
  { outputFile		:: String
  , includeMods         :: Set.Set String
  , excludeMods         :: Set.Set String
  , hpcDir		:: String
  , srcDirs		:: [String]
  , destDir		:: String

  , perModule 		:: Bool
  , decList 		:: Bool
  , xmlOutput 		:: Bool

  , funTotals           :: Bool
  , altHighlight        :: Bool

  , combineFun          :: CombineFun
  , postInvert		:: Bool
  }

default_flags = Flags
  { outputFile		= "-"
  , includeMods         = Set.empty
  , excludeMods         = Set.empty
  , hpcDir              = ".hpc"
  , srcDirs             = []
  , destDir             = "."

  , perModule           = False
  , decList		= False
  , xmlOutput		= False

  , funTotals           = False
  , altHighlight        = False

  , combineFun          = ADD
  , postInvert		= False
  }

-- We do this after reading flags, because the defaults
-- depends on if specific flags we used.

default_final_flags flags = flags 
  { srcDirs = if null (srcDirs flags)
    	      then ["."]
	      else srcDirs flags
  }

type FlagOptSeq = [OptDescr (Flags -> Flags)] -> [OptDescr (Flags -> Flags)]

noArg :: String -> String -> (Flags -> Flags) -> FlagOptSeq
noArg flag detail fn = (:) $ Option [] [flag] (NoArg $ fn) detail

anArg :: String -> String -> String -> (String -> Flags -> Flags) -> FlagOptSeq
anArg flag detail argtype fn = (:) $ Option [] [flag] (ReqArg fn argtype) detail

infoArg :: String -> FlagOptSeq
infoArg info = (:) $ Option [] [] (NoArg $ id) info

excludeOpt      = anArg "exclude"    "exclude MODULE and/or PACKAGE" "[PACKAGE:][MODULE]"  
                $ \ a f -> f { excludeMods = a `Set.insert` excludeMods f }

includeOpt      = anArg "include"    "include MODULE and/or PACKAGE" "[PACKAGE:][MODULE]"  
                $ \ a f -> f { includeMods = a `Set.insert` includeMods f }

hpcDirOpt        = anArg "hpcdir"     "sub-directory that contains .mix files" "DIR"
                   (\ a f -> f { hpcDir = a })
                .  infoArg "default .hpc [rarely used]"

srcDirOpt       = anArg "srcdir"     "path to source directory of .hs files" "DIR"
	          (\ a f -> f { srcDirs = srcDirs f ++ [a] })
	        . infoArg "multi-use of srcdir possible"
	        
destDirOpt      = anArg "destdir"   "path to write output to" "DIR"
	        $ \ a f -> f { destDir = a }

	        
outputOpt     = anArg "output"    "output FILE" "FILE"        $ \ a f -> f { outputFile = a }
-- markup

perModuleOpt  = noArg "per-module" "show module level detail" $ \ f -> f { perModule = True }
decListOpt    = noArg "decl-list"  "show unused decls"	      $ \ f -> f { decList = True }
xmlOutputOpt  = noArg "xml-output" "show output in XML"       $ \ f -> f { xmlOutput = True }  
funTotalsOpt  = noArg "fun-entry-count" "show top-level function entry counts"      
							      $ \ f -> f { funTotals = True }  
altHighlightOpt  
	      = noArg "highlight-covered" "highlight covered code, rather that code gaps"
							      $ \ f -> f { altHighlight = True }  

combineFunOpt = anArg "combine" 
	      	      "combine .tix files with join function, default = ADD" "FUNCTION"
	      $ \ a f -> case reads (map toUpper a) of
	      	          [(c,"")] -> f { combineFun = c }
			  _ -> error $ "no such combine function : " ++ a
combineFunOptInfo = infoArg 
		  $ "FUNCTION = " ++ foldr1 (\ a b -> a ++ " | " ++ b) (map fst combineFuns)

postInvertOpt = noArg "post-invert" "invert output; ticked becomes unticked, unticked becomes ticked"
							      $ \ f -> f { funTotals = True }  
-------------------------------------------------------------------------------

readMixWithFlags flags mod = readMix [ dir ++  "/" ++ hpcDir flags
                                     | dir <- srcDirs flags 
                                     ] mod

-------------------------------------------------------------------------------

command_usage plugin = 
  putStrLn $
				       "Usage: hpc " ++ (name plugin) ++ " " ++ 
				        (usage plugin) ++
				        if null (options plugin [])
				        then ""
  	                                else usageInfo "\n\nOptions:\n" (options plugin [])

hpcError :: Plugin -> String -> IO a
hpcError plugin msg = do
   putStrLn $ "Error: " ++ msg
   command_usage plugin
   exitFailure
 
-------------------------------------------------------------------------------

data Plugin = Plugin { name           :: String
     	             , usage          :: String
		     , options        :: FlagOptSeq
		     , summary        :: String
		     , implementation :: Flags -> [String] -> IO ()
		     , init_flags     :: Flags
		     , final_flags    :: Flags -> Flags
		     }

------------------------------------------------------------------------------

-- filterModules takes a list of candidate modules, 
-- and 
--  * excludes the excluded modules
--  * includes the rest if there are no explicity included modules
--  * otherwise, accepts just the included modules.

allowModule :: Flags -> String -> Bool
allowModule flags full_mod 
      | full_mod' `Set.member` excludeMods flags = False
      | pkg_name  `Set.member` excludeMods flags = False
      | mod_name  `Set.member` excludeMods flags = False
      | Set.null (includeMods flags)             = True
      | full_mod' `Set.member` includeMods flags = True
      | pkg_name  `Set.member` includeMods flags = True
      | mod_name  `Set.member` includeMods flags = True
      | otherwise	 	       	         = False
  where
          full_mod' = pkg_name ++ mod_name
      -- pkg name always ends with '/', main 
	  (pkg_name,mod_name) = 
			case span (/= '/') full_mod of
		     (p,'/':m) -> (p ++ ":",m)
		     (m,[])    -> (":",m)
		     _         -> error "impossible case in allowModule" 

filterTix :: Flags -> Tix -> Tix
filterTix flags (Tix tixs) =
     Tix $ filter (allowModule flags . tixModuleName) tixs

         

------------------------------------------------------------------------------
-- HpcCombine specifics 

data CombineFun = ADD | DIFF | SUB | ZERO
     deriving (Eq,Show, Read, Enum)

combineFuns = [ (show comb,comb) 
	      | comb <- [ADD .. ZERO]
	      ]