summaryrefslogtreecommitdiff
path: root/utils/ghc-pkg
diff options
context:
space:
mode:
authorAndrzej Rybczak <electricityispower@gmail.com>2017-02-26 16:25:17 -0500
committerBen Gamari <ben@smart-cactus.org>2017-02-26 16:34:19 -0500
commit0d86aa5904e5a06c93632357122e57e4e118fd2a (patch)
treeb2ee94d50b66f2cf4f241e8594b0c4b4acb14c36 /utils/ghc-pkg
parentb494689c30fd0394423f264792530d1f352e1ee7 (diff)
downloadhaskell-0d86aa5904e5a06c93632357122e57e4e118fd2a.tar.gz
Add support for concurrent package db access and updates
Trac issues: #13194 Reviewers: austin, hvr, erikd, bgamari, dfeuer, duncan Subscribers: DemiMarie, dfeuer, thomie Differential Revision: https://phabricator.haskell.org/D3090
Diffstat (limited to 'utils/ghc-pkg')
-rw-r--r--utils/ghc-pkg/Main.hs390
1 files changed, 259 insertions, 131 deletions
diff --git a/utils/ghc-pkg/Main.hs b/utils/ghc-pkg/Main.hs
index 3355838477..44960ca0b6 100644
--- a/utils/ghc-pkg/Main.hs
+++ b/utils/ghc-pkg/Main.hs
@@ -1,7 +1,13 @@
{-# LANGUAGE CPP #-}
-{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE TypeSynonymInstances #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE KindSignatures #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE TupleSections #-}
+{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
-----------------------------------------------------------------------------
--
@@ -53,6 +59,8 @@ import System.IO.Error
import GHC.IO.Exception (IOErrorType(InappropriateType))
import Data.List
import Control.Concurrent
+import qualified Data.Foldable as F
+import qualified Data.Traversable as F
import qualified Data.Set as Set
import qualified Data.Map as Map
@@ -527,7 +535,7 @@ readPackageArg AsDefault str = Id `fmap` readGlobPkgId str
-- Some commands operate on multiple databases, with overlapping semantics:
-- list, describe, field
-data PackageDB
+data PackageDB (mode :: GhcPkg.DbMode)
= PackageDB {
location, locationAbsolute :: !FilePath,
-- We need both possibly-relative and definitely-absolute package
@@ -536,18 +544,27 @@ data PackageDB
-- On the other hand we need the absolute path in a few places
-- particularly in relation to the ${pkgroot} stuff.
+ packageDbLock :: !(GhcPkg.DbOpenMode mode GhcPkg.PackageDbLock),
+ -- If package db is open in read write mode, we keep its lock around for
+ -- transactional updates.
+
packages :: [InstalledPackageInfo]
}
-type PackageDBStack = [PackageDB]
+type PackageDBStack = [PackageDB 'GhcPkg.DbReadOnly]
-- A stack of package databases. Convention: head is the topmost
-- in the stack.
+-- | Selector for picking the right package DB to modify as 'register' and
+-- 'recache' operate on the database on top of the stack, whereas 'modify'
+-- changes the first database that contains a specific package.
+data DbModifySelector = TopOne | ContainsPkg PackageArg
+
allPackagesInStack :: PackageDBStack -> [InstalledPackageInfo]
allPackagesInStack = concatMap packages
getPkgDatabases :: Verbosity
- -> Bool -- we are modifying, not reading
+ -> GhcPkg.DbOpenMode mode DbModifySelector
-> Bool -- use the user db
-> Bool -- read caches, if available
-> Bool -- expand vars, like ${pkgroot} and $topdir
@@ -555,7 +572,7 @@ getPkgDatabases :: Verbosity
-> IO (PackageDBStack,
-- the real package DB stack: [global,user] ++
-- DBs specified on the command line with -f.
- Maybe FilePath,
+ GhcPkg.DbOpenMode mode (PackageDB mode),
-- which one to modify, if any
PackageDBStack)
-- the package DBs specified on the command
@@ -563,7 +580,7 @@ getPkgDatabases :: Verbosity
-- is used as the list of package DBs for
-- commands that just read the DB, such as 'list'.
-getPkgDatabases verbosity modify use_user use_cache expand_vars my_flags = do
+getPkgDatabases verbosity mode use_user use_cache expand_vars my_flags = do
-- first we determine the location of the global package config. On Windows,
-- this is found relative to the ghc-pkg.exe binary, whereas on Unix the
-- location is passed to the binary using the --global-package-db flag by the
@@ -652,29 +669,117 @@ getPkgDatabases verbosity modify use_user use_cache expand_vars my_flags = do
[ f | FlagConfig f <- reverse my_flags ]
++ env_stack
- -- the database we actually modify is the one mentioned
- -- rightmost on the command-line.
- let to_modify
- | not modify = Nothing
- | null db_flags = Just virt_global_conf
- | otherwise = Just (last db_flags)
+ top_db = if null db_flags
+ then virt_global_conf
+ else last db_flags
- db_stack <- sequence
- [ do db <- readParseDatabase verbosity mb_user_conf modify use_cache db_path
- if expand_vars then return (mungePackageDBPaths top_dir db)
- else return db
- | db_path <- final_stack ]
+ (db_stack, db_to_operate_on) <- getDatabases top_dir mb_user_conf
+ flag_db_names final_stack top_db
let flag_db_stack = [ db | db_name <- flag_db_names,
db <- db_stack, location db == db_name ]
when (verbosity > Normal) $ do
infoLn ("db stack: " ++ show (map location db_stack))
- infoLn ("modifying: " ++ show to_modify)
+ F.forM_ db_to_operate_on $ \db ->
+ infoLn ("modifying: " ++ (location db))
infoLn ("flag db stack: " ++ show (map location flag_db_stack))
- return (db_stack, to_modify, flag_db_stack)
-
+ return (db_stack, db_to_operate_on, flag_db_stack)
+ where
+ getDatabases top_dir mb_user_conf flag_db_names
+ final_stack top_db = case mode of
+ -- When we open in read only mode, we simply read all of the databases/
+ GhcPkg.DbOpenReadOnly -> do
+ db_stack <- mapM readDatabase final_stack
+ return (db_stack, GhcPkg.DbOpenReadOnly)
+
+ -- The only package db we open in read write mode is the one on the top of
+ -- the stack.
+ GhcPkg.DbOpenReadWrite TopOne -> do
+ (db_stack, mto_modify) <- stateSequence Nothing
+ [ \case
+ to_modify@(Just _) -> (, to_modify) <$> readDatabase db_path
+ Nothing -> if db_path /= top_db
+ then (, Nothing) <$> readDatabase db_path
+ else do
+ db <- readParseDatabase verbosity mb_user_conf
+ mode use_cache db_path
+ `Exception.catch` couldntOpenDbForModification db_path
+ let ro_db = db { packageDbLock = GhcPkg.DbOpenReadOnly }
+ return (ro_db, Just db)
+ | db_path <- final_stack ]
+
+ to_modify <- case mto_modify of
+ Just db -> return db
+ Nothing -> die "no database selected for modification"
+
+ return (db_stack, GhcPkg.DbOpenReadWrite to_modify)
+
+ -- The package db we open in read write mode is the first one included in
+ -- flag_db_names that contains specified package. Therefore we need to
+ -- open each one in read/write mode first and decide whether it's for
+ -- modification based on its contents.
+ GhcPkg.DbOpenReadWrite (ContainsPkg pkgarg) -> do
+ (db_stack, mto_modify) <- stateSequence Nothing
+ [ \case
+ to_modify@(Just _) -> (, to_modify) <$> readDatabase db_path
+ Nothing -> if db_path `notElem` flag_db_names
+ then (, Nothing) <$> readDatabase db_path
+ else do
+ let hasPkg :: PackageDB mode -> Bool
+ hasPkg = not . null . findPackage pkgarg . packages
+
+ openRo (e::IOError) = do
+ db <- readDatabase db_path
+ if hasPkg db
+ then couldntOpenDbForModification db_path e
+ else return (db, Nothing)
+
+ -- If we fail to open the database in read/write mode, we need
+ -- to check if it's for modification first before throwing an
+ -- error, so we attempt to open it in read only mode.
+ Exception.handle openRo $ do
+ db <- readParseDatabase verbosity mb_user_conf
+ mode use_cache db_path
+ let ro_db = db { packageDbLock = GhcPkg.DbOpenReadOnly }
+ if hasPkg db
+ then return (ro_db, Just db)
+ else do
+ -- If the database is not for modification after all,
+ -- drop the write lock as we are already finished with
+ -- the database.
+ case packageDbLock db of
+ GhcPkg.DbOpenReadWrite lock ->
+ GhcPkg.unlockPackageDb lock
+ return (ro_db, Nothing)
+ | db_path <- final_stack ]
+
+ to_modify <- case mto_modify of
+ Just db -> return db
+ Nothing -> cannotFindPackage pkgarg Nothing
+
+ return (db_stack, GhcPkg.DbOpenReadWrite to_modify)
+ where
+ couldntOpenDbForModification :: FilePath -> IOError -> IO a
+ couldntOpenDbForModification db_path e = die $ "Couldn't open database "
+ ++ db_path ++ " for modification: " ++ show e
+
+ -- Parse package db in read-only mode.
+ readDatabase :: FilePath -> IO (PackageDB 'GhcPkg.DbReadOnly)
+ readDatabase db_path = do
+ db <- readParseDatabase verbosity mb_user_conf
+ GhcPkg.DbOpenReadOnly use_cache db_path
+ if expand_vars
+ then return $ mungePackageDBPaths top_dir db
+ else return db
+
+ stateSequence :: Monad m => s -> [s -> m (a, s)] -> m ([a], s)
+ stateSequence s [] = return ([], s)
+ stateSequence s (m:ms) = do
+ (a, s') <- m s
+ (as, s'') <- stateSequence s' ms
+ return (a : as, s'')
lookForPackageDBIn :: FilePath -> IO (Maybe FilePath)
lookForPackageDBIn dir = do
@@ -685,17 +790,16 @@ lookForPackageDBIn dir = do
exists_file <- doesFileExist path_file
if exists_file then return (Just path_file) else return Nothing
-readParseDatabase :: Verbosity
+readParseDatabase :: forall mode t. Verbosity
-> Maybe (FilePath,Bool)
- -> Bool -- we will be modifying, not just reading
+ -> GhcPkg.DbOpenMode mode t
-> Bool -- use cache
-> FilePath
- -> IO PackageDB
-
-readParseDatabase verbosity mb_user_conf modify use_cache path
+ -> IO (PackageDB mode)
+readParseDatabase verbosity mb_user_conf mode use_cache path
-- the user database (only) is allowed to be non-existent
| Just (user_conf,False) <- mb_user_conf, path == user_conf
- = mkPackageDB []
+ = mkPackageDB [] =<< F.mapM (const $ GhcPkg.lockPackageDb path) mode
| otherwise
= do e <- tryIO $ getDirectoryContents path
case e of
@@ -704,7 +808,7 @@ readParseDatabase verbosity mb_user_conf modify use_cache path
-- We provide a limited degree of backwards compatibility for
-- old single-file style db:
mdb <- tryReadParseOldFileStyleDatabase verbosity
- mb_user_conf modify use_cache path
+ mb_user_conf mode use_cache path
case mdb of
Just db -> return db
Nothing ->
@@ -750,8 +854,8 @@ readParseDatabase verbosity mb_user_conf modify use_cache path
then do
when (verbosity > Normal) $
infoLn ("using cache: " ++ cache)
- pkgs <- GhcPkg.readPackageDbForGhcPkg cache
- mkPackageDB pkgs
+ GhcPkg.readPackageDbForGhcPkg cache mode
+ >>= uncurry mkPackageDB
else do
whenReportCacheErrors $ do
warn ("WARNING: cache is out of date: " ++ cache)
@@ -759,19 +863,22 @@ readParseDatabase verbosity mb_user_conf modify use_cache path
"package db. " ++ recacheAdvice)
ignore_cache compareTimestampToCache
where
- ignore_cache :: (FilePath -> IO ()) -> IO PackageDB
+ ignore_cache :: (FilePath -> IO ()) -> IO (PackageDB mode)
ignore_cache checkTime = do
+ -- If we're opening for modification, we need to acquire a
+ -- lock even if we don't open the cache now, because we are
+ -- going to modify it later.
+ lock <- F.mapM (const $ GhcPkg.lockPackageDb path) mode
let confs = filter (".conf" `isSuffixOf`) fs
doFile f = do checkTime f
parseSingletonPackageConf verbosity f
pkgs <- mapM doFile $ map (path </>) confs
- mkPackageDB pkgs
+ mkPackageDB pkgs lock
-- We normally report cache errors for read-only commands,
- -- since modify commands because will usually fix the cache.
- whenReportCacheErrors =
- when ( verbosity > Normal
- || verbosity >= Normal && not modify)
+ -- since modify commands will usually fix the cache.
+ whenReportCacheErrors = when $ verbosity > Normal
+ || verbosity >= Normal && GhcPkg.isDbOpenReadMode mode
where
recacheAdvice
| Just (user_conf, True) <- mb_user_conf, path == user_conf
@@ -779,13 +886,17 @@ readParseDatabase verbosity mb_user_conf modify use_cache path
| otherwise
= "Use 'ghc-pkg recache' to fix."
- mkPackageDB pkgs = do
+ mkPackageDB :: [InstalledPackageInfo]
+ -> GhcPkg.DbOpenMode mode GhcPkg.PackageDbLock
+ -> IO (PackageDB mode)
+ mkPackageDB pkgs lock = do
path_abs <- absolutePath path
- return PackageDB {
- location = path,
- locationAbsolute = path_abs,
- packages = pkgs
- }
+ return $ PackageDB {
+ location = path,
+ locationAbsolute = path_abs,
+ packageDbLock = lock,
+ packages = pkgs
+ }
parseSingletonPackageConf :: Verbosity -> FilePath -> IO InstalledPackageInfo
parseSingletonPackageConf verbosity file = do
@@ -795,7 +906,7 @@ parseSingletonPackageConf verbosity file = do
cachefilename :: FilePath
cachefilename = "package.cache"
-mungePackageDBPaths :: FilePath -> PackageDB -> PackageDB
+mungePackageDBPaths :: FilePath -> PackageDB mode -> PackageDB mode
mungePackageDBPaths top_dir db@PackageDB { packages = pkgs } =
db { packages = map (mungePackagePaths top_dir pkgroot) pkgs }
where
@@ -872,44 +983,48 @@ mungePackagePaths top_dir pkgroot pkg =
-- ghc itself also cooperates in this workaround
tryReadParseOldFileStyleDatabase :: Verbosity -> Maybe (FilePath, Bool)
- -> Bool -> Bool -> FilePath
- -> IO (Maybe PackageDB)
-tryReadParseOldFileStyleDatabase verbosity mb_user_conf modify use_cache path = do
+ -> GhcPkg.DbOpenMode mode t -> Bool -> FilePath
+ -> IO (Maybe (PackageDB mode))
+tryReadParseOldFileStyleDatabase verbosity mb_user_conf
+ mode use_cache path = do
-- assumes we've already established that path exists and is not a dir
content <- readFile path `catchIO` \_ -> return ""
if take 2 content == "[]"
then do
path_abs <- absolutePath path
- let path_dir = path <.> "d"
+ let path_dir = adjustOldDatabasePath path
warn $ "Warning: ignoring old file-style db and trying " ++ path_dir
direxists <- doesDirectoryExist path_dir
if direxists
- then do db <- readParseDatabase verbosity mb_user_conf
- modify use_cache path_dir
- -- but pretend it was at the original location
- return $ Just db {
- location = path,
- locationAbsolute = path_abs
- }
- else return $ Just PackageDB {
- location = path,
- locationAbsolute = path_abs,
- packages = []
- }
+ then do
+ db <- readParseDatabase verbosity mb_user_conf mode use_cache path_dir
+ -- but pretend it was at the original location
+ return $ Just db {
+ location = path,
+ locationAbsolute = path_abs
+ }
+ else do
+ lock <- F.mapM (const $ GhcPkg.lockPackageDb path_dir) mode
+ return $ Just PackageDB {
+ location = path,
+ locationAbsolute = path_abs,
+ packageDbLock = lock,
+ packages = []
+ }
-- if the path is not a file, or is not an empty db then we fail
else return Nothing
-adjustOldFileStylePackageDB :: PackageDB -> IO PackageDB
+adjustOldFileStylePackageDB :: PackageDB mode -> IO (PackageDB mode)
adjustOldFileStylePackageDB db = do
-- assumes we have not yet established if it's an old style or not
mcontent <- liftM Just (readFile (location db)) `catchIO` \_ -> return Nothing
case fmap (take 2) mcontent of
-- it is an old style and empty db, so look for a dir kind in location.d/
Just "[]" -> return db {
- location = location db <.> "d",
- locationAbsolute = locationAbsolute db <.> "d"
- }
+ location = adjustOldDatabasePath $ location db,
+ locationAbsolute = adjustOldDatabasePath $ locationAbsolute db
+ }
-- it is old style but not empty, we have to bail
Just _ -> die $ "ghc no longer supports single-file style package "
++ "databases (" ++ location db ++ ") use 'ghc-pkg init'"
@@ -917,6 +1032,8 @@ adjustOldFileStylePackageDB db = do
-- probably not old style, carry on as normal
Nothing -> return db
+adjustOldDatabasePath :: FilePath -> FilePath
+adjustOldDatabasePath = (<.> "d")
-- -----------------------------------------------------------------------------
-- Creating a new package DB
@@ -928,11 +1045,15 @@ initPackageDB filename verbosity _flags = do
when b1 eexist
b2 <- doesDirectoryExist filename
when b2 eexist
+ createDirectoryIfMissing True filename
+ lock <- GhcPkg.lockPackageDb $ filename </> cachefilename
filename_abs <- absolutePath filename
changeDB verbosity [] PackageDB {
- location = filename, locationAbsolute = filename_abs,
- packages = []
- }
+ location = filename,
+ locationAbsolute = filename_abs,
+ packageDbLock = GhcPkg.DbOpenReadWrite lock,
+ packages = []
+ }
-- -----------------------------------------------------------------------------
-- Registering
@@ -947,13 +1068,12 @@ registerPackage :: FilePath
-> IO ()
registerPackage input verbosity my_flags multi_instance
expand_env_vars update force = do
- (db_stack, Just to_modify, _flag_dbs) <-
- getPkgDatabases verbosity True{-modify-} True{-use user-}
- True{-use cache-} False{-expand vars-} my_flags
+ (db_stack, GhcPkg.DbOpenReadWrite db_to_operate_on, _flag_dbs) <-
+ getPkgDatabases verbosity (GhcPkg.DbOpenReadWrite TopOne)
+ True{-use user-} True{-use cache-} False{-expand vars-} my_flags
+
+ let to_modify = location db_to_operate_on
- let
- db_to_operate_on = my_head "register" $
- filter ((== to_modify).location) db_stack
s <-
case input of
"-" -> do
@@ -1026,14 +1146,15 @@ data DBOp = RemovePackage InstalledPackageInfo
| AddPackage InstalledPackageInfo
| ModifyPackage InstalledPackageInfo
-changeDB :: Verbosity -> [DBOp] -> PackageDB -> IO ()
+changeDB :: Verbosity -> [DBOp] -> PackageDB 'GhcPkg.DbReadWrite -> IO ()
changeDB verbosity cmds db = do
let db' = updateInternalDB db cmds
db'' <- adjustOldFileStylePackageDB db'
createDirectoryIfMissing True (location db'')
changeDBDir verbosity cmds db''
-updateInternalDB :: PackageDB -> [DBOp] -> PackageDB
+updateInternalDB :: PackageDB 'GhcPkg.DbReadWrite
+ -> [DBOp] -> PackageDB 'GhcPkg.DbReadWrite
updateInternalDB db cmds = db{ packages = foldl do_cmd (packages db) cmds }
where
do_cmd pkgs (RemovePackage p) =
@@ -1043,7 +1164,7 @@ updateInternalDB db cmds = db{ packages = foldl do_cmd (packages db) cmds }
do_cmd (do_cmd pkgs (RemovePackage p)) (AddPackage p)
-changeDBDir :: Verbosity -> [DBOp] -> PackageDB -> IO ()
+changeDBDir :: Verbosity -> [DBOp] -> PackageDB 'GhcPkg.DbReadWrite -> IO ()
changeDBDir verbosity cmds db = do
mapM_ do_cmd cmds
updateDBCache verbosity db
@@ -1059,7 +1180,7 @@ changeDBDir verbosity cmds db = do
do_cmd (ModifyPackage p) =
do_cmd (AddPackage p)
-updateDBCache :: Verbosity -> PackageDB -> IO ()
+updateDBCache :: Verbosity -> PackageDB 'GhcPkg.DbReadWrite -> IO ()
updateDBCache verbosity db = do
let filename = location db </> cachefilename
@@ -1071,20 +1192,25 @@ updateDBCache verbosity db = do
when (verbosity > Normal) $
infoLn ("writing cache " ++ filename)
+
GhcPkg.writePackageDb filename pkgsGhcCacheFormat pkgsCabalFormat
`catchIO` \e ->
if isPermissionError e
- then die (filename ++ ": you don't have permission to modify this file")
+ then die $ filename ++ ": you don't have permission to modify this file"
else ioError e
+
-- See Note [writeAtomic leaky abstraction]
- -- Cross-platform "touch". This only works if filename is not empty, and not
- -- open for writing already.
+ -- Cross-platform "touch". This only works if filename is not empty, and
+ -- not open for writing already.
-- TODO. When the Win32 or directory packages have either a touchFile or a
-- setModificationTime function, use one of those.
withBinaryFile filename ReadWriteMode $ \handle -> do
- c <- hGetChar handle
- hSeek handle AbsoluteSeek 0
- hPutChar handle c
+ c <- hGetChar handle
+ hSeek handle AbsoluteSeek 0
+ hPutChar handle c
+
+ case packageDbLock db of
+ GhcPkg.DbOpenReadWrite lock -> GhcPkg.unlockPackageDb lock
type PackageCacheFormat = GhcPkg.InstalledPackageInfo
ComponentId
@@ -1192,25 +1318,29 @@ modifyPackage
-> Force
-> IO ()
modifyPackage fn pkgarg verbosity my_flags force = do
- (db_stack, Just _to_modify, flag_dbs) <-
- getPkgDatabases verbosity True{-modify-} True{-use user-}
- True{-use cache-} False{-expand vars-} my_flags
+ (db_stack, GhcPkg.DbOpenReadWrite db, _flag_dbs) <-
+ getPkgDatabases verbosity (GhcPkg.DbOpenReadWrite $ ContainsPkg pkgarg)
+ True{-use user-} True{-use cache-} False{-expand vars-} my_flags
- -- Do the search for the package respecting flags...
- (db, ps) <- fmap head $ findPackagesByDB flag_dbs pkgarg
- let
- db_name = location db
+ let db_name = location db
pkgs = packages db
- pks = map installedUnitId ps
+ -- Get package respecting flags...
+ ps = findPackage pkgarg pkgs
+
+ -- This shouldn't happen if getPkgDatabases picks the DB correctly.
+ when (null ps) $ cannotFindPackage pkgarg $ Just db
+
+ let pks = map installedUnitId ps
cmds = [ fn pkg | pkg <- pkgs, installedUnitId pkg `elem` pks ]
new_db = updateInternalDB db cmds
+ new_db_ro = new_db { packageDbLock = GhcPkg.DbOpenReadOnly }
-- ...but do consistency checks with regards to the full stack
old_broken = brokenPackages (allPackagesInStack db_stack)
rest_of_stack = filter ((/= db_name) . location) db_stack
- new_stack = new_db : rest_of_stack
+ new_stack = new_db_ro : rest_of_stack
new_broken = brokenPackages (allPackagesInStack new_stack)
newly_broken = filter ((`notElem` map installedUnitId old_broken)
. installedUnitId) new_broken
@@ -1229,13 +1359,9 @@ modifyPackage fn pkgarg verbosity my_flags force = do
recache :: Verbosity -> [Flag] -> IO ()
recache verbosity my_flags = do
- (db_stack, Just to_modify, _flag_dbs) <-
- getPkgDatabases verbosity True{-modify-} True{-use user-} False{-no cache-}
- False{-expand vars-} my_flags
- let
- db_to_operate_on = my_head "recache" $
- filter ((== to_modify).location) db_stack
- --
+ (_db_stack, GhcPkg.DbOpenReadWrite db_to_operate_on, _flag_dbs) <-
+ getPkgDatabases verbosity (GhcPkg.DbOpenReadWrite TopOne)
+ True{-use user-} False{-no cache-} False{-expand vars-} my_flags
changeDB verbosity [] db_to_operate_on
-- -----------------------------------------------------------------------------
@@ -1246,9 +1372,9 @@ listPackages :: Verbosity -> [Flag] -> Maybe PackageArg
-> IO ()
listPackages verbosity my_flags mPackageName mModuleName = do
let simple_output = FlagSimpleOutput `elem` my_flags
- (db_stack, _, flag_db_stack) <-
- getPkgDatabases verbosity False{-modify-} False{-use user-}
- True{-use cache-} False{-expand vars-} my_flags
+ (db_stack, GhcPkg.DbOpenReadOnly, flag_db_stack) <-
+ getPkgDatabases verbosity GhcPkg.DbOpenReadOnly
+ False{-use user-} True{-use cache-} False{-expand vars-} my_flags
let db_stack_filtered -- if a package is given, filter out all other packages
| Just this <- mPackageName =
@@ -1346,9 +1472,9 @@ simplePackageList my_flags pkgs = do
showPackageDot :: Verbosity -> [Flag] -> IO ()
showPackageDot verbosity myflags = do
- (_, _, flag_db_stack) <-
- getPkgDatabases verbosity False{-modify-} False{-use user-}
- True{-use cache-} False{-expand vars-} myflags
+ (_, GhcPkg.DbOpenReadOnly, flag_db_stack) <-
+ getPkgDatabases verbosity GhcPkg.DbOpenReadOnly
+ False{-use user-} True{-use cache-} False{-expand vars-} myflags
let all_pkgs = allPackagesInStack flag_db_stack
ipix = PackageIndex.fromList all_pkgs
@@ -1371,9 +1497,9 @@ showPackageDot verbosity myflags = do
-- dependencies may be varying versions
latestPackage :: Verbosity -> [Flag] -> GlobPackageIdentifier -> IO ()
latestPackage verbosity my_flags pkgid = do
- (_, _, flag_db_stack) <-
- getPkgDatabases verbosity False{-modify-} False{-use user-}
- True{-use cache-} False{-expand vars-} my_flags
+ (_, GhcPkg.DbOpenReadOnly, flag_db_stack) <-
+ getPkgDatabases verbosity GhcPkg.DbOpenReadOnly
+ False{-use user-} True{-use cache-} False{-expand vars-} my_flags
ps <- findPackages flag_db_stack (Id pkgid)
case ps of
@@ -1387,18 +1513,18 @@ latestPackage verbosity my_flags pkgid = do
describePackage :: Verbosity -> [Flag] -> PackageArg -> Bool -> IO ()
describePackage verbosity my_flags pkgarg expand_pkgroot = do
- (_, _, flag_db_stack) <-
- getPkgDatabases verbosity False{-modify-} False{-use user-}
- True{-use cache-} expand_pkgroot my_flags
+ (_, GhcPkg.DbOpenReadOnly, flag_db_stack) <-
+ getPkgDatabases verbosity GhcPkg.DbOpenReadOnly
+ False{-use user-} True{-use cache-} expand_pkgroot my_flags
dbs <- findPackagesByDB flag_db_stack pkgarg
doDump expand_pkgroot [ (pkg, locationAbsolute db)
| (db, pkgs) <- dbs, pkg <- pkgs ]
dumpPackages :: Verbosity -> [Flag] -> Bool -> IO ()
dumpPackages verbosity my_flags expand_pkgroot = do
- (_, _, flag_db_stack) <-
- getPkgDatabases verbosity False{-modify-} False{-use user-}
- True{-use cache-} expand_pkgroot my_flags
+ (_, GhcPkg.DbOpenReadOnly, flag_db_stack) <-
+ getPkgDatabases verbosity GhcPkg.DbOpenReadOnly
+ False{-use user-} True{-use cache-} expand_pkgroot my_flags
doDump expand_pkgroot [ (pkg, locationAbsolute db)
| db <- flag_db_stack, pkg <- packages db ]
@@ -1420,19 +1546,26 @@ findPackages :: PackageDBStack -> PackageArg -> IO [InstalledPackageInfo]
findPackages db_stack pkgarg
= fmap (concatMap snd) $ findPackagesByDB db_stack pkgarg
+findPackage :: PackageArg -> [InstalledPackageInfo] -> [InstalledPackageInfo]
+findPackage pkgarg pkgs = filter (pkgarg `matchesPkg`) pkgs
+
findPackagesByDB :: PackageDBStack -> PackageArg
- -> IO [(PackageDB, [InstalledPackageInfo])]
+ -> IO [(PackageDB 'GhcPkg.DbReadOnly, [InstalledPackageInfo])]
findPackagesByDB db_stack pkgarg
= case [ (db, matched)
| db <- db_stack,
- let matched = filter (pkgarg `matchesPkg`) (packages db),
+ let matched = findPackage pkgarg $ packages db,
not (null matched) ] of
- [] -> die ("cannot find package " ++ pkg_msg pkgarg)
+ [] -> cannotFindPackage pkgarg Nothing
ps -> return ps
+
+cannotFindPackage :: PackageArg -> Maybe (PackageDB mode) -> IO a
+cannotFindPackage pkgarg mdb = die $ "cannot find package " ++ pkg_msg pkgarg
+ ++ maybe "" (\db -> " in " ++ location db) mdb
where
- pkg_msg (Id pkgid) = displayGlobPkgId pkgid
- pkg_msg (IUId ipid) = display ipid
- pkg_msg (Substring pkgpat _) = "matching " ++ pkgpat
+ pkg_msg (Id pkgid) = displayGlobPkgId pkgid
+ pkg_msg (IUId ipid) = display ipid
+ pkg_msg (Substring pkgpat _) = "matching " ++ pkgpat
matches :: GlobPackageIdentifier -> PackageIdentifier -> Bool
GlobPackageIdentifier pn `matches` pid'
@@ -1451,9 +1584,9 @@ matchesPkg :: PackageArg -> InstalledPackageInfo -> Bool
describeField :: Verbosity -> [Flag] -> PackageArg -> [String] -> Bool -> IO ()
describeField verbosity my_flags pkgarg fields expand_pkgroot = do
- (_, _, flag_db_stack) <-
- getPkgDatabases verbosity False{-modify-} False{-use user-}
- True{-use cache-} expand_pkgroot my_flags
+ (_, GhcPkg.DbOpenReadOnly, flag_db_stack) <-
+ getPkgDatabases verbosity GhcPkg.DbOpenReadOnly
+ False{-use user-} True{-use cache-} expand_pkgroot my_flags
fns <- mapM toField fields
ps <- findPackages flag_db_stack pkgarg
mapM_ (selectFields fns) ps
@@ -1471,12 +1604,11 @@ describeField verbosity my_flags pkgarg fields expand_pkgroot = do
checkConsistency :: Verbosity -> [Flag] -> IO ()
checkConsistency verbosity my_flags = do
- (db_stack, _, _) <-
- getPkgDatabases verbosity False{-modify-} True{-use user-}
- True{-use cache-} True{-expand vars-}
- my_flags
- -- although check is not a modify command, we do need to use the user
- -- db, because we may need it to verify package deps.
+ (db_stack, GhcPkg.DbOpenReadOnly, _) <-
+ getPkgDatabases verbosity GhcPkg.DbOpenReadOnly
+ True{-use user-} True{-use cache-} True{-expand vars-} my_flags
+ -- although check is not a modify command, we do need to use the user
+ -- db, because we may need it to verify package deps.
let simple_output = FlagSimpleOutput `elem` my_flags
@@ -1930,10 +2062,6 @@ reportError s = do hFlush stdout; hPutStrLn stderr s
dieForcible :: String -> IO ()
dieForcible s = die (s ++ " (use --force to override)")
-my_head :: String -> [a] -> a
-my_head s [] = error s
-my_head _ (x : _) = x
-
-----------------------------------------
-- Cut and pasted from ghc/compiler/main/SysTools