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 binterfaces <- get
126 let interfaces = snd <$> binterfaces
127 liftIO $ when (Flag.print_types flags) (Print.interfaceTypes interfaces interface)
128 update name interface False
130 Left err -> liftIO $ Print.failure err
132compile :: String -> FilePath -> Build ()
133compile number filePath =
136 source <- liftIO $ readFile filePath
137 let interfaces = snd <$> binterfaces
138 name = getName source
141 createDirectoryIfMissing True (Flag.cache_dir flags)
142 createDirectoryIfMissing True (Flag.build_dir flags)
145 liftIO $ case Source.build (Flag.no_prelude flags) interfaces source of
146 Right modul -> return modul
147 Left errors -> do Print.errors errors
150 liftIO $ when (Flag.print_types flags) $ Print.moduleTypes interfaces canonicalModule
152 let newInters = Module.toInterface canonicalModule
153 generateCache name newInters canonicalModule
154 update name newInters True
157 getName source = case Parser.getModuleName source of
162 hPutStrLn stdout $ concat [ number, " Compiling ", name
163 , replicate (max 1 (20 - length name)) ' '
164 , "( " ++ filePath ++ " )" ]
166 generateCache name interfs canonicalModule = do
169 createDirectoryIfMissing True . dropFileName $ Utils.elmi flags filePath
170 writeFile (Utils.elmo flags filePath) (JS.generate canonicalModule)
171 withBinaryFile (Utils.elmi flags filePath) WriteMode $ \handle ->
172 L.hPut handle (Binary.encode (name, interfs))
174update :: String -> Module.Interface -> Bool -> Build ()
175update name inter wasUpdated =
176 do modify (Map.insert name (wasUpdated, inter))
177 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