feldspar-compiler-0.6.0.2: src/Feldspar/Compiler/Frontend/CommandLine/Main.hs
--
-- Copyright (c) 2009-2011, ERICSSON AB
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--
-- * Redistributions of source code must retain the above copyright notice,
-- this list of conditions and the following disclaimer.
-- * Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
-- * Neither the name of the ERICSSON AB nor the names of its contributors
-- may be used to endorse or promote products derived from this software
-- without specific prior written permission.
--
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-- OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--
{-# LANGUAGE ScopedTypeVariables #-}
module Main where
-- ====================================== Feldspar imports ==================================
import Feldspar.NameExtractor
import Feldspar.Compiler.Compiler
import qualified Feldspar.Compiler.Compiler as CompilerCore
import Feldspar.Compiler.Backend.C.Options
import qualified Feldspar.Compiler.Backend.C.Options as CoreOptions
import Feldspar.Compiler.Frontend.CommandLine.API.Options as StandaloneOptions
import Feldspar.Compiler.Frontend.CommandLine.API.Constants
import Feldspar.Compiler.Frontend.CommandLine.API.Library as StandaloneLib
import Feldspar.Compiler.Backend.C.Library
import Feldspar.Compiler.Frontend.CommandLine.API
import Feldspar.Compiler.Imperative.Representation
import Feldspar.Compiler.Backend.C.CodeGeneration
import Feldspar.Compiler.Error
import Feldspar.Compiler.Backend.C.Plugin.PrettyPrint
-- ====================================== System imports ==================================
import System.IO
import System.Exit
import System.Info
import System.Process
import System.IO.Error
import System.FilePath
import System.Directory
import System.Environment
import System.Console.GetOpt
-- ====================================== Control imports ==================================
import Control.Monad
import Control.Exception
import Control.Monad.Error
import Control.Monad.CatchIO
-- ====================================== Other imports ==================================
import Data.List
import Data.Maybe (fromMaybe)
import Debug.Trace
import Language.Haskell.Interpreter
data CompilationError =
InterpreterError InterpreterError
| InternalErrorCall String
compileFunction :: String -> String -> CoreOptions.Options -> OriginalFunctionSignature
-> Interpreter (String, Either SplitModuleDescriptor CompilationError)
compileFunction inFileName outFileName coreOptions originalFunctionSignature = do
let functionName = originalFunctionName originalFunctionSignature
(SomeCompilable prg) <- interpret ("SomeCompilable " ++ functionName) (as::SomeCompilable)
let splitModuleDescriptor = executePluginChain Standalone prg originalFunctionSignature coreOptions
-- XXX force evaluation in order to be able to catch the exceptions
-- liftIO $ evaluate $ compToC coreOptions compilationUnit -- XXX somehow not enough(?!) -- counter-example: structexamples
liftIO $ do
tempdir <- Control.Exception.catch getTemporaryDirectory (\(_ :: IOException) -> return ".")
(tempfile, temph) <- openTempFile tempdir "feldspar-temp.txt"
let core = compileToCCore Standalone prg (Just outFileName) IncludesNeeded originalFunctionSignature coreOptions
Control.Exception.finally (do hPutStrLn temph $ sourceCode $ sctccrSource core
hPutStrLn temph $ sourceCode $ sctccrHeader core)
(do hClose temph
removeFileIfPossible tempfile)
return (functionName, Left splitModuleDescriptor)
compileAllFunctions :: String -> String -> CoreOptions.Options -> [OriginalFunctionSignature]
-> Interpreter [(String, Either SplitModuleDescriptor CompilationError)]
compileAllFunctions inFileName outFileName options [] = return []
compileAllFunctions inFileName outFileName options (x:xs) = do
let functionName = originalFunctionName x
resultCurrent <- catchError (compileFunction inFileName outFileName options x)
(\(e::InterpreterError) -> return (functionName, Right $ InterpreterError e))
`Control.Monad.CatchIO.catch`
(\msg -> return (functionName,
Right $ InternalErrorCall $ errorPrefix ++ show (msg::Control.Exception.ErrorCall)))
resultRest <- compileAllFunctions inFileName outFileName options xs
return $ resultCurrent : resultRest
-- | Interpreter body for single-function compilation
singleFunctionCompilationBody :: String -> String -> CoreOptions.Options -> OriginalFunctionSignature
-> Interpreter (IO ())
singleFunctionCompilationBody inFileName outFileName coreOptions originalFunctionSignature = do
liftIO $ fancyWrite $ "Compiling function " ++ originalFunctionName originalFunctionSignature ++ "..."
SomeCompilable prg <-
interpret ("SomeCompilable " ++ originalFunctionName originalFunctionSignature) (as::SomeCompilable)
liftIO $ standaloneCompile prg inFileName outFileName originalFunctionSignature coreOptions
return $ return ()
mergeModules :: [Module ()] -> Module ()
mergeModules [] = handleError "Standalone" InvariantViolation "Called mergeModules with an empty list"
mergeModules [x] = x
mergeModules l@(x:xs) = Module {
entities = nub $ entities x ++ entities (mergeModules xs), -- nub is in fact a "global plugin" here
moduleLabel = ()
}
padFunctionName :: String -> String
padFunctionName n = StandaloneLib.rpadWith 50 '.' $ "Function " ++ n
writeErrors :: (String, Either a CompilationError) -> IO ()
writeErrors (functionName, Left x) = return ()
writeErrors (functionName, Right err) = case err of
InterpreterError ie -> do
withColor Red $ putStrLn $ "Error in function " ++ functionName ++ ":"
printInterpreterError ie
InternalErrorCall ec -> do
withColor Red $ putStrLn $ "Error in function " ++ functionName ++ ":"
withColor Red $ putStrLn ec
writeSummary :: (String, Either a CompilationError) -> IO ()
writeSummary (functionName, Left x) = do
withColor Cyan $ putStr $ padFunctionName functionName
withColor Green $ putStrLn "[OK]"
writeSummary (functionName, Right msg) = do
withColor Cyan $ putStr $ padFunctionName functionName
withColor Red $ putStrLn "[FAILED]"
filterLefts :: [(String, Either a b)] -> [a]
filterLefts [] = []
filterLefts [(_,Left x)] = [x]
filterLefts [(_,Right _)] = []
filterLefts ((_,Left x):xs) = x : filterLefts xs
filterLefts ((_,Right _):xs) = filterLefts xs
-- | Interpreter body for multi-function compilation
multiFunctionCompilationBody :: String -> String -> CoreOptions.Options -> [OriginalFunctionSignature] -> Interpreter (IO ())
multiFunctionCompilationBody inFileName outFileName coreOptions declarationList = do
let (hIncludes, hLineNum) = genIncludeLines coreOptions Nothing
let (cIncludes, cLineNum) = genIncludeLines coreOptions (Just outFileName)
liftIO $ appendFile (makeHFileName outFileName) hIncludes
liftIO $ appendFile (makeCFileName outFileName) cIncludes
modules <- compileAllFunctions inFileName outFileName coreOptions declarationList
liftIO $ do
mapM_ writeErrors modules
withColor Blue $ putStrLn "\n================= [ Summary of compilation results ] =================\n"
mapM_ writeSummary modules
let mergedCModules = mergeModules $ map smdSource $ filterLefts modules
let mergedHModules = mergeModules $ map smdHeader $ filterLefts modules
let cCompToCResult = compToCWithInfos ((coreOptions, Declaration_pl), cLineNum) mergedCModules
let hCompToCResult = compToCWithInfos ((coreOptions, Declaration_pl), hLineNum) mergedHModules
appendFile (makeCFileName outFileName) (fst $ snd cCompToCResult) `Control.Exception.catch` errorHandler
appendFile (makeHFileName outFileName) (fst $ snd hCompToCResult) `Control.Exception.catch` errorHandler
writeFile (makeDebugCFileName outFileName) (show $ fst cCompToCResult) `Control.Exception.catch` errorHandler
writeFile (makeDebugHFileName outFileName) (show $ fst hCompToCResult) `Control.Exception.catch` errorHandler
return $ return ()
where
errorHandler msg = withColor Red $ putStrLn $ errorPrefix ++ show (msg::Control.Exception.ErrorCall)
-- | Calculates the output file name.
convertOutputFileName :: String -> Maybe String -> String
convertOutputFileName inputFileName = fromMaybe (takeFileName $ dropExtension inputFileName)
makeBackup :: String -> IO ()
makeBackup filename = renameFile filename (filename ++ ".bak") `Control.Exception.catch` (\(_ :: IOException) -> return ())
main = do
(opts, inputFileName) <- handleOptions optionDescriptors startOptions helpHeader
let outputFileName = convertOutputFileName inputFileName (optOutputFileName opts)
prepareInputFile inputFileName
makeBackup $ makeHFileName outputFileName
makeBackup $ makeCFileName outputFileName
makeBackup $ makeDebugHFileName outputFileName
makeBackup $ makeDebugCFileName outputFileName
fileDescriptor <- openFile inputFileName ReadMode
fileContents <- hGetContents fileDescriptor
let declarationList = getExtendedDeclarationList inputFileName fileContents
let moduleName = getModuleName inputFileName fileContents
fancyWrite $ "Compilation target: module " ++ moduleName
fancyWrite $ "Output file: " ++ outputFileName
let highLevelInterpreterWithModuleInfo =
highLevelInterpreter moduleName inputFileName globalImportList False
-- C code generation
case optStandaloneMode opts of
MultiFunction
| null declarationList -> putStrLn "No functions to compile."
| otherwise -> do
fancyWrite $ "Number of functions to compile: " ++ show (length declarationList)
highLevelInterpreterWithModuleInfo
(multiFunctionCompilationBody inputFileName outputFileName (optCompilerMode opts) declarationList)
return ()
SingleFunction funName -> do
let originalFunctionSignatureNeeded =
case filter ((==funName).originalFunctionName) declarationList of
[a] -> a
[] -> error $ "Function " ++ funName ++ " not found"
_ -> error "Unexpected error SC/01"
highLevelInterpreterWithModuleInfo
(singleFunctionCompilationBody inputFileName outputFileName (optCompilerMode opts) originalFunctionSignatureNeeded)
return ()