on dcbe2fc help sign in
elk/Setup.hs
179 lines 6.4 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 }
87-- Post Build
89myPostBuild :: Args -> BuildFlags -> PackageDescription -> LocalBuildInfo -> IO ()
90myPostBuild as bfs pd lbi = do
91 putStrLn "Custom build step: compiling standard libraries"
92 (elmos, elmis) <- compileLibraries lbi
93 putStrLn "Custom build step: build interfaces.data"
94 buildInterfaces lbi elmis
95 putStrLn "Custom build step: build elm-runtime.js"
96 buildRuntime lbi elmos
97 putStrLn "Custom build step: build docs.json"
98 buildDocs lbi
99 removeDirectoryRecursive (tempDir lbi)
100 removeDirectoryRecursive ("libraries" </> "docs")
101 postBuild simpleUserHooks as bfs pd lbi
104compileLibraries lbi = do
105 let temp = tempDir lbi -- temp
106 rts = rtsDir lbi -- data
107 createDirectoryIfMissing True temp
108 createDirectoryIfMissing True rts
109 out_c <- canonicalizePath temp -- temp (root folder)
110 elm_c <- canonicalizePath (elm lbi) -- dist/build/elm/elm
111 doc_c <- canonicalizePath (document lbi) -- dist/build/elm-doc/elm-doc
112 rtd_c <- canonicalizePath rts -- data
114 let make file = do
115 -- replace 'system' call with 'runProcess' which handles args better
116 -- and allows env variable "Elm_datadir" which is used by LoadLibraries
117 -- to find docs.json
118 let args = [ "--only-js", "--make", "--no-prelude"
119 , "--cache-dir="++out_c, "--build-dir="++out_c, file ]
120 arg = Just [("Elm_datadir", rtd_c)]
121 handle <- runProcess elm_c args Nothing arg Nothing Nothing Nothing
122 exitCode <- waitForProcess handle
123 return ( out_c </> replaceExtension file "elmo"
124 , out_c </> replaceExtension file "elmi")
126 setCurrentDirectory "libraries"
127 paths <- getFiles ".elm" "."
128 files <- unzip `fmap` mapM make paths
129 mapM_ (\path -> rawSystem doc_c [path]) paths
130 setCurrentDirectory ".."
131 return files
133buildInterfaces :: LocalBuildInfo -> [FilePath] -> IO ()
134buildInterfaces lbi elmis = do
135 createDirectoryIfMissing True (rtsDir lbi)
136 let ifaces = interfaces lbi
137 ifaceHandle <- openBinaryFile ifaces WriteMode
138 BS.hPut ifaceHandle (Binary.encode (length elmis))
139 let append file = do
140 handle <- openBinaryFile file ReadMode
141 bits <- hGetContents handle
142 length bits `seq` hPutStr ifaceHandle bits
143 hClose handle
144 mapM_ append elmis
145 hClose ifaceHandle
147buildRuntime :: LocalBuildInfo -> [FilePath] -> IO ()
148buildRuntime lbi elmos = do
149 createDirectoryIfMissing True (rtsDir lbi)
150 let rts' = rts lbi
151 writeFile rts' "var Elm = {}; Elm.Native = {}; Elm.Native.Graphics = {};\n\
152 \var ElmRuntime = {}; ElmRuntime.Render = {};\n"
153 mapM_ (appendTo rts') =<< getFiles ".js" "libraries"
154 mapM_ (appendTo rts') elmos
155 mapM_ (appendTo rts') =<< getFiles ".js" "runtime"
157buildDocs :: LocalBuildInfo -> IO ()
158buildDocs lbi = do
159 createDirectoryIfMissing True (rtsDir lbi)
160 let docs' = docs lbi
161 writeFile docs' "[\n"
162 json <- getFiles ".json" ("libraries" </> "docs")
163 let appends = map (appendTo docs') json
164 addCommas = List.intersperse (appendFile docs' ",\n")
165 sequence_ (addCommas appends)
166 appendFile docs' "\n]"
168getFiles ext dir = do
169 contents <- map (dir </>) `fmap` getDirectoryContents dir
170 let files = filter (\f -> takeExtension f == ext) contents
171 dirs = filter (not . hasExtension) contents
172 filess <- mapM (getFiles ext) dirs
173 return (files ++ concat filess)
175appendTo :: FilePath -> FilePath -> IO ()
176appendTo destination source = do
177 str <- readFile source
178 length str `seq` return ()
179 appendFile destination str