git.iko.soy/typeable/cabal2nix
on master help sign in
Merge branch 'master' of https://github.com/NixOS/cabal2nix
ac7d7b4Tom Smalley
cabal2nix.cabal+9−5
@@ -3,7 +3,7 @@
3-- see: https://github.com/sol/hpack
3-- see: https://github.com/sol/hpack
4
4
5name: cabal2nix
5name: cabal2nix
6−version: 2.5
6+version: 2.6
7synopsis: Convert Cabal files into Nix build instructions.
7synopsis: Convert Cabal files into Nix build instructions.
8description: Convert Cabal files into Nix build instructions. Users of Nix can install the latest version by running:
8description: Convert Cabal files into Nix build instructions. Users of Nix can install the latest version by running:
9 .
9 .
@@ -99,7 +99,7 @@ library
99 , directory
99 , directory
100 , distribution-nixpkgs >= 1.1
100 , distribution-nixpkgs >= 1.1
101 , filepath
101 , filepath
102− , hackage-db
102+ , hackage-db > 2
103 , hopenssl >= 2
103 , hopenssl >= 2
104 , hpack
104 , hpack
105 , language-nix
105 , language-nix
@@ -109,6 +109,7 @@ library
109 , process
109 , process
110 , split
110 , split
111 , text
111 , text
112+ , time
112 , transformers
113 , transformers
113 , utf8-string
114 , utf8-string
114 , yaml
115 , yaml
@@ -149,7 +150,7 @@ executable cabal2nix
149 , directory
150 , directory
150 , distribution-nixpkgs >= 1.1
151 , distribution-nixpkgs >= 1.1
151 , filepath
152 , filepath
152− , hackage-db
153+ , hackage-db > 2
153 , hopenssl >= 2
154 , hopenssl >= 2
154 , hpack
155 , hpack
155 , language-nix
156 , language-nix
@@ -159,6 +160,7 @@ executable cabal2nix
159 , process
160 , process
160 , split
161 , split
161 , text
162 , text
163+ , time
162 , transformers
164 , transformers
163 , utf8-string
165 , utf8-string
164 , yaml
166 , yaml
@@ -183,7 +185,7 @@ executable hackage2nix
183 , directory
185 , directory
184 , distribution-nixpkgs >= 1.1
186 , distribution-nixpkgs >= 1.1
185 , filepath
187 , filepath
186− , hackage-db
188+ , hackage-db > 2
187 , hopenssl >= 2
189 , hopenssl >= 2
188 , hpack
190 , hpack
189 , language-nix
191 , language-nix
@@ -193,6 +195,7 @@ executable hackage2nix
193 , process
195 , process
194 , split
196 , split
195 , text
197 , text
198+ , time
196 , transformers
199 , transformers
197 , utf8-string
200 , utf8-string
198 , yaml
201 , yaml
@@ -224,7 +227,7 @@ test-suite doctests
224 , directory
227 , directory
225 , distribution-nixpkgs >= 1.1
228 , distribution-nixpkgs >= 1.1
226 , filepath
229 , filepath
227− , hackage-db
230+ , hackage-db > 2
228 , hopenssl >= 2
231 , hopenssl >= 2
229 , hpack
232 , hpack
230 , language-nix
233 , language-nix
@@ -234,6 +237,7 @@ test-suite doctests
234 , process
237 , process
235 , split
238 , split
236 , text
239 , text
240+ , time
237 , transformers
241 , transformers
238 , utf8-string
242 , utf8-string
239 , yaml
243 , yaml
package.yaml+3−2
@@ -1,5 +1,5 @@
1name: cabal2nix
1name: cabal2nix
2−version: "2.5"
2+version: "2.6"
3synopsis: Convert Cabal files into Nix build instructions.
3synopsis: Convert Cabal files into Nix build instructions.
4author:
4author:
5 - Peter Simons
5 - Peter Simons
@@ -90,7 +90,7 @@ dependencies:
90 - directory
90 - directory
91 - distribution-nixpkgs >= 1.1
91 - distribution-nixpkgs >= 1.1
92 - filepath
92 - filepath
93− - hackage-db
93+ - hackage-db > 2
94 - hopenssl >= 2
94 - hopenssl >= 2
95 - hpack
95 - hpack
96 - language-nix
96 - language-nix
@@ -100,6 +100,7 @@ dependencies:
100 - process
100 - process
101 - split
101 - split
102 - text
102 - text
103+ - time
103 - transformers
104 - transformers
104 - utf8-string
105 - utf8-string
105 - yaml
106 - yaml
src/Cabal2nix.hs+4−2
@@ -156,7 +156,7 @@ cabal2nix' args = do
156
156
157 shell :: Doc
157 shell :: Doc
158 shell = vcat
158 shell = vcat
159− [ text "{ nixpkgs ? import <nixpkgs> {}, compiler ? \"default\" }:"
159+ [ text "{ nixpkgs ? import <nixpkgs> {}, compiler ? \"default\", doBenchmark ? false }:"
160 , text ""
160 , text ""
161 , text "let"
161 , text "let"
162 , text ""
162 , text ""
@@ -168,7 +168,9 @@ cabal2nix' args = do
168 , text " then pkgs.haskellPackages"
168 , text " then pkgs.haskellPackages"
169 , text " else pkgs.haskell.packages.${compiler};"
169 , text " else pkgs.haskell.packages.${compiler};"
170 , text ""
170 , text ""
171+ , text " variant = if doBenchmark then pkgs.haskell.lib.doBenchmark else pkgs.lib.id;"
172+ , text ""
171− , text " drv = haskellPackages.callPackage f {};"
173+ , text " drv = variant (haskellPackages.callPackage f {});"
172 , text ""
174 , text ""
173 , text "in"
175 , text "in"
174 , text ""
176 , text ""
src/Distribution/Nixpkgs/Haskell/FromCabal/Name.hs+7−9
@@ -81,6 +81,8 @@ libNixName "ImageMagick" = return "imagemagick"
81libNixName "Imlib2" = return "imlib2"
81libNixName "Imlib2" = return "imlib2"
82libNixName "iw" = return "wirelesstools"
82libNixName "iw" = return "wirelesstools"
83libNixName "jack" = return "libjack2"
83libNixName "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.
84libNixName "jpeg" = return "libjpeg"
86libNixName "jpeg" = return "libjpeg"
85libNixName "jvm" = return "jdk"
87libNixName "jvm" = return "jdk"
86libNixName "lapack" = return "liblapack"
88libNixName "lapack" = return "liblapack"
@@ -132,25 +134,21 @@ libNixName "sndfile" = return "libsndfile"
132libNixName "sodium" = return "libsodium"
134libNixName "sodium" = return "libsodium"
133libNixName "sqlite3" = return "sqlite"
135libNixName "sqlite3" = return "sqlite"
134libNixName "ssl" = return "openssl"
136libNixName "ssl" = return "openssl"
137+libNixName "statgrab" = return "libstatgrab"
135libNixName "stdc++" = [] -- What is that?
138libNixName "stdc++" = [] -- What is that?
136libNixName "stdc++.dll" = [] -- What is that?
139libNixName "stdc++.dll" = [] -- What is that?
137libNixName "systemd-journal" = return "systemd"
140libNixName "systemd-journal" = return "systemd"
138−libNixName "taglib_c" = return "taglib"
139libNixName "tag_c" = return "taglib"
141libNixName "tag_c" = return "taglib"
142+libNixName "taglib_c" = return "taglib"
140libNixName "udev" = return "systemd";
143libNixName "udev" = return "systemd";
141libNixName "uuid" = return "libossp_uuid";
144libNixName "uuid" = return "libossp_uuid";
142libNixName "wayland-client" = return "wayland"
145libNixName "wayland-client" = return "wayland"
143libNixName "wayland-cursor" = return "wayland"
146libNixName "wayland-cursor" = return "wayland"
144libNixName "wayland-egl" = return "mesa"
147libNixName "wayland-egl" = return "mesa"
145libNixName "wayland-server" = return "wayland"
148libNixName "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
149+libNixName "webkit2gtk-4.0" = return "webkitgtk" -- 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
150+libNixName "webkit2gtk-web-extension-4.0" = return "webkitgtk-web-extension" -- This package doesn't actually exist in nixpkgs at present.
153−libNixName "webkit2gtk-web-extension-4.0" = return "webkitgtk-web-extension"
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
154libNixName "X11" = return "libX11"
152libNixName "X11" = return "libX11"
155libNixName "xau" = return "libXau"
153libNixName "xau" = return "libXau"
156libNixName "Xcursor" = return "libXcursor"
154libNixName "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
2
5
3−import Data.ByteString.Lazy ( ByteString )
6+import Data.Map as Map
4−import Distribution.Hackage.DB
5−import qualified Distribution.Hackage.DB.Parsed as Unparsed ( parsePackage )
7+import qualified Distribution.Hackage.DB.Parsed as P
8+import Distribution.Hackage.DB.Path
6−import qualified Distribution.Hackage.DB.Unparsed as Unparsed
9+import qualified Distribution.Hackage.DB.Unparsed as U
7import Distribution.Nixpkgs.Hashes
10import Distribution.Nixpkgs.Hashes
11+import Distribution.Package
12+import Distribution.PackageDescription
13+import Distribution.Version
8−import OpenSSL.Digest ( digest, digestByName )
14+import OpenSSL.Digest
15+import Data.Time
9
16
10−-- | A variant of 'readHackage' that adds the SHA256 digest of the
17+type HackageDB = Map PackageName PackageData
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.
15
18
16−readHashedHackage :: IO Hackage
19+type PackageData = Map Version VersionData
17−readHashedHackage = hackagePath >>= readHashedHackage'
18
20
19−readHashedHackage' :: FilePath -> IO Hackage
21+data VersionData = VersionData
20−readHashedHackage' = fmap parseUnparsedHackage . Unparsed.readHackage'
22+ { cabalFile :: !GenericPackageDescription
21− where
23+ , cabalFileSha256 :: !String
22− parseUnparsedHackage :: Unparsed.Hackage -> Hackage
24+ , tarballSha256 :: !(Maybe String)
23− parseUnparsedHackage = mapWithKey (mapWithKey . parsePackage)
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)
24
33
25− parsePackage :: String -> Version -> ByteString -> GenericPackageDescription
34+parsePackageData :: U.HackageDB -> PackageName -> P.PackageData -> PackageData
26− parsePackage name version buf =
35+parsePackageData dbu pn pd = mapWithKey (parseVersionData (dbu ! pn)) pd
27− let pkg = Unparsed.parsePackage name version buf
36+
37+parseVersionData :: U.PackageData -> Version -> P.VersionData -> VersionData
38+parseVersionData pdu v vd = VersionData
39+ { cabalFile = P.cabalFile vd
28− hash = printSHA256 (digest (digestByName "sha256") buf)
40+ , cabalFileSha256 = printSHA256 (digest (digestByName "sha256") file)
29− in
41+ , tarballSha256 = Map.lookup "sha256" (P.tarballHashes vd)
30− pkg { packageDescription = (packageDescription pkg) {
42+ }
31− customFieldsPD = ("X-Cabal-File-Hash", hash) : customFieldsPD (packageDescription pkg)
43+ where
32− }
44+ file = U.cabalFile (U.versions pdu ! v)
33− }
src/Distribution/Nixpkgs/Haskell/PackageSourceSpec.hs+42−32
@@ -9,19 +9,20 @@ import Control.Monad.Trans.Maybe
9import qualified Data.ByteString.Lazy.Char8 as LBS8
9import qualified Data.ByteString.Lazy.Char8 as LBS8
10import qualified Data.ByteString.Lazy.UTF8 as UTF8
10import qualified Data.ByteString.Lazy.UTF8 as UTF8
11import Data.List ( isSuffixOf, isPrefixOf )
11import Data.List ( isSuffixOf, isPrefixOf )
12+import qualified Data.Map as DB
12import Data.Maybe
13import Data.Maybe
13−import Distribution.Hackage.DB.Parsed
14import Distribution.Nixpkgs.Fetch
14import Distribution.Nixpkgs.Fetch
15import Distribution.Nixpkgs.Hashes
15import Distribution.Nixpkgs.Hashes
16import qualified Distribution.Nixpkgs.Haskell.Hackage as DB
16import qualified Distribution.Nixpkgs.Haskell.Hackage as DB
17import qualified Distribution.Package as Cabal
17import qualified Distribution.Package as Cabal
18−import qualified Distribution.Version as Cabal
19import Distribution.PackageDescription
18import Distribution.PackageDescription
20import qualified Distribution.PackageDescription as Cabal
19import qualified Distribution.PackageDescription as Cabal
20+import Distribution.PackageDescription.Parse as Cabal
21import Distribution.Text ( simpleParse, display )
21import Distribution.Text ( simpleParse, display )
22−import OpenSSL.Digest ( digest, digestByName )
22+import Distribution.Version
23import qualified Hpack.Run as Hpack
23import qualified Hpack.Run as Hpack
24import qualified Hpack.Config as Hpack
24import qualified Hpack.Config as Hpack
25+import OpenSSL.Digest ( digestString, digestByName )
25import System.Directory ( doesDirectoryExist, doesFileExist, createDirectoryIfMissing, getHomeDirectory, getDirectoryContents )
26import System.Directory ( doesDirectoryExist, doesFileExist, createDirectoryIfMissing, getHomeDirectory, getDirectoryContents )
26import System.Exit ( exitFailure )
27import System.Exit ( exitFailure )
27import System.FilePath ( (</>), (<.>) )
28import System.FilePath ( (</>), (<.>) )
@@ -35,43 +36,49 @@ data Package = Package
35 deriving (Show)
36 deriving (Show)
36
37
37getPackage :: Bool -- ^ Whether hpack should regenerate the cabal file
38getPackage :: Bool -- ^ Whether hpack should regenerate the cabal file
38− -> Maybe String -> Source -> IO Package
39+ -> Maybe FilePath -> Source -> IO Package
39getPackage optHpack optHackageDB source = do
40getPackage optHpack optHackageDB source = do
40 (derivSource, ranHpack, pkgDesc) <- fetchOrFromDB optHpack optHackageDB source
41 (derivSource, ranHpack, pkgDesc) <- fetchOrFromDB optHpack optHackageDB source
41 (\s -> Package s ranHpack pkgDesc) <$> maybe (sourceFromHackage (sourceHash source) (showPackageIdentifier pkgDesc) $ sourceCabalDir source) return derivSource
42 (\s -> Package s ranHpack pkgDesc) <$> maybe (sourceFromHackage (sourceHash source) (showPackageIdentifier pkgDesc) $ sourceCabalDir source) return derivSource
42
43
43fetchOrFromDB :: Bool -- ^ Whether hpack should regenerate the cabal file
44fetchOrFromDB :: 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)
45fetchOrFromDB optHpack optHackageDB src
46fetchOrFromDB 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
47 | otherwise = do
48 | otherwise = do
48 r <- fetch (\dir -> cabalFromPath optHpack (dir </> sourceCabalDir src)) src
49 r <- fetch (\dir -> cabalFromPath optHpack (dir </> sourceCabalDir src)) src
49 case r of
50 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?"
52 Just (derivSource, (externalSource, ranHpack, pkgDesc)) -> do
52 Just (derivSource, (externalSource, ranHpack, pkgDesc)) -> do
53 return (derivSource <$ guard externalSource, ranHpack, pkgDesc)
53 return (derivSource <$ guard externalSource, ranHpack, pkgDesc)
54
54
55−fromDB :: Maybe String -> String -> IO Cabal.GenericPackageDescription
55+fromDB :: Maybe FilePath -> String -> IO (Maybe DerivationSource, Cabal.GenericPackageDescription)
56fromDB optHackageDB pkg = do
56fromDB optHackageDB pkg = do
57− pkgDesc <- (lookupVersion <=< DB.lookup name) <$> maybe DB.readHashedHackage DB.readHashedHackage' optHackageDB
57+ dbPath <- maybe DB.hackageTarball return optHackageDB
58− case pkgDesc of
58+ db <- DB.readTarball Nothing dbPath
59− Just r -> return r
59+ vd <- maybe unknownPackageError return (DB.lookup name db >>= lookupVersion)
60− Nothing -> hPutStrLn stderr "*** no such package in the cabal database (did you run cabal update?). " >> exitFailure
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))
61 where
64 where
62 pkgId :: Cabal.PackageIdentifier
65 pkgId :: Cabal.PackageIdentifier
63 pkgId = fromMaybe (error ("invalid Haskell package id " ++ show pkg)) (simpleParse pkg)
66 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?"
65
70
71+ url = "mirror://hackage/" ++ display pkgId ++ ".tar.gz"
72+
66− version :: [Int]
73+ version :: Version
67− version = Cabal.versionNumbers $ Cabal.packageVersion pkgId
74+ version = Cabal.packageVersion pkgId
68
75
69− lookupVersion :: DB.Map DB.Version Cabal.GenericPackageDescription -> Maybe Cabal.GenericPackageDescription
76+ lookupVersion :: DB.Map Version DB.VersionData -> Maybe DB.VersionData
70 -- No version is specified, pick latest one
77 -- No version is specified, pick latest one
71− lookupVersion m | [] <- version = fmap snd . listToMaybe $ DB.toDescList m
78+ lookupVersion m | version == nullVersion = fmap snd (listToMaybe (DB.toDescList m))
72− lookupVersion m = DB.lookup (DB.makeVersion version) m
79+ lookupVersion m = DB.lookup version m
73
80
74−readFileMay :: String -> IO (Maybe String)
81+readFileMay :: FilePath -> IO (Maybe String)
75readFileMay file = do
82readFileMay file = do
76 e <- doesFileExist file
83 e <- doesFileExist file
77 if e
84 if e
@@ -85,7 +92,7 @@ hashCachePath pid = do
85 createDirectoryIfMissing True cacheDir
92 createDirectoryIfMissing True cacheDir
86 return $ cacheDir </> pid <.> "sha256"
93 return $ cacheDir </> pid <.> "sha256"
87
94
88−sourceFromHackage :: Hash -> String -> String -> IO DerivationSource
95+sourceFromHackage :: Hash -> String -> FilePath -> IO DerivationSource
89sourceFromHackage optHash pkgId cabalDir = do
96sourceFromHackage optHash pkgId cabalDir = do
90 cacheFile <- hashCachePath pkgId
97 cacheFile <- hashCachePath pkgId
91 cachedHash <-
98 cachedHash <-
@@ -149,7 +156,7 @@ cabalFromDirectory False dir = do
149 liftIO $ hPutStrLn stderr "*** found zero cabal files. Trying hpack..."
156 liftIO $ hPutStrLn stderr "*** found zero cabal files. Trying hpack..."
150 hpackDirectory dir
157 hpackDirectory dir
151 [cabalFile] -> (,) False <$> cabalFromFile True cabalFile
158 [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.")
153
160
154handleIO :: (Exception.IOException -> IO a) -> IO a -> IO a
161handleIO :: (Exception.IOException -> IO a) -> IO a -> IO a
155handleIO = Exception.handle
162handleIO = Exception.handle
@@ -181,13 +188,16 @@ cabalFromFile failHard file =
181 -- wrap the whole block in `catchIO`, because of lazy IO. The `case` will force
188 -- wrap the whole block in `catchIO`, because of lazy IO. The `case` will force
182 -- the reading of the file, so we will always catch the expression here.
189 -- the reading of the file, so we will always catch the expression here.
183 MaybeT $ handleIO (\err -> Nothing <$ hPutStrLn stderr ("*** parsing cabal file: " ++ show err)) $ do
190 MaybeT $ handleIO (\err -> Nothing <$ hPutStrLn stderr ("*** parsing cabal file: " ++ show err)) $ do
184− buf <- LBS8.readFile file
191+ buf <- readFile file
185− let hash = printSHA256 (digest (digestByName "sha256") buf)
192+ let hash = printSHA256 (digestString (digestByName "sha256") buf)
186− case parsePackage' buf of
193+ case parseGenericPackageDescription buf of
187− Left msg -> if failHard
194+ ParseFailed perr -> if failHard
188− then fail ("*** cannot parse " ++ show file ++ ": " ++ msg)
195+ then fail ("cannot parse " ++ show file ++ ": " ++ show perr)
189− else return Nothing
196+ else return Nothing
197+ ParseOk _ pkg -> return $ Just $ setCabalFileHash hash pkg
198+
199+setCabalFileHash :: String -> GenericPackageDescription -> GenericPackageDescription
190− Right pkg -> do return $ Just $ pkg { packageDescription = (packageDescription pkg) {
200+setCabalFileHash sha256 gpd = gpd { packageDescription = (packageDescription gpd) {
191− customFieldsPD = ("X-Cabal-File-Hash", hash) : customFieldsPD (packageDescription pkg)
201+ customFieldsPD = ("X-Cabal-File-Hash", sha256) : customFieldsPD (packageDescription gpd)
192− }
202+ }
193− }
203+ }