@@ -14,7 +14,6 @@
15{-# LANGUAGE DataKinds #-}
15{-# LANGUAGE DataKinds #-}
16{-# LANGUAGE FlexibleContexts #-}
16{-# LANGUAGE FlexibleContexts #-}
17−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
18{-# LANGUAGE RecordWildCards #-}
17{-# LANGUAGE RecordWildCards #-}
19{-# LANGUAGE TupleSections #-}
18{-# LANGUAGE TupleSections #-}
@@ -23,30 +22,20 @@ module Main where
23import Control.Applicative
22import Control.Applicative
24import Control.Monad
23import Control.Monad
25import Control.Monad.Error.Class (MonadError(..))
24import Control.Monad.Error.Class (MonadError(..))
26−import Control.Monad.Trans.Except
27−import Control.Monad.Reader
28import Control.Monad.Writer
25import Control.Monad.Writer
30−import Data.Maybe (fromMaybe)
31−import Data.Time.Clock
32−import Data.Traversable (traverse)
33import Data.Version (showVersion)
27import Data.Version (showVersion)
34import qualified Data.Map as M
28import qualified Data.Map as M
36import Options.Applicative as Opts
30import Options.Applicative as Opts
38−import System.Directory
39− (doesFileExist, getModificationTime, createDirectoryIfMissing)
40−import System.FilePath ((</>), takeDirectory)
41import System.Exit (exitSuccess, exitFailure)
32import System.Exit (exitSuccess, exitFailure)
42import System.IO (hPutStrLn, stderr)
33import System.IO (hPutStrLn, stderr)
43−import System.IO.Error (tryIOError)
45import qualified Language.PureScript as P
35import qualified Language.PureScript as P
46−import qualified Language.PureScript.CodeGen.JS as J
47−import qualified Language.PureScript.CoreFn as CF
48import qualified Paths_purescript as Paths
36import qualified Paths_purescript as Paths
51data PSCMakeOptions = PSCMakeOptions
40data PSCMakeOptions = PSCMakeOptions
52 { pscmInput :: [FilePath]
41 { pscmInput :: [FilePath]
@@ -61,46 +50,6 @@ data InputOptions = InputOptions
61 , ioInputFiles :: [FilePath]
50 , ioInputFiles :: [FilePath]
64−readInput :: InputOptions -> IO [(Either P.RebuildPolicy FilePath, String)]
65−readInput InputOptions{..} = forM ioInputFiles $ \inFile -> (Right inFile, ) <$> readFile inFile
67−newtype Make a = Make { unMake :: ReaderT (P.Options P.Make) (WriterT P.MultipleErrors (ExceptT P.MultipleErrors IO)) a }
68− deriving (Functor, Applicative, Monad, MonadIO, MonadError P.MultipleErrors, MonadWriter P.MultipleErrors, MonadReader (P.Options P.Make))
70−runMake :: P.Options P.Make -> Make a -> IO (Either P.MultipleErrors (a, P.MultipleErrors))
71−runMake opts = runExceptT . runWriterT . flip runReaderT opts . unMake
73−makeIO :: (IOError -> P.ErrorMessage) -> IO a -> Make a
75− e <- liftIO $ tryIOError io
76− either (throwError . P.singleError . f) return e
78−instance P.MonadMake Make where
79− getTimestamp path = makeIO (const (P.SimpleErrorWrapper $ P.CannotGetFileInfo path)) $ do
80− exists <- doesFileExist path
81− traverse (const $ getModificationTime path) $ guard exists
82− readTextFile path = makeIO (const (P.SimpleErrorWrapper $ P.CannotReadFile path)) $ do
83− putStrLn $ "Reading " ++ path
85− writeTextFile path text = makeIO (const (P.SimpleErrorWrapper $ P.CannotWriteFile path)) $ do
87− putStrLn $ "Writing " ++ path
89− progress = liftIO . putStrLn
91−-- Traverse (Either e) instance (base 4.7)
92−traverseEither :: Applicative f => (a -> f b) -> Either e a -> f (Either e b)
93−traverseEither _ (Left x) = pure (Left x)
94−traverseEither f (Right y) = Right <$> f y
96−parseInputs :: (Functor m, Applicative m, MonadError P.MultipleErrors m, MonadWriter P.MultipleErrors m)
97− => [(Either P.RebuildPolicy FilePath, String)]
98− -> [(FilePath, String)]
99− -> m ([(Either P.RebuildPolicy FilePath, P.Module)], M.Map P.ModuleName String)
100−parseInputs modules foreigns =
101− (,) <$> P.parseModulesFromFiles (either (const "") id) modules
102− <*> P.parseForeignModulesFromFiles foreigns
104compile :: PSCMakeOptions -> IO ()
53compile :: PSCMakeOptions -> IO ()
105compile (PSCMakeOptions input inputForeign outputDir opts usePrefix) = do
54compile (PSCMakeOptions input inputForeign outputDir opts usePrefix) = do
106 moduleFiles <- readInput (InputOptions (P.optionsNoPrelude opts) input)
55 moduleFiles <- readInput (InputOptions (P.optionsNoPrelude opts) input)
@@ -113,60 +62,27 @@ compile (PSCMakeOptions input inputForeign outputDir opts usePrefix) = do
113 when (P.nonEmpty warnings) $
62 when (P.nonEmpty warnings) $
114 hPutStrLn stderr (P.prettyPrintMultipleWarnings (P.optionsVerboseErrors opts) warnings)
63 hPutStrLn stderr (P.prettyPrintMultipleWarnings (P.optionsVerboseErrors opts) warnings)
115 let filePathMap = M.fromList $ map (\(fp, P.Module _ mn _ _) -> (mn, fp)) ms
64 let filePathMap = M.fromList $ map (\(fp, P.Module _ mn _ _) -> (mn, fp)) ms
116− e <- runMake opts $ P.make (getInputTimestamp filePathMap) getOutputTimestamp readExterns (codegen foreigns) ms
65+ makeActions = buildMakeActions outputDir filePathMap foreigns usePrefix
66+ e <- runMake opts $ P.make makeActions ms
117 case e of
67 case e of
118 Left errs -> do
68 Left errs -> do
119 putStrLn (P.prettyPrintMultipleErrors (P.optionsVerboseErrors opts) errs)
69 putStrLn (P.prettyPrintMultipleErrors (P.optionsVerboseErrors opts) errs)
120 exitFailure
70 exitFailure
121 Right (_, warnings') -> do
71 Right (_, warnings') -> do
122 when (P.nonEmpty warnings') $
72 when (P.nonEmpty warnings') $
123− putStrLn (P.prettyPrintMultipleWarnings (P.optionsVerboseErrors opts) warnings)
73+ putStrLn (P.prettyPrintMultipleWarnings (P.optionsVerboseErrors opts) warnings')
124 exitSuccess
74 exitSuccess
127− getInputTimestamp :: M.Map P.ModuleName (Either P.RebuildPolicy String) -> P.ModuleName -> Make (Either P.RebuildPolicy (Maybe UTCTime))
76+readInput :: InputOptions -> IO [(Either P.RebuildPolicy FilePath, String)]
128− getInputTimestamp filePathMap mn = do
77+readInput InputOptions{..} = forM ioInputFiles $ \inFile -> (Right inFile, ) <$> readFile inFile
129− let path = fromMaybe (error "Module has no filename in 'make'") $ M.lookup mn filePathMap
78+
130− traverseEither P.getTimestamp path
79+parseInputs :: (Functor m, Applicative m, MonadError P.MultipleErrors m, MonadWriter P.MultipleErrors m)
131−
80+ => [(Either P.RebuildPolicy FilePath, String)]
132− getOutputTimestamp :: P.ModuleName -> Make (Maybe UTCTime)
81+ -> [(FilePath, String)]
133− getOutputTimestamp mn = do
82+ -> m ([(Either P.RebuildPolicy FilePath, P.Module)], M.Map P.ModuleName String)
134− let filePath = P.runModuleName mn
83+parseInputs modules foreigns =
135− jsFile = outputDir </> filePath </> "index.js"
84+ (,) <$> P.parseModulesFromFiles (either (const "") id) modules
136− externsFile = outputDir </> filePath </> "externs.purs"
85+ <*> P.parseForeignModulesFromFiles foreigns
137− min <$> P.getTimestamp jsFile <*> P.getTimestamp externsFile
139− readExterns :: P.ModuleName -> Make (FilePath, String)
141− let path = outputDir </> P.runModuleName mn </> "externs.purs"
142− (path, ) <$> P.readTextFile path
144− codegen :: M.Map P.ModuleName String
150− codegen foreigns m exts _ nextVar = do
151− let requiresForeign = not $ null (CF.moduleForeign m)
152− foreignInclude <- case CF.moduleName m `M.lookup` foreigns of
153− Just _ | not requiresForeign -> error "Found unnecessary foreign module"
154− | otherwise -> return $ Just $ J.JSApp (J.JSVar "require") [J.JSStringLiteral "./foreign"]
155− Nothing | requiresForeign -> error "Foreign module missing"
156− | otherwise -> return Nothing
157− pjs <- P.evalSupplyT nextVar $ P.prettyPrintJS <$> J.moduleToJs m foreignInclude
158− let filePath = P.runModuleName $ CF.moduleName m
159− jsFile = outputDir </> filePath </> "index.js"
160− externsFile = outputDir </> filePath </> "externs.purs"
161− foreignFile = outputDir </> filePath </> "foreign.js"
162− prefix = ["Generated by psc-make version " ++ showVersion Paths.version | usePrefix]
163− js = unlines $ map ("// " ++) prefix ++ [pjs]
164− P.writeTextFile jsFile js
165− maybe (return ()) (P.writeTextFile foreignFile) $ CF.moduleName m `M.lookup` foreigns
166− P.writeTextFile externsFile exts
168−mkdirp :: FilePath -> IO ()
169−mkdirp = createDirectoryIfMissing True . takeDirectory
171inputFile :: Parser FilePath
87inputFile :: Parser FilePath
172inputFile = strArgument $
88inputFile = strArgument $