@@ -20,53 +20,51 @@ module Language.PureScript.Make
20 , inferForeignModules
20 , inferForeignModules
23−import Prelude.Compat
23+import Prelude.Compat
25−import Control.Concurrent.Lifted as C
25+import Control.Concurrent.Lifted as C
26−import Control.Monad hiding (sequence)
26+import Control.Monad hiding (sequence)
27−import Control.Monad.Base (MonadBase(..))
27+import Control.Monad.Base (MonadBase(..))
28−import Control.Monad.Error.Class (MonadError(..))
28+import Control.Monad.Error.Class (MonadError(..))
29−import Control.Monad.IO.Class
29+import Control.Monad.IO.Class
30−import Control.Monad.Logger
30+import Control.Monad.Logger
31−import Control.Monad.Reader (MonadReader(..), ReaderT(..), asks)
31+import Control.Monad.Reader (MonadReader(..), ReaderT(..), asks)
32−import Control.Monad.Supply
32+import Control.Monad.Supply
33−import Control.Monad.Trans.Class (MonadTrans(..))
33+import Control.Monad.Trans.Class (MonadTrans(..))
34−import Control.Monad.Trans.Control (MonadBaseControl(..))
34+import Control.Monad.Trans.Control (MonadBaseControl(..))
35−import Control.Monad.Trans.Except
35+import Control.Monad.Trans.Except
36−import Control.Monad.Writer.Class (MonadWriter(..))
36+import Control.Monad.Writer.Class (MonadWriter(..))
38−import Data.Aeson (encode, decode)
37+import Data.Aeson (encode, decode)
39import qualified Data.Aeson as Aeson
38import qualified Data.Aeson as Aeson
40−import Data.Either (partitionEithers)
39+import Data.Either (partitionEithers)
41−import Data.Function (on)
40+import Data.Function (on)
42−import Data.Foldable (for_)
41+import Data.Foldable (for_)
43−import Data.List (foldl', sortBy, groupBy)
42+import Data.List (foldl', sortBy, groupBy)
44−import Data.Maybe (fromMaybe, catMaybes)
43+import Data.Maybe (fromMaybe, catMaybes)
45−import Data.Monoid ((<>))
44+import Data.Monoid ((<>))
46−import Data.Time.Clock
45+import Data.Time.Clock
47−import Data.Traversable (for)
46+import Data.Traversable (for)
48−import Data.Version (showVersion)
47+import Data.Version (showVersion)
49import qualified Data.ByteString.Lazy as B
48import qualified Data.ByteString.Lazy as B
50import qualified Data.ByteString.UTF8 as BU8
49import qualified Data.ByteString.UTF8 as BU8
51import qualified Data.Map as M
50import qualified Data.Map as M
52import qualified Data.Set as S
51import qualified Data.Set as S
53import qualified Data.Text as T
52import qualified Data.Text as T
54import qualified Data.Text.Encoding as TE
53import qualified Data.Text.Encoding as TE
56−import Language.PureScript.AST
54+import Language.PureScript.AST
57−import Language.PureScript.Crash
55+import Language.PureScript.Crash
58−import Language.PureScript.Environment
56+import Language.PureScript.Environment
59−import Language.PureScript.Errors
57+import Language.PureScript.Errors
60−import Language.PureScript.Externs
58+import Language.PureScript.Externs
61−import Language.PureScript.Linter
59+import Language.PureScript.Linter
62−import Language.PureScript.ModuleDependencies
60+import Language.PureScript.ModuleDependencies
63−import Language.PureScript.Names
61+import Language.PureScript.Names
64−import Language.PureScript.Options
62+import Language.PureScript.Options
65−import Language.PureScript.Pretty
63+import Language.PureScript.Pretty
66−import Language.PureScript.Pretty.Common(SMap(..))
64+import Language.PureScript.Pretty.Common(SMap(..))
67−import Language.PureScript.Renamer
65+import Language.PureScript.Renamer
68−import Language.PureScript.Sugar
66+import Language.PureScript.Sugar
69−import Language.PureScript.TypeChecker
67+import Language.PureScript.TypeChecker
70import qualified Language.JavaScript.Parser as JS
68import qualified Language.JavaScript.Parser as JS
71import qualified Language.PureScript.Bundle as Bundle
69import qualified Language.PureScript.Bundle as Bundle
72import qualified Language.PureScript.CodeGen.JS as J
70import qualified Language.PureScript.CodeGen.JS as J
@@ -74,21 +72,18 @@ import qualified Language.PureScript.Constants as C
74import qualified Language.PureScript.CoreFn as CF
72import qualified Language.PureScript.CoreFn as CF
75import qualified Language.PureScript.CoreFn.ToJSON as CFJ
73import qualified Language.PureScript.CoreFn.ToJSON as CFJ
76import qualified Language.PureScript.Parser as PSParser
74import qualified Language.PureScript.Parser as PSParser
78import qualified Paths_purescript as Paths
75import qualified Paths_purescript as Paths
80−import SourceMap
76+import SourceMap
81−import SourceMap.Types
77+import SourceMap.Types
83−import System.Directory (doesFileExist, getModificationTime, createDirectoryIfMissing, getCurrentDirectory)
78+import System.Directory (doesFileExist, getModificationTime, createDirectoryIfMissing, getCurrentDirectory)
84−import System.FilePath ((</>), takeDirectory, makeRelative, splitPath, normalise, replaceExtension)
79+import System.FilePath ((</>), takeDirectory, makeRelative, splitPath, normalise, replaceExtension)
85−import System.IO.Error (tryIOError)
80+import System.IO.Error (tryIOError)
87import qualified Text.Parsec as Parsec
81import qualified Text.Parsec as Parsec
89-- | Progress messages from the make process
83-- | Progress messages from the make process
90data ProgressMessage
84data ProgressMessage
91 = CompilingModule ModuleName
85 = CompilingModule ModuleName
86+ -- ^ Compilation started for the specified module
92 deriving (Show, Eq, Ord)
87 deriving (Show, Eq, Ord)
94-- | Render a progress message
89-- | Render a progress message
@@ -102,7 +97,6 @@ renderProgressMessage (CompilingModule mn) = "Compiling " ++ T.unpack (runModule
102-- * The particular backend being used (Javascript, C++11, etc.)
97-- * The particular backend being used (Javascript, C++11, etc.)
104-- * The details of how files are read/written etc.
99-- * The details of how files are read/written etc.
106data MakeActions m = MakeActions
100data MakeActions m = MakeActions
107 { getInputTimestamp :: ModuleName -> m (Either RebuildPolicy (Maybe UTCTime))
101 { getInputTimestamp :: ModuleName -> m (Either RebuildPolicy (Maybe UTCTime))
108 -- ^ Get the timestamp for the input file(s) for a module. If there are multiple
102 -- ^ Get the timestamp for the input file(s) for a module. If there are multiple
@@ -121,26 +115,26 @@ data MakeActions m = MakeActions
121 -- ^ Respond to a progress update.
115 -- ^ Respond to a progress update.
125−-- Generated code for an externs file.
118+-- | Generated code for an externs file.
127type Externs = B.ByteString
119type Externs = B.ByteString
130−-- Determines when to rebuild a module
121+-- | Determines when to rebuild a module
132data RebuildPolicy
122data RebuildPolicy
133 -- | Never rebuild this module
123 -- | Never rebuild this module
134 = RebuildNever
124 = RebuildNever
135 -- | Always rebuild this module
125 -- | Always rebuild this module
136 | RebuildAlways deriving (Show, Eq, Ord)
126 | RebuildAlways deriving (Show, Eq, Ord)
138−-- | Rebuild a single module
128+-- | Rebuild a single module.
130+-- This function is used for fast-rebuild workflows (PSCi and psc-ide are examples).
139−rebuildModule :: forall m. (Monad m, MonadBaseControl IO m, MonadError MultipleErrors m, MonadWriter MultipleErrors m)
133+ . (Monad m, MonadBaseControl IO m, MonadError MultipleErrors m, MonadWriter MultipleErrors m)
140− => MakeActions m
134+ => MakeActions m
141− -> [ExternsFile]
135+ -> [ExternsFile]
142− -> Module
136+ -> Module
143− -> m ExternsFile
137+ -> m ExternsFile
144rebuildModule MakeActions{..} externs m@(Module _ _ moduleName _ _) = do
138rebuildModule MakeActions{..} externs m@(Module _ _ moduleName _ _) = do
145 progress $ CompilingModule moduleName
139 progress $ CompilingModule moduleName
146 let env = foldl' (flip applyExternsFileToEnvironment) initEnvironment externs
140 let env = foldl' (flip applyExternsFileToEnvironment) initEnvironment externs
@@ -157,12 +151,10 @@ rebuildModule MakeActions{..} externs m@(Module _ _ moduleName _ _) = do
157 evalSupplyT nextVar . codegen renamed env' . encode $ exts
151 evalSupplyT nextVar . codegen renamed env' . encode $ exts
158 return exts
152 return exts
161−-- Compiles in "make" mode, compiling each module separately to a js files and an externs file
154+-- | Compiles in "make" mode, compiling each module separately to a @.js@ file and an @externs.json@ file.
163-- If timestamps have not changed, the externs file can be used to provide the module's types without
156-- If timestamps have not changed, the externs file can be used to provide the module's types without
164-- having to typecheck the module again.
157-- having to typecheck the module again.
166make :: forall m. (Monad m, MonadBaseControl IO m, MonadError MultipleErrors m, MonadWriter MultipleErrors m)
158make :: forall m. (Monad m, MonadBaseControl IO m, MonadError MultipleErrors m, MonadWriter MultipleErrors m)
167 => MakeActions m
159 => MakeActions m
168 -> [Module]
160 -> [Module]
@@ -244,7 +236,7 @@ make ma@MakeActions{..} ms = do
244 putMVar (fst $ fromMaybe (internalError "make: no barrier") $ lookup moduleName barriers) externs
236 putMVar (fst $ fromMaybe (internalError "make: no barrier") $ lookup moduleName barriers) externs
245 putMVar (snd $ fromMaybe (internalError "make: no barrier") $ lookup moduleName barriers) errors
237 putMVar (snd $ fromMaybe (internalError "make: no barrier") $ lookup moduleName barriers) errors
247− maximumMaybe :: (Ord a) => [a] -> Maybe a
239+ maximumMaybe :: Ord a => [a] -> Maybe a
248 maximumMaybe [] = Nothing
240 maximumMaybe [] = Nothing
249 maximumMaybe xs = Just $ maximum xs
241 maximumMaybe xs = Just $ maximum xs
@@ -262,11 +254,10 @@ make ma@MakeActions{..} ms = do
262importPrim :: Module -> Module
254importPrim :: Module -> Module
263importPrim = addDefaultImport (ModuleName [ProperName C.prim])
255importPrim = addDefaultImport (ModuleName [ProperName C.prim])
266−-- A monad for running make actions
257+-- | A monad for running make actions
267−--
258+newtype Make a = Make
268−newtype Make a = Make { unMake :: ReaderT Options (ExceptT MultipleErrors (Logger MultipleErrors)) a }
259+ { unMake :: ReaderT Options (ExceptT MultipleErrors (Logger MultipleErrors)) a
269− deriving (Functor, Applicative, Monad, MonadIO, MonadError MultipleErrors, MonadWriter MultipleErrors, MonadReader Options)
260+ } deriving (Functor, Applicative, Monad, MonadIO, MonadError MultipleErrors, MonadWriter MultipleErrors, MonadReader Options)
271instance MonadBase IO Make where
262instance MonadBase IO Make where
272 liftBase = liftIO
263 liftBase = liftIO
@@ -276,12 +267,12 @@ instance MonadBaseControl IO Make where
276 liftBaseWith f = Make $ liftBaseWith $ \q -> f (q . unMake)
267 liftBaseWith f = Make $ liftBaseWith $ \q -> f (q . unMake)
277 restoreM = Make . restoreM
268 restoreM = Make . restoreM
280−-- Execute a 'Make' monad, returning either errors, or the result of the compile plus any warnings.
270+-- | Execute a 'Make' monad, returning either errors, or the result of the compile plus any warnings.
282runMake :: Options -> Make a -> IO (Either MultipleErrors a, MultipleErrors)
271runMake :: Options -> Make a -> IO (Either MultipleErrors a, MultipleErrors)
283runMake opts = runLogger' . runExceptT . flip runReaderT opts . unMake
272runMake opts = runLogger' . runExceptT . flip runReaderT opts . unMake
274+-- | Run an 'IO' action in the 'Make' monad, by specifying how IO errors should
275+-- be rendered as 'ErrorMessage' values.
285makeIO :: (IOError -> ErrorMessage) -> IO a -> Make a
276makeIO :: (IOError -> ErrorMessage) -> IO a -> Make a
286makeIO f io = do
277makeIO f io = do
287 e <- liftIO $ tryIOError io
278 e <- liftIO $ tryIOError io
@@ -299,7 +290,8 @@ inferForeignModules
299 . MonadIO m
290 . MonadIO m
300 => M.Map ModuleName (Either RebuildPolicy FilePath)
291 => M.Map ModuleName (Either RebuildPolicy FilePath)
301 -> m (M.Map ModuleName FilePath)
292 -> m (M.Map ModuleName FilePath)
293+inferForeignModules =
302−inferForeignModules = fmap (M.mapMaybe id) . traverse inferForeignModule
294+ fmap (M.mapMaybe id) . traverse inferForeignModule
304 inferForeignModule :: Either RebuildPolicy FilePath -> m (Maybe FilePath)
296 inferForeignModule :: Either RebuildPolicy FilePath -> m (Maybe FilePath)
305 inferForeignModule (Left _) = return Nothing
297 inferForeignModule (Left _) = return Nothing
@@ -310,16 +302,19 @@ inferForeignModules = fmap (M.mapMaybe id) . traverse inferForeignModule
310 then return (Just jsFile)
302 then return (Just jsFile)
311 else return Nothing
303 else return Nothing
314−-- A set of make actions that read and write modules from the given directory.
305+-- | A set of make actions that read and write modules from the given directory.
315−--
306+buildMakeActions
316−buildMakeActions :: FilePath -- ^ the output directory
308+ -- ^ the output directory
309+ -> M.Map ModuleName (Either RebuildPolicy FilePath)
317− -> M.Map ModuleName (Either RebuildPolicy FilePath) -- ^ a map between module names and paths to the file containing the PureScript module
310+ -- ^ a map between module names and paths to the file containing the PureScript module
311+ -> M.Map ModuleName FilePath
318− -> M.Map ModuleName FilePath -- ^ a map between module name and the file containing the foreign javascript for the module
312+ -- ^ a map between module name and the file containing the foreign javascript for the module
319− -> Bool -- ^ Generate a prefix comment?
314+ -- ^ Generate a prefix comment?
320− -> MakeActions Make
315+ -> MakeActions Make
321buildMakeActions outputDir filePathMap foreigns usePrefix =
316buildMakeActions outputDir filePathMap foreigns usePrefix =
322− MakeActions getInputTimestamp getOutputTimestamp readExterns codegen progress
317+ MakeActions getInputTimestamp getOutputTimestamp readExterns codegen progress
325 getInputTimestamp :: ModuleName -> Make (Either RebuildPolicy (Maybe UTCTime))
320 getInputTimestamp :: ModuleName -> Make (Either RebuildPolicy (Maybe UTCTime))
@@ -421,10 +416,8 @@ buildMakeActions outputDir filePathMap foreigns usePrefix =
421 progress :: ProgressMessage -> Make ()
416 progress :: ProgressMessage -> Make ()
422 progress = liftIO . putStrLn . renderProgressMessage
417 progress = liftIO . putStrLn . renderProgressMessage
425−-- Check that the declarations in a given PureScript module match with those
419+-- | Check that the declarations in a given PureScript module match with those
426-- in its corresponding foreign module.
420-- in its corresponding foreign module.
428checkForeignDecls :: CF.Module ann -> FilePath -> SupplyT Make ()
421checkForeignDecls :: CF.Module ann -> FilePath -> SupplyT Make ()
429checkForeignDecls m path = do
422checkForeignDecls m path = do
430 jsStr <- lift $ readTextFile path
423 jsStr <- lift $ readTextFile path