2module Build.File (build) where
4import Control.Applicative ((<$>))
5import Control.Monad.Error (runErrorT)
6import Control.Monad.RWS.Strict
12import qualified Data.Binary as Binary
13import qualified Data.List as List
14import qualified Data.Maybe as Maybe
15import qualified Data.Map as Map
16import qualified Data.ByteString.Lazy as L
18import qualified Build.Dependencies as Deps
19import qualified Build.Flags as Flag
20import qualified Build.Interface as Interface
21import qualified Build.Print as Print
22import qualified Build.Source as Source
23import qualified Build.Utils as Utils
24import qualified Generate.JavaScript as JS
25import qualified Parse.Module as Parser
26import qualified AST.Module as Module
28-- Reader: Runtime flags, always accessible
29-- Writer: Remember the last module to be accessed
30-- State: Build up a map of the module interfaces
31type BuildT m a = RWST Flag.Flags (Last String) BInterfaces m a
32type Build a = BuildT IO a
34-- Interfaces, remembering if something was recompiled
35type BInterfaces = Map.Map String (Bool, Module.Interface)
37evalBuild :: Flag.Flags -> Module.Interfaces -> Build ()
38 -> IO (Map.Map String Module.Interface, Maybe String)
39evalBuild flags interfaces build =
40 do (ifaces, moduleNames) <- execRWST build flags (fmap notUpdated interfaces)
41 return (fmap snd ifaces, getLast moduleNames)
43 notUpdated iface = (False, iface)
45-- | Builds a list of files, returning the moduleName of the last one.
46-- Returns \"\" if the list is empty
47build :: Flag.Flags -> Module.Interfaces -> [FilePath] -> IO String
48build flags interfaces files =
49 do (ifaces, topName) <- evalBuild flags interfaces (buildAll files)
50 let removeTopName = Maybe.maybe id Map.delete topName
51 mapM_ (checkPorts topName) (Map.toList $ removeTopName ifaces)
52 return $ Maybe.fromMaybe "" topName
54 checkPorts topName (name,iface)
55 | null ports = return ()
56 | otherwise = Print.failure msg
58 ports = Module.iPorts iface
60 [ "Port Error: ports may only appear in the main module, but\n"
61 , " sub-module ", name, " declares the following port"
62 , if length ports == 1 then "" else "s", ": "
63 , List.intercalate ", " ports
66 Just tname -> "\n All ports must appear in module " ++ tname
69buildAll :: [FilePath] -> Build ()
70buildAll fs = mapM_ (uncurry build1) (zip [1..] fs)
71 where build1 :: Integer -> FilePath -> Build ()
73 shouldCompile <- shouldBeCompiled fname
75 then compile number fname
78 where number = join ["[", show num, " of ", show total, "]"]
82shouldBeCompiled :: FilePath -> Build Bool
83shouldBeCompiled filePath = do
85 let alreadyCompiled = liftIO $ do
86 existsi <- doesFileExist (Utils.elmi flags filePath)
87 existso <- doesFileExist (Utils.elmo flags filePath)
88 return $ existsi && existso
90 outDated = liftIO $ do
91 tsrc <- getModificationTime filePath
92 tint <- getModificationTime (Utils.elmo flags filePath)
95 dependenciesUpdated = do
96 eDeps <- liftIO . runErrorT $ Deps.readDeps filePath
98 -- Should never actually reach here
99 Left err -> liftIO $ Print.failure err
100 Right (_, deps) -> anyM wasCompiled deps
103 in (not <$> alreadyCompiled) `orM` outDated `orM` dependenciesUpdated
105wasCompiled :: String -> Build Bool
106wasCompiled modul = maybe False fst . Map.lookup modul <$> get
108-- Short-circuiting monadic (||)
110orM :: (Monad m) => m Bool -> m Bool -> m Bool
111orM m1 m2 = do b1 <- m1
116anyM :: (Monad m) => (a -> m Bool) -> [a] -> m Bool
117anyM f = foldr (orM . f) (return False)
119retrieve :: FilePath -> Build ()
120retrieve filePath = do
122 iface <- liftIO $ Interface.load (Utils.elmi flags filePath)
123 case Interface.isValid filePath iface of
124 Right (name, interface) ->
125 do liftIO $ when (Flag.print_types flags) (Print.types (Module.iTypes interface))
126 update name interface False
128 Left err -> liftIO $ Print.failure err
130compile :: String -> FilePath -> Build ()
131compile number filePath =
134 source <- liftIO $ readFile filePath
135 let interfaces = snd <$> binterfaces
136 name = getName source
139 createDirectoryIfMissing True (Flag.cache_dir flags)
140 createDirectoryIfMissing True (Flag.build_dir flags)
143 liftIO $ case Source.build (Flag.no_prelude flags) interfaces source of
144 Right modul -> return modul
145 Left errors -> do Print.errors errors
148 liftIO $ when (Flag.print_types flags) $ do
149 Print.types (Module.types (Module.body canonicalModule))
151 let newInters = Module.toInterface canonicalModule
152 generateCache name newInters canonicalModule
153 update name newInters True
156 getName source = case Parser.getModuleName source of
161 hPutStrLn stdout $ concat [ number, " Compiling ", name
162 , replicate (max 1 (20 - length name)) ' '
163 , "( " ++ filePath ++ " )" ]
165 generateCache name interfs canonicalModule = do
168 createDirectoryIfMissing True . dropFileName $ Utils.elmi flags filePath
169 writeFile (Utils.elmo flags filePath) (JS.generate canonicalModule)
170 withBinaryFile (Utils.elmi flags filePath) WriteMode $ \handle ->
171 L.hPut handle (Binary.encode (name, interfs))
173update :: String -> Module.Interface -> Bool -> Build ()
174update name inter wasUpdated =
175 do modify (Map.insert name (wasUpdated, inter))
176 tell (Last . Just $ name)
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