1-----------------------------------------------------------------------------
4-- Copyright : (c) 2013-14 Phil Freeman, (c) 2014 Gary Burgess, and other contributors
7-- Maintainer : Andy Arvanitis
8-- Stability : experimental
13-----------------------------------------------------------------------------
15{-# LANGUAGE DataKinds #-}
16{-# LANGUAGE FlexibleContexts #-}
17{-# LANGUAGE GeneralizedNewtypeDeriving #-}
18{-# LANGUAGE TupleSections #-}
19{-# LANGUAGE TemplateHaskell #-}
28import Control.Monad.Error.Class (MonadError(..))
29import Control.Monad.Reader
31import Data.FileEmbed (embedFile)
32import Data.List (intercalate)
33import Data.Maybe (fromMaybe)
34import Data.String (fromString)
36import Data.Version (showVersion)
37import qualified Data.Map as M
38import qualified Data.ByteString.Char8 as B
40import System.Directory (doesDirectoryExist, doesFileExist, getModificationTime, createDirectoryIfMissing)
41import System.FilePath ((</>), takeDirectory, addExtension, dropExtension)
42import System.IO.Error (tryIOError)
45import Language.PureScript.Errors
46import Language.PureScript (Make, runMake)
48import qualified Language.PureScript as P
49import qualified Language.PureScript.CodeGen.Cpp as CPP
50import qualified Language.PureScript.CoreFn as CF
51import qualified Paths_purescript as Paths
53makeIO :: (IOError -> P.ErrorMessage) -> IO a -> Make a
55 e <- liftIO $ tryIOError io
56 either (throwError . P.singleError . f) return e
58-- Traverse (Either e) instance (base 4.7)
59traverseEither :: Applicative f => (a -> f b) -> Either e a -> f (Either e b)
60traverseEither _ (Left x) = pure (Left x)
61traverseEither f (Right y) = Right <$> f y
63buildMakeActions :: FilePath
64 -> M.Map P.ModuleName (Either P.RebuildPolicy FilePath)
67buildMakeActions outputDir filePathMap usePrefix =
68 P.MakeActions getInputTimestamp getOutputTimestamp readExterns codegen progress
71 getInputFile :: P.ModuleName -> FilePath
73 let path = fromMaybe (error "Module has no filename in 'make'") $ M.lookup mn filePathMap in
76 Left _ -> error "Module has no filename in 'make'"
78 getInputTimestamp :: P.ModuleName -> Make (Either P.RebuildPolicy (Maybe UTCTime))
79 getInputTimestamp mn = do
80 let path = fromMaybe (error "Module has no filename in 'make'") $ M.lookup mn filePathMap
81 traverseEither getTimestamp path
83 getOutputTimestamp :: P.ModuleName -> Make (Maybe UTCTime)
84 getOutputTimestamp mn = do
86 let filePath = dotsTo '/' $ P.runModuleName mn
87 fileBase = outputDir </> filePath </> (last . words . dotsTo ' ' $ P.runModuleName mn)
88 srcFile = addExtension fileBase "cc"
89-- headerFile = addExtension fileBase "hh"
90 externsFile = outputDir </> filePath </> "externs.purs"
91 min <$> getTimestamp srcFile <*> getTimestamp externsFile
93 readExterns :: P.ModuleName -> Make (FilePath, P.Externs)
95 let path = outputDir </> (dotsTo '/' $ P.runModuleName mn) </> "externs.purs"
96 (path, ) <$> readTextFile path
98 codegen :: CF.Module CF.Ann -> P.Environment -> P.Externs -> P.SupplyT Make ()
99 codegen m env exts = do
100 let mn = CF.moduleName m
101 let filePath = dotsTo '/' $ P.runModuleName mn
102 fileBase = outputDir </> filePath </> (last . words . dotsTo ' ' $ P.runModuleName mn)
103 srcFile = addExtension fileBase "cc"
104 headerFile = addExtension fileBase "hh"
105 externsFile = outputDir </> filePath </> "externs.purs"
106 prefix = ["Generated by pcc version " ++ showVersion Paths.version | usePrefix]
107 cpps <- CPP.moduleToCpp env m
108 let (hdrs,srcs) = span (/= CPP.CppEndOfHeader) cpps
109 psrcs <- CPP.prettyPrintCpp <$> pure srcs
110 phdrs <- CPP.prettyPrintCpp <$> pure hdrs
111 let src = unlines $ map ("// " ++) prefix ++ [psrcs]
112 hdr = unlines $ map ("// " ++) prefix ++ [phdrs]
115 writeTextFile srcFile src
116 writeTextFile headerFile hdr
117 writeTextFile externsFile exts
119 let supportDir = outputDir </> "PureScript"
120 supportFilesExist <- dirExists supportDir
121 when (not supportFilesExist) $ do
122 writeTextFile (supportDir </> "PureScript.hh") $ B.unpack $(embedFile "pcc/include/purescript.hh")
123 writeTextFile (supportDir </> "PureScript.cc") $ B.unpack $(embedFile "pcc/include/purescript.cc")
124 writeTextFile (supportDir </> "PureScript_GC.hh") $ B.unpack $(embedFile "pcc/include/purescript_gc.hh")
126 let inputPath = dropExtension $ getInputFile mn
127 hfile = addExtension inputPath "hh"
128 sfile = addExtension inputPath "cc"
129 hfileExists <- textFileExists hfile
130 when (hfileExists || requiresForeign m) $ do
131 text <- if hfileExists
132 then readTextFile hfile
134 writeTextFile (addExtension (fileBase ++ "_ffi") "hh") text
135 sfileExists <- textFileExists sfile
136 when (sfileExists) $ do
137 text' <- readTextFile sfile
138 writeTextFile (addExtension (fileBase ++ "_ffi") "cc") text'
140 requiresForeign :: CF.Module a -> Bool
141 requiresForeign = not . null . CF.moduleForeign
143 dirExists :: FilePath -> Make Bool
144 dirExists path = makeIO (const (ErrorMessage [] $ CannotReadFile path)) $ do
145 doesDirectoryExist path
147 textFileExists :: FilePath -> Make Bool
148 textFileExists path = makeIO (const (ErrorMessage [] $ CannotReadFile path)) $ do
151 getTimestamp :: FilePath -> Make (Maybe UTCTime)
152 getTimestamp path = makeIO (const (ErrorMessage [] $ CannotGetFileInfo path)) $ do
153 exists <- doesFileExist path
154 traverse (const $ getModificationTime path) $ guard exists
156 readTextFile :: FilePath -> Make String
157 readTextFile path = makeIO (const (ErrorMessage [] $ CannotReadFile path)) $ readUTF8File path
159 writeTextFile :: FilePath -> String -> Make ()
160 writeTextFile path text = makeIO (const (ErrorMessage [] $ CannotWriteFile path)) $ do
164 mkdirp :: FilePath -> IO ()
165 mkdirp = createDirectoryIfMissing True . takeDirectory
167 -- | Render a progress message
168 renderProgressMessage :: P.ProgressMessage -> String
169 renderProgressMessage (P.CompilingModule mn) = "Compiling " ++ P.runModuleName mn
171 progress :: P.ProgressMessage -> Make ()
172 progress = liftIO . putStrLn . renderProgressMessage
174dotsTo :: Char -> String -> String
175dotsTo chr = map (\c -> if c == '.' then chr else c)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list