git.iko.soy/typeable/cabal2nix
on master help sign in
Merge branch 'master' of https://github.com/NixOS/cabal2nix
commitac7d7b494de7ecd12810a893d6490d6e54baf244 authorTom Smalley <tfsmalley@gmail.com> dateSun 24 Sep 2017, 07:07 (9 years ago) parentd3a536b30558e1
6 files changed+103−77
cabal2nix.cabal+9−5 package.yaml+3−2 src/Cabal2nix.hs+4−2 src/Distribution/Nixpkgs/Haskell/FromCabal/Name.hs+7−9 src/Distribution/Nixpkgs/Haskell/Hackage.hs+38−27 src/Distribution/Nixpkgs/Haskell/PackageSourceSpec.hs+42−32
cabal2nix.cabal+9−5
@@ -3,7 +3,7 @@
33-- see: https://github.com/sol/hpack
44
55name: cabal2nix
6−version: 2.5
6+version: 2.6
77synopsis: Convert Cabal files into Nix build instructions.
88description: Convert Cabal files into Nix build instructions. Users of Nix can install the latest version by running:
99 .
@@ -99,7 +99,7 @@ library
9999 , directory
100100 , distribution-nixpkgs >= 1.1
101101 , filepath
102− , hackage-db
102+ , hackage-db > 2
103103 , hopenssl >= 2
104104 , hpack
105105 , language-nix
@@ -109,6 +109,7 @@ library
109109 , process
110110 , split
111111 , text
112+ , time
112113 , transformers
113114 , utf8-string
114115 , yaml
@@ -149,7 +150,7 @@ executable cabal2nix
149150 , directory
150151 , distribution-nixpkgs >= 1.1
151152 , filepath
152− , hackage-db
153+ , hackage-db > 2
153154 , hopenssl >= 2
154155 , hpack
155156 , language-nix
@@ -159,6 +160,7 @@ executable cabal2nix
159160 , process
160161 , split
161162 , text
163+ , time
162164 , transformers
163165 , utf8-string
164166 , yaml
@@ -183,7 +185,7 @@ executable hackage2nix
183185 , directory
184186 , distribution-nixpkgs >= 1.1
185187 , filepath
186− , hackage-db
188+ , hackage-db > 2
187189 , hopenssl >= 2
188190 , hpack
189191 , language-nix
@@ -193,6 +195,7 @@ executable hackage2nix
193195 , process
194196 , split
195197 , text
198+ , time
196199 , transformers
197200 , utf8-string
198201 , yaml
@@ -224,7 +227,7 @@ test-suite doctests
224227 , directory
225228 , distribution-nixpkgs >= 1.1
226229 , filepath
227− , hackage-db
230+ , hackage-db > 2
228231 , hopenssl >= 2
229232 , hpack
230233 , language-nix
@@ -234,6 +237,7 @@ test-suite doctests
234237 , process
235238 , split
236239 , text
240+ , time
237241 , transformers
238242 , utf8-string
239243 , yaml
package.yaml+3−2
@@ -1,5 +1,5 @@
11name: cabal2nix
2−version: "2.5"
2+version: "2.6"
33synopsis: Convert Cabal files into Nix build instructions.
44author:
55 - Peter Simons
@@ -90,7 +90,7 @@ dependencies:
9090 - directory
9191 - distribution-nixpkgs >= 1.1
9292 - filepath
93− - hackage-db
93+ - hackage-db > 2
9494 - hopenssl >= 2
9595 - hpack
9696 - language-nix
@@ -100,6 +100,7 @@ dependencies:
100100 - process
101101 - split
102102 - text
103+ - time
103104 - transformers
104105 - utf8-string
105106 - yaml
src/Cabal2nix.hs+4−2
@@ -156,7 +156,7 @@ cabal2nix' args = do
156156
157157 shell :: Doc
158158 shell = vcat
159− [ text "{ nixpkgs ? import <nixpkgs> {}, compiler ? \"default\" }:"
159+ [ text "{ nixpkgs ? import <nixpkgs> {}, compiler ? \"default\", doBenchmark ? false }:"
160160 , text ""
161161 , text "let"
162162 , text ""
@@ -168,7 +168,9 @@ cabal2nix' args = do
168168 , text " then pkgs.haskellPackages"
169169 , text " else pkgs.haskell.packages.${compiler};"
170170 , text ""
171− , text " drv = haskellPackages.callPackage f {};"
171+ , text " variant = if doBenchmark then pkgs.haskell.lib.doBenchmark else pkgs.lib.id;"
172+ , text ""
173+ , text " drv = variant (haskellPackages.callPackage f {});"
172174 , text ""
173175 , text "in"
174176 , text ""
src/Distribution/Nixpkgs/Haskell/FromCabal/Name.hs+7−9
@@ -81,6 +81,8 @@ libNixName "ImageMagick" = return "imagemagick"
8181libNixName "Imlib2" = return "imlib2"
8282libNixName "iw" = return "wirelesstools"
8383libNixName "jack" = return "libjack2"
84+libNixName "javascriptcoregtk-3.0" = return "webkitgtk24x-gtk3" -- These are the old APIs, of which 2.4 is the last provider, so map directly to that.
85+libNixName "javascriptcoregtk-4.0" = return "webkitgtk" -- This is the current API, so let it reference an alias for the latest version.
8486libNixName "jpeg" = return "libjpeg"
8587libNixName "jvm" = return "jdk"
8688libNixName "lapack" = return "liblapack"
@@ -132,25 +134,21 @@ libNixName "sndfile" = return "libsndfile"
132134libNixName "sodium" = return "libsodium"
133135libNixName "sqlite3" = return "sqlite"
134136libNixName "ssl" = return "openssl"
137+libNixName "statgrab" = return "libstatgrab"
135138libNixName "stdc++" = [] -- What is that?
136139libNixName "stdc++.dll" = [] -- What is that?
137140libNixName "systemd-journal" = return "systemd"
138−libNixName "taglib_c" = return "taglib"
139141libNixName "tag_c" = return "taglib"
142+libNixName "taglib_c" = return "taglib"
140143libNixName "udev" = return "systemd";
141144libNixName "uuid" = return "libossp_uuid";
142145libNixName "wayland-client" = return "wayland"
143146libNixName "wayland-cursor" = return "wayland"
144147libNixName "wayland-egl" = return "mesa"
145148libNixName "wayland-server" = return "wayland"
146−-- These are the old APIs, of which 2.4 is the last provider, so map directly to that
147−libNixName "javascriptcoregtk-3.0" = return "webkitgtk24x-gtk3"
148−libNixName "webkitgtk-3.0" = return "webkitgtk24x-gtk3"
149−-- This is the current API, so let it reference an alias for the latest version
150−libNixName "javascriptcoregtk-4.0" = return "webkitgtk"
151−libNixName "webkit2gtk-4.0" = return "webkitgtk"
152−-- This package doesn't actually exist in nixpkgs at present
153−libNixName "webkit2gtk-web-extension-4.0" = return "webkitgtk-web-extension"
149+libNixName "webkit2gtk-4.0" = return "webkitgtk" -- This is the current API, so let it reference an alias for the latest version.
150+libNixName "webkit2gtk-web-extension-4.0" = return "webkitgtk-web-extension" -- This package doesn't actually exist in nixpkgs at present.
151+libNixName "webkitgtk-3.0" = return "webkitgtk24x-gtk3" -- These are the old APIs, of which 2.4 is the last provider, so map directly to that
154152libNixName "X11" = return "libX11"
155153libNixName "xau" = return "libXau"
156154libNixName "Xcursor" = return "libXcursor"
src/Distribution/Nixpkgs/Haskell/Hackage.hs+38−27
@@ -1,33 +1,44 @@
1−module Distribution.Nixpkgs.Haskell.Hackage ( readHashedHackage, readHashedHackage', module Distribution.Hackage.DB ) where
1+module Distribution.Nixpkgs.Haskell.Hackage
2+ ( HackageDB, PackageData, VersionData(..)
3+ , hackageTarball, readTarball
4+ ) where
25
3−import Data.ByteString.Lazy ( ByteString )
4−import Distribution.Hackage.DB
5−import qualified Distribution.Hackage.DB.Parsed as Unparsed ( parsePackage )
6−import qualified Distribution.Hackage.DB.Unparsed as Unparsed
6+import Data.Map as Map
7+import qualified Distribution.Hackage.DB.Parsed as P
8+import Distribution.Hackage.DB.Path
9+import qualified Distribution.Hackage.DB.Unparsed as U
710import Distribution.Nixpkgs.Hashes
8−import OpenSSL.Digest ( digest, digestByName )
11+import Distribution.Package
12+import Distribution.PackageDescription
13+import Distribution.Version
14+import OpenSSL.Digest
15+import Data.Time
916
10−-- | A variant of 'readHackage' that adds the SHA256 digest of the
11−-- original Cabal file to the parsed 'GenericPackageDescription'. That
12−-- hash is required to build packages with an "edited" cabal file,
13−-- because Nix needs to download the edited file and patch it into the
14−-- original tarball.
17+type HackageDB = Map PackageName PackageData
1518
16−readHashedHackage :: IO Hackage
17−readHashedHackage = hackagePath >>= readHashedHackage'
19+type PackageData = Map Version VersionData
1820
19−readHashedHackage' :: FilePath -> IO Hackage
20−readHashedHackage' = fmap parseUnparsedHackage . Unparsed.readHackage'
21− where
22− parseUnparsedHackage :: Unparsed.Hackage -> Hackage
23− parseUnparsedHackage = mapWithKey (mapWithKey . parsePackage)
21+data VersionData = VersionData
22+ { cabalFile :: !GenericPackageDescription
23+ , cabalFileSha256 :: !String
24+ , tarballSha256 :: !(Maybe String)
25+ }
26+ deriving (Show)
27+
28+readTarball :: Maybe UTCTime -> FilePath -> IO HackageDB
29+readTarball ts p = do
30+ dbu <- U.readTarball ts p
31+ let dbp = P.parseDB dbu
32+ return (mapWithKey (parsePackageData dbu) dbp)
2433
25− parsePackage :: String -> Version -> ByteString -> GenericPackageDescription
26− parsePackage name version buf =
27− let pkg = Unparsed.parsePackage name version buf
28− hash = printSHA256 (digest (digestByName "sha256") buf)
29− in
30− pkg { packageDescription = (packageDescription pkg) {
31− customFieldsPD = ("X-Cabal-File-Hash", hash) : customFieldsPD (packageDescription pkg)
32− }
33− }
34+parsePackageData :: U.HackageDB -> PackageName -> P.PackageData -> PackageData
35+parsePackageData dbu pn pd = mapWithKey (parseVersionData (dbu ! pn)) pd
36+
37+parseVersionData :: U.PackageData -> Version -> P.VersionData -> VersionData
38+parseVersionData pdu v vd = VersionData
39+ { cabalFile = P.cabalFile vd
40+ , cabalFileSha256 = printSHA256 (digest (digestByName "sha256") file)
41+ , tarballSha256 = Map.lookup "sha256" (P.tarballHashes vd)
42+ }
43+ where
44+ file = U.cabalFile (U.versions pdu ! v)
src/Distribution/Nixpkgs/Haskell/PackageSourceSpec.hs+42−32
@@ -9,19 +9,20 @@ import Control.Monad.Trans.Maybe
99import qualified Data.ByteString.Lazy.Char8 as LBS8
1010import qualified Data.ByteString.Lazy.UTF8 as UTF8
1111import Data.List ( isSuffixOf, isPrefixOf )
12+import qualified Data.Map as DB
1213import Data.Maybe
13−import Distribution.Hackage.DB.Parsed
1414import Distribution.Nixpkgs.Fetch
1515import Distribution.Nixpkgs.Hashes
1616import qualified Distribution.Nixpkgs.Haskell.Hackage as DB
1717import qualified Distribution.Package as Cabal
18−import qualified Distribution.Version as Cabal
1918import Distribution.PackageDescription
2019import qualified Distribution.PackageDescription as Cabal
20+import Distribution.PackageDescription.Parse as Cabal
2121import Distribution.Text ( simpleParse, display )
22−import OpenSSL.Digest ( digest, digestByName )
22+import Distribution.Version
2323import qualified Hpack.Run as Hpack
2424import qualified Hpack.Config as Hpack
25+import OpenSSL.Digest ( digestString, digestByName )
2526import System.Directory ( doesDirectoryExist, doesFileExist, createDirectoryIfMissing, getHomeDirectory, getDirectoryContents )
2627import System.Exit ( exitFailure )
2728import System.FilePath ( (</>), (<.>) )
@@ -35,43 +36,49 @@ data Package = Package
3536 deriving (Show)
3637
3738getPackage :: Bool -- ^ Whether hpack should regenerate the cabal file
38− -> Maybe String -> Source -> IO Package
39+ -> Maybe FilePath -> Source -> IO Package
3940getPackage optHpack optHackageDB source = do
4041 (derivSource, ranHpack, pkgDesc) <- fetchOrFromDB optHpack optHackageDB source
4142 (\s -> Package s ranHpack pkgDesc) <$> maybe (sourceFromHackage (sourceHash source) (showPackageIdentifier pkgDesc) $ sourceCabalDir source) return derivSource
4243
4344fetchOrFromDB :: Bool -- ^ Whether hpack should regenerate the cabal file
44− -> Maybe String -> Source -> IO (Maybe DerivationSource, Bool, Cabal.GenericPackageDescription)
45+ -> Maybe FilePath -> Source -> IO (Maybe DerivationSource, Bool, Cabal.GenericPackageDescription)
4546fetchOrFromDB optHpack optHackageDB src
46− | "cabal://" `isPrefixOf` sourceUrl src = fmap ((,,) Nothing False) . fromDB optHackageDB . drop (length "cabal://") $ sourceUrl src
47+ | "cabal://" `isPrefixOf` sourceUrl src = fromDB optHackageDB . drop (length "cabal://") $ sourceUrl src
4748 | otherwise = do
4849 r <- fetch (\dir -> cabalFromPath optHpack (dir </> sourceCabalDir src)) src
4950 case r of
50− Nothing ->
51− hPutStrLn stderr "*** failed to fetch source. Does the URL exist?" >> exitFailure
51+ Nothing -> fail "Failed to fetch source. Does the URL exist?"
5252 Just (derivSource, (externalSource, ranHpack, pkgDesc)) -> do
5353 return (derivSource <$ guard externalSource, ranHpack, pkgDesc)
5454
55−fromDB :: Maybe String -> String -> IO Cabal.GenericPackageDescription
55+fromDB :: Maybe FilePath -> String -> IO (Maybe DerivationSource, Cabal.GenericPackageDescription)
5656fromDB optHackageDB pkg = do
57− pkgDesc <- (lookupVersion <=< DB.lookup name) <$> maybe DB.readHashedHackage DB.readHashedHackage' optHackageDB
58− case pkgDesc of
59− Just r -> return r
60− Nothing -> hPutStrLn stderr "*** no such package in the cabal database (did you run cabal update?). " >> exitFailure
57+ dbPath <- maybe DB.hackageTarball return optHackageDB
58+ db <- DB.readTarball Nothing dbPath
59+ vd <- maybe unknownPackageError return (DB.lookup name db >>= lookupVersion)
60+ let ds = case DB.tarballSha256 vd of
61+ Nothing -> Nothing
62+ Just hash -> Just (DerivationSource "url" url "" hash)
63+ return (ds, setCabalFileHash (DB.cabalFileSha256 vd) (DB.cabalFile vd))
6164 where
6265 pkgId :: Cabal.PackageIdentifier
6366 pkgId = fromMaybe (error ("invalid Haskell package id " ++ show pkg)) (simpleParse pkg)
64− name = Cabal.unPackageName (Cabal.packageName pkgId)
67+ name = Cabal.packageName pkgId
68+
69+ unknownPackageError = fail "No such package in the cabal database. Did you run cabal update?"
6570
66− version :: [Int]
67− version = Cabal.versionNumbers $ Cabal.packageVersion pkgId
71+ url = "mirror://hackage/" ++ display pkgId ++ ".tar.gz"
72+
73+ version :: Version
74+ version = Cabal.packageVersion pkgId
6875
69− lookupVersion :: DB.Map DB.Version Cabal.GenericPackageDescription -> Maybe Cabal.GenericPackageDescription
76+ lookupVersion :: DB.Map Version DB.VersionData -> Maybe DB.VersionData
7077 -- No version is specified, pick latest one
71− lookupVersion m | [] <- version = fmap snd . listToMaybe $ DB.toDescList m
72− lookupVersion m = DB.lookup (DB.makeVersion version) m
78+ lookupVersion m | version == nullVersion = fmap snd (listToMaybe (DB.toDescList m))
79+ lookupVersion m = DB.lookup version m
7380
74−readFileMay :: String -> IO (Maybe String)
81+readFileMay :: FilePath -> IO (Maybe String)
7582readFileMay file = do
7683 e <- doesFileExist file
7784 if e
@@ -85,7 +92,7 @@ hashCachePath pid = do
8592 createDirectoryIfMissing True cacheDir
8693 return $ cacheDir </> pid <.> "sha256"
8794
88−sourceFromHackage :: Hash -> String -> String -> IO DerivationSource
95+sourceFromHackage :: Hash -> String -> FilePath -> IO DerivationSource
8996sourceFromHackage optHash pkgId cabalDir = do
9097 cacheFile <- hashCachePath pkgId
9198 cachedHash <-
@@ -149,7 +156,7 @@ cabalFromDirectory False dir = do
149156 liftIO $ hPutStrLn stderr "*** found zero cabal files. Trying hpack..."
150157 hpackDirectory dir
151158 [cabalFile] -> (,) False <$> cabalFromFile True cabalFile
152− _ -> liftIO $ hPutStrLn stderr ("*** found more than one cabal file (" ++ show cabals ++ "). Exiting.") >> exitFailure
159+ _ -> liftIO $ fail ("*** found more than one cabal file (" ++ show cabals ++ "). Exiting.")
153160
154161handleIO :: (Exception.IOException -> IO a) -> IO a -> IO a
155162handleIO = Exception.handle
@@ -181,13 +188,16 @@ cabalFromFile failHard file =
181188 -- wrap the whole block in `catchIO`, because of lazy IO. The `case` will force
182189 -- the reading of the file, so we will always catch the expression here.
183190 MaybeT $ handleIO (\err -> Nothing <$ hPutStrLn stderr ("*** parsing cabal file: " ++ show err)) $ do
184− buf <- LBS8.readFile file
185− let hash = printSHA256 (digest (digestByName "sha256") buf)
186− case parsePackage' buf of
187− Left msg -> if failHard
188− then fail ("*** cannot parse " ++ show file ++ ": " ++ msg)
189− else return Nothing
190− Right pkg -> do return $ Just $ pkg { packageDescription = (packageDescription pkg) {
191− customFieldsPD = ("X-Cabal-File-Hash", hash) : customFieldsPD (packageDescription pkg)
192− }
193− }
191+ buf <- readFile file
192+ let hash = printSHA256 (digestString (digestByName "sha256") buf)
193+ case parseGenericPackageDescription buf of
194+ ParseFailed perr -> if failHard
195+ then fail ("cannot parse " ++ show file ++ ": " ++ show perr)
196+ else return Nothing
197+ ParseOk _ pkg -> return $ Just $ setCabalFileHash hash pkg
198+
199+setCabalFileHash :: String -> GenericPackageDescription -> GenericPackageDescription
200+setCabalFileHash sha256 gpd = gpd { packageDescription = (packageDescription gpd) {
201+ customFieldsPD = ("X-Cabal-File-Hash", sha256) : customFieldsPD (packageDescription gpd)
202+ }
203+ }