-- | This is the module which binds it all together--{-# LANGUAGE GeneralizedNewtypeDeriving #-}moduleHakyll.Core.Run(run)whereimportPreludehiding(reverse)importControl.Monad(filterM)importControl.Monad.Trans(liftIO)importControl.Applicative(Applicative,(<$>))importControl.Monad.Reader(ReaderT,runReaderT,ask)importControl.Monad.State.Strict(StateT,evalStateT,get,modify)importControl.Arrow((&&&))importqualifiedData.MapasMimportData.Monoid(mempty,mappend)importSystem.FilePath((</>))importData.Set(Set)importqualifiedData.SetasSimportHakyll.Core.RoutesimportHakyll.Core.IdentifierimportHakyll.Core.Util.FileimportHakyll.Core.CompilerimportHakyll.Core.Compiler.InternalimportHakyll.Core.ResourceProviderimportHakyll.Core.ResourceProvider.FileResourceProviderimportHakyll.Core.Rules.InternalimportHakyll.Core.DirectedGraphimportHakyll.Core.DirectedGraph.DependencySolverimportHakyll.Core.WritableimportHakyll.Core.StoreimportHakyll.Core.ConfigurationimportHakyll.Core.Logger-- | Run all rules needed, return the rule set used--run::HakyllConfiguration->Rules->IORuleSetrunconfigurationrules=dologger<-makeLoggersectionlogger"Initialising"store<-timedlogger"Creating store"$makeStore$storeDirectoryconfigurationprovider<-timedlogger"Creating provider"$fileResourceProviderconfigurationletruleSet=runRulesrulesprovidercompilers=rulesCompilersruleSet-- Extract the reader/statereader=unRuntime$addNewCompilers[]compilersstate'=runReaderTreader$envloggerruleSetproviderstoreevalStateTstate'state-- Flush and returnflushLoggerloggerreturnruleSetwhereenvloggerruleSetproviderstore=RuntimeEnvironment{hakyllLogger=logger,hakyllConfiguration=configuration,hakyllRoutes=rulesRoutesruleSet,hakyllResourceProvider=provider,hakyllStore=store}state=RuntimeState{hakyllModified=S.empty,hakyllGraph=mempty}dataRuntimeEnvironment=RuntimeEnvironment{hakyllLogger::Logger,hakyllConfiguration::HakyllConfiguration,hakyllRoutes::Routes,hakyllResourceProvider::ResourceProvider,hakyllStore::Store}dataRuntimeState=RuntimeState{hakyllModified::SetIdentifier,hakyllGraph::DirectedGraphIdentifier}newtypeRuntimea=Runtime{unRuntime::ReaderTRuntimeEnvironment(StateTRuntimeStateIO)a}deriving(Functor,Applicative,Monad)-- | Return a set of modified identifiers--modified::ResourceProvider-- ^ Resource provider->Store-- ^ Store->[Identifier]-- ^ Identifiers to check->IO(SetIdentifier)-- ^ Modified resourcesmodifiedproviderstoreids=fmapS.fromList$flipfilterMids$\id'->ifresourceExistsproviderid'thenresourceModifiedprovider(Resourceid')storeelsereturnFalse-- | Add a number of compilers and continue using these compilers--addNewCompilers::[(Identifier,Compiler()CompileRule)]-- ^ Remaining compilers yet to be run->[(Identifier,Compiler()CompileRule)]-- ^ Compilers to add->Runtime()addNewCompilersoldCompilersnewCompilers=Runtime$do-- Get some informationlogger<-hakyllLogger<$>asksectionlogger"Adding new compilers"provider<-hakyllResourceProvider<$>askstore<-hakyllStore<$>asklet-- All compilerscompilers=oldCompilers++newCompilers-- Get all dependencies for the compilersdependencies=flipmapcompilers$\(id',compiler)->letdeps=runCompilerDependenciescompilerid'providerin(id',deps)-- Create a compiler map (Id -> Compiler)compilerMap=M.fromListcompilers-- Create the dependency graphcurrentGraph=fromListdependencies-- Find the old graph and append the new graph to it. This forms the-- complete graphcompleteGraph<-timedlogger"Creating graph"$mappendcurrentGraph.hakyllGraph<$>getorderedCompilers<-timedlogger"Solving dependencies"$do-- Check which items are up-to-date. This only needs to happen for the new-- compilersoldModified<-hakyllModified<$>getnewModified<-liftIO$modifiedproviderstore$mapfstnewCompilersletmodified'=oldModified`S.union`newModified-- Find obsolete items. Every item that is reachable from a modified-- item is considered obsolete. From these obsolete items, we are only-- interested in ones that are in the current subgraph.obsolete=S.filter(`member`currentGraph)$reachableNodesmodified'$reversecompleteGraph-- Solve the graph and retain only the obsolete itemsordered=filter(`S.member`obsolete)$solveDependenciescurrentGraph-- Update the statemodify$updateStatemodified'completeGraph-- Join the order with the compilers againreturn$map(id&&&(compilerMapM.!))ordered-- Now run the ordered list of compilersunRuntime$runCompilersorderedCompilerswhere-- Add the modified information for the new compilersupdateStatemodified'graphstate=state{hakyllModified=modified',hakyllGraph=graph}runCompilers::[(Identifier,Compiler()CompileRule)]-- ^ Ordered list of compilers->Runtime()-- ^ No resultrunCompilers[]=return()runCompilers((id',compiler):compilers)=Runtime$do-- Obtain informationlogger<-hakyllLogger<$>askroutes<-hakyllRoutes<$>askprovider<-hakyllResourceProvider<$>askstore<-hakyllStore<$>askmodified'<-hakyllModified<$>getsectionlogger$"Compiling "++showid'let-- Check if the resource was modifiedisModified=id'`S.member`modified'-- Run the compilerresult<-timedlogger"Total compile time"$liftIO$runCompilercompilerid'providerroutesstoreisModifiedloggercaseresultof-- Compile rule for one item, easy stuffRight(CompileRulecompiled)->docaserunRoutesroutesid'ofNothing->return()Justurl->timedlogger("Routing to "++url)$dodestination<-destinationDirectory.hakyllConfiguration<$>askletpath=destination</>urlliftIO$makeDirectoriespathliftIO$writepathcompiled-- Continue for the remaining compilersunRuntime$runCompilerscompilers-- Metacompiler, slightly more complicatedRight(MetaCompileRulenewCompilers)->-- Actually I was just kidding, it's not hard at allunRuntime$addNewCompilerscompilersnewCompilers-- Some error happened, log and continueLefterr->dothrownloggererrunRuntime$runCompilerscompilers