on c2eecc2 help sign in
elk/Setup.hs
189 lines 6.9 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
15import qualified Data.List as List
17-- Part 1
18-- ------
19-- Add a build callout
20-- We need to build elm-doc and run it because that generates the file "docs.json"
21-- needs by Libraries.hs which is part of the elm library and executable
22-- Unfort. there seems to be no way to tell cabal that:
23-- (a) elm-doc generates docs.json, and
24-- (b) elm (library) depends on docs.json
25-- Therefore, we either use make instead (or a script), or hack around in cabal
27-- Part 2
28-- ------
29-- Add a post-build callout.
30-- We need to build the runtime.js after we've built elm (because we use elm to generate some of the JavaScript),
31-- but before cabal does the install file copy step
33-- Assumptions
34-- Elm.cabal expects the generated files to end up in dist/data
35-- git won't look in dist + cabal will clean it
36rtsDir :: LocalBuildInfo -> FilePath
37rtsDir lbi = "data"
39tempDir :: LocalBuildInfo -> FilePath
40tempDir lbi = "temp"
42-- The runtime is called:
43rts :: LocalBuildInfo -> FilePath
44rts lbi = rtsDir lbi </> "elm-runtime.js"
46-- The runtime is called:
47docs :: LocalBuildInfo -> FilePath
48docs lbi = rtsDir lbi </> "docs.json"
50-- The interfaces for the Standard Libraries live in:
51interfaces :: LocalBuildInfo -> FilePath
52interfaces lbi = rtsDir lbi </> "interfaces.data"
54elm :: LocalBuildInfo -> FilePath
55elm lbi = buildDir lbi </> "elm" </> "elm"
57document :: LocalBuildInfo -> FilePath
58document lbi = buildDir lbi </> "elm-doc" </> "elm-doc"
60-- Care! This appears to be based on an unstable API
61-- See: http://www.haskell.org/cabal/release/cabal-latest/doc/API/Cabal/Distribution-Simple.html#2
64main :: IO ()
65main = defaultMainWithHooks simpleUserHooks { postBuild = myPostBuild }
68-- Build
70-- note(1): We use to include docs.json directly into LoadLibraries at compile time
71-- If docs.json is used in other (template) haskell files, they should be copied
72-- and compiled in a separate directory (eg, dist/copiedSrc).
73-- This is to make sure they are re-compiled on docs.json changes.
74-- Copying is a better solution than 'touch'ing the source files
75-- (touch is non-portable and confusing wrt RCS).
77-- In the PackageDescription, the list of stuff to build is held in library
78-- (in a Maybe) and the executables list. We want a PackageDescription that
79-- only mentions the executable 'name'
80filterExe :: String -> PackageDescription -> PackageDescription
81filterExe name pd = pd {
82 library = Nothing,
83 executables = filter (\x -> (exeName x == name)) (executables pd)
84 }
86-- It's not enough to fix the PackageDescription, we also have to fix the
87-- LocalBuildInfo. This includes the component build order (data ComponentName)
88-- which is horribly internal.
89filterLBI :: String -> LocalBuildInfo -> LocalBuildInfo
90filterLBI name lbi = lbi {
91 libraryConfig = Nothing,
92 compBuildOrder = [CExeName name],
93 executableConfigs = filter (\a -> (fst a == name)) (executableConfigs lbi)
94 }
97-- Post Build
99myPostBuild :: Args -> BuildFlags -> PackageDescription -> LocalBuildInfo -> IO ()
100myPostBuild as bfs pd lbi = do
101 putStrLn "Custom build step: compiling standard libraries"
102 (elmos, elmis) <- compileLibraries lbi
103 putStrLn "Custom build step: build interfaces.data"
104 buildInterfaces lbi elmis
105 putStrLn "Custom build step: build elm-runtime.js"
106 buildRuntime lbi elmos
107 putStrLn "Custom build step: build docs.json"
108 buildDocs lbi
109 removeDirectoryRecursive (tempDir lbi)
110 removeDirectoryRecursive ("libraries" </> "docs")
111 postBuild simpleUserHooks as bfs pd lbi
114compileLibraries lbi = do
115 let temp = tempDir lbi -- temp
116 rts = rtsDir lbi -- data
117 createDirectoryIfMissing True temp
118 createDirectoryIfMissing True rts
119 out_c <- canonicalizePath temp -- temp (root folder)
120 elm_c <- canonicalizePath (elm lbi) -- dist/build/elm/elm
121 doc_c <- canonicalizePath (document lbi) -- dist/build/elm-doc/elm-doc
122 rtd_c <- canonicalizePath rts -- data
124 let make file = do
125 -- replace 'system' call with 'runProcess' which handles args better
126 -- and allows env variable "Elm_datadir" which is used by LoadLibraries
127 -- to find docs.json
128 let args = [ "--only-js", "--make", "--no-prelude"
129 , "--cache-dir="++out_c, "--build-dir="++out_c, file ]
130 arg = Just [("Elm_datadir", rtd_c)]
131 handle <- runProcess elm_c args Nothing arg Nothing Nothing Nothing
132 exitCode <- waitForProcess handle
133 return ( out_c </> replaceExtension file "elmo"
134 , out_c </> replaceExtension file "elmi")
136 setCurrentDirectory "libraries"
137 paths <- getFiles ".elm" "."
138 files <- unzip `fmap` mapM make paths
139 mapM_ (\path -> rawSystem doc_c [path]) paths
140 setCurrentDirectory ".."
141 return files
143buildInterfaces :: LocalBuildInfo -> [FilePath] -> IO ()
144buildInterfaces lbi elmis = do
145 createDirectoryIfMissing True (rtsDir lbi)
146 let ifaces = interfaces lbi
147 ifaceHandle <- openBinaryFile ifaces WriteMode
148 BS.hPut ifaceHandle (Binary.encode (length elmis))
149 let append file = do
150 handle <- openBinaryFile file ReadMode
151 bits <- hGetContents handle
152 length bits `seq` hPutStr ifaceHandle bits
153 hClose handle
154 mapM_ append elmis
155 hClose ifaceHandle
157buildRuntime :: LocalBuildInfo -> [FilePath] -> IO ()
158buildRuntime lbi elmos = do
159 createDirectoryIfMissing True (rtsDir lbi)
160 let rts' = rts lbi
161 writeFile rts' "var Elm = {}; Elm.Native = {}; Elm.Native.Graphics = {};\n\
162 \var ElmRuntime = {}; ElmRuntime.Render = {};\n"
163 mapM_ (appendTo rts') =<< getFiles ".js" "libraries"
164 mapM_ (appendTo rts') elmos
165 mapM_ (appendTo rts') =<< getFiles ".js" "runtime"
167buildDocs :: LocalBuildInfo -> IO ()
168buildDocs lbi = do
169 createDirectoryIfMissing True (rtsDir lbi)
170 let docs' = docs lbi
171 writeFile docs' "[\n"
172 json <- getFiles ".json" ("libraries" </> "docs")
173 let appends = map (appendTo docs') json
174 addCommas = List.intersperse (appendFile docs' ",\n")
175 sequence_ (addCommas appends)
176 appendFile docs' "\n]"
178getFiles ext dir = do
179 contents <- map (dir </>) `fmap` getDirectoryContents dir
180 let files = filter (\f -> takeExtension f == ext) contents
181 dirs = filter (not . hasExtension) contents
182 filess <- mapM (getFiles ext) dirs
183 return (files ++ concat filess)
185appendTo :: FilePath -> FilePath -> IO ()
186appendTo destination source = do
187 str <- readFile source
188 length str `seq` return ()
189 appendFile destination str