packunused 0.1.1.1 → 0.1.1.2
raw patch · 3 files changed
+94/−47 lines, 3 filesdep +optparse-applicativedep +splitdep −cmdargs
Dependencies added: optparse-applicative, split
Dependencies removed: cmdargs
Files
- changelog.md +7/−0
- packunused.cabal +9/−8
- packunused.hs +78/−39
changelog.md view
@@ -1,5 +1,12 @@ # 0.1.1.1 + - New option `--ignore-package` to white-list redundant packages by name+ - Exit with a non-zero status code if (non-ignored) redundant dependencies are found+ - Fake support for parsing `-XSafeHaskell` and `-XExplicitNamespaces` in .imports files+ - Switched from `cmdargs` to `optparse-applicative`++# 0.1.1.1+ - Minor typo in output messages fixed # 0.1.1.0
packunused.cabal view
@@ -1,5 +1,5 @@ name: packunused-version: 0.1.1.1+version: 0.1.1.2 synopsis: Tool for detecting redundant Cabal package dependencies homepage: https://github.com/hvr/packunused bug-reports: https://github.com/hvr/packunused/issues@@ -65,12 +65,13 @@ executable packunused main-is: packunused.hs default-language: Haskell2010- other-extensions: CPP, DeriveDataTypeable, RecordWildCards+ other-extensions: CPP, RecordWildCards ghc-options: -Wall -fwarn-tabs -fno-warn-unused-do-bind build-depends:- base >=4.5 && <4.8,- Cabal >=1.14 && <1.21,- cmdargs ==0.10.*,- directory >=1.1 && <1.3,- filepath ==1.3.*,- haskell-src-exts >=1.13 && <1.16+ base >=4.5 && <4.8,+ Cabal >=1.14 && <1.21,+ optparse-applicative == 0.8.*,+ directory >=1.1 && <1.3,+ filepath ==1.3.*,+ haskell-src-exts >=1.13 && <1.16,+ split ==0.2.*
packunused.hs view
@@ -1,65 +1,86 @@-{-# LANGUAGE CPP, DeriveDataTypeable, RecordWildCards #-}+{-# LANGUAGE CPP, RecordWildCards #-} module Main where import Control.Monad+import Data.IORef import Data.List+import Data.List.Split (splitOn) import Data.Maybe+import Data.Monoid import Data.Version (Version(Version), showVersion) import Distribution.InstalledPackageInfo (exposedModules, installedPackageId) import Distribution.ModuleName (ModuleName)-import Distribution.Simple.Compiler import qualified Distribution.ModuleName as MN-import Distribution.Package (InstalledPackageId(..))+import Distribution.Package (InstalledPackageId(..), packageId, pkgName) import qualified Distribution.PackageDescription as PD+import Distribution.Simple.Compiler import Distribution.Simple.Configure (localBuildInfoFile, getPersistBuildConfig, checkPersistBuildConfigOutdated) import Distribution.Simple.LocalBuildInfo import Distribution.Simple.PackageIndex (lookupInstalledPackageId) import Distribution.Simple.Utils (cabalVersion) import Distribution.Text (display) import qualified Language.Haskell.Exts as H-import System.Console.CmdArgs.Implicit+import Options.Applicative+import Options.Applicative.Help.Pretty (Doc)+import qualified Options.Applicative.Help.Pretty as P import System.Directory (getModificationTime, getDirectoryContents, doesDirectoryExist, doesFileExist) import System.Exit (exitFailure) import System.FilePath ((</>)) import Paths_packunused (version) --- |CLI Options+-- | CLI Options data Opts = Opts { ignoreEmptyImports :: Bool , ignoreMainModule :: Bool- } deriving (Show, Data, Typeable)+ , ignoredPackages :: [String]+ } deriving (Show) -opts :: Opts-opts = Opts- { ignoreEmptyImports = def &= name "ignore-empty-imports" &= explicit- , ignoreMainModule = def &= name "ignore-main-module" &= explicit- }- &= program "packunused"- &= summary ("packunused " ++ showVersion version ++ " (using Cabal "++ showVersion cabalVersion ++ ")")- &= details helpDesc+opts :: Parser Opts+opts = Opts <$> switch (long "ignore-empty-imports" <> help "ignore empty .imports files")+ <*> switch (long "ignore-main-module" <> help "ignore Main modules")+ <*> many (strOption (long "ignore-package" <> metavar "PKG" <>+ help "ignore the specfied package in the report")) -helpDesc :: [String]-helpDesc = [ "Tool to help find redundant build-dependencies in CABAL projects"- , ""- , "In order to use this package you should set up the package as follows, before executing 'packunused':"- , ""- , " cabal clean"- , " rm *.imports # (only needed for GHC<7.8)"- , " cabal configure -O0 --disable-library-profiling"- , " cabal build --ghc-option=-ddump-minimal-imports"- , " packunused"- , ""- , "Note: The 'cabal configure' command above only tests the default package configuration." ++- "You might need to repeat the process with different flags added to the 'cabal configure' step " ++- "(such as '--enable-tests' or '--enable-benchmark' or custom cabal flags) " ++- "to make sure to check all configurations"- ]+helpFooter :: Doc+helpFooter = mconcat+ [ P.text "Tool to help find redundant build-dependencies in CABAL projects", P.linebreak+ , P.hardline+ , para $ "In order to use this tool you should set up the package to be analyzed as follows, " +++ "before executing 'packunused':", P.linebreak + , P.hardline+ , P.indent 2 $ P.vcat $ P.text <$>+ [ "cabal clean"+ , "rm *.imports # (only needed for GHC<7.8)"+ , "cabal configure -O0 --disable-library-profiling"+ , "cabal build --ghc-option=-ddump-minimal-imports"+ , "packunused"+ ]+ , P.linebreak, P.hardline+ , P.text "Note:" P.<+> P.align+ (para $ "The 'cabal configure' command above only tests the default package configuration. " +++ "You might need to repeat the process with different flags added to the 'cabal configure' step " +++ "(such as '--enable-tests' or '--enable-benchmark' or custom cabal flags) " +++ "to make sure to check all configurations")+ , P.linebreak, P.hardline+ , P.text "Report bugs to https://github.com/hvr/packunused/issues"+ ]+ where+ para = P.fillSep . map P.text . words++helpHeader :: String+helpHeader = "packunused " ++ showVersion version +++ " (using Cabal "++ showVersion cabalVersion ++ ")"+ main :: IO () main = do- Opts {..} <- cmdArgs opts+ Opts {..} <- execParser $+ info (helper <*> opts)+ (header helpHeader <>+ fullDesc <>+ footerDoc (Just helpFooter)) -- print opts' @@ -111,6 +132,9 @@ -- GHC prior to 7.8.1 emitted .imports file in $PWD and therefore would risk overwriting files let multiMainIssue = not importsInOutDir && length (filter (/= CLibName) cbo) > 1 ++ ok <- newIORef True+ -- handle stanzas withAllComponentsInBuildOrder pkg lbi $ \c clbi -> do let (n,n2,cmods) = componentNameAndModules (not ignoreMainModule) c@@ -137,8 +161,10 @@ , not ("-inplace" `isSuffixOf` i) ] + (ignored, unignored) = partition (\x -> display (pkgName $ packageId x) `elem` ignoredPackages) ipinfos+ unused = [ installedPackageId ipinfo- | ipinfo <- ipinfos+ | ipinfo <- unignored , let expmods = exposedModules ipinfo , not (any (`elem` allmods) expmods) ]@@ -162,6 +188,11 @@ putStrLn " to get a more accurate result for this component." putStrLn "" + unless (null ignored) $ do+ let k = length ignored+ putStrLn $ "Ignoring " ++ show k ++ " package" ++ (if k == 1 then "" else "s")+ putStrLn ""+ if null unused then do putStrLn "no redundant packages dependencies found"@@ -171,8 +202,9 @@ putStrLn "" forM_ unused $ \pkg' -> putStrLn $ " - " ++ display pkg' putStrLn ""+ writeIORef ok False - return ()+ whenM (not <$> readIORef ok) exitFailure where distPref = "./dist" @@ -240,25 +272,32 @@ let m = MN.fromString $ take (length fn - length ".imports") fn - parseRes <- H.parseFileWithExts exts (outDir </> fn)- case parseRes of+ contents <- readFile (outDir </> fn)+ case parseImportsFile contents of (H.ParseOk (H.Module _ _ _ _ _ imps _)) -> do let imps' = [ (MN.fromString mn, extractSpecs (H.importSpecs imp)) | imp <- imps, let H.ModuleName mn = H.importModule imp ] return (m, imps')- H.ParseFailed {} -> do- print parseRes+ H.ParseFailed loc msg -> do+ putStrLn "*ERROR* failed to parse .imports file"+ putStrLn $ H.prettyPrint loc ++ ": " ++ msg exitFailure where extractSpecs (Just (False, impspecs)) = map H.prettyPrint impspecs extractSpecs _ = error "unexpected import specs" + parseImportsFile = H.parseFileContentsWithMode (H.defaultParseMode { H.extensions = exts, H.parseFilename = outDir </> fn }) . stripExplicitNamespaces . stripSafe++ -- hack to remove -XExplicitNamespaces until haskell-src-exts supports that+ stripExplicitNamespaces = unwords . splitOn " type "+ stripSafe = unwords . splitOn " safe "+ #if MIN_VERSION_haskell_src_exts(1,14,0)- exts = map H.EnableExtension [ H.MagicHash, H.PackageImports, H.CPP, H.TypeOperators, H.TypeFamilies ]+ exts = map H.EnableExtension [ H.MagicHash, H.PackageImports, H.CPP, H.TypeOperators, H.TypeFamilies {- , H.ExplicitNamespaces -} ] #else- exts = [ H.MagicHash, H.PackageImports, H.CPP, H.TypeOperators, H.TypeFamilies {- , H.ExplicitNamespaces -} ]+ exts = [ H.MagicHash, H.PackageImports, H.CPP, H.TypeOperators, H.TypeFamilies ] #endif