packages feed

cabal2nix 1.11 → 1.12

raw patch · 17 files changed

+400/−481 lines, 17 filesdep +nixos-typesdep ~Cabalsetup-changednew-component:exe:packagelist

Dependencies added: nixos-types

Dependency ranges changed: Cabal

Files

+ Setup.hs view
@@ -0,0 +1,6 @@+module Main (main) where++import Distribution.Simple++main :: IO ()+main = defaultMain
− Setup.lhs
@@ -1,8 +0,0 @@-#!/usr/bin/env runhaskell--> module Main (main) where->-> import Distribution.Simple->-> main :: IO ()-> main = defaultMain
cabal2nix.cabal view
@@ -1,6 +1,6 @@ Name:                   cabal2nix-Version:                1.11-Copyright:              (c) 2011 Peter Simons and Andres Loeh+Version:                1.12+Copyright:              Peter Simons, Andres Loeh License:                BSD3 License-File:           LICENSE Author:                 Peter Simons <simons@cryp.to>, Andres Loeh <mail@andres-loeh.de>@@ -8,9 +8,9 @@ Homepage:               http://github.com/haskell4nix/cabal2nix Category:               Distribution Synopsis:               Convert Cabal files into Nix build instructions-Cabal-Version:          >= 1.6+Cabal-Version:          >= 1.8 Build-Type:             Simple-Tested-With:            GHC == 6.12.3, GHC == 7.0.4+Tested-With:            GHC == 6.12.3, GHC == 7.0.4, GHC == 7.2.1 Description:   The cabal2nix utility converts Cabal files into Nix build instructions. The   commandline syntax is:@@ -41,43 +41,40 @@   Type:                 git   Location:             git://github.com/haskell4nix/cabal2nix.git -Library-  Build-Depends:        base >= 3 && < 5, regex-posix, pretty, Cabal-  hs-source-dirs:       src-  Extensions:           PatternGuards-  Ghc-Options:          -Wall-  Exposed-Modules:      Distribution.NixOS.Derivation.Cabal-                        Distribution.NixOS.Derivation.License-                        Distribution.NixOS.Derivation.Meta-  other-modules:        Distribution.NixOS.Derivation.PrettyPrinting- Executable cabal2nix   main-is:              Cabal2Nix.hs   hs-source-dirs:       src-  Build-Depends:        base >= 3 && < 5, regex-posix, pretty, Cabal,-                        filepath, directory, process, HTTP-  Extensions:           PatternGuards+  Build-Depends:        base >= 3 && < 5, regex-posix, pretty, Cabal >= 1.8,+                        filepath, directory, process, HTTP, nixos-types+  Extensions:           PatternGuards, RecordWildCards, CPP   Ghc-Options:          -Wall   other-modules:        Cabal2Nix.CorePackages+                        Cabal2Nix.Flags                         Cabal2Nix.Hackage+                        Cabal2Nix.License                         Cabal2Nix.Name+                        Cabal2Nix.Normalize                         Cabal2Nix.Package-                        Distribution.NixOS.Derivation.Cabal-                        Distribution.NixOS.Derivation.License-                        Distribution.NixOS.Derivation.Meta-                        Distribution.NixOS.Derivation.PrettyPrinting+                        Cabal2Nix.PostProcess  Executable hackage4nix   main-is:              Hackage4Nix.hs   hs-source-dirs:       src-  Build-Depends:        base >= 3 && < 5, regex-posix, pretty, Cabal,-                        mtl, containers, directory, filepath-  Extensions:           PatternGuards+  Build-Depends:        base >= 3 && < 5, regex-posix, pretty, Cabal >= 1.8,+                        mtl, containers, directory, filepath, nixos-types+  Extensions:           PatternGuards, RecordWildCards, CPP   Ghc-Options:          -Wall   other-modules:        Cabal2Nix.CorePackages+                        Cabal2Nix.Flags+                        Cabal2Nix.License                         Cabal2Nix.Name+                        Cabal2Nix.Normalize                         Cabal2Nix.Package-                        Distribution.NixOS.Derivation.Cabal-                        Distribution.NixOS.Derivation.License-                        Distribution.NixOS.Derivation.Meta-                        Distribution.NixOS.Derivation.PrettyPrinting+                        Cabal2Nix.PostProcess++Executable packagelist+  main-is:              PackageList.hs+  hs-source-dirs:       src+  Build-Depends:        base >= 3 && < 5, regex-posix, process, Cabal,+                        filepath, directory+  Ghc-Options:          -Wall
src/Cabal2Nix.hs view
@@ -1,77 +1,76 @@ module Main ( main ) where -import System.IO ( hPutStrLn, hFlush, stdout, stderr )-import System.Environment -- ( getArgs )+import Cabal2Nix.Hackage ( hashPackage, readCabalFile )+import Cabal2Nix.Package ( cabal2nix )+import Cabal2Nix.Normalize ( normalize )+import Distribution.NixOS.Derivation.Cabal+import Distribution.NixOS.Derivation.Meta+ import Control.Exception ( bracket )-import System.Exit ( exitFailure )-import Distribution.PackageDescription.Parse ( parsePackageDescription, ParseResult(..) )-import Distribution.PackageDescription ( package, packageDescription )-import Distribution.Text-import Data.List ( isPrefixOf ) import Control.Monad ( when )-import Network.HTTP ( simpleHTTP, getRequest, getResponseBody )+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 ) -import Cabal2Nix.Package ( cabal2nix )-import Cabal2Nix.Hackage ( hackagePath, Ext(..), hashPackage )-import Distribution.NixOS.Derivation.Cabal-import Distribution.NixOS.Derivation.Meta+data Configuration = Configuration+  { optPrintHelp :: Bool+  , optPrintVersion :: Bool+  , optSha256 :: String+  , optMaintainer :: [String]+  , optPlatform :: [String]+  , optHaddock :: Bool+  }+  deriving (Show) -readCabalFile :: FilePath -> IO String-readCabalFile path-  | "cabal://" `isPrefixOf` path = let Just pid = simpleParse (drop 8 path) in readCabalFile (hackagePath pid Cabal)-  | "http://"  `isPrefixOf` path = simpleHTTP (getRequest path) >>= getResponseBody-  | "file://"  `isPrefixOf` path = readCabalFile (drop 7 path)-  | otherwise                    = readFile path+defaultConfiguration :: Configuration+defaultConfiguration = Configuration+  { optPrintHelp = False+  , optPrintVersion = False+  , optSha256 = ""+  , optMaintainer = []+  , optPlatform = []+  , optHaddock = True+  } -data CliOption = PrintHelp | SHA256 String | Maintainer String | Platform String | NoHaddock-  deriving (Eq)+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"+  ] -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 []    ["sha256"]     (ReqArg SHA256 "HASH")            "sha256 hash of source tarball"-        , Option ['m'] ["maintainer"] (ReqArg Maintainer "MAINTAINER")  "maintainer of this package (may be specified multiple times)"-        , Option ['p'] ["platform"]   (ReqArg Platform "PLATFORM")      "supported build platforms (may be specified multiple times)"-        , Option []    ["no-haddock"] (NoArg NoHaddock)                 "don't run Haddock when building this package"+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"+        , "  http://host/path                 fetch the Cabel file via HTTP"+        , "  file:///local/path               load the Cabal file from the local disk"+        , "  /local/path                      abbreviated version of file URI"         ] -      usage :: String-      usage = usageInfo "Usage: cabal2nix [options] url-to-cabal-file" options ++-             "\n\-             \Recognized URI schemes:\n\-             \\n\-             \  cabal://pkgname-pkgversion       download the specified package from Hackage\n\-             \  http://host/path                 fetch the Cabel file via HTTP\n\-             \  file:///local/path               load the Cabal file from the local disk\n\-             \  /local/path                      abbreviated version of file URI\n"--      cmdlineError :: String -> IO a-      cmdlineError ""     = hPutStrLn stderr usage >> exitFailure-      cmdlineError errMsg = hPutStrLn stderr errMsg >> cmdlineError ""+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-  (opts,args) <- case getOpt Permute options args' of-     (o,n,[]  ) -> return (o,n)-     (_,_,errs) -> cmdlineError (concatMap (\e -> '*':'*':'*':' ':e) errs)--  when (PrintHelp `elem` opts) (cmdlineError "")--  let uri         = args-      hash        = [ h | SHA256 h <- opts ]-      noHaddock   = NoHaddock `elem` opts-      maints      = [ if '.' `elem` m then m else "self.stdenv.lib.maintainers." ++ m | Maintainer m <- opts ]-      plats'      = [ if '.' `elem` p then p else "self.stdenv.lib.platforms." ++ p | Platform p <- opts ]-      plats-        | null plats' = if not (null maints) then ["self.ghc.meta.platforms"] else []-        | otherwise   = plats'+  (cfg,args) <- case getOpt Permute options args' of+                  (o,n,[]  ) -> return (foldl (flip ($)) defaultConfiguration o,n)+                  (_,_,errs) -> cmdlineError (concatMap ("*** "++) errs) -  when (length uri /= 1) (cmdlineError "*** exactly one URI must be specified")-  when (length hash > 1) (cmdlineError "*** the --sha256 option may be specified only once")+  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 uri))+  cabal' <- fmap parsePackageDescription (readCabalFile (head args))   cabal <- case cabal' of              ParseOk _ a -> return a              ParseFailed err -> do@@ -79,14 +78,13 @@                exitFailure    let packageId = package (packageDescription cabal)--  sha <- if null hash then hashPackage packageId else return (head hash)+  sha <- if null (optSha256 cfg) then hashPackage packageId else return (optSha256 cfg) -  let deriv  = (cabal2nix cabal) { sha256 = sha, runHaddock = not noHaddock }+  let deriv  = (cabal2nix cabal) { sha256 = sha, runHaddock = optHaddock cfg }       deriv' = deriv { metaSection = (metaSection deriv)-                                     { maintainers = maints-                                     , platforms   = plats+                                     { maintainers = optMaintainer cfg+                                     , platforms   = optPlatform cfg                                      }                      } -  putStr (show (disp deriv'))+  putStr (show (disp (normalize (deriv'))))
+ src/Cabal2Nix/Flags.hs view
@@ -0,0 +1,15 @@+module Cabal2Nix.Flags ( configureFlags ) where++import Distribution.Package+import Distribution.PackageDescription++configureFlags :: PackageIdentifier -> FlagAssignment+configureFlags (PackageIdentifier (PackageName name) _)+ | name == "pandoc"     = [enable "highlighting", enable "threaded"]+ | otherwise            = []++enable :: String -> (FlagName,Bool)+enable name = (FlagName name, True)++disable :: String -> (FlagName,Bool)+disable name = (FlagName name, False)
src/Cabal2Nix/Hackage.hs view
@@ -1,11 +1,14 @@-module Cabal2Nix.Hackage ( hackagePath, Ext(..), hashPackage ) where+module Cabal2Nix.Hackage ( hashPackage, readCabalFile ) where +import Control.Monad ( when )+import Data.List ( isPrefixOf )+import Data.Version ( showVersion ) import Distribution.Package ( PackageIdentifier(..), PackageName(..) )-import System.Process ( readProcess )+import Distribution.Text+import Network.HTTP ( simpleHTTP, getRequest, getResponseBody ) import System.Directory ( doesFileExist, getHomeDirectory, createDirectoryIfMissing ) import System.FilePath ( dropFileName, (</>), (<.>) )-import Data.Version ( showVersion )-import Control.Monad ( when )+import System.Process ( readProcess )  data Ext = TarGz | Cabal deriving Eq @@ -41,3 +44,10 @@     return $ home ++ "/.cache/cabal2nix" </> name ++ "-" ++ version <.> "sha256"   where     version = showVersion version'++readCabalFile :: FilePath -> IO String+readCabalFile path+  | "cabal://" `isPrefixOf` path = let Just pid = simpleParse (drop 8 path) in readCabalFile (hackagePath pid Cabal)+  | "http://"  `isPrefixOf` path = simpleHTTP (getRequest path) >>= getResponseBody+  | "file://"  `isPrefixOf` path = readCabalFile (drop 7 path)+  | otherwise                    = readFile path
+ src/Cabal2Nix/License.hs view
@@ -0,0 +1,20 @@+module Cabal2Nix.License ( fromCabalLicense ) where++import Distribution.NixOS.Derivation.License+import Distribution.License ( License(..) )+import Data.Version++fromCabalLicense :: Distribution.License.License -> Distribution.NixOS.Derivation.License.License+fromCabalLicense (GPL Nothing)                     = Unknown (Just "GPL")+fromCabalLicense (GPL (Just (Version [2] [])))     = Known "self.stdenv.lib.licenses.gpl2"+fromCabalLicense (GPL (Just (Version [3] [])))     = Known "self.stdenv.lib.licenses.gpl3"+fromCabalLicense (LGPL Nothing)                    = Unknown (Just "LGPL")+fromCabalLicense (LGPL (Just (Version [2,1] [])))  = Known "self.stdenv.lib.licenses.lgpl21"+fromCabalLicense (LGPL (Just (Version [3] [])))    = Known "self.stdenv.lib.licenses.gpl3"+fromCabalLicense BSD3                              = Known "self.stdenv.lib.licenses.bsd3"+fromCabalLicense BSD4                              = Known "self.stdenv.lib.licenses.bsd4"+fromCabalLicense MIT                               = Known "self.stdenv.lib.licenses.mit"+fromCabalLicense PublicDomain                      = Known "self.stdenv.lib.licenses.publicDomain"+fromCabalLicense AllRightsReserved                 = Known "self.stdenv.lib.licenses.proprietary"+fromCabalLicense OtherLicense                      = Unknown Nothing+fromCabalLicense l                                 = error $ "Cabal2Nix.License.fromCabalLicense: unknown license " ++ show l
src/Cabal2Nix/Name.hs view
@@ -1,7 +1,8 @@-module Cabal2Nix.Name ( toNixName, libNixName ) where+module Cabal2Nix.Name ( toNixName, libNixName, buildToolNixName ) where  import Data.Char +-- | Map Cabal names to Nix attribute names. toNixName :: String -> String toNixName []      = error "toNixName: empty string is not a valid argument" toNixName name    = f name@@ -16,24 +17,40 @@ -- possibility of name clashes with Haskell libraries. I have included -- identity mappings to incicate that I have verified their correctness. libNixName :: String -> [String]-libNixName "adns"      = return "adns"-libNixName "cairo"     = return "cairo"-libNixName "cairo-pdf" = return "cairo"-libNixName "cairo-ps"  = return "cairo"-libNixName "cairo-svg" = return "cairo"-libNixName "crypto"    = return "openssl"-libNixName "gnome-keyring" = return "gnome_keyring"-libNixName "gnome-keyring-1" = return "gnome_keyring"-libNixName "idn"       = return "idn"-libNixName "libidn"    = return "idn"-libNixName "libzip"    = return "libzip"-libNixName "m"         = []  -- in stdenv-libNixName "pcre"      = return "pcre"-libNixName "pq"        = return "postgresql"-libNixName "sndfile"   = return "libsndfile"-libNixName "sqlite3"   = return "sqlite"-libNixName "stdc++"    = []  -- in stdenv-libNixName "xft"       = return "libXft"-libNixName "X11"       = return "libX11"-libNixName "z"         = return "zlib"-libNixName x           = return x+libNixName "adns"               = return "adns"+libNixName "cairo-pdf"          = return "cairo"+libNixName "cairo-ps"           = return "cairo"+libNixName "cairo"              = return "cairo"+libNixName "cairo-svg"          = return "cairo"+libNixName "crypto"             = return "openssl"+libNixName "glib-2.0"           = return "glib"+libNixName "gnome-keyring-1"    = return "gnome_keyring"+libNixName "gnome-keyring"      = return "gnome_keyring"+libNixName "gobject-2.0"        = return "glib"+libNixName "gthread-2.0"        = return "glib"+libNixName "gtk+-2.0"           = return "gtk"+libNixName "gtksourceview-2.0"  = return "gtksourceview"+libNixName "idn"                = return "idn"+libNixName "libglade-2.0"       = return "libglade"+libNixName "libidn"             = return "idn"+libNixName "librsvg-2.0"        = return "librsvg"+libNixName "libzip"             = return "libzip"+libNixName "m"                  = []  -- in stdenv+libNixName "pangocairo"         = return "pango"+libNixName "pcre"               = return "pcre"+libNixName "pq"                 = return "postgresql"+libNixName "sndfile"            = return "libsndfile"+libNixName "sqlite3"            = return "sqlite"+libNixName "stdc++"             = []  -- in stdenv+libNixName "X11"                = return "libX11"+libNixName "xft"                = return "libXft"+libNixName "z"                  = return "zlib"+libNixName x                    = return x++-- | Map build tool names to Nix attribute names.+buildToolNixName :: String -> [String]+buildToolNixName "cabal"                = return "cabalInstall"+buildToolNixName "gtk2hsC2hs"           = return "gtk2hsBuildtools"+buildToolNixName "gtk2hsHookGenerator"  = return "gtk2hsBuildtools"+buildToolNixName "gtk2hsTypeGen"        = return "gtk2hsBuildtools"+buildToolNixName x                      = return (toNixName x)
+ src/Cabal2Nix/Normalize.hs view
@@ -0,0 +1,58 @@+{-# LANGUAGE RecordWildCards #-}++module Cabal2Nix.Normalize ( normalize ) where++import Distribution.NixOS.Derivation.Cabal+import Cabal2Nix.Name+import Cabal2Nix.CorePackages+import Data.List+import Data.Char++normalize :: Derivation -> Derivation+normalize deriv@(MkDerivation {..}) = deriv+  { buildDepends = normalizeNixNames (filter (`notElem` (pname : corePackages)) $ buildDepends)+  , buildTools   = normalizeNixBuildTools (filter (`notElem` coreBuildTools) $ buildTools)+  , extraLibs    = normalizeNixLibs extraLibs+  , pkgConfDeps  = normalizeNixLibs pkgConfDeps+  , metaSection  = normalizeMeta metaSection+  }++normalizeMeta :: Meta -> Meta+normalizeMeta meta@(Meta {..}) = meta+  { description = normalizeDescription description+  , maintainers = normalizeMaintainers maintainers+  , platforms   = normalizePlatforms platforms+  }++normalizeDescription :: String -> String+normalizeDescription desc+  | null desc                                             = []+  | last desc == '.' && length (filter ('.'==) desc) == 1 = normalizeDescription (reverse (tail (reverse desc)))+  | otherwise                                             = quote (unwords (words desc))++quote :: String -> String+quote ('\\':c:cs) = '\\' : c : quote cs+quote ('"':cs)    = '\\' : '"' : quote cs+quote (c:cs)      = c : quote cs+quote []          = []++normalizeList :: [String] -> [String]+normalizeList = nub . sortBy (\x y -> compare (map toLower x) (map toLower y))++normalizeNixNames :: [String] -> [String]+normalizeNixNames = normalizeList . map toNixName++normalizeNixLibs :: [String] -> [String]+normalizeNixLibs = normalizeList . concatMap libNixName++normalizeNixBuildTools :: [String] -> [String]+normalizeNixBuildTools = normalizeList . concatMap buildToolNixName++normalizeMaintainers :: [String] -> [String]+normalizeMaintainers maints = normalizeList+  [ if '.' `elem` m then m else "self.stdenv.lib.maintainers." ++ m | m <- maints ]++normalizePlatforms :: [String] -> [String]+normalizePlatforms [] = ["self.ghc.meta.platforms"]+normalizePlatforms plats = normalizeList+  [ if '.' `elem` p then p else "self.stdenv.lib.platforms." ++ p | p <- plats ]
src/Cabal2Nix/Package.hs view
@@ -1,5 +1,9 @@ module Cabal2Nix.Package ( cabal2nix ) where +import Cabal2Nix.License+import Cabal2Nix.PostProcess+import Cabal2Nix.Normalize+import Cabal2Nix.Flags import Data.Maybe import Distribution.Compiler import qualified Distribution.Package as Cabal@@ -8,26 +12,22 @@ import Distribution.System import Distribution.Version import Distribution.NixOS.Derivation.Cabal-import Cabal2Nix.Name-import Cabal2Nix.CorePackages-import Data.List-import Data.Char  cabal2nix :: Cabal.GenericPackageDescription -> Derivation-cabal2nix cabal = MkDerivation-  { pname        = Cabal.pkgName pkg+cabal2nix cabal = normalize $ postProcess $ MkDerivation+  { pname        = let Cabal.PackageName x = Cabal.pkgName pkg in x   , version      = Cabal.pkgVersion pkg   , sha256       = "cabal2nix left the she256 field undefined"   , isLibrary    = isJust (Cabal.library tpkg)   , isExecutable = not (null (Cabal.executables tpkg))-  , buildDepends = normalizeNixNames (filter (`notElem` (name : corePackages)) $ map unDep deps)-  , buildTools   = normalizeNixNames (filter (`notElem` coreBuildTools) $ map unDep tools)-  , extraLibs    = normalizeNixLibs libs-  , pkgConfDeps  = normalizeNixLibs pcs+  , buildDepends = map unDep deps+  , buildTools   = map unDep tools+  , extraLibs    = libs+  , pkgConfDeps  = pcs   , runHaddock   = True   , metaSection  = Meta                    { homepage    = Cabal.homepage descr-                   , description = normalizeDescription (Cabal.synopsis descr)+                   , description = Cabal.synopsis descr                    , license     = fromCabalLicense (Cabal.license descr)                    , platforms   = []                    , maintainers = []@@ -36,36 +36,18 @@   where     descr = Cabal.packageDescription cabal     pkg   = Cabal.package descr-    Cabal.PackageName name  = Cabal.pkgName pkg     deps    = Cabal.buildDepends tpkg     libDeps = map Cabal.libBuildInfo (maybeToList (Cabal.library tpkg))     exeDeps = map Cabal.buildInfo (Cabal.executables tpkg)     tools   = concatMap Cabal.buildTools (libDeps ++ exeDeps)     libs    = concatMap Cabal.extraLibs (libDeps ++ exeDeps)     pcs     = map unDep (concatMap Cabal.pkgconfigDepends (libDeps ++ exeDeps))-    Right (tpkg, _) = finalizePackageDescription [] (const True)+    Right (tpkg, _) = finalizePackageDescription+                        (configureFlags pkg)+                        (const True)                         (Platform I386 Linux)                   -- shouldn't be hardcoded                         (CompilerId GHC (Version [7,0,4] []))   -- dito                         [] cabal  unDep :: Cabal.Dependency -> String unDep (Cabal.Dependency (Cabal.PackageName x) _) = x--normalizeDescription :: String -> String-normalizeDescription desc-  | null desc                                             = []-  | last desc == '.' && length (filter ('.'==) desc) == 1 = normalizeDescription (reverse (tail (reverse desc)))-  | otherwise                                             = unwords (words desc) >>= quote--quote :: Char -> [Char]-quote '"'  = "\\\""-quote c    = [c]--normalizeList :: [String] -> [String]-normalizeList = nub . sortBy (\x y -> compare (map toLower x) (map toLower y))--normalizeNixNames :: [String] -> [String]-normalizeNixNames = normalizeList . map toNixName--normalizeNixLibs :: [String] -> [String]-normalizeNixLibs = normalizeList . concatMap libNixName
+ src/Cabal2Nix/PostProcess.hs view
@@ -0,0 +1,35 @@+{-# LANGUAGE RecordWildCards #-}++module Cabal2Nix.PostProcess ( postProcess ) where++import Distribution.NixOS.Derivation.Cabal+import Data.List++postProcess :: Derivation -> Derivation+postProcess deriv@(MkDerivation {..})+  | pname == "alex"             = deriv { buildTools = "perl":buildTools }+  | pname == "cairo"            = deriv { extraLibs = "pkgconfig":"glibc":"cairo":"zlib":extraLibs }+  | pname == "editline"         = deriv { extraLibs = "libedit":extraLibs }+  | pname == "epic"             = deriv { extraLibs = "gmp":"boehmgc":extraLibs, buildTools = "happy":buildTools }+  | pname == "glade"            = deriv { extraLibs = "pkgconfig":"glibc":extraLibs, pkgConfDeps = "gtkC":(delete "gtk" pkgConfDeps) }+  | pname == "glib"             = deriv { extraLibs = "pkgconfig":"glibc":extraLibs }+  | pname == "GLUT"             = deriv { extraLibs = "glut":"libSM":"libICE":"libXmu":"libXi":"mesa":extraLibs }+  | pname == "gtk"              = deriv { extraLibs = "pkgconfig":"glibc":extraLibs, buildDepends = delete "gio" buildDepends }+  | pname == "gtksourceview2"   = deriv { extraLibs = "pkgconfig":"glibc":extraLibs }+  | pname == "haskell-src"      = deriv { buildTools = "happy":buildTools }+  | pname == "hmatrix"          = deriv { extraLibs = "gsl":"liblapack":"blas":extraLibs }+  | pname == "idris"            = deriv { buildTools = "happy":buildTools }+  | pname == "OpenAL"           = deriv { extraLibs = "openal":extraLibs }+  | pname == "OpenGL"           = deriv { extraLibs = "mesa":"libX11":extraLibs }+  | pname == "pango"            = deriv { extraLibs = "pkgconfig":"glibc":extraLibs }+  | pname == "repa-examples"    = deriv { extraLibs = "llvm":extraLibs }+  | pname == "SDL-image"        = deriv { extraLibs = "SDL_image":extraLibs }+  | pname == "SDL-mixer"        = deriv { extraLibs = "SDL_mixer":extraLibs }+  | pname == "SDL-ttf"          = deriv { extraLibs = "SDL_ttf":extraLibs }+  | pname == "svgcairo"         = deriv { extraLibs = "glibc":extraLibs }+  | pname == "terminfo"         = deriv { extraLibs = "ncurses":extraLibs }+  | pname == "vacuum"           = deriv { extraLibs = "ghcPaths":extraLibs }+  | pname == "wxcore"           = deriv { extraLibs = "wxGTK":"mesa":"libX11":extraLibs }+  | pname == "xmonad"           = deriv { extraLibs = delete "libmpd" extraLibs }+  | pname == "xmonad-extras"    = deriv { buildDepends = delete "libmpd" buildDepends }+  | otherwise                   = deriv
− src/Distribution/NixOS/Derivation/Cabal.hs
@@ -1,106 +0,0 @@-{-# LANGUAGE PatternGuards #-}--module Distribution.NixOS.Derivation.Cabal-  ( Derivation(..)-  , parseDerivation-  , module Distribution.NixOS.Derivation.Meta-  , Version(..), PackageName(..)-  )-  where--import Distribution.NixOS.Derivation.PrettyPrinting-import Distribution.NixOS.Derivation.Meta-import Distribution.Package-import Distribution.Text-import Data.Version-import Data.List-import Data.Char-import Text.PrettyPrint-import Text.ParserCombinators.ReadP ( readP_to_S )-import Text.Regex.Posix--data Derivation = MkDerivation-  { pname        :: PackageName-  , version      :: Version-  , sha256       :: String-  , isLibrary    :: Bool-  , isExecutable :: Bool-  , buildDepends :: [String]-  , buildTools   :: [String]-  , extraLibs    :: [String]-  , pkgConfDeps  :: [String]-  , runHaddock   :: Bool-  , metaSection  :: Meta-  }-  deriving (Show, Eq, Ord)--instance Text Derivation where-  disp  = renderDerivation-  parse = error "parsing Distribution.NixOS.Derivation.Cabal.Derivation is not supported yet"--renderDerivation :: Derivation -> Doc-renderDerivation deriv = funargs (map text ("cabal" : inputs)) $$ vcat-  [ text ""-  , text "cabal.mkDerivation" <+> lparen <> text "self" <> colon <+> lbrace-  , nest 2 $ vcat-    [ attr "pname"   $ doubleQuotes (disp (pname deriv))-    , attr "version" $ doubleQuotes (disp (version deriv))-    , attr "sha256"  $ string (sha256 deriv)-    , boolattr "isLibrary" (not (isLibrary deriv) || (isExecutable deriv)) (isLibrary deriv)-    , boolattr "isExecutable" (not (isLibrary deriv) || (isExecutable deriv)) (isExecutable deriv)-    , listattr "buildDepends" (buildDepends deriv)-    , listattr "buildTools" (buildTools deriv)-    , listattr "extraLibraries" (extraLibs deriv)-    , listattr "pkgconfigDepends" (pkgConfDeps deriv)-    , boolattr "noHaddock" (not (runHaddock deriv)) (not (runHaddock deriv))-    , disp (metaSection deriv)-    ]-  , rbrace <> rparen-  , text ""-  ]-  where-    inputs = nub $ sortBy (\x y -> compare (map toLower x) (map toLower y)) $-              filter (/="cabal") $ filter (/="Cabal") $-                buildDepends deriv ++ buildTools deriv ++ extraLibs deriv ++ pkgConfDeps deriv--parseDerivation :: String -> Maybe Derivation-parseDerivation buf-  | buf =~ "cabal.mkDerivation"-  , [name]    <- buf `regsubmatch` "pname *= *\"([^\"]+)\""-  , [vers]    <- buf `regsubmatch` "version *= *\"([^\"]+)\""-  , [sha]     <- buf `regsubmatch` "sha256 *= *\"([^\"]+)\""-  , plats     <- buf `regsubmatch` "platforms *= *([^;]+);"-  , maint     <- buf `regsubmatch` "maintainers *= *\\[([^\"]+)]"-  , noHaddock <- buf `regsubmatch` "noHaddock *= *(true|false) *;"-              = Just $ MkDerivation-                  { pname        = PackageName name-                  , version      = readVersion vers-                  , sha256       = sha-                  , isLibrary    = False-                  , isExecutable = False-                  , buildDepends = []-                  , buildTools   = []-                  , extraLibs    = []-                  , pkgConfDeps  = []-                  , runHaddock   = case noHaddock of "true":[] -> False-                                                     _         -> True-                  , metaSection  = Meta-                                   { homepage    = ""-                                   , description = ""-                                   , license     = Unknown Nothing-                                   , maintainers = concatMap words maint-                                   , platforms   = concatMap words (map (map (\c -> if c == '+' then ' ' else c)) plats)-                                   }-                  }-  | otherwise = Nothing--regsubmatch :: String -> String -> [String]-regsubmatch buf patt = let (_,_,_,x) = f in x-  where f :: (String,String,String,[String])-        f = match (makeRegexOpts compExtended execBlank patt) buf--readVersion :: String -> Version-readVersion str =-  case [ v | (v,[]) <- readP_to_S parseVersion str ] of-    [ v' ] -> v'-    _      -> error ("invalid version specifier " ++ show str)
− src/Distribution/NixOS/Derivation/License.hs
@@ -1,54 +0,0 @@-module Distribution.NixOS.Derivation.License-  ( License(..)-  , fromCabalLicense-  )-  where--import qualified Distribution.License as Cabal ( License(..) )-import Data.Version ( Version(..) )-import Distribution.Text-import Text.PrettyPrint--data License-  = GPL2-  | GPL3-  | LGPL21-  | LGPL3-  | BSD3-  | BSD4-  | MIT-  | PublicDomain-  | Proprietary-  | Unknown (Maybe String)-  deriving (Eq, Ord)--instance Show License where-  show GPL2                     = "self.stdenv.lib.licenses.gpl2"-  show GPL3                     = "self.stdenv.lib.licenses.gpl3"-  show LGPL21                   = "self.stdenv.lib.licenses.lgpl21"-  show LGPL3                    = "self.stdenv.lib.licenses.gpl3"-  show BSD3                     = "self.stdenv.lib.licenses.bsd3"-  show BSD4                     = "self.stdenv.lib.licenses.bsd4"-  show MIT                      = "self.stdenv.lib.licenses.mit"-  show PublicDomain             = "self.stdenv.lib.licenses.publicDomain"-  show Proprietary              = "self.stdenv.lib.licenses.proprietary"-  show (Unknown lic)            = show (maybe "unknown" id lic)--instance Text License where-  disp = text . show-  parse = error "parsing Distribution.NixOS.Derivation.License is not supported yet"--fromCabalLicense :: Cabal.License -> License-fromCabalLicense (Cabal.GPL Nothing)                     = Unknown (Just "GPL")-fromCabalLicense (Cabal.GPL (Just (Version [2] [])))     = GPL2-fromCabalLicense (Cabal.GPL (Just (Version [3] [])))     = GPL3-fromCabalLicense (Cabal.LGPL Nothing)                    = Unknown (Just "LGPL")-fromCabalLicense (Cabal.LGPL (Just (Version [2,1] [])))  = LGPL21-fromCabalLicense (Cabal.LGPL (Just (Version [3] [])))    = LGPL3-fromCabalLicense Cabal.BSD3                              = BSD3-fromCabalLicense Cabal.BSD4                              = BSD4-fromCabalLicense Cabal.MIT                               = MIT-fromCabalLicense Cabal.PublicDomain                      = PublicDomain-fromCabalLicense Cabal.AllRightsReserved                 = Proprietary-fromCabalLicense Cabal.OtherLicense                      = Unknown Nothing-fromCabalLicense l                                       = error $ "Distribution.NixOS.Derivation.License.fromCabalLicense: unknown license " ++ show l
− src/Distribution/NixOS/Derivation/Meta.hs
@@ -1,39 +0,0 @@-module Distribution.NixOS.Derivation.Meta-  ( Meta(..)-  , module Distribution.NixOS.Derivation.License-  )-  where--import Distribution.NixOS.Derivation.PrettyPrinting-import Distribution.NixOS.Derivation.License-import Distribution.Text-import Text.PrettyPrint--data Meta = Meta-  { homepage    :: String-  , description :: String-  , license     :: License-  , platforms   :: [String]-  , maintainers :: [String]-  }-  deriving (Show, Eq, Ord)--instance Text Meta where-  disp  = renderMeta-  parse = error "parsing Distribution.NixOS.Derivation.Cabal.Meta is not supported yet"--renderMeta :: Meta -> Doc-renderMeta meta = vcat-  [ text "meta" <+> equals <+> lbrace-  , nest 2 $ vcat-    [ onlyIf (homepage meta) $ attr "homepage" $ string (homepage meta)-    , onlyIf (description meta) $ attr "description" $ string (description meta)-    , attr "license" $ disp (license meta)-    , onlyIf (platforms meta) $ sep-      [ text "platforms" <+> equals-      , nest 2 ((fsep $ punctuate (text " ++") $ map text (platforms meta))) <> semi-      ]-    , listattr "maintainers" (maintainers meta)-    ]-  , rbrace <> semi-  ]
− src/Distribution/NixOS/Derivation/PrettyPrinting.hs
@@ -1,47 +0,0 @@-module Distribution.NixOS.Derivation.PrettyPrinting-  ( onlyIf-  , listattr-  , boolattr-  , attr-  , string-  , funargs-  )-  where--import Text.PrettyPrint---- Pretty-printing helpers for Nix expressions.--attr :: String -> Doc -> Doc-attr n v = text n <+> equals <+> v <> semi--onlyIf :: [a] -> Doc -> Doc-onlyIf p d = if not (null p) then d else empty--boolattr :: String -> Bool -> Bool -> Doc-boolattr n p v = if p then attr n (bool v) else empty--listattr :: String -> [String] -> Doc-listattr n vs = onlyIf vs $-                sep [ text n <+> equals <+> lbrack,-                      nest 2 $ fsep $ map text vs,-                      rbrack <> semi-                    ]--bool :: Bool -> Doc-bool True  = text "true"-bool False = text "false"--string :: String -> Doc-string = doubleQuotes . text--prepunctuate :: Doc -> [Doc] -> [Doc]-prepunctuate _ []     = []-prepunctuate p (d:ds) = d : map (p <>) ds--funargs :: [Doc] -> Doc-funargs xs = sep [-               lbrace <+> (fcat $ prepunctuate (comma <> text " ") $-                           map (nest 2) xs),-               rbrace <> colon-             ]
src/Hackage4Nix.hs view
@@ -11,12 +11,13 @@ import Control.Exception ( bracket ) import Text.Regex.Posix import Data.Version-import Text.ParserCombinators.ReadP ( readP_to_S ) import Distribution.PackageDescription.Parse ( parsePackageDescription, ParseResult(..) )+import Distribution.Package import Distribution.PackageDescription ( GenericPackageDescription() ) import Distribution.Text import System.Console.GetOpt ( OptDescr(..), ArgDescr(..), ArgOrder(..), usageInfo, getOpt ) import Cabal2Nix.Package+import Cabal2Nix.Normalize import Distribution.NixOS.Derivation.Cabal import Distribution.NixOS.Derivation.Meta @@ -65,24 +66,19 @@   return pkg  discoverNixFiles :: (FilePath -> Hackage4Nix ()) -> FilePath -> Hackage4Nix ()-discoverNixFiles yield dirOrFile-  | "." `isPrefixOf` takeFileName dirOrFile  = return ()-  | otherwise                                = do-     isFile <- io (doesFileExist dirOrFile)-     case (isFile, takeExtension dirOrFile) of-       (True,".nix") -> yield dirOrFile-       (True,_)     -> return ()-       (False,_)    -> io (readDirectory dirOrFile) >>= mapM_ (discoverNixFiles yield . (dirOrFile </>))+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 deriv buf = not (display (pname deriv) `elem` patchedPackages) &&-                                 not (buf =~ "preConfigure|configureFlags|postInstall|patchPhase")+regenerateDerivation deriv buf = not (pname deriv `elem` patchedPackages) &&+                                 not (buf =~ "(pre|post)Configure|configureFlags|(pre|post)Install|patchPhase")  readVersion :: String -> Version-readVersion str =-  case [ v | (v,[]) <- readP_to_S parseVersion str ] of-    [ v' ] -> v'-    _      -> error ("invalid version specifier " ++ show str)+readVersion str = maybe (error $ "cannot parse version " ++ show str) id (simpleParse str)  parseNixFile :: FilePath -> String -> Hackage4Nix (Maybe Pkg) parseNixFile path buf@@ -121,26 +117,23 @@         let Pkg deriv path regenerate = nix             maints = maintainers (metaSection deriv)             plats  = platforms (metaSection deriv)-        when (null maints) (msgInfo ("no maintainers configured for " ++ path))         modify $ \cfg -> cfg { _pkgset = Set.insert nix (_pkgset cfg) }         when regenerate $ do           msgDebug ("re-generate " ++ path)-          let maints' = nub (sort (maints ++ ["self.stdenv.lib.maintainers.andres","self.stdenv.lib.maintainers.simons"]))-              plats'-                | null plats && not (null maints) = ["self.ghc.meta.platforms"]-                | otherwise                       = plats-          pkg <- readCabalFile (display (pname deriv)) (display (version deriv))+          pkg <- readCabalFile (pname deriv) (display (version deriv))           let deriv'  = (cabal2nix pkg) { sha256 = sha256 deriv, runHaddock = runHaddock deriv }-              deriv'' = deriv' { metaSection = (metaSection deriv')-                                               { maintainers = maints'+              meta    = metaSection deriv'+              plats'  = if null plats then platforms meta else plats+              deriv'' = deriv' { metaSection = meta+                                               { maintainers = maints ++ ["simons","andres"]                                                , platforms   = plats'                                                }                                }-          io $ writeFile path (show (disp deriv''))+          io $ writeFile path (show (disp (normalize (deriv''))))   pkgset <- gets (selectLatestVersions . _pkgset)   updates' <- flip mapM (Set.elems pkgset) $ \pkg -> do     let Pkg deriv _ _ = pkg-    updates <- discoverUpdates (display (pname deriv)) (display (version deriv))+    updates <- discoverUpdates (pname deriv) (display (version deriv))     return (pkg,updates)   let updates = [ u | u@(_,(_:_)) <- updates' ]   when (not (null updates)) $ do@@ -148,7 +141,7 @@     flip mapM_ updates $ \(pkg,versions) -> do       let Pkg deriv path regenerate = pkg       msgInfo ""-      msgInfo $ (display (pname deriv)) ++ "-" ++ (display (version deriv)) ++ ":"+      msgInfo $ (display (packageId deriv)) ++ ":"       flip mapM_ versions $ \newVersion -> do         let deriv' = deriv { version = readVersion newVersion }         msgInfo $ "  " ++ genCabal2NixCmdline (Pkg deriv' path regenerate)@@ -159,7 +152,7 @@   where     meta = metaSection deriv     opts = [cabal] ++ maints' ++ plats' ++ if runHaddock deriv then [] else ["--no-haddock"]-    cabal = "cabal://" ++ display (pname deriv) ++ "-" ++ display (version deriv)+    cabal = "cabal://" ++ display (packageId deriv)     maints' = [ "--maintainer=" ++ normalizeMaintainer m | m <- maintainers meta ]     plats'       | ["self.ghc.meta.platforms"] == platforms meta = []@@ -186,21 +179,22 @@         ]        usage :: String-      usage = usageInfo "Usage: hackage4nix [options] [dir-or-file ...]" options ++-        "\n\-        \The purpose of 'hackage4nix' is to keep all Haskell packages in our\n\-        \repository packages up-to-date. It scans a checked-out copy of\n\-        \Nixpkgs for expressions that use 'cabal.mkDerivation', and\n\-        \re-generates them in-place with cabal2nix.\n\-        \\n\-        \Because we don't want to generate a barrage of HTTP requests during\n\-        \that procedure, the tool expects a copy of the Hackage database\n\-        \available at some local path, i.e. \"/dev/shm/hackage\" by default.\n\-        \That directory can be set up as follows:\n\-        \\n\-        \  cabal update\n\-        \  mkdir -p /dev/shm/hackage\n\-        \  tar xf ~/.cabal/packages/hackage.haskell.org/00-index.tar -C /dev/shm/hackage\n"+      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."+              , ""+              , "Because we don't want to generate a barrage of HTTP requests during"+              , "that procedure, the tool expects a copy of the Hackage database"+              , "available at some local path, i.e. \"/dev/shm/hackage\" by default."+              , "That directory can be set up as follows:"+              , ""+              , "  cabal update"+              , "  mkdir -p /dev/shm/hackage"+              , "  tar xf ~/.cabal/packages/hackage.haskell.org/00-index.tar -C /dev/shm/hackage\n"+              ]        cmdlineError :: String -> IO a       cmdlineError ""     = hPutStrLn stderr usage >> exitFailure@@ -230,38 +224,6 @@  patchedPackages :: [String] patchedPackages =-   [ "alex"              -- undeclared dependencies-   , "cairo"             -- undeclared dependencies-   , "citeproc-hs"       -- undeclared dependencies-   , "editline"          -- undeclared dependencies-   , "glade"             -- undeclared dependencies-   , "glib"              -- undeclared dependencies-   , "epic"              -- undeclared dependencies-   , "GLUT"              -- undeclared dependencies-   , "gtk"               -- undeclared dependencies-   , "gtksourceview2"    -- undeclared dependencies-   , "haddock"           -- undeclared dependencies-   , "haskell-src"       -- undeclared dependencies-   , "hmatrix"           -- undeclared dependencies-   , "hp2any-graph"      -- undeclared dependencies-   , "lhs2tex"           -- undeclared dependencies-   , "OpenAL"            -- undeclared dependencies-   , "OpenGL"            -- undeclared dependencies-   , "pango"             -- undeclared dependencies-   , "idris"             -- undeclared dependencies-   , "readline"          -- undeclared dependencies-   , "repa-examples"     -- undeclared dependencies-   , "scion"             -- expects non-existent networkBytestring-   , "SDL"               -- undeclared dependencies-   , "SDL-image"         -- undeclared dependencies-   , "SDL-mixer"         -- undeclared dependencies-   , "SDL-ttf"           -- undeclared dependencies-   , "svgcairo"          -- undeclared dependencies-   , "terminfo"          -- undeclared dependencies-   , "xmonad"            -- undeclared dependencies-   , "xmonad-extras"     -- undeclared dependencies-   , "vacuum"            -- undeclared dependencies-   , "wxcore"            -- undeclared dependencies-   , "X11"               -- undeclared dependencies-   , "X11-xft"           -- undeclared dependencies+   [ "X11"               -- expression uses function arguments to determine feature set+   , "xmonad-extras"     -- the generated expression doesn't build, don't know why    ]
+ src/PackageList.hs view
@@ -0,0 +1,73 @@+module Main ( main ) where++import Control.Exception ( assert )+import Control.Monad ( when, filterM )+import Data.Char ( toLower )+import Data.List ( nubBy, sortBy )+import Data.Ord ( comparing )+import Data.Version ( Version(..) )+import Distribution.Package ( PackageIdentifier(..), PackageName(..) )+import Distribution.Text ( simpleParse, display )+import System.Directory ( doesDirectoryExist, doesFileExist )+import System.FilePath ( (</>), (<.>) )+import System.Process ( readProcess )+import Text.Regex.Posix ( (=~), match, makeRegexOpts, compExtended, execBlank )++type Pkg    = (String,Version,String) -- (Name, Version, Attribute)+type Pkgset = [Pkg]++comparePkgByVersion :: Pkg -> Pkg -> Ordering  -- prefers the latest version+comparePkgByVersion (n1,v1,a1) (n2,v2,a2)+  | a1 == a2    = assert (n1 == n2) (compare v2 v1)+  | otherwise   = compare a2 a1++comparePkgByName :: Pkg -> Pkg-> Ordering+comparePkgByName (n1,_,_) (n2,_,_) = comparing (map toLower) n1 n2++parseHaskellPackageName :: String -> Maybe Pkg+parseHaskellPackageName name =+  case name `regsubmatch` "^(haskellPackages[^ \t]+)[ \t]+(.+)$" of+    [attr,name'] -> case name' `regsubmatch` "^haskell-(.+)-ghc[0-9.]+-(.+)$" of+                      [name'',version] -> case simpleParse version of+                                            Just version' -> Just (name'',version',attr)+                                            _             -> error ("cannot parse " ++ show name)+                      _                -> case simpleParse name' of+                                            Just (PackageIdentifier (PackageName n) v) -> Just (n,v,attr)+                                            _                                          -> error ("cannot parse " ++ show name)+    _            -> Nothing++getHaskellPackageList :: IO Pkgset+getHaskellPackageList = do+  allPkgs <- fmap lines (readProcess "bash" ["-c", "exec nix-env -qaP \\* 2>/dev/tty"] "")+  return [ p | Just p <- map parseHaskellPackageName allPkgs ]++stripProfilingVersions :: Pkgset -> Pkgset+stripProfilingVersions pkgs = [ p | p@(_,_,attr) <- pkgs , not (attr =~ "ghc[0-9.]+_profiling") ]++stripGhc721Versions :: Pkgset -> Pkgset+stripGhc721Versions pkgs = [ p | p@(_,_,attr) <- pkgs , not (attr =~ "ghc721") ]++selectLatestVersions :: Pkgset -> Pkgset+selectLatestVersions = nubBy (\x y -> comparePkgByName x y == EQ) . sortBy comparePkgByVersion++isHackagePackage :: Pkg -> IO Bool+isHackagePackage (name,version,_) = doesFileExist path+  where path = "/dev/shm/hackage/" </> name </> display version </> name <.> "cabal"++formatPackageLine :: Pkg -> String+formatPackageLine (name,version,attr) = show (name, display version, Just url)+  where+    url = "http://hydra.nixos.org/job/nixpkgs/trunk/" ++ attr++regsubmatch :: String -> String -> [String]+regsubmatch buf patt = let (_,_,_,x) = f in x+  where f :: (String,String,String,[String])+        f = match (makeRegexOpts compExtended execBlank patt) buf++main :: IO ()+main = do+  haveHackage <- doesDirectoryExist "/dev/shm/hackage"+  when (not haveHackage) (fail "cannot find hackage database at /dev/shm/hackage")+  pkgset' <- fmap (selectLatestVersions . stripGhc721Versions . stripProfilingVersions) getHaskellPackageList+  pkgset <- filterM isHackagePackage pkgset'+  mapM_ (putStrLn . formatPackageLine) (sortBy comparePkgByName pkgset)