packages feed

ddc-tools-0.4.3.1: src/ddc-main/Main.hs

-- | Unix-style command line interface to DDC.
--    This interface exports commands that work on whole modules at a time,
--    and does unix-style command line argument parsing.
--
import DDC.Main.Config
import DDC.Main.Help
import DDC.Main.Args
import DDC.Main.OptLevels

import DDC.Driver.Command.Scan
import DDC.Driver.Command.Parse
import DDC.Driver.Command.Check
import DDC.Driver.Command.Load
import DDC.Driver.Command.Compile
import DDC.Driver.Command.Build
import DDC.Driver.Command.BaseBuild

import DDC.Driver.Command.Tetra.Curry
import DDC.Driver.Command.Tetra.Boxing

import DDC.Driver.Command.Flow.Prep
import DDC.Driver.Command.Flow.Lower
import DDC.Driver.Command.Flow.Concretize
import DDC.Driver.Command.Flow.Melt
import DDC.Driver.Command.Flow.Wind
import DDC.Driver.Command.Flow.Thread
import qualified DDC.Core.Flow          as Flow

import DDC.Driver.Command.ToSalt
import DDC.Driver.Command.ToC
import DDC.Driver.Command.ToLlvm
import DDC.Driver.Command.ToPHP

import DDC.Driver.Interface.Source
import DDC.Build.Builder
import DDC.Data.Pretty
import System.Environment
import System.IO
import System.Exit
import Data.Maybe
import Control.Monad
import Control.Monad.Trans.Except
import qualified System.FilePath                as System
import qualified System.Directory               as System
import qualified DDC.Driver.Stage               as Driver
import qualified DDC.Driver.Config              as Driver
import qualified DDC.Core.Salt.Runtime          as Runtime
import qualified DDC.Core.Simplifier.Recipe     as Simplifier
import qualified DDC.Version                    as Version
import qualified DDC.Build.Interface.Store      as Store


main :: IO ()
main
 = do   args    <- getArgs

        -- Get the default configuration.
        --  This contains static information such as where the code
        --  for the base libraries and runtime system is installed.
        config0 <- getDefaultConfig

        -- Update the static config with dynamic config read from
        --   the command-line arguments.
        config  <- parseArgs args config0


        -- Run the main compiler.
        run config


run :: Config -> IO ()
run config
 = case configMode config of
        -- We didn't get any arguments on the command line.
        ModeNone
         ->     putStr hello

        -- Display the version string.
        ModeVersion
         ->     putStrLn Version.splash

        -- Display the help page.
        ModeHelp
         ->     putStrLn help

        -- Scan a module.
        ModeScan filePath bPrintLocs
         -> do  dconfig <- getDriverConfig config (Just filePath)
                runError $ cmdScanFromFile  dconfig filePath bPrintLocs

        -- Parse a module.
        ModeParse filePath
         -> do  dconfig <- getDriverConfig config (Just filePath)
                runError $ cmdParseFromFile dconfig filePath

        -- Parse and type check a module.
        ModeCheck filePath
         -> do  dconfig <- getDriverConfig config (Just filePath)
                store   <- Store.new
                result  <- runExceptT $  cmdCheckFromFile dconfig store filePath
                
                case result of
                 Left err        
                  -> do putStrLn err
                        exitWith $ ExitFailure 1

                 Right ()
                  -> return ()

        -- Parse, type check and transform a module.
        ModeLoad filePath
         -> do  dconfig <- getDriverConfig config (Just filePath)
                store   <- Store.new
                runError $ cmdLoadFromFile dconfig store
                                (configTrans config) (configWith config) 
                                filePath

        -- Compile a module to object code.
        ModeCompile filePath
         -> do  forceBaseBuild config
                dconfig <- getDriverConfig config (Just filePath)
                store   <- Store.new
                runError $ cmdCompileRecursive dconfig False store filePath 

        -- Compile a module into an executable.
        ModeMake filePath
         -> do  forceBaseBuild config
                dconfig <- getDriverConfig config (Just filePath)
                store   <- Store.new
                runError $ cmdCompileRecursive dconfig True store filePath

        -- Build libraries or executables following a .spec file.
        ModeBuild filePath
         -> do  forceBaseBuild config
                dconfig <- getDriverConfig config (Just filePath)
                store   <- Store.new
                runError $ cmdBuild dconfig store filePath

        -- Convert a module to Salt.
        ModeToSalt filePath
         -> do  dconfig <- getDriverConfig config (Just filePath)
                store   <- Store.new
                runError $ cmdToSaltFromFile dconfig store filePath

        -- Convert a module to C
        ModeToC filePath
         -> do  dconfig <- getDriverConfig config (Just filePath)
                store   <- Store.new
                runError $ cmdToSeaFromFile  dconfig store filePath

        -- Convert a module to LLVM
        ModeToLLVM filePath
         -> do  dconfig <- getDriverConfig config (Just filePath)
                store   <- Store.new
                runError $ cmdToLlvmFromFile dconfig store filePath

        -- Convert a module to C
        ModeToPHP filePath
         -> do  dconfig <- getDriverConfig config (Just filePath)
                store   <- Store.new
                runError $ cmdToPHPFromFile  dconfig store filePath


        -- Tetra specific -----------------------------------------------------
        ModeTetraCurry filePath
         -> do  dconfig <- getDriverConfig config (Just filePath)
                str     <- readFile filePath
                runError $ cmdTetraCurry dconfig (SourceFile filePath) str

        ModeTetraBoxing filePath
         -> do  dconfig <- getDriverConfig config (Just filePath)
                str     <- readFile filePath
                runError $ cmdTetraBoxing dconfig (SourceFile filePath) str

        -- Flow specific ------------------------------------------------------
        -- Prepare a Disciple Core Flow program for lowering.
        ModeFlowPrep filePath
         -> do  dconfig <- getDriverConfig config (Just filePath)
                str     <- readFile filePath
                runError $ cmdFlowPrep dconfig (SourceFile filePath) str

        -- Lower a Disciple Core Flow program to loops.
        ModeFlowLower filePath
         -> do  dconfig <- getDriverConfig config (Just filePath)
                str     <- readFile filePath
                runError 
                 $ cmdFlowLower dconfig Flow.defaultConfigScalar
                        (SourceFile filePath) str

        -- Lower a Disciple Core Flow program to loops.
        ModeFlowLowerKernel filePath
         -> do  dconfig <- getDriverConfig config (Just filePath)
                str     <- readFile filePath
                runError 
                 $ cmdFlowLower dconfig Flow.defaultConfigKernel
                        (SourceFile filePath) str

        -- Lower a Disciple Core Flow program to loops.
        ModeFlowLowerVector filePath
         -> do  dconfig <- getDriverConfig config (Just filePath)
                str     <- readFile filePath
                runError 
                 $ cmdFlowLower dconfig Flow.defaultConfigVector 
                        (SourceFile filePath) str

        -- Concretize rate type variables in a Disciple Core Flow program.
        ModeFlowConcretize filePath
         -> do  dconfig <- getDriverConfig config (Just filePath)
                str     <- readFile filePath
                runError $ cmdFlowConcretize dconfig (SourceFile filePath) str

        -- Melt compound data structures.
        ModeFlowMelt filePath
         -> do  dconfig <- getDriverConfig config (Just filePath)
                str     <- readFile filePath
                runError $ cmdFlowMelt dconfig (SourceFile filePath) str

        -- Wind loop primops into tail recursive loops.
        ModeFlowWind filePath
         -> do  dconfig <- getDriverConfig config (Just filePath)
                str     <- readFile filePath
                runError $ cmdFlowWind dconfig (SourceFile filePath) str

        -- Thread the world token through a Disciple Core Flow program.
        ModeFlowThread filePath
         -> do  dconfig <- getDriverConfig config (Just filePath)
                str     <- readFile filePath
                runError $ cmdFlowThread dconfig (SourceFile filePath) str

        -- Build --------------------------------------------------------------
        -- Build the runtime and base libraries.
        ModeBaseBuild
         -> do  dconfig <- getDriverConfig config Nothing
                store   <- Store.new
                runError $ cmdBaseBuild dconfig store

        -- Print --------------------------------------------------------------
        -- Print the external builder info for this platform.
        ModePrintBuilder
         -> do  dconfig <- getDriverConfig config Nothing
                putStrLn $ renderIndent $ ppr (Driver.configBuilder dconfig)

        -- Print where the runtime and base libraries are installed.
        ModePrintBaseDir
         ->     putStrLn $ configBaseDir config


-- | Get the compile driver from the config.
getDriverConfig :: Config -> Maybe FilePath -> IO Driver.Config
getDriverConfig config mFilePath
 = do   
        -- Determine the default builder config.
        Just builder    
                <- determineDefaultBuilder 
                $  defaultBuilderConfig config

        -- Treat the directory holding a module to compile as a base
        -- directory where we look for other modules.
        let moduleBaseDirs
                = maybeToList
                $ fmap System.takeDirectory mFilePath

        -- The default runtime system config.
        let runtimeConfig
             = Runtime.Config
             { Runtime.configHeapSize             = configRuntimeHeapSize config }

        -- Build driver config.
        let dconfig
             = Driver.Config
             { Driver.configLogBuild              = True
             , Driver.configDump                  = configDump config
             , Driver.configInferTypes            = configInferTypes config
             , Driver.configSimplSalt             = Simplifier.idsimp
             , Driver.configViaBackend            = configViaBackend config
             , Driver.configRuntime               = runtimeConfig
             , Driver.configBuilder               = builder
             , Driver.configPretty                = Driver.defaultConfigPretty
             , Driver.configSuppressHashImports   = False
             , Driver.configModuleBaseDirectories = moduleBaseDirs
             , Driver.configOutputFile            = configOutputFile config
             , Driver.configOutputDir             = configOutputDir  config 
             , Driver.configKeepLlvmFiles         = configKeepLlvmFiles config
             , Driver.configKeepSeaFiles          = configKeepSeaFiles  config
             , Driver.configKeepAsmFiles          = configKeepAsmFiles  config 
             , Driver.configTaintAvoidTypeChecks  = configTaintAvoidTypeChecks config
             , Driver.configRuntimeLinkStrategy   = configRuntimeLinkStrategy config }
        
        -- We need to force -infer on because the inliner templates may not
        -- have full type annotations.
        simplSalt <- getSimplSaltOfConfig config 
                        dconfig { Driver.configInferTypes = True }
                        builder runtimeConfig mFilePath

        return  $ dconfig        
                { Driver.configSimplSalt        = simplSalt }


-- | Print errors to stderr and set the exit code.
runError :: ExceptT String IO a -> IO ()
runError m
 = do   result  <- runExceptT m
        case result of
         Left err       
          -> do hPutStrLn stderr err
                exitWith $ ExitFailure 1

         Right _
          -> return ()


-- | Force build of base library if it doesn't already exist.
-- 
--   Due to bugs in cabal-install 1.22 it can't be trusted to run the
--   post-install hook that builds our base library. To work around this
--   we check that it is built before running any command that might
--   need it.
--
forceBaseBuild :: Config -> IO ()
forceBaseBuild config
 = do   
        -- Check if the runtime library has already been built.
        -- If it hasn't then force a basebuild.
        exist   <- System.doesFileExist 
                $  configBaseDir config 
                        System.</> "build" 
                        System.</> "libddc-runtime.a"

        when (not exist)
         $ do   putStrLn "* Building base library..."
                dconfig <- getDriverConfig config Nothing
                store   <- Store.new
                runError $ cmdBaseBuild dconfig store