{-# LANGUAGE CPP #-}

{-|
This module provides simple dependency graph making for rpm packages:

@
import "Distribution.RPM.Build.Graph"

graph <- 'createGraph' ["pkg1", "pkg2", "../pkg3"]
@
-}

module Distribution.RPM.Build.Graph
  (PackageGraph,
   createGraph,
   createGraphRpmOpts,
   createGraph1,
   createGraph2,
   createGraph3,
   createGraph4,
   createGraph',
   createGraph'',
   createGraph''',
   createGraph'''',
   dependencyNodes,
   subgraph',
   packageLayers,
   lowestLayer,
   lowestLayer',
   packageLeaves,
   separatePackages,
   printGraph,
   renderGraph,
   depsGraph,
   depsGraphDeps,
   Components (..),
   topsortGraph,
  ) where

import Data.Graph.Inductive.Query.DFS (components, scc, topsort', xdfsWith)
import Data.Graph.Inductive.Query.SP (sp)
import Data.Graph.Inductive.PatriciaTree (Gr)
import qualified Data.Graph.Inductive.Graph as G

#if !MIN_VERSION_base(4,8,0)
import Control.Applicative ((<$>))
#endif
import Control.Monad.Extra (forM_, guard, when, unless, unlessM)
import Data.Maybe (catMaybes, fromMaybe, mapMaybe)
import Data.List.Extra
import Data.GraphViz
import SimpleCmd
import System.Directory (doesDirectoryExist, doesFileExist,
                         withCurrentDirectory, listDirectory)
import System.FilePath

import Distribution.RPM.Build.ProvReqs (rpmspecProvidesBuildRequires)

data SourcePackage =
  SourcePackage {
    SourcePackage -> String
packagePath :: FilePath,
    SourcePackage -> [String]
dependencies :: [FilePath]
   }
   deriving SourcePackage -> SourcePackage -> Bool
(SourcePackage -> SourcePackage -> Bool)
-> (SourcePackage -> SourcePackage -> Bool) -> Eq SourcePackage
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: SourcePackage -> SourcePackage -> Bool
== :: SourcePackage -> SourcePackage -> Bool
$c/= :: SourcePackage -> SourcePackage -> Bool
/= :: SourcePackage -> SourcePackage -> Bool
Eq

-- | alias for a package dependency graph
type PackageGraph = Gr FilePath ()

-- | Get all of the dependencies of a subset of one or more packages within full PackageGraph.
-- The subset paths should be written in the same way as for the graph.
dependencyNodes :: [FilePath] -- ^ subset of packages to start from
                -> PackageGraph -- ^ dependency graph
                -> [FilePath] -- ^ dependencies of subset
dependencyNodes :: [String] -> PackageGraph -> [String]
dependencyNodes [String]
subset PackageGraph
graph =
  let nodes :: [LNode String]
nodes = PackageGraph -> [LNode String]
forall a b. Gr a b -> [LNode a]
forall (gr :: * -> * -> *) a b. Graph gr => gr a b -> [LNode a]
G.labNodes PackageGraph
graph
      subnodes :: [Node]
subnodes = (String -> Maybe Node) -> [String] -> [Node]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe ([LNode String] -> String -> Maybe Node
pkgNode [LNode String]
nodes) [String]
subset
  in CFun String () [Node]
-> CFun String () String -> [Node] -> PackageGraph -> [String]
forall (gr :: * -> * -> *) a b c.
Graph gr =>
CFun a b [Node] -> CFun a b c -> [Node] -> gr a b -> [c]
xdfsWith CFun String () [Node]
forall a b. Context a b -> [Node]
G.pre' CFun String () String
forall {a} {b} {c} {d}. (a, b, c, d) -> c
third [Node]
subnodes PackageGraph
graph
  where
    pkgNode :: [G.LNode FilePath] -> FilePath -> Maybe Int
    pkgNode :: [LNode String] -> String -> Maybe Node
pkgNode [] String
_ = Maybe Node
forall a. Maybe a
Nothing
    pkgNode ((Node
i,String
l):[LNode String]
ns) String
p = if String -> String -> String
forall a. Eq a => [a] -> [a] -> [a]
dropSuffix String
"/" String
p String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String -> String -> String
forall a. Eq a => [a] -> [a] -> [a]
dropSuffix String
"/" String
l then Node -> Maybe Node
forall a. a -> Maybe a
Just Node
i else [LNode String] -> String -> Maybe Node
pkgNode [LNode String]
ns String
p

    third :: (a, b, c, d) -> c
third (a
_, b
_, c
c, d
_) = c
c

-- | Create a directed dependency graph for a set of packages
-- This is a convenience wrapper for createGraph1 False False True Nothing
createGraph :: [FilePath] -- ^ package paths (directories or spec filepaths)
            -> IO PackageGraph -- ^ dependency graph labelled by package paths
createGraph :: [String] -> IO PackageGraph
createGraph = Bool -> Bool -> Bool -> Maybe String -> [String] -> IO PackageGraph
createGraph1 Bool
False Bool
False Bool
True Maybe String
forall a. Maybe a
Nothing

-- | Create a directed dependency graph for a set of packages setting rpm options
-- This is a convenience wrapper for @createGraph2 rpmopts False False True Nothing@
--
-- @since 0.4.2
createGraphRpmOpts :: [String] -- ^ rpmspec options
                   -> [FilePath] -- ^ package paths (directories or spec filepaths)
                   -> IO PackageGraph -- ^ dependency graph labelled by package paths
createGraphRpmOpts :: [String] -> [String] -> IO PackageGraph
createGraphRpmOpts [String]
rpmopts =
  [String]
-> Bool
-> Bool
-> Bool
-> Maybe String
-> [String]
-> IO PackageGraph
createGraph2 [String]
rpmopts Bool
False Bool
False Bool
True Maybe String
forall a. Maybe a
Nothing

-- | Create a directed dependency graph for a set of packages
-- For the (createGraph default) reverse deps graph the arrows point back
-- from the dependencies to the dependendent (parent/consumer) packages,
-- and this allows forward sorting by dependencies (ie lowest deps first).
--
-- This is the same as @createGraph2 []@
--
-- @since 0.4.6
createGraph1 :: Bool -- ^ verbose
             -> Bool -- ^ lenient (skip rpmspec failures)
             -> Bool -- ^ reverse dependency graph
             -> Maybe FilePath -- ^ look for spec file in a subdirectory
             -> [FilePath] -- ^ package paths (directories or spec filepaths)
             -> IO PackageGraph -- ^ dependency graph labelled by package paths
createGraph1 :: Bool -> Bool -> Bool -> Maybe String -> [String] -> IO PackageGraph
createGraph1 = [String]
-> Bool
-> Bool
-> Bool
-> Maybe String
-> [String]
-> IO PackageGraph
createGraph2 []

-- | Alias for createGraph1
--
-- deprecated
createGraph' :: Bool -- ^ verbose
             -> Bool -- ^ lenient (skip rpmspec failures)
             -> Bool -- ^ reverse dependency graph
             -> Maybe FilePath -- ^ look for spec file in a subdirectory
             -> [FilePath] -- ^ package paths (directories or spec filepaths)
             -> IO PackageGraph -- ^ dependency graph labelled by package paths
createGraph' :: Bool -> Bool -> Bool -> Maybe String -> [String] -> IO PackageGraph
createGraph' = Bool -> Bool -> Bool -> Maybe String -> [String] -> IO PackageGraph
createGraph1

-- | Create a directed dependency graph for a set of packages
-- For the (createGraph default) reverse deps graph the arrows point back
-- from the dependencies to the dependendent (parent/consumer) packages,
-- and this allows forward sorting by dependencies (ie lowest deps first).
--
-- Additionally this function allows passing options to rpmspec:
-- eg `--with bootstrap` etc
--
-- @since 0.4.6
createGraph2 :: [String] -- ^ rpmspec options
             -> Bool -- ^ verbose
             -> Bool -- ^ lenient (skip rpmspec failures)
             -> Bool -- ^ reverse dependency graph
             -> Maybe FilePath -- ^ look for spec file in a subdirectory
             -> [FilePath] -- ^ package paths (directories or spec filepaths)
             -> IO PackageGraph -- ^ dependency graph labelled by package paths
createGraph2 :: [String]
-> Bool
-> Bool
-> Bool
-> Maybe String
-> [String]
-> IO PackageGraph
createGraph2 = [String]
-> [String]
-> Bool
-> Bool
-> Bool
-> Maybe String
-> [String]
-> IO PackageGraph
createGraph3 []

-- | Alias for createGraph2
--
-- @since 0.4.2 (deprecated)
createGraph'' :: [String] -- ^ rpmspec options
              -> Bool -- ^ verbose
              -> Bool -- ^ lenient (skip rpmspec failures)
              -> Bool -- ^ reverse dependency graph
              -> Maybe FilePath -- ^ look for spec file in a subdirectory
              -> [FilePath] -- ^ package paths (directories or spec filepaths)
              -> IO PackageGraph -- ^ dependency graph labelled by package paths
createGraph'' :: [String]
-> Bool
-> Bool
-> Bool
-> Maybe String
-> [String]
-> IO PackageGraph
createGraph'' = [String]
-> Bool
-> Bool
-> Bool
-> Maybe String
-> [String]
-> IO PackageGraph
createGraph2

-- | Create a directed dependency graph for a set of packages
--
-- Like createGraph2 but with additional parameter for any BRs to be ignored.
--
-- @since 0.4.6
createGraph3 :: [String] -- ^ ignored BuildRequires
             -> [String] -- ^ rpmspec options
             -> Bool -- ^ verbose
             -> Bool -- ^ lenient (skip rpmspec failures)
             -> Bool -- ^ reverse dependency graph
             -> Maybe FilePath -- ^ look for spec file in a subdirectory
             -> [FilePath] -- ^ package paths (directories or spec filepaths)
             -> IO PackageGraph -- ^ dependency graph labelled by package paths
createGraph3 :: [String]
-> [String]
-> Bool
-> Bool
-> Bool
-> Maybe String
-> [String]
-> IO PackageGraph
createGraph3 = Bool
-> [String]
-> [String]
-> Bool
-> Bool
-> Bool
-> Maybe String
-> [String]
-> IO PackageGraph
createGraph4 Bool
True

-- | Alias for createGraph3
--
-- @since 0.4.3 (deprecated)
createGraph''' :: [String] -- ^ ignored BuildRequires
               -> [String] -- ^ rpmspec options
               -> Bool -- ^ verbose
               -> Bool -- ^ lenient (skip rpmspec failures)
               -> Bool -- ^ reverse dependency graph
               -> Maybe FilePath -- ^ look for spec file in a subdirectory
               -> [FilePath] -- ^ package paths (directories or spec filepaths)
               -> IO PackageGraph -- ^ dependency graph labelled by package paths
createGraph''' :: [String]
-> [String]
-> Bool
-> Bool
-> Bool
-> Maybe String
-> [String]
-> IO PackageGraph
createGraph''' = [String]
-> [String]
-> Bool
-> Bool
-> Bool
-> Maybe String
-> [String]
-> IO PackageGraph
createGraph3

-- | Create a directed dependency graph for a set of packages
--
-- Like createGraph3 but can disable check for cycles
--
-- @since 0.4.6
createGraph4 :: Bool -- ^ check for cycles
             -> [String] -- ^ ignored BuildRequires
             -> [String] -- ^ rpmspec options
             -> Bool -- ^ verbose
             -> Bool -- ^ lenient (skip rpmspec failures)
             -> Bool -- ^ reverse dependency graph
             -> Maybe FilePath -- ^ look for spec file in a subdirectory
             -> [FilePath] -- ^ package paths (directories or spec filepaths)
             -> IO PackageGraph -- ^ dependency graph labelled by package paths
createGraph4 :: Bool
-> [String]
-> [String]
-> Bool
-> Bool
-> Bool
-> Maybe String
-> [String]
-> IO PackageGraph
createGraph4 Bool
checkcycles [String]
ignoredBRs [String]
rpmopts Bool
verbose Bool
lenient Bool
rev Maybe String
mdir [String]
paths =
  do
  [(String, [String], [String])]
metadata <- [Maybe (String, [String], [String])]
-> [(String, [String], [String])]
forall a. [Maybe a] -> [a]
catMaybes ([Maybe (String, [String], [String])]
 -> [(String, [String], [String])])
-> IO [Maybe (String, [String], [String])]
-> IO [(String, [String], [String])]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (String -> IO (Maybe (String, [String], [String])))
-> [String] -> IO [Maybe (String, [String], [String])]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
mapM String -> IO (Maybe (String, [String], [String]))
readSpecMetadata [String]
paths
  let realpkgs :: [String]
realpkgs = ((String, [String], [String]) -> String)
-> [(String, [String], [String])] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (String, [String], [String]) -> String
forall a b c. (a, b, c) -> a
fst3 [(String, [String], [String])]
metadata
      deps :: [[String]]
deps = (String -> Maybe [String]) -> [String] -> [[String]]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe ([(String, [String], [String])] -> String -> Maybe [String]
getDepsSrcResolved [(String, [String], [String])]
metadata) [String]
realpkgs
      spkgs :: [SourcePackage]
spkgs = (String -> [String] -> SourcePackage)
-> [String] -> [[String]] -> [SourcePackage]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith String -> [String] -> SourcePackage
SourcePackage [String]
realpkgs [[String]]
deps
      graph :: PackageGraph
graph = [SourcePackage] -> PackageGraph
getBuildGraph [SourcePackage]
spkgs
  Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when Bool
checkcycles (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$
    PackageGraph -> IO ()
checkForCycles PackageGraph
graph
  PackageGraph -> IO PackageGraph
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return PackageGraph
graph
  where
    readSpecMetadata :: FilePath -> IO (Maybe (FilePath,[String],[String]))
    readSpecMetadata :: String -> IO (Maybe (String, [String], [String]))
readSpecMetadata String
path = do
      Maybe (String, String)
mspecdir <- IO (Maybe (String, String))
findSpec
      case Maybe (String, String)
mspecdir of
        Maybe (String, String)
Nothing -> Maybe (String, [String], [String])
-> IO (Maybe (String, [String], [String]))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe (String, [String], [String])
forall a. Maybe a
Nothing
        Just (String
dir,String
spec) -> do
          Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when Bool
verbose (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ String -> IO ()
warning String
spec
          String
-> IO (Maybe (String, [String], [String]))
-> IO (Maybe (String, [String], [String]))
forall a. String -> IO a -> IO a
withCurrentDirectory String
dir (IO (Maybe (String, [String], [String]))
 -> IO (Maybe (String, [String], [String])))
-> IO (Maybe (String, [String], [String]))
-> IO (Maybe (String, [String], [String]))
forall a b. (a -> b) -> a -> b
$ do
            Maybe ([String], [String])
mprovbrs <- Bool -> [String] -> String -> IO (Maybe ([String], [String]))
rpmspecProvidesBuildRequires Bool
lenient [String]
rpmopts String
spec
            case Maybe ([String], [String])
mprovbrs of
              Maybe ([String], [String])
Nothing -> Maybe (String, [String], [String])
-> IO (Maybe (String, [String], [String]))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe (String, [String], [String])
forall a. Maybe a
Nothing
              Just ([String]
provs,[String]
brs) -> do
                -- FIXME do not hardcode arch
                let provs' :: [String]
provs' = (String -> Bool) -> [String] -> [String]
forall a. (a -> Bool) -> [a] -> [a]
filter (Bool -> Bool
not (Bool -> Bool) -> (String -> Bool) -> String -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String
"(x86-64)" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isSuffixOf`)) [String]
provs
                Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when Bool
verbose (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
                  String -> IO ()
warning (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ [String] -> String
forall a. Show a => a -> String
show ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ [String] -> [String]
forall a. Ord a => [a] -> [a]
sort [String]
provs'
                  String -> IO ()
warning (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ [String] -> String
forall a. Show a => a -> String
show ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ [String] -> [String]
forall a. Ord a => [a] -> [a]
sort [String]
brs
                Maybe (String, [String], [String])
-> IO (Maybe (String, [String], [String]))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe (String, [String], [String])
 -> IO (Maybe (String, [String], [String])))
-> Maybe (String, [String], [String])
-> IO (Maybe (String, [String], [String]))
forall a b. (a -> b) -> a -> b
$ (String, [String], [String]) -> Maybe (String, [String], [String])
forall a. a -> Maybe a
Just (String
path,
                               [String] -> [String]
forall a. Eq a => [a] -> [a]
nub [String]
provs',
                               [String] -> [String]
forall a. Eq a => [a] -> [a]
nub [String]
brs [String] -> [String] -> [String]
forall a. Eq a => [a] -> [a] -> [a]
\\ [String]
ignoredBRs)
      where
        -- (dir,specfile)
        findSpec :: IO (Maybe (FilePath,FilePath))
        findSpec :: IO (Maybe (String, String))
findSpec =
          (String -> (String, String))
-> Maybe String -> Maybe (String, String)
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap String -> (String, String)
splitFileName (Maybe String -> Maybe (String, String))
-> IO (Maybe String) -> IO (Maybe (String, String))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$>
          if String -> String
takeExtension String
path String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
".spec"
            then Bool -> String -> IO (Maybe String)
checkFile Bool
lenient String
path
            else do
            Bool
dirp <- String -> IO Bool
doesDirectoryExist String
path
            if Bool
dirp
              then do
              let dir :: String
dir = String -> (String -> String) -> Maybe String -> String
forall b a. b -> (a -> b) -> Maybe a -> b
maybe String
path (String
path String -> String -> String
</>) Maybe String
mdir
                  dirname :: String
dirname = String -> String
takeFileName (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ String -> String -> String
forall a. Eq a => [a] -> [a] -> [a]
dropSuffix String
"/" String
path
              Maybe String
mspec <- Bool -> String -> IO (Maybe String)
checkFile Bool
True (String -> IO (Maybe String)) -> String -> IO (Maybe String)
forall a b. (a -> b) -> a -> b
$ String
dir String -> String -> String
</> String
dirname String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
".spec"
              case Maybe String
mspec of
                Just String
spec -> Maybe String -> IO (Maybe String)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe String -> IO (Maybe String))
-> Maybe String -> IO (Maybe String)
forall a b. (a -> b) -> a -> b
$ String -> Maybe String
forall a. a -> Maybe a
Just String
spec
                Maybe String
Nothing -> do
                  Bool
dead <- String -> IO Bool
doesFileExist (String -> IO Bool) -> String -> IO Bool
forall a b. (a -> b) -> a -> b
$ String
dir String -> String -> String
</> String
"dead.package"
                  if Bool
dead then Maybe String -> IO (Maybe String)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe String
forall a. Maybe a
Nothing
                    else do
                    [String]
specs <- String -> String -> IO [String]
filesWithExtension String
dir String
".spec"
                    case [String]
specs of
                      [String
spec] -> Maybe String -> IO (Maybe String)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe String -> IO (Maybe String))
-> Maybe String -> IO (Maybe String)
forall a b. (a -> b) -> a -> b
$ String -> Maybe String
forall a. a -> Maybe a
Just (String -> Maybe String) -> String -> Maybe String
forall a b. (a -> b) -> a -> b
$ String
dir String -> String -> String
</> String
spec
                      [String]
_ -> if Bool
lenient then Maybe String -> IO (Maybe String)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe String
forall a. Maybe a
Nothing
                           else String -> IO (Maybe String)
forall a. String -> a
error' (String -> IO (Maybe String)) -> String -> IO (Maybe String)
forall a b. (a -> b) -> a -> b
$
                                if [String] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [String]
specs
                                then String
"No spec file found in " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
path
                                else String
"More than one .spec file found in " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
dir
              else String -> IO (Maybe String)
forall a. String -> a
error' (String -> IO (Maybe String)) -> String -> IO (Maybe String)
forall a b. (a -> b) -> a -> b
$ String
"No spec file found for " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
path
          where
            checkFile :: Bool -> FilePath -> IO (Maybe FilePath)
            checkFile :: Bool -> String -> IO (Maybe String)
checkFile Bool
may String
f = do
              Bool
e <- String -> IO Bool
doesFileExist String
f
              Maybe String -> IO (Maybe String)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe String -> IO (Maybe String))
-> Maybe String -> IO (Maybe String)
forall a b. (a -> b) -> a -> b
$ if Bool
e
                       then String -> Maybe String
forall a. a -> Maybe a
Just String
f
                       else if Bool
may
                            then Maybe String
forall a. Maybe a
Nothing
                            else String -> Maybe String
forall a. String -> a
error' (String -> Maybe String) -> String -> Maybe String
forall a b. (a -> b) -> a -> b
$ String
f String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" not found"

    getBuildGraph :: [SourcePackage] -> PackageGraph
    getBuildGraph :: [SourcePackage] -> PackageGraph
getBuildGraph [SourcePackage]
srcPkgs =
       let nodes :: [(Node, SourcePackage)]
nodes = [Node] -> [SourcePackage] -> [(Node, SourcePackage)]
forall a b. [a] -> [b] -> [(a, b)]
zip [Node
0..] [SourcePackage]
srcPkgs
           nodeDict :: [(String, Node)]
nodeDict = [String] -> [Node] -> [(String, Node)]
forall a b. [a] -> [b] -> [(a, b)]
zip ((SourcePackage -> String) -> [SourcePackage] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map SourcePackage -> String
packagePath [SourcePackage]
srcPkgs) [Node
0..]
           edges :: [LEdge ()]
edges = do
              (Node
srcNode,SourcePackage
srcPkg) <- [(Node, SourcePackage)]
nodes
              Node
dstNode <- (String -> Maybe Node) -> [String] -> [Node]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe (String -> [(String, Node)] -> Maybe Node
forall a b. Eq a => a -> [(a, b)] -> Maybe b
`lookup` [(String, Node)]
nodeDict) (SourcePackage -> [String]
dependencies SourcePackage
srcPkg)
              Bool -> [()]
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Node
dstNode Node -> Node -> Bool
forall a. Eq a => a -> a -> Bool
/= Node
srcNode)
              LEdge () -> [LEdge ()]
forall a. a -> [a]
forall (m :: * -> *) a. Monad m => a -> m a
return (LEdge () -> [LEdge ()]) -> LEdge () -> [LEdge ()]
forall a b. (a -> b) -> a -> b
$ if Bool
rev
                       then (Node
dstNode, Node
srcNode, ())
                       else (Node
srcNode, Node
dstNode,  ())
       in [LNode String] -> [LEdge ()] -> PackageGraph
forall a b. [LNode a] -> [LEdge b] -> Gr a b
forall (gr :: * -> * -> *) a b.
Graph gr =>
[LNode a] -> [LEdge b] -> gr a b
G.mkGraph (((Node, SourcePackage) -> LNode String)
-> [(Node, SourcePackage)] -> [LNode String]
forall a b. (a -> b) -> [a] -> [b]
map ((SourcePackage -> String) -> (Node, SourcePackage) -> LNode String
forall a b. (a -> b) -> (Node, a) -> (Node, b)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap SourcePackage -> String
packagePath) [(Node, SourcePackage)]
nodes) ([LEdge ()] -> PackageGraph) -> [LEdge ()] -> PackageGraph
forall a b. (a -> b) -> a -> b
$ [LEdge ()] -> [LEdge ()]
forall a. Eq a => [a] -> [a]
nub [LEdge ()]
edges

    checkForCycles :: PackageGraph -> IO ()
    checkForCycles :: PackageGraph -> IO ()
checkForCycles PackageGraph
graph = do
      let cycles :: [[Node]]
cycles = ([Node] -> Bool) -> [[Node]] -> [[Node]]
forall a. (a -> Bool) -> [a] -> [a]
filter ((Node -> Node -> Bool
forall a. Ord a => a -> a -> Bool
>= Node
2) (Node -> Bool) -> ([Node] -> Node) -> [Node] -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Node] -> Node
forall a. [a] -> Node
forall (t :: * -> *) a. Foldable t => t a -> Node
length) (PackageGraph -> [[Node]]
forall (gr :: * -> * -> *) a b. Graph gr => gr a b -> [[Node]]
scc PackageGraph
graph)
      Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless ([[Node]] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [[Node]]
cycles) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
        let plural :: String
plural = if [[Node]] -> Node
forall a. [a] -> Node
forall (t :: * -> *) a. Foldable t => t a -> Node
length [[Node]]
cycles Node -> Node -> Bool
forall a. Ord a => a -> a -> Bool
> Node
1 then String
"s" else String
""
        String -> IO ()
forall a. String -> a
error' (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ [String] -> String
unlines ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$
          (String
"ordering not possible due to build dependency cycle" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
plural String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
":\n") String -> [String] -> [String]
forall a. a -> [a] -> [a]
: [String] -> [[String]] -> [String]
forall a. [a] -> [[a]] -> [a]
intercalate [String
""] (([Node] -> [String]) -> [[Node]] -> [[String]]
forall a b. (a -> b) -> [a] -> [b]
map (([String], [[String]]) -> [String]
renderCycles (([String], [[String]]) -> [String])
-> ([Node] -> ([String], [[String]])) -> [Node] -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Node] -> ([String], [[String]])
subcycles) [[Node]]
cycles)
      where
        -- shortest subcycle
        subcycles :: [G.Node] -> ([FilePath],[[FilePath]])
        subcycles :: [Node] -> ([String], [[String]])
subcycles [] = String -> ([String], [[String]])
forall a. HasCallStack => String -> a
error String
"cyclic graph with no nodes!"
        subcycles [Node]
cycle' =
          let shorter :: [[Node]]
shorter = ([Node] -> [Node]) -> [[Node]] -> [[Node]]
forall b a. Ord b => (a -> b) -> [a] -> [a]
nubOrdOn [Node] -> [Node]
forall a. Ord a => [a] -> [a]
sort ([[Node]] -> [[Node]]) -> [[Node]] -> [[Node]]
forall a b. (a -> b) -> a -> b
$ ([Node] -> Node) -> [[Node]] -> [[Node]]
forall b a. Ord b => (a -> b) -> [a] -> [a]
sortOn [Node] -> Node
forall a. [a] -> Node
forall (t :: * -> *) a. Foldable t => t a -> Node
length ([[Node]] -> [[Node]]) -> [[Node]] -> [[Node]]
forall a b. (a -> b) -> a -> b
$ ([Node] -> Bool) -> [[Node]] -> [[Node]]
forall a. (a -> Bool) -> [a] -> [a]
filter ((Node -> Node -> Bool
forall a. Ord a => a -> a -> Bool
< [Node] -> Node
forall a. [a] -> Node
forall (t :: * -> *) a. Foldable t => t a -> Node
length [Node]
cycle') (Node -> Bool) -> ([Node] -> Node) -> [Node] -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Node] -> Node
forall a. [a] -> Node
forall (t :: * -> *) a. Foldable t => t a -> Node
length) ([[Node]] -> [[Node]]) -> [[Node]] -> [[Node]]
forall a b. (a -> b) -> a -> b
$ ((Node, Node, Node) -> Maybe [Node])
-> [(Node, Node, Node)] -> [[Node]]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe (Node, Node, Node) -> Maybe [Node]
forall {c}. (Node, Node, c) -> Maybe [Node]
findSp ([(Node, Node, Node)] -> [[Node]])
-> [(Node, Node, Node)] -> [[Node]]
forall a b. (a -> b) -> a -> b
$ Gr String Node -> [(Node, Node, Node)]
forall a b. Gr a b -> [LEdge b]
forall (gr :: * -> * -> *) a b. Graph gr => gr a b -> [LEdge b]
G.labEdges Gr String Node
sg
          in (PackageGraph -> [Node] -> [String]
forall a b. Gr a b -> [Node] -> [a]
nodeLabels PackageGraph
graph [Node]
cycle', ([Node] -> [String]) -> [[Node]] -> [[String]]
forall a b. (a -> b) -> [a] -> [b]
map (Gr String Node -> [Node] -> [String]
forall a b. Gr a b -> [Node] -> [a]
nodeLabels Gr String Node
sg) [[Node]]
shorter)
          where
            sg :: Gr String Node
sg = (() -> Node) -> PackageGraph -> Gr String Node
forall (gr :: * -> * -> *) b c a.
DynGraph gr =>
(b -> c) -> gr a b -> gr a c
G.emap (Node -> () -> Node
forall a b. a -> b -> a
const (Node
1 :: Int)) (PackageGraph -> Gr String Node) -> PackageGraph -> Gr String Node
forall a b. (a -> b) -> a -> b
$ [Node] -> PackageGraph -> PackageGraph
forall (gr :: * -> * -> *) a b.
DynGraph gr =>
[Node] -> gr a b -> gr a b
G.subgraph [Node]
cycle' PackageGraph
graph

            findSp :: (Node, Node, c) -> Maybe [Node]
findSp (Node
i,Node
j,c
_) | Gr String Node -> Edge -> Bool
forall (gr :: * -> * -> *) a b. Graph gr => gr a b -> Edge -> Bool
G.hasEdge Gr String Node
sg (Node
i,Node
j) = Node -> Node -> Gr String Node -> Maybe [Node]
forall (gr :: * -> * -> *) b a.
(Graph gr, Real b) =>
Node -> Node -> gr a b -> Maybe [Node]
sp Node
j Node
i Gr String Node
sg
                           | Gr String Node -> Edge -> Bool
forall (gr :: * -> * -> *) a b. Graph gr => gr a b -> Edge -> Bool
G.hasEdge Gr String Node
sg (Node
j,Node
i) = Node -> Node -> Gr String Node -> Maybe [Node]
forall (gr :: * -> * -> *) b a.
(Graph gr, Real b) =>
Node -> Node -> gr a b -> Maybe [Node]
sp Node
i Node
j Gr String Node
sg
                           | Bool
otherwise = Maybe [Node]
forall a. Maybe a
Nothing

        renderCycles :: ([FilePath],[[FilePath]]) -> [String]
        renderCycles :: ([String], [[String]]) -> [String]
renderCycles ([String]
c,[[String]]
sc) =
          [String] -> String
unwords [String]
c String -> [String] -> [String]
forall a. a -> [a] -> [a]
: if [[String]] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [[String]]
sc then [] else String
"\nShortest path subcycles: " String -> [String] -> [String]
forall a. a -> [a] -> [a]
: ([String] -> String) -> [[String]] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map [String] -> String
unwords [[String]]
sc

    getDepsSrcResolved :: [(FilePath,[String],[String])] -> FilePath -> Maybe [FilePath]
    getDepsSrcResolved :: [(String, [String], [String])] -> String -> Maybe [String]
getDepsSrcResolved [(String, [String], [String])]
metadata String
pkg =
      (String -> String) -> [String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map String -> String
resolveBase ([String] -> [String])
-> ((String, [String], [String]) -> [String])
-> (String, [String], [String])
-> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String, [String], [String]) -> [String]
forall {a} {b} {c}. (a, b, c) -> c
thd ((String, [String], [String]) -> [String])
-> Maybe (String, [String], [String]) -> Maybe [String]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ((String, [String], [String]) -> Bool)
-> [(String, [String], [String])]
-> Maybe (String, [String], [String])
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find ((String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
pkg) (String -> Bool)
-> ((String, [String], [String]) -> String)
-> (String, [String], [String])
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String, [String], [String]) -> String
forall a b c. (a, b, c) -> a
fst3) [(String, [String], [String])]
metadata
      where
        resolveBase :: FilePath -> FilePath
        resolveBase :: String -> String
resolveBase String
br =
          case ((String, [String], [String]) -> Maybe String)
-> [(String, [String], [String])] -> [String]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe (\ (String
p,[String]
provs,[String]
_) -> if String
br String -> [String] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String]
provs then String -> Maybe String
forall a. a -> Maybe a
Just String
p else Maybe String
forall a. Maybe a
Nothing) [(String, [String], [String])]
metadata of
            [] -> String
br
            [String
p] -> String
p
            [String]
ps -> String -> String
forall a. String -> a
error' (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ String
pkg String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
": " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
br String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" is provided by: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ [String] -> String
unwords [String]
ps

        thd :: (a, b, c) -> c
thd (a
_,b
_,c
c) = c
c

    fst3 :: (a,b,c) -> a
    fst3 :: forall a b c. (a, b, c) -> a
fst3 (a
a,b
_,c
_) = a
a

    nodeLabels :: Gr a b -> [G.Node] -> [a]
    nodeLabels :: forall a b. Gr a b -> [Node] -> [a]
nodeLabels Gr a b
graph =
       (Node -> a) -> [Node] -> [a]
forall a b. (a -> b) -> [a] -> [b]
map (a -> Maybe a -> a
forall a. a -> Maybe a -> a
fromMaybe (String -> a
forall a. HasCallStack => String -> a
error String
"node not found in graph") (Maybe a -> a) -> (Node -> Maybe a) -> Node -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
            Gr a b -> Node -> Maybe a
forall (gr :: * -> * -> *) a b.
Graph gr =>
gr a b -> Node -> Maybe a
G.lab Gr a b
graph)

-- | Alias for createGraph4
--
-- @since 0.4.4 (deprecated)
createGraph'''' :: Bool -- ^ check for cycles
                -> [String] -- ^ ignored BuildRequires
                -> [String] -- ^ rpmspec options
                -> Bool -- ^ verbose
                -> Bool -- ^ lenient (skip rpmspec failures)
                -> Bool -- ^ reverse dependency graph
                -> Maybe FilePath -- ^ look for spec file in a subdirectory
                -> [FilePath] -- ^ package paths (directories or spec filepaths)
                -> IO PackageGraph -- ^ dependency graph labelled by package paths
createGraph'''' :: Bool
-> [String]
-> [String]
-> Bool
-> Bool
-> Bool
-> Maybe String
-> [String]
-> IO PackageGraph
createGraph'''' = Bool
-> [String]
-> [String]
-> Bool
-> Bool
-> Bool
-> Maybe String
-> [String]
-> IO PackageGraph
createGraph4

-- | A flipped version of subgraph
subgraph' :: Gr a b -> [G.Node] -> Gr a b
subgraph' :: forall a b. Gr a b -> [Node] -> Gr a b
subgraph' = ([Node] -> Gr a b -> Gr a b) -> Gr a b -> [Node] -> Gr a b
forall a b c. (a -> b -> c) -> b -> a -> c
flip [Node] -> Gr a b -> Gr a b
forall (gr :: * -> * -> *) a b.
DynGraph gr =>
[Node] -> gr a b -> gr a b
G.subgraph

-- | Return the bottom-up list of dependency layers of a graph
packageLayers :: PackageGraph -> [[FilePath]]
packageLayers :: PackageGraph -> [[String]]
packageLayers PackageGraph
graph =
  if PackageGraph -> Bool
forall a b. Gr a b -> Bool
forall (gr :: * -> * -> *) a b. Graph gr => gr a b -> Bool
G.isEmpty PackageGraph
graph then []
  else
    let layer :: [LNode String]
layer = PackageGraph -> [LNode String]
lowestLayer' PackageGraph
graph
    in (LNode String -> String) -> [LNode String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map LNode String -> String
forall a b. (a, b) -> b
snd [LNode String]
layer [String] -> [[String]] -> [[String]]
forall a. a -> [a] -> [a]
: PackageGraph -> [[String]]
packageLayers ([Node] -> PackageGraph -> PackageGraph
forall (gr :: * -> * -> *) a b.
Graph gr =>
[Node] -> gr a b -> gr a b
G.delNodes ((LNode String -> Node) -> [LNode String] -> [Node]
forall a b. (a -> b) -> [a] -> [b]
map LNode String -> Node
forall a b. (a, b) -> a
fst [LNode String]
layer) PackageGraph
graph)

-- | The lowest dependencies of a PackageGraph
lowestLayer :: PackageGraph -> [FilePath]
lowestLayer :: PackageGraph -> [String]
lowestLayer PackageGraph
graph =
  (LNode String -> String) -> [LNode String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map LNode String -> String
forall a b. (a, b) -> b
snd ([LNode String] -> [String]) -> [LNode String] -> [String]
forall a b. (a -> b) -> a -> b
$ PackageGraph -> [LNode String]
forall a b. Gr a b -> [LNode a]
forall (gr :: * -> * -> *) a b. Graph gr => gr a b -> [LNode a]
G.labNodes (PackageGraph -> [LNode String]) -> PackageGraph -> [LNode String]
forall a b. (a -> b) -> a -> b
$ (Node -> Bool) -> PackageGraph -> PackageGraph
forall (gr :: * -> * -> *) a b.
DynGraph gr =>
(Node -> Bool) -> gr a b -> gr a b
G.nfilter ((Node -> Node -> Bool
forall a. Eq a => a -> a -> Bool
==Node
0) (Node -> Bool) -> (Node -> Node) -> Node -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PackageGraph -> Node -> Node
forall (gr :: * -> * -> *) a b. Graph gr => gr a b -> Node -> Node
G.indeg PackageGraph
graph) PackageGraph
graph

-- | The lowest dependency nodes of a PackageGraph
lowestLayer' :: PackageGraph -> [G.LNode FilePath]
lowestLayer' :: PackageGraph -> [LNode String]
lowestLayer' PackageGraph
graph =
  PackageGraph -> [LNode String]
forall a b. Gr a b -> [LNode a]
forall (gr :: * -> * -> *) a b. Graph gr => gr a b -> [LNode a]
G.labNodes (PackageGraph -> [LNode String]) -> PackageGraph -> [LNode String]
forall a b. (a -> b) -> a -> b
$ (Node -> Bool) -> PackageGraph -> PackageGraph
forall (gr :: * -> * -> *) a b.
DynGraph gr =>
(Node -> Bool) -> gr a b -> gr a b
G.nfilter ((Node -> Node -> Bool
forall a. Eq a => a -> a -> Bool
==Node
0) (Node -> Bool) -> (Node -> Node) -> Node -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PackageGraph -> Node -> Node
forall (gr :: * -> * -> *) a b. Graph gr => gr a b -> Node -> Node
G.indeg PackageGraph
graph) PackageGraph
graph

-- | The leaf (outer) packages of a PackageGraph
packageLeaves :: PackageGraph -> [FilePath]
packageLeaves :: PackageGraph -> [String]
packageLeaves PackageGraph
graph =
  (LNode String -> String) -> [LNode String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map LNode String -> String
forall a b. (a, b) -> b
snd ([LNode String] -> [String]) -> [LNode String] -> [String]
forall a b. (a -> b) -> a -> b
$ PackageGraph -> [LNode String]
forall a b. Gr a b -> [LNode a]
forall (gr :: * -> * -> *) a b. Graph gr => gr a b -> [LNode a]
G.labNodes (PackageGraph -> [LNode String]) -> PackageGraph -> [LNode String]
forall a b. (a -> b) -> a -> b
$ (Node -> Bool) -> PackageGraph -> PackageGraph
forall (gr :: * -> * -> *) a b.
DynGraph gr =>
(Node -> Bool) -> gr a b -> gr a b
G.nfilter ((Node -> Node -> Bool
forall a. Eq a => a -> a -> Bool
==Node
0) (Node -> Bool) -> (Node -> Node) -> Node -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PackageGraph -> Node -> Node
forall (gr :: * -> * -> *) a b. Graph gr => gr a b -> Node -> Node
G.outdeg PackageGraph
graph) PackageGraph
graph

-- | Returns packages independent of all the rest of the graph
separatePackages :: PackageGraph -> [FilePath]
separatePackages :: PackageGraph -> [String]
separatePackages PackageGraph
graph =
  (LNode String -> String) -> [LNode String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map LNode String -> String
forall a b. (a, b) -> b
snd ([LNode String] -> [String]) -> [LNode String] -> [String]
forall a b. (a -> b) -> a -> b
$ PackageGraph -> [LNode String]
forall a b. Gr a b -> [LNode a]
forall (gr :: * -> * -> *) a b. Graph gr => gr a b -> [LNode a]
G.labNodes (PackageGraph -> [LNode String]) -> PackageGraph -> [LNode String]
forall a b. (a -> b) -> a -> b
$ (Node -> Bool) -> PackageGraph -> PackageGraph
forall (gr :: * -> * -> *) a b.
DynGraph gr =>
(Node -> Bool) -> gr a b -> gr a b
G.nfilter ((Node -> Node -> Bool
forall a. Eq a => a -> a -> Bool
==Node
0) (Node -> Bool) -> (Node -> Node) -> Node -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PackageGraph -> Node -> Node
forall (gr :: * -> * -> *) a b. Graph gr => gr a b -> Node -> Node
G.deg PackageGraph
graph) PackageGraph
graph

-- | Return graphviz dot format of graph
printGraph :: PackageGraph -> IO ()
printGraph :: PackageGraph -> IO ()
printGraph PackageGraph
g = do
  String -> IO ()
putStrLn String
"digraph {"
  [LNode String] -> (LNode String -> IO ()) -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ (PackageGraph -> [LNode String]
forall a b. Gr a b -> [LNode a]
forall (gr :: * -> * -> *) a b. Graph gr => gr a b -> [LNode a]
G.labNodes PackageGraph
g) ((LNode String -> IO ()) -> IO ())
-> (LNode String -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \ (Node
n,String
l) -> do
    String -> IO ()
putStr (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String -> String
forall a. Show a => a -> String
show String
l
    String -> IO ()
putStrLn (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ [String] -> String
renderDeps ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ (String -> String) -> [String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map String -> String
forall a. Show a => a -> String
show ([String] -> [String]) -> [String] -> [String]
forall a b. (a -> b) -> a -> b
$ ((Node, ()) -> Maybe String) -> [(Node, ())] -> [String]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe (PackageGraph -> Node -> Maybe String
forall (gr :: * -> * -> *) a b.
Graph gr =>
gr a b -> Node -> Maybe a
G.lab PackageGraph
g (Node -> Maybe String)
-> ((Node, ()) -> Node) -> (Node, ()) -> Maybe String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Node, ()) -> Node
forall a b. (a, b) -> a
fst) ([(Node, ())] -> [String]) -> [(Node, ())] -> [String]
forall a b. (a -> b) -> a -> b
$ PackageGraph -> Node -> [(Node, ())]
forall (gr :: * -> * -> *) a b.
Graph gr =>
gr a b -> Node -> [(Node, b)]
G.lsuc PackageGraph
g Node
n
  String -> IO ()
putStrLn String
"}"
  where
    renderDeps :: [String] -> String
    renderDeps :: [String] -> String
renderDeps [] = String
""
    renderDeps [String
d] = String
" -> " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
d
    renderDeps [String]
ds = String
" -> {" String -> String -> String
forall a. [a] -> [a] -> [a]
++ [String] -> String
unwords [String]
ds String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"}"

-- | Render graph with graphviz X11 preview
renderGraph :: PackageGraph -> IO ()
renderGraph :: PackageGraph -> IO ()
renderGraph PackageGraph
graph = do
  Bool
gv <- IO Bool
isGraphvizInstalled
  if Bool
gv
    then do
    let g :: Gr String String
g = (() -> String) -> PackageGraph -> Gr String String
forall (gr :: * -> * -> *) b c a.
DynGraph gr =>
(b -> c) -> gr a b -> gr a c
G.emap (String -> () -> String
forall a b. a -> b -> a
const (String
"" :: String)) PackageGraph
graph
    DotGraph Node -> GraphvizCanvas -> IO ()
forall (dg :: * -> *) n.
PrintDotRepr dg n =>
dg n -> GraphvizCanvas -> IO ()
runGraphvizCanvas' ((GraphvizParams Node String String () String
 -> Gr String String -> DotGraph Node)
-> GraphvizParams Node String String () String
-> Gr String String
-> DotGraph Node
forall el (gr :: * -> * -> *) nl cl l a.
(Ord el, Graph gr) =>
(GraphvizParams Node nl el cl l -> gr nl el -> a)
-> GraphvizParams Node nl el cl l -> gr nl el -> a
setDirectedness GraphvizParams Node String String () String
-> Gr String String -> DotGraph Node
forall cl (gr :: * -> * -> *) nl el l.
(Ord cl, Graph gr) =>
GraphvizParams Node nl el cl l -> gr nl el -> DotGraph Node
graphToDot GraphvizParams Node String String () String
forall nl el n.
(Labellable nl, Labellable el) =>
GraphvizParams n nl el () nl
quickParams Gr String String
g) GraphvizCanvas
Xlib
    else String -> IO ()
forall a. String -> a
error' String
"please install graphviz first"

-- | Given a list of one or more packages, look for dependencies
-- in neighboring packages and return a package dependency graph
--
-- @since 0.4.9
depsGraph :: Bool -- ^ whether to look for reverse dependencies
          -> [String] -- ^ rpm options
          -> Bool -- ^ verbose output
          -> [String] -- ^ packages to exclude
          -> [String] -- ^ buildrequires to ignore
          -> Bool -- ^ allow rpmspec failures
          -> Maybe FilePath -- ^ subdir for packages
          -> [FilePath] -- ^ list of package paths
          -> IO PackageGraph -- ^ dependency graph of the packages
depsGraph :: Bool
-> [String]
-> Bool
-> [String]
-> [String]
-> Bool
-> Maybe String
-> [String]
-> IO PackageGraph
depsGraph Bool
rev [String]
rpmopts Bool
verbose [String]
excludedPkgs [String]
ignoredBRs Bool
lenient Maybe String
mdir [String]
pkgs =
  String -> IO [String]
listDirectory String
"." IO [String] -> ([String] -> IO PackageGraph) -> IO PackageGraph
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>=
  Bool
-> [String]
-> Bool
-> [String]
-> [String]
-> Bool
-> Maybe String
-> [String]
-> [String]
-> IO PackageGraph
depsGraphDeps Bool
rev [String]
rpmopts Bool
verbose [String]
excludedPkgs [String]
ignoredBRs Bool
lenient Maybe String
mdir [String]
pkgs ([String] -> IO PackageGraph)
-> ([String] -> [String]) -> [String] -> IO PackageGraph
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
  ([String] -> [String] -> [String]
forall a. Eq a => [a] -> [a] -> [a]
`union` [String]
pkgs)

-- | Given a list of one or more packages and a list of potential dependencies,
-- return a package dependency graph
--
-- @since 0.4.10
depsGraphDeps :: Bool -- ^ whether to look for reverse dependencies
             -> [String] -- ^ rpm options
             -> Bool -- ^ verbose output
             -> [String] -- ^ packages to exclude
             -> [String] -- ^ buildrequires to ignore
             -> Bool -- ^ allow rpmspec failures
             -> Maybe FilePath -- ^ subdir for packages
             -> [FilePath] -- ^ list of package paths
             -> [FilePath] -- ^ list of potential dependency paths
             -> IO PackageGraph -- ^ dependency graph of the packages
depsGraphDeps :: Bool
-> [String]
-> Bool
-> [String]
-> [String]
-> Bool
-> Maybe String
-> [String]
-> [String]
-> IO PackageGraph
depsGraphDeps Bool
rev [String]
rpmopts Bool
verbose [String]
excludedPkgs [String]
ignoredBRs Bool
lenient Maybe String
mdir [String]
pkgs [String]
deps = do
  IO Bool -> IO () -> IO ()
forall (m :: * -> *). Monad m => m Bool -> m () -> m ()
unlessM ([Bool] -> Bool
forall (t :: * -> *). Foldable t => t Bool -> Bool
and ([Bool] -> Bool) -> IO [Bool] -> IO Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (String -> IO Bool) -> [String] -> IO [Bool]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
mapM String -> IO Bool
doesDirectoryExist [String]
pkgs) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$
    String -> IO ()
forall a. String -> a
errorWithoutStackTrace String
"Please use package directory paths"
  -- filter out dotfiles
  [String]
-> [String]
-> Bool
-> Bool
-> Bool
-> Maybe String
-> [String]
-> IO PackageGraph
createGraph3 [String]
ignoredBRs [String]
rpmopts Bool
verbose Bool
lenient (Bool -> Bool
not Bool
rev) Maybe String
mdir ((String -> Bool) -> [String] -> [String]
forall a. (a -> Bool) -> [a] -> [a]
filter (\String
d -> String
d String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` [String]
excludedPkgs Bool -> Bool -> Bool
&& String -> Char
forall a. HasCallStack => [a] -> a
head String
d Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
'.') [String]
deps) IO PackageGraph
-> (PackageGraph -> IO PackageGraph) -> IO PackageGraph
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>=
    [String]
-> Bool
-> Bool
-> Bool
-> Maybe String
-> [String]
-> IO PackageGraph
createGraph2 [String]
rpmopts Bool
verbose Bool
lenient Bool
True Maybe String
mdir ([String] -> IO PackageGraph)
-> (PackageGraph -> [String]) -> PackageGraph -> IO PackageGraph
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [String] -> PackageGraph -> [String]
dependencyNodes [String]
pkgs

-- | Used to control the output from sortGraph
data Components = Parallel -- ^ separate independent stacks
                | Combine -- ^ combine indepdendent stacks together
                | Connected -- ^ only stack of packages
                | Separate -- ^ only independent packages in the package set

-- | topological sort packages from a PackageGraph arranged by Components
--
-- @since 0.4.10
topsortGraph :: Components -> PackageGraph -> [[String]]
topsortGraph :: Components -> PackageGraph -> [[String]]
topsortGraph Components
opt PackageGraph
graph =
  case Components
opt of
    Components
Parallel -> ([Node] -> [String]) -> [[Node]] -> [[String]]
forall a b. (a -> b) -> [a] -> [b]
map (PackageGraph -> [String]
forall (gr :: * -> * -> *) a b. Graph gr => gr a b -> [a]
topsort' (PackageGraph -> [String])
-> ([Node] -> PackageGraph) -> [Node] -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PackageGraph -> [Node] -> PackageGraph
forall a b. Gr a b -> [Node] -> Gr a b
subgraph' PackageGraph
graph) (PackageGraph -> [[Node]]
forall (gr :: * -> * -> *) a b. Graph gr => gr a b -> [[Node]]
components PackageGraph
graph)
    Components
Combine -> [String] -> [[String]]
forall a. a -> [a]
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([String] -> [[String]]) -> [String] -> [[String]]
forall a b. (a -> b) -> a -> b
$ PackageGraph -> [String]
forall (gr :: * -> * -> *) a b. Graph gr => gr a b -> [a]
topsort' PackageGraph
graph
    Components
Connected ->
      ([Node] -> [String]) -> [[Node]] -> [[String]]
forall a b. (a -> b) -> [a] -> [b]
map (PackageGraph -> [String]
forall (gr :: * -> * -> *) a b. Graph gr => gr a b -> [a]
topsort' (PackageGraph -> [String])
-> ([Node] -> PackageGraph) -> [Node] -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PackageGraph -> [Node] -> PackageGraph
forall a b. Gr a b -> [Node] -> Gr a b
subgraph' PackageGraph
graph) ([[Node]] -> [[String]]) -> [[Node]] -> [[String]]
forall a b. (a -> b) -> a -> b
$ ([Node] -> Bool) -> [[Node]] -> [[Node]]
forall a. (a -> Bool) -> [a] -> [a]
filter ((Node -> Node -> Bool
forall a. Ord a => a -> a -> Bool
>Node
1) (Node -> Bool) -> ([Node] -> Node) -> [Node] -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Node] -> Node
forall a. [a] -> Node
forall (t :: * -> *) a. Foldable t => t a -> Node
length) (PackageGraph -> [[Node]]
forall (gr :: * -> * -> *) a b. Graph gr => gr a b -> [[Node]]
components PackageGraph
graph)
    Components
Separate -> [String] -> [[String]]
forall a. a -> [a]
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([String] -> [[String]]) -> [String] -> [[String]]
forall a b. (a -> b) -> a -> b
$ PackageGraph -> [String]
separatePackages PackageGraph
graph

#if !MIN_VERSION_simple_cmd(0,2,4)
filesWithExtension :: FilePath -> String -> IO [FilePath]
filesWithExtension dir ext =
  filter (ext `isExtensionOf`) <$> listDirectory dir

#if !MIN_VERSION_filepath(1,4,2)
isExtensionOf :: String -> FilePath -> Bool
isExtensionOf ext@('.':_) = isSuffixOf ext . takeExtensions
isExtensionOf ext         = isSuffixOf ('.':ext) . takeExtensions
#endif
#endif