diff --git a/ChangeLog b/ChangeLog
new file mode 100644
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,3 @@
+* 0.1
+- initial release with sort, deps and rdeps (reverse dependencies) commands
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,31 @@
+Copyright (c) 2010, Henning Thielemann
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * The names of contributors may not be used to endorse or promote
+      products derived from this software without specific prior
+      written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,32 @@
+[![Build Status](https://travis-ci.org/juhp/rpmbuild-order.png)](https://travis-ci.org/juhp/rpmbuild-order)
+
+# rpmbuild-order
+
+This package based on code from [cabal-sort](http://hackage.haskell.org/package/cabal-sort), sorts rpm package spec files by build order.
+
+    $ rpmbuild-order --help
+    $ rpmbuild-order sort mycore mylib myapp
+    mylib
+    mycore
+    myapp
+
+The arguments passed can either be directories named after the package, or spec files.
+
+By default it outputs the package names, but it can also output
+the spec filenames or directory paths for easier scripting.
+
+Using the rpmbuild-order `deps` and `rdeps` commands the ordered
+dependencies and reverse dependencies of a package can be obtained
+from the current set of checked out package sources.
+
+## Known problems
+Given packages A, B, C, where C depends on B, and B depends on A,
+and you call
+
+    rpmbuild-order C.spec A.spec
+
+then the output may be wrong if C does not have a direct dependency on A.
+Even if the order is correct, B is missing in the output
+and thus in this case the list of packages cannot be reliably used
+for a sequence of builds.
+
diff --git a/RpmbuildOrder.hs b/RpmbuildOrder.hs
new file mode 100644
--- /dev/null
+++ b/RpmbuildOrder.hs
@@ -0,0 +1,318 @@
+{-# LANGUAGE LambdaCase #-}
+
+module Main where
+
+import qualified Distribution.Verbosity as Verbosity
+import qualified Distribution.ReadE as ReadE
+
+import System.Console.GetOpt
+          (getOpt, ArgOrder(..), OptDescr(..), ArgDescr(..), usageInfo)
+import System.Exit (exitSuccess, exitFailure)
+import qualified System.Environment as Env
+import System.FilePath
+
+import System.Directory (doesDirectoryExist, doesFileExist,
+#if (defined(MIN_VERSION_directory) && MIN_VERSION_directory(1,2,5))
+                         listDirectory
+#else
+                         getDirectoryContents
+#endif
+  )
+import System.IO (hPutStrLn, stderr)
+import System.Process (readProcess)
+
+import Data.Graph.Inductive.Query.DFS (xdfsWith, topsort', scc, components)
+import Data.Graph.Inductive.Tree (Gr)
+import qualified Data.Graph.Inductive.Graph as Graph
+
+import qualified Control.Monad.Exception.Synchronous as E
+import qualified Control.Monad.Trans.Class as T
+
+import qualified Data.Set as Set
+import Control.Monad (guard, when, unless)
+import Data.Maybe (catMaybes, fromMaybe, mapMaybe)
+import Data.List (delete, intersperse, stripPrefix)
+
+#if (defined(MIN_VERSION_base) && MIN_VERSION_base(4,8,2))
+#else
+import Control.Applicative ((<$>))
+#endif
+
+#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
+
+main :: IO ()
+main =
+  E.resolveT handleException $ do
+  argv <- T.lift Env.getArgs
+  let (opts, args, errors) = getOpt Permute options argv
+  if length args < 2
+    then T.lift $ help >> exitFailure
+    else do
+      let (com:pkgs) = args
+      unless (null errors) $ E.throwT $ concat errors
+      unless (com `elem` ["sort", "deps", "rdeps"]) $
+        E.throwT $ "Unknown command " ++ com
+      flags <-
+         E.ExceptionalT $ return $
+            foldr (=<<)
+               (return
+                Flags {optHelp = False,
+                       optVerbosity = Verbosity.silent,
+                       optFormat = package,
+                       optParallel = Nothing,
+                       optBranch = Nothing})
+               opts
+      if optHelp flags
+        then T.lift $ help >> exitSuccess
+        else runCommand flags com $ map (removeSuffix "/") pkgs
+  where
+    help =
+      Env.getProgName >>= \programName ->
+        putStrLn
+             (usageInfo ("Usage: " ++ programName ++
+                         " [OPTIONS] [sort|deps|rdeps] PKG-SPEC-OR-DIR ...") options)
+
+handleException :: String -> IO ()
+handleException msg = do
+   putStrLn $ "Aborted: " ++ msg
+   exitFailure
+
+findSpec :: Maybe FilePath -> FilePath -> IO (Maybe FilePath)
+findSpec mdir file =
+  if takeExtension file == ".spec"
+    then checkFile file
+    else do
+    dirp <- doesDirectoryExist file
+    if dirp
+      then
+      let dir = maybe file (file </>) mdir
+          pkg = takeBaseName file in
+        checkFile $ dir </> pkg ++ ".spec"
+      else return Nothing
+  where
+    checkFile :: FilePath -> IO (Maybe FilePath)
+    checkFile f = do
+      e <- doesFileExist f
+      if e
+        then return $ Just f
+        else return Nothing
+
+data Flags =
+   Flags {
+      optHelp :: Bool,
+      optVerbosity :: Verbosity.Verbosity,
+      optFormat :: SourcePackage -> String,
+      optParallel :: Maybe String,
+      optBranch :: Maybe FilePath
+   }
+
+options :: [OptDescr (Flags -> E.Exceptional String Flags)]
+options =
+  [
+    Option ['h'] ["help"]
+      (NoArg (\flags -> return $ flags{optHelp = True}))
+      "Show options"
+  , Option ['p'] ["parallel"]
+      (OptArg
+        (\mstr flags ->
+            fmap (\cs -> flags{optParallel = Just cs})
+            (E.Success (fromMaybe "" mstr)))
+         "SEPARATOR")
+      "Display independently buildable groups of packages, optionally with separator"
+  , Option ['b'] ["branch"]
+      (ReqArg
+         (\str flags ->
+            fmap (\mb -> flags{optBranch = mb})
+            (E.Success (Just str)))
+         "BRANCHDIR")
+    "branch directory"
+  , Option ['f'] ["format"]
+      (ReqArg
+         (\str flags ->
+            fmap (\select -> flags{optFormat = select}) $
+            case str of
+               "package" -> E.Success package
+               "spec" -> E.Success location
+               "dir"  -> E.Success (takeDirectory . location)
+               _ ->
+                  E.Exception $
+                  "unknown info type " ++ str)
+         "KIND")
+      "output format: 'package' (default), 'spec', or 'dir'"
+  , Option ['v'] ["verbose"]
+      (ReqArg
+         (\str flags ->
+            fmap (\n -> flags{optVerbosity = n}) $
+            E.fromEither $
+            ReadE.runReadE Verbosity.flagToVerbosity str)
+         "N")
+      "verbosity level: 0..3"
+  ]
+
+type Package = String
+
+data SourcePackage =
+   SourcePackage {
+      location :: FilePath,
+      package :: Package,
+      dependencies :: [Package]
+   }
+   deriving (Show, Eq)
+
+type Command = String
+
+runCommand :: Flags -> Command -> [Package] -> E.ExceptionalT String IO ()
+runCommand flags "sort" pkgs = sortSpecFiles flags pkgs
+runCommand flags "deps" pkgs = depsSpecFiles False flags pkgs
+runCommand flags "rdeps" pkgs = depsSpecFiles True flags pkgs
+runCommand _ _ _ = E.throwT "impossible happened"
+
+createGraphNodes :: Flags -> [Package] -> [Package] ->
+               E.ExceptionalT String IO (Gr SourcePackage (), [Graph.Node])
+createGraphNodes flags pkgs subset = do
+  unless (all (`elem` pkgs) subset) $
+    E.throwT "Packages must be in the current directory"
+  specPaths <- T.lift $ catMaybes <$> mapM (findSpec (optBranch flags)) (filter (/= fromMaybe "" (optParallel flags)) pkgs)
+  let names = map takeBaseName specPaths
+  provs <-
+     T.lift $
+     mapM (readProvides (optVerbosity flags)) specPaths
+  let resolves = zip names provs
+  deps <-
+     T.lift $
+     mapM (getDepsSrcResolved (optVerbosity flags) resolves) specPaths
+  let spkgs = zipWith3 SourcePackage specPaths names deps
+      graph = getBuildGraph spkgs
+  checkForCycles graph
+  let nodes = Graph.labNodes graph
+      subnodes = mapMaybe (pkgNode nodes) subset
+  return (graph, subnodes)
+  where
+    pkgNode [] _ = Nothing
+    pkgNode ((i,l):ns) p = if p == package l then Just i else pkgNode ns p
+
+sortSpecFiles :: Flags -> [Package] -> E.ExceptionalT String IO ()
+sortSpecFiles flags pkgs = do
+      (graph, _) <- createGraphNodes flags pkgs []
+      T.lift $
+         case optParallel flags of
+           Just s ->
+             mapM_ ((putStrLn . unwords . (if null s then id else intersperse s) . map (optFormat flags)) . topsort' . subgraph graph)
+                 (components graph)
+           Nothing ->
+             mapM_ (putStrLn . optFormat flags) $ topsort' graph
+ 
+depsSpecFiles :: Bool -> Flags -> [Package] -> E.ExceptionalT String IO ()
+depsSpecFiles rev flags pkgs = do
+  allpkgs <- T.lift $ listDirectory "."
+  (graph, nodes) <- createGraphNodes flags allpkgs pkgs
+  let dir = if rev then Graph.suc' else Graph.pre'
+  sortSpecFiles flags $ map package $ xdfsWith dir third nodes graph
+  where
+    third (_, _, c, _) = c
+
+readProvides :: Verbosity.Verbosity -> FilePath -> IO [String]
+readProvides verbose file = do
+  when (verbose >= Verbosity.verbose) $ hPutStrLn stderr file
+  pkgs <- lines <$>
+    rpmspec ["--rpms", "--qf=%{name}\n", "--define", "ghc_version any"] Nothing file
+  let pkg = takeBaseName file
+  return $ delete pkg pkgs
+
+getDepsSrcResolved :: Verbosity.Verbosity -> [(String,[String])] -> FilePath -> IO [String]
+getDepsSrcResolved verbose provides file =
+  map (resolveBase provides) <$> do
+      when (verbose >= Verbosity.verbose) $ hPutStrLn stderr file
+      -- ignore version bounds
+      map (head . words) . lines <$>
+        rpmspec ["--buildrequires", "--define", "ghc_version any"] Nothing file
+  where
+    resolveBase :: [(String,[String])] -> String -> String
+    resolveBase provs br =
+      case mapMaybe (\ (pkg,subs) -> if br `elem` subs then Just pkg else Nothing) provs of
+        [] -> br
+        [p] -> p
+        ps -> error $ br ++ "is provided by: " ++ unwords ps
+
+removeSuffix :: String -> String -> String
+removeSuffix suffix orig =
+  fromMaybe orig $ stripSuffix suffix orig
+  where
+    stripSuffix sf str = reverse <$> stripPrefix (reverse sf) (reverse str)
+
+cmdStdIn :: String -> [String] -> String -> IO String
+cmdStdIn c as inp = removeTrailingNewline <$> readProcess c as inp
+  where
+    removeTrailingNewline :: String -> String
+    removeTrailingNewline "" = ""
+    removeTrailingNewline str =
+      if last str == '\n'
+      then init str
+      else str
+
+cmd :: String -> [String] -> IO String
+cmd c as = cmdStdIn c as ""
+
+rpmspec :: [String] -> Maybe String -> FilePath -> IO String
+rpmspec args mqf spec = do
+  let qf = maybe [] (\ q -> ["--queryformat", q]) mqf
+  cmd "rpmspec" (["-q"] ++ args ++ qf ++ [spec])
+
+getDeps :: Gr SourcePackage () -> [(SourcePackage, [SourcePackage])]
+getDeps gr =
+    let c2dep :: Graph.Context SourcePackage () -> (SourcePackage, [SourcePackage])
+        c2dep ctx =
+           (Graph.lab' ctx,
+            map (Graph.lab' . Graph.context gr) (Graph.pre gr . Graph.node' $ ctx))
+    in  Graph.ufold (\ctx ds -> c2dep ctx : ds) [] gr
+
+getBuildGraph :: [SourcePackage] -> Gr SourcePackage ()
+getBuildGraph srcPkgs =
+   let nodes = zip [0..] srcPkgs
+       nodeDict = zip (map package srcPkgs) [0..]
+       edges = do
+          (srcNode,srcPkg) <- nodes
+          dstNode <- mapMaybe (`lookup` nodeDict) (dependencies srcPkg)
+          guard (dstNode /= srcNode)
+          return (dstNode, srcNode, ())
+   in Graph.mkGraph nodes edges
+
+checkForCycles ::
+   Monad m =>
+   Gr SourcePackage () ->
+   E.ExceptionalT String m ()
+checkForCycles graph =
+   case getCycles graph of
+      [] -> return ()
+      cycles ->
+         E.throwT $ unlines $
+         "Cycles in dependencies:" :
+         map (unwords . map location . nodeLabels graph) cycles
+
+nodeLabels :: Gr a b -> [Graph.Node] -> [a]
+nodeLabels graph =
+   map (fromMaybe (error "node not found in graph") .
+        Graph.lab graph)
+
+subgraph :: Gr a b -> [Graph.Node] -> Gr a b
+subgraph graph nodes =
+   let nodeSet = Set.fromList nodes
+       edges = do
+           from <- nodes
+           (to, lab) <- Graph.lsuc graph from
+           guard $ Set.member from nodeSet && Set.member to nodeSet
+           return (from,to,lab)
+   in  Graph.mkGraph (zip nodes $ nodeLabels graph nodes) edges
+
+getCycles :: Gr a b -> [[Graph.Node]]
+getCycles =
+   filter (\case
+              _:_:_ -> True
+              _ -> False)
+   . scc
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/TODO b/TODO
new file mode 100644
--- /dev/null
+++ b/TODO
@@ -0,0 +1,1 @@
+add leaf command
diff --git a/rpmbuild-order.cabal b/rpmbuild-order.cabal
new file mode 100644
--- /dev/null
+++ b/rpmbuild-order.cabal
@@ -0,0 +1,47 @@
+Name:             rpmbuild-order
+Version:          0.1
+License:          BSD3
+License-File:     LICENSE
+Author:           Henning Thielemann <haskell@henning-thielemann.de>
+Maintainer:       Jens Petersen <petersen@redhat.com>
+Copyright:        2010-2018  Henning Thielemann,
+                  2018 Jens Petersen <petersen@redhat.com>
+Category:         Distribution
+Homepage:         https://github.com/juhp/rpmbuild-order
+Bug-reports:      https://github.com/juhp/rpmbuild-order/issues
+Synopsis:         Order RPM packages by dependencies
+Description:
+  The rpmbuild-order tool orders RPM packages by dependencies, so that they can
+  be built in the correct order. It does this by reading RPM package spec files
+  and then topologically sorts them according to their dependencies.
+  The code originates from cabal-sort by Henning Thielemann.
+  It can also output the ordered dependencies or reverse depends
+  for one or more packages, provided all the packages are checked out
+  in neighbouring directories. This is also useful to see what packages
+  are affected when a low-level package changes.
+Tested-with:       GHC == 7.4.2, GHC == 7.6.3, GHC == 7.8.4,
+                   GHC == 7.10.3, GHC == 8.0.2, GHC == 8.2.2,
+                   GHC == 8.4.3
+Cabal-Version:     >=1.6
+Build-Type:        Simple
+Extra-source-files: README.md TODO ChangeLog
+
+source-repository head
+  type:     git
+  location: https://github.com/juhp/rpmbuild-order
+
+Executable rpmbuild-order
+  Build-Depends:
+    base < 5,
+    Cabal,
+    containers,
+    directory,
+    explicit-exception,
+    filepath,
+    fgl, 
+    process,
+    transformers
+
+  GHC-Options:      -Wall
+  Main-Is:          RpmbuildOrder.hs
+  Extensions:       CPP
