git.iko.soy/typeable/cabal2nix
on master help sign in
Merge pull request #80 from bennofs/fix-local-bugs

Bugfixes and docfixes for local project patch

commit997bd029b63cee6ab9691243968d1edca809c652 authorPeter Simons <simons@cryp.to> dateMon 18 Aug 2014, 12:17 (12 years ago) parent813d429b217210
4 files changed+108−22
README.md+47−2 cabal2nix.cabal+8−2 src/Cabal2Nix/Package.hs+44−17 src/Distribution/NixOS/Fetch.hs+9−1
README.md+47−2
@@ -44,8 +44,10 @@ Cabal files can be referred to using the magic URL
4444`cabal://NAME-VERSION`, which will automatically download the file from
4545Hackage. Alternatively, a direct `http://host/path/pkg.cabal` URL can be
4646provided, as well as a `file:///local/path/pkg.cabal` URI that doesn't
47−depend on network access. Run the utility with `--help` to see the
48−complete list of supported command line flags.
47+depend on network access. However, if the source hash is not already in `cabal2nix`'s cache or
48+provided using the `--sha256` option, `cabal2nix` still needs to download the source code to
49+compute the hash, which obviously still causes network traffic.
50+Run the utility with `--help` to see the complete list of supported command line flags.
4951
5052To add a new package to Nix, checkout the Nixpkgs project from
5153https://github.com/nixos/nixpkgs.git and run
@@ -59,6 +61,49 @@ Then add an appropriate attribute to
5961
6062 foo = callPackage ../development/libraries/haskell/foo {};
6163
64+`cabal2nix` can also build derivations for projects from other
65+sources than hackage. You only need to provide an URI that points
66+to a cabal project. The most common usecase for this is probably to
67+generate a derivation for a project on the local file system:
68+
69+ aeson:$ cabal2nix ./.
70+ # This file was auto-generated by cabal2nix. Please do NOT edit manually!
71+
72+ { cabal, attoparsec, blazeBuilder, deepseq, dlist, hashable, HUnit
73+ , mtl, QuickCheck, scientific, syb, testFramework
74+ , testFrameworkHunit, testFrameworkQuickcheck2, text, time
75+ , unorderedContainers, vector
76+ }
77+
78+ cabal.mkDerivation (self: {
79+ pname = "aeson";
80+ version = "0.7.0.2";
81+ src = ./.;
82+ buildDepends = [
83+ attoparsec blazeBuilder deepseq dlist hashable mtl scientific syb
84+ text time unorderedContainers vector
85+ ];
86+ testDepends = [
87+ attoparsec HUnit QuickCheck testFramework testFrameworkHunit
88+ testFrameworkQuickcheck2 text time unorderedContainers vector
89+ ];
90+ meta = {
91+ homepage = "https://github.com/bos/aeson";
92+ description = "Fast JSON parsing and encoding";
93+ license = self.stdenv.lib.licenses.bsd3;
94+ platforms = self.ghc.meta.platforms;
95+ };
96+ })
97+
98+This derivation will not fetch from hackage, but instead use the directory which
99+contains the derivation as the source repository.
100+
101+`cabal2nix` currently supports the following respository types:
102+
103+* directory
104+* source archive (zip, tar.gz, ...) from http or https URL or local file.
105+* git, mercurial, svn or bazaar repository
106+
62107### Hackage4nix
63108
64109The `hackage4nix` utility re-generates *all* expressions found in the
cabal2nix.cabal+8−2
@@ -16,7 +16,7 @@ Description:
1616 The @cabal2nix@ utility converts Cabal files into Nix build instructions. The
1717 commandline syntax is:
1818 .
19− > Usage: cabal2nix [options] url-to-cabal-file
19+ > Usage: cabal2nix [options] url-to-cabal-file-or-repo
2020 > -h --help show this help text
2121 > --hackage-db=FILEPATH path to the local hackage db in tar format
2222 > --sha256=HASH sha256 hash of source tarball
@@ -33,7 +33,8 @@ Description:
3333 > cabal://pkgname download latest version of the specified package from Hackage
3434 > http://host/path fetch the Cabal file via HTTP
3535 > file:///local/path load the Cabal file from the local disk
36− > /local/path abbreviated version of file URI
36+ > /local/path.cabal abbreviated version of file URI
37+ > <git/svn/bzr/hg URL> download the source from the specified repository
3738 .
3839 The only required argument is the path to the cabal file. For example:
3940 .
@@ -43,6 +44,11 @@ Description:
4344 If the @--sha256@ option has not been specified, cabal2nix calls
4445 @nix-prefetch-url@ to determine the hash automatically. This causes
4546 network traffic, obviously.
47+ .
48+ If the argument refers to a source repository instead of a cabal file,
49+ cabal2nix will use that source repository to fetch from instead of hackage.
50+ Currently, cabal2nix supports directories, archives (fetched via http or https) and
51+ git, mercurial, svn or bazaar repositories.
4652
4753Source-Repository head
4854 Type: git
src/Cabal2Nix/Package.hs+44−17
@@ -1,9 +1,9 @@
11module Cabal2Nix.Package where
22
3+import Control.Applicative
34import Control.Monad
45import Control.Monad.IO.Class
56import Control.Monad.Trans.Maybe
6−import Data.Functor ( (<$>), (<$) )
77import Data.List ( isSuffixOf, isPrefixOf )
88import Data.Maybe ( listToMaybe )
99import Distribution.NixOS.Derivation.Cabal
@@ -19,6 +19,9 @@ import qualified Distribution.Package as Cabal
1919import qualified Distribution.PackageDescription as Cabal
2020import qualified Distribution.PackageDescription.Parse as Cabal
2121
22+import qualified Distribution.Compat.Exception as Compat
23+import qualified Distribution.ParseUtils as ParseUtils
24+
2225data Package = Package
2326 { source :: DerivationSource
2427 , cabal :: Cabal.GenericPackageDescription
@@ -36,7 +39,8 @@ fetchOrFromDB optHackageDB src
3639 | otherwise = do
3740 r <- fetch cabalFromPath src
3841 case r of
39− Nothing -> hPutStrLn stderr "*** failed to fetch source. Does the URL exist?" >> exitFailure
42+ Nothing ->
43+ hPutStrLn stderr "*** failed to fetch source. Does the URL exist?" >> exitFailure
4044 Just (derivSource, (externalSource, pkgDesc)) ->
4145 return (derivSource <$ guard externalSource, pkgDesc)
4246
@@ -74,15 +78,28 @@ hashCachePath pid = do
7478sourceFromHackage :: Maybe String -> String -> IO DerivationSource
7579sourceFromHackage optHash pkgId = do
7680 cacheFile <- hashCachePath pkgId
77− hash <- maybe (readFileMay cacheFile) (return . Just) optHash
78− res <- runMaybeT $ fst <$> fetchWith (False, "url") (Source ("mirror://hackage/" ++ pkgId ++ ".tar.gz") "" hash)
79− case res of
80− Just r -> writeFile cacheFile (derivHash r) >> return r
81+ let cachedHash = MaybeT $ maybe (readFileMay cacheFile) (return . Just) optHash
82+ url = "mirror://hackage/" ++ pkgId ++ ".tar.gz"
83+
84+ -- Use the cached hash (either from cache file or given on cmdline via sha256 opt)
85+ -- if available, otherwise download from hackage to compute hash.
86+ maybeHash <- runMaybeT $ cachedHash <|> derivHash . fst <$> fetchWith (False, "url") (Source url "" Nothing)
87+ case maybeHash of
88+ Just hash ->
89+ -- We need to force the hash here. If we didn't do this, then when reading the
90+ -- hash from the cache file, the cache file will still be open for reading
91+ -- (because lazy io) when writeFile opens the file again for writing. By forcing
92+ -- the hash here, we ensure that the file is closed before opening it again.
93+ seq (length hash) $
94+ DerivationSource "url" url "" hash <$ writeFile cacheFile hash
8195 Nothing -> do
8296 hPutStr stderr $ unlines
8397 [ "*** cannot compute hash. (Not a hackage project?)"
84− , " Specify hash explicitly via --sha256 and add appropriate \"src\" attribute"
85− , " to resulting nix expression."
98+ , " If your project is not on hackage, please supply the path to the root directory of"
99+ , " the project, not to the cabal file."
100+ , ""
101+ , " If your project is on hackage but you still want to specify the hash manually, you"
102+ , " can use the --sha256 option."
86103 ]
87104 exitFailure
88105
@@ -96,19 +113,29 @@ cabalFromPath :: FilePath -> MaybeT IO (Bool, Cabal.GenericPackageDescription)
96113cabalFromPath path = do
97114 d <- liftIO $ doesDirectoryExist path
98115 (,) d <$> if d
99− then cabalFromDirectory path
100− else cabalFromFile path
116+ then cabalFromDirectory path
117+ else cabalFromFile False path
101118
102119cabalFromDirectory :: FilePath -> MaybeT IO Cabal.GenericPackageDescription
103120cabalFromDirectory dir = do
104121 cabals <- liftIO $ getDirectoryContents dir >>= filterM doesFileExist . map (dir </>) . filter (".cabal" `isSuffixOf`)
105122 case cabals of
106− [cabalFile] -> cabalFromFile cabalFile
123+ [cabalFile] -> cabalFromFile True cabalFile
107124 _ -> liftIO $ hPutStrLn stderr "*** found zero or more than one cabal file. Exiting." >> exitFailure
108125
109−cabalFromFile :: FilePath -> MaybeT IO Cabal.GenericPackageDescription
110−cabalFromFile file = do
111− content <- liftIO $ readFile file
112− case Cabal.parsePackageDescription content of
113− Cabal.ParseFailed _ -> mzero
114− Cabal.ParseOk _ a -> return a
126+cabalFromFile :: Bool -> FilePath -> MaybeT IO Cabal.GenericPackageDescription
127+cabalFromFile failHard file =
128+ -- readFile throws an error if it's used on binary files which contain sequences
129+ -- that do not represent valid characters. To catch that exception, we need to
130+ -- wrap the whole block in `catchIO`, because of lazy IO. The `case` will force
131+ -- the reading of the file, so we will always catch the expression here.
132+ MaybeT $ flip Compat.catchIO (const $ return Nothing) $ do
133+ content <- readFile file
134+ case Cabal.parsePackageDescription content of
135+ Cabal.ParseFailed e | failHard -> do
136+ let (line, err) = ParseUtils.locatedErrorMsg e
137+ msg = maybe "" ((++ ": ") . show) line ++ err
138+ putStrLn $ "*** error parsing cabal file: " ++ msg
139+ exitFailure
140+ Cabal.ParseFailed _ -> return Nothing
141+ Cabal.ParseOk _ a -> return (Just a)
src/Distribution/NixOS/Fetch.hs+9−1
@@ -62,7 +62,15 @@ fetch f = runMaybeT . fetchers where
6262 existsFile <- liftIO $ doesFileExist path
6363 existsDir <- liftIO $ doesDirectoryExist path
6464 guard $ existsDir || existsFile
65− process (DerivationSource "" path "" "", path)
65+ let path' | '/' `elem` path = path
66+ | otherwise = "./" ++ path
67+ process (DerivationSource "" path' "" "", path') <|> localArchive path'
68+
69+ localArchive :: FilePath -> MaybeT IO (DerivationSource, a)
70+ localArchive path = do
71+ absolutePath <- liftIO $ canonicalizePath path
72+ unpacked <- snd <$> fetchWith (False, "zip") (Source ("file://" ++ absolutePath) "" Nothing)
73+ process (DerivationSource "" absolutePath "" "", unpacked)
6674
6775 process :: (DerivationSource, FilePath) -> MaybeT IO (DerivationSource, a)
6876 process (derivSource, file) = (,) derivSource <$> f file