Skip to content

Commit

Permalink
build: Print dependency graph on failure
Browse files Browse the repository at this point in the history
  • Loading branch information
jtojnar committed Nov 27, 2021
1 parent e0e88d3 commit 0d40ac1
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 0 deletions.
14 changes: 14 additions & 0 deletions hakyll.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ Source-Repository head
Type: git
Location: git://github.com/jaspervdj/hakyll.git

Flag dependencyVisualization
Description: Include tool for generating dependency graph
Default: True

Flag previewServer
Description: Include the preview server
Default: True
Expand Down Expand Up @@ -210,6 +214,12 @@ Library
vector >= 0.11 && < 0.13,
yaml >= 0.8.11 && < 0.12

If flag(dependencyVisualization)
Build-depends:
graphviz >= 2999.20 && < 2999.21
Cpp-options:
-DDEPENDENCY_VISUALIZATION

If flag(previewServer)
Build-depends:
wai >= 3.2 && < 3.3,
Expand Down Expand Up @@ -294,6 +304,10 @@ Test-suite hakyll-tests
unordered-containers >= 0.2 && < 0.3,
yaml >= 0.8.11 && < 0.12

If flag(dependencyVisualization)
Cpp-options:
-DDEPENDENCY_VISUALIZATION

If flag(previewServer)
Cpp-options:
-DPREVIEW_SERVER
Expand Down
40 changes: 40 additions & 0 deletions lib/Hakyll/Core/Runtime.hs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
--------------------------------------------------------------------------------
{-# LANGUAGE CPP #-}
module Hakyll.Core.Runtime
( run
, RunMode(..)
Expand All @@ -22,6 +23,13 @@ import Data.Traversable (for)
import System.Exit (ExitCode (..))
import System.FilePath ((</>))

#ifdef DEPENDENCY_VISUALIZATION
import Data.GraphViz
import Data.GraphViz.Printing (printIt, toDot, unqtText)
import qualified Data.Text.Lazy as LT
import qualified Data.Text.Lazy.IO as LT
#endif


--------------------------------------------------------------------------------
import Hakyll.Core.Compiler.Internal
Expand Down Expand Up @@ -76,6 +84,7 @@ run mode config logger rules = do
, runtimeTodo = M.empty
, runtimeFacts = oldFacts
, runtimeDependencies = M.empty
, runtimeFullDependencies = M.empty
}

-- Build runtime read/state
Expand Down Expand Up @@ -125,6 +134,7 @@ data RuntimeState = RuntimeState
, runtimeTodo :: Map Identifier (Compiler SomeItem)
, runtimeFacts :: DependencyFacts
, runtimeDependencies :: Map Identifier (Set (Identifier, Snapshot))
, runtimeFullDependencies :: Map Identifier (Set (Identifier, Snapshot))
}


Expand Down Expand Up @@ -154,6 +164,12 @@ build mode = do
RunModeNormal -> do
Logger.header logger "Compiling"
pickAndChase

#ifdef DEPENDENCY_VISUALIZATION
fullDeps <- runtimeFullDependencies <$> getRuntimeState
liftIO . LT.putStrLn . printIt $ dependencyGraph fullDeps
#endif

Logger.header logger "Success"
facts <- runtimeFacts <$> getRuntimeState
store <- runtimeStore <$> ask
Expand Down Expand Up @@ -204,10 +220,33 @@ pickAndChase = do
-- This clause happens when chasing *every item* in `todo` resulted in
-- idling because tasks are all waiting on something: a dependency cycle
deps <- runtimeDependencies <$> getRuntimeState
#ifdef DEPENDENCY_VISUALIZATION
fullDeps <- runtimeFullDependencies <$> getRuntimeState
liftIO . LT.putStrLn . printIt $ dependencyGraph fullDeps
#endif
throwError $ "Hakyll.Core.Runtime.pickAndChase: Dependency cycle detected: " ++
intercalate ", " [show k ++ " depends on " ++ show (S.toList v) | (k, v) <- M.toList deps]
pickAndChase

--------------------------------------------------------------------------------
#ifdef DEPENDENCY_VISUALIZATION
instance PrintDot Identifier where
unqtDot = unqtText . LT.pack . show
toDot = toDot . LT.pack . show

-- | Produces a GraphViz representation of the runtimeDependencies graph.
dependencyGraph :: Map Identifier (Set (Identifier, Snapshot)) -> DotGraph Identifier
dependencyGraph fullDeps = graphElemsToDot graphParams (map labelNodes nodes) edges
where
nodes = S.toList (M.keysSet fullDeps `S.union` foldMap (S.map fst) fullDeps)
edges = S.toList (M.foldMapWithKey (\ident -> S.map (\(dep, snap) -> (ident, dep, snap))) fullDeps)
graphParams = defaultParams
{ clusterBy = \(n, nl) -> C (toFilePath n) (N (n, nl))
, clusterID = Str . LT.pack
, fmtEdge = \(_, _, el) -> if null el then [] else [textLabel (LT.pack el)]
}
labelNodes ident = (ident, show ident)
#endif

--------------------------------------------------------------------------------
-- | Tracks whether a set of tasks has progressed overall (at least one task progressed)
Expand Down Expand Up @@ -330,6 +369,7 @@ chase id' = do
(runtimeTodo s)
-- We track dependencies only to inform users when an infinite loop is detected
, runtimeDependencies = M.insertWith S.union id' (S.fromList deps) (runtimeDependencies s)
, runtimeFullDependencies = M.insertWith S.union id' (S.fromList reqs) (runtimeFullDependencies s)
}

-- Progress has been made if at least one of the
Expand Down

0 comments on commit 0d40ac1

Please sign in to comment.