-- | Convenient common interface for command line Futhark compilers. -- Using this module ensures that all compilers take the same options. -- A small amount of flexibility is provided for backend-specific -- options. module Futhark.Compiler.CLI ( compilerMain , CompilerOption , CompilerMode(..) ) where import Control.Monad import Data.Maybe import System.FilePath import System.Console.GetOpt import System.IO import Futhark.Pipeline import Futhark.Compiler import Futhark.Representation.AST (Prog) import Futhark.Representation.SOACS (SOACS) import Futhark.Util.Options -- | Run a parameterised Futhark compiler, where @cfg@ is a user-given -- configuration type. Call this from @main@. compilerMain :: cfg -- ^ Initial configuration. -> [CompilerOption cfg] -- ^ Options that affect the configuration. -> String -- ^ The short action name (e.g. "compile to C"). -> String -- ^ The longer action description. -> Pipeline SOACS lore -- ^ The pipeline to use. -> (cfg -> CompilerMode -> FilePath -> Prog lore -> FutharkM ()) -- ^ The action to take on the result of the pipeline. -> String -- ^ Program name -> [String] -- ^ Command line arguments. -> IO () compilerMain cfg cfg_opts name desc pipeline doIt prog args = do hSetEncoding stdout utf8 hSetEncoding stderr utf8 mainWithOptions (newCompilerConfig cfg) (commandLineOptions ++ map wrapOption cfg_opts) "options... program" inspectNonOptions prog args where inspectNonOptions [file] config = Just $ compile config file inspectNonOptions _ _ = Nothing compile config filepath = runCompilerOnProgram (futharkConfig config) pipeline (action config filepath) filepath action config filepath = Action { actionName = name , actionDescription = desc , actionProcedure = doIt (compilerConfig config) (compilerMode config) $ outputFilePath filepath config } -- | An option that modifies the configuration of type @cfg@. type CompilerOption cfg = OptDescr (Either (IO ()) (cfg -> cfg)) type CoreCompilerOption cfg = OptDescr (Either (IO ()) (CompilerConfig cfg -> CompilerConfig cfg)) commandLineOptions :: [CoreCompilerOption cfg] commandLineOptions = [ Option "o" [] (ReqArg (\filename -> Right $ \config -> config { compilerOutput = Just filename }) "FILE") "Name of the compiled binary." , Option "v" ["verbose"] (OptArg (Right . incVerbosity) "FILE") "Print verbose output on standard error; wrong program to FILE." , Option [] ["library"] (NoArg $ Right $ \config -> config { compilerMode = ToLibrary }) "Generate a library instead of an executable." , Option [] ["executable"] (NoArg $ Right $ \config -> config { compilerMode = ToExecutable }) "Generate an executable instead of a library (set by default)." , Option [] ["Werror"] (NoArg $ Right $ \config -> config { compilerWerror = True }) "Treat warnings as errors." , Option [] ["safe"] (NoArg $ Right $ \config -> config { compilerSafe = True }) "Ignore 'unsafe' in code." ] wrapOption :: CompilerOption cfg -> CoreCompilerOption cfg wrapOption = fmap wrap where wrap f = do g <- f return $ \cfg -> cfg { compilerConfig = g (compilerConfig cfg) } incVerbosity :: Maybe FilePath -> CompilerConfig cfg -> CompilerConfig cfg incVerbosity file cfg = cfg { compilerVerbose = (v, file `mplus` snd (compilerVerbose cfg)) } where v = case fst $ compilerVerbose cfg of NotVerbose -> Verbose Verbose -> VeryVerbose VeryVerbose -> VeryVerbose data CompilerConfig cfg = CompilerConfig { compilerOutput :: Maybe FilePath , compilerVerbose :: (Verbosity, Maybe FilePath) , compilerMode :: CompilerMode , compilerWerror :: Bool , compilerSafe :: Bool , compilerConfig :: cfg } -- | Are we compiling a library or an executable? data CompilerMode = ToLibrary | ToExecutable deriving (Eq, Ord, Show) -- | The configuration of the compiler. newCompilerConfig :: cfg -> CompilerConfig cfg newCompilerConfig x = CompilerConfig { compilerOutput = Nothing , compilerVerbose = (NotVerbose, Nothing) , compilerMode = ToExecutable , compilerWerror = False , compilerSafe = False , compilerConfig = x } outputFilePath :: FilePath -> CompilerConfig cfg -> FilePath outputFilePath srcfile = fromMaybe (srcfile `replaceExtension` "") . compilerOutput futharkConfig :: CompilerConfig cfg -> FutharkConfig futharkConfig config = newFutharkConfig { futharkVerbose = compilerVerbose config , futharkWerror = compilerWerror config , futharkSafe = compilerSafe config }