on 79093ff help sign in
elk/Setup.hs
149 lines 5.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
14-- Part 1
15-- ------
16-- Add a build callout
17-- We need to build elm-doc and run it because that generates the file "docs.json" needs by Libraries.hs
18-- which is part of the elm library and executable
19-- Unfort. there seems to be no way to tell cabal that:
20-- (a) elm-doc generates docs.json, and
21-- (b) elm (library) depends on docs.json
22-- Therefore, we either use make instead (or a script), or hack around in cabal
24-- Part 2
25-- ------
26-- Add a post-build callout.
27-- We need to build the runtime.js after we've built elm (because we use elm to generate some of the JavaScript),
28-- but before cabal does the install file copy step
30-- Assumptions
31-- Elm.cabal expects the generated files to end up in dist/data
32rtsDir lbi = (buildDir lbi) </> ".." </> "data" -- git won't look in dist + cabal will clean it
33jsDir lbi = (buildDir lbi) </> ".." </> "js"
35-- The runtime is called:
36rts lbi = (rtsDir lbi) </> "elm-runtime.js"
38-- The json file is called:
40-- The elm-docs executable is called:
41elmDoc = "elm-doc"
42elm_doc lbi = (buildDir lbi) </> elmDoc </> elmDoc
44types lbi = (rtsDir lbi) </> "docs.json"
46-- buildDir with LocalBuildInfo points to "dist/build" (usually)
47elm lbi = (buildDir lbi) </> "elm" </> "elm"
49-- Care! This appears to be based on an unstable API
50-- See: http://www.haskell.org/cabal/release/cabal-latest/doc/API/Cabal/Distribution-Simple.html#2
53main :: IO ()
54main = defaultMainWithHooks simpleUserHooks { buildHook = myBuild, postBuild = myPostBuild }
57-- Build
59myBuild :: PackageDescription -> LocalBuildInfo -> UserHooks -> BuildFlags -> IO ()
60myBuild pd lbi uh bf = do
61 putStrLn $ "Custom build step started: compile " ++ elmDoc
62 withExe pd (\x -> putStrLn (exeName x))
63 buildHook simpleUserHooks (filterExe elmDoc pd) (filterLBI elmDoc lbi) uh bf
64 putStrLn "Custom build step started: build docs.json"
65 buildTypes lbi -- see note(1) below
66 putStrLn "Custom build step started: compile everything"
67 buildHook simpleUserHooks pd lbi uh bf
69-- note(1): We use to include docs.json directly into LoadLibraries at compile time
70-- If docs.json is used in other (template) haskell files, they should be copied
71-- and compiled in a separate directory (eg, dist/copiedSrc).
72-- This is to make sure they are re-compiled on docs.json changes.
73-- Copying is a better solution than 'touch'ing the source files
74-- (touch is non-portable and confusing wrt RCS).
76-- In the PackageDescription, the list of stuff to build is held in library (in a Maybe)
77-- and the executables list. We want a PackageDescription that only mentions the executable 'name'
78filterExe name pd = pd {
79 library = Nothing,
80 executables = filter (\x -> (exeName x == name)) (executables pd)
81 }
83-- It's not enough to fix the PackageDescription, we also have to fix the LocalBuildInfo.
84-- This includes the component build order (data ComponentName) which is horribly internal.
85filterLBI name lbi = lbi {
86 libraryConfig = Nothing,
87 compBuildOrder = [CExeName name],
88 executableConfigs = filter (\a -> (fst a == name)) (executableConfigs lbi)
89 }
91buildTypes lbi = do
92 createDirectoryIfMissing False (rtsDir lbi) -- dist should already exist
93 files <- getFiles ".elm" "libraries"
94 system (elm_doc lbi ++ " " ++ unwords files ++ " > " ++ (types lbi))
95 putStrLn $ "Custom build step completed: " ++ elmDoc
98-- Post Build
100myPostBuild :: Args -> BuildFlags -> PackageDescription -> LocalBuildInfo -> IO ()
101myPostBuild as bfs pd lbi = do
102 putStrLn "Custom post build step started: build elm-runtime.js"
103 buildRuntime lbi
104 postBuild simpleUserHooks as bfs pd lbi
106getFiles ext dir = do
107 contents <- map (dir </>) `fmap` getDirectoryContents dir
108 let files = filter (\f -> takeExtension f == ext) contents
109 dirs = filter (not . hasExtension) contents
110 filess <- mapM (getFiles ext) dirs
111 return (files ++ concat filess)
113appendJS lbi file = do
114 putStrLn (dropExtension file)
115 str <- readFile file
116 length str `seq` return ()
117 appendFile (rts lbi) str
119appendElm lbi file = do
120 jsFile <- runElm lbi file
121 appendJS lbi jsFile
123-- replace 'system' call with 'runProcess' which handles args better and allows env variable
124-- "Elm_datadir" which is used by LoadLibraries to find docs.json
125runElm :: LocalBuildInfo -> String -> IO FilePath
126runElm lbi file = do
127 rts_c <- canonicalizePath (rts lbi) -- dist/data/elm-runtime.js
128 let js = jsDir lbi -- dist/js
129 let j = dropFileName (js </> file) -- dist/js/libraries/
130 createDirectoryIfMissing True j -- must do before any canonicalization
131 out_c <- canonicalizePath js -- dist/js (root folder)
132 elm_c <- canonicalizePath (elm lbi) -- dist/build/elm/elm
133 rtd_c <- canonicalizePath (rtsDir lbi) -- dist/data (for docs.json)
134 handle <- runProcess elm_c ["--only-js", "--runtime="++rts_c, "--output-directory="++out_c, file]
135 Nothing (Just [("Elm_datadir", rtd_c)]) Nothing Nothing Nothing
136 waitForProcess handle
137 return $ j </> replaceExtension (takeFileName file) ".js"
140buildRuntime lbi = do
141 createDirectoryIfMissing False (rtsDir lbi) -- dist should already exist
142 writeFile (rts lbi) "Elm = {}; Elm.Native = {}; Elm.Native.Graphics = {};\n\
143 \Elm.Graphics = {}; ElmRuntime = {}; ElmRuntime.Render = {}\n"
144 mapM_ (appendJS lbi) =<< getFiles ".js" "libraries"
145 mapM_ (appendElm lbi) =<< getFiles ".elm" "libraries"
146 mapM_ (appendJS lbi) =<< getFiles ".js" "runtime"
147 putStrLn "\n+------------------------------------------+\
148 \\n| Success building runtime and libraries! |\
149 \\n+------------------------------------------+\n"