packages feed

packdeps (empty) → 0.0.0

raw patch · 5 files changed

+274/−0 lines, 5 filesdep +Cabaldep +basedep +bytestringsetup-changed

Dependencies added: Cabal, base, bytestring, containers, directory, split, tar, text, time

Files

+ Distribution/PackDeps.hs view
@@ -0,0 +1,174 @@+module Distribution.PackDeps+    ( -- * Data types+      Newest+    , CheckDepsRes (..)+    , DescInfo+      -- * Read package database+    , loadNewest+    , loadNewestFrom+    , parseNewest+      -- * Check a package+    , checkDeps+      -- * Get a single package+    , getPackage+    , parsePackage+    , loadPackage+      -- * Get multiple packages+    , filterPackages+    ) where++import System.Directory (getAppUserDataDirectory)+import qualified Data.Map as Map+import Data.List (foldl', group, sort, isInfixOf)+import Data.Time (UTCTime (UTCTime), addUTCTime)+import Data.Maybe (mapMaybe)++import Distribution.Package+import Distribution.PackageDescription+import Distribution.PackageDescription.Parse+import Distribution.Version+import Distribution.Text++import Data.Char (toLower)+import qualified Data.Text.Lazy as T+import qualified Data.Text.Lazy.Encoding as T+import qualified Data.Text.Encoding.Error as T+import qualified Data.ByteString.Lazy as L++import Data.List.Split (splitOn)+import qualified Codec.Archive.Tar as Tar+import qualified Codec.Archive.Tar.Entry as Tar++loadNewest :: IO Newest+loadNewest = do+    c <- getAppUserDataDirectory "cabal"+    let fn = c ++ "/packages/hackage.haskell.org/00-index.tar"+    loadNewestFrom fn++loadNewestFrom :: FilePath -> IO Newest+loadNewestFrom = fmap parseNewest . L.readFile++parseNewest :: L.ByteString -> Newest+parseNewest = foldl' addPackage Map.empty . entriesToList . Tar.read++entriesToList :: Tar.Entries -> [Tar.Entry]+entriesToList Tar.Done = []+entriesToList (Tar.Fail s) = error s+entriesToList (Tar.Next e es) = e : entriesToList es++addPackage :: Newest -> Tar.Entry -> Newest+addPackage m entry =+    case splitOn "/" $ Tar.entryPath entry of+        [package', versionS, _] ->+            case simpleParse versionS of+                Just version ->+                    case Map.lookup package' m of+                        Nothing -> go package' version+                        Just PackInfo { piVersion = oldv } ->+                            if version > oldv+                                then go package' version+                                else m+                Nothing -> m+        _ -> m+  where+    go package' version =+        case Tar.entryContent entry of+            Tar.NormalFile bs _ ->+                 Map.insert package' PackInfo+                        { piVersion = version+                        , piDesc = parsePackage bs+                        , piEpoch = Tar.entryTime entry+                        } m+            _ -> m++data PackInfo = PackInfo+    { piVersion :: Version+    , piDesc :: Maybe DescInfo+    , piEpoch :: Tar.EpochTime+    }+    deriving (Show, Read)++-- | The newest version of every package.+type Newest = Map.Map String PackInfo++-- | Information on a single package.+data DescInfo = DescInfo+    { diHaystack :: String+    , diDeps :: [Dependency]+    , diPackage :: PackageIdentifier+    }+    deriving (Show, Read)++getDescInfo :: GenericPackageDescription -> DescInfo+getDescInfo gpd = DescInfo+    { diHaystack = map toLower $ author p ++ maintainer p ++ name+    , diDeps = getDeps gpd+    , diPackage = pi'+    }+  where+    p = packageDescription gpd+    pi'@(PackageIdentifier (PackageName name) _) = package p++getDeps :: GenericPackageDescription -> [Dependency]+getDeps x = concat+          $ maybe id ((:) . condTreeConstraints) (condLibrary x)+          $ map (condTreeConstraints . snd) (condExecutables x)++checkDeps :: Newest -> DescInfo+          -> (PackageName, Version, CheckDepsRes)+checkDeps newest desc =+    case mapMaybe (notNewest newest) $ diDeps desc of+        [] -> (name, version, AllNewest)+        x -> let y = map head $ group $ sort $ map fst x+                 et = maximum $ map snd x+              in (name, version, WontAccept y $ epochToTime et)+  where+    PackageIdentifier name version = diPackage desc++-- | Whether or not a package can accept all of the newest versions of its+-- dependencies. If not, it returns a list of packages which are not accepted,+-- and a timestamp of the most recently updated package.+data CheckDepsRes = AllNewest+                  | WontAccept [(String, String)] UTCTime+    deriving Show++epochToTime :: Tar.EpochTime -> UTCTime+epochToTime e = addUTCTime (fromIntegral e) $ UTCTime (read "1970-01-01") 0++notNewest :: Newest -> Dependency -> Maybe ((String, String), Tar.EpochTime)+notNewest newest (Dependency (PackageName s) range) =+    case Map.lookup s newest of+        Nothing -> Just ((s, " no version found"), 0)+        Just PackInfo { piVersion = version, piEpoch = e } ->+            if withinRange version range+                then Nothing+                else Just ((s, display version), e)++-- | Loads up the newest version of a package from the 'Newest' list, if+-- available.+getPackage :: String -> Newest -> Maybe DescInfo+getPackage s n = Map.lookup s n >>= piDesc++-- | Parse information on a package from the contents of a cabal file.+parsePackage :: L.ByteString -> Maybe DescInfo+parsePackage lbs =+    case parsePackageDescription $ T.unpack+       $ T.decodeUtf8With T.lenientDecode lbs of+        ParseOk _ x -> Just $ getDescInfo x+        _ -> Nothing++-- | Load a single package from a cabal file.+loadPackage :: FilePath -> IO (Maybe DescInfo)+loadPackage = fmap parsePackage . L.readFile++-- | Find all of the packages matching a given search string.+filterPackages :: String -> Newest -> [DescInfo]+filterPackages needle =+    mapMaybe go . Map.elems+  where+    needle' = map toLower needle+    go PackInfo { piDesc = Just desc } =+        if needle' `isInfixOf` diHaystack desc+            then Just desc+            else Nothing+    go _ = Nothing
+ LICENSE view
@@ -0,0 +1,25 @@+The following license covers this documentation, and the source code, except+where otherwise indicated.++Copyright 2010, Michael Snoyman. 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.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "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 HOLDERS 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.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ packdeps-cli.hs view
@@ -0,0 +1,36 @@+import Distribution.PackDeps+import System.Environment (getArgs)+import Distribution.Text (display)+import Distribution.Package (PackageName (PackageName))++main :: IO ()+main = do+    newest <- loadNewest+    getArgs >>= mapM_ (go newest)+  where+    go newest fp = do+        mdi <- loadPackage fp+        di <-+            case mdi of+                Just di -> return di+                Nothing -> error $ "Could not parse cabal file: " ++ fp+        case checkDeps newest di of+            (pn, v, AllNewest) ->+                putStrLn $ concat+                    [ unPackageName pn+                    , "-"+                    , display v+                    , ": Can use newest versions of all dependencies"+                    ]+            (pn, v, WontAccept p _) -> do+                putStrLn $ concat+                    [ unPackageName pn+                    , "-"+                    , display v+                    , ": Cannot accept the following packages"+                    ]+                flip mapM_ p $ \(x, y) -> putStrLn $ x ++ " " ++ y+        putStrLn ""++unPackageName :: PackageName -> String+unPackageName (PackageName n) = n
+ packdeps.cabal view
@@ -0,0 +1,37 @@+name:            packdeps+version:         0.0.0+license:         BSD3+license-file:    LICENSE+author:          Michael Snoyman <michael@snoyman.com>+maintainer:      Michael Snoyman <michael@snoyman.com>+synopsis:        Check your cabal packages for lagging dependencies.+description:+    This provides a library and command line tool for checking if the upper bounds in your package's dependency list excludes the newest package available. The code was originally available only as a web interface at <http://packdeps.haskellers.com/>, but is now available for standalone use as well.+    .+    The command line tool has an incredibly simple interface: simply pass it a list of cabal files, and it will tell you what dependencies- if any- are restricted.+category:        Distribution+stability:       Stable+cabal-version:   >= 1.6+build-type:      Simple+homepage:        http://packdeps.haskellers.com/++library+    build-depends:   base                      >= 4        && < 5+                   , tar                       >= 0.3.1    && < 0.4+                   , split                     >= 0.1.2.3  && < 0.2+                   , bytestring                >= 0.9      && < 0.10+                   , text                      >= 0.7      && < 0.12+                   , Cabal                     >= 1.8      && < 1.11+                   , time                      >= 1.1.4    && < 1.3+                   , containers                >= 0.2      && < 0.5+                   , directory                 >= 1.0      && < 1.2+    exposed-modules: Distribution.PackDeps+    ghc-options:     -Wall++executable             packdeps+    ghc-options:       -Wall+    main-is:           packdeps-cli.hs++source-repository head+  type:     git+  location: git://github.com/snoyberg/packdeps.git