packages feed

cabal2nix 1.52 → 1.53

raw patch · 10 files changed

+352/−328 lines, 10 files

Files

cabal2nix.cabal view
@@ -1,5 +1,5 @@ Name:                   cabal2nix-Version:                1.52+Version:                1.53 Copyright:              Peter Simons, Andres Loeh License:                BSD3 License-File:           LICENSE@@ -34,8 +34,8 @@   .   The only required argument is the path to the cabal file. For example:   .-  > cabal2nix http://hackage.haskell.org/packages/archive/cabal2nix/1.52/cabal2nix.cabal-  > cabal2nix cabal://cabal2nix-1.52+  > cabal2nix http://hackage.haskell.org/packages/archive/cabal2nix/1.53/cabal2nix.cabal+  > cabal2nix cabal://cabal2nix-1.53   .   If the @--sha256@ option has not been specified, cabal2nix calls   @nix-prefetch-url@ to determine the hash automatically. This causes@@ -46,7 +46,7 @@   Location:             git://github.com/NixOS/cabal2nix.git  Executable cabal2nix-  main-is:              Cabal2Nix.hs+  main-is:              cabal2nix.hs   hs-source-dirs:       src   Build-Depends:        base >= 3 && < 5, regex-posix, pretty, Cabal >= 1.10,                         filepath, directory, process, HTTP, hackage-db@@ -66,7 +66,7 @@                         Distribution.NixOS.PrettyPrinting  Executable hackage4nix-  main-is:              Hackage4Nix.hs+  main-is:              hackage4nix.hs   hs-source-dirs:       src   Build-Depends:        base >= 3 && < 5, regex-posix, pretty, Cabal >= 1.10,                         mtl, containers, directory, filepath, hackage-db
− src/Cabal2Nix.hs
@@ -1,97 +0,0 @@-module Main ( main ) where--import Cabal2Nix.Hackage ( hashPackage, readCabalFile )-import Cabal2Nix.Generate ( cabal2nix )-import Cabal2Nix.Normalize ( normalize )-import Distribution.NixOS.Derivation.Cabal--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 )-import System.Exit ( exitFailure, exitSuccess )-import System.IO ( hPutStrLn, hFlush, stdout, stderr )--data Configuration = Configuration-  { optPrintHelp :: Bool-  , optPrintVersion :: Bool-  , optSha256 :: String-  , optMaintainer :: [String]-  , optPlatform :: [String]-  , optHaddock :: Bool-  , optDoCheck :: Bool-  , optJailbreak :: Bool-  }-  deriving (Show)--defaultConfiguration :: Configuration-defaultConfiguration = Configuration-  { optPrintHelp = False-  , optPrintVersion = False-  , optSha256 = ""-  , optMaintainer = []-  , optPlatform = []-  , optHaddock = True-  , optDoCheck = True-  , optJailbreak = False-  }--options :: [OptDescr (Configuration -> Configuration)]-options =-  [ Option "h" ["help"]       (NoArg (\o -> o { optPrintHelp = True }))                                  "show this help text"-  , Option ""  ["sha256"]     (ReqArg (\x o -> o { optSha256 = x }) "HASH")                              "sha256 hash of source tarball"-  , Option "m" ["maintainer"] (ReqArg (\x o -> o { optMaintainer = x : optMaintainer o }) "MAINTAINER")  "maintainer of this package (may be specified multiple times)"-  , Option "p" ["platform"]   (ReqArg (\x o -> o { optPlatform = x : optPlatform o }) "PLATFORM")        "supported build platforms (may be specified multiple times)"-  , 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 ""  ["jailbreak"]  (NoArg (\o -> o { optJailbreak = True }))                                  "don't honor version restrictions on build inputs"-  ]--usage :: String-usage = usageInfo "Usage: cabal2nix [options] url-to-cabal-file" 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"-        ]--cmdlineError :: String -> IO a-cmdlineError ""     = hPutStrLn stderr usage >> exitFailure-cmdlineError errMsg = hPutStrLn stderr errMsg >> cmdlineError ""--main :: IO ()-main = bracket (return ()) (\() -> hFlush stdout >> hFlush stderr) $ \() -> do-  args' <- getArgs-  (cfg,args) <- case getOpt Permute options args' of-                  (o,n,[]  ) -> return (foldl (flip ($)) defaultConfiguration o,n)-                  (_,_,errs) -> cmdlineError (concatMap ("*** "++) errs)--  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 (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 <- if null (optSha256 cfg) then hashPackage packageId else return (optSha256 cfg)--  let deriv  = (cabal2nix cabal) { sha256 = sha, runHaddock = optHaddock cfg, jailbreak = optJailbreak cfg }-      deriv' = deriv { metaSection = (metaSection deriv)-                                     { maintainers = optMaintainer cfg-                                     , platforms   = optPlatform cfg-                                     }-                     , doCheck = doCheck deriv && optDoCheck cfg-                     }--  putStr (show (disp (normalize deriv')))
src/Cabal2Nix/Flags.hs view
@@ -8,7 +8,7 @@  | name == "accelerate-examples"= [disable "opencl"]  | name == "pandoc"             = [enable "blaze_html_0_5"]  | name == "git-annex"          = [ enable "S3", enable "WebDAV", enable "Inotify"-                                  , enable "Dbus", disable "Assistant", disable "Webapp"+                                  , enable "Dbus", enable "Assistant", enable "Webapp"                                   , enable "Pairing", enable "XMPP", enable "DNS"                                   , enable "Production", enable "TDFA"]  | name == "haskeline"          = [enable "terminfo"]
src/Cabal2Nix/Name.hs view
@@ -24,6 +24,7 @@ libNixName "cairo"              = return "cairo" libNixName "cairo-svg"          = return "cairo" libNixName "crypto"             = return "openssl"+libNixName "gconf-2.0"          = return "gconf" libNixName "gio-2.0"            = return "glib" libNixName "glib-2.0"           = return "glib" libNixName "GL"                 = return "mesa"@@ -32,8 +33,16 @@ libNixName "gnome-keyring-1"    = return "gnome_keyring" libNixName "gnome-keyring"      = return "gnome_keyring" libNixName "gobject-2.0"        = return "glib"+libNixName "gstreamer-0.10"     = return "gstreamer"+libNixName "gstreamer-audio-0.10"               = return "gstreamer-audio"+libNixName "gstreamer-base-0.10"                = return "gstreamer-base"+libNixName "gstreamer-controller-0.10"          = return "gstreamer-controller"+libNixName "gstreamer-dataprotocol-0.10"        = return "gstreamer-dataprotocol"+libNixName "gstreamer-net-0.10"                 = return "gstreamer-net"+libNixName "gstreamer-plugins-base-0.10"        = return "gstreamer-plugins-base" libNixName "gthread-2.0"        = return "glib" libNixName "gtk+-2.0"           = return "gtk"+libNixName "gtkglext-1.0"       = return "gtkglext" libNixName "gtksourceview-2.0"  = return "gtksourceview" libNixName "icudata"            = return "icu" libNixName "icui18n"            = return "icu"@@ -51,10 +60,12 @@ libNixName "pcre"               = return "pcre" libNixName "png"                = return "libpng" libNixName "pq"                 = return "postgresql"+libNixName "pthread"            = [] libNixName "sndfile"            = return "libsndfile" libNixName "sqlite3"            = return "sqlite" libNixName "ssl"                = return "openssl" libNixName "stdc++"             = []  -- in stdenv+libNixName "webkit-1.0"         = return "webkit" libNixName "X11"                = return "libX11" libNixName "Xext"               = return "libXext" libNixName "xft"                = return "libXft"
src/Cabal2Nix/Normalize.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE RecordWildCards #-} -module Cabal2Nix.Normalize ( normalize ) where+module Cabal2Nix.Normalize ( normalize, normalizeList ) where  import Distribution.NixOS.Derivation.Cabal import Cabal2Nix.Name
src/Cabal2Nix/PostProcess.hs view
@@ -15,8 +15,10 @@   | pname == "darcs"            = deriv { phaseOverrides = darcsInstallPostInstall }   | pname == "editline"         = deriv { extraLibs = "libedit":extraLibs }   | pname == "epic"             = deriv { extraLibs = "gmp":"boehmgc":extraLibs, buildTools = "happy":buildTools }+  | pname == "ghc-heap-view"    = deriv { phaseOverrides = ghciPostInstall }   | pname == "ghc-mod"          = deriv { phaseOverrides = ghcModPostInstall, buildTools = "emacs":buildTools }-  | pname == "git-annex"        = deriv { phaseOverrides = gitAnnexOverrides, buildTools = "git":"rsync":"gnupg1":"curl":"lsof":"openssh":"bup":buildTools }+  | pname == "ghc-vis"          = deriv { phaseOverrides = ghciPostInstall }+  | pname == "git-annex"        = deriv { phaseOverrides = gitAnnexOverrides, buildTools = "git":"rsync":"gnupg1":"curl":"lsof":"openssh":"which":"bup":buildTools }   | pname == "glade"            = deriv { extraLibs = "pkgconfig":"libc":extraLibs, pkgConfDeps = "gtkC":delete "gtk" pkgConfDeps }   | pname == "glib"             = deriv { extraLibs = "pkgconfig":"libc":extraLibs }   | pname == "GLUT"             = deriv { extraLibs = "glut":"libSM":"libICE":"libXmu":"libXi":"mesa":extraLibs }@@ -31,7 +33,9 @@   | pname == "highlighting-kate"= highlightingKatePostProcessing deriv   | pname == "hmatrix"          = deriv { extraLibs = "gsl":"liblapack":"blas":extraLibs }   | pname == "hspec"            = deriv { doCheck = False }-  | pname == "idris"            = deriv { buildTools = "happy":buildTools, extraLibs = "gmp":extraLibs }+  | pname == "idris"            = if version < Version [0,9,9] []+                                    then deriv { buildTools = "happy":buildTools, extraLibs = "gmp":extraLibs }+                                    else deriv { buildTools = "happy":buildTools, extraLibs = "gmp":"boehmgc":extraLibs }   | pname == "language-c-quote" = deriv { buildTools = "alex":"happy":buildTools }   | pname == "language-java"    = deriv { buildDepends = "syb":buildDepends }   | pname == "leksah-server"    = deriv { buildDepends = "process-leksah":buildDepends }@@ -141,10 +145,19 @@ gitAnnexOverrides :: String gitAnnexOverrides = unlines   [ "preConfigure = \"patchShebangs .\";"+  , "installPhase = \"make PREFIX=$out CABAL=./Setup docs install\";"   , "checkPhase = ''"   , "  export HOME=\"$NIX_BUILD_TOP/tmp\""   , "  mkdir \"$HOME\""   , "  cp dist/build/git-annex/git-annex git-annex"   , "  ./git-annex test"   , "'';"+  ]++ghciPostInstall :: String+ghciPostInstall = unlines+  [ "postInstall = ''"+  , "    ensureDir \"$out/share/ghci\""+  , "    ln -s \"$out/share/$pname-$version/ghci\" \"$out/share/ghci/$pname\""+  , "  '';"   ]
− src/Hackage4Nix.hs
@@ -1,220 +0,0 @@-module Main ( main ) where--import Cabal2Nix.Normalize-import Cabal2Nix.Generate-import Control.Exception ( bracket )-import Control.Monad.RWS-import Data.List-import qualified Data.Set as Set-import Data.Version-import qualified Distribution.Hackage.DB as DB-import Distribution.NixOS.Derivation.Cabal-import Distribution.Package-import Distribution.PackageDescription ( GenericPackageDescription() )-import Distribution.Text-import System.Console.GetOpt ( OptDescr(..), ArgDescr(..), ArgOrder(..), usageInfo, getOpt )-import System.Directory-import System.Environment-import System.Exit-import System.FilePath-import System.IO-import Text.Regex.Posix--data Pkg = Pkg Derivation FilePath Bool-  deriving (Show, Eq, Ord)--type PkgSet = Set.Set Pkg--data Configuration = Configuration-  { _msgDebug  :: String -> IO ()-  , _msgInfo   :: String -> IO ()-  , _hackageDb :: DB.Hackage-  }--defaultConfiguration :: Configuration-defaultConfiguration = Configuration-  { _msgDebug  = hPutStrLn stderr-  , _msgInfo   = hPutStrLn stderr-  , _hackageDb = DB.empty-  }--type Hackage4Nix a = RWST Configuration () PkgSet IO a--io :: (MonadIO m) => IO a -> m a-io = liftIO--readDirectory :: FilePath -> IO [FilePath]-readDirectory dirpath = do-  entries <- getDirectoryContents dirpath-  return [ x | x <- entries, x /= ".", x /= ".." ]--msgDebug, msgInfo :: String -> Hackage4Nix ()-msgDebug msg = ask >>= \s -> io (_msgDebug s msg)-msgInfo msg = ask >>= \s -> io (_msgInfo s msg)--getCabalPackage :: String -> Version -> Hackage4Nix GenericPackageDescription-getCabalPackage name vers = do-  db <- asks _hackageDb-  case DB.lookup name db of-    Just db' -> case DB.lookup vers db' of-                  Just pkg -> return pkg-                  Nothing  -> fail ("hackage doesn't know about " ++ name ++ " version " ++ display vers)-    Nothing  -> fail ("hackage doesn't know about " ++ show name)--discoverNixFiles :: (FilePath -> Hackage4Nix ()) -> FilePath -> Hackage4Nix ()-discoverNixFiles yield dirOrFile = do-  isFile <- io (doesFileExist dirOrFile)-  case (isFile, takeExtension dirOrFile) of-    (True,".nix") -> yield dirOrFile-    (True,_)     -> return ()-    (False,_)    -> io (readDirectory dirOrFile) >>= mapM_ (discoverNixFiles yield . (dirOrFile </>))--regenerateDerivation :: Derivation -> String -> Bool-regenerateDerivation _ buf = not (buf =~ "(pre|post)Configure|(pre|post)Install|patchPhase|patches")--parseNixFile :: FilePath -> String -> Hackage4Nix (Maybe Pkg)-parseNixFile path buf-  | not (buf =~ "cabal.mkDerivation")-               = msgDebug ("ignore non-cabal package " ++ path) >> return Nothing-  | any (`isSuffixOf`path) badPackagePaths-               = msgDebug ("ignore known bad package " ++ path) >> return Nothing-  | Just deriv <- parseDerivation buf-               = return (Just (Pkg deriv path (regenerateDerivation deriv buf)))-  | otherwise = msgInfo ("failed to parse file " ++ path) >> return Nothing--selectLatestVersions :: PkgSet -> PkgSet-selectLatestVersions = Set.fromList . nubBy f2 . sortBy f1 . Set.toList-  where-    f1 (Pkg deriv1 _ _) (Pkg deriv2 _ _)-      | pname deriv1 == pname deriv2     = compare (version deriv2) (version deriv1)-      | otherwise                        = compare (pname deriv1) (pname deriv2)-    f2 (Pkg deriv1 _ _) (Pkg deriv2 _ _) = pname deriv1 == pname deriv2--discoverUpdates :: String -> Version -> Hackage4Nix [Version]-discoverUpdates name vers = do-  db <- asks _hackageDb-  case DB.lookup name db of-    Just pkgs -> return (filter (>vers) (DB.keys pkgs))-    Nothing   -> fail ("discoverUpdates cannot find package " ++ show name ++ " on Hackage")--updateNixPkgs :: [FilePath] -> Hackage4Nix ()-updateNixPkgs paths = do-  msgDebug $ "updating = " ++ show paths-  forM_ paths $ \fileOrDir ->-    flip discoverNixFiles fileOrDir $ \file -> do-      nix' <- io (readFile file) >>= parseNixFile file-      flip (maybe (return ())) nix' $ \nix -> do-        let Pkg deriv path regenerate = nix-            maints = maintainers (metaSection deriv)-            plats  = platforms (metaSection deriv)-        modify (Set.insert nix)-        when regenerate $ do-          msgDebug ("re-generate " ++ path)-          pkg <- getCabalPackage (pname deriv) (version deriv)-          let deriv'  = (cabal2nix pkg) { sha256 = sha256 deriv-                                        , runHaddock = runHaddock deriv-                                        , doCheck = doCheck deriv-                                        , jailbreak = jailbreak deriv-                                        }-              meta    = metaSection deriv'-              plats'  = if null plats then platforms meta else plats-              deriv'' = deriv' { metaSection = meta-                                               { maintainers = maints -- ++ ["andres","simons"]-                                               , platforms   = plats'-                                               }-                               }-          io $ writeFile path (show (disp (normalize deriv'')))-  pkgset <- gets selectLatestVersions-  updates' <- forM (Set.elems pkgset) $ \pkg -> do-    let Pkg deriv _ _ = pkg-    updates <- discoverUpdates (pname deriv) (version deriv)-    return (pkg,updates)-  let updates = [ u | u@(_,_:_) <- updates' ]-  unless (null updates) $ do-    msgInfo "The following updates are available:"-    forM_ updates $ \(pkg,versions) -> do-      let Pkg deriv path regenerate = pkg-      msgInfo ""-      msgInfo $ display (packageId deriv) ++ ":"-      forM_ versions $ \newVersion -> do-        let deriv' = deriv { version = newVersion }-        msgInfo $ "  " ++ genCabal2NixCmdline (Pkg deriv' path regenerate)-  return ()--genCabal2NixCmdline :: Pkg -> String-genCabal2NixCmdline (Pkg deriv path _) = unwords $ ["cabal2nix"] ++ opts ++ ['>':path']-  where-    meta = metaSection deriv-    opts = [cabal] ++ maints' ++ plats'-                   ++ (if jailbreak deriv then ["--jailbreak"] else [])-                   ++ (if runHaddock deriv then [] else ["--no-haddock"])-                   ++ (if doCheck deriv then [] else ["--no-check"])-    cabal = "cabal://" ++ display (packageId deriv)-    maints' = [ "--maintainer=" ++ normalizeMaintainer m | m <- maintainers meta ]-    plats'-      | ["self.ghc.meta.platforms"] == platforms meta = []-      | otherwise                                     = [ "--platform=" ++ p | p <- platforms meta ]-    path'-      | path =~ "/[0-9\\.]+\\.nix$" = replaceFileName path (display (version deriv) <.> "nix")-      | otherwise                   = path--normalizeMaintainer :: String -> String-normalizeMaintainer x-  | "self.stdenv.lib.maintainers." `isPrefixOf` x = drop 28 x-  | otherwise                                     = x--data CliOption = PrintHelp | Verbose-  deriving (Eq)--main :: IO ()-main = bracket (return ()) (\() -> hFlush stdout >> hFlush stderr) $ \() -> do-  let options :: [OptDescr CliOption]-      options =-        [ Option "h" ["help"]     (NoArg PrintHelp)                 "show this help text"-        , Option "v" ["verbose"]  (NoArg Verbose)                   "enable noisy debug output"-        ]--      usage :: String-      usage = usageInfo "Usage: hackage4nix [options] [dir-or-file ...]" options ++ unlines-              [ ""-              , "The purpose of 'hackage4nix' is to keep all Haskell packages in our"-              , "repository packages up-to-date. It scans a checked-out copy of"-              , "Nixpkgs for expressions that use 'cabal.mkDerivation', and"-              , "re-generates them in-place with cabal2nix."-              ]--      cmdlineError :: String -> IO a-      cmdlineError ""     = hPutStrLn stderr usage >> exitFailure-      cmdlineError errMsg = hPutStrLn stderr errMsg >> cmdlineError ""--  args' <- getArgs-  (opts,args) <- case getOpt Permute options args' of-     (o,n,[]  ) -> return (o,n)-     (_,_,errs) -> cmdlineError (concatMap (\e -> '*':'*':'*':' ':e) errs)--  when (PrintHelp `elem` opts) (cmdlineError "")--  hackage <- DB.readHackage-  let cfg = defaultConfiguration-            { _msgDebug  = if Verbose `elem` opts then _msgDebug defaultConfiguration else const (return ())-            , _hackageDb = hackage-            }-  ((),_,()) <- runRWST (updateNixPkgs args) cfg Set.empty-  return ()----- Packages that cabal2nix cannot generate build expressions for.--badPackagePaths :: [FilePath]-badPackagePaths = [ -- These expression are not found on Hackage:-                    "haskell-platform/2009.2.0.2.nix", "haskell-platform/2010.1.0.0.nix"-                  , "haskell-platform/2010.2.0.0.nix", "haskell-platform/2011.2.0.0.nix"-                  , "haskell-platform/2011.2.0.1.nix", "haskell-platform/2011.4.0.0.nix"-                  , "haskell-platform/2012.2.0.0.nix", "haskell-platform/2012.4.0.0.nix"-                  , "haskell-platform/2013.2.0.0.nix", "compilers/flapjax/default.nix"-                  , "pkgs/games/uqm/3dovideo.nix"-                    -- Our primitive parser cannot handle these files.-                  , "top-level/all-packages.nix", "top-level/haskell-packages.nix"-                    -- This build is way too complicated to maintain it automatically.-                  , "pkgs/development/compilers/pakcs/default.nix"-                  ]
+ src/cabal2nix.hs view
@@ -0,0 +1,97 @@+module Main ( main ) where++import Cabal2Nix.Hackage ( hashPackage, readCabalFile )+import Cabal2Nix.Generate ( cabal2nix )+import Cabal2Nix.Normalize ( normalize )+import Distribution.NixOS.Derivation.Cabal++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 )+import System.Exit ( exitFailure, exitSuccess )+import System.IO ( hPutStrLn, hFlush, stdout, stderr )++data Configuration = Configuration+  { optPrintHelp :: Bool+  , optPrintVersion :: Bool+  , optSha256 :: String+  , optMaintainer :: [String]+  , optPlatform :: [String]+  , optHaddock :: Bool+  , optDoCheck :: Bool+  , optJailbreak :: Bool+  }+  deriving (Show)++defaultConfiguration :: Configuration+defaultConfiguration = Configuration+  { optPrintHelp = False+  , optPrintVersion = False+  , optSha256 = ""+  , optMaintainer = []+  , optPlatform = []+  , optHaddock = True+  , optDoCheck = True+  , optJailbreak = False+  }++options :: [OptDescr (Configuration -> Configuration)]+options =+  [ Option "h" ["help"]       (NoArg (\o -> o { optPrintHelp = True }))                                  "show this help text"+  , Option ""  ["sha256"]     (ReqArg (\x o -> o { optSha256 = x }) "HASH")                              "sha256 hash of source tarball"+  , Option "m" ["maintainer"] (ReqArg (\x o -> o { optMaintainer = x : optMaintainer o }) "MAINTAINER")  "maintainer of this package (may be specified multiple times)"+  , Option "p" ["platform"]   (ReqArg (\x o -> o { optPlatform = x : optPlatform o }) "PLATFORM")        "supported build platforms (may be specified multiple times)"+  , 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 ""  ["jailbreak"]  (NoArg (\o -> o { optJailbreak = True }))                                  "don't honor version restrictions on build inputs"+  ]++usage :: String+usage = usageInfo "Usage: cabal2nix [options] url-to-cabal-file" 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"+        ]++cmdlineError :: String -> IO a+cmdlineError ""     = hPutStrLn stderr usage >> exitFailure+cmdlineError errMsg = hPutStrLn stderr errMsg >> cmdlineError ""++main :: IO ()+main = bracket (return ()) (\() -> hFlush stdout >> hFlush stderr) $ \() -> do+  args' <- getArgs+  (cfg,args) <- case getOpt Permute options args' of+                  (o,n,[]  ) -> return (foldl (flip ($)) defaultConfiguration o,n)+                  (_,_,errs) -> cmdlineError (concatMap ("*** "++) errs)++  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 (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 <- if null (optSha256 cfg) then hashPackage packageId else return (optSha256 cfg)++  let deriv  = (cabal2nix cabal) { sha256 = sha, runHaddock = optHaddock cfg, jailbreak = optJailbreak cfg }+      deriv' = deriv { metaSection = (metaSection deriv)+                                     { maintainers = optMaintainer cfg+                                     , platforms   = optPlatform cfg+                                     }+                     , doCheck = doCheck deriv && optDoCheck cfg+                     }++  putStr (show (disp (normalize deriv')))
+ src/hackage4nix.hs view
@@ -0,0 +1,220 @@+module Main ( main ) where++import Cabal2Nix.Normalize+import Cabal2Nix.Generate+import Control.Exception ( bracket )+import Control.Monad.RWS+import Data.List+import qualified Data.Set as Set+import Data.Version+import qualified Distribution.Hackage.DB as DB+import Distribution.NixOS.Derivation.Cabal+import Distribution.Package+import Distribution.PackageDescription ( GenericPackageDescription() )+import Distribution.Text+import System.Console.GetOpt ( OptDescr(..), ArgDescr(..), ArgOrder(..), usageInfo, getOpt )+import System.Directory+import System.Environment+import System.Exit+import System.FilePath+import System.IO+import Text.Regex.Posix++data Pkg = Pkg Derivation FilePath Bool+  deriving (Show, Eq, Ord)++type PkgSet = Set.Set Pkg++data Configuration = Configuration+  { _msgDebug  :: String -> IO ()+  , _msgInfo   :: String -> IO ()+  , _hackageDb :: DB.Hackage+  }++defaultConfiguration :: Configuration+defaultConfiguration = Configuration+  { _msgDebug  = hPutStrLn stderr+  , _msgInfo   = hPutStrLn stderr+  , _hackageDb = DB.empty+  }++type Hackage4Nix a = RWST Configuration () PkgSet IO a++io :: (MonadIO m) => IO a -> m a+io = liftIO++readDirectory :: FilePath -> IO [FilePath]+readDirectory dirpath = do+  entries <- getDirectoryContents dirpath+  return [ x | x <- entries, x /= ".", x /= ".." ]++msgDebug, msgInfo :: String -> Hackage4Nix ()+msgDebug msg = ask >>= \s -> io (_msgDebug s msg)+msgInfo msg = ask >>= \s -> io (_msgInfo s msg)++getCabalPackage :: String -> Version -> Hackage4Nix GenericPackageDescription+getCabalPackage name vers = do+  db <- asks _hackageDb+  case DB.lookup name db of+    Just db' -> case DB.lookup vers db' of+                  Just pkg -> return pkg+                  Nothing  -> fail ("hackage doesn't know about " ++ name ++ " version " ++ display vers)+    Nothing  -> fail ("hackage doesn't know about " ++ show name)++discoverNixFiles :: (FilePath -> Hackage4Nix ()) -> FilePath -> Hackage4Nix ()+discoverNixFiles yield dirOrFile = do+  isFile <- io (doesFileExist dirOrFile)+  case (isFile, takeExtension dirOrFile) of+    (True,".nix") -> yield dirOrFile+    (True,_)     -> return ()+    (False,_)    -> io (readDirectory dirOrFile) >>= mapM_ (discoverNixFiles yield . (dirOrFile </>))++regenerateDerivation :: Derivation -> String -> Bool+regenerateDerivation _ buf = not (buf =~ "(pre|post)Configure|(pre|post)Install|patchPhase|patches")++parseNixFile :: FilePath -> String -> Hackage4Nix (Maybe Pkg)+parseNixFile path buf+  | not (buf =~ "cabal.mkDerivation")+               = msgDebug ("ignore non-cabal package " ++ path) >> return Nothing+  | any (`isSuffixOf`path) badPackagePaths+               = msgDebug ("ignore known bad package " ++ path) >> return Nothing+  | Just deriv <- parseDerivation buf+               = return (Just (Pkg deriv path (regenerateDerivation deriv buf)))+  | otherwise = msgInfo ("failed to parse file " ++ path) >> return Nothing++selectLatestVersions :: PkgSet -> PkgSet+selectLatestVersions = Set.fromList . nubBy f2 . sortBy f1 . Set.toList+  where+    f1 (Pkg deriv1 _ _) (Pkg deriv2 _ _)+      | pname deriv1 == pname deriv2     = compare (version deriv2) (version deriv1)+      | otherwise                        = compare (pname deriv1) (pname deriv2)+    f2 (Pkg deriv1 _ _) (Pkg deriv2 _ _) = pname deriv1 == pname deriv2++discoverUpdates :: String -> Version -> Hackage4Nix [Version]+discoverUpdates name vers = do+  db <- asks _hackageDb+  case DB.lookup name db of+    Just pkgs -> return (filter (>vers) (DB.keys pkgs))+    Nothing   -> fail ("discoverUpdates cannot find package " ++ show name ++ " on Hackage")++updateNixPkgs :: [FilePath] -> Hackage4Nix ()+updateNixPkgs paths = do+  msgDebug $ "updating = " ++ show paths+  forM_ paths $ \fileOrDir ->+    flip discoverNixFiles fileOrDir $ \file -> do+      nix' <- io (readFile file) >>= parseNixFile file+      flip (maybe (return ())) nix' $ \nix -> do+        let Pkg deriv path regenerate = nix+            maints = maintainers (metaSection deriv)+            plats  = platforms (metaSection deriv)+        modify (Set.insert nix)+        when regenerate $ do+          msgDebug ("re-generate " ++ path)+          pkg <- getCabalPackage (pname deriv) (version deriv)+          let deriv'  = (cabal2nix pkg) { sha256 = sha256 deriv+                                        , runHaddock = runHaddock deriv+                                        , doCheck = doCheck deriv+                                        , jailbreak = jailbreak deriv+                                        }+              meta    = metaSection deriv'+              plats'  = if null plats then platforms meta else plats+              deriv'' = deriv' { metaSection = meta+                                               { maintainers = maints -- ++ ["andres","simons"]+                                               , platforms   = plats'+                                               }+                               }+          io $ writeFile path (show (disp (normalize deriv'')))+  pkgset <- gets selectLatestVersions+  updates' <- forM (Set.elems pkgset) $ \pkg -> do+    let Pkg deriv _ _ = pkg+    updates <- discoverUpdates (pname deriv) (version deriv)+    return (pkg,updates)+  let updates = [ u | u@(_,_:_) <- updates' ]+  unless (null updates) $ do+    msgInfo "The following updates are available:"+    forM_ updates $ \(pkg,versions) -> do+      let Pkg deriv path regenerate = pkg+      msgInfo ""+      msgInfo $ display (packageId deriv) ++ ":"+      forM_ versions $ \newVersion -> do+        let deriv' = deriv { version = newVersion }+        msgInfo $ "  " ++ genCabal2NixCmdline (Pkg deriv' path regenerate)+  return ()++genCabal2NixCmdline :: Pkg -> String+genCabal2NixCmdline (Pkg deriv path _) = unwords $ ["cabal2nix"] ++ opts ++ ['>':path']+  where+    meta = metaSection deriv+    opts = [cabal] ++ maints' ++ plats'+                   ++ (if jailbreak deriv then ["--jailbreak"] else [])+                   ++ (if runHaddock deriv then [] else ["--no-haddock"])+                   ++ (if doCheck deriv then [] else ["--no-check"])+    cabal = "cabal://" ++ display (packageId deriv)+    maints' = [ "--maintainer=" ++ normalizeMaintainer m | m <- maintainers meta ]+    plats'+      | ["self.ghc.meta.platforms"] == platforms meta = []+      | otherwise                                     = [ "--platform=" ++ p | p <- platforms meta ]+    path'+      | path =~ "/[0-9\\.]+\\.nix$" = replaceFileName path (display (version deriv) <.> "nix")+      | otherwise                   = path++normalizeMaintainer :: String -> String+normalizeMaintainer x+  | "self.stdenv.lib.maintainers." `isPrefixOf` x = drop 28 x+  | otherwise                                     = x++data CliOption = PrintHelp | Verbose+  deriving (Eq)++main :: IO ()+main = bracket (return ()) (\() -> hFlush stdout >> hFlush stderr) $ \() -> do+  let options :: [OptDescr CliOption]+      options =+        [ Option "h" ["help"]     (NoArg PrintHelp)                 "show this help text"+        , Option "v" ["verbose"]  (NoArg Verbose)                   "enable noisy debug output"+        ]++      usage :: String+      usage = usageInfo "Usage: hackage4nix [options] [dir-or-file ...]" options ++ unlines+              [ ""+              , "The purpose of 'hackage4nix' is to keep all Haskell packages in our"+              , "repository packages up-to-date. It scans a checked-out copy of"+              , "Nixpkgs for expressions that use 'cabal.mkDerivation', and"+              , "re-generates them in-place with cabal2nix."+              ]++      cmdlineError :: String -> IO a+      cmdlineError ""     = hPutStrLn stderr usage >> exitFailure+      cmdlineError errMsg = hPutStrLn stderr errMsg >> cmdlineError ""++  args' <- getArgs+  (opts,args) <- case getOpt Permute options args' of+     (o,n,[]  ) -> return (o,n)+     (_,_,errs) -> cmdlineError (concatMap (\e -> '*':'*':'*':' ':e) errs)++  when (PrintHelp `elem` opts) (cmdlineError "")++  hackage <- DB.readHackage+  let cfg = defaultConfiguration+            { _msgDebug  = if Verbose `elem` opts then _msgDebug defaultConfiguration else const (return ())+            , _hackageDb = hackage+            }+  ((),_,()) <- runRWST (updateNixPkgs args) cfg Set.empty+  return ()+++-- Packages that cabal2nix cannot generate build expressions for.++badPackagePaths :: [FilePath]+badPackagePaths = [ -- These expression are not found on Hackage:+                    "haskell-platform/2009.2.0.2.nix", "haskell-platform/2010.1.0.0.nix"+                  , "haskell-platform/2010.2.0.0.nix", "haskell-platform/2011.2.0.0.nix"+                  , "haskell-platform/2011.2.0.1.nix", "haskell-platform/2011.4.0.0.nix"+                  , "haskell-platform/2012.2.0.0.nix", "haskell-platform/2012.4.0.0.nix"+                  , "haskell-platform/2013.2.0.0.nix", "compilers/flapjax/default.nix"+                  , "pkgs/games/uqm/3dovideo.nix"+                    -- Our primitive parser cannot handle these files.+                  , "top-level/all-packages.nix", "top-level/haskell-packages.nix"+                    -- This build is way too complicated to maintain it automatically.+                  , "pkgs/development/compilers/pakcs/default.nix"+                  ]
test/doc-test.hs view
@@ -29,5 +29,5 @@              , "src/Distribution/NixOS/Derivation/Meta.hs"              , "src/Distribution/NixOS/PrettyPrinting.hs"              ]-  doctest $ "src/Cabal2Nix.hs" : libs-  doctest $ "src/Hackage4Nix.hs" : libs+  doctest $ "src/cabal2nix.hs" : libs+  doctest $ "src/hackage4nix.hs" : libs