-- |Netrium is Copyright Anthony Waite, Dave Hetwett, Shaun Laurens 2009-2015, and files herein are licensed -- |under the MIT license, the text of which can be found in license.txt -- module Main where import ObservableDB import UnitsDB import Paths_netrium_demo import Control.Monad (liftM, liftM2, when) import Data.Version import System.Environment (getArgs, getProgName) import System.Exit (exitFailure, exitWith, ExitCode(..)) import System.Directory (getTemporaryDirectory, canonicalizePath, removeFile) import System.IO (openTempFile, hPutStr, hClose) import System.Process (runProcess, waitForProcess) import System.FilePath ((), dropExtension, addExtension, takeDirectory) import System.Console.GetOpt import Text.XML.HaXml.XmlContent data Options = Options { optObsDBs :: [FilePath] , optUnitDBs :: [FilePath] , optImportDirs :: [FilePath] , optFast :: Bool , optVersion :: Bool } defaultOptions = Options { optObsDBs = [] , optUnitDBs = [] , optImportDirs = [] , optFast = False , optVersion = False } options :: [OptDescr (Options -> Options)] options = [Option [] ["obs-db"] (ReqArg (\ db opts -> opts { optObsDBs = db : optObsDBs opts }) "") "use the observable database " ,Option [] ["units-db"] (ReqArg (\ db opts -> opts { optUnitDBs = db : optUnitDBs opts }) "") "use the units (products, currencies etc) database " ,Option [] ["import-dir"] (ReqArg (\ db opts -> opts { optImportDirs = db : optImportDirs opts }) "DIR") "Allow contracts to import modules from this directory" ,Option [] ["fast"] (NoArg (\ opts -> opts { optFast = True })) "Generate the output XML quickly but without any nice formatting" ,Option [] ["version"] (NoArg (\ opts -> opts { optVersion = True })) "Print version information" ] main :: IO () main = do plainArgs <- getArgs let (optMods, args, errs) = getOpt Permute options plainArgs let opts = foldl (flip ($)) defaultOptions optMods case args of _ | optVersion opts -> printVersion [input] | null errs -> normalise opts input output where output = addExtension input "xml" [input, output] | null errs -> normalise opts input output _ -> exit exit :: IO () exit = do p <- getProgName let txt = "Usage: " ++ p ++ " [options] []\n\n" ++ "Flags:" putStrLn $ usageInfo txt options exitFailure printVersion :: IO () printVersion = do p <- getProgName putStrLn $ "netrium-demo " ++ p ++ " version " ++ showVersion version getObservableDBs :: Options -> IO [ObservableDB] getObservableDBs = mapM fReadXml . optObsDBs getUnitsDBs :: Options -> IO [UnitsDB] getUnitsDBs = mapM fReadXml . optUnitDBs normalise :: Options -> FilePath -> FilePath -> IO () normalise opts input output = do tdir <- getTemporaryDirectory let cdir = case takeDirectory input of "" -> "."; dir -> dir -- read and process the various input files obsDBs <- getObservableDBs opts unitsDBs <- getUnitsDBs opts wrapper <- readFile =<< getDataFileName "normalise-wrapper.hs" contract <- readFile input absOutput <- canonicalizePath output -- write the temporary source file (fp, h) <- openTempFile tdir "norm.hs" hPutStr h $ generateContractProgram (optFast opts) obsDBs unitsDBs wrapper "normalise-wrapper.hs" contract input absOutput hClose h -- compile and run it ddir <- getDataDir let ghcargs = [ "-package", "netrium-demo-" ++ showVersion version ] ++ [ "-i" ++ dir | dir <- ddir : optImportDirs opts ++ [cdir] ] args = map ("--ghc-arg="++) ghcargs ++ [fp] ph <- runProcess "runghc" args Nothing Nothing Nothing Nothing Nothing exit <- waitForProcess ph removeFile fp when (exit /= ExitSuccess) exitFailure generateContractProgram :: Bool -> [ObservableDB] -> [UnitsDB] -> String -> FilePath -> String -> FilePath -> FilePath -> String generateContractProgram fast obsDBs unitsDBs wrapper wrapperFile contract contractFile outputFile = unlines [ "-- This is a generated file; do not edit.\n" , "{-# LINE 1 " ++ show wrapperFile ++ " #-}" , wrapper , "{-# LINE 1 " ++ show contractFile ++ " #-}" , contract , "{-# LINE 1 \"observables database\" #-}" , unlines (map compileObservableDB obsDBs) , "{-# LINE 1 \"units database\" #-}" , unlines (map compileUnitsDB unitsDBs) , "{-# LINE 1 \"generated contract program\" #-}" , "entrypoint :: Contract" , "entrypoint = contract" , "main = writeFile " ++ show outputFile ++ " $ " ++ outputCode ] where outputCode | fast = "PP.renderStyle PP.style { PP.mode = PP.OneLineMode } $ " ++ "XML.PP.document $ XML.toXml False entrypoint" | otherwise = "XML.showXml False entrypoint"