@@ -1,225 +1,68 @@
1−{-# LANGUAGE DeriveDataTypeable #-}
2module Main where
1module Main where
4−import Control.Monad
3+import Control.Monad (foldM)
5import qualified Data.Map as Map
4import qualified Data.Map as Map
6−import qualified Data.List as List
5+import qualified Data.Maybe as Maybe
6+import Text.Blaze.Html.Renderer.String (renderHtml)
7−import qualified Data.Binary as Binary
7+import qualified Data.ByteString.Lazy.Char8 as BS
8−import Data.Version (showVersion)
9−import System.Console.CmdArgs hiding (program)
8+import qualified System.Console.CmdArgs as CmdArgs
10import System.Directory
9import System.Directory
12import System.FilePath
10import System.FilePath
14import GHC.Conc
11import GHC.Conc
16−import Text.Blaze.Html.Renderer.Pretty (renderHtml)
13+import Build.Dependencies (getSortedDependencies)
17−import qualified Data.ByteString.Lazy.Char8 as BS
14+import qualified Generate.Html as Html
18−import qualified Data.ByteString.Lazy as L
20import qualified Metadata.Prelude as Prelude
15import qualified Metadata.Prelude as Prelude
21−import qualified Transform.Canonicalize as Canonical
16+import qualified Build.Utils as Utils
22−import SourceSyntax.Module
23−import Parse.Module (getModuleName)
24−import Initialize (buildFromSource, getSortedDependencies)
25−import Generate.JavaScript (jsModule)
26−import Generate.Html (createHtml, JSSource(..))
27−import qualified InterfaceSerialization as IS
17+import qualified Build.Flags as Flag
30−import SourceSyntax.PrettyPrint (pretty, variable)
31−import Text.PrettyPrint as P
32−import qualified Type.Type as Type
18+import qualified Build.File as File
33−import qualified Type.Alias as Alias
19+import qualified Elm.Internal.Paths as Path
37− , files :: [FilePath]
38− , runtime :: Maybe FilePath
40− , print_types :: Bool
41− , scripts :: [FilePath]
43− , cache_dir :: FilePath
44− , build_dir :: FilePath
45− , src_dir :: [FilePath]
47− deriving (Data,Typeable,Show,Eq)
50− { files = def &= args &= typ "FILES"
52− &= help "automatically compile dependencies."
54− &= help "Compile only to JavaScript."
56− &= help "Do not import Prelude by default, used only when compiling standard libraries."
57− , scripts = [] &= typFile
58− &= help "Load JavaScript files in generated HTML. Files will be included in the given order."
59− , runtime = Nothing &= typFile
60− &= help "Specify a custom location for Elm's runtime system."
61− , cache_dir = "cache" &= typFile
62− &= help "Directory for files cached to make builds faster. Defaults to cache/ directory."
63− , build_dir = "build" &= typFile
64− &= help "Directory for generated HTML and JS files. Defaults to build/ directory."
65− , src_dir = ["."] &= typFile
66− &= help "Additional source directories besides project root. Searched when using --make"
67− , print_types = False
68− &= help "Print out infered types of top-level definitions."
69− } &= help "Compile Elm programs to HTML, CSS, and JavaScript."
70− &= helpArg [explicit, name "help", name "h"]
71− &= versionArg [explicit, name "version", name "v", summary (showVersion version)]
72− &= summary ("The Elm Compiler " ++ showVersion version ++ ", (c) Evan Czaplicki 2011-2013")
74main :: IO ()
21main :: IO ()
75main = do setNumCapabilities =<< getNumProcessors
22main = do setNumCapabilities =<< getNumProcessors
76− compileArgs =<< cmdArgs flags
23+ compileArgs =<< CmdArgs.cmdArgs Flag.flags
78−compileArgs :: Flags -> IO ()
25+compileArgs :: Flag.Flags -> IO ()
79compileArgs flags =
26compileArgs flags =
80− case files flags of
27+ case Flag.files flags of
81 [] -> putStrLn "Usage: elm [OPTIONS] [FILES]\nFor more help: elm --help"
28 [] -> putStrLn "Usage: elm [OPTIONS] [FILES]\nFor more help: elm --help"
82 fs -> mapM_ (build flags) fs
29 fs -> mapM_ (build flags) fs
31+build :: Flag.Flags -> FilePath -> IO ()
32+build flags rootFile =
33+ do let noPrelude = Flag.no_prelude flags
34+ builtIns <- if noPrelude then return Map.empty else Prelude.interfaces
85−buildPath :: Flags -> FilePath -> String -> FilePath
86−buildPath flags filePath ext = build_dir flags </> replaceExtension filePath ext
88−cachePath :: Flags -> FilePath -> String -> FilePath
89−cachePath flags filePath ext = cache_dir flags </> replaceExtension filePath ext
91−elmo :: Flags -> FilePath -> FilePath
92−elmo flags filePath = cachePath flags filePath "elmo"
94−elmi :: Flags -> FilePath -> FilePath
95−elmi flags filePath = cachePath flags filePath "elmi"
97−buildFile :: Flags -> Int -> Int -> Interfaces -> FilePath -> IO (String, ModuleInterface)
98−buildFile flags moduleNum numModules interfaces filePath = do
99− compiled <- alreadyCompiled
100− if not compiled then compile else do
101− bytes <- IS.loadInterface (elmi flags filePath)
102− let binary = IS.interfaceDecode (elmi flags filePath) =<< bytes
103− case IS.validVersion filePath =<< binary of
105− hPutStrLn stderr err
108− Right (name, interface) -> do
109− when (print_types flags) $
110− printTypes interfaces
112− (iAliases interface)
113− (iImports interface)
115− return (name, interface)
118− alreadyCompiled :: IO Bool
119− alreadyCompiled = do
120− existsi <- doesFileExist (elmi flags filePath)
121− existso <- doesFileExist (elmo flags filePath)
122− if not existsi || not existso
124− else do tsrc <- getModificationTime filePath
125− tint <- getModificationTime (elmo flags filePath)
126− return (tsrc <= tint)
129− number = "[" ++ show moduleNum ++ " of " ++ show numModules ++ "]"
131− compile :: IO (String,ModuleInterface)
133− source <- readFile filePath
134− let name = case getModuleName source of
137− putStrLn $ concat [ number, " Compiling ", name
138− , replicate (max 1 (20 - length name)) ' '
139− , "( " ++ filePath ++ " )" ]
141− createDirectoryIfMissing True (cache_dir flags)
142− createDirectoryIfMissing True (build_dir flags)
145− case buildFromSource (no_prelude flags) interfaces source of
147− mapM print (List.intersperse (P.text " ") errors)
149− Right modul -> return modul
151− when (print_types flags)
152− (printTypes interfaces
153− (types metaModule) (aliases metaModule) (imports metaModule))
155− let interface = Canonical.interface name $ ModuleInterface {
156− iVersion = showVersion version,
157− iTypes = types metaModule,
158− iImports = imports metaModule,
159− iAdts = datatypes metaModule,
160− iAliases = aliases metaModule,
161− iFixities = fixities metaModule
164− createDirectoryIfMissing True . dropFileName $ elmi flags filePath
165− handle <- openBinaryFile (elmi flags filePath) WriteMode
166− L.hPut handle (Binary.encode (name,interface))
168− writeFile (elmo flags filePath) (jsModule metaModule)
169− return (name,interface)
171−printTypes interfaces moduleTypes moduleAliases moduleImports = do
173− let rules = Alias.rules interfaces moduleAliases moduleImports
174− forM_ (Map.toList moduleTypes) $ \(n,t) -> do
175− print $ variable n <+> P.text ":" <+> pretty (Alias.realias rules t)
178−getRuntime :: Flags -> IO FilePath
180− case runtime flags of
181− Just fp -> return fp
182− Nothing -> getDataFileName "elm-runtime.js"
184−build :: Flags -> FilePath -> IO ()
185−build flags rootFile = do
186− let noPrelude = no_prelude flags
187− builtIns <- if noPrelude then return Map.empty else Prelude.interfaces
189− files <- if make flags
36+ files <- if Flag.make flags
190− then getSortedDependencies (src_dir flags) builtIns rootFile
37+ then getSortedDependencies (Flag.src_dir flags) builtIns rootFile
191− else return [rootFile]
38+ else return [rootFile]
40+ (moduleName, interfaces) <-
193− (moduleName, interfaces) <- buildFiles flags (length files) builtIns "" files
41+ File.build flags (length files) builtIns "" files
195− js <- foldM appendToOutput BS.empty files
43+ js <- foldM appendToOutput BS.empty files
197− (extension, code) <- case only_js flags of
45+ (extension, code) <-
198− True -> do
46+ if Flag.only_js flags
199− putStr "Generating JavaScript ... "
47+ then do putStr "Generating JavaScript ... "
200− return ("js", js)
48+ return ("js", js)
202− putStr "Generating HTML ... "
49+ else do putStr "Generating HTML ... "
203− rtsPath <- getRuntime flags
50+ return (makeHtml js moduleName)
204− return ("html", BS.pack . renderHtml $
205− createHtml rtsPath (takeBaseName rootFile) (sources js) moduleName "")
207− let targetFile = buildPath flags rootFile extension
52+ let targetFile = Utils.buildPath flags rootFile extension
208− createDirectoryIfMissing True (takeDirectory targetFile)
53+ createDirectoryIfMissing True (takeDirectory targetFile)
209− BS.writeFile targetFile code
54+ BS.writeFile targetFile code
210− putStrLn "Done"
55+ putStrLn "Done"
213 appendToOutput :: BS.ByteString -> FilePath -> IO BS.ByteString
58 appendToOutput :: BS.ByteString -> FilePath -> IO BS.ByteString
214− appendToOutput js filePath =
59+ appendToOutput js filePath = do
215− do src <- BS.readFile (elmo flags filePath)
60+ src <- BS.readFile (Utils.elmo flags filePath)
216− return (BS.append src js)
61+ return (BS.append src js)
218− sources js = map Link (scripts flags) ++ [ Source js ]
63+ sources js = map Html.Link (Flag.scripts flags) ++ [ Html.Source js ]
220−buildFiles :: Flags -> Int -> Interfaces -> String -> [FilePath] -> IO (String, Interfaces)
65+ makeHtml js moduleName = ("html", BS.pack $ renderHtml html)
221−buildFiles _ _ interfaces moduleName [] = return (moduleName, interfaces)
66+ where
222−buildFiles flags numModules interfaces _ (filePath:rest) = do
67+ rtsPath = Maybe.fromMaybe Path.runtime (Flag.runtime flags)
223− (name,interface) <- buildFile flags (numModules - length rest) numModules interfaces filePath
68+ html = Html.generate rtsPath (takeBaseName rootFile) (sources js) moduleName ""
224− let interfaces' = Map.insert name interface interfaces
225− buildFiles flags numModules interfaces' name rest