package-o-tron (empty) → 0.1.0.0
raw patch · 12 files changed
+849/−0 lines, 12 filesdep +Cabaldep +basedep +filemanipsetup-changed
Dependencies added: Cabal, base, filemanip, filepath, groom, packdeps, process
Files
- Distribution/Pot/Modules.lhs +140/−0
- Distribution/Pot/Packages.lhs +38/−0
- LICENSE +30/−0
- Makefile +59/−0
- README +107/−0
- Setup.hs +2/−0
- autorules.mk +156/−0
- exe-src/CabalLint.lhs +124/−0
- exe-src/Makefilerize.lhs +92/−0
- exe-src/ShowPackages.lhs +23/−0
- package-o-tron.cabal +57/−0
- todo +21/−0
+ Distribution/Pot/Modules.lhs view
@@ -0,0 +1,140 @@++simplistic parser which can list the modules imported by a haskell+source file++provides a recursive-memoized mode which can efficiently get the+complete transitive imports of a set of files++provides a method of splitting the imports into modules which are in+the local fileset, and modules which aren't (the local module+dependencies can be used for a build tool, and the non-local ones for+determining which packages are referenced)++> {-# LANGUAGE TupleSections #-}+> module Distribution.Pot.Modules+> (ModuleInfo(..)+> ,modulesInfo+> ,showmi+> ,filterModules) where++> import Data.Maybe+> import qualified System.FilePath.Find as F+> import System.FilePath.Find ((==?),(||?))+> import System.FilePath+> import Text.Groom+> import Data.List+> import Distribution.Pot.Packages+> import Control.Arrow++> parseImports :: String -> [String]+> parseImports src = mapMaybe (im . words) $ lines src+> where+> im (">":x:_) | x /= "import" = Nothing+> im (">":"import":"qualified":nm:_) = Just nm+> im (">":"import":nm:_) = Just nm+> im (x:_) | x /= "import" = Nothing+> im ("import":"qualified":nm:_) = Just nm+> im ("import":nm:_) = Just nm+> im _ = Nothing++reads the folders passed in as source roots and returns a list of all+the modules++> getLocalModules :: [FilePath] -> IO [(FilePath,FilePath)]+> getLocalModules dirs =+> concat `fmap` mapM findHs dirs+> where+> findHs dir =+> map ((dir,) . makeRelative dir) `fmap`+> F.find (return True) (F.extension ==? ".lhs" ||? F.extension ==? ".hs") dir++> -- | The collected information on one module+> data ModuleInfo =+> ModuleInfo+> {miFileName :: FilePath+> ,miFilePrefix :: FilePath+> ,miModuleFile :: FilePath+> ,miModuleName :: String+> ,miLocalDependencies :: [((FilePath,FilePath),String)]+> ,miLocalTransitiveDependencies :: [((FilePath,FilePath),String)]+> ,miPackages :: [String]+> ,miTransitivePackages :: [String]+> } deriving Show+++> -- | Takes a set of source files and gets the ModuleInfo information+> -- for all of them+> modulesInfo :: [FilePath] -- ^ the root folders containing the source files+> -- to analyze+> -> IO [(FilePath,ModuleInfo)]+> modulesInfo srcs = do+> lms <- getLocalModules srcs+> mps <- mapM (\(a,b) -> ((a,b),) `fmap` parseImports `fmap` readFile (a </> b)) lms+> -- mps is a map from (sourceroot,modulepath) to [modules imported]+> --putStrLn $ intercalate "\n\n" $ map (\(n,is) -> show n ++ "\n" ++ groom is) mps+> -- split mps 2nd elements into local modules and other modules+> let localModuleFiles = map (snd . fst) mps+> localModuleNames = map (repSlash . dropExtension) localModuleFiles+> --putStrLn $ groom localModuleNames+> let mps2 = map (second (partition (`elem` localModuleNames))) mps+> --putStrLn $ intercalate "\n\n" $ map (\(n,is) -> show n ++ "\n" ++ groom is) mps2+> -- process the other module lists to get list of packages instead+> pkgs <- readPackages+> let mps3 = map (second $ second $ getPackages pkgs) mps2+> tms = map mm mps3+> -- add the transitive local dependencies+> ldeps = map (\((_,mf),(lds,_)) ->+> (repSlash (dropExtension mf), lds)) mps3+> allDeps :: String -> [String]+> allDeps f = sort $ nub+> $ maybe []+> (\x -> x ++ concatMap allDeps x)+> $ lookup f ldeps+> tmslds = map (second $ \m -> m {miLocalTransitiveDependencies =+> map (("",""),) $ allDeps (miModuleName m)}) tms+> -- add the transitive packages+> packm :: [(String,[String])]+> packm = map (\((_,mf),(_,ps)) ->+> (repSlash (dropExtension mf), ps)) mps3+> packs s = fromMaybe [] $ lookup s packm+> tmspacks = map (second $ \m -> m {miTransitivePackages =+> sort $ nub+> $ concatMap packs (miModuleName m+> : map snd (miLocalTransitiveDependencies m))})+> tmslds+> lmFile (_,m) =+> maybe (error $ "couldn't get filename for " ++ m)+> (\(r,f) -> ((r, f), m))+> $ find ((==m) . repSlash . dropExtension . snd) lms+> ff x = x {miLocalDependencies = map lmFile $ miLocalDependencies x+> ,miLocalTransitiveDependencies = map lmFile $ miLocalTransitiveDependencies x}+> --putStrLn $ intercalate "\n\n" $ map (\(n,is) -> show n ++ "\n" ++ groom is) mps3+> t2 = map (second ff) tmspacks+> return t2+> where+> mm ((r,f),(lds,ps)) = (r </> f+> ,ModuleInfo {miFileName = r </> f+> ,miFilePrefix = r+> ,miModuleFile = f+> ,miModuleName = repSlash (dropExtension f)+> ,miLocalDependencies = map (("",""),) lds+> ,miLocalTransitiveDependencies = []+> ,miPackages = ps+> ,miTransitivePackages = []})+> repSlash = map $ \c -> case c of+> '/' -> '.'+> _ -> c+> getPackages :: [(String,[String])] -> [String] -> [String]+> getPackages pkgs ms =+> let pkg m = fst `fmap` find (\(_,pms) -> m `elem` pms) pkgs+> p1 = mapMaybe pkg ms+> in sort $ nub p1++> -- | helper to filter the two module lists according to some function+> filterModules :: (String -> Bool) -> ModuleInfo -> ModuleInfo+> filterModules f mi = mi {miPackages = filter f $ miPackages mi+> ,miTransitivePackages = filter f $ miTransitivePackages mi}+++> showmi :: [(FilePath,ModuleInfo)] -> String+> showmi = intercalate "\n\n" . map (\(f,i) -> f ++ "\n" ++ groom i)
+ Distribution/Pot/Packages.lhs view
@@ -0,0 +1,38 @@++> module Distribution.Pot.Packages (readPackages) where++> import Data.List+> import System.Process+> import Text.Groom+++> -- | returns a map from package name to the names of the modules in+> -- that package. The information is from the output of ghc-pkg dump, so+> -- only includes information from installed packages+> readPackages :: IO [(String,[String])]+> readPackages = do+> inf <- readProcess "ghc-pkg" ["dump"] ""+> return $ kludgePackages $ parsePackages [] $ lines inf+> where+> parsePackages acc (f:v) | "name: " `isPrefixOf` f =+> parseModules acc (drop (length "name: ") f) v+> parsePackages acc (_:v) = parsePackages acc v+> parsePackages acc [] = acc+> parseModules acc nm (f:v) | "exposed-modules:" `isPrefixOf` f =+> parseMoreModules acc nm [drop (length "exposed-modules:") f] v+> parseModules acc nm (_:v) = parseModules acc nm v+> parseModules _ nm [] = error $ "didn't find exposed-modules for package " ++ nm+> parseMoreModules acc nm macc ((' ':f):v) = parseMoreModules acc nm (f:macc) v+> parseMoreModules acc nm macc v =+> let ms = (nm, words $ unwords macc)+> in parsePackages (ms:acc) v++> kludgePackages :: [(String,[String])] -> [(String,[String])]+> -- lots of modules appear in base, haskell98, haskell2010+> -- just use base for now.+> kludgePackages = filter ((`notElem` ["haskell98"+> ,"haskell2010"]) . fst)+++> _s :: [(String,[String])] -> String+> _s = intercalate "\n\n" . map (\(n,ms) -> n ++ "\n" ++ groom ms ++ "\n")
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2012, Jake Wheat++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.++ * Neither the name of Jake Wheat nor the names of other+ contributors may 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.
+ Makefile view
@@ -0,0 +1,59 @@++# Example makefile which can compile the source for this project.+# Look in the autorules.mk to see the automatically generated+# support. You can regenerate the autorules.mk using the autorules+# rule++# the source directories where the haskell source roots are (paths+# relative to this Makefile)+SRC_DIRS = . exe-src++# the names of exe files to compile (these need to be the full paths+# from the Makefile). You have to use ./ prefix if it is in the root folder+EXE_FILES = exe-src/Makefilerize exe-src/Dump exe-src/DumpPackageDB \+ exe-src/ShowPackages exe-src/CabalLint++# folder to put build and exe files in+BUILD = build++# the command and options used to compile .hs/.lhs to .o+HC = ghc+HC_BASIC_OPTS = -Wall++space :=+space +=+comma := ,+HC_INCLUDE_DIRS = -i$(subst $(space),:,$(SRC_DIRS))++HC_OPTS = $(HC_BASIC_OPTS) $(HC_INCLUDE_DIRS)++# the command and options used to link .o files to an executable+# usually the same as the compile commands+# you could put options which are needed to link all the exes+# here+# there is also a variable per exe to add link specific commands+# for that exe (e.g. some of your exes need to link to a .so+# and some don't)+HL = $(HC)+HL_OPTS = $(HC_OPTS)+++# create an all rule which builds all the exes+# (the exes are all created directly in the $(BUILD) folder+EXE_FILES_TARGETS = $(addprefix $(BUILD)/, $(notdir $(EXE_FILES)))+all : $(EXE_FILES_TARGETS)++# include the autogenerated rules+-include autorules.mk++# regenerate the dependency and rules for exe compiles+# use cabal configure && cabal build to make the Makefilerize exe+.PHONY : autorules+autorules :+ Makefilerize package-o-tron FLDS $(SRC_DIRS) EXES $(EXE_FILES) > \+ autorules.mk++.PHONY : clean+clean :+ -rm -Rf dist/+ -rm -Rf build/
+ README view
@@ -0,0 +1,107 @@+Package-o-tron is a collection of utilities to help managing cabal+files for your projects, maintaining Makefiles and other build+systems, and also some which help with your package database.+++The central code is a function which takes a list of folders, then+parses all the imports in all the haskell source in these folders. It+then creates the dependency information between the different files,+and the dependency on packages information (using ghc-pkg dump)+++Currently implemented:++The Makefile in the package-o-tron package gives an example of how to+use package-o-tron to help manage a makefile for haskell projects,+using the Makefilerize command.++You could use a Makefile:++* to quickly develop when you have multiple .cabal projects and don't+ want to spend time reinstalling them when you do a rebuild (this can+ also be much faster if you use -j and have lots of executables)++* if you have files to compile as part of the development which aren't+ in a cabal package, or if you want to integrate with existing+ Makefiles, e.g. for mixed haskell/c projects.++See the Makefile and autorules.mk in the repo (link below) for an+example of how this works. This tool is used in the hssqlppp project+Makefile (https://github.com/JakeWheat/hssqlppp), see the Makefile and+autorules.mk there for another example.+++CabalLint++CabalLint will check the following issues in your cabal file:++* missing and superfluous other-modules and build-deps in library,exe+ and test sections++* version ranges which exclude the latest versions of a build-dep+ (just uses packdeps from hackage)++run it like this:+CabalLint package-o-tron.cabal+No output means everything OK+++ShowPackages++Run on a set of source folders, and it will show you all the packages+referenced by the source in those folders++++Planned utils:++Showpackages: extend to show indirectly depended on packages as well++blessed package set creation: run on some folders to get a package+list. Then it follows the package dependencies to get all the needed+non-system packages. Then it looks in your .cabal and copies out the+tarballs for all these packages. Now you can create a fresh install in+a sandbox or fresh machine without needing an internet connection, or+worrying about if hackage is down, or if the latest set of packages+don't install.++Cabal build testing++Use sandboxing to check the sdists from a .cabal install without error+and if there are tests they run and pass. The sandboxing is used to+check a range of versions of ghc crossed with the lowest and latest+versions of the dependencies listed in the .cabal. Later, maybe this+can be extended to search earlier versions of dependencies to get more+accurate lower bounds.++Package database lint:++check your ghc package database for the following issues:+duplicate packages installed+packages which aren't the latest versions installed+not sure how to automatically help with these situations though++Third party cabal install checker:++run a wrapper around cabal-install ... --dry-run. reports:+any reinstalls (easy since cabal already does this)+any different versions of already installed packages would be+ installed+any versions of packages which aren't the latest of those packages+ would be installed++Third party cabal file hacker:++when you try to install a lot of packages, and some of them have+version constraints on their dependencies which lead to another+package being installed not at the latest version: this will cabal+unpack the offending packages and alter their constraints to allow all+the latest versions of packages automatically. Not completely safe of+course, but I always end up doing this manually and it is really+tedious.++++Repository: https://github.com/JakeWheat/package-o-tron++Contact: jakewheatmail@gmail.com
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ autorules.mk view
@@ -0,0 +1,156 @@+$(BUILD)/Distribution/Pot/Modules.o : ./Distribution/Pot/Modules.lhs \+ $(BUILD)/Distribution/Pot/Packages.hi+ -mkdir -p $(BUILD)/Distribution/Pot/+ $(HC) $(HC_OPTS) -hide-all-packages -outputdir $(BUILD)/ -package base -package filemanip -package filepath -package groom -c $< -o $(BUILD)/Distribution/Pot/Modules.o \+ -i$(BUILD)/++$(BUILD)/Distribution/Pot/Packages.o : ./Distribution/Pot/Packages.lhs+ -mkdir -p $(BUILD)/Distribution/Pot/+ $(HC) $(HC_OPTS) -hide-all-packages -outputdir $(BUILD)/ -package base -package groom -package process -c $< -o $(BUILD)/Distribution/Pot/Packages.o \+ -i$(BUILD)/++$(BUILD)/Setup.o : ./Setup.hs+ -mkdir -p $(BUILD)/+ $(HC) $(HC_OPTS) -hide-all-packages -outputdir $(BUILD)/ -package base -package Cabal -c $< -o $(BUILD)/Setup.o \+ -i$(BUILD)/++$(BUILD)/exe-src/CabalLint.o : ./exe-src/CabalLint.lhs \+ $(BUILD)/Distribution/Pot/Modules.hi+ -mkdir -p $(BUILD)/exe-src/+ $(HC) $(HC_OPTS) -hide-all-packages -outputdir $(BUILD)/ -package Cabal -package base -package filepath -package packdeps -c $< -o $(BUILD)/exe-src/CabalLint.o \+ -i$(BUILD)/++$(BUILD)/exe-src/Dump.o : ./exe-src/Dump.lhs \+ $(BUILD)/Distribution/Pot/Modules.hi+ -mkdir -p $(BUILD)/exe-src/+ $(HC) $(HC_OPTS) -hide-all-packages -outputdir $(BUILD)/ -package base -c $< -o $(BUILD)/exe-src/Dump.o \+ -i$(BUILD)/++$(BUILD)/exe-src/DumpPackageDB.o : ./exe-src/DumpPackageDB.lhs \+ $(BUILD)/Distribution/Pot/Packages.hi+ -mkdir -p $(BUILD)/exe-src/+ $(HC) $(HC_OPTS) -hide-all-packages -outputdir $(BUILD)/ -package base -c $< -o $(BUILD)/exe-src/DumpPackageDB.o \+ -i$(BUILD)/++$(BUILD)/exe-src/Makefilerize.o : ./exe-src/Makefilerize.lhs \+ $(BUILD)/Distribution/Pot/Modules.hi+ -mkdir -p $(BUILD)/exe-src/+ $(HC) $(HC_OPTS) -hide-all-packages -outputdir $(BUILD)/ -package base -package filepath -c $< -o $(BUILD)/exe-src/Makefilerize.o \+ -i$(BUILD)/++$(BUILD)/exe-src/ShowPackages.o : ./exe-src/ShowPackages.lhs \+ $(BUILD)/Distribution/Pot/Modules.hi+ -mkdir -p $(BUILD)/exe-src/+ $(HC) $(HC_OPTS) -hide-all-packages -outputdir $(BUILD)/ -package base -c $< -o $(BUILD)/exe-src/ShowPackages.o \+ -i$(BUILD)/++$(BUILD)/CabalLint.o : exe-src/CabalLint.lhs \+ $(BUILD)/Distribution/Pot/Modules.hi+ -mkdir -p $(BUILD)/+ $(HC) $(HC_OPTS) -hide-all-packages -outputdir $(BUILD)/ -package Cabal -package base -package filepath -package packdeps -c $< -o $(BUILD)/CabalLint.o \+ -i$(BUILD)/++$(BUILD)/Dump.o : exe-src/Dump.lhs \+ $(BUILD)/Distribution/Pot/Modules.hi+ -mkdir -p $(BUILD)/+ $(HC) $(HC_OPTS) -hide-all-packages -outputdir $(BUILD)/ -package base -c $< -o $(BUILD)/Dump.o \+ -i$(BUILD)/++$(BUILD)/DumpPackageDB.o : exe-src/DumpPackageDB.lhs \+ $(BUILD)/Distribution/Pot/Packages.hi+ -mkdir -p $(BUILD)/+ $(HC) $(HC_OPTS) -hide-all-packages -outputdir $(BUILD)/ -package base -c $< -o $(BUILD)/DumpPackageDB.o \+ -i$(BUILD)/++$(BUILD)/Makefilerize.o : exe-src/Makefilerize.lhs \+ $(BUILD)/Distribution/Pot/Modules.hi+ -mkdir -p $(BUILD)/+ $(HC) $(HC_OPTS) -hide-all-packages -outputdir $(BUILD)/ -package base -package filepath -c $< -o $(BUILD)/Makefilerize.o \+ -i$(BUILD)/++$(BUILD)/ShowPackages.o : exe-src/ShowPackages.lhs \+ $(BUILD)/Distribution/Pot/Modules.hi+ -mkdir -p $(BUILD)/+ $(HC) $(HC_OPTS) -hide-all-packages -outputdir $(BUILD)/ -package base -c $< -o $(BUILD)/ShowPackages.o \+ -i$(BUILD)/+$(BUILD)/Makefilerize : $(BUILD)/Makefilerize.o \+ $(BUILD)/Distribution/Pot/Modules.o \+ $(BUILD)/Distribution/Pot/Packages.o+ -mkdir -p $(BUILD)/+ $(HL) $(HL_OPTS) $(MAKEFILERIZE_EXTRA) \+ -o $(BUILD)/Makefilerize \+ $(BUILD)/Makefilerize.o \+ $(BUILD)/Distribution/Pot/Modules.o \+ $(BUILD)/Distribution/Pot/Packages.o \+ -hide-all-packages \+ -package base \+ -package filemanip \+ -package filepath \+ -package groom \+ -package process++$(BUILD)/Dump : $(BUILD)/Dump.o \+ $(BUILD)/Distribution/Pot/Modules.o \+ $(BUILD)/Distribution/Pot/Packages.o+ -mkdir -p $(BUILD)/+ $(HL) $(HL_OPTS) $(DUMP_EXTRA) \+ -o $(BUILD)/Dump \+ $(BUILD)/Dump.o \+ $(BUILD)/Distribution/Pot/Modules.o \+ $(BUILD)/Distribution/Pot/Packages.o \+ -hide-all-packages \+ -package base \+ -package filemanip \+ -package filepath \+ -package groom \+ -package process++$(BUILD)/DumpPackageDB : $(BUILD)/DumpPackageDB.o \+ $(BUILD)/Distribution/Pot/Packages.o+ -mkdir -p $(BUILD)/+ $(HL) $(HL_OPTS) $(DUMPPACKAGEDB_EXTRA) \+ -o $(BUILD)/DumpPackageDB \+ $(BUILD)/DumpPackageDB.o \+ $(BUILD)/Distribution/Pot/Packages.o \+ -hide-all-packages \+ -package base \+ -package groom \+ -package process++$(BUILD)/ShowPackages : $(BUILD)/ShowPackages.o \+ $(BUILD)/Distribution/Pot/Modules.o \+ $(BUILD)/Distribution/Pot/Packages.o+ -mkdir -p $(BUILD)/+ $(HL) $(HL_OPTS) $(SHOWPACKAGES_EXTRA) \+ -o $(BUILD)/ShowPackages \+ $(BUILD)/ShowPackages.o \+ $(BUILD)/Distribution/Pot/Modules.o \+ $(BUILD)/Distribution/Pot/Packages.o \+ -hide-all-packages \+ -package base \+ -package filemanip \+ -package filepath \+ -package groom \+ -package process++$(BUILD)/CabalLint : $(BUILD)/CabalLint.o \+ $(BUILD)/Distribution/Pot/Modules.o \+ $(BUILD)/Distribution/Pot/Packages.o+ -mkdir -p $(BUILD)/+ $(HL) $(HL_OPTS) $(CABALLINT_EXTRA) \+ -o $(BUILD)/CabalLint \+ $(BUILD)/CabalLint.o \+ $(BUILD)/Distribution/Pot/Modules.o \+ $(BUILD)/Distribution/Pot/Packages.o \+ -hide-all-packages \+ -package Cabal \+ -package base \+ -package filemanip \+ -package filepath \+ -package groom \+ -package packdeps \+ -package process+++%.hi : %.o+ @:
+ exe-src/CabalLint.lhs view
@@ -0,0 +1,124 @@++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
+ exe-src/Makefilerize.lhs view
@@ -0,0 +1,92 @@+Example of calling this file:++Makefilerize package-o-tron FLDS . exe-src EXES exe-src/Makefilerize exe-src/ShowPackages exe-src/CabalLint++write the packages to ignore first the the text 'FLDS'+then write the folders which source appears in (same folders you would+pass to -i with ghc) then the text 'EXES'+write the paths to the exes++It will generate rules to compile all the .o files and link the exes,+with explicit package lists.++See the included Makefile, autorules.mk file for an example of how to+use this, and an example of what is output.++TODO:++support two stage .o, .dyn_o to get creation of .so with source that+uses template haskell++> import Distribution.Pot.Modules+> import Data.List+> import System.FilePath+> import System.Environment+> import Control.Arrow+> import Data.Char+> import Data.Maybe++> moduleCompile :: ModuleInfo -> String+> moduleCompile mi =+> objOf mi+> ++ " : " ++ intercalate " \\\n " (miFileName mi : map (fhiOf . fst) (miLocalDependencies mi))+> ++ "\n\t-mkdir -p " ++ dropFileName (objOf mi)+> ++ "\n\t$(HC) $(HC_OPTS) -hide-all-packages -outputdir $(BUILD)/ "+> ++ unwords (map ("-package " ++) $ addBase $ miPackages mi)+> ++ " -c $< -o " ++ objOf mi+> ++ " \\\n -i$(BUILD)/"+++> objOf :: ModuleInfo -> FilePath+> objOf = ("$(BUILD)/" ++) . flip replaceExtension "o" . miModuleFile+> fobjOf :: (FilePath,FilePath) -> FilePath+> fobjOf = ("$(BUILD)/" ++) . flip replaceExtension "o" . snd+> fhiOf :: (FilePath,FilePath) -> FilePath+> fhiOf = ("$(BUILD)/" ++) . flip replaceExtension "hi" . snd+> exeOf :: ModuleInfo -> FilePath+> exeOf = ("$(BUILD)/" ++) . takeFileName . dropExtension . miModuleFile++> addBase :: [String] -> [String]+> addBase x | "base" `elem` x = x+> addBase x = "base" : x++> exeLink :: ModuleInfo -> String+> exeLink mi =+> exeOf mi ++ " : "+> ++ intercalate " \\\n " (objOf mi : map (fobjOf . fst) (miLocalTransitiveDependencies mi))+> ++ "\n\t-mkdir -p $(BUILD)/"+> ++ "\n\t$(HL) $(HL_OPTS) $(" ++ mangledExeName (miFileName mi)+> ++ ") \\\n "+> ++ intercalate " \\\n "+> (["-o " ++ exeOf mi, objOf mi]+> ++ map (fobjOf . fst) (miLocalTransitiveDependencies mi)+> ++ ["-hide-all-packages"]+> ++ map ("-package " ++) (addBase $ miTransitivePackages mi))+> where+> mangledExeName = (++ "_EXTRA")+> . map toUpper+> . map (\c -> case c of+> '/' -> '_'+> _ -> c)+> . takeFileName+> . dropExtension++> main :: IO ()+> main = do+> args <- getArgs+> let (hidepacks,args') = second (drop 1) $ break (=="FLDS") args+> (modules,exes) = second (drop 1) $ break (=="EXES") args'+> mis <- map (second $ hidePackage hidepacks) `fmap` modulesInfo modules+> putStrLn $ intercalate "\n\n" $ map (moduleCompile . snd) mis+> let exeMis = map exeMi exes+> exeMi exe = fromMaybe+> (error $ "source file for exe not found: " ++ show exe+> ++ " in\n"+> ++ intercalate "\n" (map (dropExtension . miFileName . snd) mis))+> $ find ((== exe) . dropExtension . miFileName . snd) mis+> putStrLn $ intercalate "\n\n" $ map (exeLink . snd) exeMis+> putStrLn "\n\n%.hi : %.o\n\t@:"+> where+> hidePackage ps m = m {miPackages = filter (`notElem` ps) $ miPackages m+> ,miTransitivePackages = filter (`notElem` ps) $ miTransitivePackages m}+
+ exe-src/ShowPackages.lhs view
@@ -0,0 +1,23 @@++input a set of folders++finds all the haskell source in these folders and follows all the+imports of other source in these folders, and outputs the complete+list of directly referenced packages++TODO: option to include all the indirectly depended packages+to output package versions as well+to exclude system packages+to collect the tarballs for all these packages++> import Distribution.Pot.Modules+> import System.Environment+> import Control.Applicative+> import Data.List++> main :: IO ()+> main = do+> args <- getArgs+> mi <- map snd <$> modulesInfo args+> let ps = sort $ nub $ concatMap miPackages mi+> putStrLn $ intercalate "\n" ps
+ package-o-tron.cabal view
@@ -0,0 +1,57 @@+name: package-o-tron+version: 0.1.0.0+synopsis: Utilities for working with cabal packages and your package database++description: Utility to help managing Makefiles for Haskell projects, a cabal lint+ which can check the other-modules and build-deps+ sections in your cabal files, and a quick command+ that can show the direct package dependencies of+ a set of Haskell source files. See the README in+ the repo for more information:+ <https://github.com/JakeWheat/package-o-tron>.+++license: BSD3+license-file: LICENSE+author: Jake Wheat+maintainer: jakewheatmail@gmail.com+copyright: Jake Wheat 2012+category: Development+build-type: Simple+cabal-version: >=1.8++extra-source-files: autorules.mk+ LICENSE+ README+ Makefile+ todo++source-repository head+ type: git+ location: https://github.com/JakeWheat/package-o-tron.git++library+ exposed-modules: Distribution.Pot.Packages, Distribution.Pot.Modules+ build-depends: base >= 4 && < 5,+ groom,process,filepath,filemanip++executable Makefilerize+ build-depends: base >= 4 && < 5,+ filepath,groom,process,filemanip+ main-is: Makefilerize.lhs+ hs-source-dirs: .,exe-src+ other-modules: Distribution.Pot.Packages, Distribution.Pot.Modules++executable CabalLint+ build-depends: base >= 4 && < 5,+ filepath,groom,process,filemanip,Cabal,packdeps+ main-is: CabalLint.lhs+ hs-source-dirs: .,exe-src+ other-modules: Distribution.Pot.Packages, Distribution.Pot.Modules++executable ShowPackages+ build-depends: base >= 4 && < 5,+ filepath,groom,process,filemanip+ main-is: ShowPackages.lhs+ hs-source-dirs: .,exe-src+ other-modules: Distribution.Pot.Packages, Distribution.Pot.Modules
+ todo view
@@ -0,0 +1,21 @@+find a better name for 'Makefilerize'+lint: duplicate listing, unreferenced files, extensions, benchmark+ sections+fix the dump exes++blessed .tar.gz set generation++check cabal --dry-run for dups/ not latest version++package database checker: dup versions, non latest versions++tester: use sandboxing to:+ compile and run tests -> successful compile and successful test+ run is the validation+ start with the sdist tarball+ check ghc versions: target 7.0.x through 7.6.x+ check it works with all the latest package versions in the+ dependency ranges on each ghc+ check it works with all the earliest "++third party package hacker