package-o-tron-0.1.0.0: exe-src/CabalLint.lhs
Cabal lint checks your cabal file
It currently reports:
missing other modules and build-deps (build-deps missing is usually
hard to miss!)
superfluous other modules and build-deps
it checks library,exe, and v10 test sections
it also runs the packdeps check to check your upper bounds on the
build-deps
TODO:
check for duplicates in other-modules and build-depends
and other-modules include root modules (already does this?)
list files in project which aren't included in .cabal
verbose mode
> import System.Environment
> import Distribution.PackDeps
> import Distribution.PackageDescription.Parse
> import Distribution.Verbosity
> import Distribution.PackageDescription
> import Distribution.Pot.Modules
> import Control.Applicative
> import Data.List
> import Distribution.ModuleName hiding (main)
> import Control.Monad
> import Distribution.Package
> import System.FilePath
> import Data.Maybe
> import System.Exit
> data SectionInformation =
> SI
> {siSrcDirs :: [FilePath]
> ,siSectionName :: String
> ,siModRoots :: [ModuleName]
> ,siOtherMods :: [ModuleName]
> ,siBuildDeps :: [Dependency]
> }
> main :: IO ()
> main = do
> [fp] <- getArgs
> di <- readPackageDescription normal fp
> --putStrLn $ groom di
> let -- get the source dirs, add "." if empty list
> -- not sure if "." should always be added?
> sd bi = map (dropFileName fp </>) $
> let x = hsSourceDirs bi
> in if null x
> then ["."]
> else x
> libSi = do
> cnd <- condLibrary di
> let ctd = condTreeData cnd
> srcDirs = sd $ libBuildInfo ctd
> return $ SI srcDirs "library" (exposedModules ctd)
> (otherModules $ libBuildInfo ctd)
> (condTreeConstraints cnd)
> exeSis = flip map (condExecutables di) $ \(n,cnd) ->
> let ctd = condTreeData cnd
> srcDirs = sd $ buildInfo ctd
> in SI srcDirs ("Executable: " ++ n)
> [fromString $ dropExtension $ modulePath ctd]
> (otherModules $ buildInfo ctd)
> (condTreeConstraints cnd)
> testsSis = flip map (condTestSuites di) $ \(n,cnd) ->
> let ctd = condTreeData cnd
> srcDirs = sd $ testBuildInfo ctd
> modName = case testInterface ctd of
> TestSuiteExeV10 _ p -> [fromString $ dropExtension p]
> x -> error $ "test suite type not supported: " ++ show x
> in SI srcDirs ("Executable: " ++ n)
> modName
> (otherModules $ testBuildInfo ctd)
> (condTreeConstraints cnd)
> benchmarksSis = [] -- TODO
> sis = maybeToList libSi ++ exeSis ++ testsSis ++ benchmarksSis
> allMis <- modulesInfo $ nub $ concatMap siSrcDirs sis
> psGood <- and <$> mapM (checkModulesAndPackages allMis) sis
> vsGood <- checkNewestVersions fp
> if psGood && vsGood
> then exitSuccess
> else exitFailure
> where
> moduleString = intercalate "." . components
> dependencyName (Dependency (PackageName s) _) = s
> checkLists section field cab fil = do
> let cab' = nub cab
> fil' = nub fil
> extraCab = cab' \\ fil'
> missingCab = fil' \\ cab'
> unless (null extraCab) $ putStrLn $ "extra unneeded " ++ field ++ " in " ++ section ++ ":\n" ++ intercalate "\n" extraCab
> unless (null missingCab) $ putStrLn $ "missing " ++ field ++ " in " ++ section ++ ":\n" ++ intercalate "\n" missingCab
> return $ null extraCab && null missingCab
> checkModulesAndPackages allMis si = do
> let rootModules = map moduleString $ siModRoots si
> mis = filter ((`elem` rootModules) . miModuleName . snd) allMis
> filesModules = sort $ nub $ concatMap (map snd . miLocalTransitiveDependencies . snd) mis
> filesPackages = sort $ nub $ concatMap (miTransitivePackages . snd) mis
> cabalOms = map moduleString (siOtherMods si)
> cabalPackages = map dependencyName (siBuildDeps si)
> a <- checkLists (siSectionName si) "other-modules" cabalOms (filesModules \\ rootModules)
> b <- checkLists (siSectionName si) "build-depends" cabalPackages filesPackages
> return $ a && b
> checkNewestVersions :: FilePath -> IO Bool
> checkNewestVersions fp = do
> newest <- loadNewest
> mdi <- loadPackage fp
> di2 <- case mdi of
> Just di2 -> return di2
> Nothing -> error $ "Could not parse cabal file: " ++ fp
> case checkDeps newest di2 of
> (_pn, _v, AllNewest) ->
> return True
> (_pn, _v, WontAccept p _) -> do
> putStrLn "Cannot accept the following packages"
> forM_ p $ \(x, y) -> putStrLn $ x ++ " " ++ y
> return False