cabal-sort (empty) → 0.0.1
raw patch · 4 files changed
+270/−0 lines, 4 filesdep +Cabaldep +basedep +containerssetup-changed
Dependencies added: Cabal, base, containers, directory, explicit-exception, fgl, filepath, transformers
Files
- LICENSE +31/−0
- Setup.lhs +3/−0
- cabal-sort.cabal +67/−0
- src/Main.hs +169/−0
+ LICENSE view
@@ -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.
+ Setup.lhs view
@@ -0,0 +1,3 @@+#! /usr/bin/env runhaskell+> import Distribution.Simple+> main = defaultMain
+ cabal-sort.cabal view
@@ -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
+ src/Main.hs view
@@ -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)