packages feed

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 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