-- | 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 (..), module Futhark.Pipeline, module Futhark.Compiler, ) where import Control.Monad import Data.Maybe import Futhark.Compiler import Futhark.IR (Name, Prog, nameFromString) import Futhark.IR.SOACS (SOACS) import Futhark.Pipeline import Futhark.Util.Options import System.FilePath import System.IO -- | Run a parameterised Futhark compiler, where @cfg@ is a user-given -- configuration type. Call this from @main@. compilerMain :: -- | Initial configuration. cfg -> -- | Options that affect the configuration. [CompilerOption cfg] -> -- | The short action name (e.g. "compile to C"). String -> -- | The longer action description. String -> -- | The pipeline to use. Pipeline SOACS lore -> -- | The action to take on the result of the pipeline. ( FutharkConfig -> cfg -> CompilerMode -> FilePath -> Prog lore -> FutharkM () ) -> -- | Program name String -> -- | Command line arguments. [String] -> 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... " 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 (futharkConfig config) (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 [] ["server"] (NoArg $ Right $ \config -> config {compilerMode = ToServer}) "Generate a server executable instead of a library.", Option "w" [] (NoArg $ Right $ \config -> config {compilerWarn = False}) "Disable all warnings.", Option [] ["Werror"] (NoArg $ Right $ \config -> config {compilerWerror = True}) "Treat warnings as errors.", Option [] ["safe"] (NoArg $ Right $ \config -> config {compilerSafe = True}) "Ignore 'unsafe' in code.", Option [] ["entry-points"] ( ReqArg ( \arg -> Right $ \config -> config { compilerEntryPoints = nameFromString arg : compilerEntryPoints config } ) "NAME" ) "Treat this function as an additional entry point." ] 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, compilerWarn :: Bool, compilerConfig :: cfg, compilerEntryPoints :: [Name] } -- | Are we compiling a library or an executable? data CompilerMode = ToLibrary | ToExecutable | ToServer 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, compilerWarn = True, compilerConfig = x, compilerEntryPoints = mempty } 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, futharkWarn = compilerWarn config, futharkEntryPoints = compilerEntryPoints config }