@@ -18,6 +18,8 @@ import qualified Distribution.PackageDescription as Cabal
18import Distribution.PackageDescription.Parse as Cabal
18import Distribution.PackageDescription.Parse as Cabal
19import Distribution.Text ( simpleParse, display )
19import Distribution.Text ( simpleParse, display )
20import Distribution.Version
20import Distribution.Version
21+import qualified Hpack.Run as Hpack
22+import qualified Hpack.Config as Hpack
21import OpenSSL.Digest ( digestString, digestByName )
23import OpenSSL.Digest ( digestString, digestByName )
22import System.Directory ( doesDirectoryExist, doesFileExist, createDirectoryIfMissing, getHomeDirectory, getDirectoryContents )
24import System.Directory ( doesDirectoryExist, doesFileExist, createDirectoryIfMissing, getHomeDirectory, getDirectoryContents )
23import System.Exit ( exitFailure )
25import System.Exit ( exitFailure )
@@ -25,25 +27,30 @@ import System.FilePath ( (</>), (<.>) )
25import System.IO ( hPutStrLn, stderr, hPutStr )
27import System.IO ( hPutStrLn, stderr, hPutStr )
27data Package = Package
29data Package = Package
28− { pkgSource :: DerivationSource
30+ { pkgSource :: DerivationSource
31+ , pkgRanHpack :: Bool -- ^ If hpack generated a new cabal file
29− , pkgCabal :: Cabal.GenericPackageDescription
32+ , pkgCabal :: Cabal.GenericPackageDescription
31 deriving (Show)
34 deriving (Show)
36+getPackage :: Bool -- ^ Whether hpack should regenerate the cabal file
33−getPackage :: Maybe FilePath -> Source -> IO Package
37+ -> Maybe FilePath -> Source -> IO Package
34−getPackage optHackageDB source = do
38+getPackage optHpack optHackageDB source = do
35− (derivSource, pkgDesc) <- fetchOrFromDB optHackageDB source
39+ (derivSource, ranHpack, pkgDesc) <- fetchOrFromDB optHpack optHackageDB source
36− flip Package pkgDesc <$> maybe (sourceFromHackage (sourceHash source) (showPackageIdentifier pkgDesc) $ sourceCabalDir source) return derivSource
40+ (\s -> Package s ranHpack pkgDesc) <$> maybe (sourceFromHackage (sourceHash source) (showPackageIdentifier pkgDesc) $ sourceCabalDir source) return derivSource
42+fetchOrFromDB :: Bool -- ^ Whether hpack should regenerate the cabal file
38−fetchOrFromDB :: Maybe FilePath -> Source -> IO (Maybe DerivationSource, Cabal.GenericPackageDescription)
43+ -> Maybe FilePath -> Source -> IO (Maybe DerivationSource, Bool, Cabal.GenericPackageDescription)
39−fetchOrFromDB optHackageDB src
44+fetchOrFromDB optHpack optHackageDB src
45+ | "cabal://" `isPrefixOf` sourceUrl src = do
40− | "cabal://" `isPrefixOf` sourceUrl src = fromDB optHackageDB . drop (length "cabal://") $ sourceUrl src
46+ (msrc, pkgDesc) <- fromDB optHackageDB . drop (length "cabal://") $ sourceUrl src
47+ return (msrc, False, pkgDesc)
41 | otherwise = do
48 | otherwise = do
42− r <- fetch (\dir -> cabalFromPath (dir </> sourceCabalDir src)) src
49+ r <- fetch (\dir -> cabalFromPath optHpack (dir </> sourceCabalDir src)) src
44 Nothing -> fail "Failed to fetch source. Does the URL exist?"
51 Nothing -> fail "Failed to fetch source. Does the URL exist?"
45− Just (derivSource, (externalSource, pkgDesc)) -> do
52+ Just (derivSource, (externalSource, ranHpack, pkgDesc)) -> do
46− return (derivSource <$ guard externalSource, pkgDesc)
53+ return (derivSource <$ guard externalSource, ranHpack, pkgDesc)
48fromDB :: Maybe FilePath -> String -> IO (Maybe DerivationSource, Cabal.GenericPackageDescription)
55fromDB :: Maybe FilePath -> String -> IO (Maybe DerivationSource, Cabal.GenericPackageDescription)
49fromDB optHackageDB pkg = do
56fromDB optHackageDB pkg = do
@@ -129,23 +136,46 @@ showPackageIdentifier pkgDesc = name ++ "-" ++ display version where
129 name = Cabal.unPackageName (Cabal.packageName pkgId)
136 name = Cabal.unPackageName (Cabal.packageName pkgId)
130 version = Cabal.packageVersion pkgId
137 version = Cabal.packageVersion pkgId
139+cabalFromPath :: Bool -- ^ Whether hpack should regenerate the cabal file
132−cabalFromPath :: FilePath -> MaybeT IO (Bool, Cabal.GenericPackageDescription)
140+ -> FilePath -> MaybeT IO (Bool, Bool, Cabal.GenericPackageDescription)
133−cabalFromPath path = do
141+cabalFromPath optHpack path = do
134 d <- liftIO $ doesDirectoryExist path
142 d <- liftIO $ doesDirectoryExist path
135− (,) d <$> if d
143+ if d
136− then cabalFromDirectory path
145+ (ranHpack, pkg) <- cabalFromDirectory optHpack path
146+ return (d, ranHpack, pkg)
137− else cabalFromFile False path
147+ else (,,) d False <$> cabalFromFile False path
149+cabalFromDirectory :: Bool -- ^ Whether hpack should regenerate the cabal file
139−cabalFromDirectory :: FilePath -> MaybeT IO Cabal.GenericPackageDescription
150+ -> FilePath -> MaybeT IO (Bool, Cabal.GenericPackageDescription)
140−cabalFromDirectory dir = do
151+cabalFromDirectory True dir = hpackDirectory dir
152+cabalFromDirectory False dir = do
141 cabals <- liftIO $ getDirectoryContents dir >>= filterM doesFileExist . map (dir </>) . filter (".cabal" `isSuffixOf`)
153 cabals <- liftIO $ getDirectoryContents dir >>= filterM doesFileExist . map (dir </>) . filter (".cabal" `isSuffixOf`)
142 case cabals of
154 case cabals of
156+ liftIO $ hPutStrLn stderr "*** found zero cabal files. Trying hpack..."
143− [cabalFile] -> cabalFromFile True cabalFile
158+ [cabalFile] -> (,) False <$> cabalFromFile True cabalFile
144− _ -> liftIO $ fail ("Found zero or more than one cabal files: " ++ show cabals ++ ". Exiting.")
159+ _ -> liftIO $ fail ("*** found more than one cabal file (" ++ show cabals ++ "). Exiting.")
146handleIO :: (Exception.IOException -> IO a) -> IO a -> IO a
161handleIO :: (Exception.IOException -> IO a) -> IO a -> IO a
147handleIO = Exception.handle
162handleIO = Exception.handle
164+hpackDirectory :: FilePath -> MaybeT IO (Bool, Cabal.GenericPackageDescription)
165+hpackDirectory dir = do
166+ mPackage <- liftIO $ Hpack.readPackageConfig $ dir </> "package.yaml"
168+ Left err -> liftIO $ hPutStrLn stderr ("*** hpack error: " ++ show err ++ ". Exiting.") >> exitFailure
169+ Right (_, pkg') -> do
170+ let hpackOutput = Hpack.renderPackage Hpack.defaultRenderSettings 2 [] [] pkg'
171+ hash = printSHA256 $ digestString (digestByName "sha256") hpackOutput
172+ case parseGenericPackageDescription hpackOutput of
173+ ParseFailed perr -> liftIO $ do
174+ hPutStrLn stderr $ "*** cannot parse hpack output: " ++ show perr
175+ hPutStrLn stderr $ "*** hpack output:\n" ++ hpackOutput
177+ ParseOk _ pkg -> MaybeT $ return $ Just $ (,) True $ setCabalFileHash hash pkg
149cabalFromFile :: Bool -> FilePath -> MaybeT IO Cabal.GenericPackageDescription
179cabalFromFile :: Bool -> FilePath -> MaybeT IO Cabal.GenericPackageDescription
150cabalFromFile failHard file =
180cabalFromFile failHard file =
151 -- readFile throws an error if it's used on binary files which contain sequences
181 -- readFile throws an error if it's used on binary files which contain sequences