diff --git a/src/HpcMain.hs b/src/HpcMain.hs index 13069ef6dbc3827195b17b73f7891cdbf7776a17..fe07047d85601baa3d728c5905abe47e1209880c 100644 --- a/src/HpcMain.hs +++ b/src/HpcMain.hs @@ -8,30 +8,35 @@ import Data.Bifunctor (bimap) import Data.List (intercalate, partition, uncons) import Data.List.NonEmpty (NonEmpty((:|))) import Data.Maybe (catMaybes, isJust) -import Data.Version -import System.Environment -import System.Exit -import System.Console.GetOpt +import Data.Version ( showVersion ) +import System.Environment ( getArgs ) +import System.Exit ( exitFailure, exitWith, ExitCode(ExitSuccess) ) +import System.Console.GetOpt ( getOpt, ArgOrder(Permute) ) import System.Directory (doesPathExist) import HpcFlags -import HpcReport -import HpcMarkup -import HpcCombine -import HpcShowTix -import HpcDraft -import HpcOverlay -import Paths_hpc_bin + ( Plugin(..), + Flags, + default_flags, + default_final_flags, + command_usage ) +import HpcReport ( report_plugin ) +import HpcMarkup ( markup_plugin ) +import HpcCombine ( sum_plugin, combine_plugin, map_plugin ) +import HpcShowTix ( showtix_plugin ) +import HpcDraft ( draft_plugin ) +import HpcOverlay ( overlay_plugin ) +import Paths_hpc_bin ( version ) helpList :: IO () helpList = do putStrLn $ - "Usage: hpc COMMAND ...\n\n" ++ - section "Commands" help ++ - section "Reporting Coverage" reporting ++ - section "Processing Coverage files" processing ++ - section "Coverage Overlays" overlays ++ - section "Others" other ++ + "Usage: hpc COMMAND ...\n\n" <> + section "Commands" [help_plugin] <> + section "Reporting Coverage" [report_plugin, markup_plugin] <> + section "Processing Coverage files" [sum_plugin, combine_plugin, map_plugin] <> + section "Coverage Overlays" [overlay_plugin, draft_plugin] <> + section "Others" [showtix_plugin, version_plugin] <> "" putStrLn "" putStrLn "or: hpc @response_file_1 @response_file_2 ..." @@ -42,35 +47,31 @@ helpList = do putStrLn "example:" putStrLn "report my_library.tix --include=ModuleA \\" putStrLn "--include=ModuleB" + +-- | Print the summaries of all plugins belonging to a given section. +section :: String -- ^ Name of the section. + -> [Plugin] -- ^ Plugins belonging to that section. + -> String +section msg plugins = msg <> ":\n" <> unlines summaries where - help = ["help"] - reporting = ["report","markup"] - overlays = ["overlay","draft"] - processing = ["sum","combine","map"] - other = [ name hook - | hook <- hooks - , name hook `notElem` - (concat [help,reporting,processing,overlays]) - ] - -section :: String -> [String] -> String -section _ [] = "" -section msg cmds = msg ++ ":\n" - ++ unlines [ take 14 (" " ++ cmd ++ repeat ' ') ++ summary hook - | cmd <- cmds - , hook <- hooks - , name hook == cmd - ] + summaries = [ take 14 (" " <> name plugin <> repeat ' ') <> summary plugin | plugin <- plugins ] + +main :: IO () +main = do + args <- getArgs + dispatch args +-- | The main dispatch function. It either accepts a valid command followed by a list of its arguments, +-- or a list of response files of the form '@filename'. dispatch :: [String] -> IO () dispatch [] = do helpList exitWith ExitSuccess dispatch (txt:args0) = do case lookup txt hooks' of - Just plugin -> parse plugin args0 + Just plugin -> dispatchOnPlugin plugin args0 _ -> case getResponseFileName txt of - Nothing -> parse help_plugin (txt:args0) + Nothing -> dispatchOnPlugin help_plugin (txt:args0) Just firstResponseFileName -> do let (responseFileNames', nonResponseFileNames) = partitionFileNames args0 @@ -82,38 +83,7 @@ dispatch (txt:args0) = do putStrLn $ "When first argument is a Response File, " <> "all arguments should be Response Files." exitFailure - let - responseFileNames :: NonEmpty FilePath - responseFileNames = firstResponseFileName :| responseFileNames' - - forM_ responseFileNames $ \responseFileName -> do - exists <- doesPathExist responseFileName - when (not exists) $ do - putStrLn $ "Response File '" <> responseFileName <> "' does not exist" - exitFailure - - -- read all Response Files - responseFileNamesAndText :: NonEmpty (FilePath, String) <- - forM responseFileNames $ \responseFileName -> - fmap (responseFileName, ) (readFile responseFileName) - forM_ responseFileNamesAndText $ \(responseFileName, responseFileText) -> - -- parse first word of Response File, which should be a command - case uncons $ words responseFileText of - Nothing -> do - putStrLn $ "Response File '" <> responseFileName <> "' has no command" - exitFailure - Just (responseFileCommand, args1) -> case lookup responseFileCommand hooks' of - -- check command for validity - -- It is important than a Response File cannot specify another Response File; - -- this is prevented - Nothing -> do - putStrLn $ "Response File '" <> responseFileName <> - "' command '" <> responseFileCommand <> "' invalid" - exitFailure - Just plugin -> do - putStrLn $ "Response File '" <> responseFileName <> "':" - parse plugin args1 - + dispatchOnResponseFiles (firstResponseFileName :| responseFileNames') where getResponseFileName :: String -> Maybe FilePath getResponseFileName s = do @@ -132,26 +102,52 @@ dispatch (txt:args0) = do bimap (fmap snd) (fmap fst) $ partition (isJust . snd) hasFileName in (catMaybes fileNames, nonFileNames) - parse plugin args = - case getOpt Permute (options plugin []) args of - (_,_,errs) | not (null errs) - -> do putStrLn "hpc failed:" - sequence_ [ putStr (" " ++ err) - | err <- errs - ] - putStrLn $ "\n" - command_usage plugin - exitFailure - (o,ns,_) -> do - let flags = final_flags plugin - . foldr (.) id o - $ init_flags plugin - implementation plugin flags ns - -main :: IO () -main = do - args <- getArgs - dispatch args +-- | Dispatch on a given list of response files. +dispatchOnResponseFiles :: NonEmpty FilePath -> IO () +dispatchOnResponseFiles fps = do + forM_ fps $ \responseFileName -> do + exists <- doesPathExist responseFileName + when (not exists) $ do + putStrLn $ "Response File '" <> responseFileName <> "' does not exist" + exitFailure + + -- read all Response Files + responseFileNamesAndText :: NonEmpty (FilePath, String) <- + forM fps $ \responseFileName -> + fmap (responseFileName, ) (readFile responseFileName) + forM_ responseFileNamesAndText $ \(responseFileName, responseFileText) -> + -- parse first word of Response File, which should be a command + case uncons $ words responseFileText of + Nothing -> do + putStrLn $ "Response File '" <> responseFileName <> "' has no command" + exitFailure + Just (responseFileCommand, args1) -> case lookup responseFileCommand hooks' of + -- check command for validity + -- It is important than a Response File cannot specify another Response File; + -- this is prevented + Nothing -> do + putStrLn $ "Response File '" <> responseFileName <> + "' command '" <> responseFileCommand <> "' invalid" + exitFailure + Just plugin -> do + putStrLn $ "Response File '" <> responseFileName <> "':" + dispatchOnPlugin plugin args1 + +-- | Dispatch on a given plugin and its arguments. +dispatchOnPlugin :: Plugin -> [String] -> IO () +dispatchOnPlugin plugin args = + case getOpt Permute (options plugin []) args of + (_,_,errs) | not (null errs) -> do + putStrLn "hpc failed:" + sequence_ [ putStr (" " <> err) | err <- errs ] + putStrLn $ "\n" + command_usage plugin + exitFailure + (o,ns,_) -> do + let flags = final_flags plugin + . foldr (.) id o + $ init_flags plugin + implementation plugin flags ns ------------------------------------------------------------------------------ @@ -177,7 +173,7 @@ help_plugin :: Plugin help_plugin = Plugin { name = "help" , usage = "[<HPC_COMMAND>]" , summary = "Display help for hpc or a single command" - , options = help_options + , options = id , implementation = help_main , init_flags = default_flags , final_flags = default_final_flags @@ -196,9 +192,6 @@ help_main _ (sub_txt:_) = do command_usage plugin' exitWith ExitSuccess -help_options :: FlagOptSeq -help_options = id - ------------------------------------------------------------------------------ version_plugin :: Plugin