diff --git a/README.md b/README.md index c8f4c9b..669ba44 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ nix run github:utdemir/nix-tree -- --help ```console $ nix-tree --help -Usage: nix-tree [INSTALLABLE] [--store STORE] [--version] [--derivation] [--impure] +Usage: nix-tree [INSTALLABLE] [--store STORE] [--version] [--derivation] [--impure] [--dot] Interactively browse dependency graphs of Nix derivations. @@ -43,6 +43,7 @@ Available options: --version Show the nix-tree version --derivation Operate on the store derivation rather than its outputs --impure Allow access to mutable paths and repositories + --dot Print the dependency graph in dot format -h,--help Show this help text Keybindings: diff --git a/nix-tree.cabal b/nix-tree.cabal index 027fff2..2dd3b52 100644 --- a/nix-tree.cabal +++ b/nix-tree.cabal @@ -61,6 +61,7 @@ common common-options , directory , optparse-applicative , microlens + , dot executable nix-tree import: common-options diff --git a/src/NixTree/Main.hs b/src/NixTree/Main.hs index 914b707..d9d45a0 100644 --- a/src/NixTree/Main.hs +++ b/src/NixTree/Main.hs @@ -22,7 +22,8 @@ data Opts = Opts oStore :: String, oVersion :: Bool, oDerivation :: Bool, - oImpure :: Bool + oImpure :: Bool, + oDot :: Bool } optsParser :: Opts.ParserInfo Opts @@ -65,6 +66,7 @@ optsParser = <*> Opts.switch (Opts.long "version" <> Opts.help "Show the nix-tree version") <*> Opts.switch (Opts.long "derivation" <> Opts.help "Operate on the store derivation rather than its outputs") <*> Opts.switch (Opts.long "impure" <> Opts.help "Allow access to mutable paths and repositories") + <*> Opts.switch (Opts.long "dot" <> Opts.help "Print the dependency graph in dot format") keybindingsHelp :: Opts.Doc keybindingsHelp = @@ -123,7 +125,9 @@ main = do & chunks 50 & mapConcurrently_ (mapM_ (\p -> evaluate (rnf p) >> incProgress bar 1)) - run env + if opts & oDot + then putTextLn $ storeEnvToDot env + else run env chunks :: Int -> [a] -> [[a]] chunks _ [] = [] diff --git a/src/NixTree/StorePath.hs b/src/NixTree/StorePath.hs index 99bb862..e025e16 100644 --- a/src/NixTree/StorePath.hs +++ b/src/NixTree/StorePath.hs @@ -15,6 +15,7 @@ module NixTree.StorePath seBottomUp, seFetchRefs, mkStoreName, + storeEnvToDot, ) where @@ -25,7 +26,9 @@ import Data.Aeson.Types (Parser) import qualified Data.ByteString.Lazy as BL import qualified Data.HashMap.Strict as HM import qualified Data.HashSet as HS +import qualified Data.Set as Set import qualified Data.Text as T +import qualified Dot import System.FilePath.Posix (addTrailingPathSeparator, splitDirectories, ()) import System.IO (hPutStrLn) import System.Process.Typed (proc, readProcessStdout_) @@ -319,6 +322,39 @@ seBottomUp f StoreEnv {sePaths, seRoots} = -------------------------------------------------------------------------------- +storeEnvToDot :: StoreEnv s a -> Text +storeEnvToDot env = + seBottomUp go env + & seGetRoots + & toList + & map spPayload + & mconcat + & render + where + go sp = + fromList [Set.singleton (spName sp, spName ref) <> spPayload ref | ref <- spRefs sp] + & mconcat + + render :: Set (StoreName s, StoreName s) -> Text + render edges = + Dot.DotGraph + Dot.Strict + Dot.Directed + Nothing + [ Dot.StatementEdge + ( Dot.EdgeStatement + (Dot.ListTwo (Dot.EdgeNode (mkNodeId from)) (Dot.EdgeNode (mkNodeId to)) []) + [] + ) + | (from, to) <- toList edges + ] + & Dot.encode + + mkNodeId :: StoreName s -> Dot.NodeId + mkNodeId = fromString . toString . storeNameToShortText + +-------------------------------------------------------------------------------- + data NixPathInfo = NixPathInfo { npiPath :: FilePath, npiNarSize :: Int,