61 lines
		
	
	
		
			1.9 KiB
		
	
	
	
		
			Haskell
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			61 lines
		
	
	
		
			1.9 KiB
		
	
	
	
		
			Haskell
		
	
	
		
			Executable File
		
	
	
	
	
#!/usr/bin/env runhaskell
 | 
						|
 | 
						|
import System
 | 
						|
import System.Directory
 | 
						|
import System.IO
 | 
						|
import Data.List
 | 
						|
import Data.Char
 | 
						|
import Data.Maybe
 | 
						|
import Data.Ord
 | 
						|
 | 
						|
{-
 | 
						|
    Read a Haskell *.hs file and get a list of all the modules
 | 
						|
    that it imports.
 | 
						|
-}
 | 
						|
findDeps base pkg = do
 | 
						|
        let hi = base ++ map dotToSlash pkg ++ ".hs"
 | 
						|
        ex <- doesFileExist hi
 | 
						|
        if not ex then return [] else do
 | 
						|
            src <- readFile hi
 | 
						|
            let imps = filter isImport (lines src)
 | 
						|
            return $ mapMaybe getTargetModule imps
 | 
						|
 | 
						|
    where dotToSlash '.' = '/'
 | 
						|
          dotToSlash x   = x
 | 
						|
 | 
						|
          isImport (' ':t)  = isImport t
 | 
						|
          isImport ('\t':t) = isImport t
 | 
						|
          isImport t        = "import" `isPrefixOf` t
 | 
						|
 | 
						|
          getTargetModule s = let pre = takeWhile (/= '(') s in
 | 
						|
                              find (isUpper . head) (words pre)
 | 
						|
 | 
						|
{-
 | 
						|
    Find the transitive, reflexive closure of the relation defined
 | 
						|
    by the findDeps function.  This returns a list of ALL modules
 | 
						|
    that this one uses or depends on, directly or indirectly.
 | 
						|
-}
 | 
						|
allDeps base mod = allDeps' [mod] [mod] where
 | 
						|
    allDeps' (m:ms) full = do d <- findDeps base m
 | 
						|
                              let d' = filter (not . flip elem full) d
 | 
						|
                              t <- allDeps' (d' ++ ms) (d' ++ full)
 | 
						|
                              return (m : t)
 | 
						|
    allDeps' [] _        = return []
 | 
						|
 | 
						|
{-
 | 
						|
    Usage: OrderByComplexity
 | 
						|
 | 
						|
        = directory where source code is found.  This MUST
 | 
						|
                end in '/'
 | 
						|
     = file that lists the modules you're interested in,
 | 
						|
                one per line.  This is often taken from a .cabal
 | 
						|
-}
 | 
						|
main = do [ base, pkgFile ] <- getArgs
 | 
						|
          pkgStr            <- readFile pkgFile
 | 
						|
          let pkgs           = lines pkgStr
 | 
						|
          mods              <- mapM (allDeps base) pkgs
 | 
						|
          let deps           = zip pkgs mods
 | 
						|
          let deps'          = sortBy (comparing fst) deps
 | 
						|
          mapM_ (print . fst) (sortBy (comparing $ length . snd) deps')
 | 
						|
 |