1-- | This is the module which binds it all together
3{-# LANGUAGE GeneralizedNewtypeDeriving, OverloadedStrings #-}
8import Prelude hiding (reverse)
9import Control.Monad (filterM, forM_)
10import Control.Monad.Trans (liftIO)
11import Control.Applicative (Applicative, (<$>))
12import Control.Monad.Reader (ReaderT, runReaderT, ask)
13import Control.Monad.State.Strict (StateT, runStateT, get, put)
15import qualified Data.Map as M
16import Data.Monoid (mempty, mappend)
17import System.FilePath ((</>))
18import qualified Data.Set as S
20import Hakyll.Core.Routes
21import Hakyll.Core.Identifier
22import Hakyll.Core.Util.File
23import Hakyll.Core.Compiler
24import Hakyll.Core.Compiler.Internal
25import Hakyll.Core.Resource
26import Hakyll.Core.Resource.Provider
27import Hakyll.Core.Resource.Provider.File
28import Hakyll.Core.Rules.Internal
29import Hakyll.Core.DirectedGraph
30import Hakyll.Core.DependencyAnalyzer
31import Hakyll.Core.Writable
32import Hakyll.Core.Store
33import Hakyll.Core.Configuration
34import Hakyll.Core.Logger
36-- | Run all rules needed, return the rule set used
38run :: HakyllConfiguration -> RulesM a -> IO RuleSet
39run configuration rules = do
40 logger <- makeLogger putStrLn
42 section logger "Initialising"
43 store <- timed logger "Creating store" $
44 makeStore $ storeDirectory configuration
45 provider <- timed logger "Creating provider" $
46 fileResourceProvider configuration
48 -- Fetch the old graph from the store. If we don't find it, we consider this
49 -- to be the first run
50 graph <- storeGet store "Hakyll.Core.Run.run" "dependencies"
51 let (firstRun, oldGraph) = case graph of Found g -> (False, g)
54 let ruleSet = runRules rules provider
55 compilers = rulesCompilers ruleSet
57 -- Extract the reader/state
58 reader = unRuntime $ addNewCompilers compilers
59 stateT = runReaderT reader $ RuntimeEnvironment
60 { hakyllLogger = logger
61 , hakyllConfiguration = configuration
62 , hakyllRoutes = rulesRoutes ruleSet
63 , hakyllResourceProvider = provider
65 , hakyllFirstRun = firstRun
68 -- Run the program and fetch the resulting state
69 ((), state') <- runStateT stateT $ RuntimeState
70 { hakyllAnalyzer = makeDependencyAnalyzer mempty (const False) oldGraph
71 , hakyllCompilers = M.empty
74 -- We want to save the final dependency graph for the next run
75 storeSet store "Hakyll.Core.Run.run" "dependencies" $
76 analyzerGraph $ hakyllAnalyzer state'
82data RuntimeEnvironment = RuntimeEnvironment
83 { hakyllLogger :: Logger
84 , hakyllConfiguration :: HakyllConfiguration
85 , hakyllRoutes :: Routes
86 , hakyllResourceProvider :: ResourceProvider
87 , hakyllStore :: Store
88 , hakyllFirstRun :: Bool
91data RuntimeState = RuntimeState
92 { hakyllAnalyzer :: DependencyAnalyzer (Identifier ())
93 , hakyllCompilers :: Map (Identifier ()) (Compiler () CompileRule)
96newtype Runtime a = Runtime
97 { unRuntime :: ReaderT RuntimeEnvironment (StateT RuntimeState IO) a
98 } deriving (Functor, Applicative, Monad)
100-- | Add a number of compilers and continue using these compilers
102addNewCompilers :: [(Identifier (), Compiler () CompileRule)]
103 -- ^ Compilers to add
105addNewCompilers newCompilers = Runtime $ do
106 -- Get some information
107 logger <- hakyllLogger <$> ask
108 section logger "Adding new compilers"
109 provider <- hakyllResourceProvider <$> ask
110 store <- hakyllStore <$> ask
111 firstRun <- hakyllFirstRun <$> ask
113 -- Old state information
114 oldCompilers <- hakyllCompilers <$> get
115 oldAnalyzer <- hakyllAnalyzer <$> get
117 let -- All known compilers
118 universe = M.keys oldCompilers ++ map fst newCompilers
120 -- Create a new partial dependency graph
121 dependencies = flip map newCompilers $ \(id', compiler) ->
122 let deps = runCompilerDependencies compiler id' universe
125 -- Create the dependency graph
126 newGraph = fromList dependencies
128 -- Check which items have been modified
129 modified <- fmap S.fromList $ flip filterM (map fst newCompilers) $
130 liftIO . resourceModified provider store . fromIdentifier
131 let checkModified = if firstRun then const True else (`S.member` modified)
133 -- Create a new analyzer and append it to the currect one
134 let newAnalyzer = makeDependencyAnalyzer newGraph checkModified $
135 analyzerPreviousGraph oldAnalyzer
136 analyzer = mappend oldAnalyzer newAnalyzer
140 { hakyllAnalyzer = analyzer
141 , hakyllCompilers = M.union oldCompilers (M.fromList newCompilers)
145 unRuntime stepAnalyzer
147stepAnalyzer :: Runtime ()
148stepAnalyzer = Runtime $ do
151 let (signal, analyzer') = step $ hakyllAnalyzer state
152 put $ state { hakyllAnalyzer = analyzer' }
154 case signal of Done -> return ()
155 Cycle c -> unRuntime $ dumpCycle c
156 Build id' -> unRuntime $ build id'
158-- | Dump cyclic error and quit
160dumpCycle :: [Identifier ()] -> Runtime ()
161dumpCycle cycle' = Runtime $ do
162 logger <- hakyllLogger <$> ask
163 section logger "Dependency cycle detected! Conflict:"
164 forM_ (zip cycle' $ drop 1 cycle') $ \(x, y) ->
165 report logger $ show x ++ " -> " ++ show y
167build :: Identifier () -> Runtime ()
168build id' = Runtime $ do
169 logger <- hakyllLogger <$> ask
170 routes <- hakyllRoutes <$> ask
171 provider <- hakyllResourceProvider <$> ask
172 store <- hakyllStore <$> ask
173 compilers <- hakyllCompilers <$> get
175 section logger $ "Compiling " ++ show id'
177 -- Fetch the right compiler from the map
178 let compiler = compilers M.! id'
180 -- Check if the resource was modified
181 isModified <- liftIO $ resourceModified provider store $ fromIdentifier id'
184 result <- timed logger "Total compile time" $ liftIO $
185 runCompiler compiler id' provider (M.keys compilers) routes
186 store isModified logger
189 -- Compile rule for one item, easy stuff
190 Right (CompileRule compiled) -> do
191 case runRoutes routes id' of
193 Just url -> timed logger ("Routing to " ++ url) $ do
195 destinationDirectory . hakyllConfiguration <$> ask
196 let path = destination </> url
197 liftIO $ makeDirectories path
198 liftIO $ write path compiled
200 -- Continue for the remaining compilers
201 unRuntime stepAnalyzer
203 -- Metacompiler, slightly more complicated
204 Right (MetaCompileRule newCompilers) ->
205 -- Actually I was just kidding, it's not hard at all
206 unRuntime $ addNewCompilers newCompilers
208 -- Some error happened, log and continue
211 unRuntime stepAnalyzer
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