@@ -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)
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
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
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)
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
62+ Just hash -> Just (DerivationSource "url" url "" hash)
63+ return (ds, setCabalFileHash (DB.cabalFileSha256 vd) (DB.cabalFile vd))
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
69+ unknownPackageError = fail "No such package in the cabal database. Did you run cabal update?"
71+ url = "mirror://hackage/" ++ display pkgId ++ ".tar.gz"
66− version :: [Int]
73+ version :: Version
67− version = Cabal.versionNumbers $ Cabal.packageVersion pkgId
74+ version = Cabal.packageVersion pkgId
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
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
@@ -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"
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.")
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
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)