@@ -12,17 +12,21 @@
13-----------------------------------------------------------------------------
13-----------------------------------------------------------------------------
15+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
15module Main where
17module Main where
17import Control.Applicative
19import Control.Applicative
18−import Control.Monad (forM)
20+import Control.Monad.Error
20import Data.Version (showVersion)
22import Data.Version (showVersion)
22import System.Console.CmdTheLine
24import System.Console.CmdTheLine
23−import System.Directory (createDirectoryIfMissing)
25+import System.Directory
26+ (doesFileExist, getModificationTime, createDirectoryIfMissing)
24import System.FilePath (takeDirectory)
27import System.FilePath (takeDirectory)
25import System.Exit (exitSuccess, exitFailure)
28import System.Exit (exitSuccess, exitFailure)
29+import System.IO.Error (tryIOError)
27import Text.Parsec (ParseError)
31import Text.Parsec (ParseError)
@@ -33,35 +37,73 @@ import qualified System.IO.UTF8 as U
33preludeFilename :: IO FilePath
37preludeFilename :: IO FilePath
34preludeFilename = Paths.getDataFileName "prelude/prelude.purs"
38preludeFilename = Paths.getDataFileName "prelude/prelude.purs"
36−readInput :: Maybe [FilePath] -> IO (Either ParseError [P.Module])
40+readInput :: Maybe [FilePath] -> IO (Either ParseError [(FilePath, P.Module)])
41+readInput Nothing = do
37−readInput Nothing = P.runIndentParser "" P.parseModules <$> getContents
43+ return $ map ((,) undefined) <$> P.runIndentParser "" P.parseModules text
38−readInput (Just input) = fmap (fmap concat . sequence) $ forM input $ \inputFile -> do
44+readInput (Just input) = fmap collect $ forM input $ \inputFile -> do
39 text <- U.readFile inputFile
45 text <- U.readFile inputFile
40− return $ P.runIndentParser inputFile P.parseModules text
46+ return $ (inputFile, P.runIndentParser inputFile P.parseModules text)
48+ collect :: [(FilePath, Either ParseError [P.Module])] -> Either ParseError [(FilePath, P.Module)]
49+ collect = fmap concat . sequence . map (\(fp, e) -> fmap (map ((,) fp)) e)
51+newtype Make a = Make { unMake :: ErrorT String IO a } deriving (Functor, Monad, MonadIO, MonadError String)
53+runMake :: Make a -> IO (Either String a)
54+runMake = runErrorT . unMake
56+makeIO :: IO a -> Make a
57+makeIO = Make . ErrorT . fmap (either (Left . show) Right) . tryIOError
59+instance P.MonadMake Make where
60+ getTimestamp path = makeIO $ do
61+ exists <- doesFileExist path
63+ True -> Just <$> getModificationTime path
64+ False -> return Nothing
65+ readTextFile path = makeIO $ do
66+ U.putStrLn $ "Reading " ++ path
68+ writeTextFile path text = makeIO $ do
70+ U.putStrLn $ "Writing " ++ path
71+ U.writeFile path text
72+ liftError = either throwError return
42−compile :: P.Options -> Maybe [FilePath] -> Maybe FilePath -> Maybe FilePath -> IO ()
74+compile :: Bool -> P.Options -> Maybe [FilePath] -> Maybe FilePath -> Maybe FilePath -> IO ()
43−compile opts input output externs = do
75+compile makeMode opts input output externs = do
44 modules <- readInput input
76 modules <- readInput input
45 case modules of
77 case modules of
46 Left err -> do
78 Left err -> do
47 U.print err
79 U.print err
48 exitFailure
80 exitFailure
49 Right ms ->
81 Right ms ->
84+ e <- runMake $ P.make opts ms
50− case P.compile opts ms of
92+ case P.compile opts (map snd ms) of
51− Left err -> do
93+ Left err -> do
52− U.putStrLn err
94+ U.putStrLn err
53− exitFailure
95+ exitFailure
54− Right (js, exts, _) -> do
96+ Right (js, exts, _) -> do
55− case output of
97+ case output of
56− Just path -> mkdirp path >> U.writeFile path js
98+ Just path -> mkdirp path >> U.writeFile path js
57− Nothing -> U.putStrLn js
99+ Nothing -> U.putStrLn js
58− case externs of
100+ case externs of
59− Just path -> mkdirp path >> U.writeFile path exts
101+ Just path -> mkdirp path >> U.writeFile path exts
60− Nothing -> return ()
102+ Nothing -> return ()
61− exitSuccess
103+ exitSuccess
63− mkdirp :: FilePath -> IO ()
105+mkdirp :: FilePath -> IO ()
64− mkdirp = createDirectoryIfMissing True . takeDirectory
106+mkdirp = createDirectoryIfMissing True . takeDirectory
66useStdIn :: Term Bool
108useStdIn :: Term Bool
67useStdIn = value . flag $ (optInfo [ "s", "stdin" ])
109useStdIn = value . flag $ (optInfo [ "s", "stdin" ])
@@ -103,6 +145,10 @@ noOpts :: Term Bool
103noOpts = value $ flag $ (optInfo [ "no-opts" ])
145noOpts = value $ flag $ (optInfo [ "no-opts" ])
104 { optDoc = "Skip the optimization phase." }
146 { optDoc = "Skip the optimization phase." }
149+make = value $ flag $ (optInfo [ "make" ])
150+ { optDoc = "Run in make mode" }
106browserNamespace :: Term String
152browserNamespace :: Term String
107browserNamespace = value $ opt "PS" $ (optInfo [ "browser-namespace" ])
153browserNamespace = value $ opt "PS" $ (optInfo [ "browser-namespace" ])
108 { optDoc = "Specify the namespace that PureScript modules will be exported to when running in the browser." }
154 { optDoc = "Specify the namespace that PureScript modules will be exported to when running in the browser." }
@@ -119,14 +165,15 @@ options :: Term P.Options
119options = P.Options <$> tco <*> performRuntimeTypeChecks <*> magicDo <*> runMain <*> noOpts <*> browserNamespace <*> dceModules <*> codeGenModules
165options = P.Options <$> tco <*> performRuntimeTypeChecks <*> magicDo <*> runMain <*> noOpts <*> browserNamespace <*> dceModules <*> codeGenModules
121stdInOrInputFiles :: FilePath -> Term (Maybe [FilePath])
167stdInOrInputFiles :: FilePath -> Term (Maybe [FilePath])
122−stdInOrInputFiles prelude = combine <$> useStdIn <*> (not <$> noPrelude) <*> inputFiles
168+stdInOrInputFiles prelude = combine <$> useStdIn <*> (not <$> noPrelude) <*> inputFiles <*> make
170+ combine _ _ input True = Just input
124− combine False True input = Just (prelude : input)
171+ combine False True input _ = Just (prelude : input)
125− combine False False input = Just input
172+ combine False False input _ = Just input
126− combine True _ _ = Nothing
173+ combine True _ _ _ = Nothing
128term :: FilePath -> Term (IO ())
175term :: FilePath -> Term (IO ())
129−term prelude = compile <$> options <*> stdInOrInputFiles prelude <*> outputFile <*> externsFile
176+term prelude = compile <$> make <*> options <*> stdInOrInputFiles prelude <*> outputFile <*> externsFile
131termInfo :: TermInfo
178termInfo :: TermInfo
132termInfo = defTI
179termInfo = defTI