Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

build: Print dependency graph on failure #906

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 visualizing the 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
43 changes: 43 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, 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,13 @@ build mode = do
RunModeNormal -> do
Logger.header logger "Compiling"
pickAndChase

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

Logger.header logger "Success"
facts <- runtimeFacts <$> getRuntimeState
store <- runtimeStore <$> ask
Expand Down Expand Up @@ -204,10 +221,35 @@ 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
done <- runtimeDone <$> getRuntimeState
liftIO . LT.putStrLn . printIt $ dependencyGraph fullDeps done
#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)) -> Set Identifier -> DotGraph Identifier
dependencyGraph fullDeps done = 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)]
, fmtNode = \(n, l) -> [styles [if n `S.member` done then bold else dashed]]
}
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 +372,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