packages feed

cabal2nix-1.12: src/PackageList.hs

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)