packages feed

ddc-war-0.4.1.1: DDC/War/Job/CompileDC.hs

module DDC.War.Job.CompileDC
        ( Spec     (..)
        , Fragment (..)
        , Result   (..)
        , resultSuccess
        , build)
where
import BuildBox.Command.File
import BuildBox.Command.System
import BuildBox.Build.Benchmark
import BuildBox.Data.Physical
import BuildBox.IO.Directory
import BuildBox.Pretty
import BuildBox
import System.FilePath
import qualified System.FilePath.Posix as P
import System.Directory
import Control.Monad
import Data.List


-- | Use ddci-core to compile/make file a DCE file
data Spec
        = Spec
        { -- | Root source file of the program (the 'Main.ds')
          specFile               :: FilePath 
                       
          -- | Extra DDC options for building in this way.
        , specOptionsDDC         :: [String]

          -- | Language fragment.
        , specFragment           :: Fragment

          -- | Scratch dir to do the build in.
        , specScratchDir         :: String

           -- | Put what DDC says to stdout here.
        , specCompileStdout      :: FilePath
                
           -- | Put what DDC says to stderr here.
        , specCompileStderr      :: FilePath 

          -- | If Just, then we're making an executable, and put the binary here.
          --   Otherwise simply compile it
        , specMaybeMainBin       :: Maybe FilePath 
                
          -- | True if the compile is expected to succeed, else not.
        , specShouldSucceed      :: Bool }
        deriving Show


-- | Language fragments that we can compile.
data Fragment
        = FragmentSalt          -- File.dce
        | FragmentLite          -- File.dcl
        deriving Show


-- | Result of a compilation test.
data Result
        = ResultSuccess Seconds
        | ResultUnexpectedFailure
        | ResultUnexpectedSuccess
        deriving Show


resultSuccess :: Result -> Bool
resultSuccess result
 = case result of
        ResultSuccess{} -> True
        _               -> False


instance Pretty Result where
 ppr result
  = case result of 
        ResultSuccess seconds   -> text "success" <+> parens (ppr seconds)
        ResultUnexpectedFailure -> text "failed"
        ResultUnexpectedSuccess -> text "unexpected"


-- Build ----------------------------------------------------------------------
-- | Compile a Disciple Core Sea source file.
build :: Spec -> Build Result
build   (Spec   srcDC_ optionsDDC _fragment 
                buildDir mainCompOut mainCompErr
                mMainBin shouldSucceed)

 = do   let ddcExe = "bin/ddc" <.> exe

        needs srcDC_
        needs ddcExe

        ddcBin  <- io $ canonicalizePath ddcExe

        -- Normalise the file name relative to the current directory
        -- so that error messages don't change between hosts.
        srcDC'  <- io $ makeRelativeToCurrentDirectory srcDC_

        -- Make the file path use consistent path separators across all hosts.
        let srcDC = flip map srcDC' $
                \c -> if isPathSeparator c
                        then P.pathSeparator
                        else c

        -- The directory holding the Main.dce file.
        let (srcDir, _srcFile)  = splitFileName srcDC
                
        -- Touch the .dce and .dcl files to the build directory to ensure they're built.
        sources <- io
                $  liftM (filter (\f -> isSuffixOf ".dcs" f || isSuffixOf ".dcl" f))
                $  lsFilesIn srcDir

        ssystemq $ "touch " ++ (intercalate " " sources)

        -- ensure the output directory exists
        ensureDir buildDir

        -- Do the compile.
        let compile
                | Just mainBin  <- mMainBin
                = do    -- If there is an existing binary then remove it.
                        ssystemq $ "rm -f " ++ mainBin

                        -- Build the program.
                        timeBuild
                         $ systemTee False 
                                (ddcBin
                                ++ " "               ++ intercalate " " optionsDDC
                                ++ " -output "       ++ mainBin
                                ++ " -output-dir "   ++ buildDir
                                ++ " -make "         ++ srcDC)
                                ""

                -- Compile the program.
                | otherwise
                = do    timeBuild
                         $ systemTee False
                                (ddcBin
                                ++ " "               ++ intercalate " " optionsDDC
                                ++ " -output-dir "   ++ buildDir
                                ++ " -compile "      ++ srcDC)
                                ""
        (time, (code, strOut, strErr))
                <- compile

        atomicWriteFile mainCompOut strOut
        atomicWriteFile mainCompErr strErr

        case code of
         ExitFailure _
          | shouldSucceed       -> return ResultUnexpectedFailure
        
         ExitSuccess
          | not shouldSucceed   -> return ResultUnexpectedSuccess

         _                      -> return $ ResultSuccess time