> -- | 
> -- Module      : EMachine.Compiler
> -- Copyright   : Edwin Brady
> -- Licence     : BSD-style (see LICENSE in the distribution)
> --
> -- Maintainer  : eb@dcs.st-and.ac.uk
> -- Stability   : experimental
> -- Portability : portable
> -- 
> -- Public interface for Epigram Supercombinator Compiler
> module Epic.Compiler(CompileOptions(..),
>                      compile, 
>                      compileOpts,
>                      compileDecls,
>                      link) where
Brings everything together; parsing, checking, code generation
> import System
> import System.IO
> import System.Directory
> import System.Environment
> import Char
> import Epic.Language
> import Epic.Parser
> import Epic.Scopecheck
> import Epic.CodegenC
> import Epic.Simplify
> import Paths_epic
> addGCC :: [CompileOptions] -> String
> addGCC [] = ""
> addGCC ((GCCOpt s):xs) = s ++ " " ++ addGCC xs
> addGCC (_:xs) = addGCC xs
> outputHeader :: [CompileOptions] -> Maybe FilePath
> outputHeader [] = Nothing
> outputHeader ((MakeHeader f):_) = Just f
> outputHeader (_:xs) = outputHeader xs
> doTrace opts | elem Trace opts = " -DTRACEON"
>              | otherwise = ""
> -- |Compile a source file in supercombinator language to a .o
> compile :: FilePath -- ^ Input file name
>            -> FilePath -- ^ Output file name
>            -> Maybe FilePath -- ^ Interface (.ei) file name, if desired
>            -> IO ()
> compile fn outf iface
>     = compileOpts fn outf iface []
Chop off everything after the last / - get the directory a file is in
> trimLast f = case span (\x -> x /= '/') (reverse f) of
>                 (eman, htap) -> reverse htap
> compileOpts :: FilePath -- ^ Input file name
>            -> FilePath -- ^ Output file name
>            -> Maybe FilePath -- ^ Interface (.ei) file name, if desired
>            -> [CompileOptions] -- Keep the C file
>            -> IO ()
> compileOpts fn outf iface opts
>     = do input <- readFile fn
>          -- prelude <- readFile (libdir ++ "/Prelude.e")
>          let s = parse input fn
>          case s of
>              Failure err _ _ -> fail err
>              Success ds -> do
>                 compileDecls outf iface ds opts
> compileDecls :: FilePath -- ^ Output file name
>              -> Maybe FilePath -- ^ Interface (.ei) file name, if desired
>              -> [Decl] -- ^ Declarations
>              -> [CompileOptions]
>              -> IO ()
> compileDecls outf iface ds opts
>     = do (tmpn,tmph) <- tempfile
>          let hdr = outputHeader opts
>          scchecked <- checkAll opts ds
>          let simplified = simplifyAll scchecked
>          checked <- docompileDecls simplified tmph hdr
>          fp <- getDataFileName "evm/closure.h"
>          let libdir = trimLast fp
>          let dbg = if (elem Debug opts) then "-g" else "-O3"
>          let cmd = "gcc -DUSE_BOEHM -c " ++ dbg ++ " -foptimize-sibling-calls -x c " ++ tmpn ++ " -I" ++ libdir ++ " -o " ++ outf ++ " " ++ addGCC opts ++ doTrace opts
>          -- putStrLn $ cmd
>          -- putStrLn $ fp
>          exit <- system cmd
>          if (elem KeepC opts)
>             then do system $ "cp " ++ tmpn ++ " " ++ 
>                                (getRoot outf) ++ ".c"
>                     return ()
>             else return ()
>          -- removeFile tmpn
>          if (exit /= ExitSuccess) 
>             then fail $ "gcc failed"
>             else return ()
>          case iface of
>             Nothing -> return ()
>             (Just fn) -> do writeFile fn (writeIFace checked)
> getRoot fn = case span (/='.') fn of
>     (stem,_) -> stem
> docompileDecls (ctxt, decls) outh hdr
>     = do hPutStr outh $ codegenC ctxt decls
>          case hdr of
>              Just fpath ->
>                   do let hout = codegenH (filter isAlpha (map toUpper (getRoot fpath))) decls
>                      writeFile fpath hout
>              Nothing -> return ()
>          hFlush outh
>          hClose outh
>          return decls
> getExtra :: [CompileOptions] -> IO [String]
> getExtra ((MainInc x):xs) = do fns <- getExtra xs
>                                return (x:fns)
> getExtra (_:xs) = getExtra xs
> getExtra [] = return []
> -- |Link a collection of .o files into an executable
> link :: [FilePath] -- ^ Object files
>         -> FilePath -- ^ Executable filename
>         -> [CompileOptions] -- Keep the C file
>         -> IO ()
> link infs outf opts = do
>     extraIncs <- getExtra opts
>     mainprog <- if (not (elem ExternalMain opts)) then mkMain extraIncs else return ""
>     fp <- getDataFileName "evm/closure.h"
>     let libdir = trimLast fp
>     let dbg = if (elem Debug opts) then "-g" else "-O3"
>     let cmd = "gcc -DUSE_BOEHM -x c " ++ dbg ++ " -foptimize-sibling-calls " ++ mainprog ++ " -x none -L" ++
>               libdir++" -I"++libdir ++ " " ++
>               (concat (map (++" ") infs)) ++ 
>               " -levm -lgc -lpthread -lgmp -o "++outf ++ " " ++ addGCC opts
>     -- putStrLn $ cmd
>     exit <- system cmd
>     if (exit /= ExitSuccess)
>        then fail $ "Linking failed"
>        else return ()
Output the main progam, adding any extra includes needed. (Some libraries need the extra includes, notably SDL, to compile correctly. Grr.)
> mkMain :: [FilePath] -> IO FilePath
> mkMain extra = 
>    do mppath <- getDataFileName "evm/mainprog.c"
>       mp <- readFile mppath
>       (tmp, tmpH) <- tempfile
>       hPutStr tmpH (concat (map (\x -> "#include <" ++ x ++ ">\n") extra))
>       hPutStr tmpH mp
>       hClose tmpH
>       return tmp
-- |Get the path where the required C libraries and include files are stored libdir :: FilePath libdir = libprefix ++ "/lib/evm"