summaryrefslogtreecommitdiff
path: root/src/Hakyll/Core/Run.hs
blob: e9ec47e0b0d2e361dd44a1ea6a28e4b69bcd4c4d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
-- | This is the module which binds it all together
--
module Hakyll.Core.Run where

import Control.Arrow ((&&&))
import Control.Monad (foldM, forM_, forM, filterM)
import Data.Map (Map)
import qualified Data.Map as M
import Data.Monoid (mempty)
import Data.Typeable (Typeable)
import Data.Binary (Binary)
import System.FilePath ((</>))
import Control.Applicative ((<$>))
import Data.Set (Set)
import qualified Data.Set as S

import Hakyll.Core.Route
import Hakyll.Core.Identifier
import Hakyll.Core.Util.File
import Hakyll.Core.Compiler
import Hakyll.Core.Compiler.Internal
import Hakyll.Core.ResourceProvider
import Hakyll.Core.ResourceProvider.FileResourceProvider
import Hakyll.Core.Rules
import Hakyll.Core.DirectedGraph
import Hakyll.Core.DirectedGraph.Dot
import Hakyll.Core.DirectedGraph.DependencySolver
import Hakyll.Core.DirectedGraph.ObsoleteFilter
import Hakyll.Core.Writable
import Hakyll.Core.Store
import Hakyll.Core.CompiledItem

hakyll :: Rules -> IO ()
hakyll rules = do
    store <- makeStore "_store"
    provider <- fileResourceProvider
    hakyllWith rules provider store

hakyllWith :: Rules -> ResourceProvider -> Store -> IO ()
hakyllWith rules provider store = do
    let -- Get the rule set
        ruleSet = runRules rules provider

        -- Get all identifiers and compilers
        compilers = rulesCompilers ruleSet

        -- Get all dependencies
        dependencies = flip map compilers $ \(id', compiler) ->
            let deps = runCompilerDependencies compiler provider
            in (id', deps)

        -- Create a compiler map
        compilerMap = M.fromList compilers

        -- Create the graph
        graph = fromList dependencies

    putStrLn "Writing dependency graph to dependencies.dot..."
    writeDot "dependencies.dot" show graph

    -- Check which items are up-to-date
    modified' <- modified provider store $ map fst compilers

    let -- Try to reduce the graph
        reducedGraph = filterObsolete modified' graph

    putStrLn "Writing reduced graph to reduced.dot..."
    writeDot "reduced.dot" show reducedGraph

    let -- Solve the graph
        ordered = solveDependencies reducedGraph

        -- Join the order with the compilers again
        orderedCompilers = map (id &&& (compilerMap M.!)) ordered

        -- Fetch the routes
        route' = rulesRoute ruleSet

    putStrLn $ show reducedGraph
    putStrLn $ show ordered

    -- Generate all the targets in order
    _ <- foldM (addTarget route' modified') M.empty orderedCompilers

    putStrLn "DONE."
  where
    addTarget route' modified' map' (id', comp) = do
        let url = runRoute route' id'
        
        -- Check if the resource was modified
        let isModified = id' `S.member` modified'

        -- Run the compiler
        compiled <- runCompilerJob comp id' provider (dependencyLookup map')
                                   url store isModified
        putStrLn $ "Generated target: " ++ show id'

        case url of
            Nothing -> return ()
            Just r  -> do
                putStrLn $ "Routing " ++ show id' ++ " to " ++ r
                let path = "_site" </> r
                makeDirectories path
                write path compiled

        -- Store it in the cache
        storeResult store id' compiled

        putStrLn ""
        return $ M.insert id' compiled map'

    dependencyLookup map' id' = M.lookup id' map'

modified :: ResourceProvider     -- ^ Resource provider
         -> Store                -- ^ Store
         -> [Identifier]         -- ^ Identifiers to check
         -> IO (Set Identifier)  -- ^ Modified resources
modified provider store ids = fmap S.fromList $ flip filterM ids $ \id' ->
    if resourceExists provider id' then resourceModified provider id' store
                                   else return False