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/Setup.lhs b/Setup.lhs
new file mode 100644
--- /dev/null
+++ b/Setup.lhs
@@ -0,0 +1,3 @@
+#! /usr/bin/env runhaskell
+> import Distribution.Simple
+> main = defaultMain
diff --git a/cabal-sort.cabal b/cabal-sort.cabal
new file mode 100644
--- /dev/null
+++ b/cabal-sort.cabal
@@ -0,0 +1,67 @@
+Name:             cabal-sort
+Version:          0.0.1
+License:          BSD3
+License-File:     LICENSE
+Author:           Henning Thielemann <haskell@henning-thielemann.de>
+Maintainer:       Henning Thielemann <haskell@henning-thielemann.de>
+-- Homepage:         http://www.haskell.org/haskellwiki/Cabal
+Category:         Distribution
+Synopsis:         Topologically sort cabal packages
+Description:
+  If you have a bunch of packages you may want to compile or recompile,
+  then you need an order of compilation that meets the dependencies.
+  Given a number of cabal package files,
+  this program reads all those files
+  and emits them topologically sorted according to their dependencies.
+  This way you can compile many packages at once,
+  say if a very low-level package has changed.
+  .
+  For compiling a couple of packages from their local darcs repositories
+  in the right order, you may run something like
+  .
+  > for dir in `find . -name "*.cabal" | fgrep -v _darcs | xargs cabal-sort --info=dir`; do (cd $dir && cabal install); done
+  .
+  For uploading a set of packages to Hackage in the right order
+  you may run
+  .
+  > for dir in `find . -name "*.cabal" | fgrep -v _darcs | xargs cabal-sort --info=dir`; do (cd $dir && rm dist/*.tar.gz && cabal sdist && cabal upload dist/*.tar.gz); done
+  .
+  Problem: Given packages A, B, C,
+  where C depends on B and B depends on A,
+  and you call
+  .
+  > cabal-sort C.cabal A.cabal
+  .
+  then the emitted order of packages may be wrong,
+  because cabal-sort does not get to know the dependency of C on B.
+  Even if the order is correct,
+  B.cabal is missing in the output
+  and thus the list of cabal files cannot immediately be used
+  for a sequence of cabal-install runs.
+Tested-With:       GHC==6.10.4
+Cabal-Version:     >=1.6
+Build-Type:        Simple
+Source-Repository head
+  type:     darcs
+  location: http://code.haskell.org/~thielema/cabal-sort/
+
+Source-Repository this
+  type:     darcs
+  location: http://code.haskell.org/~thielema/cabal-sort/
+  tag:      0.0.1
+
+
+Executable cabal-sort
+  Build-Depends:
+    Cabal >=1.6 && <1.10,
+    fgl >=5.4.2 && <5.5,
+    directory >=1 && <1.1,
+    filepath >=1.1 && <1.2,
+    containers >=0.2 && <0.4,
+    explicit-exception >=0.1.4 && <0.2,
+    transformers >=0.2 && <0.3,
+    base >=2 && <5
+
+  GHC-Options:      -Wall
+  Hs-source-dirs:   src
+  Main-Is:          Main.hs
diff --git a/src/Main.hs b/src/Main.hs
new file mode 100644
--- /dev/null
+++ b/src/Main.hs
@@ -0,0 +1,169 @@
+module Main where
+
+import qualified Distribution.PackageDescription as P
+import Distribution.PackageDescription
+         (GenericPackageDescription, PackageDescription,
+          package, packageDescription, buildDepends, )
+import Distribution.PackageDescription.Parse (readPackageDescription, )
+import Distribution.Package
+         (Dependency(Dependency), PackageName(PackageName), pkgName, )
+
+import qualified Distribution.Verbosity as Verbosity
+import qualified Distribution.ReadE as ReadE
+
+import System.Console.GetOpt
+          (getOpt, ArgOrder(..), OptDescr(..), ArgDescr(..), usageInfo, )
+import System.Exit (exitWith, ExitCode(..), )
+import qualified System.Environment as Env
+import qualified System.FilePath as FilePath
+
+import Data.Graph.Inductive.Query.DFS (topsort', scc, )
+import Data.Graph.Inductive.Tree (Gr, )
+import qualified Data.Graph.Inductive.Graph as Graph
+
+import qualified Control.Monad.Exception.Synchronous as Exc
+import qualified Control.Monad.Trans.Class as Trans
+
+import qualified Data.Set as Set
+import Control.Monad (when, )
+import Data.Maybe (mapMaybe, )
+
+
+main :: IO ()
+main =
+   Exc.resolveT (\e -> putStr $ "Aborted: " ++ e ++ "\n") $ do
+      argv <- Trans.lift Env.getArgs
+      let (opts, cabalPaths, errors) =
+             getOpt RequireOrder options argv
+      when (not (null errors)) $ Exc.throwT $ concat $ errors
+      flags <-
+         Exc.ExceptionalT $ return $
+            foldr (flip (>>=))
+               (return $
+                Flags {optHelp = False,
+                       optVerbosity = Verbosity.silent,
+                       optInfo = location})
+               opts
+      when (optHelp flags)
+         (Trans.lift $
+          Env.getProgName >>= \programName ->
+          putStrLn
+             (usageInfo ("Usage: " ++ programName ++
+                         " [OPTIONS] CABAL-FILES ...") options) >>
+          exitWith ExitSuccess)
+
+      Trans.lift $ sortCabalFiles flags cabalPaths
+
+
+data Flags =
+   Flags {
+      optHelp :: Bool,
+      optVerbosity :: Verbosity.Verbosity,
+      optInfo :: SourcePackage -> String
+   }
+
+options :: [OptDescr (Flags -> Exc.Exceptional String Flags)]
+options =
+   Option ['h'] ["help"]
+      (NoArg (\flags -> return $ flags{optHelp = True}))
+      "show options" :
+   Option ['v'] ["verbose"]
+      (ReqArg
+         (\str flags ->
+            fmap (\n -> flags{optVerbosity = n}) $
+            Exc.fromEither $
+            ReadE.runReadE Verbosity.flagToVerbosity str)
+         "N")
+      "verbosity level: 0..3" :
+   Option [] ["info"]
+      (ReqArg
+         (\str flags ->
+            fmap (\select -> flags{optInfo = select}) $
+            case str of
+               "name" -> Exc.Success
+                            (getPkgName . pkgName . package .
+                             packageDescription . description)
+               "path" -> Exc.Success location
+               "dir"  -> Exc.Success (FilePath.takeDirectory . location)
+               _ ->
+                  Exc.Exception $
+                  "unknown info type " ++ str)
+         "KIND")
+      "kind of output: name, path, dir" :
+   []
+
+
+
+data SourcePackage =
+   SourcePackage {
+      location :: FilePath,
+      description :: GenericPackageDescription
+   }
+
+sortCabalFiles :: Flags -> [FilePath] -> IO ()
+sortCabalFiles flags cabalPaths =
+   do pkgDescs <-
+         mapM (readPackageDescription (optVerbosity flags)) cabalPaths
+      when (optVerbosity flags >= Verbosity.verbose) $
+         flip mapM_ pkgDescs $ \pkgDesc -> do
+            putStrLn
+               ((getPkgName . pkgName . package . packageDescription $ pkgDesc) ++ ":")
+            let deps =
+                   Set.toAscList $ Set.fromList $
+                   map (getPkgName . depName) $
+                   allDependencies pkgDesc
+            flip mapM_ deps $ \dep ->
+               putStrLn $ "  " ++ dep
+      mapM_ (putStrLn . optInfo flags) $
+         getBuildOrder $
+         zipWith SourcePackage cabalPaths pkgDescs
+
+
+getBuildOrder ::
+   [SourcePackage] ->
+   [SourcePackage]
+getBuildOrder srcPkgs =
+   let nodes = zip [0..] srcPkgs
+       nodeDict =
+          zip
+             (map (pkgName . package . packageDescription . description)
+                  srcPkgs)
+             [0..]
+       edges = do
+          (srcNode,desc) <- nodes
+          dstNode <-
+             mapMaybe
+                (flip lookup nodeDict . depName)
+                (allDependencies $ description desc)
+          return (dstNode, srcNode, ())
+       graph :: Gr SourcePackage ()
+       graph = Graph.mkGraph nodes edges
+
+   in  if hasCycle graph
+         then error "cycle in dependencies"
+         else topsort' graph
+
+allDependencies :: GenericPackageDescription -> [Dependency]
+allDependencies pkg =
+   P.buildDepends (packageDescription pkg) ++
+   maybe [] (concatMap snd . flattenCondTree) (P.condLibrary pkg) ++
+   concatMap (concatMap snd . flattenCondTree . snd) (P.condExecutables pkg)
+
+flattenCondTree :: P.CondTree v c a -> [(a,c)]
+flattenCondTree tree =
+   (P.condTreeData tree, P.condTreeConstraints tree) :
+   concatMap
+      (\(_, thenBranch, elseBranch) ->
+         flattenCondTree thenBranch ++
+         maybe [] flattenCondTree elseBranch)
+      (P.condTreeComponents tree)
+
+depName :: Dependency -> PackageName
+depName (Dependency name _) = name
+
+getPkgName :: PackageName -> String
+getPkgName (PackageName name) = name
+
+hasCycle :: Gr a b -> Bool
+hasCycle graph =
+   length (scc graph) < length (Graph.nodes graph)
