@@ -48,14 +48,18 @@ import Control.Monad.Writer.Strict
48import Control.Monad.Supply
48import Control.Monad.Supply
50import Data.Function (on)
50import Data.Function (on)
51+import Data.Either (partitionEithers)
51−import Data.List (sortBy, groupBy)
52+import Data.List (sortBy, groupBy, foldl')
52import Data.Maybe (fromMaybe)
53import Data.Maybe (fromMaybe)
53import Data.Time.Clock
54import Data.Time.Clock
55+import Data.String (fromString)
54import Data.Foldable (for_)
56import Data.Foldable (for_)
55#if __GLASGOW_HASKELL__ < 710
57#if __GLASGOW_HASKELL__ < 710
56import Data.Traversable (traverse)
58import Data.Traversable (traverse)
58import Data.Version (showVersion)
60import Data.Version (showVersion)
61+import Data.Aeson (encode, decode)
62+import qualified Data.ByteString.Lazy as B
59import qualified Data.Map as M
63import qualified Data.Map as M
60import qualified Data.Set as S
64import qualified Data.Set as S
@@ -65,14 +69,13 @@ import System.FilePath ((</>), takeDirectory)
65import System.IO.Error (tryIOError)
69import System.IO.Error (tryIOError)
67import Language.PureScript.AST
71import Language.PureScript.AST
68−import Language.PureScript.CodeGen.Externs (moduleToPs)
72+import Language.PureScript.Externs
69import Language.PureScript.Environment
73import Language.PureScript.Environment
70import Language.PureScript.Errors
74import Language.PureScript.Errors
71import Language.PureScript.Linter
75import Language.PureScript.Linter
72import Language.PureScript.ModuleDependencies
76import Language.PureScript.ModuleDependencies
73import Language.PureScript.Names
77import Language.PureScript.Names
74import Language.PureScript.Options
78import Language.PureScript.Options
75−import Language.PureScript.Parser
76import Language.PureScript.Pretty
79import Language.PureScript.Pretty
77import Language.PureScript.Renamer
80import Language.PureScript.Renamer
78import Language.PureScript.Sugar
81import Language.PureScript.Sugar
@@ -116,7 +119,7 @@ data MakeActions m = MakeActions {
117 -- Read the externs file for a module as a string and also return the actual
120 -- Read the externs file for a module as a string and also return the actual
118 -- path for the file.
121 -- path for the file.
119− , readExterns :: ModuleName -> m (FilePath, String)
122+ , readExterns :: ModuleName -> m (FilePath, B.ByteString)
121 -- Run the code generator for the module and write any required output files.
124 -- Run the code generator for the module and write any required output files.
@@ -130,7 +133,7 @@ data MakeActions m = MakeActions {
131-- Generated code for an externs file.
134-- Generated code for an externs file.
133−type Externs = String
136+type Externs = B.ByteString
136-- Determines when to rebuild a module
139-- Determines when to rebuild a module
@@ -161,18 +164,16 @@ make MakeActions{..} ms = do
161 (Left RebuildNever, Just _) -> s
164 (Left RebuildNever, Just _) -> s
162 _ -> S.insert moduleName' s) S.empty sorted
165 _ -> S.insert moduleName' s) S.empty sorted
164− marked <- rebuildIfNecessary (reverseDependencies graph) toRebuild sorted
167+ (externs, toBuild) <- partitionEithers <$> rebuildIfNecessary (reverseDependencies graph) toRebuild sorted
165− for_ marked $ \(willRebuild, m) -> when willRebuild (lint m)
168+ for_ toBuild lint
166− (desugared, nextVar) <- runSupplyT 0 $ zip (map fst marked) <$> desugar (map snd marked)
169+ (desugared, nextVar) <- runSupplyT 0 $ desugar externs toBuild
170+ let env = foldl' (flip applyExternsFileToEnvironment) initEnvironment externs
167− evalSupplyT nextVar $ go initEnvironment desugared
171+ evalSupplyT nextVar $ go env desugared
170− go :: Environment -> [(Bool, Module)] -> SupplyT m Environment
174+ go :: Environment -> [Module] -> SupplyT m Environment
171 go env [] = return env
175 go env [] = return env
172− go env ((False, m) : ms') = do
173− (_, env') <- lift . runCheck' env $ typeCheckModule Nothing m
175− go env ((True, m@(Module ss coms moduleName' _ exps)) : ms') = do
176+ go env (m@(Module ss coms moduleName' _ exps) : ms') = do
176 lift . progress $ CompilingModule moduleName'
177 lift . progress $ CompilingModule moduleName'
177 (checked@(Module _ _ _ elaborated _), env') <- lift . runCheck' env $ typeCheckModule Nothing m
178 (checked@(Module _ _ _ elaborated _), env') <- lift . runCheck' env $ typeCheckModule Nothing m
178 checkExhaustiveModule env' checked
179 checkExhaustiveModule env' checked
@@ -180,27 +181,28 @@ make MakeActions{..} ms = do
180 let mod' = Module ss coms moduleName' regrouped exps
181 let mod' = Module ss coms moduleName' regrouped exps
181 corefn = CF.moduleToCoreFn env' mod'
182 corefn = CF.moduleToCoreFn env' mod'
182 [renamed] = renameInModules [corefn]
183 [renamed] = renameInModules [corefn]
183− exts = moduleToPs mod' env'
184+ exts = encode $ moduleToExternsFile mod' env'
184 codegen renamed env' exts
185 codegen renamed env' exts
185 go env' ms'
186 go env' ms'
187− rebuildIfNecessary :: M.Map ModuleName [ModuleName] -> S.Set ModuleName -> [Module] -> m [(Bool, Module)]
188+ rebuildIfNecessary :: M.Map ModuleName [ModuleName] -> S.Set ModuleName -> [Module] -> m [Either ExternsFile Module]
188− rebuildIfNecessary _ _ [] = return []
189− rebuildIfNecessary graph toRebuild (m@(Module _ _ moduleName' _ _) : ms') | moduleName' `S.member` toRebuild = do
190− let deps = fromMaybe [] $ moduleName' `M.lookup` graph
191− toRebuild' = toRebuild `S.union` S.fromList deps
192− (:) (True, m) <$> rebuildIfNecessary graph toRebuild' ms'
193− rebuildIfNecessary graph toRebuild (Module _ _ moduleName' _ _ : ms') = do
189+ rebuildIfNecessary graph = rebuildIfNecessary'
194− (path, externs) <- readExterns moduleName'
195− externsModules <- fmap (map snd) . alterErrors $ parseModulesFromFiles id [(path, externs)]
196− case externsModules of
197− [m'@(Module _ _ moduleName'' _ _)] | moduleName'' == moduleName' -> (:) (False, m') <$> rebuildIfNecessary graph toRebuild ms'
198− _ -> throwError . errorMessage . InvalidExternsFile $ path
200− alterErrors = flip catchError $ \(MultipleErrors errs) ->
191+ rebuildIfNecessary' :: S.Set ModuleName -> [Module] -> m [Either ExternsFile Module]
201− throwError . MultipleErrors $ flip map errs $ \e -> case e of
192+ rebuildIfNecessary' _ [] = return []
202− SimpleErrorWrapper (ErrorParsingModule err) -> SimpleErrorWrapper (ErrorParsingExterns err)
193+ rebuildIfNecessary' toRebuild (m@(Module _ _ moduleName' _ _) : ms')
203− _ -> e
194+ | moduleName' `S.member` toRebuild = rebuild toRebuild m moduleName' ms'
195+ rebuildIfNecessary' toRebuild (m@(Module _ _ moduleName' _ _) : ms') = do
196+ (_, externsJson) <- readExterns moduleName'
197+ case decode externsJson of
199+ | efVersion externs == showVersion Paths.version -> (Left externs :) <$> rebuildIfNecessary' toRebuild ms'
200+ _ -> rebuild toRebuild m moduleName' ms'
202+ rebuild :: S.Set ModuleName -> Module -> ModuleName -> [Module] -> m [Either ExternsFile Module]
203+ rebuild toRebuild m moduleName ms' = do
204+ let deps = fromMaybe [] $ moduleName `M.lookup` graph
205+ (Right m :) <$> rebuildIfNecessary' (toRebuild `S.union` S.fromList deps) ms'
205reverseDependencies :: ModuleGraph -> M.Map ModuleName [ModuleName]
207reverseDependencies :: ModuleGraph -> M.Map ModuleName [ModuleName]
206reverseDependencies g = combine [ (dep, mn) | (mn, deps) <- g, dep <- deps ]
208reverseDependencies g = combine [ (dep, mn) | (mn, deps) <- g, dep <- deps ]
@@ -268,12 +270,12 @@ buildMakeActions outputDir filePathMap foreigns usePrefix =
268 getOutputTimestamp mn = do
270 getOutputTimestamp mn = do
269 let filePath = runModuleName mn
271 let filePath = runModuleName mn
270 jsFile = outputDir </> filePath </> "index.js"
272 jsFile = outputDir </> filePath </> "index.js"
271− externsFile = outputDir </> filePath </> "externs.purs"
273+ externsFile = outputDir </> filePath </> "externs.json"
272 min <$> getTimestamp jsFile <*> getTimestamp externsFile
274 min <$> getTimestamp jsFile <*> getTimestamp externsFile
274− readExterns :: ModuleName -> Make (FilePath, String)
276+ readExterns :: ModuleName -> Make (FilePath, B.ByteString)
275 readExterns mn = do
277 readExterns mn = do
276− let path = outputDir </> runModuleName mn </> "externs.purs"
278+ let path = outputDir </> runModuleName mn </> "externs.json"
277 (path, ) <$> readTextFile path
279 (path, ) <$> readTextFile path
279 codegen :: CF.Module CF.Ann -> Environment -> Externs -> SupplyT Make ()
281 codegen :: CF.Module CF.Ann -> Environment -> Externs -> SupplyT Make ()
@@ -290,12 +292,12 @@ buildMakeActions outputDir filePathMap foreigns usePrefix =
290 pjs <- prettyPrintJS <$> J.moduleToJs m foreignInclude
292 pjs <- prettyPrintJS <$> J.moduleToJs m foreignInclude
291 let filePath = runModuleName mn
293 let filePath = runModuleName mn
292 jsFile = outputDir </> filePath </> "index.js"
294 jsFile = outputDir </> filePath </> "index.js"
293− externsFile = outputDir </> filePath </> "externs.purs"
295+ externsFile = outputDir </> filePath </> "externs.json"
294 foreignFile = outputDir </> filePath </> "foreign.js"
296 foreignFile = outputDir </> filePath </> "foreign.js"
295 prefix = ["Generated by psc version " ++ showVersion Paths.version | usePrefix]
297 prefix = ["Generated by psc version " ++ showVersion Paths.version | usePrefix]
296 js = unlines $ map ("// " ++) prefix ++ [pjs]
298 js = unlines $ map ("// " ++) prefix ++ [pjs]
297 lift $ do
299 lift $ do
298− writeTextFile jsFile js
300+ writeTextFile jsFile (fromString js)
299 for_ (mn `M.lookup` foreigns) (readTextFile >=> writeTextFile foreignFile)
301 for_ (mn `M.lookup` foreigns) (readTextFile >=> writeTextFile foreignFile)
300 writeTextFile externsFile exts
302 writeTextFile externsFile exts
@@ -307,13 +309,13 @@ buildMakeActions outputDir filePathMap foreigns usePrefix =
307 exists <- doesFileExist path
309 exists <- doesFileExist path
308 traverse (const $ getModificationTime path) $ guard exists
310 traverse (const $ getModificationTime path) $ guard exists
310− readTextFile :: FilePath -> Make String
312+ readTextFile :: FilePath -> Make B.ByteString
311− readTextFile path = makeIO (const (SimpleErrorWrapper $ CannotReadFile path)) $ readFile path
313+ readTextFile path = makeIO (const (SimpleErrorWrapper $ CannotReadFile path)) $ B.readFile path
313− writeTextFile :: FilePath -> String -> Make ()
315+ writeTextFile :: FilePath -> B.ByteString -> Make ()
314 writeTextFile path text = makeIO (const (SimpleErrorWrapper $ CannotWriteFile path)) $ do
316 writeTextFile path text = makeIO (const (SimpleErrorWrapper $ CannotWriteFile path)) $ do
315 mkdirp path
317 mkdirp path
316− writeFile path text
318+ B.writeFile path text
318 mkdirp :: FilePath -> IO ()
320 mkdirp :: FilePath -> IO ()
319 mkdirp = createDirectoryIfMissing True . takeDirectory
321 mkdirp = createDirectoryIfMissing True . takeDirectory