packages feed

cabal2nix 1.68 → 1.69

raw patch · 12 files changed

+406/−135 lines, 12 filesdep +transformersdep −HTTP

Dependencies added: transformers

Dependencies removed: HTTP

Files

README.md view
@@ -44,8 +44,10 @@ `cabal://NAME-VERSION`, which will automatically download the file from Hackage. Alternatively, a direct `http://host/path/pkg.cabal` URL can be provided, as well as a `file:///local/path/pkg.cabal` URI that doesn't-depend on network access. Run the utility with `--help` to see the-complete list of supported command line flags.+depend on network access. However, if the source hash is not already in `cabal2nix`'s cache or+provided using the `--sha256` option, `cabal2nix` still needs to download the source code to+compute the hash, which obviously still causes network traffic.+Run the utility with `--help` to see the complete list of supported command line flags.  To add a new package to Nix, checkout the Nixpkgs project from https://github.com/nixos/nixpkgs.git and run@@ -58,6 +60,49 @@ `pkgs/top-level/haskell-packages.nix`, for example:      foo = callPackage ../development/libraries/haskell/foo {};++`cabal2nix` can also build derivations for projects from other+sources than hackage. You only need to provide an URI that points+to a cabal project. The most common usecase for this is probably to+generate a derivation for a project on the local file system:++    aeson:$ cabal2nix ./.+    # This file was auto-generated by cabal2nix. Please do NOT edit manually!++    { cabal, attoparsec, blazeBuilder, deepseq, dlist, hashable, HUnit+    , mtl, QuickCheck, scientific, syb, testFramework+    , testFrameworkHunit, testFrameworkQuickcheck2, text, time+    , unorderedContainers, vector+    }++    cabal.mkDerivation (self: {+      pname = "aeson";+      version = "0.7.0.2";+      src = ./.;+      buildDepends = [+        attoparsec blazeBuilder deepseq dlist hashable mtl scientific syb+        text time unorderedContainers vector+      ];+      testDepends = [+        attoparsec HUnit QuickCheck testFramework testFrameworkHunit+        testFrameworkQuickcheck2 text time unorderedContainers vector+      ];+      meta = {+        homepage = "https://github.com/bos/aeson";+        description = "Fast JSON parsing and encoding";+        license = self.stdenv.lib.licenses.bsd3;+        platforms = self.ghc.meta.platforms;+      };+    })++This derivation will not fetch from hackage, but instead use the directory which+contains the derivation as the source repository.++`cabal2nix` currently supports the following respository types:++* directory+* source archive (zip, tar.gz, ...) from http or https URL or local file.+* git, mercurial, svn or bazaar repository  ### Hackage4nix 
cabal2nix.cabal view
@@ -1,5 +1,5 @@ Name:                   cabal2nix-Version:                1.68+Version:                1.69 Copyright:              Peter Simons, Andres Loeh License:                BSD3 License-File:           LICENSE@@ -16,7 +16,7 @@   The @cabal2nix@ utility converts Cabal files into Nix build instructions. The   commandline syntax is:   .-  > Usage: cabal2nix [options] url-to-cabal-file+  > Usage: cabal2nix [options] url-to-cabal-file-or-repo   >   -h             --help                   show this help text   >                  --hackage-db=FILEPATH    path to the local hackage db in tar format   >                  --sha256=HASH            sha256 hash of source tarball@@ -33,16 +33,22 @@   >   cabal://pkgname                  download latest version of the specified package from Hackage   >   http://host/path                 fetch the Cabal file via HTTP   >   file:///local/path               load the Cabal file from the local disk-  >   /local/path                      abbreviated version of file URI+  >   /local/path.cabal                abbreviated version of file URI+  >   <git/svn/bzr/hg URL>             download the source from the specified repository   .   The only required argument is the path to the cabal file. For example:   .-  > cabal2nix http://hackage.haskell.org/packages/archive/cabal2nix/1.68/cabal2nix.cabal-  > cabal2nix cabal://cabal2nix-1.68+  > cabal2nix http://hackage.haskell.org/packages/archive/cabal2nix/1.69/cabal2nix.cabal+  > cabal2nix cabal://cabal2nix-1.69   .   If the @--sha256@ option has not been specified, cabal2nix calls   @nix-prefetch-url@ to determine the hash automatically. This causes   network traffic, obviously.+  .+  If the argument refers to a source repository instead of a cabal file,+  cabal2nix will use that source repository to fetch from instead of hackage.+  Currently, cabal2nix supports directories, archives (fetched via http or https) and+  git, mercurial, svn or bazaar repositories.  Source-Repository head   Type:                 git@@ -52,17 +58,18 @@   main-is:              cabal2nix.hs   hs-source-dirs:       src   Build-Depends:        base >= 3 && < 5, regex-posix, pretty, Cabal >= 1.16,-                        filepath, directory, process, HTTP, hackage-db+                        filepath, directory, process, hackage-db, transformers   Extensions:           PatternGuards, RecordWildCards, CPP   Ghc-Options:          -Wall   other-modules:        Cabal2Nix.CorePackages                         Cabal2Nix.Flags                         Cabal2Nix.Generate-                        Cabal2Nix.Hackage+                        Cabal2Nix.Package                         Cabal2Nix.License                         Cabal2Nix.Name                         Cabal2Nix.Normalize                         Cabal2Nix.PostProcess+                        Distribution.NixOS.Fetch                         Distribution.NixOS.Derivation.Cabal                         Distribution.NixOS.Derivation.License                         Distribution.NixOS.Derivation.Meta@@ -73,7 +80,7 @@   main-is:              hackage4nix.hs   hs-source-dirs:       src   Build-Depends:        base >= 3 && < 5, regex-posix, pretty, Cabal >= 1.16,-                        mtl, containers, directory, filepath, hackage-db+                        mtl, containers, directory, filepath, hackage-db, transformers, process   Extensions:           PatternGuards, RecordWildCards, CPP   Ghc-Options:          -Wall   other-modules:        Cabal2Nix.CorePackages@@ -83,6 +90,7 @@                         Cabal2Nix.Name                         Cabal2Nix.Normalize                         Cabal2Nix.PostProcess+                        Distribution.NixOS.Fetch                         Distribution.NixOS.Derivation.Cabal                         Distribution.NixOS.Derivation.License                         Distribution.NixOS.Derivation.Meta
src/Cabal2Nix/Generate.hs view
@@ -1,23 +1,22 @@ module Cabal2Nix.Generate ( cabal2nix ) where +import Cabal2Nix.Flags import Cabal2Nix.License-import Cabal2Nix.PostProcess import Cabal2Nix.Normalize-import Cabal2Nix.Flags+import Cabal2Nix.PostProcess import Data.Maybe import Distribution.Compiler+import Distribution.NixOS.Derivation.Cabal import qualified Distribution.Package as Cabal import qualified Distribution.PackageDescription as Cabal import Distribution.PackageDescription.Configuration import Distribution.System-import Distribution.Version-import Distribution.NixOS.Derivation.Cabal  cabal2nix :: Cabal.GenericPackageDescription -> Derivation cabal2nix cabal = normalize $ postProcess MkDerivation   { pname          = let Cabal.PackageName x = Cabal.pkgName pkg in x   , version        = Cabal.pkgVersion pkg-  , sha256         = "cabal2nix left the sha256 field undefined"+  , src            = error "cabal2nix left the src field undefined"   , isLibrary      = isJust (Cabal.library tpkg)   , isExecutable   = not (null (Cabal.executables tpkg))   , extraFunctionArgs = []
− src/Cabal2Nix/Hackage.hs
@@ -1,84 +0,0 @@-module Cabal2Nix.Hackage ( hashPackage, readCabalFile ) where--import Control.Monad ( unless )-import Data.List ( isPrefixOf )-import Data.Maybe ( fromJust )-import Data.Version ( showVersion, versionBranch )-import Distribution.Package ( PackageIdentifier(..), PackageName(..) )-import Distribution.Text-import Network.HTTP ( getRequest, rspBody )-import Network.Browser ( browse, request, setCheckForProxy, setDebugLog, setOutHandler )-import System.Directory ( doesFileExist, getHomeDirectory, createDirectoryIfMissing )-import System.FilePath ( dropFileName, (</>), (<.>) )-import System.Process ( readProcess )-import Control.Exception (handle, SomeException(..))--import qualified Distribution.Hackage.DB as DB--data Ext = TarGz | Cabal deriving Eq--showExt :: Ext -> String-showExt TarGz = ".tar.gz"-showExt Cabal = ".cabal"--hackagePath :: PackageIdentifier -> Ext -> String-hackagePath (PackageIdentifier (PackageName name) version') ext =-    "http://hackage.haskell.org/packages/archive/" ++-    name ++ "/" ++ version ++ "/" ++ name ++-    (if ext == TarGz then '-' : version else "") ++-    showExt ext-  where-    version = showVersion version'--hashPackage :: PackageIdentifier -> IO String-hashPackage pkg = do-    cachePath <- hashCachePath pkg-    exists <- doesFileExist cachePath-    hash' <- if exists-               then readFile cachePath-               else getHackageHash-    let hash = reverse (dropWhile (=='\n') (reverse hash'))-    unless exists $ do-      createDirectoryIfMissing True (dropFileName cachePath)-      writeFile cachePath hash-    return hash-  where getHackageHash = do-            let command = "exec nix-prefetch-url 2>/dev/tty " ++ hackagePath pkg TarGz-            handle handlePrefetchError (readProcess "bash" ["-c", command] "")-        handlePrefetchError (SomeException _) =-           error $ "\nError: Cannot compute hash. (Not a hackage project?)\n" ++-                  "Specify hash explicitly via --sha256 and add appropriate \"src\" attribute " ++-                  "to resulting nix expression."--hashCachePath :: PackageIdentifier -> IO FilePath-hashCachePath (PackageIdentifier (PackageName name) version') = do-    home <- getHomeDirectory-    return $ home ++ "/.cache/cabal2nix" </> name ++ "-" ++ version <.> "sha256"-  where-    version = showVersion version'--readCabalFile :: Maybe FilePath -> FilePath -> IO String-readCabalFile hackageDbPath path-  | "cabal://" `isPrefixOf` path = do let pid p = fromJust $ simpleParse p-                                          packageName = drop 8 path-                                      case versionBranch $ pkgVersion $ pid packageName of-                                        [] -> do-                                          packageDescription <- DB.lookup packageName `fmap` maybe DB.readHackage DB.readHackage' hackageDbPath-                                          case packageDescription of-                                            Just d -> do-                                              let version = showVersion $ last $ DB.keys d-                                              readCabalFile Nothing $ hackagePath (pid $ packageName ++ "-" ++ version) Cabal-                                            Nothing -> error "No such package"-                                        _  -> readCabalFile Nothing $ hackagePath (pid packageName) Cabal-  | "http://"  `isPrefixOf` path = fetchUrl path-  | "file://"  `isPrefixOf` path = readCabalFile Nothing (drop 7 path)-  | otherwise                    = readFile path--fetchUrl :: String -> IO String-fetchUrl url = do-  (_,rsp) <- browse $ do-     setCheckForProxy True-     setDebugLog Nothing-     setOutHandler (\_ -> return ())-     request (getRequest url)-  return (rspBody rsp)
src/Cabal2Nix/Name.hs view
@@ -48,6 +48,7 @@ libNixName "gstreamer-plugins-base-0.10"        = return "gstreamer-plugins-base" libNixName "gthread-2.0"                        = return "glib" libNixName "gtk+-2.0"                           = return "gtk"+libNixName "gtk+-3.0"                           = return "gtk3" libNixName "gtkglext-1.0"                       = return "gtkglext" libNixName "gtksourceview-2.0"                  = return "gtksourceview" libNixName "icudata"                            = return "icu"
+ src/Cabal2Nix/Package.hs view
@@ -0,0 +1,144 @@+module Cabal2Nix.Package where++import Control.Applicative+import Control.Monad+import Control.Monad.IO.Class+import Control.Monad.Trans.Maybe+import Data.List ( isSuffixOf, isPrefixOf )+import Data.Maybe ( listToMaybe )+import Distribution.NixOS.Derivation.Cabal+import Distribution.NixOS.Fetch+import Distribution.Text ( simpleParse )+import System.Directory ( doesDirectoryExist, doesFileExist, createDirectoryIfMissing, getHomeDirectory, getDirectoryContents )+import System.Exit ( exitFailure )+import System.FilePath ( (</>), (<.>) )+import System.IO ( hPutStrLn, stderr, hPutStr )++import qualified Distribution.Hackage.DB as DB+import qualified Distribution.Package as Cabal+import qualified Distribution.PackageDescription as Cabal+import qualified Distribution.PackageDescription.Parse as Cabal+import qualified Distribution.ParseUtils as ParseUtils++import qualified Control.Exception as Exception++data Package = Package+  { source :: DerivationSource+  , cabal :: Cabal.GenericPackageDescription+  }++getPackage :: Maybe String -> Source -> IO Package+getPackage optHackageDB source = do+  (derivSource, pkgDesc) <- fetchOrFromDB optHackageDB source+  flip Package pkgDesc <$> maybe (sourceFromHackage (sourceHash source) $ showPackageIdentifier pkgDesc) return derivSource+++fetchOrFromDB :: Maybe String -> Source -> IO (Maybe DerivationSource, Cabal.GenericPackageDescription)+fetchOrFromDB optHackageDB src+  | "cabal://" `isPrefixOf` sourceUrl src = fmap ((,) Nothing) . fromDB optHackageDB . drop (length "cabal://") $ sourceUrl src+  | otherwise                             = do+    r <- fetch cabalFromPath src+    case r of+      Nothing ->+        hPutStrLn stderr "*** failed to fetch source. Does the URL exist?" >> exitFailure+      Just (derivSource, (externalSource, pkgDesc)) ->+        return (derivSource <$ guard externalSource, pkgDesc)++fromDB :: Maybe String -> String -> IO Cabal.GenericPackageDescription+fromDB optHackageDB pkg = do+  pkgDesc <- (lookupVersion <=< DB.lookup name) <$> maybe DB.readHackage DB.readHackage' optHackageDB+  case pkgDesc of+    Just r -> return r+    Nothing -> hPutStrLn stderr "*** no such package in the cabal database (did you run cabal update?). " >> exitFailure+ where+  Just pkgId = simpleParse pkg+  Cabal.PackageName name = Cabal.pkgName pkgId+  version = Cabal.pkgVersion pkgId++  lookupVersion :: DB.Map DB.Version Cabal.GenericPackageDescription -> Maybe Cabal.GenericPackageDescription+  lookupVersion+    | null (versionBranch version) = fmap snd . listToMaybe . reverse . DB.toAscList+    | otherwise                    = DB.lookup version+++readFileMay :: String -> IO (Maybe String)+readFileMay file = do+  e <- doesFileExist file+  if e+    then Just <$> readFile file+    else return Nothing++hashCachePath :: String -> IO String+hashCachePath pid = do+  home <- getHomeDirectory+  let cacheDir = home </> ".cache/cabal2nix"+  createDirectoryIfMissing True cacheDir+  return $ cacheDir </> pid <.> "sha256"++sourceFromHackage :: Maybe String -> String -> IO DerivationSource+sourceFromHackage optHash pkgId = do+  cacheFile <- hashCachePath pkgId+  let cachedHash = MaybeT $ maybe (readFileMay cacheFile) (return . Just) optHash+      url = "mirror://hackage/" ++ pkgId ++ ".tar.gz"++  -- Use the cached hash (either from cache file or given on cmdline via sha256 opt)+  -- if available, otherwise download from hackage to compute hash.+  maybeHash <- runMaybeT $ cachedHash <|> derivHash . fst <$> fetchWith (False, "url") (Source url "" Nothing)+  case maybeHash of+    Just hash ->+      -- We need to force the hash here. If we didn't do this, then when reading the+      -- hash from the cache file, the cache file will still be open for reading+      -- (because lazy io) when writeFile opens the file again for writing. By forcing+      -- the hash here, we ensure that the file is closed before opening it again.+      seq (length hash) $+      DerivationSource "url" url "" hash <$ writeFile cacheFile hash+    Nothing -> do+      hPutStr stderr $ unlines+        [ "*** cannot compute hash. (Not a hackage project?)"+        , " If your project is not on hackage, please supply the path to the root directory of"+        , " the project, not to the cabal file."+        , ""+        , " If your project is on hackage but you still want to specify the hash manually, you"+        , " can use the --sha256 option."+        ]+      exitFailure++showPackageIdentifier :: Cabal.GenericPackageDescription -> String+showPackageIdentifier pkgDesc = name ++ "-" ++ showVersion version where+  pkgId = Cabal.package . Cabal.packageDescription $ pkgDesc+  Cabal.PackageName name = Cabal.packageName pkgId+  version = Cabal.packageVersion pkgId++cabalFromPath :: FilePath -> MaybeT IO (Bool, Cabal.GenericPackageDescription)+cabalFromPath path = do+  d <- liftIO $ doesDirectoryExist path+  (,) d <$> if d+    then cabalFromDirectory  path+    else cabalFromFile False path++cabalFromDirectory :: FilePath -> MaybeT IO Cabal.GenericPackageDescription+cabalFromDirectory dir = do+  cabals <- liftIO $ getDirectoryContents dir >>= filterM doesFileExist . map (dir </>) . filter (".cabal" `isSuffixOf`)+  case cabals of+    [cabalFile] -> cabalFromFile True cabalFile+    _       -> liftIO $ hPutStrLn stderr "*** found zero or more than one cabal file. Exiting." >> exitFailure++handleIO :: (Exception.IOException -> IO a) -> IO a -> IO a+handleIO = Exception.handle++cabalFromFile :: Bool -> FilePath -> MaybeT IO Cabal.GenericPackageDescription+cabalFromFile failHard file =+  -- readFile throws an error if it's used on binary files which contain sequences+  -- that do not represent valid characters. To catch that exception, we need to+  -- wrap the whole block in `catchIO`, because of lazy IO. The `case` will force+  -- the reading of the file, so we will always catch the expression here.+  MaybeT $ handleIO (const $ return Nothing) $ do+    content <- readFile file+    case Cabal.parsePackageDescription content of+      Cabal.ParseFailed e | failHard -> do+        let (line, err) = ParseUtils.locatedErrorMsg e+            msg = maybe "" ((++ ": ") . show) line ++ err+        putStrLn $ "*** error parsing cabal file: " ++ msg+        exitFailure+      Cabal.ParseFailed _  -> return Nothing+      Cabal.ParseOk     _ a -> return (Just a)
src/Cabal2Nix/PostProcess.hs view
@@ -15,7 +15,9 @@   | pname == "alex" && version >= Version [3,1] []                                 = deriv { buildTools = "perl":"happy":buildTools }   | pname == "bindings-GLFW"    = deriv { extraLibs = "libXext":"libXfixes":extraLibs }+  | pname == "bits-extras"      = deriv { configureFlags = "--ghc-option=-lgcc_s":configureFlags, extraLibs = filter (/= "gcc_s") extraLibs }   | pname == "cabal2nix"        = deriv { doCheck = True, phaseOverrides = cabal2nixDoCheckHook }+  | pname == "cabal-bounds"      = deriv { buildTools = "cabalInstall":buildTools }   | pname == "cabal-install" && version >= Version [0,14] []                                 = deriv { phaseOverrides = cabalInstallPostInstall }   | pname == "cairo"            = deriv { extraLibs = "pkgconfig":"libc":"cairo":"zlib":extraLibs }@@ -43,6 +45,7 @@   | pname == "gtksourceview2"   = deriv { extraLibs = "pkgconfig":"libc":extraLibs }   | pname == "haddock" && version < Version [2,14] []                                 = deriv { buildTools = "alex":"happy":buildTools }+  | pname == "haddock"          = deriv { phaseOverrides = haddockPreCheck }   | pname == "happy"            = deriv { buildTools = "perl":buildTools }   | pname == "haskeline"        = deriv { buildDepends = "utf8String":buildDepends }   | pname == "haskell-src"      = deriv { buildTools = "happy":buildTools }@@ -95,7 +98,6 @@                                 = deriv { doCheck = True, phaseOverrides = sybDoCheck }   | pname == "tar"              = deriv { runHaddock = True, phaseOverrides = tarNoHaddock }   | pname == "terminfo"         = deriv { extraLibs = "ncurses":extraLibs }-  | pname == "text-icu"         = deriv { doCheck = True, phaseOverrides = textIcuDoCheckHook }   | pname == "threadscope"      = deriv { configureFlags = "--ghc-options=-rtsopts":configureFlags }   | pname == "thyme"            = deriv { buildTools = "cpphs":buildTools }   | pname == "transformers" && version >= Version [0,4,1] []@@ -257,9 +259,6 @@ cookieDoCheckHook :: String cookieDoCheckHook = "doCheck = self.stdenv.lib.versionOlder \"7.8\" self.ghc.version;" -textIcuDoCheckHook :: String-textIcuDoCheckHook = "doCheck = !self.stdenv.isDarwin;"- eitherNoHaddock :: String eitherNoHaddock = "noHaddock = self.stdenv.lib.versionOlder self.ghc.version \"7.6\";" @@ -302,3 +301,6 @@ sybDoCheck, splitDoCheck :: String sybDoCheck = "doCheck = self.stdenv.lib.versionOlder self.ghc.version \"7.9\";" splitDoCheck = sybDoCheck++haddockPreCheck :: String+haddockPreCheck = "preCheck = \"unset GHC_PACKAGE_PATH\";"
src/Distribution/NixOS/Derivation/Cabal.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE PatternGuards, CPP #-}+{-# LANGUAGE PatternGuards, RecordWildCards, CPP #-} {- |    Module      :  Distribution.NixOS.Derivation.Cabal    License     :  BSD3@@ -14,16 +14,18 @@ module Distribution.NixOS.Derivation.Cabal   ( Derivation(..)   , parseDerivation+  , DerivationSource(..)   , module Distribution.NixOS.Derivation.Meta   , module Data.Version   )   where  import Distribution.NixOS.Derivation.Meta+import Distribution.NixOS.Fetch import Distribution.NixOS.PrettyPrinting import Distribution.NixOS.Regex hiding ( empty )-import Distribution.Text import Distribution.Package+import Distribution.Text #ifdef __HADDOCK__ import Distribution.PackageDescription ( PackageDescription ) #endif@@ -39,11 +41,10 @@ -- -- Note that the "Text" instance definition provides pretty-printing, -- but no parsing as of now!- data Derivation = MkDerivation   { pname               :: String   , version             :: Version-  , sha256              :: String+  , src                 :: DerivationSource   , isLibrary           :: Bool   , isExecutable        :: Bool   , extraFunctionArgs   :: [String]@@ -82,7 +83,7 @@   , nest 2 $ vcat     [ attr "pname"   $ string (pname deriv)     , attr "version" $ doubleQuotes (disp (version deriv))-    , attr "sha256"  $ string (sha256 deriv)+    , sourceAttr (src deriv)     , boolattr "isLibrary" (not (isLibrary deriv) || isExecutable deriv) (isLibrary deriv)     , boolattr "isExecutable" (not (isLibrary deriv) || isExecutable deriv) (isExecutable deriv)     , listattr "buildDepends" empty (buildDepends deriv)@@ -106,20 +107,36 @@   where     inputs = nub $ sortBy (compare `on` map toLower) $ filter (/="cabal") $ filter (not . isPrefixOf "self.") $               buildDepends deriv ++ testDepends deriv ++ buildTools deriv ++ extraLibs deriv ++ pkgConfDeps deriv ++ extraFunctionArgs deriv+           ++ ["fetch" ++ derivKind (src deriv) | derivKind (src deriv) /= "" && not isHackagePackage]     renderedFlags =  [ text "-f" <> (if enable then empty else char '-') <> text f | (FlagName f, enable) <- cabalFlags deriv ]                   ++ map text (configureFlags deriv)+    isHackagePackage = "mirror://hackage/" `isPrefixOf` derivUrl (src deriv)+    sourceAttr (DerivationSource{..})+      | isHackagePackage = attr "sha256" $ string derivHash+      | derivKind /= "" = vcat+         [ text "src" <+> equals <+> text ("fetch" ++ derivKind) <+> lbrace+         , nest 2 $ vcat+           [ attr "url" $ string derivUrl+           , attr "sha256" $ string derivHash+           , if derivRevision /= "" then attr "rev" (string derivRevision) else empty+           ]+         , rbrace <> semi+         ]+      | otherwise = attr "src" $ text derivUrl  -- | A very incomplete parser that extracts 'pname', 'version', -- 'sha256', 'platforms', 'hydraPlatforms', 'maintainers', 'doCheck', -- 'jailbreak', and 'runHaddock' from the given Nix expression.- parseDerivation :: String -> Maybe Derivation parseDerivation buf   | buf =~ "cabal.mkDerivation"   , [name]    <- buf `regsubmatch` "pname *= *\"([^\"]+)\""   , [vers']   <- buf `regsubmatch` "version *= *\"([^\"]+)\""   , Just vers <- simpleParse vers'-  , [sha]     <- buf `regsubmatch` "sha256 *= *\"([^\"]+)\""+  , sha       <- buf `regsubmatch` "sha256 *= *\"([^\"]+)\""+  , sr        <- buf `regsubmatch` "src *= *([^;]+);"+  , url       <- buf `regsubmatch` "url *= *\"?([^;\"]+)\"? *;"+  , rev       <- buf `regsubmatch` "rev *= *\"([^\"]+)\""   , hplats    <- buf `regsubmatch` "hydraPlatforms *= *([^;]+);"   , plats     <- buf `regsubmatch` "platforms *= *([^;]+);"   , maint     <- buf `regsubmatch` "maintainers *= *(with [^;]+;)? \\[([^\"]+)]"@@ -131,7 +148,12 @@               = Just MkDerivation                   { pname          = name                   , version        = vers-                  , sha256         = sha+                  , src            = case (sr,url) of+                                       ([]   , _      ) -> DerivationSource "url" ("mirror://hackage/" ++ name ++ "-" ++ vers' ++ ".tar.gz") "" $ head sha+                                       (sr':_, []     ) -> DerivationSource "" sr' "" ""+                                       (sr':_, url':_ ) -> DerivationSource (drop (length "fetch") . head . words $ sr')+                                                                           url' (head $ rev ++ [""]) $ head sha+                   , isLibrary      = False                   , isExecutable   = False                   , extraFunctionArgs = []
+ src/Distribution/NixOS/Fetch.hs view
@@ -0,0 +1,133 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+module Distribution.NixOS.Fetch+  ( Source(..)+  , DerivationSource(..), fromDerivationSource+  , fetch+  , fetchWith+  ) where++import Control.Applicative+import Control.Monad+import Control.Monad.IO.Class+import Control.Monad.Trans.Maybe+import Data.Maybe+import System.Directory+import System.Environment+import System.Exit+import System.IO+import System.Process++-- | A source is a location from which we can fetch, such as a HTTP URL, a GIT URL, ....+data Source = Source+  { sourceUrl       :: String       -- ^ URL to fetch from.+  , sourceRevision  :: String       -- ^ Revision to use. For protocols where this doesn't make sense (such as HTTP), this+                                   --   should be the empty string.+  , sourceHash      :: Maybe String -- ^ The expected hash of the source, if available.+  } deriving (Show, Eq, Ord)++-- | A source for a derivation. It always needs a hash and also has a protocol attached to it (url, git, svn, ...).+-- A @DerivationSource@ also always has it's revision fully resolved (not relative revisions like @master@, @HEAD@, etc).+data DerivationSource = DerivationSource+  { derivKind     :: String -- ^ The kind of the source. The name of the build-support fetch derivation should be fetch<kind>.+  , derivUrl      :: String -- ^ URL to fetch from.+  , derivRevision :: String -- ^ Revision to use. Leave empty if the fetcher doesn't support revisions.+  , derivHash     :: String -- ^ The hash of the source.+  } deriving (Show, Eq, Ord)++fromDerivationSource :: DerivationSource -> Source+fromDerivationSource DerivationSource{..} = Source derivUrl derivRevision $ Just derivHash++-- | Fetch a source, trying any of the various nix-prefetch-* scripts.+fetch :: forall a. (String -> MaybeT IO a)      -- ^ This function is passed the output path name as an argument.+                                                -- It should return 'Nothing' if the file doesn't match the expected format.+                                                -- This is required, because we cannot always check if a download succeeded otherwise.+      -> Source                                 -- ^ The source to fetch from.+      -> IO (Maybe (DerivationSource, a))       -- ^ The derivation source and the result of the processing function. Returns Nothing if the download failed.+fetch f = runMaybeT . fetchers where+  fetchers :: Source -> MaybeT IO (DerivationSource, a)+  fetchers source = msum . (fetchLocal source :) $ map (\fetcher -> fetchWith fetcher source >>= process)+    [ (False, "zip")+    , (False, "url")+    , (True, "git")+    , (True, "hg")+    , (True, "svn")+    , (True, "bzr")+    ]++  fetchLocal :: Source -> MaybeT IO (DerivationSource, a)+  fetchLocal src = do+    let path = stripPrefix "file://" $ sourceUrl src+    existsFile <- liftIO $ doesFileExist path+    existsDir  <- liftIO $ doesDirectoryExist path+    guard $ existsDir || existsFile+    let path' | '/' `elem` path = path+              | otherwise       = "./" ++ path+    process (DerivationSource "" path' "" "", path') <|> localArchive path'++  localArchive :: FilePath -> MaybeT IO (DerivationSource, a)+  localArchive path = do+    absolutePath <- liftIO $ canonicalizePath path+    unpacked <- snd <$> fetchWith (False, "zip") (Source ("file://" ++ absolutePath) "" Nothing)+    process (DerivationSource "" absolutePath "" "", unpacked)++  process :: (DerivationSource, FilePath) -> MaybeT IO (DerivationSource, a)+  process (derivSource, file) = (,) derivSource <$> f file++-- | Like 'fetch', but allows to specify which script to use.+fetchWith :: (Bool, String) -> Source -> MaybeT IO (DerivationSource, FilePath)+fetchWith (supportsRev, kind) source = do+  unless ((sourceRevision source /= "") || isNothing (sourceHash source) || not supportsRev) $+    liftIO (hPutStrLn stderr "** need a revision for VCS when the hash is given. skipping.") >> mzero++  MaybeT $ liftIO $ do+    envs <- getEnvironment+    (Nothing, Just stdoutH, _, processH) <- createProcess (proc script args)+      { env = Just $ ("PRINT_PATH", "1") : envs+      , std_in = Inherit+      , std_err = Inherit+      , std_out = CreatePipe+      }++    exitCode <- waitForProcess processH+    case exitCode of+      ExitSuccess   -> fmap Just . processOutputSwitch . lines =<< hGetContents stdoutH+      ExitFailure _ -> return Nothing++ where++   script :: String+   script = "nix-prefetch-" ++ kind++   args :: [String]+   args = sourceUrl source : [ sourceRevision source | supportsRev ] ++ maybeToList (sourceHash source)++   processOutputWithRev :: [String] -> IO (DerivationSource, FilePath)+   processOutputWithRev [rev,hash,path] = return (DerivationSource kind (sourceUrl source) (extractRevision rev) hash, path)+   processOutputWithRev out = unexpectedOutput out++   extractRevision :: String -> String+   extractRevision = unwords . drop 3 . words -- drop the "<vcs> revision is" prefix++   processOutput :: [String] -> IO (DerivationSource, FilePath)+   processOutput [hash,path] = return (DerivationSource kind (sourceUrl source) (sourceRevision source) hash, path)+   processOutput out = unexpectedOutput out++   unexpectedOutput :: [String] -> IO a+   unexpectedOutput out = do+     hPutStrLn stderr $ "*** unexpected output from " ++ "nix-prefetch-" ++ kind ++ " script: "+     hPutStr   stderr $ unlines out+     exitFailure++   processOutputSwitch :: [String] -> IO (DerivationSource, FilePath)+   processOutputSwitch+     | supportsRev && isNothing (sourceHash source) = processOutputWithRev+     | otherwise                                   = processOutput++stripPrefix :: Eq a => [a] -> [a] -> [a]+stripPrefix prefix as+  | prefix' == prefix = stripped+  | otherwise = as+ where+  (prefix', stripped) = splitAt (length prefix) as
src/cabal2nix.hs view
@@ -1,14 +1,13 @@ module Main ( main ) where -import Cabal2Nix.Hackage ( hashPackage, readCabalFile ) import Cabal2Nix.Generate ( cabal2nix ) import Cabal2Nix.Normalize ( normalize )+import Cabal2Nix.Package import Distribution.NixOS.Derivation.Cabal+import Distribution.NixOS.Fetch  import Control.Exception ( bracket ) import Control.Monad ( when )-import Distribution.PackageDescription ( package, packageDescription )-import Distribution.PackageDescription.Parse ( parsePackageDescription, ParseResult(..) ) import Distribution.Text ( disp ) import System.Console.GetOpt ( OptDescr(..), ArgDescr(..), ArgOrder(..), usageInfo, getOpt ) import System.Environment ( getArgs )@@ -24,6 +23,7 @@   , optHaddock :: Bool   , optDoCheck :: Bool   , optJailbreak :: Bool+  , optRevision :: String   , optHyperlinkSource :: Bool   , optHackageDb :: Maybe FilePath   }@@ -39,6 +39,7 @@   , optHaddock = True   , optDoCheck = True   , optJailbreak = False+  , optRevision = ""   , optHyperlinkSource = True   , optHackageDb = Nothing   }@@ -53,19 +54,25 @@   , Option ""  ["jailbreak"]  (NoArg (\o -> o { optJailbreak = True }))                                  "don't honor version restrictions on build inputs"   , Option ""  ["no-haddock"] (NoArg (\o -> o { optHaddock = False }))                                   "don't run Haddock when building this package"   , Option ""  ["no-check"]   (NoArg (\o -> o { optDoCheck = False }))                                   "don't run regression test suites of this package"+  , Option ""  ["rev"]        (ReqArg (\x o -> o { optRevision = x }) "REVISION")                        "revision, only used when fetching from VCS"   , Option ""  ["no-hyperlink-source"] (NoArg (\o -> o { optHyperlinkSource = False }))                  "don't add pretty-printed source code to the documentation"   ]  usage :: String-usage = usageInfo "Usage: cabal2nix [options] url-to-cabal-file" options ++ unlines+usage = usageInfo "Usage: cabal2nix [options] URI" options ++ unlines         [ ""         , "Recognized URI schemes:"         , ""-        , "  cabal://pkgname-pkgversion       download the specified package from Hackage"-        , "  cabal://pkgname                  download latest version of the specified package from Hackage"-        , "  http://host/path                 fetch the Cabal file via HTTP"-        , "  file:///local/path               load the Cabal file from the local disk"-        , "  /local/path                      abbreviated version of file URI"+        , "  cabal://pkgname-pkgversion     download the specified package from Hackage"+        , "  cabal://pkgname                download latest version of the specified package from Hackage"+        , "  file:///local/path             load the Cabal file from the local disk"+        , "  /local/path                    abbreviated version of file URI"+        , "  <git/svn/bzr/hg URL>           download the source from the specified repository"+        , ""+        , "If the URI refers to a cabal file, information for building the package will be retrieved from that file, but "+        , "hackage will be used as a source for the derivation."+        , "Otherwise, the supplied URI will be used to as the source for the derivation and the information is taken"+        , "from the cabal file at the root of the downloaded source."         ]  cmdlineError :: String -> IO a@@ -82,19 +89,9 @@   when (optPrintHelp cfg) (putStr usage >> exitSuccess)   when (length args /= 1) (cmdlineError "*** exactly one url-to-cabal-file must be specified\n") -  cabal' <- fmap parsePackageDescription (readCabalFile (optHackageDb cfg) (head args))-  cabal <- case cabal' of-             ParseOk _ a -> return a-             ParseFailed err -> do-               hPutStrLn stderr ("*** cannot parse cabal file: " ++ show err)-               exitFailure--  let packageId = package (packageDescription cabal)-  sha <- case optSha256 cfg of-              Just hash -> return hash-              Nothing -> hashPackage packageId+  pkg <- getPackage (optHackageDb cfg) $ Source (head args) (optRevision cfg) (optSha256 cfg) -  let deriv  = (cabal2nix cabal) { sha256 = sha, runHaddock = optHaddock cfg, jailbreak = optJailbreak cfg }+  let deriv  = (cabal2nix $ cabal pkg) { src = source pkg, runHaddock = optHaddock cfg, jailbreak = optJailbreak cfg }       deriv' = deriv { metaSection = (metaSection deriv)                                      { maintainers = optMaintainer cfg                                      , platforms   = optPlatform cfg
src/hackage4nix.hs view
@@ -1,7 +1,7 @@ module Main ( main ) where -import Cabal2Nix.Normalize import Cabal2Nix.Generate+import Cabal2Nix.Normalize import Control.Exception ( bracket ) import Control.Monad.RWS import Data.List@@ -111,6 +111,7 @@       nix' <- io (readFile file) >>= parseNixFile file       flip (maybe (return ())) nix' $ \nix ->         modify (Set.insert nix)+   -- Re-generate all Haskell packages in-place (unless   -- 'regenerateDerivation' decided that this particular package   -- shouldn't be touched.@@ -124,10 +125,11 @@         maints = maintainers (metaSection deriv)         plats  = platforms (metaSection deriv)         hplats = if isLatest deriv then hydraPlatforms (metaSection deriv) else ["self.stdenv.lib.platforms.none"]+     when regenerate $ do       msgDebug ("re-generate " ++ path)       pkg <- getCabalPackage (pname deriv) (version deriv)-      let deriv'  = (cabal2nix pkg) { sha256 = sha256 deriv+      let deriv'  = (cabal2nix pkg) { src = src deriv                                     , runHaddock = runHaddock deriv                                     , doCheck = doCheck deriv                                     , jailbreak = jailbreak deriv@@ -143,6 +145,7 @@                                            }                            }       io $ writeFile path (show (disp (normalize deriv'')))+   -- Discover available updates and print the appropriate cabal2nix   -- command line to the console for the user to execute at his/her   -- discretion.
test/doc-test.hs view
@@ -20,13 +20,14 @@              , "src/Cabal2Nix/CorePackages.hs"              , "src/Cabal2Nix/Flags.hs"              , "src/Cabal2Nix/Generate.hs"-             , "src/Cabal2Nix/Hackage.hs"+             , "src/Cabal2Nix/Package.hs"              , "src/Cabal2Nix/Normalize.hs"              , "src/Cabal2Nix/Name.hs"              , "src/Cabal2Nix/PostProcess.hs"              , "src/Distribution/NixOS/Derivation/License.hs"              , "src/Distribution/NixOS/Derivation/Cabal.hs"              , "src/Distribution/NixOS/Derivation/Meta.hs"+             , "src/Distribution/NixOS/Fetch.hs"              , "src/Distribution/NixOS/PrettyPrinting.hs"              , "src/Distribution/NixOS/Regex.hs"              ]