1-- | A Compiler manages targets and dependencies between targets
3-- The most distinguishing property of a 'Compiler' is that it is an Arrow. A
4-- compiler of the type @Compiler a b@ is simply a compilation phase which takes
5-- an @a@ as input, and produces a @b@ as output.
7-- Compilers are chained using the '>>>' arrow operation. If we have a compiler
9-- > getResourceString :: Compiler Resource String
11-- which reads the resource, and a compiler
13-- > readPage :: Compiler String (Page String)
15-- we can chain these two compilers to get a
17-- > (getResourceString >>> readPage) :: Compiler Resource (Page String)
19-- Most compilers can be created by combining smaller compilers using '>>>'.
21-- More advanced constructions are also possible using arrow, and sometimes
22-- these are needed. For a good introduction to arrow, you can refer to
24-- <http://en.wikibooks.org/wiki/Haskell/Understanding_arrows>
26-- A construction worth writing a few paragraphs about here are the 'require'
27-- functions. Different variants of this function are exported here, but they
28-- all serve more or less the same goal.
30-- When you use only '>>>' to chain your compilers, you get a linear pipeline --
31-- it is not possible to add extra items from other compilers along the way.
32-- This is where the 'require' functions come in.
34-- This function allows you to reference other items, which are then added to
35-- the pipeline. Let's look at this crappy ASCII illustration which represents
36-- a pretty common scenario:
38-- > read resource >>> pandoc render >>> layout >>> relativize URL's
40-- > @templates/fancy.html@
42-- We want to construct a pipeline of compilers to go from our resource to a
43-- proper webpage. However, the @layout@ compiler takes more than just the
44-- rendered page as input: it needs the @templates/fancy.html@ template as well.
46-- This is an example of where we need the @require@ function. We can solve
47-- this using a construction that looks like:
49-- > ... >>> pandoc render >>> require >>> layout >>> ...
51-- > @templates/fancy.html@ ------/
53-- This illustration can help us understand the type signature of 'require'.
55-- > require :: (Binary a, Typeable a, Writable a)
60-- Let's look at it in detail:
62-- > (Binary a, Typeable a, Writable a)
64-- These are constraints for the @a@ type. @a@ (the template) needs to have
65-- certain properties for it to be required.
69-- This is simply @templates/fancy.html@: the 'Identifier' of the item we want
70-- to 'require', in other words, the name of the item we want to add to the
75-- This is a function given by the user, specifying /how/ the two items shall be
76-- merged. @b@ is the output of the previous compiler, and @a@ is the item we
77-- just required -- the template. This means @c@ will be the final output of the
78-- 'require' combinator.
82-- Indeed, we have now constructed a compiler which takes a @b@ and produces a
83-- @c@. This means that we have a linear pipeline again, thanks to the 'require'
84-- function. So, the 'require' function actually helps to reduce to complexity
85-- of Hakyll applications!
87-- Note that require will fetch a previously compiled item: in our example of
88-- the type @a@. It is /very/ important that the compiler which produced this
89-- value, produced the right type as well!
91{-# LANGUAGE GeneralizedNewtypeDeriving #-}
92module Hakyll.Core.Compiler
117import Prelude hiding ((.), id)
118import Control.Arrow ((>>>), (&&&), arr)
119import Control.Applicative ((<$>))
120import Control.Monad.Reader (ask)
121import Control.Monad.Trans (liftIO)
122import Control.Monad.Error (throwError)
123import Control.Category (Category, (.), id)
124import Data.Maybe (fromMaybe)
125import System.FilePath (takeExtension)
127import Data.Binary (Binary)
128import Data.Typeable (Typeable)
129import Data.ByteString.Lazy (ByteString)
131import Hakyll.Core.Identifier
132import Hakyll.Core.Identifier.Pattern
133import Hakyll.Core.CompiledItem
134import Hakyll.Core.Writable
135import Hakyll.Core.Resource
136import Hakyll.Core.Resource.Provider
137import Hakyll.Core.Compiler.Internal
138import Hakyll.Core.Store
139import Hakyll.Core.Rules.Internal
140import Hakyll.Core.Routes
141import Hakyll.Core.Logger
143-- | Run a compiler, yielding the resulting target and it's dependencies. This
144-- version of 'runCompilerJob' also stores the result
146runCompiler :: Compiler () CompileRule -- ^ Compiler to run
147 -> Identifier () -- ^ Target identifier
148 -> ResourceProvider -- ^ Resource provider
149 -> [Identifier ()] -- ^ Universe
152 -> Bool -- ^ Was the resource modified?
153 -> Logger -- ^ Logger
154 -> IO (Throwing CompileRule) -- ^ Resulting item
155runCompiler compiler id' provider universe routes store modified logger = do
156 -- Run the compiler job
157 result <- runCompilerJob compiler id' provider universe
158 routes store modified logger
160 -- Inspect the result
162 -- In case we compiled an item, we will store a copy in the cache first,
163 -- before we return control. This makes sure the compiled item can later
164 -- be accessed by e.g. require.
165 Right (CompileRule (CompiledItem x)) ->
166 storeSet store "Hakyll.Core.Compiler.runCompiler"
167 (castIdentifier id') x
169 -- Otherwise, we do nothing here
174-- | Get the identifier of the item that is currently being compiled
176getIdentifier :: Compiler a (Identifier b)
177getIdentifier = fromJob $ const $ CompilerM $
178 castIdentifier . compilerIdentifier <$> ask
180-- | Get the resource that is currently being compiled
182getResource :: Compiler a Resource
183getResource = getIdentifier >>> arr fromIdentifier
185-- | Get the route we are using for this item
187getRoute :: Compiler a (Maybe FilePath)
188getRoute = getIdentifier >>> getRouteFor
190-- | Get the route for a specified item
192getRouteFor :: Compiler (Identifier a) (Maybe FilePath)
193getRouteFor = fromJob $ \identifier -> CompilerM $ do
194 routes <- compilerRoutes <$> ask
195 return $ runRoutes routes identifier
197-- | Get the resource we are compiling as a string
199getResourceString :: Compiler Resource String
200getResourceString = getResourceWith resourceString
202-- | Get the resource we are compiling as a lazy bytestring
204getResourceLBS :: Compiler Resource ByteString
205getResourceLBS = getResourceWith resourceLBS
207-- | Overloadable function for 'getResourceString' and 'getResourceLBS'
209getResourceWith :: (ResourceProvider -> Resource -> IO a)
210 -> Compiler Resource a
211getResourceWith reader = fromJob $ \resource -> CompilerM $ do
212 let identifier = unResource resource
213 provider <- compilerResourceProvider <$> ask
214 if resourceExists provider resource
215 then liftIO $ reader provider resource
216 else throwError $ error' identifier
218 error' id' = "Hakyll.Core.Compiler.getResourceWith: resource "
219 ++ show id' ++ " not found"
221-- | Auxiliary: get a dependency
223getDependency :: (Binary a, Writable a, Typeable a)
224 => Identifier a -> CompilerM a
225getDependency id' = CompilerM $ do
226 store <- compilerStore <$> ask
227 result <- liftIO $ storeGet store "Hakyll.Core.Compiler.runCompiler" id'
229 NotFound -> throwError notFound
230 WrongType e r -> throwError $ wrongType e r
234 "Hakyll.Core.Compiler.getDependency: " ++ show id' ++ " was " ++
235 "not found in the cache, the cache might be corrupted or " ++
236 "the item you are referring to might not exist"
238 "Hakyll.Core.Compiler.getDependency: " ++ show id' ++ " was found " ++
239 "in the cache, but does not have the right type: expected " ++ show e ++
240 " but got " ++ show r
242-- | Variant of 'require' which drops the current value
244require_ :: (Binary a, Typeable a, Writable a)
248 fromDependency identifier >>> fromJob (const $ getDependency identifier)
250-- | Require another target. Using this function ensures automatic handling of
253require :: (Binary a, Typeable a, Writable a)
257require identifier = requireA identifier . arr . uncurry
259-- | Arrow-based variant of 'require'
261requireA :: (Binary a, Typeable a, Writable a)
265requireA identifier = (id &&& require_ identifier >>>)
267-- | Variant of 'requireAll' which drops the current value
269requireAll_ :: (Binary a, Typeable a, Writable a)
272requireAll_ pattern = fromDependencies (const getDeps) >>> fromJob requireAll_'
274 getDeps = map castIdentifier . filterMatches pattern . map castIdentifier
275 requireAll_' = const $ CompilerM $ do
276 deps <- getDeps . compilerUniverse <$> ask
277 mapM (unCompilerM . getDependency) deps
279-- | Require a number of targets. Using this function ensures automatic handling
282requireAll :: (Binary a, Typeable a, Writable a)
286requireAll pattern = requireAllA pattern . arr . uncurry
288-- | Arrow-based variant of 'requireAll'
290requireAllA :: (Binary a, Typeable a, Writable a)
292 -> Compiler (b, [a]) c
294requireAllA pattern = (id &&& requireAll_ pattern >>>)
296cached :: (Binary a, Typeable a, Writable a)
298 -> Compiler Resource a
299 -> Compiler Resource a
300cached name (Compiler d j) = Compiler d $ const $ CompilerM $ do
301 logger <- compilerLogger <$> ask
302 identifier <- castIdentifier . compilerIdentifier <$> ask
303 store <- compilerStore <$> ask
304 modified <- compilerResourceModified <$> ask
305 report logger $ "Checking cache: " ++ if modified then "modified" else "OK"
307 then do v <- unCompilerM $ j $ fromIdentifier identifier
308 liftIO $ storeSet store name identifier v
310 else do v <- liftIO $ storeGet store name identifier
311 case v of Found v' -> return v'
312 _ -> throwError error'
314 error' = "Hakyll.Core.Compiler.cached: Cache corrupt!"
316-- | Create an unsafe compiler from a function in IO
318unsafeCompiler :: (a -> IO b) -- ^ Function to lift
319 -> Compiler a b -- ^ Resulting compiler
320unsafeCompiler f = fromJob $ CompilerM . liftIO . f
322-- | Compiler for debugging purposes
324traceShowCompiler :: Show a => Compiler a a
325traceShowCompiler = fromJob $ \x -> CompilerM $ do
326 logger <- compilerLogger <$> ask
327 report logger $ show x
330-- | Map over a compiler
332mapCompiler :: Compiler a b
334mapCompiler (Compiler d j) = Compiler d $ mapM j
336-- | Log and time a compiler
338timedCompiler :: String -- ^ Message
339 -> Compiler a b -- ^ Compiler to time
340 -> Compiler a b -- ^ Resulting compiler
341timedCompiler msg (Compiler d j) = Compiler d $ \x -> CompilerM $ do
342 logger <- compilerLogger <$> ask
343 timed logger msg $ unCompilerM $ j x
345-- | Choose a compiler by extension
349-- > route "css/*" $ setExtension "css"
350-- > compile "css/*" $ byExtension (error "Not a (S)CSS file")
351-- > [ (".css", compressCssCompiler)
352-- > , (".scss", sass)
355-- This piece of code will select the @compressCssCompiler@ for @.css@ files,
356-- and the @sass@ compiler (defined elsewhere) for @.scss@ files.
358byExtension :: Compiler a b -- ^ Default compiler
359 -> [(String, Compiler a b)] -- ^ Choices
360 -> Compiler a b -- ^ Resulting compiler
361byExtension defaultCompiler choices = Compiler deps job
363 -- Lookup the compiler, give an error when it is not found
365 let extension = takeExtension $ toFilePath identifier
366 in fromMaybe defaultCompiler $ lookup extension choices
367 -- Collect the dependencies of the choice
369 identifier <- dependencyIdentifier <$> ask
370 compilerDependencies $ lookup' identifier
371 -- Collect the job of the choice
372 job x = CompilerM $ do
373 identifier <- compilerIdentifier <$> ask
374 unCompilerM $ compilerJob (lookup' identifier) x
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