git.iko.soy/typeable/cabal2nix
on master help sign in
hackage2nix: read Cabal files from https://github.com/commercialhaskell/all-cabal-hashes/

The git repository contains all Cabal files as well as the respective SHA256 hashes for the source tarballs, which is awfully convenient for us because we no longer have to compute the hashes ourselves. This patch resolves https://github.com/NixOS/cabal2nix/issues/118

commitfdaff15d9d6647eb623f4fda71ba7a85a04b17f1 authorPeter Simons <simons@cryp.to> dateMon 10 Aug 2015, 15:09 (11 years ago) parente2a125b
5 files changed+114−9
src/Cabal2Nix/Generate.hs+1−1 src/Cabal2Nix/Hackage.hs+1−1 src/Cabal2Nix/HackageGit.hs+102 src/hackage2nix.hs+9−6 update-nixpkgs.sh+1−1
src/Cabal2Nix/Generate.hs+1−1
@@ -64,7 +64,7 @@ cabal2nix' PackageDescription {..} = normalize $ postProcess $
6464 & hyperlinkSource .~ True
6565 & enableSplitObjs .~ True
6666 & phaseOverrides .~ mempty
67− & editedCabalFile .~ (if xrev > 0 then fromJust (lookup "x-cabal-file-hash" customFieldsPD) else "")
67+ & editedCabalFile .~ (if xrev > 0 then fromJust (lookup "X-Cabal-File-Hash" customFieldsPD) else "")
6868 & metaSection .~ ( Nix.nullMeta
6969 & Nix.homepage .~ homepage
7070 & Nix.description .~ synopsis
src/Cabal2Nix/Hackage.hs+1−1
@@ -24,6 +24,6 @@ readHashedHackage = fmap parseUnparsedHackage Unparsed.readHackage
2424 hash = showDigest (sha256 buf)
2525 in
2626 pkg { packageDescription = (packageDescription pkg) {
27− customFieldsPD = ("x-cabal-file-hash", hash) : customFieldsPD (packageDescription pkg)
27+ customFieldsPD = ("X-Cabal-File-Hash", hash) : customFieldsPD (packageDescription pkg)
2828 }
2929 }
src/Cabal2Nix/HackageGit.hsnew file+102
@@ -0,0 +1,102 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+
3+module Cabal2Nix.HackageGit ( Hackage, readHackage, module Data.Map ) where
4+
5+import Data.ByteString.Char8 ( ByteString )
6+import qualified Data.ByteString.Char8 as BS8 ( readFile )
7+import Data.ByteString.Lazy.Char8 ( fromStrict )
8+import Data.Map
9+import Data.Aeson
10+import Data.Maybe ( fromMaybe )
11+import Data.Version
12+import Distribution.Text ( simpleParse )
13+import System.FilePath
14+import System.Directory
15+import Control.Monad
16+import Distribution.Package
17+import Distribution.PackageDescription
18+import Distribution.PackageDescription.Parse ( parsePackageDescription, ParseResult(..) )
19+import Distribution.Text ( display )
20+import Data.String.UTF8 ( toString, fromRep )
21+import Data.Digest.Pure.SHA ( sha256, showDigest )
22+
23+-- | A 'Map' representation of the Hackage database. Every package name
24+-- maps to a non-empty set of version, and for every version there is a
25+-- Cabal file stored as a (lazy) 'ByteString'.
26+
27+type Hackage = Map String (Map Version GenericPackageDescription)
28+
29+getSubDirs :: FilePath -> IO [FilePath]
30+getSubDirs path = do
31+ let isDirectory p = doesDirectoryExist (path </> p)
32+ getDirectoryContents path >>= filterM isDirectory . Prelude.filter (\x -> head x /= '.')
33+
34+-- | Read the Hackage database from the given 'FilePath' and return a
35+-- 'Hackage' map that provides fast access to its contents.
36+
37+readHackage :: FilePath -> IO Hackage
38+readHackage root = do
39+ pkglist <- getSubDirs root
40+ x <- mapM (readHackagePackage root) pkglist
41+ return (fromList x)
42+
43+readHackagePackage :: FilePath -> FilePath -> IO (FilePath, Map Version GenericPackageDescription)
44+readHackagePackage root pkg = do
45+ versions <- getSubDirs (root </> pkg)
46+ x <- mapM (readHackagePackageVersion root pkg) versions
47+ return (pkg, fromList x)
48+
49+readHackagePackageVersion :: FilePath -> FilePath -> FilePath -> IO (Version, GenericPackageDescription)
50+readHackagePackageVersion root pkg version = do
51+ let v = pVersion version
52+ buf <- BS8.readFile (root </> pkg </> version </> pkg <.> "cabal")
53+ let cabal = parsePackage pkg v buf
54+ meta <- parseMeta pkg v <$> BS8.readFile (root </> pkg </> version </> pkg <.> "json")
55+ let pd = packageDescription cabal
56+ cf = customFieldsPD pd ++ ("X-Cabal-File-Hash", mkSHA256 buf)
57+ : [ ("X-Package-" ++ k, v') | (k,v') <- toList (hashes meta) ]
58+ cabal' = cabal { packageDescription = pd { customFieldsPD = cf } }
59+ return (v, cabal')
60+
61+pVersion :: String -> Version
62+pVersion str = fromMaybe (error $ "hackage-db: cannot parse version " ++ show str) (simpleParse str)
63+
64+-- | Convenience wrapper around 'parsePackage'' to parse a single Cabal
65+-- file. Failure is reported with 'error'.
66+
67+parsePackage :: String -> Version -> ByteString -> GenericPackageDescription
68+parsePackage name version buf = case parsePackage' buf of
69+ Right a -> a
70+ Left err -> error $ "cannot parse cabal package " ++ show name ++ "-" ++ display version ++ ": " ++ err
71+
72+-- | Parse a single Cabal file.
73+
74+parsePackage' :: ByteString -> Either String GenericPackageDescription
75+parsePackage' buf = case parsePackageDescription (decodeUTF8 buf) of
76+ ParseOk _ a -> Right a
77+ ParseFailed err -> Left (show err)
78+ where
79+ decodeUTF8 :: ByteString -> String
80+ decodeUTF8 = toString . fromRep
81+
82+data Meta = Meta { hashes :: Map String String {-, locations :: [String], pkgsize :: Int-} }
83+ deriving Show
84+
85+instance FromJSON Meta where
86+ parseJSON (Object v) = Meta <$>
87+ v .: "package-hashes" {- <*>
88+ v .: "package-locations" <*>
89+ v .: "package-size" -}
90+ -- A non-Object value is of the wrong type, so fail.
91+ parseJSON _ = mzero
92+
93+parseMeta :: String -> Version -> ByteString -> Meta
94+parseMeta pkg version buf =
95+ case eitherDecodeStrict buf of
96+ Left msg -> error $ "hackage-db: cannot decode JSON meta data for " ++ display pkgid ++ ":" ++ msg
97+ Right x -> x
98+ where
99+ pkgid = PackageIdentifier (PackageName pkg) version
100+
101+mkSHA256 :: ByteString -> String
102+mkSHA256 = showDigest . sha256 . fromStrict
src/hackage2nix.hs+9−6
@@ -1,10 +1,8 @@
1−{-# LANGUAGE PatternGuards #-}
2−
31module Main ( main ) where
42
53import Cabal2Nix.Flags ( configureCabalFlags )
64import Cabal2Nix.Generate ( cabal2nix' )
7−import Cabal2Nix.Hackage ( readHashedHackage, Hackage )
5+import Cabal2Nix.HackageGit ( readHackage, Hackage )
86import Cabal2Nix.Package
97import Data.List
108import Control.Lens
@@ -81,7 +79,7 @@ data Configuration = Configuration
8179
8280main :: IO ()
8381main = do
84− hackage <- readHashedHackage
82+ hackage <- readHackage "hackage"
8583 nixpkgs <- readNixpkgPackageMap
8684 let fixup = Map.delete "acme-everything" -- TODO: https://github.com/NixOS/cabal2nix/issues/164
8785 . Map.delete "som" -- TODO: https://github.com/NixOS/cabal2nix/issues/164
@@ -138,7 +136,6 @@ generatePackageSet config hackage nixpkgs = do
138136 putStrLn ""
139137 pkgs <- flip parMapM (Map.toAscList db) $ \(name, vs) -> do
140138 defs <- forM (Set.toAscList vs) $ \pkgversion -> do
141− srcSpec <- liftIO $ sourceFromHackage UnknownHash (name ++ "-" ++ display pkgversion)
142139 let -- TODO: Include list of broken dependencies in the generated output.
143140 descr = hackage Map.! name Map.! pkgversion
144141 (missingDeps, _, drv') = cabal2nix resolver descr
@@ -178,7 +175,13 @@ generatePackageSet config hackage nixpkgs = do
178175 attr | Just v <- Map.lookup name generatedDefaultPackageSet, v == pkgversion = name
179176 | otherwise = name ++ '_' : [ if c == '.' then '_' else c | c <- display pkgversion ]
180177
181− drv = drv' & src .~ srcSpec
178+ sha256 :: String
179+ sha256 | Just x <- lookup "X-Package-SHA256" (customFieldsPD (packageDescription descr)) = x
180+ | otherwise = error $ display (packageId descr) ++ " has no hash"
181+
182+ srcSpec <- liftIO $ sourceFromHackage (Certain sha256) (name ++ "-" ++ display pkgversion)
183+
184+ let drv = drv' & src .~ srcSpec
182185 & jailbreak .~ (not (null missingDeps) && (name /= "jailbreak-cabal")) -- Dependency constraints aren't fulfilled
183186 & metaSection . broken .~ not (Set.null missing) -- Missing Haskell dependencies!
184187 & metaSection.hydraPlatforms .~ (if PackageName name `Set.member` brokenPackages config
update-nixpkgs.sh+1−1
@@ -41,6 +41,6 @@ cd nixpkgs
4141git pull -q
4242mv ../hackage-packages.nix pkgs/development/haskell-modules/hackage-packages.nix
4343if [ -n "$(git status --porcelain)" ]; then
44− git commit -q -m "hackage-packages.nix: update to https://github.com/commercialhaskell/all-cabal-files/commit/$hackage with hackage2nix revision $cabal2nix" -- pkgs/development/haskell-modules/hackage-packages.nix
44+ git commit -q -m "hackage-packages.nix: update to https://github.com/commercialhaskell/all-cabal-hashes/commit/$hackage with hackage2nix $cabal2nix" -- pkgs/development/haskell-modules/hackage-packages.nix
4545 git push -q
4646fi