1--------------------------------------------------------------------------------
2module Hakyll.Core.Runtime
7--------------------------------------------------------------------------------
8import Control.Applicative ((<$>))
9import Control.Monad (filterM)
10import Control.Monad.Error (ErrorT, runErrorT, throwError)
11import Control.Monad.Reader (ask)
12import Control.Monad.RWS (RWST, runRWST)
13import Control.Monad.State (get, modify)
14import Control.Monad.Trans (liftIO)
16import qualified Data.Map as M
17import Data.Monoid (mempty)
19import qualified Data.Set as S
20import System.Exit (ExitCode (..), exitWith)
21import System.FilePath ((</>))
24--------------------------------------------------------------------------------
25import Hakyll.Core.Compiler.Internal
26import Hakyll.Core.Compiler.Require
27import Hakyll.Core.Configuration
28import Hakyll.Core.Dependencies
29import Hakyll.Core.Identifier
30import Hakyll.Core.Item
31import Hakyll.Core.Item.SomeItem
32import Hakyll.Core.Logger (Logger)
33import qualified Hakyll.Core.Logger as Logger
34import Hakyll.Core.Provider
35import Hakyll.Core.Routes
36import Hakyll.Core.Rules.Internal
37import Hakyll.Core.Store (Store)
38import qualified Hakyll.Core.Store as Store
39import Hakyll.Core.Util.File
40import Hakyll.Core.Writable
43--------------------------------------------------------------------------------
44run :: Configuration -> Rules a -> IO RuleSet
45run configuration rules = do
47 logger <- Logger.new Logger.Debug putStrLn
48 Logger.header logger "Initialising..."
49 Logger.message logger "Creating store..."
50 store <- Store.new (inMemoryCache configuration) $
51 storeDirectory configuration
52 Logger.message logger "Creating provider..."
53 provider <- newProvider store (ignoreFile configuration) "."
54 Logger.message logger "Running rules..."
55 ruleSet <- runRules rules provider
58 mOldFacts <- Store.get store factsKey
59 let (oldFacts) = case mOldFacts of Store.Found f -> f
62 -- Build runtime read/state
63 let compilers = rulesCompilers ruleSet
65 { runtimeConfiguration = configuration
66 , runtimeLogger = logger
67 , runtimeProvider = provider
68 , runtimeStore = store
69 , runtimeRoutes = rulesRoutes ruleSet
70 , runtimeUniverse = compilers
73 { runtimeDone = S.empty
74 , runtimeTodo = M.empty
75 , runtimeFacts = oldFacts
78 -- Run the program and fetch the resulting state
79 result <- runErrorT $ runRWST build read' state
84 exitWith $ ExitFailure 1
87 Store.set store factsKey $ runtimeFacts s
91 factsKey = ["Hakyll.Core.Runtime.run", "facts"]
94--------------------------------------------------------------------------------
95data RuntimeRead = RuntimeRead
96 { runtimeConfiguration :: Configuration
97 , runtimeLogger :: Logger
98 , runtimeProvider :: Provider
99 , runtimeStore :: Store
100 , runtimeRoutes :: Routes
101 , runtimeUniverse :: [(Identifier, Compiler SomeItem)]
105--------------------------------------------------------------------------------
106data RuntimeState = RuntimeState
107 { runtimeDone :: Set Identifier
108 , runtimeTodo :: Map Identifier (Compiler SomeItem)
109 , runtimeFacts :: DependencyFacts
113--------------------------------------------------------------------------------
114type Runtime a = RWST RuntimeRead () RuntimeState (ErrorT String IO) a
117--------------------------------------------------------------------------------
120 logger <- runtimeLogger <$> ask
121 Logger.header logger "Checking for out-of-date items"
123 Logger.header logger "Compiling"
125 Logger.header logger "Success"
128--------------------------------------------------------------------------------
129scheduleOutOfDate :: Runtime ()
130scheduleOutOfDate = do
131 logger <- runtimeLogger <$> ask
132 provider <- runtimeProvider <$> ask
133 universe <- runtimeUniverse <$> ask
134 facts <- runtimeFacts <$> get
135 todo <- runtimeTodo <$> get
137 let identifiers = map fst universe
138 modified <- fmap S.fromList $ flip filterM identifiers $
139 liftIO . resourceModified provider
140 let (ood, facts', msgs) = outOfDate identifiers modified facts
142 [(id', c) | (id', c) <- universe, id' `S.member` ood]
145 mapM_ (Logger.debug logger) msgs
147 -- Update facts and todo items
149 { runtimeDone = runtimeDone s `S.union`
150 (S.fromList identifiers `S.difference` ood)
151 , runtimeTodo = todo `M.union` todo'
152 , runtimeFacts = facts'
156--------------------------------------------------------------------------------
157pickAndChase :: Runtime ()
159 todo <- runtimeTodo <$> get
160 case M.minViewWithKey todo of
162 Just ((id', _), _) -> do
167--------------------------------------------------------------------------------
168chase :: [Identifier] -> Identifier -> Runtime ()
170 | id' `elem` trail = return () -- Cycle detected!
172 logger <- runtimeLogger <$> ask
173 todo <- runtimeTodo <$> get
174 provider <- runtimeProvider <$> ask
175 universe <- runtimeUniverse <$> ask
176 routes <- runtimeRoutes <$> ask
177 store <- runtimeStore <$> ask
178 config <- runtimeConfiguration <$> ask
180 let compiler = todo M.! id'
182 { compilerUnderlying = id'
183 , compilerProvider = provider
184 , compilerUniverse = map fst universe
185 , compilerRoutes = routes
186 , compilerStore = store
187 , compilerLogger = logger
190 result <- liftIO $ runCompiler compiler read'
193 CompilerError e -> throwError e
196 CompilerDone (SomeItem item) cwrite -> do
197 -- TODO: Sanity check on itemIdentifier?
198 let body = itemBody item
199 facts = compilerDependencies cwrite
201 | compilerCacheHits cwrite <= 0 = "updated"
202 | otherwise = "cached "
205 Logger.message logger $ cacheHits ++ " " ++ show id'
207 -- Write if necessary
208 case runRoutes routes id' of
211 let path = destinationDirectory config </> url
212 liftIO $ makeDirectories path
213 liftIO $ write path item
214 Logger.debug logger $ "Routed to " ++ path
216 -- Save! (For require)
217 liftIO $ save store id' body
221 { runtimeDone = S.insert id' (runtimeDone s)
222 , runtimeTodo = M.delete id' (runtimeTodo s)
223 , runtimeFacts = M.insert id' facts (runtimeFacts s)
226 -- Try something else first
227 CompilerRequire dep c -> do
228 -- Update the compiler so we don't execute it twice
229 depDone <- (dep `S.member`) . runtimeDone <$> get
231 { runtimeTodo = M.insert id'
232 (if depDone then c else compilerResult result)
236 -- If the required item is already compiled, continue, or, start
238 if depDone then chase trail id' else chase (id' : trail) dep
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