packages feed

hackage-revdeps 0.1 → 0.1.1

raw patch · 6 files changed

+74/−61 lines, 6 filesdep +cabal-installdep −cabal-install-parsersdep ~Cabaldep ~Cabal-syntaxdep ~filepathPVP ok

version bump matches the API change (PVP)

Dependencies added: cabal-install

Dependencies removed: cabal-install-parsers

Dependency ranges changed: Cabal, Cabal-syntax, filepath, optparse-applicative, time

API changes (from Hackage documentation)

Files

CHANGELOG.md view
@@ -1,3 +1,7 @@+## 0.1.1++* Replace dependency on `cabal-install-parsers` with `cabal-install` proper.+ ## 0.1  * Initial release.
README.md view
@@ -2,4 +2,4 @@  Command-line tool to list Hackage reverse dependencies. -It is different from how Hackage itself tracks them: this tool accounts for all package components, including tests and benchmarks, and counts dependencies only across the latest releases. The approach is roughly equivalent to what [packdeps.haskellers.com](https://packdeps.haskellers.com) used to do.+It is different from how Hackage itself tracks them: this tool accounts for all package components, including tests and benchmarks, and counts dependencies only across the latest releases. The approach is roughly equivalent to what [packdeps.haskellers.com](https://packdeps.haskellers.com) does.
app/History.hs view
@@ -5,7 +5,6 @@   main, ) where -import Cabal.Config (cfgRepoIndex, hackageHaskellOrg, readConfig) import Data.ByteString.Char8 qualified as B import Data.Foldable (for_) import Data.List qualified as L@@ -13,12 +12,15 @@ import Data.List.NonEmpty qualified as NE import Data.Map.Strict qualified as M import Data.Time (Day, UTCTime (..), addDays, getCurrentTime)+import Distribution.Client.Config (loadConfig, savedGlobalFlags)+import Distribution.Client.GlobalFlags (globalCacheDir)+import Distribution.Simple.Flag (fromFlag) import Distribution.Types.PackageName (PackageName, unPackageName) import Hackage.RevDeps (extractDependencies, latestReleases) import Options.Applicative (Parser, auto, execParser, fullDesc, help, helper, info, long, metavar, option, progDesc, showDefault, strArgument, value) import Options.Applicative.NonEmpty (some1) import System.Console.ANSI (hSupportsANSI, hyperlinkCode)-import System.Exit (die)+import System.FilePath ((</>)) import System.IO (stdout)  data Config = Config@@ -66,18 +68,17 @@       dates = L.nub $ [cnfStart, addDays (fromIntegral cnfStep) cnfStart .. cnfFinish] ++ [cnfFinish]   supportsAnsi <- hSupportsANSI stdout -  cnf <- readConfig-  case cfgRepoIndex cnf hackageHaskellOrg of-    Nothing -> die $ hackageHaskellOrg ++ " not found in cabal.config, aborting"-    Just idx -> do-      putStrLn $ unwords $ "Date      " : map (showPackage supportsAnsi) args-      let needles = map (B.pack . unPackageName) args-      for_ dates $ \date -> do-        releases <- latestReleases needles idx (Just $ UTCTime date 0)-        let pkgs = fmap (extractDependencies args) releases-            pkgs' = M.mapWithKey (\k v -> M.delete k v) pkgs-            counters = M.unionsWith (+) $ fmap (fmap (const (1 :: Int))) pkgs'-        putStrLn $ unwords $ show date : map (\pkg -> showPair (pkg, M.findWithDefault 0 pkg counters)) args+  cnf <- loadConfig minBound mempty+  let cacheDir = fromFlag $ globalCacheDir $ savedGlobalFlags cnf+      idx = cacheDir </> hackageHaskellOrg </> "01-index.tar"+  putStrLn $ unwords $ "Date      " : map (showPackage supportsAnsi) args+  let needles = map (B.pack . unPackageName) args+  for_ dates $ \date -> do+    releases <- latestReleases needles idx (Just $ UTCTime date 0)+    let pkgs = fmap (extractDependencies args) releases+        pkgs' = M.mapWithKey M.delete pkgs+        counters = M.unionsWith (+) $ fmap (fmap (const (1 :: Int))) pkgs'+    putStrLn $ unwords $ show date : map (\pkg -> showPair (pkg, M.findWithDefault 0 pkg counters)) args  showPair :: Show v => (PackageName, v) -> String showPair (k, v) =@@ -91,3 +92,6 @@     else xs   where     xs = unPackageName p++hackageHaskellOrg :: FilePath+hackageHaskellOrg = "hackage.haskell.org"
app/Main.hs view
@@ -5,15 +5,17 @@   main, ) where -import Cabal.Config (cfgRepoIndex, hackageHaskellOrg, readConfig) import Data.ByteString.Char8 qualified as B import Data.List.NonEmpty (NonEmpty) import Data.List.NonEmpty qualified as NE import Data.Map.Strict (Map) import Data.Map.Strict qualified as M import Data.Time (Day, UTCTime (..))+import Distribution.Client.Config (loadConfig, savedGlobalFlags)+import Distribution.Client.GlobalFlags (globalCacheDir) import Distribution.Compat.NonEmptySet (singleton) import Distribution.Pretty (pretty)+import Distribution.Simple.Flag (fromFlag) import Distribution.Types.Dependency (Dependency (..)) import Distribution.Types.LibraryName (LibraryName (..)) import Distribution.Types.PackageName (PackageName, unPackageName)@@ -22,7 +24,7 @@ import Options.Applicative (Parser, ReadM, auto, execParser, fullDesc, help, helper, info, long, metavar, option, optional, progDesc, strArgument) import Options.Applicative.NonEmpty (some1) import System.Console.ANSI (hSupportsANSI, hyperlinkCode)-import System.Exit (die)+import System.FilePath ((</>)) import System.IO (stdout)  data Config = Config@@ -34,7 +36,7 @@ parseArgs = do   cnfIndexState <-     optional $-      option (fmap (\d -> UTCTime d 0) (auto :: ReadM Day)) $+      option (fmap (`UTCTime` 0) (auto :: ReadM Day)) $         long "index-state"           <> help "Timestamp of index state at which to stop scanning, YYYY-MM-DD"   cnfPackageNames <-@@ -52,26 +54,27 @@       info (helper <*> parseArgs) (fullDesc <> progDesc desc)   let args = NE.toList cnfPackageNames -  cnf <- readConfig-  case cfgRepoIndex cnf hackageHaskellOrg of-    Nothing -> die $ hackageHaskellOrg ++ " not found in cabal.config, aborting"-    Just idx -> do-      let needles = map (B.pack . unPackageName) args-      releases <- latestReleases needles idx cnfIndexState-      let pkgs = fmap (extractDependencies args) releases-          pkgs' = M.mapWithKey (\k v -> M.delete k v) pkgs-      report $ M.filter (not . null) pkgs'+  cnf <- loadConfig minBound mempty+  let cacheDir = fromFlag $ globalCacheDir $ savedGlobalFlags cnf+      idx = cacheDir </> hackageHaskellOrg </> "01-index.tar"+      needles = map (B.pack . unPackageName) args+  releases <- latestReleases needles idx cnfIndexState+  let pkgs = fmap (extractDependencies args) releases+      pkgs' = M.mapWithKey M.delete pkgs+  report $ M.filter (not . null) pkgs'  report :: Map PackageName (Map PackageName VersionRange) -> IO ()-report pkgs = do-  supportsAnsi <- hSupportsANSI stdout-  putStrLn "Reverse dependencies:"-  let prettify (k, v) = pretty $ Dependency k v (singleton LMainLibName)-      pkgs' = fmap (map prettify . M.toAscList) pkgs-  reportTable supportsAnsi pkgs'-  putStrLn "Total count:"-  let counters = M.unionsWith (+) $ fmap (fmap (const (1 :: Int))) pkgs-  reportTable supportsAnsi counters+report pkgs+  | M.null pkgs = putStrLn "No reverse dependencies found"+  | otherwise = do+      supportsAnsi <- hSupportsANSI stdout+      putStrLn "Reverse dependencies:"+      let prettify (k, v) = pretty $ Dependency k v (singleton LMainLibName)+          pkgs' = fmap (map prettify . M.toAscList) pkgs+      reportTable supportsAnsi pkgs'+      putStrLn "Total count:"+      let counters = M.unionsWith (+) $ fmap (fmap (const (1 :: Int))) pkgs+      reportTable supportsAnsi counters  reportTable :: Show v => Bool -> Map PackageName v -> IO () reportTable supportsAnsi kvs = putStrLn $ unlines $ map showPair $ M.toAscList kvs@@ -89,3 +92,6 @@     else xs   where     xs = unPackageName p++hackageHaskellOrg :: FilePath+hackageHaskellOrg = "hackage.haskell.org"
hackage-revdeps.cabal view
@@ -1,12 +1,12 @@ cabal-version:   2.2 name:            hackage-revdeps-version:         0.1+version:         0.1.1 license:         BSD-3-Clause license-file:    LICENSE maintainer:      andrew.lelechenko@gmail.com author:          Bodigrim tested-with:-    ghc ==9.12.1 ghc ==9.10.1 ghc ==9.8.4 ghc ==9.6.6 ghc ==9.4.8+    ghc ==9.12.2 ghc ==9.10.1 ghc ==9.8.4 ghc ==9.6.7 ghc ==9.4.8     ghc ==9.2.8  synopsis:        List Hackage reverse dependencies@@ -16,7 +16,7 @@     this tool accounts for all package components, including     tests and benchmarks, and counts dependencies only     across the latest releases. The approach is roughly-    equivalent to what <https://packdeps.haskellers.com> used to do.+    equivalent to what <https://packdeps.haskellers.com> does.  category:        Development build-type:      Simple@@ -40,7 +40,7 @@         alfred-margaret >=2.0 && <2.2,         base >=4.16 && <5,         bytestring <0.13,-        containers <0.8,+        containers <0.9,         filepath <1.6,         tar <0.7,         text >=2.0 && <2.2,@@ -61,17 +61,14 @@         base,         ansi-terminal >=0.11.3 && <1.2,         bytestring,-        cabal-install-parsers <0.7,+        cabal-install >=3.8 && <3.15,         containers,+        filepath,         hackage-revdeps,         optparse-applicative >=0.16 && <0.19,-        time <1.15--    if flag(cabal-syntax)-        build-depends: Cabal-syntax >=3.8 && <3.15--    else-        build-depends: Cabal <3.7+        time <1.15,+        Cabal-syntax >=3.8 && <3.15,+        Cabal >=3.8 && <3.15  executable hackage-revdeps-history     main-is:          History.hs@@ -82,14 +79,11 @@         base,         ansi-terminal >=0.11.3 && <1.2,         bytestring,-        cabal-install-parsers <0.7,+        cabal-install >=3.8 && <3.15,         containers,+        filepath,         hackage-revdeps,         optparse-applicative >=0.16 && <0.19,-        time <1.15--    if flag(cabal-syntax)-        build-depends: Cabal-syntax >=3.8 && <3.15--    else-        build-depends: Cabal <3.7+        time <1.15,+        Cabal-syntax >=3.8 && <3.15,+        Cabal >=3.8 && <3.15
src/Hackage/RevDeps.hs view
@@ -32,12 +32,14 @@ import System.FilePath (isPathSeparator)  -- | Scan Cabal index @01-index.tar@ and return Cabal files--- of latest releases (not necessarily largest versions), which+-- of latest releases / revisions (not necessarily largest versions), which -- contain one of the needles as an entire word (separated by spaces -- or punctuation). -- -- To avoid ambiguity: we first select the latest releases, -- then filter them by needles.+--+-- @since 0.1 latestReleases   :: [ByteString]   -- ^ Needles to search in Cabal files.@@ -51,16 +53,17 @@   -- ^ Map from latest releases to their Cabal files. latestReleases needles idx indexState =   M.filter (containsAnyAsWholeWord machine . decodeUtf8Lenient)-    <$> getLatestReleases idx indexState+    <$> allLatestReleases idx indexState   where     machine = Aho.build (map ((\x -> (x, x)) . decodeUtf8Lenient) needles) --- | Strip revisions and releases except the latest one.-getLatestReleases+-- | Scan Cabal index @01-index.tar@ and return Cabal files+-- of latest releases / revisions (not necessarily largest versions).+allLatestReleases   :: FilePath   -> Maybe UTCTime   -> IO (Map PackageName ByteString)-getLatestReleases idx indexState = foldCabalFilesInIndex idx indexState mempty M.insert+allLatestReleases idx indexState = foldCabalFilesInIndex idx indexState mempty M.insert  containsAnyAsWholeWord :: Aho.AcMachine Text -> Text -> Bool containsAnyAsWholeWord machine hay = Aho.runText False go machine hay@@ -123,6 +126,8 @@  -- | Scan Cabal file looking for package names, -- coalescing version bounds from all components and under all conditions.+--+-- @since 0.1 extractDependencies   :: [PackageName]   -- ^ Needles to search.