on fa132ea help sign in
elk/Setup.hs
154 lines 5.7 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 createDirectoryIfMissing True temp
105 out_c <- canonicalizePath temp -- dist/temp (root folder)
106 elm_c <- canonicalizePath (elm lbi) -- dist/build/elm/elm
107 rtd_c <- canonicalizePath (rtsDir lbi) -- dist/data (for docs.json)
109 let make file = do
110 -- replace 'system' call with 'runProcess' which handles args better
111 -- and allows env variable "Elm_datadir" which is used by LoadLibraries
112 -- to find docs.json
113 let args = ["--only-js","--make","--no-prelude","--output-directory="++out_c,file]
114 arg = Just [("Elm_datadir", rtd_c)]
115 handle <- runProcess elm_c args Nothing arg Nothing Nothing Nothing
116 exitCode <- waitForProcess handle
117 return ( out_c </> replaceExtension file "elmo"
118 , out_c </> replaceExtension file "elmi")
120 setCurrentDirectory "libraries"
121 print =<< getCurrentDirectory
122 files <- getFiles ".elm" "."
123 files <- unzip `fmap` mapM make files
124 setCurrentDirectory ".."
125 return files
127buildInterfaces :: LocalBuildInfo -> [FilePath] -> IO ()
128buildInterfaces lbi elmis = do
129 createDirectoryIfMissing True (rtsDir lbi)
130 let ifaces = interfaces lbi
131 Binary.encodeFile ifaces (length elmis)
132 mapM_ (\elmi -> readFile elmi >>= appendFile ifaces) elmis
134buildRuntime :: LocalBuildInfo -> [FilePath] -> IO ()
135buildRuntime lbi elmos = do
136 createDirectoryIfMissing True (rtsDir lbi)
137 writeFile (rts lbi) "Elm = {}; Elm.Native = {}; Elm.Native.Graphics = {};\n\
138 \Elm.Graphics = {}; ElmRuntime = {}; ElmRuntime.Render = {};\n"
139 mapM_ (appendJS lbi) =<< getFiles ".js" "libraries"
140 mapM_ (appendJS lbi) elmos
141 mapM_ (appendJS lbi) =<< getFiles ".js" "runtime"
143getFiles ext dir = do
144 contents <- map (dir </>) `fmap` getDirectoryContents dir
145 let files = filter (\f -> takeExtension f == ext) contents
146 dirs = filter (not . hasExtension) contents
147 filess <- mapM (getFiles ext) dirs
148 return (files ++ concat filess)
150appendJS lbi file = do
151 putStrLn (dropExtension file)
152 str <- readFile file
153 length str `seq` return ()
154 appendFile (rts lbi) str