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) (sortOn (length . snd) deps')
 | |
| 
 |