module PackageManagement ( Transplantable(..) , parseVersion , parsePkgInfo ) where import Distribution.Package (PackageIdentifier(..), PackageName(..)) import Distribution.Version (Version(..)) import Control.Monad (unless) import Types import MyMonad import Process (outsideGhcPkg, insideGhcPkg) import Util.Cabal (prettyPkgInfo, prettyVersion) import qualified Util.Cabal (parseVersion, parsePkgInfo) parseVersion :: String -> MyMonad Version parseVersion s = case Util.Cabal.parseVersion s of Nothing -> throwError $ MyException $ "Couldn't parse " ++ s ++ " as a package version" Just version -> return version parsePkgInfo :: String -> MyMonad PackageIdentifier parsePkgInfo s = case Util.Cabal.parsePkgInfo s of Nothing -> throwError $ MyException $ "Couldn't parse package identifier " ++ s Just pkgInfo -> return pkgInfo getDeps :: PackageIdentifier -> MyMonad [PackageIdentifier] getDeps pkgInfo = do let prettyPkg = prettyPkgInfo pkgInfo debug $ "Extracting dependencies of " ++ prettyPkg out <- indentMessages $ outsideGhcPkg ["field", prettyPkg, "depends"] -- example output: -- depends: ghc-prim-0.2.0.0-3fbcc20c802efcd7c82089ec77d92990 -- integer-gmp-0.2.0.0-fa82a0df93dc30b4a7c5654dd7c68cf4 builtin_rts case words out of [] -> throwError $ MyException $ "Couldn't parse ghc-pkg output to find dependencies of " ++ prettyPkg _:depStrings -> do -- skip 'depends:' indentMessages $ trace $ "Found dependency strings: " ++ unwords depStrings mapM parsePkgInfo depStrings -- things that can be copied from system's GHC pkg database -- to GHC pkg database inside virtual environment class Transplantable a where transplantPackage :: a -> MyMonad () -- choose the highest installed version of package with this name instance Transplantable PackageName where transplantPackage (PackageName packageName) = do debug $ "Copying package " ++ packageName ++ " to Virtual Haskell Environment." indentMessages $ do debug "Choosing package with highest version number." out <- indentMessages $ outsideGhcPkg ["field", packageName, "version"] -- example output: -- version: 1.1.4 -- version: 1.2.0.3 let extractVersionString :: String -> MyMonad String extractVersionString line = case words line of [_, x] -> return x _ -> throwError $ MyException $ "Couldn't extract version string from: " ++ line versionStrings <- mapM extractVersionString $ lines out indentMessages $ trace $ "Found version strings: " ++ unwords versionStrings versions <- mapM parseVersion versionStrings case versions of [] -> throwError $ MyException $ "No versions of package " ++ packageName ++ " found" (v:vs) -> do indentMessages $ debug $ "Found: " ++ unwords (map prettyVersion versions) let highestVersion = foldr max v vs indentMessages $ debug $ "Using version: " ++ prettyVersion highestVersion let pkgInfo = PackageIdentifier (PackageName packageName) highestVersion transplantPackage pkgInfo -- check if this package is already installed in Virtual Haskell Environment checkIfInstalled :: PackageIdentifier -> MyMonad Bool checkIfInstalled pkgInfo = do let package = prettyPkgInfo pkgInfo debug $ "Checking if " ++ package ++ " is already installed." (do _ <- indentMessages $ insideGhcPkg ["describe", package] Nothing indentMessages $ debug "It is." return True) `catchError` handler where handler _ = do debug "It's not." return False instance Transplantable PackageIdentifier where transplantPackage pkgInfo = do let prettyPkg = prettyPkgInfo pkgInfo debug $ "Copying package " ++ prettyPkg ++ " to Virtual Haskell Environment." indentMessages $ do flag <- checkIfInstalled pkgInfo unless flag $ do deps <- getDeps pkgInfo debug $ "Found: " ++ unwords (map prettyPkgInfo deps) mapM_ transplantPackage deps movePackage pkgInfo -- copy single package that already has all deps satisfied movePackage :: PackageIdentifier -> MyMonad () movePackage pkgInfo = do let prettyPkg = prettyPkgInfo pkgInfo debug $ "Moving package " ++ prettyPkg ++ " to Virtual Haskell Environment." out <- outsideGhcPkg ["describe", prettyPkg] _ <- insideGhcPkg ["register", "-"] (Just out) return ()