cabal2nix 1.52 → 1.53
raw patch · 10 files changed
+352/−328 lines, 10 files
Files
- cabal2nix.cabal +5/−5
- src/Cabal2Nix.hs +0/−97
- src/Cabal2Nix/Flags.hs +1/−1
- src/Cabal2Nix/Name.hs +11/−0
- src/Cabal2Nix/Normalize.hs +1/−1
- src/Cabal2Nix/PostProcess.hs +15/−2
- src/Hackage4Nix.hs +0/−220
- src/cabal2nix.hs +97/−0
- src/hackage4nix.hs +220/−0
- test/doc-test.hs +2/−2
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