git.iko.soy/typeable/cabal2nix
on master help sign in
Merge pull request #304 from tomsmalley/master
3c87c13Peter Simons
cabal2nix.cabal+4
@@ -101,6 +101,7 @@ library
101 , filepath
101 , filepath
102 , hackage-db > 2
102 , hackage-db > 2
103 , hopenssl >= 2
103 , hopenssl >= 2
104+ , hpack
104 , language-nix
105 , language-nix
105 , lens
106 , lens
106 , optparse-applicative
107 , optparse-applicative
@@ -150,6 +151,7 @@ executable cabal2nix
150 , filepath
151 , filepath
151 , hackage-db > 2
152 , hackage-db > 2
152 , hopenssl >= 2
153 , hopenssl >= 2
154+ , hpack
153 , language-nix
155 , language-nix
154 , lens
156 , lens
155 , optparse-applicative
157 , optparse-applicative
@@ -183,6 +185,7 @@ executable hackage2nix
183 , filepath
185 , filepath
184 , hackage-db > 2
186 , hackage-db > 2
185 , hopenssl >= 2
187 , hopenssl >= 2
188+ , hpack
186 , language-nix
189 , language-nix
187 , lens
190 , lens
188 , optparse-applicative
191 , optparse-applicative
@@ -223,6 +226,7 @@ test-suite doctests
223 , filepath
226 , filepath
224 , hackage-db > 2
227 , hackage-db > 2
225 , hopenssl >= 2
228 , hopenssl >= 2
229+ , hpack
226 , language-nix
230 , language-nix
227 , lens
231 , lens
228 , optparse-applicative
232 , optparse-applicative
package.yaml+1
@@ -92,6 +92,7 @@ dependencies:
92 - filepath
92 - filepath
93 - hackage-db > 2
93 - hackage-db > 2
94 - hopenssl >= 2
94 - hopenssl >= 2
95+ - hpack
95 - language-nix
96 - language-nix
96 - lens
97 - lens
97 - optparse-applicative
98 - optparse-applicative
src/Cabal2nix.hs+11−2
@@ -17,6 +17,7 @@ import Distribution.Compiler
17import Distribution.Nixpkgs.Fetch
17import Distribution.Nixpkgs.Fetch
18import Distribution.Nixpkgs.Haskell
18import Distribution.Nixpkgs.Haskell
19import Distribution.Nixpkgs.Haskell.FromCabal
19import Distribution.Nixpkgs.Haskell.FromCabal
20+import qualified Distribution.Nixpkgs.Haskell.FromCabal.PostProcess as PP (pkg)
20import Distribution.Nixpkgs.Haskell.PackageSourceSpec
21import Distribution.Nixpkgs.Haskell.PackageSourceSpec
21import Distribution.Nixpkgs.Meta
22import Distribution.Nixpkgs.Meta
22import Distribution.PackageDescription ( mkFlagName, FlagAssignment )
23import Distribution.PackageDescription ( mkFlagName, FlagAssignment )
@@ -36,6 +37,7 @@ data Options = Options
36 , optMaintainer :: [String]
37 , optMaintainer :: [String]
37--, optPlatform :: [String] -- TODO: fix command line handling of platforms
38--, optPlatform :: [String] -- TODO: fix command line handling of platforms
38 , optHaddock :: Bool
39 , optHaddock :: Bool
40+ , optHpack :: Bool
39 , optDoCheck :: Bool
41 , optDoCheck :: Bool
40 , optJailbreak :: Bool
42 , optJailbreak :: Bool
41 , optRevision :: Maybe String
43 , optRevision :: Maybe String
@@ -60,6 +62,7 @@ options = Options
60 <*> many (strOption $ long "maintainer" <> metavar "MAINTAINER" <> help "maintainer of this package (may be specified multiple times)")
62 <*> many (strOption $ long "maintainer" <> metavar "MAINTAINER" <> help "maintainer of this package (may be specified multiple times)")
61-- <*> many (strOption $ long "platform" <> metavar "PLATFORM" <> help "supported build platforms (may be specified multiple times)")
63-- <*> many (strOption $ long "platform" <> metavar "PLATFORM" <> help "supported build platforms (may be specified multiple times)")
62 <*> flag True False (long "no-haddock" <> help "don't run Haddock when building this package")
64 <*> 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)")
63 <*> flag True False (long "no-check" <> help "don't run regression test suites of this package")
66 <*> flag True False (long "no-check" <> help "don't run regression test suites of this package")
64 <*> switch (long "jailbreak" <> help "disregard version restrictions on build inputs")
67 <*> switch (long "jailbreak" <> help "disregard version restrictions on build inputs")
65 <*> optional (strOption $ long "revision" <> help "revision to use when fetching from VCS")
68 <*> optional (strOption $ long "revision" <> help "revision to use when fetching from VCS")
@@ -118,15 +121,21 @@ main :: IO ()
118main = bracket (return ()) (\() -> hFlush stdout >> hFlush stderr) $ \() ->
121main = bracket (return ()) (\() -> hFlush stdout >> hFlush stderr) $ \() ->
119 cabal2nix =<< getArgs
122 cabal2nix =<< getArgs
120
123
124+hpackOverrides :: Derivation -> Derivation
125+hpackOverrides = over phaseOverrides (++ "preConfigure = \"hpack\";")
126+ . set (libraryDepends . tool . contains (PP.pkg "hpack")) True
127+
121cabal2nix' :: [String] -> IO (Either Doc Derivation)
128cabal2nix' :: [String] -> IO (Either Doc Derivation)
122cabal2nix' args = do
129cabal2nix' args = do
123 Options {..} <- handleParseResult $ execParserPure defaultPrefs pinfo args
130 Options {..} <- handleParseResult $ execParserPure defaultPrefs pinfo args
124
131
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)
126
133
127 let
134 let
135+ withHpackOverrides :: Derivation -> Derivation
136+ withHpackOverrides = if pkgRanHpack pkg then hpackOverrides else id
128 deriv :: Derivation
137 deriv :: Derivation
129− deriv = fromGenericPackageDescription (const True)
138+ deriv = withHpackOverrides $ fromGenericPackageDescription (const True)
130 (\i -> Just (binding # (i, path # [i])))
139 (\i -> Just (binding # (i, path # [i])))
131 optSystem
140 optSystem
132 (unknownCompilerInfo optCompiler NoAbiTag)
141 (unknownCompilerInfo optCompiler NoAbiTag)
src/Distribution/Nixpkgs/Haskell/FromCabal.hs+1−1
@@ -17,7 +17,7 @@ import Distribution.Nixpkgs.Haskell.Constraint
17import Distribution.Nixpkgs.Haskell.FromCabal.License
17import Distribution.Nixpkgs.Haskell.FromCabal.License
18import Distribution.Nixpkgs.Haskell.FromCabal.Name
18import Distribution.Nixpkgs.Haskell.FromCabal.Name
19import Distribution.Nixpkgs.Haskell.FromCabal.Normalize
19import Distribution.Nixpkgs.Haskell.FromCabal.Normalize
20−import Distribution.Nixpkgs.Haskell.FromCabal.PostProcess
20+import Distribution.Nixpkgs.Haskell.FromCabal.PostProcess (postProcess)
21import qualified Distribution.Nixpkgs.Meta as Nix
21import qualified Distribution.Nixpkgs.Meta as Nix
22import Distribution.Package
22import Distribution.Package
23import Distribution.PackageDescription
23import Distribution.PackageDescription
src/Distribution/Nixpkgs/Haskell/FromCabal/PostProcess.hs+1−1
@@ -1,6 +1,6 @@
1{-# LANGUAGE OverloadedStrings #-}
1{-# LANGUAGE OverloadedStrings #-}
2
2
3−module Distribution.Nixpkgs.Haskell.FromCabal.PostProcess ( postProcess ) where
3+module Distribution.Nixpkgs.Haskell.FromCabal.PostProcess ( postProcess, pkg ) where
4
4
5import Control.Lens
5import Control.Lens
6import Data.List.Split
6import Data.List.Split
src/Distribution/Nixpkgs/Haskell/PackageSourceSpec.hs+53−23
@@ -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 )
26
28
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
30 }
33 }
31 deriving (Show)
34 deriving (Show)
32
35
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
37−
41+
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
43 case r of
50 case r of
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)
47
54
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
131
138
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
144+ then do
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
138−
148+
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
155+ [] -> do
156+ liftIO $ hPutStrLn stderr "*** found zero cabal files. Trying hpack..."
157+ hpackDirectory dir
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.")
145
160
146handleIO :: (Exception.IOException -> IO a) -> IO a -> IO a
161handleIO :: (Exception.IOException -> IO a) -> IO a -> IO a
147handleIO = Exception.handle
162handleIO = Exception.handle
148
163
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+
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