@@ -0,0 +1,208 @@
1+--------------------------------------------------------------------------------
2+module Hakyll.Core.Runtime
7+--------------------------------------------------------------------------------
8+import Control.Applicative ((<$>))
9+import Control.Monad (filterM)
10+import Control.Monad.Error (ErrorT, runErrorT, throwError)
11+import Control.Monad.Reader (ask)
12+import Control.Monad.RWS (RWST, runRWST)
13+import Control.Monad.State (get, modify)
14+import Control.Monad.Trans (liftIO)
16+import qualified Data.Map as M
17+import Data.Monoid (mempty)
19+import qualified Data.Set as S
20+import System.FilePath ((</>))
23+--------------------------------------------------------------------------------
24+import Hakyll.Core.CompiledItem
25+import Hakyll.Core.Compiler.Internal
26+import Hakyll.Core.Configuration
27+import Hakyll.Core.Dependencies
28+import Hakyll.Core.Identifier
29+import Hakyll.Core.Logger
30+import Hakyll.Core.ResourceProvider
31+import Hakyll.Core.Routes
32+import Hakyll.Core.Rules.Internal
33+import Hakyll.Core.Store (Store)
34+import qualified Hakyll.Core.Store as Store
35+import Hakyll.Core.Util.File
36+import Hakyll.Core.Writable
39+--------------------------------------------------------------------------------
40+run :: Configuration -> Rules a -> IO RuleSet
41+run configuration rules = do
43+ logger <- makeLogger putStrLn
44+ section logger "Initialising"
45+ store <- timed logger "Creating store" $
46+ Store.new (inMemoryCache configuration) $ storeDirectory configuration
47+ provider <- timed logger "Creating provider" $
48+ newResourceProvider store (ignoreFile configuration) "."
49+ ruleSet <- timed logger "Running rules" $ runRules rules provider
52+ mOldFacts <- Store.get store factsKey
53+ let (oldFacts) = case mOldFacts of Store.Found f -> f
56+ -- Build runtime read/state
57+ let compilers = rulesCompilers ruleSet
59+ { runtimeConfiguration = configuration
60+ , runtimeLogger = logger
61+ , runtimeProvider = provider
62+ , runtimeStore = store
63+ , runtimeRoutes = rulesRoutes ruleSet
64+ , runtimeUniverse = compilers
67+ { runtimeDone = S.empty
68+ , runtimeTodo = M.empty
69+ , runtimeFacts = oldFacts
72+ -- Run the program and fetch the resulting state
73+ result <- runErrorT $ runRWST build read' state
75+ Left e -> thrown logger e
76+ Right (_, s, _) -> Store.set store factsKey $ runtimeFacts s
82+ factsKey = ["Hakyll.Core.Runtime.run", "facts"]
85+--------------------------------------------------------------------------------
86+data RuntimeRead = RuntimeRead
87+ { runtimeConfiguration :: Configuration
88+ , runtimeLogger :: Logger
89+ , runtimeProvider :: ResourceProvider
90+ , runtimeStore :: Store
91+ , runtimeRoutes :: Routes
92+ , runtimeUniverse :: [(Identifier, Compiler CompiledItem)]
96+--------------------------------------------------------------------------------
97+data RuntimeState = RuntimeState
98+ { runtimeDone :: Set Identifier
99+ , runtimeTodo :: Map Identifier (Compiler CompiledItem)
100+ , runtimeFacts :: DependencyFacts
104+--------------------------------------------------------------------------------
105+type Runtime a = RWST RuntimeRead () RuntimeState (ErrorT String IO) a
108+--------------------------------------------------------------------------------
115+--------------------------------------------------------------------------------
116+scheduleOutOfDate :: Runtime ()
117+scheduleOutOfDate = do
118+ logger <- runtimeLogger <$> ask
119+ provider <- runtimeProvider <$> ask
120+ universe <- runtimeUniverse <$> ask
121+ facts <- runtimeFacts <$> get
122+ todo <- runtimeTodo <$> get
124+ let identifiers = map fst universe
125+ modified <- timed logger "Checking for modified items" $
126+ fmap S.fromList $ flip filterM identifiers $
127+ liftIO . resourceModified provider
128+ let (ood, facts', _) = outOfDate identifiers modified facts
130+ [(id', c) | (id', c) <- universe, id' `S.member` ood]
132+ -- Update facts and todo items
134+ { runtimeTodo = todo `M.union` todo'
135+ , runtimeFacts = facts'
139+--------------------------------------------------------------------------------
140+pickAndChase :: Runtime ()
142+ todo <- runtimeTodo <$> get
143+ case M.minViewWithKey todo of
144+ Nothing -> return ()
145+ Just ((id', _), _) -> chase [] id'
148+--------------------------------------------------------------------------------
149+chase :: [Identifier] -> Identifier -> Runtime ()
151+ | id' `elem` trail = return () -- Cycle detected!
153+ logger <- runtimeLogger <$> ask
154+ todo <- runtimeTodo <$> get
155+ provider <- runtimeProvider <$> ask
156+ universe <- runtimeUniverse <$> ask
157+ routes <- runtimeRoutes <$> ask
158+ store <- runtimeStore <$> ask
159+ config <- runtimeConfiguration <$> ask
161+ section logger $ "Processing " ++ show id'
162+ isModified <- liftIO $ resourceModified provider id'
163+ let compiler = todo M.! id'
164+ read' = CompilerRead
165+ { compilerIdentifier = id'
166+ , compilerResourceProvider = provider
167+ , compilerUniverse = map fst universe
168+ , compilerRoutes = routes
169+ , compilerStore = store
170+ , compilerResourceModified = isModified
171+ , compilerLogger = logger
174+ result <- timed logger "Compiling" $ liftIO $ runCompiler compiler read'
177+ CompilerError e -> throwError e
180+ CompilerDone compiled facts -> do
181+ -- Write if necessary
182+ case runRoutes routes id' of
183+ Nothing -> return ()
184+ Just url -> timed logger ("Routing to " ++ url) $ do
185+ let path = destinationDirectory config </> url
186+ liftIO $ makeDirectories path
187+ liftIO $ write path compiled
191+ { runtimeDone = S.insert id' (runtimeDone s)
192+ , runtimeTodo = M.delete id' (runtimeTodo s)
193+ , runtimeFacts = M.insert id' facts (runtimeFacts s)
196+ -- Try something else first
197+ CompilerRequire dep c -> do
198+ -- Update the compiler so we don't execute it twice
199+ depDone <- (dep `S.member`) . runtimeDone <$> get
201+ { runtimeTodo = M.insert id'
202+ (if depDone then c else compilerResult result)
206+ -- If the required item is already compiled, continue, or, start
208+ if depDone then chase trail id' else chase (id' : trail) dep