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
116import Prelude hiding ((.), id)
117import Control.Arrow ((>>>), (&&&), arr)
118import Control.Applicative ((<$>))
119import Control.Monad.Reader (ask)
120import Control.Monad.Trans (liftIO)
121import Control.Monad.Error (throwError)
122import Control.Category (Category, (.), id)
123import Data.Maybe (fromMaybe)
124import System.FilePath (takeExtension)
126import Data.Binary (Binary)
127import Data.Typeable (Typeable)
128import Data.ByteString.Lazy (ByteString)
130import Hakyll.Core.Identifier
131import Hakyll.Core.Identifier.Pattern
132import Hakyll.Core.CompiledItem
133import Hakyll.Core.Writable
134import Hakyll.Core.Resource
135import Hakyll.Core.Resource.Provider
136import Hakyll.Core.Compiler.Internal
137import Hakyll.Core.Store
138import Hakyll.Core.Rules.Internal
139import Hakyll.Core.Routes
140import Hakyll.Core.Logger
142-- | Run a compiler, yielding the resulting target and it's dependencies. This
143-- version of 'runCompilerJob' also stores the result
145runCompiler :: Compiler () CompileRule -- ^ Compiler to run
146 -> Identifier () -- ^ Target identifier
147 -> ResourceProvider -- ^ Resource provider
148 -> [Identifier ()] -- ^ Universe
151 -> Bool -- ^ Was the resource modified?
152 -> Logger -- ^ Logger
153 -> IO (Throwing CompileRule) -- ^ Resulting item
154runCompiler compiler id' provider universe routes store modified logger = do
155 -- Run the compiler job
156 result <- runCompilerJob compiler id' provider universe
157 routes store modified logger
159 -- Inspect the result
161 -- In case we compiled an item, we will store a copy in the cache first,
162 -- before we return control. This makes sure the compiled item can later
163 -- be accessed by e.g. require.
164 Right (CompileRule (CompiledItem x)) ->
165 storeSet store "Hakyll.Core.Compiler.runCompiler"
166 (castIdentifier id') x
168 -- Otherwise, we do nothing here
173-- | Get the identifier of the item that is currently being compiled
175getIdentifier :: Compiler a (Identifier b)
176getIdentifier = fromJob $ const $ CompilerM $
177 castIdentifier . compilerIdentifier <$> ask
179-- | Get the resource that is currently being compiled
181getResource :: Compiler a Resource
182getResource = getIdentifier >>> arr fromIdentifier
184-- | Get the route we are using for this item
186getRoute :: Compiler a (Maybe FilePath)
187getRoute = getIdentifier >>> getRouteFor
189-- | Get the route for a specified item
191getRouteFor :: Compiler (Identifier a) (Maybe FilePath)
192getRouteFor = fromJob $ \identifier -> CompilerM $ do
193 routes <- compilerRoutes <$> ask
194 return $ runRoutes routes identifier
196-- | Get the resource we are compiling as a string
198getResourceString :: Compiler Resource String
199getResourceString = getResourceWith resourceString
201-- | Get the resource we are compiling as a lazy bytestring
203getResourceLBS :: Compiler Resource ByteString
204getResourceLBS = getResourceWith resourceLBS
206-- | Overloadable function for 'getResourceString' and 'getResourceLBS'
208getResourceWith :: (ResourceProvider -> Resource -> IO a)
209 -> Compiler Resource a
210getResourceWith reader = fromJob $ \resource -> CompilerM $ do
211 let identifier = unResource resource
212 provider <- compilerResourceProvider <$> ask
213 if resourceExists provider resource
214 then liftIO $ reader provider resource
215 else throwError $ error' identifier
217 error' id' = "Hakyll.Core.Compiler.getResourceWith: resource "
218 ++ show id' ++ " not found"
220-- | Auxiliary: get a dependency
222getDependency :: (Binary a, Writable a, Typeable a)
223 => Identifier a -> CompilerM a
224getDependency id' = CompilerM $ do
225 store <- compilerStore <$> ask
226 result <- liftIO $ storeGet store "Hakyll.Core.Compiler.runCompiler" id'
228 NotFound -> throwError notFound
229 WrongType e r -> throwError $ wrongType e r
233 "Hakyll.Core.Compiler.getDependency: " ++ show id' ++ " was " ++
234 "not found in the cache, the cache might be corrupted or " ++
235 "the item you are referring to might not exist"
237 "Hakyll.Core.Compiler.getDependency: " ++ show id' ++ " was found " ++
238 "in the cache, but does not have the right type: expected " ++ show e ++
239 " but got " ++ show r
241-- | Variant of 'require' which drops the current value
243require_ :: (Binary a, Typeable a, Writable a)
247 fromDependency identifier >>> fromJob (const $ getDependency identifier)
249-- | Require another target. Using this function ensures automatic handling of
252require :: (Binary a, Typeable a, Writable a)
256require identifier = requireA identifier . arr . uncurry
258-- | Arrow-based variant of 'require'
260requireA :: (Binary a, Typeable a, Writable a)
264requireA identifier = (id &&& require_ identifier >>>)
266-- | Variant of 'requireAll' which drops the current value
268requireAll_ :: (Binary a, Typeable a, Writable a)
271requireAll_ pattern = fromDependencies (const getDeps) >>> fromJob requireAll_'
273 getDeps = map castIdentifier . filterMatches pattern . map castIdentifier
274 requireAll_' = const $ CompilerM $ do
275 deps <- getDeps . compilerUniverse <$> ask
276 mapM (unCompilerM . getDependency) deps
278-- | Require a number of targets. Using this function ensures automatic handling
281requireAll :: (Binary a, Typeable a, Writable a)
285requireAll pattern = requireAllA pattern . arr . uncurry
287-- | Arrow-based variant of 'requireAll'
289requireAllA :: (Binary a, Typeable a, Writable a)
291 -> Compiler (b, [a]) c
293requireAllA pattern = (id &&& requireAll_ pattern >>>)
295cached :: (Binary a, Typeable a, Writable a)
297 -> Compiler Resource a
298 -> Compiler Resource a
299cached name (Compiler d j) = Compiler d $ const $ CompilerM $ do
300 logger <- compilerLogger <$> ask
301 identifier <- castIdentifier . compilerIdentifier <$> ask
302 store <- compilerStore <$> ask
303 modified <- compilerResourceModified <$> ask
304 report logger $ "Checking cache: " ++ if modified then "modified" else "OK"
306 then do v <- unCompilerM $ j $ fromIdentifier identifier
307 liftIO $ storeSet store name identifier v
309 else do v <- liftIO $ storeGet store name identifier
310 case v of Found v' -> return v'
311 _ -> throwError error'
313 error' = "Hakyll.Core.Compiler.cached: Cache corrupt!"
315-- | Create an unsafe compiler from a function in IO
317unsafeCompiler :: (a -> IO b) -- ^ Function to lift
318 -> Compiler a b -- ^ Resulting compiler
319unsafeCompiler f = fromJob $ CompilerM . liftIO . f
321-- | Compiler for debugging purposes
323traceShowCompiler :: Show a => Compiler a a
324traceShowCompiler = fromJob $ \x -> CompilerM $ do
325 logger <- compilerLogger <$> ask
326 report logger $ show x
329-- | Map over a compiler
331mapCompiler :: Compiler a b
333mapCompiler (Compiler d j) = Compiler d $ mapM j
335-- | Log and time a compiler
337timedCompiler :: String -- ^ Message
338 -> Compiler a b -- ^ Compiler to time
339 -> Compiler a b -- ^ Resulting compiler
340timedCompiler msg (Compiler d j) = Compiler d $ \x -> CompilerM $ do
341 logger <- compilerLogger <$> ask
342 timed logger msg $ unCompilerM $ j x
344-- | Choose a compiler by extension
348-- > route "css/*" $ setExtension "css"
349-- > compile "css/*" $ byExtension (error "Not a (S)CSS file")
350-- > [ (".css", compressCssCompiler)
351-- > , (".scss", sass)
354-- This piece of code will select the @compressCssCompiler@ for @.css@ files,
355-- and the @sass@ compiler (defined elsewhere) for @.scss@ files.
357byExtension :: Compiler a b -- ^ Default compiler
358 -> [(String, Compiler a b)] -- ^ Choices
359 -> Compiler a b -- ^ Resulting compiler
360byExtension defaultCompiler choices = Compiler deps job
362 -- Lookup the compiler, give an error when it is not found
364 let extension = takeExtension $ toFilePath identifier
365 in fromMaybe defaultCompiler $ lookup extension choices
366 -- Collect the dependencies of the choice
368 identifier <- dependencyIdentifier <$> ask
369 compilerDependencies $ lookup' identifier
370 -- Collect the job of the choice
371 job x = CompilerM $ do
372 identifier <- compilerIdentifier <$> ask
373 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