Add unique package identifiers (InstalledPackageId) in the package DB
[ghc-hetmet.git] / compiler / main / Packages.lhs
index c8b7d69..38a1f9d 100644 (file)
@@ -1,9 +1,8 @@
- %
-% (c) The University of Glasgow, 2006
 %
-% Package manipulation
+% (c) The University of Glasgow, 2006
 %
 \begin{code}
+-- | Package manipulation
 module Packages (
        module PackageConfig,
 
@@ -26,6 +25,9 @@ module Packages (
        getPackageFrameworks,
        getPreloadPackagesAnd,
 
+        collectIncludeDirs, collectLibraryPaths, collectLinkOpts,
+        packageHsLibs,
+
        -- * Utils
        isDllName
     )
@@ -36,60 +38,60 @@ where
 import PackageConfig   
 import ParsePkgConf    ( loadPackageConfig )
 import DynFlags                ( dopt, DynFlag(..), DynFlags(..), PackageFlag(..) )
-import StaticFlags     ( opt_Static )
+import StaticFlags
 import Config          ( cProjectVersion )
 import Name            ( Name, nameModule_maybe )
 import UniqFM
+import FiniteMap
 import Module
 import Util
-import Maybes          ( expectJust, MaybeErr(..) )
 import Panic
 import Outputable
+import Maybes
 
 import System.Environment ( getEnv )
-import Distribution.InstalledPackageInfo hiding (depends)
-import Distribution.Package hiding (depends)
-import Distribution.Version
+import Distribution.InstalledPackageInfo
+import Distribution.Package hiding (PackageId,depends)
 import FastString
 import ErrUtils         ( debugTraceMsg, putMsg, Message )
+import Exception
 
 import System.Directory
 import System.FilePath
 import Data.Maybe
 import Control.Monad
-import Data.List
-import Control.Exception        ( throwDyn )
+import Data.List as List
 
 -- ---------------------------------------------------------------------------
 -- The Package state
 
--- Package state is all stored in DynFlags, including the details of
+-- | Package state is all stored in 'DynFlag's, including the details of
 -- all packages, which packages are exposed, and which modules they
 -- provide.
-
--- The package state is computed by initPackages, and kept in DynFlags.
 --
---   * -package <pkg> causes <pkg> to become exposed, and all other packages 
+-- The package state is computed by 'initPackages', and kept in DynFlags.
+--
+--   * @-package <pkg>@ causes @<pkg>@ to become exposed, and all other packages 
 --     with the same name to become hidden.
 -- 
---   * -hide-package <pkg> causes <pkg> to become hidden.
+--   * @-hide-package <pkg>@ causes @<pkg>@ to become hidden.
 -- 
---   * Let exposedPackages be the set of packages thus exposed.  
---     Let depExposedPackages be the transitive closure from exposedPackages of
+--   * Let @exposedPackages@ be the set of packages thus exposed.  
+--     Let @depExposedPackages@ be the transitive closure from @exposedPackages@ of
 --     their dependencies.
 --
 --   * When searching for a module from an preload import declaration,
---     only the exposed modules in exposedPackages are valid.
+--     only the exposed modules in @exposedPackages@ are valid.
 --
 --   * When searching for a module from an implicit import, all modules
---     from depExposedPackages are valid.
+--     from @depExposedPackages@ are valid.
 --
---   * When linking in a comp manager mode, we link in packages the
+--   * When linking in a compilation manager mode, we link in packages the
 --     program depends on (the compiler knows this list by the
 --     time it gets to the link step).  Also, we link in all packages
---     which were mentioned with preload -package flags on the command-line,
---     or are a transitive dependency of same, or are "base"/"rts".
---     The reason for (b) is that we might need packages which don't
+--     which were mentioned with preload @-package@ flags on the command-line,
+--     or are a transitive dependency of same, or are \"base\"\/\"rts\".
+--     The reason for this is that we might need packages which don't
 --     contain any Haskell modules, and therefore won't be discovered
 --     by the normal mechanism of dependency tracking.
 
@@ -112,19 +114,22 @@ data PackageState = PackageState {
        -- should be in reverse dependency order; that is, a package
        -- is always mentioned before the packages it depends on.
 
-  moduleToPkgConfAll   :: UniqFM [(PackageConfig,Bool)] -- ModuleEnv mapping
+  moduleToPkgConfAll   :: UniqFM [(PackageConfig,Bool)], -- ModuleEnv mapping
        -- Derived from pkgIdMap.       
        -- Maps Module to (pkgconf,exposed), where pkgconf is the
        -- PackageConfig for the package containing the module, and
        -- exposed is True if the package exposes that module.
+
+  installedPackageIdMap :: FiniteMap InstalledPackageId PackageId
   }
 
--- A PackageConfigMap maps a PackageId to a PackageConfig
+-- | A PackageConfigMap maps a 'PackageId' to a 'PackageConfig'
 type PackageConfigMap = UniqFM PackageConfig
 
 emptyPackageConfigMap :: PackageConfigMap
 emptyPackageConfigMap = emptyUFM
 
+-- | Find the package we know about with the given id (e.g. \"foo-1.0\"), if any
 lookupPackage :: PackageConfigMap -> PackageId -> Maybe PackageConfig
 lookupPackage = lookupUFM
 
@@ -134,8 +139,10 @@ extendPackageConfigMap pkg_map new_pkgs
   = foldl add pkg_map new_pkgs
   where add pkg_map p = addToUFM pkg_map (packageConfigId p) p
 
+-- | Looks up the package with the given id in the package state, panicing if it is
+-- not found
 getPackageDetails :: PackageState -> PackageId -> PackageConfig
-getPackageDetails dflags ps = expectJust "getPackageDetails" (lookupPackage (pkgIdMap dflags) ps)
+getPackageDetails ps pid = expectJust "getPackageDetails" (lookupPackage (pkgIdMap ps) pid)
 
 -- ----------------------------------------------------------------------------
 -- Loading the package config files and building up the package state
@@ -147,11 +154,11 @@ getPackageDetails dflags ps = expectJust "getPackageDetails" (lookupPackage (pkg
 --
 -- Returns a list of packages to link in if we're doing dynamic linking.
 -- This list contains the packages that the user explicitly mentioned with
--- -package flags.
+-- @-package@ flags.
 --
 -- 'initPackages' can be called again subsequently after updating the
 -- 'packageFlags' field of the 'DynFlags', and it will update the
--- 'packageState' in 'DynFlags' and return a list of packages to
+-- 'pkgState' in 'DynFlags' and return a list of packages to
 -- link in.
 initPackages :: DynFlags -> IO (DynFlags, [PackageId])
 initPackages dflags = do 
@@ -170,7 +177,7 @@ initPackages dflags = do
 
 readPackageConfigs :: DynFlags -> IO PackageConfigMap
 readPackageConfigs dflags = do
-   e_pkg_path <- try (getEnv "GHC_PACKAGE_PATH")
+   e_pkg_path <- tryIO (getEnv "GHC_PACKAGE_PATH")
    system_pkgconfs <- getSystemPackageConfigs dflags
 
    let pkgconfs = case e_pkg_path of
@@ -213,7 +220,7 @@ getSystemPackageConfigs dflags = do
        -- unless the -no-user-package-conf flag was given.
        -- We only do this when getAppUserDataDirectory is available 
        -- (GHC >= 6.3).
-   user_pkgconf <- handle (\_ -> return []) $ do
+   user_pkgconf <- do
       appdir <- getAppUserDataDirectory "ghc"
       let 
         pkgconf = appdir
@@ -223,6 +230,7 @@ getSystemPackageConfigs dflags = do
       if (flg && dopt Opt_ReadUserPackageConf dflags)
        then return [pkgconf]
        else return []
+    `catchIO` (\_ -> return [])
 
    return (user_pkgconf ++ system_pkgconfs ++ [system_pkgconf])
 
@@ -260,8 +268,8 @@ mungePackagePaths top_dir ps = map munge_pkg ps
   munge_paths = map munge_path
 
   munge_path p 
-         | Just p' <- maybePrefixMatch "$topdir"     p =            top_dir ++ p'
-         | Just p' <- maybePrefixMatch "$httptopdir" p = toHttpPath top_dir ++ p'
+         | Just p' <- stripPrefix "$topdir"     p =            top_dir ++ p'
+         | Just p' <- stripPrefix "$httptopdir" p = toHttpPath top_dir ++ p'
          | otherwise                               = p
 
   toHttpPath p = "file:///" ++ p
@@ -310,15 +318,16 @@ applyPackageFlag pkgs flag =
 matchingPackages :: String -> [PackageConfig]
          -> Maybe ([PackageConfig], [PackageConfig])
 matchingPackages str pkgs
-  = case partition (matches str) pkgs of
+  = case partition (packageMatches str) pkgs of
        ([],_)    -> Nothing
        (ps,rest) -> Just (sortByVersion ps, rest)
-  where
-        -- A package named on the command line can either include the
-       -- version, or just the name if it is unambiguous.
-       matches str p
-               =  str == display (package p)
-               || str == pkgName (package p)
+
+-- A package named on the command line can either include the
+-- version, or just the name if it is unambiguous.
+packageMatches :: String -> PackageConfig -> Bool
+packageMatches str p
+       =  str == display (package p)
+       || str == display (pkgName (package p))
 
 pickPackages :: [PackageConfig] -> [String] -> [PackageConfig]
 pickPackages pkgs strs = 
@@ -364,27 +373,27 @@ hideOldPackages dflags pkgs = mapM maybe_hide pkgs
 findWiredInPackages
    :: DynFlags
    -> [PackageConfig]           -- database
-   -> [PackageIdentifier]       -- preload packages
-   -> PackageId                -- this package
-   -> IO ([PackageConfig],
-          [PackageIdentifier],
-          PackageId)
+   -> IO [PackageConfig]
 
-findWiredInPackages dflags pkgs preload this_package = do
+findWiredInPackages dflags pkgs = do
   --
   -- Now we must find our wired-in packages, and rename them to
   -- their canonical names (eg. base-1.0 ==> base).
   --
   let
-        wired_in_pkgids = [ primPackageId,
+        wired_in_pkgids :: [String]
+        wired_in_pkgids = map packageIdString
+                          [ primPackageId,
                             integerPackageId,
                             basePackageId,
                             rtsPackageId,
                             haskell98PackageId,
                             thPackageId,
-                            ndpPackageId ]
+                            dphSeqPackageId,
+                            dphParPackageId ]
 
-       wired_in_names = map packageIdString wired_in_pkgids
+        matches :: PackageConfig -> String -> Bool
+        pc `matches` pid = display (pkgName (package pc)) == pid
 
        -- find which package corresponds to each wired-in package
        -- delete any other packages with the same name
@@ -397,14 +406,12 @@ findWiredInPackages dflags pkgs preload this_package = do
         -- could be used to hide newer versions.
         --
        findWiredInPackage :: [PackageConfig] -> String
-                          -> IO (Maybe PackageIdentifier)
+                          -> IO (Maybe InstalledPackageId)
        findWiredInPackage pkgs wired_pkg =
-           let all_ps = [ p | p <- pkgs, pkgName (package p) == wired_pkg ] in
-          case filter exposed all_ps of
-               [] -> case all_ps of
-                        []   -> notfound
-                        many -> pick (head (sortByVersion many))
-               many  -> pick (head (sortByVersion many))
+           let all_ps = [ p | p <- pkgs, p `matches` wired_pkg ] in
+          case all_ps of
+               []   -> notfound
+               many -> pick (head (sortByVersion many))
           where
                 notfound = do
                          debugTraceMsg dflags 2 $
@@ -412,43 +419,42 @@ findWiredInPackages dflags pkgs preload this_package = do
                                 <> text wired_pkg
                                 <> ptext (sLit " not found.")
                          return Nothing
+               pick :: InstalledPackageInfo_ ModuleName
+                     -> IO (Maybe InstalledPackageId)
                 pick pkg = do
                         debugTraceMsg dflags 2 $
                            ptext (sLit "wired-in package ")
                                 <> text wired_pkg
                                 <> ptext (sLit " mapped to ")
                                 <> text (display (package pkg))
-                       return (Just (package pkg))
+                       return (Just (installedPackageId pkg))
 
 
-  mb_wired_in_ids <- mapM (findWiredInPackage pkgs) wired_in_names
+  mb_wired_in_ids <- mapM (findWiredInPackage pkgs) wired_in_pkgids
   let 
         wired_in_ids = catMaybes mb_wired_in_ids
 
-       deleteOtherWiredInPackages pkgs = filter ok pkgs
-         where ok p = pkgName (package p) `notElem` wired_in_names
-                     || package p `elem` wired_in_ids
+        -- this is old: we used to assume that if there were
+        -- multiple versions of wired-in packages installed that
+        -- they were mutually exclusive.  Now we're assuming that
+        -- you have one "main" version of each wired-in package
+        -- (the latest version), and the others are backward-compat
+        -- wrappers that depend on this one.  e.g. base-4.0 is the
+        -- latest, base-3.0 is a compat wrapper depending on base-4.0.
+        {-
+       deleteOtherWiredInPackages pkgs = filterOut bad pkgs
+         where bad p = any (p `matches`) wired_in_pkgids
+                      && package p `notElem` map fst wired_in_ids
+        -}
 
        updateWiredInDependencies pkgs = map upd_pkg pkgs
-         where upd_pkg p = p{ package = upd_pid (package p),
-                              depends = map upd_pid (depends p) }
+         where upd_pkg p
+                  | installedPackageId p `elem` wired_in_ids
+                  = p { package = (package p){ pkgVersion = Version [] [] } }
+                  | otherwise
+                  = p
 
-       upd_pid pid = case filter (== pid) wired_in_ids of
-                               [] -> pid
-                               (x:_) -> x{ pkgVersion = Version [] [] }
-
-        pkgs1 = deleteOtherWiredInPackages pkgs
-
-        pkgs2 = updateWiredInDependencies pkgs1
-
-        preload1 = map upd_pid preload
-
-        -- we must return an updated thisPackage, just in case we
-        -- are actually compiling one of the wired-in packages
-        Just old_this_pkg = unpackPackageId this_package
-        new_this_pkg = mkPackageId (upd_pid old_this_pkg)
-
-  return (pkgs2, preload1, new_this_pkg)
+  return $ updateWiredInDependencies pkgs
 
 -- ----------------------------------------------------------------------------
 --
@@ -474,12 +480,12 @@ elimDanglingDeps dflags pkgs ignored = go [] pkgs'
         (new_avail, not_avail) -> go (new_avail ++ avail) (map fst not_avail)
 
    depsAvailable :: [PackageConfig] -> PackageConfig
-                 -> Either PackageConfig (PackageConfig, [PackageIdentifier])
+                 -> Either PackageConfig (PackageConfig, [InstalledPackageId])
    depsAvailable pkgs_ok pkg 
         | null dangling = Left pkg
         | otherwise     = Right (pkg, dangling)
         where dangling = filter (`notElem` pids) (depends pkg)
-              pids = map package pkgs_ok
+              pids = map installedPackageId pkgs_ok
 
    reportElim (p, deps) = 
         debugTraceMsg dflags 2 $
@@ -517,15 +523,14 @@ mkPackageState dflags orig_pkg_db preload0 this_package = do
   -- should contain at least rts & base, which is why we pretend that
   -- the command line contains -package rts & -package base.
   --
-  let new_preload_packages = 
-        map package (pickPackages pkgs0 [ p | ExposePackage p <- flags ])
+  let preload1 = map installedPackageId $ 
+                 pickPackages pkgs0 [ p | ExposePackage p <- flags ]
 
   -- hide packages that are subsumed by later versions
   pkgs2 <- hideOldPackages dflags pkgs1
 
   -- sort out which packages are wired in
-  (pkgs3, preload1, new_this_pkg)
-        <- findWiredInPackages dflags pkgs2 new_preload_packages this_package
+  pkgs3 <- findWiredInPackages dflags pkgs2
 
   let ignored = map packageConfigId $
                    pickPackages pkgs0 [ p | IgnorePackage p <- flags ]
@@ -533,25 +538,38 @@ mkPackageState dflags orig_pkg_db preload0 this_package = do
 
   let pkg_db = extendPackageConfigMap emptyPackageConfigMap pkgs
 
+      ipid_map = listToFM [ (installedPackageId p, packageConfigId p)
+                          | p <- pkgs ]
+
+      lookupIPID ipid@(InstalledPackageId str)
+         | Just pid <- lookupFM ipid_map ipid = return pid
+         | otherwise                          = missingPackageErr str
+
+  preload2 <- mapM lookupIPID preload1
+
+  let
       -- add base & rts to the preload packages
-      basicLinkedPackages = filter (flip elemUFM pkg_db)
-                                [basePackageId,rtsPackageId]
+      basicLinkedPackages
+       | dopt Opt_AutoLinkPackages dflags
+          = filter (flip elemUFM pkg_db) [basePackageId, rtsPackageId]
+       | otherwise = []
       -- but in any case remove the current package from the set of
       -- preloaded packages so that base/rts does not end up in the
       -- set up preloaded package when we are just building it
-      preload2 = nub (filter (/= new_this_pkg)
-                            (basicLinkedPackages ++ map mkPackageId preload1))
+      preload3 = nub $ filter (/= this_package)
+                     $ (basicLinkedPackages ++ preload2)
 
   -- Close the preload packages with their dependencies
-  dep_preload <- closeDeps pkg_db (zip preload2 (repeat Nothing))
+  dep_preload <- closeDeps pkg_db ipid_map (zip preload3 (repeat Nothing))
   let new_dep_preload = filter (`notElem` preload0) dep_preload
 
   let pstate = PackageState{ preloadPackages     = dep_preload,
                             pkgIdMap            = pkg_db,
-                            moduleToPkgConfAll  = mkModuleMap pkg_db
+                            moduleToPkgConfAll  = mkModuleMap pkg_db,
+                             installedPackageIdMap = ipid_map
                           }
 
-  return (pstate, new_dep_preload, new_this_pkg)
+  return (pstate, new_dep_preload, this_package)
 
 
 -- -----------------------------------------------------------------------------
@@ -587,27 +605,45 @@ pprPkg p = text (display (package p))
 -- of preload (command-line) packages to determine which packages to
 -- use.
 
+-- | Find all the include directories in these and the preload packages
 getPackageIncludePath :: DynFlags -> [PackageId] -> IO [String]
-getPackageIncludePath dflags pkgs = do
-  ps <- getPreloadPackagesAnd dflags pkgs
-  return (nub (filter notNull (concatMap includeDirs ps)))
+getPackageIncludePath dflags pkgs =
+  collectIncludeDirs `fmap` getPreloadPackagesAnd dflags pkgs
+
+collectIncludeDirs :: [PackageConfig] -> [FilePath] 
+collectIncludeDirs ps = nub (filter notNull (concatMap includeDirs ps))
 
+-- | Find all the library paths in these and the preload packages
 getPackageLibraryPath :: DynFlags -> [PackageId] -> IO [String]
-getPackageLibraryPath dflags pkgs = do 
-  ps <- getPreloadPackagesAnd dflags pkgs
-  return (nub (filter notNull (concatMap libraryDirs ps)))
+getPackageLibraryPath dflags pkgs =
+  collectLibraryPaths `fmap` getPreloadPackagesAnd dflags pkgs
 
+collectLibraryPaths :: [PackageConfig] -> [FilePath]
+collectLibraryPaths ps = nub (filter notNull (concatMap libraryDirs ps))
+
+-- | Find all the link options in these and the preload packages
 getPackageLinkOpts :: DynFlags -> [PackageId] -> IO [String]
-getPackageLinkOpts dflags pkgs = do
-  ps <- getPreloadPackagesAnd dflags pkgs
-  let tag = buildTag dflags
-      rts_tag = rtsBuildTag dflags
-  let 
+getPackageLinkOpts dflags pkgs = 
+  collectLinkOpts dflags `fmap` getPreloadPackagesAnd dflags pkgs
+
+collectLinkOpts :: DynFlags -> [PackageConfig] -> [String]
+collectLinkOpts dflags ps = concat (map all_opts ps)
+  where
+       libs p     = packageHsLibs dflags p ++ extraLibraries p
+       all_opts p = map ("-l" ++) (libs p) ++ ldOptions p
+
+packageHsLibs :: DynFlags -> PackageConfig -> [String]
+packageHsLibs dflags p = map (mkDynName . addSuffix) (hsLibraries p)
+  where
+        non_dyn_ways = filter ((/= WayDyn) . wayName) (ways dflags)
+        -- the name of a shared library is libHSfoo-ghc<version>.so
+        -- we leave out the _dyn, because it is superfluous
+
+        tag     = mkBuildTag (filter (not . wayRTSOnly) non_dyn_ways)
+        rts_tag = mkBuildTag non_dyn_ways
+
        mkDynName | opt_Static = id
                  | otherwise = (++ ("-ghc" ++ cProjectVersion))
-       libs p     = map (mkDynName . addSuffix) (hsLibraries p)
-                        ++ extraLibraries p
-       all_opts p = map ("-l" ++) (libs p) ++ ldOptions p
 
         addSuffix rts@"HSrts"    = rts       ++ (expandTag rts_tag)
         addSuffix other_lib      = other_lib ++ (expandTag tag)
@@ -615,18 +651,19 @@ getPackageLinkOpts dflags pkgs = do
         expandTag t | null t = ""
                    | otherwise = '_':t
 
-  return (concat (map all_opts ps))
-
+-- | Find all the C-compiler options in these and the preload packages
 getPackageExtraCcOpts :: DynFlags -> [PackageId] -> IO [String]
 getPackageExtraCcOpts dflags pkgs = do
   ps <- getPreloadPackagesAnd dflags pkgs
   return (concatMap ccOptions ps)
 
+-- | Find all the package framework paths in these and the preload packages
 getPackageFrameworkPath  :: DynFlags -> [PackageId] -> IO [String]
 getPackageFrameworkPath dflags pkgs = do
   ps <- getPreloadPackagesAnd dflags pkgs
   return (nub (filter notNull (concatMap frameworkDirs ps)))
 
+-- | Find all the package frameworks in these and the preload packages
 getPackageFrameworks  :: DynFlags -> [PackageId] -> IO [String]
 getPackageFrameworks dflags pkgs = do
   ps <- getPreloadPackagesAnd dflags pkgs
@@ -635,45 +672,55 @@ getPackageFrameworks dflags pkgs = do
 -- -----------------------------------------------------------------------------
 -- Package Utils
 
--- | Takes a Module, and if the module is in a package returns 
--- @(pkgconf,exposed)@ where pkgconf is the PackageConfig for that package,
--- and exposed is True if the package exposes the module.
+-- | Takes a 'Module', and if the module is in a package returns 
+-- @(pkgconf, exposed)@ where pkgconf is the PackageConfig for that package,
+-- and exposed is @True@ if the package exposes the module.
 lookupModuleInAllPackages :: DynFlags -> ModuleName -> [(PackageConfig,Bool)]
 lookupModuleInAllPackages dflags m =
   case lookupUFM (moduleToPkgConfAll (pkgState dflags)) m of
        Nothing -> []
        Just ps -> ps
 
+-- | Find all the 'PackageConfig' in both the preload packages from 'DynFlags' and corresponding to the list of
+-- 'PackageConfig's
 getPreloadPackagesAnd :: DynFlags -> [PackageId] -> IO [PackageConfig]
 getPreloadPackagesAnd dflags pkgids =
   let 
       state   = pkgState dflags
       pkg_map = pkgIdMap state
+      ipid_map = installedPackageIdMap state
       preload = preloadPackages state
       pairs = zip pkgids (repeat Nothing)
   in do
-  all_pkgs <- throwErr (foldM (add_package pkg_map) preload pairs)
+  all_pkgs <- throwErr (foldM (add_package pkg_map ipid_map) preload pairs)
   return (map (getPackageDetails state) all_pkgs)
 
 -- Takes a list of packages, and returns the list with dependencies included,
 -- in reverse dependency order (a package appears before those it depends on).
-closeDeps :: PackageConfigMap -> [(PackageId, Maybe PackageId)]
-        -> IO [PackageId]
-closeDeps pkg_map ps = throwErr (closeDepsErr pkg_map ps)
+closeDeps :: PackageConfigMap
+          -> FiniteMap InstalledPackageId PackageId
+          -> [(PackageId, Maybe PackageId)]
+          -> IO [PackageId]
+closeDeps pkg_map ipid_map ps = throwErr (closeDepsErr pkg_map ipid_map ps)
 
 throwErr :: MaybeErr Message a -> IO a
 throwErr m = case m of
-               Failed e    -> throwDyn (CmdLineError (showSDoc e))
+               Failed e    -> ghcError (CmdLineError (showSDoc e))
                Succeeded r -> return r
 
-closeDepsErr :: PackageConfigMap -> [(PackageId,Maybe PackageId)]
-       -> MaybeErr Message [PackageId]
-closeDepsErr pkg_map ps = foldM (add_package pkg_map) [] ps
+closeDepsErr :: PackageConfigMap
+             -> FiniteMap InstalledPackageId PackageId
+             -> [(PackageId,Maybe PackageId)]
+             -> MaybeErr Message [PackageId]
+closeDepsErr pkg_map ipid_map ps = foldM (add_package pkg_map ipid_map) [] ps
 
 -- internal helper
-add_package :: PackageConfigMap -> [PackageId] -> (PackageId,Maybe PackageId)
-       -> MaybeErr Message [PackageId]
-add_package pkg_db ps (p, mb_parent)
+add_package :: PackageConfigMap 
+            -> FiniteMap InstalledPackageId PackageId
+            -> [PackageId]
+            -> (PackageId,Maybe PackageId)
+            -> MaybeErr Message [PackageId]
+add_package pkg_db ipid_map ps (p, mb_parent)
   | p `elem` ps = return ps    -- Check if we've already added this package
   | otherwise =
       case lookupPackage pkg_db p of
@@ -681,12 +728,17 @@ add_package pkg_db ps (p, mb_parent)
                            missingDependencyMsg mb_parent)
         Just pkg -> do
           -- Add the package's dependents also
-          let deps = map mkPackageId (depends pkg)
-          ps' <- foldM (add_package pkg_db) ps (zip deps (repeat (Just p)))
+          ps' <- foldM add_package_ipid ps (depends pkg)
           return (p : ps')
+          where
+            add_package_ipid ps ipid@(InstalledPackageId str)
+              | Just pid <- lookupFM ipid_map ipid
+              = add_package pkg_db ipid_map ps (pid, Just p)
+              | otherwise
+              = Failed (missingPackageMsg str <> missingDependencyMsg mb_parent)
 
-missingPackageErr :: String -> IO [PackageConfig]
-missingPackageErr p = throwDyn (CmdLineError (showSDoc (missingPackageMsg p)))
+missingPackageErr :: String -> IO a
+missingPackageErr p = ghcError (CmdLineError (showSDoc (missingPackageMsg p)))
 
 missingPackageMsg :: String -> SDoc
 missingPackageMsg p = ptext (sLit "unknown package:") <+> text p
@@ -698,6 +750,7 @@ missingDependencyMsg (Just parent)
 
 -- -----------------------------------------------------------------------------
 
+-- | Will the 'Name' come from a dynamically linked library?
 isDllName :: PackageId -> Name -> Bool
 isDllName this_pkg name
   | opt_Static = False
@@ -707,15 +760,12 @@ isDllName this_pkg name
 -- -----------------------------------------------------------------------------
 -- Displaying packages
 
+-- | Show package info on console, if verbosity is >= 3
 dumpPackages :: DynFlags -> IO ()
--- Show package info on console, if verbosity is >= 3
 dumpPackages dflags
   = do  let pkg_map = pkgIdMap (pkgState dflags)
        putMsg dflags $
-             vcat (map (text.showInstalledPackageInfo.to_ipi) (eltsUFM pkg_map))
- where
-  to_ipi pkgconf@(InstalledPackageInfo { exposedModules = e,
-                                         hiddenModules = h }) = 
-    pkgconf{ exposedModules = map moduleNameString e,
-             hiddenModules  = map moduleNameString h }
+              vcat (map (text . showInstalledPackageInfo
+                              . packageConfigToInstalledPackageInfo)
+                        (eltsUFM pkg_map))
 \end{code}