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