{-# LANGUAGE CPP #-}
module Main (main) where
import Control.Applicative (
#if !MIN_VERSION_simple_cmd_args(0,1,3)
(<|>),
#endif
#if !MIN_VERSION_simple_cmd_args(0,1,4)
many, some,
#endif
#if !MIN_VERSION_base(4,8,0)
(<$>), (<*>)
#endif
)
import Control.Monad.Extra
import Data.Graph.Inductive.Query.DFS (components)
import Data.List (intercalate)
import Data.List.Extra (dropSuffix)
import SimpleCmdArgs
import System.Directory (doesDirectoryExist,
#if MIN_VERSION_directory(1,2,5)
listDirectory
#else
getDirectoryContents
#endif
)
import Distribution.RPM.Build.Graph
import Distribution.RPM.Build.Order
import Paths_rpmbuild_order (version)
main :: IO ()
main =
simpleCmdArgs (Just version) "Order packages by build dependencies"
"Sort package sources (spec files) in build dependency order" $
subcommands
[ Subcommand "sort" "sort packages" $
sortPackages <$> rpmOpts <*> verboseOpt <*> lenientOpt <*> componentsOpt <*> subdirOpt <*> pkgArgs
, Subcommand "deps" "sort dependencies" $
depsPackages False <$> rpmOpts <*> verboseOpt <*> excludeOpts <*> ignoredBRopts <*> lenientOpt <*> combineOpt <*> subdirOpt <*> pkgArgs
, Subcommand "rdeps" "sort dependents" $
depsPackages True <$> rpmOpts <*> verboseOpt <*> excludeOpts <*> ignoredBRopts <*> lenientOpt <*> combineOpt <*> subdirOpt <*> pkgArgs
, Subcommand "layers" "ordered output of dependency layers" $
layerPackages <$> rpmOpts <*> verboseOpt <*> lenientOpt <*> combineOpt <*> subdirOpt <*> pkgArgs
, Subcommand "chain" "ordered output suitable for a chain-build" $
chainPackages <$> rpmOpts <*> verboseOpt <*> lenientOpt <*> combineOpt <*> subdirOpt <*> pkgArgs
, Subcommand "leaves" "List of the top leaves of package graph" $
leavesPackages <$> rpmOpts <*> verboseOpt <*> lenientOpt <*> subdirOpt <*> pkgArgs
, Subcommand "roots" "List lowest root packages" $
rootPackages <$> rpmOpts <*> verboseOpt <*> lenientOpt <*> subdirOpt <*> pkgArgs
, Subcommand "render" "Show graph with graphviz" $
renderPkgGraph <$> switchWith 'g' "gv-output" "Output graph in gv/dot format" <*> rpmOpts <*> verboseOpt <*> lenientOpt <*> subdirOpt <*> pkgArgs
]
where
verboseOpt = switchWith 'v' "verbose" "Verbose output for debugging"
lenientOpt = switchWith 'l' "lenient" "Ignore rpmspec errors"
combineOpt = switchWith 'c' "combine" "Combine independent packages"
subdirOpt = optional (strOptionWith 'd' "dir" "SUBDIR" "Branch directory")
pkgArgs = some (strArg "PKG...")
componentsOpt =
flagWith' Connected 'C' "connected" "Only include connected packages" <|>
flagWith' Separate 'i' "independent" "Only list independent packages" <|>
flagWith Parallel Combine 'c' "combine" "Combine connected and independent packages"
rpmOpts = many (strOptionWith 'r' "rpmopt" "RPMOPT" "Option for rpmspec")
ignoredBRopts = many (strOptionWith 'I' "ignore-BR" "PKG" "BuildRequires to exclude from graph")
excludeOpts = many (dropSuffix "/" <$> strOptionWith 'x' "exclude" "PKG" "Package dirs to exclude from graph")
sortPackages :: [String] -> Bool -> Bool -> Components -> Maybe FilePath -> [FilePath] -> IO ()
sortPackages rpmopts verbose lenient opts mdir pkgs = do
createGraph2 rpmopts verbose lenient True mdir pkgs >>= sortGraph opts
depsPackages :: Bool -> [String] -> Bool-> [String] -> [String] -> Bool -> Bool -> Maybe FilePath -> [FilePath] -> IO ()
depsPackages rev rpmopts verbose excludedPkgs ignoredBRs lenient parallel mdir pkgs = do
unlessM (and <$> mapM doesDirectoryExist pkgs) $
errorWithoutStackTrace "Please use package directory paths"
listDirectory "." >>=
-- filter out dotfiles
createGraph3 ignoredBRs rpmopts verbose lenient (not rev) mdir . filter ((/= '.') . head) . filter (`notElem` excludedPkgs) >>=
createGraph2 rpmopts verbose lenient True mdir . dependencyNodes pkgs >>=
sortGraph (if parallel then Parallel else Combine)
#if (defined(MIN_VERSION_directory) && MIN_VERSION_directory(1,2,5))
#else
listDirectory :: FilePath -> IO [FilePath]
listDirectory path =
filter f <$> getDirectoryContents path
where f filename = filename /= "." && filename /= ".."
#endif
layerPackages :: [String] -> Bool -> Bool -> Bool -> Maybe FilePath -> [FilePath] -> IO ()
layerPackages rpmopts verbose lenient combine mdir pkgs = do
graph <- createGraph2 rpmopts verbose lenient True mdir pkgs
if combine
then printLayers graph
else mapM_ (printLayers . subgraph' graph) (components graph)
where
printLayers = putStrLn . unlines . map unwords . packageLayers
chainPackages :: [String] -> Bool -> Bool -> Bool -> Maybe FilePath -> [FilePath] -> IO ()
chainPackages rpmopts verbose lenient combine mdir pkgs = do
graph <- createGraph2 rpmopts verbose lenient True mdir pkgs
if combine then doChain graph
else mapM_ (doChain . subgraph' graph) (components graph)
where
doChain graph =
let chain = intercalate [":"] $ packageLayers graph
in putStrLn $ unwords chain
leavesPackages :: [String] -> Bool -> Bool -> Maybe FilePath -> [FilePath] -> IO ()
leavesPackages rpmopts verbose lenient mdir pkgs = do
graph <- createGraph2 rpmopts verbose lenient True mdir pkgs
mapM_ putStrLn $ packageLeaves graph
rootPackages :: [String] -> Bool -> Bool -> Maybe FilePath -> [FilePath] -> IO ()
rootPackages rpmopts verbose lenient mdir pkgs = do
graph <- createGraph2 rpmopts verbose lenient True mdir pkgs
mapM_ putStrLn $ lowestLayer graph
renderPkgGraph :: Bool -> [String] -> Bool -> Bool -> Maybe FilePath
-> [FilePath] -> IO ()
renderPkgGraph dot rpmopts verbose lenient mdir pkgs =
createGraph4 False [] rpmopts verbose lenient False mdir pkgs >>=
if dot then printGraph else renderGraph