on master help sign in
Fix cabal install missing docs.json (followup)
175f268ngunn
Elm.cabal−2
@@ -33,8 +33,6 @@ Library
33 exposed-modules: Language.Elm,
33 exposed-modules: Language.Elm,
34 Language.Elm.Quasi
34 Language.Elm.Quasi
35 Hs-Source-Dirs: compiler, compiler/Gen, compiler/Model, compiler/Transform
35 Hs-Source-Dirs: compiler, compiler/Gen, compiler/Model, compiler/Transform
36− extensions: CPP
37− cpp-options: -DELM_COMPILEDATADIR="dist/data"
38 other-modules: Ast,
36 other-modules: Ast,
39 Context,
37 Context,
40 CompileToJS,
38 CompileToJS,
Setup.hs+28−12
@@ -7,6 +7,7 @@ import System.Cmd
7import System.Directory
7import System.Directory
8import System.FilePath
8import System.FilePath
9import System.IO
9import System.IO
10+import System.Process
10
11
11import Control.Monad
12import Control.Monad
12
13
@@ -29,6 +30,7 @@ import Control.Monad
29-- Assumptions
30-- Assumptions
30-- Elm.cabal expects the generated files to end up in dist/data
31-- Elm.cabal expects the generated files to end up in dist/data
31rtsDir lbi = (buildDir lbi) </> ".." </> "data" -- git won't look in dist + cabal will clean it
32rtsDir lbi = (buildDir lbi) </> ".." </> "data" -- git won't look in dist + cabal will clean it
33+jsDir lbi = (buildDir lbi) </> ".." </> "js"
32
34
33-- The runtime is called:
35-- The runtime is called:
34rts lbi = (rtsDir lbi) </> "elm-runtime.js"
36rts lbi = (rtsDir lbi) </> "elm-runtime.js"
@@ -44,13 +46,6 @@ types lbi = (rtsDir lbi) </> "docs.json"
44-- buildDir with LocalBuildInfo points to "dist/build" (usually)
46-- buildDir with LocalBuildInfo points to "dist/build" (usually)
45elm lbi = (buildDir lbi) </> "elm" </> "elm"
47elm lbi = (buildDir lbi) </> "elm" </> "elm"
46
48
47−-- "touch" is available as a system command.
48−-- There is no portable file touch (unix-compat might work but leads to cabal library dependency
49−-- issues: see: http://hackage.haskell.org/trac/summer-of-code/ticket/1602 )
50−-- Files that need recompilation after docs.json is updated
51−-- (Would be nice if this list was in the cabal file, but can't see any way to do that)
52−docTouches = ["compiler" </> "Model" </> "LoadLibraries.hs"]
53−
54-- Care! This appears to be based on an unstable API
49-- Care! This appears to be based on an unstable API
55-- See: http://www.haskell.org/cabal/release/cabal-latest/doc/API/Cabal/Distribution-Simple.html#2
50-- See: http://www.haskell.org/cabal/release/cabal-latest/doc/API/Cabal/Distribution-Simple.html#2
56
51
@@ -67,11 +62,17 @@ myBuild pd lbi uh bf = do
67 withExe pd (\x -> putStrLn (exeName x))
62 withExe pd (\x -> putStrLn (exeName x))
68 buildHook simpleUserHooks (filterExe elmDoc pd) (filterLBI elmDoc lbi) uh bf
63 buildHook simpleUserHooks (filterExe elmDoc pd) (filterLBI elmDoc lbi) uh bf
69 putStrLn "Custom build step started: build docs.json"
64 putStrLn "Custom build step started: build docs.json"
70− buildTypes lbi
65+ buildTypes lbi -- see note(1) below
71− forM_ docTouches (\s -> system ("touch " ++ s)) -- NB non-portable
72 putStrLn "Custom build step started: compile everything"
66 putStrLn "Custom build step started: compile everything"
73 buildHook simpleUserHooks pd lbi uh bf
67 buildHook simpleUserHooks pd lbi uh bf
74
68
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).
75+
75-- In the PackageDescription, the list of stuff to build is held in library (in a Maybe)
76-- In the PackageDescription, the list of stuff to build is held in library (in a Maybe)
76-- and the executables list. We want a PackageDescription that only mentions the executable 'name'
77-- and the executables list. We want a PackageDescription that only mentions the executable 'name'
77filterExe name pd = pd {
78filterExe name pd = pd {
@@ -116,10 +117,25 @@ appendJS lbi file = do
116 appendFile (rts lbi) str
117 appendFile (rts lbi) str
117
118
118appendElm lbi file = do
119appendElm lbi file = do
119− system (elm lbi ++ " --only-js " ++ file)
120+ jsFile <- runElm lbi file
120− let jsFile = replaceExtension file ".js"
121 appendJS lbi jsFile
121 appendJS lbi jsFile
122− removeFile jsFile
122+
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
125+runElm :: LocalBuildInfo -> String -> IO FilePath
126+runElm 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"
138+
123
139
124buildRuntime lbi = do
140buildRuntime lbi = do
125 createDirectoryIfMissing False (rtsDir lbi) -- dist should already exist
141 createDirectoryIfMissing False (rtsDir lbi) -- dist should already exist
compiler/Model/Libraries.hs+2−4
@@ -1,10 +1,8 @@
1−{-# LANGUAGE TemplateHaskell #-}
2−
3module Libraries (libraries) where
1module Libraries (libraries) where
4
2
5import qualified Data.Map as Map
3import qualified Data.Map as Map
6import Text.JSON
4import Text.JSON
7−import LoadLibraries
5+import LoadLibraries (docs)
8
6
9libraries :: Map.Map String (Map.Map String String)
7libraries :: Map.Map String (Map.Map String String)
10libraries = case getLibs of
8libraries = case getLibs of
@@ -13,7 +11,7 @@ libraries = case getLibs of
13
11
14getLibs :: Result (Map.Map String (Map.Map String String))
12getLibs :: Result (Map.Map String (Map.Map String String))
15getLibs = do
13getLibs = do
16− obj <- decodeStrict "{\"modules\":[]}" -- $(docs) :: Result (JSObject JSValue)
14+ obj <- decodeStrict docs :: Result (JSObject JSValue)
17 modules <- valFromObj "modules" obj :: Result [JSObject JSValue]
15 modules <- valFromObj "modules" obj :: Result [JSObject JSValue]
18 Map.fromList `fmap` mapM getValues modules
16 Map.fromList `fmap` mapM getValues modules
19
17
compiler/Model/LoadLibraries.hs+21−21
@@ -1,36 +1,36 @@
1−{-# LANGUAGE CPP #-}
2−
3module LoadLibraries (docs) where
1module LoadLibraries (docs) where
4
2
5−import Language.Haskell.TH.Syntax
3+import Control.DeepSeq (force)
4+import qualified Control.Exception as E
6import Paths_Elm
5import Paths_Elm
7import System.Directory
6import System.Directory
8import System.FilePath
7import System.FilePath
8+import System.IO.Unsafe (unsafePerformIO)
9+
10+-- See stackoverflow discussion for trade-off between using unsafeIO or TemplateHaskell:
11+-- http://stackoverflow.com/questions/12716215/load-pure-global-variable-from-file
9
12
10−docs :: Q Exp
13+-- Given the awkwardness of including a compile-time generated file
11−docs = liftString =<< qRunIO (readDocs dataDir)
14+-- vs loading static data, then the unsafeIO seems better.
12
15
13−-- When compiling elm (library and executable), docs.json isn't (yet) in the standard location
16+{-# NOINLINE docs #-}
14−-- Therefore, Elm.cabal overrides the location.
17+docs :: String
15−-- Since this is only used by the TemplateHaskell, which reads the file at compile time,
18+docs = force . unsafePerformIO . safeReadDocs . getDataFileName $ "docs.json"
16−-- this should be ok.
17
19
18−dataDir :: IO FilePath
20+safeReadDocs :: IO FilePath -> IO String
19−#if defined ELM_COMPILEDATADIR
21+safeReadDocs path = E.catch (readDocs path) (emitError path)
20−dataDir = canonicalizePath (ELM_COMPILEDATADIR </> "docs.json")
21−#else
22−dataDir = getDataFileName "docs.json"
23−#endif
24
22
25readDocs :: IO FilePath -> IO String
23readDocs :: IO FilePath -> IO String
26readDocs path = do
24readDocs path = do
27 name <- path
25 name <- path
28 exists <- doesFileExist name
26 exists <- doesFileExist name
29 if exists then readFile name
27 if exists then readFile name
30− else putStrLn warning >> return "{\"modules\":[]}"
28+ else ioError . userError $ "File Not Found"
29+
31
30
31+emitError :: IO FilePath -> IOError -> IO String
32+emitError path err = do
33+ name <- path
32−warning = "Warning! Types for standard library not loaded properly!\n\
34+ putStrLn $ "Error! Types for standard library not loaded properly!\n File should be here:" ++ name ++ "\n The file is created and copied by command: cabal install"
33− Run the following commands after compilation:\n\
35+ putStrLn (show err)
34− \n\
36+ return "{\"modules\":[]}"
35− touch compiler/Model/LoadLibraries.hs\n\
36− cabal install\n\n"