packages feed

rpmbuild-order 0.4.10 → 0.4.11

raw patch · 13 files changed

+323/−134 lines, 13 filesdep +regex-tdfadep −optparse-applicativePVP ok

version bump matches the API change (PVP)

Dependencies added: regex-tdfa

Dependencies removed: optparse-applicative

API changes (from Hackage documentation)

+ Distribution.RPM.Build.Order: dependencyLayersRpmOpts :: [String] -> [FilePath] -> IO [[FilePath]]
+ Distribution.RPM.Build.ProvReqs: rpmspecProvidesBuildRequires :: Bool -> [String] -> FilePath -> IO (Maybe ([String], [String]))

Files

ChangeLog.md view
@@ -1,3 +1,10 @@+# 0.4.11 (2024-02-18)+- ProvReqs: new module for rpmspec dependency functions+- ProvReqs: generate pkgconfig and cmake provides from %files (#4)+- Graph depsGraph: include package set in potential deps+- Order: add dependencyLayersRpmOpts+- more tests+ # 0.4.10 (2022-10-28) - Graph: add depsGraphDeps which takes a list of possible deps - Graph: add topsortGraph (factored out of sortGraph)
README.md view
@@ -11,13 +11,16 @@  ## Usage +`$ rpmbuild-order --version` ```-$ rpmbuild-order --version-0.4.10-$ rpmbuild-order --help+0.4.11+```+`$ rpmbuild-order --help`+``` Order packages by build dependencies  Usage: rpmbuild-order [--version] COMMAND+   Sort package sources (spec files) in build dependency order  Available options:@@ -33,7 +36,9 @@   leaves                   List of the top leaves of package graph   roots                    List lowest root packages   render                   Show graph with graphviz+``` +``` $ rpmbuild-order sort mycore mylib myapp mylib mycore myapp ```@@ -48,7 +53,9 @@ dependencies and reverse dependencies of a package can be obtained within the current set of checked out package sources. ie If you have a directory with packages:-`pkg1/ pkg2/ lib1/ lib2/ lib3/ misc1/`+```+pkg1/ pkg2/ lib1/ lib2/ lib3/ misc1/+``` then the output of `rpmbuild-order deps pkg1` might be `lib1 lib3 pkg1` for example. @@ -68,6 +75,8 @@ Please see their documentation for more details.  ## Notes and known problems+0. Handles pkgconfig() provides by grepping .spec for .pc files+ 1. Given packages A, B, C, where C depends on B, and B depends on A, and you call 
rpmbuild-order.1 view
@@ -1,7 +1,7 @@-.\" DO NOT MODIFY THIS FILE!  It was generated by help2man 1.49.2.-.TH RPMBUILD-ORDER "1" "August 2022" "rpmbuild-order 0.4.9" "User Commands"+.\" DO NOT MODIFY THIS FILE!  It was generated by help2man 1.49.3.+.TH RPMBUILD-ORDER "1" "February 2024" "rpmbuild-order 0.4.11" "User Commands" .SH NAME-rpmbuild-order \- manual page for rpmbuild-order 0.4.9+rpmbuild-order \- manual page for rpmbuild-order 0.4.11 .SH SYNOPSIS .B rpmbuild-order [\fI\,--version\/\fR] \fI\,COMMAND\/\fR
rpmbuild-order.cabal view
@@ -1,12 +1,12 @@ Name:             rpmbuild-order-Version:          0.4.10+Version:          0.4.11 License:          BSD3 License-File:     LICENSE Author:           Jens Petersen <petersen@redhat.com>,                   Henning Thielemann <haskell@henning-thielemann.de> Maintainer:       Jens Petersen <petersen@redhat.com> Copyright:        2010-2018  Henning Thielemann,-                  2018-2022 Jens Petersen <petersen@redhat.com>+                  2018-2024 Jens Petersen <petersen@redhat.com> Category:         Distribution Homepage:         https://github.com/juhp/rpmbuild-order Bug-reports:      https://github.com/juhp/rpmbuild-order/issues@@ -24,9 +24,9 @@   which can affect dependencies. It can also output dependency graphs.    Since version 0.4, a library API is provided.-Tested-with:       GHC == 7.10.3, GHC == 8.0.2, GHC == 8.2.2,-                   GHC == 8.4.4, GHC == 8.6.5, GHC == 8.8.4,-                   GHC == 8.10.7, GHC == 9.0.2+Tested-with:       GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.4, GHC == 8.6.5,+                   GHC == 8.8.4, GHC == 8.10.7, GHC == 9.0.2, GHC == 9.2.8,+                   GHC == 9.4.8, GHC == 9.6.4, GHC == 9.8.1 Cabal-Version:     1.18 Build-Type:        Simple Extra-doc-files:    README.md TODO ChangeLog.md@@ -35,7 +35,11 @@                     test/pkgs/B/B.spec                     test/pkgs/C/C.spec                     test/pkgs/D1.0/D1.0.spec+                    test/pkgs/cmake/A/A.spec+                    test/pkgs/cmake/B/B.spec                     test/pkgs/dynbr/A/A.spec+                    test/pkgs/pkgconf/A/A.spec+                    test/pkgs/pkgconf/C/C.spec  source-repository head   type:     git@@ -45,7 +49,6 @@   Build-Depends: base < 5,                  extra,                  fgl,-                 optparse-applicative,                  rpmbuild-order,                  simple-cmd-args   Main-Is:          src/Main.hs@@ -69,6 +72,7 @@ Library   Exposed-Modules:  Distribution.RPM.Build.Graph                     Distribution.RPM.Build.Order+                    Distribution.RPM.Build.ProvReqs   Build-Depends: base < 5,                  case-insensitive,                  directory >= 1.2.5,@@ -76,6 +80,7 @@                  filepath,                  fgl >= 5.5.4,                  graphviz,+                 regex-tdfa,                  simple-cmd >= 0.2.3   HS-Source-Dirs:      src   default-language:    Haskell2010
src/Distribution/RPM/Build/Graph.hs view
@@ -1,5 +1,4 @@ {-# LANGUAGE CPP #-}-{-# LANGUAGE OverloadedStrings #-}  {-| This module provides simple dependency graph making for rpm packages:@@ -38,7 +37,6 @@    topsortGraph,   ) where -import qualified Data.CaseInsensitive as CI import Data.Graph.Inductive.Query.DFS (components, scc, topsort', xdfsWith) import Data.Graph.Inductive.Query.SP (sp) import Data.Graph.Inductive.PatriciaTree (Gr)@@ -53,13 +51,11 @@ import Data.GraphViz import SimpleCmd import System.Directory (doesDirectoryExist, doesFileExist,-                         getCurrentDirectory, withCurrentDirectory,-                         listDirectory-                        )-import System.Exit (exitFailure)+                         withCurrentDirectory, listDirectory) import System.FilePath-import System.IO.Extra (withTempDir) +import Distribution.RPM.Build.ProvReqs (rpmspecProvidesBuildRequires)+ data SourcePackage =   SourcePackage {     packagePath :: FilePath,@@ -220,31 +216,7 @@         Just (dir,spec) -> do           when verbose $ warning spec           withCurrentDirectory dir $ do-            dynbr <- egrep_ "^\\(%generate_buildrequires\\|%gometa\\)" spec-            mprovbrs <--              if dynbr-              then do-                brs <- rpmspecDynBuildRequires spec-                provs <- do-                  dynprovs <--                    if "golang-" `isPrefixOf` takeBaseName spec-                    then do-                      macro <- grep "%global goipath" spec-                      return $-                        case macro of-                          [def] -> ["golang(" ++ last (words def) ++ ")"]-                          _ -> error' $ "failed to find %goipath in " ++ spec-                    else return []-                  prs <- rpmspecProvides spec-                  return $ dynprovs ++ prs-                return $ Just (provs,brs)-              else do-                mcontent <- rpmspecParse spec-                return $ case mcontent of-                           Nothing -> Nothing-                           Just content ->-                             let pkg = takeBaseName spec-                             in Just $ extractMetadata pkg ([],[]) $ lines content+            mprovbrs <- rpmspecProvidesBuildRequires lenient rpmopts spec             case mprovbrs of               Nothing -> return Nothing               Just (provs,brs) -> do@@ -252,10 +224,10 @@                 let provs' = filter (not . ("(x86-64)" `isSuffixOf`)) provs                 when verbose $ do                   warning $ show $ sort provs'-                  warning $ show $ mapMaybe simplifyDep $ sort brs+                  warning $ show $ sort brs                 return $ Just (path,                                nub provs',-                               nub (mapMaybe simplifyDep brs) \\ ignoredBRs)+                               nub brs \\ ignoredBRs)       where         -- (dir,specfile)         findSpec :: IO (Maybe (FilePath,FilePath))@@ -295,27 +267,6 @@                             then Nothing                             else error' $ f ++ " not found" -        extractMetadata :: FilePath -> ([String],[String]) -> [String] -> ([String],[String])-        extractMetadata _ acc [] = acc-        extractMetadata pkg acc@(provs,brs) (l:ls) =-          let ws = words l in-            if length ws < 2 then extractMetadata pkg acc ls-            else case CI.mk (head ws) of-              "BuildRequires:" ->-                let br = (head . tail) ws-                    brs' = if br `elem` ignoredBRs then brs else br:brs-                in extractMetadata pkg (provs, brs') ls-              "Name:" -> extractMetadata pkg ((head . tail) ws : provs, brs) ls-              "Provides:" -> extractMetadata pkg ((head . tail) ws : provs, brs) ls-              "%package" ->-                let subpkg =-                      let sub = last ws in-                        if length ws == 2-                        then pkg ++ '-' : sub-                        else sub-                in extractMetadata pkg (subpkg : provs, brs) ls-              _ -> extractMetadata pkg acc ls-     getBuildGraph :: [SourcePackage] -> PackageGraph     getBuildGraph srcPkgs =        let nodes = zip [0..] srcPkgs@@ -375,50 +326,6 @@        map (fromMaybe (error "node not found in graph") .             G.lab graph) -    rpmspecParse :: FilePath -> IO (Maybe String)-    rpmspecParse spec = do-      (ok, out, err) <- cmdFull "rpmspec" (["-P", "--define", "ghc_version any"] ++ rpmopts ++ [spec]) ""-      unless (null err) $ warning $ spec +-+ err-      if ok-        then return $ Just out-        else if lenient then return Nothing else exitFailure--    rpmspecProvides :: FilePath -> IO [String]-    rpmspecProvides spec = do-      (ok, out, err) <- cmdFull "rpmspec" (["--define", "ghc_version any", "-q", "--provides"] ++ rpmopts ++ [spec]) ""-      unless (null err) $ warning err-      if ok-        then return $ map (head . words) $ lines out-        else if lenient then return [] else exitFailure--    rpmspecDynBuildRequires :: FilePath -> IO [String]-    rpmspecDynBuildRequires spec = do-      cwd <- getCurrentDirectory-      withTempDir $ \tmpdir -> do-        (out,err) <- cmdStdErr "rpmbuild" ["-br", "--nodeps", "--define", "_srcrpmdir " ++ tmpdir, cwd </> spec]-        unless (null err) $ when verbose $ warning err-        -- Wrote: /current/dir/SRPMS/name-version-release.buildreqs.nosrc.rpm-        case words out of-          [] -> error' $ spec +-+ "could not generate source rpm for dynamic buildrequires"-          ws -> cmdLines "rpm" ["-qp", "--requires", last ws]--    simplifyDep br =-      case (head . words) br of-        '(':dep -> simplifyDep dep-        dep -> case splitOn "(" (dropSuffix ")" dep) of-          ("rpmlib":_) -> Nothing-          ("crate":[crate]) -> Just $ "rust-" ++ replace "/" "+" crate ++ "-devel"-          ("rubygem":[gem]) -> Just $ "rubygem-" ++ gem-          _ -> Just dep--    -- rpmspecBuildRequires :: FilePath -> IO [String]-    -- rpmspecBuildRequires spec = do-    --   (ok, out, err) <- cmdFull "rpmspec" (["--define", "ghc_version any", "-q", "--buildrequires"] ++ rpmopts ++ [spec]) ""-    --   unless (null err) $ warning err-    --   if ok-    --     then return $ lines out-    --     else if lenient then return [] else exitFailure- -- | Alias for createGraph4 -- -- @since 0.4.4 (deprecated)@@ -504,7 +411,8 @@           -> IO PackageGraph -- ^ dependency graph of the packages depsGraph rev rpmopts verbose excludedPkgs ignoredBRs lenient mdir pkgs =   listDirectory "." >>=-  depsGraphDeps rev rpmopts verbose excludedPkgs ignoredBRs lenient mdir pkgs+  depsGraphDeps rev rpmopts verbose excludedPkgs ignoredBRs lenient mdir pkgs .+  (`union` pkgs)  -- | Given a list of one or more packages and a list of potential dependencies, -- return a package dependency graph@@ -524,7 +432,7 @@   unlessM (and <$> mapM doesDirectoryExist pkgs) $     errorWithoutStackTrace "Please use package directory paths"   -- filter out dotfiles-  createGraph3 ignoredBRs rpmopts verbose lenient (not rev) mdir (filter ((/= '.') . head) (filter (`notElem` excludedPkgs) deps)) >>=+  createGraph3 ignoredBRs rpmopts verbose lenient (not rev) mdir (filter (\d -> d `notElem` excludedPkgs && head d /= '.') deps) >>=     createGraph2 rpmopts verbose lenient True mdir . dependencyNodes pkgs  -- | Used to control the output from sortGraph
src/Distribution/RPM/Build/Order.hs view
@@ -20,6 +20,7 @@    dependencySortRpmOpts,    dependencySortParallel,    dependencyLayers,+   dependencyLayersRpmOpts,    leafPackages,    independentPackages,    Components (..),@@ -58,6 +59,14 @@   graph <- createGraph pkgs   return $ packageLayers graph +-- | like dependencyLayers but with rpm options+--+-- @since 0.4.11+dependencyLayersRpmOpts :: [String] -> [FilePath] -> IO [[FilePath]]+dependencyLayersRpmOpts rpmopts pkgs = do+  graph <- createGraphRpmOpts rpmopts pkgs+  return $ packageLayers graph+ -- | returns the leaves of a set of packages leafPackages :: [FilePath] -> IO [FilePath] leafPackages pkgs = do@@ -75,9 +84,9 @@ sortGraph opt graph =   mapM_ (putStrLn . unwords) $   case opt of-    Parallel -> intersperse ["\n"]+    Parallel -> intersperse [""]     Combine -> id-    Connected -> intersperse ["\n"]+    Connected -> intersperse [""]     Separate -> id   $ topsortGraph opt graph 
+ src/Distribution/RPM/Build/ProvReqs.hs view
@@ -0,0 +1,140 @@+{-# LANGUAGE OverloadedStrings #-}++{-|+This module has commands for reading the Requires and Provides+from an RPM package spec file.+-}++module Distribution.RPM.Build.ProvReqs+  (rpmspecProvidesBuildRequires)+where++import Control.Monad (unless)+import qualified Data.CaseInsensitive as CI+import Data.List.Extra+import Data.Maybe (mapMaybe)+import SimpleCmd (cmdFull, cmdLines, cmdStdErr, egrep_, error',+                  grep, warning, (+-+))+import System.Directory (doesFileExist)+import System.Exit (exitFailure)+import System.FilePath+import System.IO.Extra (withTempDir)+import Text.Regex.TDFA ((=~))++generateBuildRequires :: FilePath -> IO Bool+generateBuildRequires =+  egrep_ "^\\(%generate_buildrequires\\|%gometa\\)"++-- | Get RPM Provides and BuildRequires based on spec file.+rpmspecProvidesBuildRequires :: Bool -- ^ lenient (allow failure)+                             -> [String] -- ^ RPM opts+                             -> FilePath -- ^ spec file+                             -> IO (Maybe ([String], -- ^ Provides+                                           [String]  -- ^ BuildRequires+                                          ))+rpmspecProvidesBuildRequires lenient rpmopts spec = do+  dynbr <- generateBuildRequires spec+  if dynbr+    then do+    brs <- rpmspecDynBuildRequires spec+    provs <- do+      dynprovs <- dynProvides+      prs <- rpmspecProvides lenient rpmopts spec+      return $ dynprovs ++ prs+    return $ Just (provs, brs)+    else do+    mcontent <- rpmspecParse+    case mcontent of+      Nothing -> return Nothing+      Just content ->+        let pkg = takeBaseName spec+        in fmap Just <$> extractMetadata pkg ([],[]) $ lines content+  where+    extractMetadata :: FilePath -> ([String],[String]) -> [String]+                    -> IO ([String],[String])+    extractMetadata _ (provs,brs) [] =+      return (provs, mapMaybe simplifyDep brs)+    extractMetadata pkg acc@(provs,brs) (l:ls) =+      case words l of+        [] -> extractMetadata pkg acc ls+        [w]+          | w =~ ("^/usr/(lib(64)?|share)/pkgconfig/.*\\.pc" :: String) ->+              let pc = metaName "pkgconfig" $ takeBaseName w+              in extractMetadata pkg (pc : provs, brs) ls+          | w =~ ("^/usr/(lib(64)?|share)/cmake/[^/]*$" :: String) ->+              let p = takeFileName w+                  cm = map (metaName "cmake") $+                       if lower p == p then [p] else [p, lower p]+              in extractMetadata pkg (provs ++ cm, brs) ls+          | otherwise -> extractMetadata pkg acc ls+        (w:w':ws) ->+            case CI.mk w of+              "BuildRequires:" ->+                -- FIXME could be more than one package: parse ws+                extractMetadata pkg (provs, w':brs) ls+              "Name:" -> extractMetadata pkg (w' : provs, brs) ls+              "Provides:" -> extractMetadata pkg (w' : provs, brs) ls+              "%package" ->+                let subpkg =+                      if null ws+                      then pkg ++ '-' : w'+                      else last ws+                in extractMetadata pkg (subpkg : provs, brs) ls+              _ -> extractMetadata pkg acc ls++    rpmspecParse :: IO (Maybe String)+    rpmspecParse = do+      (ok, out, err) <- cmdFull "rpmspec" (["-P"] ++ rpmopts ++ [spec]) ""+      unless (null err) $ warning $ spec +-+ err+      if ok+        then return $ Just out+        else if lenient then return Nothing else exitFailure++    dynProvides :: IO [String]+    dynProvides =+      if "golang-" `isPrefixOf` takeBaseName spec+      then do+        macro <- grep "%global goipath" spec+        return $+          case macro of+            [def] -> ["golang(" ++ last (words def) ++ ")"]+            _ -> error' $ "failed to find %goipath in" +-+ spec+      else return []++    simplifyDep br =+      case (head . words) br of+        '(':dep -> simplifyDep dep+        dep -> case splitOn "(" (dropSuffix ")" dep) of+          ("rpmlib":_) -> Nothing+          ("crate":[crate]) -> Just $ "rust-" ++ replace "/" "+" crate ++ "-devel"+          ("rubygem":[gem]) -> Just $ "rubygem-" ++ gem+          _ -> Just dep++rpmspecDynBuildRequires :: FilePath -> IO [String]+rpmspecDynBuildRequires spec = do+  withTempDir $ \tmpdir -> do+    (out,err) <- cmdStdErr "rpmbuild" ["-br", "--nodeps", "--define", "_srcrpmdir" +-+ tmpdir, spec]+    -- Wrote: /current/dir/SRPMS/name-version-release.buildreqs.nosrc.rpm+    let errmsg =+          "failed to generate srpm for dynamic buildrequires for" +-+ spec +++          "\n\n" ++ err+    case words out of+      [] -> error' errmsg+      ws -> do+        let srpm = last ws+        exists <- doesFileExist srpm+        if exists+          then cmdLines "rpm" ["-qp", "--requires", last ws]+          else error' errmsg++rpmspecProvides :: Bool -> [String] -> FilePath -> IO [String]+rpmspecProvides lenient rpmopts spec = do+  (ok, out, err) <- cmdFull "rpmspec" (["-q", "--provides"] ++ rpmopts ++ [spec]) ""+  unless (null err) $ warning err+  if ok+    then return $ map (head . words) $ lines out+    else if lenient then return [] else exitFailure++metaName :: String -> String -> String+metaName meta name =+  meta ++ '(' : name ++ ")"
src/Main.hs view
@@ -59,11 +59,13 @@     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 :: [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 -layerPackages :: [String] -> Bool -> Bool -> Bool -> Maybe FilePath -> [FilePath] -> IO ()+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@@ -72,7 +74,8 @@   where     printLayers =  putStrLn . unlines . map unwords . packageLayers -chainPackages :: [String] -> Bool -> Bool -> Bool -> Maybe FilePath -> [FilePath] -> IO ()+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@@ -82,12 +85,14 @@       let chain = intercalate [":"] $ packageLayers graph       in putStrLn $ unwords chain -leavesPackages :: [String] -> Bool -> Bool -> Maybe FilePath -> [FilePath] -> IO ()+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 :: [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
test/Spec.hs view
@@ -17,53 +17,70 @@ pkg :: FilePath -> FilePath pkg = ("test/pkgs/" ++) +-- FIXME refactor to test both subdir and withCurrentDirectory spec :: Version -> Spec spec rpmver = do   describe "sort" $ do     it "A B" $-      dependencySort [pkg "A", pkg "B"] >>=+      both dependencySort [pkg "A", pkg "B"] >>=       (`shouldBe` [pkg "B", pkg "A"])      it "1 2" $-      dependencySort [pkg "1", pkg "2"] >>=+      both dependencySort [pkg "1", pkg "2"] >>=       (`shouldBe` [pkg "2", pkg "1"])      it "A/ B/" $-      dependencySort [pkg "A/", pkg "B/"] >>=+      both dependencySort [pkg "A/", pkg "B/"] >>=       (`shouldBe` [pkg "B/", pkg "A/"])      it "1/ 2/" $-      dependencySort [pkg "1/", pkg "2/"] >>=+      both dependencySort [pkg "1/", pkg "2/"] >>=       (`shouldBe` [pkg "2/", pkg "1/"])      it "A.spec B.spec" $-      dependencySort [pkg "A/A.spec", pkg "B/B.spec"] >>=+      both dependencySort [pkg "A/A.spec", pkg "B/B.spec"] >>=       (`shouldBe` [pkg "B/B.spec", pkg "A/A.spec"])      it "1.spec 2.spec" $-      dependencySort [pkg "1/A.spec", pkg "2/B.spec"] >>=+      both dependencySort [pkg "1/A.spec", pkg "2/B.spec"] >>=       (`shouldBe` [pkg "2/B.spec", pkg "1/A.spec"])      it "A/ B.spec" $-      dependencySort [pkg "A/", pkg "B/B.spec"] >>=+      both dependencySort [pkg "A/", pkg "B/B.spec"] >>=       (`shouldBe` [pkg "B/B.spec", pkg "A/"])      it "circular A B C" $-      dependencySort [pkg "A", pkg "B", pkg "C"]+      both dependencySort [pkg "A", pkg "B", pkg "C"]       `shouldThrow` anyException      it "A B D1.0" $       dependencySort [pkg "A", pkg "B/", pkg "D1.0"] >>=       (`shouldBe` [pkg "B/", pkg "D1.0", pkg "A"]) +    it "A D" $+      indy dependencySort [pkg "A", pkg "D1.0"] >>=+      (`shouldBe` [pkg "A", pkg "D1.0"])++    it "C D" $+      indy dependencySort [pkg "C", pkg "D1.0"] >>=+      (`shouldBe` [pkg "C", pkg "D1.0"])++    it "pkgconf" $+      both dependencySort [pkg "pkgconf/C", pkg "pkgconf/A"] >>=+      (`shouldBe` [pkg "pkgconf/C", pkg "pkgconf/A"])++    it "cmake" $+      both dependencySort [pkg "cmake/A", pkg "cmake/B"] >>=+      (`shouldBe` [pkg "cmake/B", pkg "cmake/A"])+     when (rpmver > makeVersion [4,15,0]) $       it "dynbr A B" $-      dependencySort [pkg "dynbr/A", pkg "B"] >>=+      both dependencySort [pkg "dynbr/A", pkg "B"] >>=       (`shouldBe` [pkg "B", pkg "dynbr/A"])      when (rpmver > makeVersion [4,15,1]) $       it "circular A B C boot" $-      dependencySortRpmOpts ["--with=boot"] [pkg "A", pkg "B", pkg "C"] >>=+      both (dependencySortRpmOpts ["--with=boot"]) [pkg "A", pkg "B", pkg "C"] >>=       (`shouldBe` [pkg "C", pkg "B", pkg "A"])    describe "layers" $@@ -76,17 +93,44 @@       leafPackages [pkg "A", pkg "B", pkg "D1.0"] >>=       (`shouldBe` [pkg "A", pkg "D1.0"]) +  let rpmbuildOrder f c = fmap f . cmd "rpmbuild-order" . (c:)+   describe "rpmbuild-order" $ do     it "sort A B" $-      cmd "rpmbuild-order" ["sort", pkg "A", pkg "B"] >>=-      (`shouldBe` unwords [pkg "B", pkg "A"])+      both (rpmbuildOrder words "sort") [pkg "A", pkg "B"] >>=+      (`shouldBe` [pkg "B", pkg "A"]) +    it "sort A D" $+      indy (rpmbuildOrder lines "sort") [pkg "A", pkg "D1.0"] >>=+      (`shouldBe` [pkg "D1.0", "", pkg "A"])++    it "sort pkgconf" $+      both (rpmbuildOrder words "sort") [pkg "pkgconf/A", pkg "pkgconf/C"] >>=+      (`shouldBe` [pkg "pkgconf/C", pkg "pkgconf/A"])++    it "sort cmake" $+      both (rpmbuildOrder words "sort") [pkg "cmake/A", pkg "cmake/B"] >>=+      (`shouldBe` [pkg "cmake/B", pkg "cmake/A"])+     it "deps A" $       withCurrentDirectory "test/pkgs" $       cmd "rpmbuild-order"-      ["deps", "-x", "dynbr", "-x", "1", "-x", "2", "-x", "C", "A"] >>=+      ["deps", "-x", "dynbr", "-x", "pkgconf", "-x", "1", "-x", "2", "-x", "C","-x", "cmake", "A"] >>=       (`shouldBe` unwords ["B", "A"])+  where+    both :: ([FilePath] -> IO [FilePath]) -> [FilePath] -> IO [FilePath]+    both io fs = do+      r <- io fs+      r' <- io $ reverse fs+      r `shouldBe` r'+      return r +    indy :: ([FilePath] -> IO [FilePath]) -> [FilePath] -> IO [FilePath]+    indy io fs = do+      r <- io fs+      r' <- io $ reverse fs+      r `shouldBe` reverse r'+      return r'  setupSymlinks :: IO () setupSymlinks =
+ test/pkgs/cmake/A/A.spec view
@@ -0,0 +1,13 @@+Name:           A+Version:        1+Release:        1%{?dist}+Summary:        package A+License:        GPL+BuildRequires:  cmake(libB)++%description+Test package A pkgconfig++%changelog+* Wed Jul 29 13:38:27 +08 2020 Jens Petersen <petersen@redhat.com>+-
+ test/pkgs/cmake/B/B.spec view
@@ -0,0 +1,22 @@+Name:           B+Version:        1+Release:        1%{?dist}+Summary:        package B+License:        GPL++%description+Test package B++%prep++%build++%install++%files+%{_libdir}/cmake/libB+%{_libdir}/cmake/libB/B.cmake++%changelog+* Wed Jul 29 13:38:27 +08 2020 Jens Petersen <petersen@redhat.com>+-
+ test/pkgs/pkgconf/A/A.spec view
@@ -0,0 +1,13 @@+Name:           A+Version:        1+Release:        1%{?dist}+Summary:        package A+License:        GPL+BuildRequires:  pkgconfig(libC)++%description+Test package A pkgconfig++%changelog+* Wed Jul 29 13:38:27 +08 2020 Jens Petersen <petersen@redhat.com>+-
+ test/pkgs/pkgconf/C/C.spec view
@@ -0,0 +1,14 @@+Name:           C+Version:        1+Release:        1%{?dist}+Summary:        package C+License:        GPL++%description++%files+%{_libdir}/pkgconfig/libC.pc++%changelog+* Wed Jul 29 13:38:27 +08 2020 Jens Petersen <petersen@redhat.com>+-