on fecc96d help sign in
elk/Setup.hs
165 lines 6.0 kB history raw
1import Distribution.Simple
2import Distribution.Simple.LocalBuildInfo
3import Distribution.Simple.Setup
4import Distribution.PackageDescription
6import System.Cmd
7import System.Directory
8import System.FilePath
9import System.IO
10import System.Process
12import Control.Monad
13import qualified Data.Binary as Binary
14import qualified Data.ByteString.Lazy as BS
16-- Part 1
17-- ------
18-- Add a build callout
19-- We need to build elm-doc and run it because that generates the file "docs.json"
20-- needs by Libraries.hs which is part of the elm library and executable
21-- Unfort. there seems to be no way to tell cabal that:
22-- (a) elm-doc generates docs.json, and
23-- (b) elm (library) depends on docs.json
24-- Therefore, we either use make instead (or a script), or hack around in cabal
26-- Part 2
27-- ------
28-- Add a post-build callout.
29-- We need to build the runtime.js after we've built elm (because we use elm to generate some of the JavaScript),
30-- but before cabal does the install file copy step
32-- Assumptions
33-- Elm.cabal expects the generated files to end up in dist/data
34-- git won't look in dist + cabal will clean it
35rtsDir :: LocalBuildInfo -> FilePath
36rtsDir lbi = "data"
38tempDir :: LocalBuildInfo -> FilePath
39tempDir lbi = "temp"
41-- The runtime is called:
42rts :: LocalBuildInfo -> FilePath
43rts lbi = rtsDir lbi </> "elm-runtime.js"
45-- The interfaces for the Standard Libraries live in:
46interfaces :: LocalBuildInfo -> FilePath
47interfaces lbi = rtsDir lbi </> "interfaces.data"
49-- buildDir with LocalBuildInfo points to "dist/build" (usually)
50elm lbi = buildDir lbi </> "elm" </> "elm"
52-- Care! This appears to be based on an unstable API
53-- See: http://www.haskell.org/cabal/release/cabal-latest/doc/API/Cabal/Distribution-Simple.html#2
56main :: IO ()
57main = defaultMainWithHooks simpleUserHooks { postBuild = myPostBuild }
60-- Build
62-- note(1): We use to include docs.json directly into LoadLibraries at compile time
63-- If docs.json is used in other (template) haskell files, they should be copied
64-- and compiled in a separate directory (eg, dist/copiedSrc).
65-- This is to make sure they are re-compiled on docs.json changes.
66-- Copying is a better solution than 'touch'ing the source files
67-- (touch is non-portable and confusing wrt RCS).
69-- In the PackageDescription, the list of stuff to build is held in library
70-- (in a Maybe) and the executables list. We want a PackageDescription that
71-- only mentions the executable 'name'
72filterExe :: String -> PackageDescription -> PackageDescription
73filterExe name pd = pd {
74 library = Nothing,
75 executables = filter (\x -> (exeName x == name)) (executables pd)
76 }
78-- It's not enough to fix the PackageDescription, we also have to fix the
79-- LocalBuildInfo. This includes the component build order (data ComponentName)
80-- which is horribly internal.
81filterLBI :: String -> LocalBuildInfo -> LocalBuildInfo
82filterLBI name lbi = lbi {
83 libraryConfig = Nothing,
84 compBuildOrder = [CExeName name],
85 executableConfigs = filter (\a -> (fst a == name)) (executableConfigs lbi)
86 }
89-- Post Build
91myPostBuild :: Args -> BuildFlags -> PackageDescription -> LocalBuildInfo -> IO ()
92myPostBuild as bfs pd lbi = do
93 putStrLn "Custom build step: compiling standard libraries"
94 (elmos, elmis) <- compileLibraries lbi
95 putStrLn "Custom build step: build interfaces.data"
96 buildInterfaces lbi elmis
97 putStrLn "Custom build step: build elm-runtime.js"
98 buildRuntime lbi elmos
99 removeDirectoryRecursive (tempDir lbi)
100 postBuild simpleUserHooks as bfs pd lbi
103compileLibraries lbi = do
104 let temp = tempDir lbi -- temp
105 rts = rtsDir lbi -- data
106 createDirectoryIfMissing True temp
107 createDirectoryIfMissing True rts
108 out_c <- canonicalizePath temp -- temp (root folder)
109 elm_c <- canonicalizePath (elm lbi) -- dist/build/elm/elm
110 rtd_c <- canonicalizePath rts -- data
112 let make file = do
113 -- replace 'system' call with 'runProcess' which handles args better
114 -- and allows env variable "Elm_datadir" which is used by LoadLibraries
115 -- to find docs.json
116 let args = [ "--only-js", "--make", "--no-prelude"
117 , "--cache-dir="++out_c, "--build-dir="++out_c, file ]
118 arg = Just [("Elm_datadir", rtd_c)]
119 handle <- runProcess elm_c args Nothing arg Nothing Nothing Nothing
120 exitCode <- waitForProcess handle
121 return ( out_c </> replaceExtension file "elmo"
122 , out_c </> replaceExtension file "elmi")
124 setCurrentDirectory "libraries"
125 print =<< getCurrentDirectory
126 files <- getFiles ".elm" "."
127 files <- unzip `fmap` mapM make files
128 setCurrentDirectory ".."
129 return files
131buildInterfaces :: LocalBuildInfo -> [FilePath] -> IO ()
132buildInterfaces lbi elmis = do
133 createDirectoryIfMissing True (rtsDir lbi)
134 let ifaces = interfaces lbi
135 ifaceHandle <- openBinaryFile ifaces WriteMode
136 BS.hPut ifaceHandle (Binary.encode (length elmis))
137 let append file = do
138 handle <- openBinaryFile file ReadMode
139 bits <- hGetContents handle
140 length bits `seq` hPutStr ifaceHandle bits
141 hClose handle
142 mapM_ append elmis
143 hClose ifaceHandle
145buildRuntime :: LocalBuildInfo -> [FilePath] -> IO ()
146buildRuntime lbi elmos = do
147 createDirectoryIfMissing True (rtsDir lbi)
148 writeFile (rts lbi) "var Elm = {}; Elm.Native = {}; Elm.Native.Graphics = {};\n\
149 \var ElmRuntime = {}; ElmRuntime.Render = {};\n"
150 mapM_ (appendJS lbi) =<< getFiles ".js" "libraries"
151 mapM_ (appendJS lbi) elmos
152 mapM_ (appendJS lbi) =<< getFiles ".js" "runtime"
154getFiles ext dir = do
155 contents <- map (dir </>) `fmap` getDirectoryContents dir
156 let files = filter (\f -> takeExtension f == ext) contents
157 dirs = filter (not . hasExtension) contents
158 filess <- mapM (getFiles ext) dirs
159 return (files ++ concat filess)
161appendJS lbi file = do
162 putStrLn (dropExtension file)
163 str <- readFile file
164 length str `seq` return ()
165 appendFile (rts lbi) str