git.iko.soy/typeable/cabal2nix
on master help sign in
Merge pull request #304 from tomsmalley/master

Try hpack when cabal file is missing (issue #249)

commit3c87c13c3dab0c664f4844a82f24390323349929 authorPeter Simons <simons@cryp.to> dateSun 24 Sep 2017, 14:14 (9 years ago) parent30558e159f1e2f
6 files changed+71−27
cabal2nix.cabal+4 package.yaml+1 src/Cabal2nix.hs+11−2 src/Distribution/Nixpkgs/Haskell/FromCabal.hs+1−1 src/Distribution/Nixpkgs/Haskell/FromCabal/PostProcess.hs+1−1 src/Distribution/Nixpkgs/Haskell/PackageSourceSpec.hs+53−23
cabal2nix.cabal+4
@@ -101,6 +101,7 @@ library
101101 , filepath
102102 , hackage-db > 2
103103 , hopenssl >= 2
104+ , hpack
104105 , language-nix
105106 , lens
106107 , optparse-applicative
@@ -150,6 +151,7 @@ executable cabal2nix
150151 , filepath
151152 , hackage-db > 2
152153 , hopenssl >= 2
154+ , hpack
153155 , language-nix
154156 , lens
155157 , optparse-applicative
@@ -183,6 +185,7 @@ executable hackage2nix
183185 , filepath
184186 , hackage-db > 2
185187 , hopenssl >= 2
188+ , hpack
186189 , language-nix
187190 , lens
188191 , optparse-applicative
@@ -223,6 +226,7 @@ test-suite doctests
223226 , filepath
224227 , hackage-db > 2
225228 , hopenssl >= 2
229+ , hpack
226230 , language-nix
227231 , lens
228232 , optparse-applicative
package.yaml+1
@@ -92,6 +92,7 @@ dependencies:
9292 - filepath
9393 - hackage-db > 2
9494 - hopenssl >= 2
95+ - hpack
9596 - language-nix
9697 - lens
9798 - optparse-applicative
src/Cabal2nix.hs+11−2
@@ -17,6 +17,7 @@ import Distribution.Compiler
1717import Distribution.Nixpkgs.Fetch
1818import Distribution.Nixpkgs.Haskell
1919import Distribution.Nixpkgs.Haskell.FromCabal
20+import qualified Distribution.Nixpkgs.Haskell.FromCabal.PostProcess as PP (pkg)
2021import Distribution.Nixpkgs.Haskell.PackageSourceSpec
2122import Distribution.Nixpkgs.Meta
2223import Distribution.PackageDescription ( mkFlagName, FlagAssignment )
@@ -36,6 +37,7 @@ data Options = Options
3637 , optMaintainer :: [String]
3738--, optPlatform :: [String] -- TODO: fix command line handling of platforms
3839 , optHaddock :: Bool
40+ , optHpack :: Bool
3941 , optDoCheck :: Bool
4042 , optJailbreak :: Bool
4143 , optRevision :: Maybe String
@@ -60,6 +62,7 @@ options = Options
6062 <*> many (strOption $ long "maintainer" <> metavar "MAINTAINER" <> help "maintainer of this package (may be specified multiple times)")
6163-- <*> many (strOption $ long "platform" <> metavar "PLATFORM" <> help "supported build platforms (may be specified multiple times)")
6264 <*> flag True False (long "no-haddock" <> help "don't run Haddock when building this package")
65+ <*> switch (long "hpack" <> help "run hpack before configuring this package (only non-hackage packages)")
6366 <*> flag True False (long "no-check" <> help "don't run regression test suites of this package")
6467 <*> switch (long "jailbreak" <> help "disregard version restrictions on build inputs")
6568 <*> optional (strOption $ long "revision" <> help "revision to use when fetching from VCS")
@@ -118,15 +121,21 @@ main :: IO ()
118121main = bracket (return ()) (\() -> hFlush stdout >> hFlush stderr) $ \() ->
119122 cabal2nix =<< getArgs
120123
124+hpackOverrides :: Derivation -> Derivation
125+hpackOverrides = over phaseOverrides (++ "preConfigure = \"hpack\";")
126+ . set (libraryDepends . tool . contains (PP.pkg "hpack")) True
127+
121128cabal2nix' :: [String] -> IO (Either Doc Derivation)
122129cabal2nix' args = do
123130 Options {..} <- handleParseResult $ execParserPure defaultPrefs pinfo args
124131
125− pkg <- getPackage optHackageDb $ Source optUrl (fromMaybe "" optRevision) (maybe UnknownHash Guess optSha256) (fromMaybe "" optSubpath)
132+ pkg <- getPackage optHpack optHackageDb $ Source optUrl (fromMaybe "" optRevision) (maybe UnknownHash Guess optSha256) (fromMaybe "" optSubpath)
126133
127134 let
135+ withHpackOverrides :: Derivation -> Derivation
136+ withHpackOverrides = if pkgRanHpack pkg then hpackOverrides else id
128137 deriv :: Derivation
129− deriv = fromGenericPackageDescription (const True)
138+ deriv = withHpackOverrides $ fromGenericPackageDescription (const True)
130139 (\i -> Just (binding # (i, path # [i])))
131140 optSystem
132141 (unknownCompilerInfo optCompiler NoAbiTag)
src/Distribution/Nixpkgs/Haskell/FromCabal.hs+1−1
@@ -17,7 +17,7 @@ import Distribution.Nixpkgs.Haskell.Constraint
1717import Distribution.Nixpkgs.Haskell.FromCabal.License
1818import Distribution.Nixpkgs.Haskell.FromCabal.Name
1919import Distribution.Nixpkgs.Haskell.FromCabal.Normalize
20−import Distribution.Nixpkgs.Haskell.FromCabal.PostProcess
20+import Distribution.Nixpkgs.Haskell.FromCabal.PostProcess (postProcess)
2121import qualified Distribution.Nixpkgs.Meta as Nix
2222import Distribution.Package
2323import Distribution.PackageDescription
src/Distribution/Nixpkgs/Haskell/FromCabal/PostProcess.hs+1−1
@@ -1,6 +1,6 @@
11{-# LANGUAGE OverloadedStrings #-}
22
3−module Distribution.Nixpkgs.Haskell.FromCabal.PostProcess ( postProcess ) where
3+module Distribution.Nixpkgs.Haskell.FromCabal.PostProcess ( postProcess, pkg ) where
44
55import Control.Lens
66import Data.List.Split
src/Distribution/Nixpkgs/Haskell/PackageSourceSpec.hs+53−23
@@ -18,6 +18,8 @@ import qualified Distribution.PackageDescription as Cabal
1818import Distribution.PackageDescription.Parse as Cabal
1919import Distribution.Text ( simpleParse, display )
2020import Distribution.Version
21+import qualified Hpack.Run as Hpack
22+import qualified Hpack.Config as Hpack
2123import OpenSSL.Digest ( digestString, digestByName )
2224import System.Directory ( doesDirectoryExist, doesFileExist, createDirectoryIfMissing, getHomeDirectory, getDirectoryContents )
2325import System.Exit ( exitFailure )
@@ -25,25 +27,30 @@ import System.FilePath ( (</>), (<.>) )
2527import System.IO ( hPutStrLn, stderr, hPutStr )
2628
2729data Package = Package
28− { pkgSource :: DerivationSource
29− , pkgCabal :: Cabal.GenericPackageDescription
30+ { pkgSource :: DerivationSource
31+ , pkgRanHpack :: Bool -- ^ If hpack generated a new cabal file
32+ , pkgCabal :: Cabal.GenericPackageDescription
3033 }
3134 deriving (Show)
3235
33−getPackage :: Maybe FilePath -> Source -> IO Package
34−getPackage optHackageDB source = do
35− (derivSource, pkgDesc) <- fetchOrFromDB optHackageDB source
36− flip Package pkgDesc <$> maybe (sourceFromHackage (sourceHash source) (showPackageIdentifier pkgDesc) $ sourceCabalDir source) return derivSource
37−
38−fetchOrFromDB :: Maybe FilePath -> Source -> IO (Maybe DerivationSource, Cabal.GenericPackageDescription)
39−fetchOrFromDB optHackageDB src
40− | "cabal://" `isPrefixOf` sourceUrl src = fromDB optHackageDB . drop (length "cabal://") $ sourceUrl src
36+getPackage :: Bool -- ^ Whether hpack should regenerate the cabal file
37+ -> Maybe FilePath -> Source -> IO Package
38+getPackage optHpack optHackageDB source = do
39+ (derivSource, ranHpack, pkgDesc) <- fetchOrFromDB optHpack optHackageDB source
40+ (\s -> Package s ranHpack pkgDesc) <$> maybe (sourceFromHackage (sourceHash source) (showPackageIdentifier pkgDesc) $ sourceCabalDir source) return derivSource
41+
42+fetchOrFromDB :: Bool -- ^ Whether hpack should regenerate the cabal file
43+ -> Maybe FilePath -> Source -> IO (Maybe DerivationSource, Bool, Cabal.GenericPackageDescription)
44+fetchOrFromDB optHpack optHackageDB src
45+ | "cabal://" `isPrefixOf` sourceUrl src = do
46+ (msrc, pkgDesc) <- fromDB optHackageDB . drop (length "cabal://") $ sourceUrl src
47+ return (msrc, False, pkgDesc)
4148 | otherwise = do
42− r <- fetch (\dir -> cabalFromPath (dir </> sourceCabalDir src)) src
49+ r <- fetch (\dir -> cabalFromPath optHpack (dir </> sourceCabalDir src)) src
4350 case r of
4451 Nothing -> fail "Failed to fetch source. Does the URL exist?"
45− Just (derivSource, (externalSource, pkgDesc)) -> do
46− return (derivSource <$ guard externalSource, pkgDesc)
52+ Just (derivSource, (externalSource, ranHpack, pkgDesc)) -> do
53+ return (derivSource <$ guard externalSource, ranHpack, pkgDesc)
4754
4855fromDB :: Maybe FilePath -> String -> IO (Maybe DerivationSource, Cabal.GenericPackageDescription)
4956fromDB optHackageDB pkg = do
@@ -129,23 +136,46 @@ showPackageIdentifier pkgDesc = name ++ "-" ++ display version where
129136 name = Cabal.unPackageName (Cabal.packageName pkgId)
130137 version = Cabal.packageVersion pkgId
131138
132−cabalFromPath :: FilePath -> MaybeT IO (Bool, Cabal.GenericPackageDescription)
133−cabalFromPath path = do
139+cabalFromPath :: Bool -- ^ Whether hpack should regenerate the cabal file
140+ -> FilePath -> MaybeT IO (Bool, Bool, Cabal.GenericPackageDescription)
141+cabalFromPath optHpack path = do
134142 d <- liftIO $ doesDirectoryExist path
135− (,) d <$> if d
136− then cabalFromDirectory path
137− else cabalFromFile False path
138−
139−cabalFromDirectory :: FilePath -> MaybeT IO Cabal.GenericPackageDescription
140−cabalFromDirectory dir = do
143+ if d
144+ then do
145+ (ranHpack, pkg) <- cabalFromDirectory optHpack path
146+ return (d, ranHpack, pkg)
147+ else (,,) d False <$> cabalFromFile False path
148+
149+cabalFromDirectory :: Bool -- ^ Whether hpack should regenerate the cabal file
150+ -> FilePath -> MaybeT IO (Bool, Cabal.GenericPackageDescription)
151+cabalFromDirectory True dir = hpackDirectory dir
152+cabalFromDirectory False dir = do
141153 cabals <- liftIO $ getDirectoryContents dir >>= filterM doesFileExist . map (dir </>) . filter (".cabal" `isSuffixOf`)
142154 case cabals of
143− [cabalFile] -> cabalFromFile True cabalFile
144− _ -> liftIO $ fail ("Found zero or more than one cabal files: " ++ show cabals ++ ". Exiting.")
155+ [] -> do
156+ liftIO $ hPutStrLn stderr "*** found zero cabal files. Trying hpack..."
157+ hpackDirectory dir
158+ [cabalFile] -> (,) False <$> cabalFromFile True cabalFile
159+ _ -> liftIO $ fail ("*** found more than one cabal file (" ++ show cabals ++ "). Exiting.")
145160
146161handleIO :: (Exception.IOException -> IO a) -> IO a -> IO a
147162handleIO = Exception.handle
148163
164+hpackDirectory :: FilePath -> MaybeT IO (Bool, Cabal.GenericPackageDescription)
165+hpackDirectory dir = do
166+ mPackage <- liftIO $ Hpack.readPackageConfig $ dir </> "package.yaml"
167+ case mPackage of
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
176+ fail "*** Exiting."
177+ ParseOk _ pkg -> MaybeT $ return $ Just $ (,) True $ setCabalFileHash hash pkg
178+
149179cabalFromFile :: Bool -> FilePath -> MaybeT IO Cabal.GenericPackageDescription
150180cabalFromFile failHard file =
151181 -- readFile throws an error if it's used on binary files which contain sequences