----------------------------------------------------------------------------- -- | -- Module : Windll -- Copyright : (c) Tamar Christina 2009 - 2010 -- License : BSD3 -- -- Maintainer : tamar@zhox.com -- Stability : experimental -- Portability : portable -- -- A module to handle commandline arguments and parsing -- for the test generator program. -- ----------------------------------------------------------------------------- module Tests.Exec.CmdArgs where import WinDll.Session.Hs2lib import Tests.Exec.Version import WinDll.Utils.Feedback import WinDll.Structs.Structures import WinDll.Utils.Pragma import System.Directory import System.Environment import System.FilePath import System.Console.GetOpt import System.Exit import Data.Maybe import Data.Char import Data.List import Control.Monad -- | just renaming it to something that might make more sense to read in this module type Config = Session -- | Default configuration set for both the client and server. defaultConfig :: Mode -> Config defaultConfig Windows = newSession defaultConfig Unix = let session = newSession in session { call = CCall } -- | The parse mode for processArgs type Mode = Platform -- | Read all the pragmas that contain options and then update the session with the new flags enablePragmas :: Exec () enablePragmas = do session <- get inform _normal "Enabling commandline pragmas..." let prgmas = (pragmas.workingset) session cmds = getPragmas (upper_name ++ "_OPTS") prgmas args = mainFile session : concatMap (\(Pragma _ x)->x) cmds mode = platform session session' <- processConversionPragmas prgmas session (result,errs) <- if (null args) then return (session',[]) else liftIO $ do (result,errs) <- processArgs session' mode args if (not.null) errs then putStrLn " Error(s):" else do return () mapM_ (\i -> putStrLn (" -> unrecognized input '"++i++"')")) errs return (result,errs) if null errs then put result else die "Error processing dynamic options" -- | Process args tries to parse the commandline arguments for either the client or the server. -- 1 = The session to initialize things with -- 2 = Mode to parse for -- 3 = arguments to parse -- return = Parsed config or error message processArgs :: Config -> Mode -> [String] -> IO (Config, [String]) processArgs cfg mode argv= case getOpt Permute opt argv of (o,n,[]) -> process (foldl (flip id) cfg o) n opt (_,_,errs) -> ioError (userError (concat errs ++ usageInfo header opt)) where header = "Usage: " ++ name ++ " [OPTION...]" name = if mode == Windows then exename ++ ".exe" else exename opt = optDescr --if mode == Windows then optDescrClient else optDescrServer process a r opt = case Help `elem` opts of True -> ioError (userError $ usageInfo header opt) False -> case Version `elem` opts of True -> putStrLn versionmsg >> exitSuccess False -> case r of (x:[]) -> do path <- canonicalizePath (addTrailingPathSeparator (tempDIR a)) base <- getCurrentDirectory abs_path <- guessPath x let dir = takeDirectory (normalise x) bin = combine base dir return (a { mainFile = x , platform = mode , tempDIR = path , absPath = abs_path , baseDir = base },[]) _ -> ioError (userError $ usageInfo header opt) where opts = options a -- | Create a boolean flag boolFlag :: String -> String -> (a->Bool->a) -> String -> [OptDescr (a->a)] boolFlag short long update descr = [ template (update' True) short long descr , template (update' False) "" ("no"++long) [] ] where template value short long descr = Option short [long] (NoArg value) descr update' = flip update -- | Collection of options available optDescr = optVerbose : optCall : optNamespace : optTempDir : optVersion : optHelp : optGhc : boolFlag "W" "warnings" (\cfg new -> cfg { warnings = new }) "print all warnings (Default True)" ++ boolFlag "E" "warnings-as-errors" (\cfg new -> cfg { warnings_as_errors = new , warnings = new || warnings cfg }) "treat all warnings as errors (Default False)" ++ boolFlag "T" "keep-temp-files" (\cfg new -> cfg { keep_temps = new }) "keep all the temporary files generated by the pre-processor (Default False)" ++ boolFlag "" "debug" (\cfg new -> cfg { debugging = new }) ("Enable debugging support which enabled memory profiling.") -- | Collection of simple actions optVerbose = Option ['v'] ["verbose","verbosity" ] (OptArg (\value cfg -> cfg { verbosity = fread (verbosity cfg) value }) "") "set the verbosity level. Requires a number between 0 (off) and 3" optCall = Option ['C'] ["convention" ] (ReqArg (\value cfg -> cfg { call = cread StdCall value }) "") "set the calling convention used in FFI (Default stdcall)" optNamespace = Option ['n'] ["name" ] (ReqArg (\value cfg -> cfg { namespace = correct value }) "") ("set the name to use in every generated code. (Default \"" ++ exename ++ "\")") optTempDir = Option [ ] ["temp","temp-directory" ] (ReqArg (\value cfg -> cfg { tempDIR = value }) "") "set the temporary directory of the pre-processor (Default system temp)" optHelp = Option ['?'] ["h","help" ] (NoArg (\cfg -> cfg {options = Help:(options cfg)}) ) "Show this help screen" optVersion = Option [ ] ["version" ] (NoArg (\cfg -> cfg {options = Version:(options cfg)}) ) "Show program version information" optGhc = Option [ ] ["opt-ghc" ] (ReqArg (\value cfg -> cfg { optGHC = value }) "\"\"") "options to pass along unmodified to ghc during main compilation." fread :: Int -> Maybe String -> Int fread c q = let p = maybe 1 (read' 1) q in if (p + c) > 3 || (c + p) < 0 then 1 else (c+p) cread c q = let p = reads q hasValue = not $ null p value = if hasValue then fst $ head p else c in value -- | Get the read in namespace name into a useable format correct :: String -> String correct [] = [] correct (x:xs) = toUpper x : filter isAlphaNum xs -- | Because read can fail for parsing stuff other than strings. -- this function is used to either accept the change. or use the default read' :: Int -> String -> Int read' def str = let r = reads str in if null r then def else (fst.head) r -- | The main parse function to call. It takes a mode as parameters and takes a pointer -- to a function to call should it all succeed. goArgs :: Mode -> (Config -> IO ()) -> IO () goArgs mode call = do args <- getArgs (result,errs) <- processArgs (defaultConfig mode) mode args if (not.null) errs then putStrLn " Errors:" else do return () mapM_ (\i -> putStrLn (" -> unrecognized input '"++i++"')")) errs if null errs then call result else do return ()