@@ -48,14 +48,18 @@ import Control.Monad.Writer.Strict
4848import Control.Monad.Supply
4949
5050import Data.Function (on)
51−import Data.List (sortBy, groupBy)
51+import Data.Either (partitionEithers)
52+import Data.List (sortBy, groupBy, foldl')
5253import Data.Maybe (fromMaybe)
5354import Data.Time.Clock
55+import Data.String (fromString)
5456import Data.Foldable (for_)
5557#if __GLASGOW_HASKELL__ < 710
5658import Data.Traversable (traverse)
5759#endif
5860import Data.Version (showVersion)
61+import Data.Aeson (encode, decode)
62+import qualified Data.ByteString.Lazy as B
5963import qualified Data.Map as M
6064import qualified Data.Set as S
6165
@@ -65,14 +69,13 @@ import System.FilePath ((</>), takeDirectory)
6569import System.IO.Error (tryIOError)
6670
6771import Language.PureScript.AST
68−import Language.PureScript.CodeGen.Externs (moduleToPs)
72+import Language.PureScript.Externs
6973import Language.PureScript.Environment
7074import Language.PureScript.Errors
7175import Language.PureScript.Linter
7276import Language.PureScript.ModuleDependencies
7377import Language.PureScript.Names
7478import Language.PureScript.Options
75−import Language.PureScript.Parser
7679import Language.PureScript.Pretty
7780import Language.PureScript.Renamer
7881import Language.PureScript.Sugar
@@ -116,7 +119,7 @@ data MakeActions m = MakeActions {
116119 -- |
117120 -- Read the externs file for a module as a string and also return the actual
118121 -- path for the file.
119− , readExterns :: ModuleName -> m (FilePath, String)
122+ , readExterns :: ModuleName -> m (FilePath, B.ByteString)
120123 -- |
121124 -- Run the code generator for the module and write any required output files.
122125 --
@@ -130,7 +133,7 @@ data MakeActions m = MakeActions {
130133-- |
131134-- Generated code for an externs file.
132135--
133−type Externs = String
136+type Externs = B.ByteString
134137
135138-- |
136139-- Determines when to rebuild a module
@@ -161,18 +164,16 @@ make MakeActions{..} ms = do
161164 (Left RebuildNever, Just _) -> s
162165 _ -> S.insert moduleName' s) S.empty sorted
163166
164− marked <- rebuildIfNecessary (reverseDependencies graph) toRebuild sorted
165− for_ marked $ \(willRebuild, m) -> when willRebuild (lint m)
166− (desugared, nextVar) <- runSupplyT 0 $ zip (map fst marked) <$> desugar (map snd marked)
167− evalSupplyT nextVar $ go initEnvironment desugared
167+ (externs, toBuild) <- partitionEithers <$> rebuildIfNecessary (reverseDependencies graph) toRebuild sorted
168+ for_ toBuild lint
169+ (desugared, nextVar) <- runSupplyT 0 $ desugar externs toBuild
170+ let env = foldl' (flip applyExternsFileToEnvironment) initEnvironment externs
171+ evalSupplyT nextVar $ go env desugared
168172 where
169173
170− go :: Environment -> [(Bool, Module)] -> SupplyT m Environment
174+ go :: Environment -> [Module] -> SupplyT m Environment
171175 go env [] = return env
172− go env ((False, m) : ms') = do
173− (_, env') <- lift . runCheck' env $ typeCheckModule Nothing m
174− go env' ms'
175− go env ((True, m@(Module ss coms moduleName' _ exps)) : ms') = do
176+ go env (m@(Module ss coms moduleName' _ exps) : ms') = do
176177 lift . progress $ CompilingModule moduleName'
177178 (checked@(Module _ _ _ elaborated _), env') <- lift . runCheck' env $ typeCheckModule Nothing m
178179 checkExhaustiveModule env' checked
@@ -180,27 +181,28 @@ make MakeActions{..} ms = do
180181 let mod' = Module ss coms moduleName' regrouped exps
181182 corefn = CF.moduleToCoreFn env' mod'
182183 [renamed] = renameInModules [corefn]
183− exts = moduleToPs mod' env'
184+ exts = encode $ moduleToExternsFile mod' env'
184185 codegen renamed env' exts
185186 go env' ms'
186187
187− rebuildIfNecessary :: M.Map ModuleName [ModuleName] -> S.Set ModuleName -> [Module] -> m [(Bool, 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
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
188+ rebuildIfNecessary :: M.Map ModuleName [ModuleName] -> S.Set ModuleName -> [Module] -> m [Either ExternsFile Module]
189+ rebuildIfNecessary graph = rebuildIfNecessary'
199190 where
200− alterErrors = flip catchError $ \(MultipleErrors errs) ->
201− throwError . MultipleErrors $ flip map errs $ \e -> case e of
202− SimpleErrorWrapper (ErrorParsingModule err) -> SimpleErrorWrapper (ErrorParsingExterns err)
203− _ -> e
191+ rebuildIfNecessary' :: S.Set ModuleName -> [Module] -> m [Either ExternsFile Module]
192+ rebuildIfNecessary' _ [] = return []
193+ rebuildIfNecessary' toRebuild (m@(Module _ _ moduleName' _ _) : ms')
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
198+ Just externs
199+ | efVersion externs == showVersion Paths.version -> (Left externs :) <$> rebuildIfNecessary' toRebuild ms'
200+ _ -> rebuild toRebuild m moduleName' ms'
201+
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'
204206
205207reverseDependencies :: ModuleGraph -> M.Map ModuleName [ModuleName]
206208reverseDependencies g = combine [ (dep, mn) | (mn, deps) <- g, dep <- deps ]
@@ -268,12 +270,12 @@ buildMakeActions outputDir filePathMap foreigns usePrefix =
268270 getOutputTimestamp mn = do
269271 let filePath = runModuleName mn
270272 jsFile = outputDir </> filePath </> "index.js"
271− externsFile = outputDir </> filePath </> "externs.purs"
273+ externsFile = outputDir </> filePath </> "externs.json"
272274 min <$> getTimestamp jsFile <*> getTimestamp externsFile
273275
274− readExterns :: ModuleName -> Make (FilePath, String)
276+ readExterns :: ModuleName -> Make (FilePath, B.ByteString)
275277 readExterns mn = do
276− let path = outputDir </> runModuleName mn </> "externs.purs"
278+ let path = outputDir </> runModuleName mn </> "externs.json"
277279 (path, ) <$> readTextFile path
278280
279281 codegen :: CF.Module CF.Ann -> Environment -> Externs -> SupplyT Make ()
@@ -290,12 +292,12 @@ buildMakeActions outputDir filePathMap foreigns usePrefix =
290292 pjs <- prettyPrintJS <$> J.moduleToJs m foreignInclude
291293 let filePath = runModuleName mn
292294 jsFile = outputDir </> filePath </> "index.js"
293− externsFile = outputDir </> filePath </> "externs.purs"
295+ externsFile = outputDir </> filePath </> "externs.json"
294296 foreignFile = outputDir </> filePath </> "foreign.js"
295297 prefix = ["Generated by psc version " ++ showVersion Paths.version | usePrefix]
296298 js = unlines $ map ("// " ++) prefix ++ [pjs]
297299 lift $ do
298− writeTextFile jsFile js
300+ writeTextFile jsFile (fromString js)
299301 for_ (mn `M.lookup` foreigns) (readTextFile >=> writeTextFile foreignFile)
300302 writeTextFile externsFile exts
301303
@@ -307,13 +309,13 @@ buildMakeActions outputDir filePathMap foreigns usePrefix =
307309 exists <- doesFileExist path
308310 traverse (const $ getModificationTime path) $ guard exists
309311
310− readTextFile :: FilePath -> Make String
311− readTextFile path = makeIO (const (SimpleErrorWrapper $ CannotReadFile path)) $ readFile path
312+ readTextFile :: FilePath -> Make B.ByteString
313+ readTextFile path = makeIO (const (SimpleErrorWrapper $ CannotReadFile path)) $ B.readFile path
312314
313− writeTextFile :: FilePath -> String -> Make ()
315+ writeTextFile :: FilePath -> B.ByteString -> Make ()
314316 writeTextFile path text = makeIO (const (SimpleErrorWrapper $ CannotWriteFile path)) $ do
315317 mkdirp path
316− writeFile path text
318+ B.writeFile path text
317319 where
318320 mkdirp :: FilePath -> IO ()
319321 mkdirp = createDirectoryIfMissing True . takeDirectory