Change the representation of the package database
authorSimon Marlow <marlowsd@gmail.com>
Thu, 10 Sep 2009 10:27:03 +0000 (10:27 +0000)
committerSimon Marlow <marlowsd@gmail.com>
Thu, 10 Sep 2009 10:27:03 +0000 (10:27 +0000)
 - the package DB is a directory containing one file per package
   instance (#723)

 - there is a binary cache of the database (#593, #2089)

 - the binary package is now a boot package

 - there is a new package, bin-package-db, containing the Binary
   instance of InstalledPackageInfo for the binary cache.

Also included in this patch

 - Use colour in 'ghc-pkg list' to indicate broken or hidden packages

   Broken packages are red, hidden packages are

   Colour support comes from the terminfo package, and is only used when
    - not --simple-output
    - stdout is a TTY
    - the terminal type has colour capability

 - Fix the bug that 'ghc-pkg list --user' shows everything as broken

13 files changed:
compiler/ghc.cabal.in
compiler/main/PackageConfig.hs
compiler/main/Packages.lhs
compiler/main/SysTools.lhs
ghc.mk
libraries/bin-package-db/Distribution/InstalledPackageInfo/Binary.hs [new file with mode: 0644]
libraries/bin-package-db/bin-package-db.cabal [new file with mode: 0644]
mk/config.mk.in
packages
utils/ghc-cabal/ghc-cabal.hs
utils/ghc-pkg/Main.hs
utils/ghc-pkg/ghc-pkg.cabal
utils/ghc-pkg/ghc.mk

index d05dc1a..bed13f0 100644 (file)
@@ -88,6 +88,8 @@ Library
     if !flag(ncg)
         CPP-Options: -DOMIT_NATIVE_CODEGEN
 
     if !flag(ncg)
         CPP-Options: -DOMIT_NATIVE_CODEGEN
 
+    Build-Depends: bin-package-db
+
     -- GHC 6.4.2 needs to be able to find WCsubst.c, which needs to be
     -- able to find WCsubst.h
     Include-Dirs: ../libraries/base/cbits, ../libraries/base/include
     -- GHC 6.4.2 needs to be able to find WCsubst.c, which needs to be
     -- able to find WCsubst.h
     Include-Dirs: ../libraries/base/cbits, ../libraries/base/include
index ac1a9fe..50c1e71 100644 (file)
@@ -74,11 +74,9 @@ packageConfigToInstalledPackageInfo
 
 -- | Turn an 'InstalledPackageInfo', which contains Cabal 'Distribution.ModuleName.ModuleName's
 -- into a GHC specific 'PackageConfig' which contains GHC 'Module.ModuleName's
 
 -- | Turn an 'InstalledPackageInfo', which contains Cabal 'Distribution.ModuleName.ModuleName's
 -- into a GHC specific 'PackageConfig' which contains GHC 'Module.ModuleName's
-installedPackageInfoToPackageConfig :: InstalledPackageInfo -> PackageConfig
+installedPackageInfoToPackageConfig :: InstalledPackageInfo_ String -> PackageConfig
 installedPackageInfoToPackageConfig
     (pkgconf@(InstalledPackageInfo { exposedModules = e,
                                      hiddenModules = h })) =
 installedPackageInfoToPackageConfig
     (pkgconf@(InstalledPackageInfo { exposedModules = e,
                                      hiddenModules = h })) =
-        pkgconf{ exposedModules = map convert e,
-                 hiddenModules  = map convert h }
-    where convert :: Distribution.ModuleName.ModuleName -> Module.ModuleName
-          convert = mkModuleName . display
+        pkgconf{ exposedModules = map mkModuleName e,
+                 hiddenModules  = map mkModuleName h }
index 2e91ac8..0cfd00f 100644 (file)
@@ -51,6 +51,7 @@ import Maybes
 
 import System.Environment ( getEnv )
 import Distribution.InstalledPackageInfo
 
 import System.Environment ( getEnv )
 import Distribution.InstalledPackageInfo
+import Distribution.InstalledPackageInfo.Binary
 import Distribution.Package hiding (PackageId,depends)
 import FastString
 import ErrUtils         ( debugTraceMsg, putMsg, Message )
 import Distribution.Package hiding (PackageId,depends)
 import FastString
 import ErrUtils         ( debugTraceMsg, putMsg, Message )
@@ -204,44 +205,40 @@ getSystemPackageConfigs dflags = do
        -- System one always comes first
    let system_pkgconf = systemPackageConfig dflags
 
        -- System one always comes first
    let system_pkgconf = systemPackageConfig dflags
 
-       -- allow package.conf.d to contain a bunch of .conf files
-       -- containing package specifications.  This is an easier way
-       -- to maintain the package database on systems with a package
-       -- management system, or systems that don't want to run ghc-pkg
-       -- to register or unregister packages.  Undocumented feature for now.
-   let system_pkgconf_dir = system_pkgconf <.> "d"
-   system_pkgconf_dir_exists <- doesDirectoryExist system_pkgconf_dir
-   system_pkgconfs <-
-     if system_pkgconf_dir_exists
-       then do files <- getDirectoryContents system_pkgconf_dir
-               return [ system_pkgconf_dir </> file
-                      | file <- files
-                      , takeExtension file == ".conf" ]
-       else return []
-
        -- Read user's package conf (eg. ~/.ghc/i386-linux-6.3/package.conf)
        -- unless the -no-user-package-conf flag was given.
        -- Read user's package conf (eg. ~/.ghc/i386-linux-6.3/package.conf)
        -- unless the -no-user-package-conf flag was given.
-       -- We only do this when getAppUserDataDirectory is available 
-       -- (GHC >= 6.3).
    user_pkgconf <- do
    user_pkgconf <- do
+      if not (dopt Opt_ReadUserPackageConf dflags) then return [] else do
       appdir <- getAppUserDataDirectory "ghc"
       let 
       appdir <- getAppUserDataDirectory "ghc"
       let 
-        pkgconf = appdir
-                  </> (TARGET_ARCH ++ '-':TARGET_OS ++ '-':cProjectVersion)
-                  </> "package.conf"
-      flg <- doesFileExist pkgconf
-      if (flg && dopt Opt_ReadUserPackageConf dflags)
-       then return [pkgconf]
-       else return []
+        dir = appdir </> (TARGET_ARCH ++ '-':TARGET_OS ++ '-':cProjectVersion)
+         pkgconf = dir </> "package.conf.d"
+      --
+      exist <- doesDirectoryExist pkgconf
+      if exist then return [pkgconf] else return []
     `catchIO` (\_ -> return [])
 
     `catchIO` (\_ -> return [])
 
-   return (user_pkgconf ++ system_pkgconfs ++ [system_pkgconf])
-
+   return (user_pkgconf ++ [system_pkgconf])
 
 readPackageConfig :: DynFlags -> FilePath -> IO [PackageConfig]
 readPackageConfig dflags conf_file = do
 
 readPackageConfig :: DynFlags -> FilePath -> IO [PackageConfig]
 readPackageConfig dflags conf_file = do
-  debugTraceMsg dflags 2 (text "Using package config file:" <+> text conf_file)
-  proto_pkg_configs <- loadPackageConfig dflags conf_file
+  isdir <- doesDirectoryExist conf_file
+
+  proto_pkg_configs <- 
+    if isdir
+       then do let filename = conf_file </> "package.cache"
+               debugTraceMsg dflags 2 (text "Using binary package database:" <+> text filename)
+               conf <- readBinPackageDB filename
+               return (map installedPackageInfoToPackageConfig conf)
+
+       else do 
+            isfile <- doesFileExist conf_file
+            when (not isfile) $
+              ghcError $ InstallationError $ 
+                "can't find a package database at " ++ conf_file
+            debugTraceMsg dflags 2 (text "Using package config file:" <+> text conf_file)
+            loadPackageConfig dflags conf_file
+
   let
       top_dir = topDir dflags
       pkg_configs1 = mungePackagePaths top_dir proto_pkg_configs
   let
       top_dir = topDir dflags
       pkg_configs1 = mungePackagePaths top_dir proto_pkg_configs
index f793a17..3a6f9f2 100644 (file)
@@ -160,7 +160,7 @@ initSysTools mbMinusB dflags0
               installed file = top_dir </> file
               installed_mingw_bin file = top_dir </> ".." </> "mingw" </> "bin" </> file
 
               installed file = top_dir </> file
               installed_mingw_bin file = top_dir </> ".." </> "mingw" </> "bin" </> file
 
-        ; let pkgconfig_path      = installed "package.conf"
+        ; let pkgconfig_path = installed "package.conf.d"
               ghc_usage_msg_path  = installed "ghc-usage.txt"
               ghci_usage_msg_path = installed "ghci-usage.txt"
 
               ghc_usage_msg_path  = installed "ghc-usage.txt"
               ghci_usage_msg_path = installed "ghci-usage.txt"
 
@@ -177,12 +177,6 @@ initSysTools mbMinusB dflags0
         ; tmpdir <- getTemporaryDirectory
         ; let dflags1 = setTmpDir tmpdir dflags0
 
         ; tmpdir <- getTemporaryDirectory
         ; let dflags1 = setTmpDir tmpdir dflags0
 
-        -- Check that the package config exists
-        ; config_exists <- doesFileExist pkgconfig_path
-        ; when (not config_exists) $
-             ghcError (InstallationError
-                         ("Can't find package.conf as " ++ pkgconfig_path))
-
         -- On Windows, mingw is distributed with GHC,
         --      so we look in TopDir/../mingw/bin
         ; let
         -- On Windows, mingw is distributed with GHC,
         --      so we look in TopDir/../mingw/bin
         ; let
diff --git a/ghc.mk b/ghc.mk
index 7f653d0..91b5d3d 100644 (file)
--- a/ghc.mk
+++ b/ghc.mk
@@ -171,6 +171,8 @@ libraries/hpc_dist-boot_DO_HADDOCK = NO
 libraries/Cabal_dist-boot_DO_HADDOCK = NO
 libraries/extensible-exceptions_dist-boot_DO_HADDOCK = NO
 libraries/filepath_dist-boot_DO_HADDOCK = NO
 libraries/Cabal_dist-boot_DO_HADDOCK = NO
 libraries/extensible-exceptions_dist-boot_DO_HADDOCK = NO
 libraries/filepath_dist-boot_DO_HADDOCK = NO
+libraries/binary_dist-boot_DO_HADDOCK = NO
+libraries/bin-package-db_dist-boot_DO_HADDOCK = NO
 
 # -----------------------------------------------------------------------------
 # Ways
 
 # -----------------------------------------------------------------------------
 # Ways
@@ -320,6 +322,8 @@ $(eval $(call addPackage,syb))
 $(eval $(call addPackage,template-haskell))
 $(eval $(call addPackage,base3-compat))
 $(eval $(call addPackage,Cabal))
 $(eval $(call addPackage,template-haskell))
 $(eval $(call addPackage,base3-compat))
 $(eval $(call addPackage,Cabal))
+$(eval $(call addPackage,binary))
+$(eval $(call addPackage,bin-package-db))
 $(eval $(call addPackage,mtl))
 $(eval $(call addPackage,utf8-string))
 
 $(eval $(call addPackage,mtl))
 $(eval $(call addPackage,utf8-string))
 
@@ -337,7 +341,9 @@ PACKAGES_STAGE2 += \
        dph/dph-par
 endif
 
        dph/dph-par
 endif
 
-BOOT_PKGS = Cabal hpc extensible-exceptions
+# We assume that the stage0 compiler has a suitable bytestring package,
+# so we don't have to include it below.
+BOOT_PKGS = Cabal hpc extensible-exceptions binary bin-package-db
 
 # The actual .a and .so/.dll files: needed for dependencies.
 ALL_STAGE1_LIBS  = $(foreach lib,$(PACKAGES),$(libraries/$(lib)_dist-install_v_LIB))
 
 # The actual .a and .so/.dll files: needed for dependencies.
 ALL_STAGE1_LIBS  = $(foreach lib,$(PACKAGES),$(libraries/$(lib)_dist-install_v_LIB))
@@ -628,6 +634,8 @@ $(eval $(call clean-target,$(BOOTSTRAPPING_CONF),,$(BOOTSTRAPPING_CONF)))
 $(eval $(call build-package,libraries/hpc,dist-boot,0))
 $(eval $(call build-package,libraries/extensible-exceptions,dist-boot,0))
 $(eval $(call build-package,libraries/Cabal,dist-boot,0))
 $(eval $(call build-package,libraries/hpc,dist-boot,0))
 $(eval $(call build-package,libraries/extensible-exceptions,dist-boot,0))
 $(eval $(call build-package,libraries/Cabal,dist-boot,0))
+$(eval $(call build-package,libraries/binary,dist-boot,0))
+$(eval $(call build-package,libraries/bin-package-db,dist-boot,0))
 
 # register the boot packages in strict sequence, because running
 # multiple ghc-pkgs in parallel doesn't work (registrations may get
 
 # register the boot packages in strict sequence, because running
 # multiple ghc-pkgs in parallel doesn't work (registrations may get
@@ -638,13 +646,23 @@ $(foreach pkg,$(BOOT_PKGS),$(eval $(call fixed_pkg_dep,$(pkg),dist-boot)))
 compiler/stage1/package-data.mk : \
     libraries/Cabal/dist-boot/package-data.mk \
     libraries/hpc/dist-boot/package-data.mk \
 compiler/stage1/package-data.mk : \
     libraries/Cabal/dist-boot/package-data.mk \
     libraries/hpc/dist-boot/package-data.mk \
-    libraries/extensible-exceptions/dist-boot/package-data.mk
+    libraries/extensible-exceptions/dist-boot/package-data.mk \
+    libraries/bin-package-db/dist-boot/package-data.mk
 
 # These are necessary because the bootstrapping compiler may not know
 # about cross-package dependencies:
 $(compiler_stage1_depfile) : $(BOOT_LIBS)
 $(ghc_stage1_depfile) : $(compiler_stage1_v_LIB)
 
 
 # These are necessary because the bootstrapping compiler may not know
 # about cross-package dependencies:
 $(compiler_stage1_depfile) : $(BOOT_LIBS)
 $(ghc_stage1_depfile) : $(compiler_stage1_v_LIB)
 
+# A few careful dependencies between bootstrapping packages.  When we
+# can rely on the stage 0 compiler being able to generate
+# cross-package dependencies with -M (fixed in GHC 6.12.1) we can drop
+# these, and also some of the phases.
+#
+# If you miss any out here, then 'make -j8' will probably tell you.
+#
+libraries/bin-package-db/dist-boot/build/Distribution/InstalledPackageInfo/Binary.$(v_osuf) : libraries/binary/dist-boot/build/Data/Binary.$(v_hisuf)
+
 $(foreach pkg,$(BOOT_PKGS),$(eval libraries/$(pkg)_dist-boot_HC_OPTS += $$(GhcBootLibHcOpts)))
 
 endif
 $(foreach pkg,$(BOOT_PKGS),$(eval libraries/$(pkg)_dist-boot_HC_OPTS += $$(GhcBootLibHcOpts)))
 
 endif
@@ -770,7 +788,7 @@ install_docs: $(INSTALL_HEADERS)
                $(INSTALL_DOC) $(INSTALL_OPTS) $$i/* $(DESTDIR)$(docdir)/html/`basename $$i`; \
        done
 
                $(INSTALL_DOC) $(INSTALL_OPTS) $$i/* $(DESTDIR)$(docdir)/html/`basename $$i`; \
        done
 
-INSTALLED_PACKAGE_CONF=$(DESTDIR)$(topdir)/package.conf
+INSTALLED_PACKAGE_CONF=$(DESTDIR)$(topdir)/package.conf.d
 
 # Install packages in the right order, so that ghc-pkg doesn't complain.
 # Also, install ghc-pkg first.
 
 # Install packages in the right order, so that ghc-pkg doesn't complain.
 # Also, install ghc-pkg first.
@@ -785,9 +803,8 @@ endif
 install_packages: install_libexecs
 install_packages: libffi/package.conf.install rts/package.conf.install
        $(INSTALL_DIR) $(DESTDIR)$(topdir)
 install_packages: install_libexecs
 install_packages: libffi/package.conf.install rts/package.conf.install
        $(INSTALL_DIR) $(DESTDIR)$(topdir)
-       "$(RM)" $(RM_OPTS) $(INSTALLED_PACKAGE_CONF)
-       $(CREATE_DATA)     $(INSTALLED_PACKAGE_CONF)
-       echo "[]"       >> $(INSTALLED_PACKAGE_CONF)
+       "$(RM)" -r $(RM_OPTS) $(INSTALLED_PACKAGE_CONF)
+       $(INSTALL_DIR) $(INSTALLED_PACKAGE_CONF)
        "$(INSTALLED_GHC_PKG_REAL)" --force --global-conf $(INSTALLED_PACKAGE_CONF) update libffi/package.conf.install
        "$(INSTALLED_GHC_PKG_REAL)" --force --global-conf $(INSTALLED_PACKAGE_CONF) update rts/package.conf.install
        $(foreach p, $(PACKAGES) $(PACKAGES_STAGE2),\
        "$(INSTALLED_GHC_PKG_REAL)" --force --global-conf $(INSTALLED_PACKAGE_CONF) update libffi/package.conf.install
        "$(INSTALLED_GHC_PKG_REAL)" --force --global-conf $(INSTALLED_PACKAGE_CONF) update rts/package.conf.install
        $(foreach p, $(PACKAGES) $(PACKAGES_STAGE2),\
diff --git a/libraries/bin-package-db/Distribution/InstalledPackageInfo/Binary.hs b/libraries/bin-package-db/Distribution/InstalledPackageInfo/Binary.hs
new file mode 100644 (file)
index 0000000..212c8f9
--- /dev/null
@@ -0,0 +1,133 @@
+{-# LANGUAGE RecordWildCards, TypeSynonymInstances, StandaloneDeriving, GeneralizedNewtypeDeriving #-}
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Distribution.InstalledPackageInfo.Binary
+-- Copyright   :  (c) The University of Glasgow 2009
+--
+-- Maintainer  :  cvs-ghc@haskell.org
+-- Portability :  portable
+--
+
+module Distribution.InstalledPackageInfo.Binary (
+       readBinPackageDB,
+       writeBinPackageDB
+  ) where
+
+import Distribution.Version
+import Distribution.Package
+import Distribution.License
+import Distribution.InstalledPackageInfo as IPI
+import Data.Binary as Bin
+
+readBinPackageDB :: Binary m => FilePath -> IO [InstalledPackageInfo_ m]
+readBinPackageDB file = Bin.decodeFile file
+
+writeBinPackageDB :: Binary m => FilePath -> [InstalledPackageInfo_ m] -> IO ()
+writeBinPackageDB file ipis = Bin.encodeFile file ipis
+
+instance Binary m => Binary (InstalledPackageInfo_ m) where
+  put = putInstalledPackageInfo
+  get = getInstalledPackageInfo
+
+putInstalledPackageInfo :: Binary m => InstalledPackageInfo_ m -> Put
+putInstalledPackageInfo ipi = do
+  put (sourcePackageId ipi)
+  put (installedPackageId ipi)
+  put (license ipi)
+  put (copyright ipi)
+  put (maintainer ipi)
+  put (author ipi)
+  put (stability ipi)
+  put (homepage ipi)
+  put (pkgUrl ipi)
+  put (description ipi)
+  put (category ipi)
+  put (exposed ipi)
+  put (exposedModules ipi)
+  put (hiddenModules ipi)
+  put (importDirs ipi)
+  put (libraryDirs ipi)
+  put (hsLibraries ipi)
+  put (extraLibraries ipi)
+  put (extraGHCiLibraries ipi)
+  put (includeDirs ipi)
+  put (includes ipi)
+  put (IPI.depends ipi)
+  put (hugsOptions ipi)
+  put (ccOptions ipi)
+  put (ldOptions ipi)
+  put (frameworkDirs ipi)
+  put (frameworks ipi)
+  put (haddockInterfaces ipi)
+  put (haddockHTMLs ipi)
+
+getInstalledPackageInfo :: Binary m => Get (InstalledPackageInfo_ m)
+getInstalledPackageInfo = do
+  sourcePackageId <- get
+  installedPackageId <- get
+  license <- get
+  copyright <- get
+  maintainer <- get
+  author <- get
+  stability <- get
+  homepage <- get
+  pkgUrl <- get
+  description <- get
+  category <- get
+  exposed <- get
+  exposedModules <- get
+  hiddenModules <- get
+  importDirs <- get
+  libraryDirs <- get
+  hsLibraries <- get
+  extraLibraries <- get
+  extraGHCiLibraries <- get
+  includeDirs <- get
+  includes <- get
+  depends <- get
+  hugsOptions <- get
+  ccOptions <- get
+  ldOptions <- get
+  frameworkDirs <- get
+  frameworks <- get
+  haddockInterfaces <- get
+  haddockHTMLs <- get
+  return InstalledPackageInfo{..}
+
+instance Binary PackageIdentifier where
+  put pid = do put (pkgName pid); put (pkgVersion pid)
+  get = do 
+    pkgName <- get
+    pkgVersion <- get
+    return PackageIdentifier{..}
+
+instance Binary License where
+  put (GPL v)              = do putWord8 0; put v
+  put (LGPL v)             = do putWord8 1; put v
+  put BSD3                 = do putWord8 2
+  put BSD4                 = do putWord8 3
+  put MIT                  = do putWord8 4
+  put PublicDomain         = do putWord8 5
+  put AllRightsReserved    = do putWord8 6
+  put OtherLicense         = do putWord8 7
+  put (UnknownLicense str) = do putWord8 8; put str
+
+  get = do
+    n <- getWord8
+    case n of
+      0 -> do v <- get; return (GPL v)
+      1 -> do v <- get; return (LGPL v)
+      2 -> return BSD3
+      3 -> return BSD4
+      4 -> return MIT
+      5 -> return PublicDomain
+      6 -> return AllRightsReserved
+      7 -> return OtherLicense
+      8 -> do str <- get; return (UnknownLicense str)
+
+instance Binary Version where
+  put v = do put (versionBranch v); put (versionTags v)
+  get = do versionBranch <- get; versionTags <- get; return Version{..}
+
+deriving instance Binary PackageName
+deriving instance Binary InstalledPackageId
diff --git a/libraries/bin-package-db/bin-package-db.cabal b/libraries/bin-package-db/bin-package-db.cabal
new file mode 100644 (file)
index 0000000..abeb9e5
--- /dev/null
@@ -0,0 +1,21 @@
+name:           bin-package-db
+version:        0.0.0.0
+license:        BSD3
+maintainer:     cvs-ghc@haskell.org
+bug-reports:    glasgow-haskell-bugs@haskell.org
+synopsis:       A binary format for the package database
+cabal-version:  >=1.6
+build-type: Simple
+
+source-repository head
+    type:     darcs
+    location: http://darcs.haskell.org/ghc
+
+Library {
+    exposed-modules:
+            Distribution.InstalledPackageInfo.Binary
+
+    build-depends: base == 4.*,
+                   binary == 0.5.*,
+                   Cabal == 1.7.*
+}
index 813a912..a120e46 100644 (file)
@@ -500,7 +500,7 @@ INSTALL_GHC_STAGE=2
 
 BOOTSTRAPPING_CONF = libraries/bootstrapping.conf
 
 
 BOOTSTRAPPING_CONF = libraries/bootstrapping.conf
 
-INPLACE_PACKAGE_CONF = $(INPLACE_LIB)/package.conf
+INPLACE_PACKAGE_CONF = $(INPLACE_LIB)/package.conf.d
 
 GhcVersion     = @GhcVersion@
 GhcPatchLevel  = @GhcPatchLevel@
 
 GhcVersion     = @GhcVersion@
 GhcPatchLevel  = @GhcPatchLevel@
index 2eea464..be62ec7 100644 (file)
--- a/packages
+++ b/packages
@@ -22,6 +22,7 @@ utils/haddock                           haddock2                        darcs
 libraries/array                         packages/array                  darcs
 libraries/base                          packages/base                   darcs
 libraries/base3-compat                  packages/base3-compat           darcs
 libraries/array                         packages/array                  darcs
 libraries/base                          packages/base                   darcs
 libraries/base3-compat                  packages/base3-compat           darcs
+libraries/binary                        packages/binary                 darcs
 libraries/bytestring                    packages/bytestring             darcs
 libraries/Cabal                         packages/Cabal                  darcs
 libraries/containers                    packages/containers             darcs
 libraries/bytestring                    packages/bytestring             darcs
 libraries/Cabal                         packages/Cabal                  darcs
 libraries/containers                    packages/containers             darcs
index e97850d..3d9cf13 100644 (file)
@@ -155,7 +155,7 @@ doInstall ghc ghcpkg topdir directory distDir myDestDir myPrefix myLibdir myDocd
                               programArgs = ["-B" ++ topdir],
                               programLocation = UserSpecified ghc
                           }
                               programArgs = ["-B" ++ topdir],
                               programLocation = UserSpecified ghc
                           }
-                ghcpkgconf = topdir </> "package.conf"
+                ghcpkgconf = topdir </> "package.conf.d"
                 ghcPkgProg = ConfiguredProgram {
                                  programId = programName ghcPkgProgram,
                                  programVersion = Nothing,
                 ghcPkgProg = ConfiguredProgram {
                                  programId = programName ghcPkgProgram,
                                  programVersion = Nothing,
index ee2f319..411dc56 100644 (file)
@@ -10,6 +10,7 @@
 module Main (main) where
 
 import Version ( version, targetOS, targetARCH )
 module Main (main) where
 
 import Version ( version, targetOS, targetARCH )
+import Distribution.InstalledPackageInfo.Binary
 import qualified Distribution.Simple.PackageIndex as PackageIndex
 import Distribution.ModuleName hiding (main)
 import Distribution.InstalledPackageInfo
 import qualified Distribution.Simple.PackageIndex as PackageIndex
 import Distribution.ModuleName hiding (main)
 import Distribution.InstalledPackageInfo
@@ -20,14 +21,15 @@ import Distribution.Text
 import Distribution.Version
 import System.FilePath
 import System.Cmd       ( rawSystem )
 import Distribution.Version
 import System.FilePath
 import System.Cmd       ( rawSystem )
-import System.Directory ( getAppUserDataDirectory, createDirectoryIfMissing )
+import System.Directory ( getAppUserDataDirectory, createDirectoryIfMissing,
+                          getModificationTime )
+import Text.Printf
 
 import Prelude
 
 #include "../../includes/ghcconfig.h"
 
 import System.Console.GetOpt
 
 import Prelude
 
 #include "../../includes/ghcconfig.h"
 
 import System.Console.GetOpt
-import Text.PrettyPrint
 #if __GLASGOW_HASKELL__ >= 609
 import qualified Control.Exception as Exception
 #else
 #if __GLASGOW_HASKELL__ >= 609
 import qualified Control.Exception as Exception
 #else
@@ -67,6 +69,10 @@ import System.Process(runInteractiveCommand)
 import qualified System.Info(os)
 #endif
 
 import qualified System.Info(os)
 #endif
 
+#if __GLASGOW_HASKELL__ >= 611
+import System.Console.Terminfo as Terminfo
+#endif
+
 -- -----------------------------------------------------------------------------
 -- Entry point
 
 -- -----------------------------------------------------------------------------
 -- Entry point
 
@@ -323,23 +329,27 @@ runit verbosity cli nonopts = do
         listPackages verbosity cli Nothing (Just match)
     ["latest", pkgid_str] -> do
         pkgid <- readGlobPkgId pkgid_str
         listPackages verbosity cli Nothing (Just match)
     ["latest", pkgid_str] -> do
         pkgid <- readGlobPkgId pkgid_str
-        latestPackage cli pkgid
+        latestPackage verbosity cli pkgid
     ["describe", pkgid_str] ->
         case substringCheck pkgid_str of
           Nothing -> do pkgid <- readGlobPkgId pkgid_str
     ["describe", pkgid_str] ->
         case substringCheck pkgid_str of
           Nothing -> do pkgid <- readGlobPkgId pkgid_str
-                        describePackage cli (Id pkgid)
-          Just m -> describePackage cli (Substring pkgid_str m)
+                        describePackage verbosity cli (Id pkgid)
+          Just m -> describePackage verbosity cli (Substring pkgid_str m)
     ["field", pkgid_str, fields] ->
         case substringCheck pkgid_str of
           Nothing -> do pkgid <- readGlobPkgId pkgid_str
     ["field", pkgid_str, fields] ->
         case substringCheck pkgid_str of
           Nothing -> do pkgid <- readGlobPkgId pkgid_str
-                        describeField cli (Id pkgid) (splitFields fields)
-          Just m -> describeField cli (Substring pkgid_str m)
+                        describeField verbosity cli (Id pkgid) 
+                                      (splitFields fields)
+          Just m -> describeField verbosity cli (Substring pkgid_str m)
                                       (splitFields fields)
     ["check"] -> do
                                       (splitFields fields)
     ["check"] -> do
-        checkConsistency cli
+        checkConsistency verbosity cli
 
     ["dump"] -> do
 
     ["dump"] -> do
-        dumpPackages cli
+        dumpPackages verbosity cli
+
+    ["recache"] -> do
+        recache verbosity cli
 
     [] -> do
         die ("missing command\n" ++
 
     [] -> do
         die ("missing command\n" ++
@@ -381,19 +391,33 @@ globVersion = Version{ versionBranch=[], versionTags=["*"] }
 -- Some commands operate  on multiple databases, with overlapping semantics:
 --      list, describe, field
 
 -- Some commands operate  on multiple databases, with overlapping semantics:
 --      list, describe, field
 
-type PackageDBName  = FilePath
-type PackageDB      = [InstalledPackageInfo]
+data PackageDB 
+  = PackageDB { location :: FilePath,
+                packages :: [InstalledPackageInfo] }
 
 
-type NamedPackageDB = (PackageDBName, PackageDB)
-type PackageDBStack = [NamedPackageDB]
+type PackageDBStack = [PackageDB]
         -- A stack of package databases.  Convention: head is the topmost
         -- A stack of package databases.  Convention: head is the topmost
-        -- in the stack.  Earlier entries override later one.
+        -- in the stack.
 
 allPackagesInStack :: PackageDBStack -> [InstalledPackageInfo]
 
 allPackagesInStack :: PackageDBStack -> [InstalledPackageInfo]
-allPackagesInStack = concatMap snd
+allPackagesInStack = concatMap packages
 
 
-getPkgDatabases :: Bool -> [Flag] -> IO (PackageDBStack, Maybe PackageDBName)
-getPkgDatabases modify my_flags = do
+getPkgDatabases :: Verbosity
+                -> Bool    -- we are modifying, not reading
+                -> Bool    -- read caches, if available
+                -> [Flag]
+                -> IO (PackageDBStack, 
+                          -- the real package DB stack: [global,user] ++ 
+                          -- DBs specified on the command line with -f.
+                       Maybe FilePath,
+                          -- which one to modify, if any
+                       PackageDBStack)
+                          -- the package DBs specified on the command
+                          -- line, or [global,user] otherwise.  This
+                          -- is used as the list of package DBs for
+                          -- commands that just read the DB, such as 'list'.
+
+getPkgDatabases verbosity modify use_cache 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-config flag by the
   -- 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-config flag by the
@@ -403,47 +427,38 @@ getPkgDatabases modify my_flags = do
      case [ f | FlagGlobalConfig f <- my_flags ] of
         [] -> do mb_dir <- getLibDir
                  case mb_dir of
      case [ f | FlagGlobalConfig f <- my_flags ] of
         [] -> do mb_dir <- getLibDir
                  case mb_dir of
-                        Nothing  -> die err_msg
-                        Just dir ->
-                            do let path = dir </> "package.conf"
-                               exists <- doesFileExist path
-                               unless exists $ die "Can't find package.conf"
-                               return path
+                   Nothing  -> die err_msg
+                   Just dir -> do
+                     r <- lookForPackageDBIn dir
+                     case r of
+                       Nothing -> die ("Can't find package database in " ++ dir)
+                       Just path -> return path
         fs -> return (last fs)
 
         fs -> return (last fs)
 
-  let global_conf_dir = global_conf ++ ".d"
-  global_conf_dir_exists <- doesDirectoryExist global_conf_dir
-  global_confs <-
-    if global_conf_dir_exists
-      then do files <- getDirectoryContents global_conf_dir
-              return [ global_conf_dir ++ '/' : file
-                     | file <- files
-                     , isSuffixOf ".conf" file]
-      else return []
-
   let no_user_db = FlagNoUserDb `elem` my_flags
 
   -- get the location of the user package database, and create it if necessary
   -- getAppUserDataDirectory can fail (e.g. if $HOME isn't set)
   let no_user_db = FlagNoUserDb `elem` my_flags
 
   -- get the location of the user package database, and create it if necessary
   -- getAppUserDataDirectory can fail (e.g. if $HOME isn't set)
-  appdir <- try $ getAppUserDataDirectory "ghc"
+  e_appdir <- try $ getAppUserDataDirectory "ghc"
 
   mb_user_conf <-
      if no_user_db then return Nothing else
 
   mb_user_conf <-
      if no_user_db then return Nothing else
-     case appdir of
-       Right dir -> do
-               let subdir = targetARCH ++ '-':targetOS ++ '-':Version.version
-                   user_conf = dir </> subdir </> "package.conf"
-               user_exists <- doesFileExist user_conf
-               return (Just (user_conf,user_exists))
-       Left _ ->
-               return Nothing
+     case e_appdir of
+       Left _    -> return Nothing
+       Right appdir -> do
+         let subdir = targetARCH ++ '-':targetOS ++ '-':Version.version
+             dir = appdir </> subdir
+         r <- lookForPackageDBIn dir
+         case r of
+           Nothing -> return (Just (dir </> "package.conf.d", False))
+           Just f  -> return (Just (f, True))
 
   -- If the user database doesn't exist, and this command isn't a
   -- "modify" command, then we won't attempt to create or use it.
   let sys_databases
         | Just (user_conf,user_exists) <- mb_user_conf,
 
   -- If the user database doesn't exist, and this command isn't a
   -- "modify" command, then we won't attempt to create or use it.
   let sys_databases
         | Just (user_conf,user_exists) <- mb_user_conf,
-          modify || user_exists = user_conf : global_confs ++ [global_conf]
-        | otherwise             = global_confs ++ [global_conf]
+          modify || user_exists = [user_conf, global_conf]
+        | otherwise             = [global_conf]
 
   e_pkg_path <- try (System.Environment.getEnv "GHC_PACKAGE_PATH")
   let env_stack =
 
   e_pkg_path <- try (System.Environment.getEnv "GHC_PACKAGE_PATH")
   let env_stack =
@@ -466,52 +481,108 @@ getPkgDatabases modify my_flags = do
                is_db_flag (FlagConfig f) = Just f
                is_db_flag _              = Nothing
 
                is_db_flag (FlagConfig f) = Just f
                is_db_flag _              = Nothing
 
-  (final_stack, to_modify) <-
-     if not modify
-        then    -- For a "read" command, we use all the databases
-                -- specified on the command line.  If there are no
-                -- command-line flags specifying databases, the default
-                -- is to use all the ones we know about.
-             if null db_flags then return (env_stack, Nothing)
-                              else return (reverse (nub db_flags), Nothing)
-        else let
-                -- For a "modify" command, treat all the databases as
-                -- a stack, where we are modifying the top one, but it
-                -- can refer to packages in databases further down the
-                -- stack.
-
-                -- -f flags on the command line add to the database
-                -- stack, unless any of them are present in the stack
-                -- already.
-                flag_stack = filter (`notElem` env_stack)
-                                [ f | FlagConfig f <- reverse my_flags ]
-                                ++ env_stack
-
-                -- the database we actually modify is the one mentioned
-                -- rightmost on the command-line.
-                to_modify = if null db_flags 
-                                then Just virt_global_conf
-                                else Just (last db_flags)
-             in
-                return (flag_stack, to_modify)
-
-  db_stack <- mapM (readParseDatabase mb_user_conf) final_stack
-  return (db_stack, to_modify)
-
-readParseDatabase :: Maybe (PackageDBName,Bool)
-                  -> PackageDBName
-                  -> IO (PackageDBName,PackageDB)
-readParseDatabase mb_user_conf filename
+  let flag_db_names | null db_flags = env_stack
+                    | otherwise     = reverse (nub db_flags)
+
+  -- For a "modify" command, treat all the databases as
+  -- a stack, where we are modifying the top one, but it
+  -- can refer to packages in databases further down the
+  -- stack.
+
+  -- -f flags on the command line add to the database
+  -- stack, unless any of them are present in the stack
+  -- already.
+  let final_stack = filter (`notElem` env_stack)
+                     [ 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)
+
+  db_stack <- mapM (readParseDatabase verbosity mb_user_conf use_cache) final_stack
+
+  let flag_db_stack = [ db | db_name <- flag_db_names,
+                        db <- db_stack, location db == db_name ]
+
+  return (db_stack, to_modify, flag_db_stack)
+
+
+lookForPackageDBIn :: FilePath -> IO (Maybe FilePath)
+lookForPackageDBIn dir = do
+  let path_dir = dir </> "package.conf.d"
+  exists_dir <- doesDirectoryExist path_dir
+  if exists_dir then return (Just path_dir) else do
+  let path_file = dir </> "package.conf"
+  exists_file <- doesFileExist path_file
+  if exists_file then return (Just path_file) else return Nothing
+
+readParseDatabase :: Verbosity
+                  -> Maybe (FilePath,Bool)
+                  -> Bool -- use cache
+                  -> FilePath
+                  -> IO PackageDB
+
+readParseDatabase verbosity mb_user_conf use_cache path
   -- the user database (only) is allowed to be non-existent
   -- the user database (only) is allowed to be non-existent
-  | Just (user_conf,False) <- mb_user_conf, filename == user_conf
-  = return (filename, [])
+  | Just (user_conf,False) <- mb_user_conf, path == user_conf
+  = return PackageDB { location = path, packages = [] }
   | otherwise
   | otherwise
-  = do str <- readFile filename
-       let packages = map convertPackageInfoIn $ read str
-       _ <- Exception.evaluate packages
-         `catchError` \e->
-            die ("error while parsing " ++ filename ++ ": " ++ show e)
-       return (filename,packages)
+  = do e <- try $ getDirectoryContents path
+       case e of
+         Left _   -> do
+              pkgs <- parseMultiPackageConf verbosity path
+              return PackageDB{ location = path, packages = pkgs }              
+         Right fs
+           | not use_cache -> ignore_cache
+           | otherwise -> do
+              let cache = path </> cachefilename
+              tdir     <- getModificationTime path
+              e_tcache <- try $ getModificationTime cache
+              case e_tcache of
+                Left ex -> do
+                     when (verbosity > Normal) $
+                        putStrLn ("warning: cannot read cache file " ++ cache ++ ": " ++ show ex)
+                     ignore_cache
+                Right tcache
+                  | tcache >= tdir -> do
+                     when (verbosity > Normal) $
+                        putStrLn ("using cache: " ++ cache)
+                     pkgs <- readBinPackageDB cache
+                     let pkgs' = map convertPackageInfoIn pkgs
+                     return PackageDB { location = path, packages = pkgs' }
+                  | otherwise -> do
+                     when (verbosity >= Normal) $ do
+                        putStrLn ("WARNING: cache is out of date: " ++ cache)
+                        putStrLn "  use 'ghc-pkg recache' to fix."
+                     ignore_cache
+            where
+                 ignore_cache = do
+                     let confs = filter (".conf" `isSuffixOf`) fs
+                     pkgs <- mapM (parseSingletonPackageConf verbosity) $
+                                   map (path </>) confs
+                     return PackageDB { location = path, packages = pkgs }
+
+
+parseMultiPackageConf :: Verbosity -> FilePath -> IO [InstalledPackageInfo]
+parseMultiPackageConf verbosity file = do
+  when (verbosity > Normal) $ putStrLn ("reading package database: " ++ file)
+  str <- readFile file
+  let pkgs = map convertPackageInfoIn $ read str
+  Exception.evaluate pkgs
+    `catchError` \e->
+       die ("error while parsing " ++ file ++ ": " ++ show e)
+  
+parseSingletonPackageConf :: Verbosity -> FilePath -> IO InstalledPackageInfo
+parseSingletonPackageConf verbosity file = do
+  when (verbosity > Normal) $ putStrLn ("reading package config: " ++ file)
+  readFile file >>= parsePackageInfo
+
+cachefilename :: FilePath
+cachefilename = "package.cache"
 
 -- -----------------------------------------------------------------------------
 -- Registering
 
 -- -----------------------------------------------------------------------------
 -- Registering
@@ -524,10 +595,12 @@ registerPackage :: FilePath
                 -> Force
                 -> IO ()
 registerPackage input verbosity my_flags auto_ghci_libs update force = do
                 -> Force
                 -> IO ()
 registerPackage input verbosity my_flags auto_ghci_libs update force = do
-  (db_stack, Just to_modify) <- getPkgDatabases True my_flags
+  (db_stack, Just to_modify, _flag_dbs) <- 
+      getPkgDatabases verbosity True True my_flags
+
   let
         db_to_operate_on = my_head "register" $
   let
         db_to_operate_on = my_head "register" $
-                           filter ((== to_modify).fst) db_stack
+                           filter ((== to_modify).location) db_stack
   --
   s <-
     case input of
   --
   s <-
     case input of
@@ -546,13 +619,16 @@ registerPackage input verbosity my_flags auto_ghci_libs update force = do
   when (verbosity >= Normal) $
       putStrLn "done."
 
   when (verbosity >= Normal) $
       putStrLn "done."
 
-  let truncated_stack = dropWhile ((/= to_modify).fst) db_stack
+  let truncated_stack = dropWhile ((/= to_modify).location) db_stack
   -- truncate the stack for validation, because we don't allow
   -- packages lower in the stack to refer to those higher up.
   validatePackageConfig pkg truncated_stack auto_ghci_libs update force
   -- truncate the stack for validation, because we don't allow
   -- packages lower in the stack to refer to those higher up.
   validatePackageConfig pkg truncated_stack auto_ghci_libs update force
-  let new_details = filter not_this (snd db_to_operate_on) ++ [pkg]
-      not_this p = sourcePackageId p /= sourcePackageId pkg
-  writeNewConfig verbosity to_modify new_details
+  let 
+     removes = [ RemovePackage p
+               | p <- packages db_to_operate_on,
+                 sourcePackageId p == sourcePackageId pkg ]
+  --
+  changeDB verbosity (removes ++ [AddPackage pkg]) db_to_operate_on
 
 parsePackageInfo
         :: String
 
 parsePackageInfo
         :: String
@@ -565,41 +641,95 @@ parsePackageInfo str =
                            (Just l, s) -> die (show l ++ ": " ++ s)
 
 -- -----------------------------------------------------------------------------
                            (Just l, s) -> die (show l ++ ": " ++ s)
 
 -- -----------------------------------------------------------------------------
+-- Making changes to a package database
+
+data DBOp = RemovePackage InstalledPackageInfo
+          | AddPackage    InstalledPackageInfo
+          | ModifyPackage InstalledPackageInfo
+
+changeDB :: Verbosity -> [DBOp] -> PackageDB -> IO ()
+changeDB verbosity cmds db = do
+  let db' = updateInternalDB db cmds
+  isfile <- doesFileExist (location db)
+  if isfile
+     then writeNewConfig verbosity (location db') (packages db')
+     else do
+       createDirectoryIfMissing True (location db)
+       changeDBDir verbosity cmds db'
+
+updateInternalDB :: PackageDB -> [DBOp] -> PackageDB
+updateInternalDB db cmds = db{ packages = foldl do_cmd (packages db) cmds }
+ where
+  do_cmd pkgs (RemovePackage p) = 
+    filter ((/= installedPackageId p) . installedPackageId) pkgs
+  do_cmd pkgs (AddPackage p) = p : pkgs
+  do_cmd pkgs (ModifyPackage p) = 
+    do_cmd (do_cmd pkgs (RemovePackage p)) (AddPackage p)
+    
+
+changeDBDir :: Verbosity -> [DBOp] -> PackageDB -> IO ()
+changeDBDir verbosity cmds db = do
+  mapM_ do_cmd cmds
+  updateDBCache verbosity db
+ where
+  do_cmd (RemovePackage p) = do
+    let file = location db </> display (installedPackageId p) <.> "conf"
+    when (verbosity > Normal) $ putStrLn ("removing " ++ file)
+    removeFile file
+  do_cmd (AddPackage p) = do
+    let file = location db </> display (installedPackageId p) <.> "conf"
+    when (verbosity > Normal) $ putStrLn ("writing " ++ file)
+    writeFileAtomic file (showInstalledPackageInfo p)
+  do_cmd (ModifyPackage p) = 
+    do_cmd (AddPackage p)
+
+updateDBCache :: Verbosity -> PackageDB -> IO ()
+updateDBCache verbosity db = do
+  let filename = location db </> cachefilename
+  when (verbosity > Normal) $
+      putStrLn ("writing cache " ++ filename)
+  writeBinPackageDB filename (map convertPackageInfoOut (packages db))
+    `catch` \e ->
+      if isPermissionError e
+      then die (filename ++ ": you don't have permission to modify this file")
+      else ioError e
+
+-- -----------------------------------------------------------------------------
 -- Exposing, Hiding, Unregistering are all similar
 
 exposePackage :: PackageIdentifier -> Verbosity -> [Flag] -> Force -> IO ()
 -- Exposing, Hiding, Unregistering are all similar
 
 exposePackage :: PackageIdentifier -> Verbosity -> [Flag] -> Force -> IO ()
-exposePackage = modifyPackage (\p -> [p{exposed=True}])
+exposePackage = modifyPackage (\p -> ModifyPackage p{exposed=True})
 
 hidePackage :: PackageIdentifier -> Verbosity -> [Flag] -> Force -> IO ()
 
 hidePackage :: PackageIdentifier -> Verbosity -> [Flag] -> Force -> IO ()
-hidePackage = modifyPackage (\p -> [p{exposed=False}])
+hidePackage = modifyPackage (\p -> ModifyPackage p{exposed=False})
 
 unregisterPackage :: PackageIdentifier -> Verbosity -> [Flag] -> Force -> IO ()
 
 unregisterPackage :: PackageIdentifier -> Verbosity -> [Flag] -> Force -> IO ()
-unregisterPackage = modifyPackage (\_ -> [])
+unregisterPackage = modifyPackage RemovePackage
 
 modifyPackage
 
 modifyPackage
-  :: (InstalledPackageInfo -> [InstalledPackageInfo])
+  :: (InstalledPackageInfo -> DBOp)
   -> PackageIdentifier
   -> Verbosity
   -> [Flag]
   -> Force
   -> IO ()
 modifyPackage fn pkgid verbosity my_flags force = do
   -> PackageIdentifier
   -> Verbosity
   -> [Flag]
   -> Force
   -> IO ()
 modifyPackage fn pkgid verbosity my_flags force = do
-  (db_stack, Just _to_modify) <- getPkgDatabases True{-modify-} my_flags
-  ((db_name, pkgs), ps) <- fmap head $ findPackagesByDB db_stack (Id pkgid)
---  let ((db_name, pkgs) : rest_of_stack) = db_stack
---  ps <- findPackages [(db_name,pkgs)] (Id pkgid)
+  (db_stack, Just _to_modify, _flag_dbs) <- 
+      getPkgDatabases verbosity True{-modify-} True{-use cache-} my_flags
+
+  (db, ps) <- fmap head $ findPackagesByDB db_stack (Id pkgid)
   let 
   let 
+      db_name = location db
+      pkgs    = packages db
+
       pids = map sourcePackageId ps
       pids = map sourcePackageId ps
-      modify pkg
-          | sourcePackageId pkg `elem` pids = fn pkg
-          | otherwise                       = [pkg]
-      new_config = concat (map modify pkgs)
 
 
-  let
+      cmds = [ fn pkg | pkg <- pkgs, sourcePackageId pkg `elem` pids ]
+      new_db = updateInternalDB db cmds
+
       old_broken = brokenPackages (allPackagesInStack db_stack)
       old_broken = brokenPackages (allPackagesInStack db_stack)
-      rest_of_stack = [ (nm, mypkgs)
-                      | (nm, mypkgs) <- db_stack, nm /= db_name ]
-      new_stack = (db_name,new_config) : rest_of_stack
+      rest_of_stack = filter ((/= db_name) . location) db_stack
+      new_stack = new_db : rest_of_stack
       new_broken = map sourcePackageId (brokenPackages (allPackagesInStack new_stack))
       newly_broken = filter (`notElem` map sourcePackageId old_broken) new_broken
   --
       new_broken = map sourcePackageId (brokenPackages (allPackagesInStack new_stack))
       newly_broken = filter (`notElem` map sourcePackageId old_broken) new_broken
   --
@@ -608,7 +738,17 @@ modifyPackage fn pkgid verbosity my_flags force = do
            " would break the following packages: "
               ++ unwords (map display newly_broken))
 
            " would break the following packages: "
               ++ unwords (map display newly_broken))
 
-  writeNewConfig verbosity db_name new_config
+  changeDB verbosity cmds db
+
+recache :: Verbosity -> [Flag] -> IO ()
+recache verbosity my_flags = do
+  (db_stack, Just to_modify, _flag_dbs) <- 
+     getPkgDatabases verbosity True{-modify-} False{-no cache-} my_flags
+  let
+        db_to_operate_on = my_head "recache" $
+                           filter ((== to_modify).location) db_stack
+  --
+  changeDB verbosity [] db_to_operate_on
 
 -- -----------------------------------------------------------------------------
 -- Listing packages
 
 -- -----------------------------------------------------------------------------
 -- Listing packages
@@ -618,18 +758,21 @@ listPackages ::  Verbosity -> [Flag] -> Maybe PackageArg
              -> IO ()
 listPackages verbosity my_flags mPackageName mModuleName = do
   let simple_output = FlagSimpleOutput `elem` my_flags
              -> IO ()
 listPackages verbosity my_flags mPackageName mModuleName = do
   let simple_output = FlagSimpleOutput `elem` my_flags
-  (db_stack, _) <- getPkgDatabases False my_flags
+  (db_stack, _, flag_db_stack) <- 
+     getPkgDatabases verbosity False True{-use cache-} my_flags
+
   let db_stack_filtered -- if a package is given, filter out all other packages
         | Just this <- mPackageName =
   let db_stack_filtered -- if a package is given, filter out all other packages
         | Just this <- mPackageName =
-            map (\(conf,pkgs) -> (conf, filter (this `matchesPkg`) pkgs))
-                db_stack
+            [ db{ packages = filter (this `matchesPkg`) (packages db) }
+            | db <- flag_db_stack ]
         | Just match <- mModuleName = -- packages which expose mModuleName
         | Just match <- mModuleName = -- packages which expose mModuleName
-            map (\(conf,pkgs) -> (conf, filter (match `exposedInPkg`) pkgs))
-                db_stack
-        | otherwise = db_stack
+            [ db{ packages = filter (match `exposedInPkg`) (packages db) }
+            | db <- flag_db_stack ]
+        | otherwise = flag_db_stack
 
       db_stack_sorted
 
       db_stack_sorted
-          = [ (db, sort_pkgs pkgs) | (db,pkgs) <- db_stack_filtered ]
+          = [ db{ packages = sort_pkgs (packages db) }
+            | db <- db_stack_filtered ]
           where sort_pkgs = sortBy cmpPkgIds
                 cmpPkgIds pkg1 pkg2 =
                    case pkgName p1 `compare` pkgName p2 of
           where sort_pkgs = sortBy cmpPkgIds
                 cmpPkgIds pkg1 pkg2 =
                    case pkgName p1 `compare` pkgName p2 of
@@ -638,38 +781,65 @@ listPackages verbosity my_flags mPackageName mModuleName = do
                         EQ -> pkgVersion p1 `compare` pkgVersion p2
                    where (p1,p2) = (sourcePackageId pkg1, sourcePackageId pkg2)
 
                         EQ -> pkgVersion p1 `compare` pkgVersion p2
                    where (p1,p2) = (sourcePackageId pkg1, sourcePackageId pkg2)
 
+      stack = reverse db_stack_sorted
+
       match `exposedInPkg` pkg = any match (map display $ exposedModules pkg)
 
       pkg_map = allPackagesInStack db_stack
       broken = map sourcePackageId (brokenPackages pkg_map)
 
       match `exposedInPkg` pkg = any match (map display $ exposedModules pkg)
 
       pkg_map = allPackagesInStack db_stack
       broken = map sourcePackageId (brokenPackages pkg_map)
 
-      show_func = if simple_output then show_simple else mapM_ show_normal
-
-      show_normal (db_name,pkg_confs) =
-          hPutStrLn stdout (render $
-                text db_name <> colon $$ nest 4 packages
-                )
-           where packages
-                    | verbosity >= Verbose = vcat (map pp_pkg pkg_confs)
-                    | otherwise = fsep (punctuate comma (map pp_pkg pkg_confs))
+      show_normal PackageDB{ location = db_name, packages = pkg_confs } =
+          hPutStrLn stdout $ unlines ((db_name ++ ":") : map ("    " ++) pp_pkgs)
+           where
+                 pp_pkgs = map pp_pkg pkg_confs
                  pp_pkg p
                  pp_pkg p
-                   | sourcePackageId p `elem` broken = braces doc
+                   | sourcePackageId p `elem` broken = printf "{%s}" doc
                    | exposed p = doc
                    | exposed p = doc
-                   | otherwise = parens doc
-                   where doc | verbosity >= Verbose = pkg <+> parens ipid
+                   | otherwise = printf "(%s)" doc
+                   where doc | verbosity >= Verbose = printf "%s (%s)" pkg ipid
                              | otherwise            = pkg
                           where
                              | otherwise            = pkg
                           where
-                          InstalledPackageId ipid_str = installedPackageId p
-                          ipid = text ipid_str
-                          pkg = text (display (sourcePackageId p))
+                          InstalledPackageId ipid = installedPackageId p
+                          pkg = display (sourcePackageId p)
 
       show_simple = simplePackageList my_flags . allPackagesInStack
 
 
       show_simple = simplePackageList my_flags . allPackagesInStack
 
-  when (not (null broken) && verbosity /= Silent) $ do
+  when (not (null broken) && not simple_output && verbosity /= Silent) $ do
      prog <- getProgramName
      putStrLn ("WARNING: there are broken packages.  Run '" ++ prog ++ " check' for more details.")
 
      prog <- getProgramName
      putStrLn ("WARNING: there are broken packages.  Run '" ++ prog ++ " check' for more details.")
 
-  show_func (reverse db_stack_sorted)
+  if simple_output then show_simple stack else do
+
+#if __GLASGOW_HASKELL__ < 611
+  mapM_ show_normal stack
+#else
+  let
+     show_colour withF db =
+         mconcat $ map (<#> termText "\n") $
+             (termText (location db) :
+                map (termText "   " <#>) (map pp_pkg (packages db)))
+        where
+                 pp_pkg p
+                   | sourcePackageId p `elem` broken = withF Red  doc
+                   | exposed p                       = doc
+                   | otherwise                       = withF Blue doc
+                   where doc | verbosity >= Verbose
+                             = termText (printf "%s (%s)" pkg ipid)
+                             | otherwise
+                             = termText pkg
+                          where
+                          InstalledPackageId ipid = installedPackageId p
+                          pkg = display (sourcePackageId p)
+
+  is_tty <- hIsTerminalDevice stdout
+  if not is_tty
+     then mapM_ show_normal stack
+     else do tty <- Terminfo.setupTermFromEnv
+             case Terminfo.getCapability tty withForegroundColor of
+                 Nothing -> mapM_ show_normal stack
+                 Just w  -> runTermOutput tty $ mconcat $
+                                                map (show_colour w) stack
+#endif
 
 simplePackageList :: [Flag] -> [InstalledPackageInfo] -> IO ()
 simplePackageList my_flags pkgs = do
 
 simplePackageList :: [Flag] -> [InstalledPackageInfo] -> IO ()
 simplePackageList my_flags pkgs = do
@@ -680,9 +850,11 @@ simplePackageList my_flags pkgs = do
       hPutStrLn stdout $ concat $ intersperse " " strs
 
 showPackageDot :: Verbosity -> [Flag] -> IO ()
       hPutStrLn stdout $ concat $ intersperse " " strs
 
 showPackageDot :: Verbosity -> [Flag] -> IO ()
-showPackageDot _verbosity myflags = do
-  (db_stack, _) <- getPkgDatabases False myflags
-  let all_pkgs = allPackagesInStack db_stack
+showPackageDot verbosity myflags = do
+  (_, _, flag_db_stack) <- 
+      getPkgDatabases verbosity False True{-use cache-} myflags
+
+  let all_pkgs = allPackagesInStack flag_db_stack
       ipix  = PackageIndex.listToInstalledPackageIndex all_pkgs
 
   putStrLn "digraph {"
       ipix  = PackageIndex.listToInstalledPackageIndex all_pkgs
 
   putStrLn "digraph {"
@@ -699,10 +871,12 @@ showPackageDot _verbosity myflags = do
 -- -----------------------------------------------------------------------------
 -- Prints the highest (hidden or exposed) version of a package
 
 -- -----------------------------------------------------------------------------
 -- Prints the highest (hidden or exposed) version of a package
 
-latestPackage ::  [Flag] -> PackageIdentifier -> IO ()
-latestPackage my_flags pkgid = do
-  (db_stack, _) <- getPkgDatabases False my_flags
-  ps <- findPackages db_stack (Id pkgid)
+latestPackage ::  Verbosity -> [Flag] -> PackageIdentifier -> IO ()
+latestPackage verbosity my_flags pkgid = do
+  (_, _, flag_db_stack) <- 
+     getPkgDatabases verbosity False True{-use cache-} my_flags
+
+  ps <- findPackages flag_db_stack (Id pkgid)
   show_pkg (sortBy compPkgIdVer (map sourcePackageId ps))
   where
     show_pkg [] = die "no matches"
   show_pkg (sortBy compPkgIdVer (map sourcePackageId ps))
   where
     show_pkg [] = die "no matches"
@@ -711,16 +885,18 @@ latestPackage my_flags pkgid = do
 -- -----------------------------------------------------------------------------
 -- Describe
 
 -- -----------------------------------------------------------------------------
 -- Describe
 
-describePackage :: [Flag] -> PackageArg -> IO ()
-describePackage my_flags pkgarg = do
-  (db_stack, _) <- getPkgDatabases False my_flags
-  ps <- findPackages db_stack pkgarg
+describePackage :: Verbosity -> [Flag] -> PackageArg -> IO ()
+describePackage verbosity my_flags pkgarg = do
+  (_, _, flag_db_stack) <- 
+      getPkgDatabases verbosity False True{-use cache-} my_flags
+  ps <- findPackages flag_db_stack pkgarg
   doDump ps
 
   doDump ps
 
-dumpPackages :: [Flag] -> IO ()
-dumpPackages my_flags = do
-  (db_stack, _) <- getPkgDatabases False my_flags
-  doDump (allPackagesInStack db_stack)
+dumpPackages :: Verbosity -> [Flag] -> IO ()
+dumpPackages verbosity my_flags = do
+  (_, _, flag_db_stack) <- 
+     getPkgDatabases verbosity False True{-use cache-} my_flags
+  doDump (allPackagesInStack flag_db_stack)
 
 doDump :: [InstalledPackageInfo] -> IO ()
 doDump = mapM_ putStrLn . intersperse "---" . map showInstalledPackageInfo
 
 doDump :: [InstalledPackageInfo] -> IO ()
 doDump = mapM_ putStrLn . intersperse "---" . map showInstalledPackageInfo
@@ -731,11 +907,11 @@ findPackages db_stack pkgarg
   = fmap (concatMap snd) $ findPackagesByDB db_stack pkgarg
 
 findPackagesByDB :: PackageDBStack -> PackageArg
   = fmap (concatMap snd) $ findPackagesByDB db_stack pkgarg
 
 findPackagesByDB :: PackageDBStack -> PackageArg
-                 -> IO [(NamedPackageDB, [InstalledPackageInfo])]
+                 -> IO [(PackageDB, [InstalledPackageInfo])]
 findPackagesByDB db_stack pkgarg
   = case [ (db, matched)
 findPackagesByDB db_stack pkgarg
   = case [ (db, matched)
-         | db@(_, pkgs) <- db_stack,
-           let matched = filter (pkgarg `matchesPkg`) pkgs,
+         | db <- db_stack,
+           let matched = filter (pkgarg `matchesPkg`) (packages db),
            not (null matched) ] of
         [] -> die ("cannot find package " ++ pkg_msg pkgarg)
         ps -> return ps
            not (null matched) ] of
         [] -> die ("cannot find package " ++ pkg_msg pkgarg)
         ps -> return ps
@@ -762,12 +938,13 @@ compPkgIdVer p1 p2 = pkgVersion p1 `compare` pkgVersion p2
 -- -----------------------------------------------------------------------------
 -- Field
 
 -- -----------------------------------------------------------------------------
 -- Field
 
-describeField :: [Flag] -> PackageArg -> [String] -> IO ()
-describeField my_flags pkgarg fields = do
-  (db_stack, _) <- getPkgDatabases False my_flags
+describeField :: Verbosity -> [Flag] -> PackageArg -> [String] -> IO ()
+describeField verbosity my_flags pkgarg fields = do
+  (_, _, flag_db_stack) <- 
+      getPkgDatabases verbosity False True{-use cache-} my_flags
   fns <- toFields fields
   fns <- toFields fields
-  ps <- findPackages db_stack pkgarg
-  let top_dir = takeDirectory (fst (last db_stack))
+  ps <- findPackages flag_db_stack pkgarg
+  let top_dir = takeDirectory (location (last flag_db_stack))
   mapM_ (selectFields fns) (mungePackagePaths top_dir ps)
   where toFields [] = return []
         toFields (f:fs) = case toField f of
   mapM_ (selectFields fns) (mungePackagePaths top_dir ps)
   where toFields [] = return []
         toFields (f:fs) = case toField f of
@@ -828,9 +1005,9 @@ strList = show
 -- -----------------------------------------------------------------------------
 -- Check: Check consistency of installed packages
 
 -- -----------------------------------------------------------------------------
 -- Check: Check consistency of installed packages
 
-checkConsistency :: [Flag] -> IO ()
-checkConsistency my_flags = do
-  (db_stack, _) <- getPkgDatabases True my_flags
+checkConsistency :: Verbosity -> [Flag] -> IO ()
+checkConsistency verbosity my_flags = do
+  (db_stack, _, _) <- getPkgDatabases verbosity True True{-use cache-} my_flags
          -- check behaves like modify for the purposes of deciding which
          -- databases to use, because ordering is important.
 
          -- check behaves like modify for the purposes of deciding which
          -- databases to use, because ordering is important.
 
@@ -912,12 +1089,12 @@ convertPackageInfoIn
     where convert = fromJust . simpleParse
 
 writeNewConfig :: Verbosity -> FilePath -> [InstalledPackageInfo] -> IO ()
     where convert = fromJust . simpleParse
 
 writeNewConfig :: Verbosity -> FilePath -> [InstalledPackageInfo] -> IO ()
-writeNewConfig verbosity filename packages = do
+writeNewConfig verbosity filename ipis = do
   when (verbosity >= Normal) $
       hPutStr stdout "Writing new package config file... "
   createDirectoryIfMissing True $ takeDirectory filename
   let shown = concat $ intersperse ",\n "
   when (verbosity >= Normal) $
       hPutStr stdout "Writing new package config file... "
   createDirectoryIfMissing True $ takeDirectory filename
   let shown = concat $ intersperse ",\n "
-                     $ map (show . convertPackageInfoOut) packages
+                     $ map (show . convertPackageInfoOut) ipis
       fileContents = "[" ++ shown ++ "\n]"
   writeFileAtomic filename fileContents
     `catch` \e ->
       fileContents = "[" ++ shown ++ "\n]"
   writeFileAtomic filename fileContents
     `catch` \e ->
@@ -1028,7 +1205,7 @@ checkDuplicates :: PackageDBStack -> InstalledPackageInfo -> Bool -> Validate ()
 checkDuplicates db_stack pkg update = do
   let
         pkgid = sourcePackageId pkg
 checkDuplicates db_stack pkg update = do
   let
         pkgid = sourcePackageId pkg
-        (_top_db_name, pkgs) : _  = db_stack
+        pkgs  = packages (head db_stack)
   --
   -- Check whether this package id already exists in this DB
   --
   --
   -- Check whether this package id already exists in this DB
   --
index 393e44d..0b8bb37 100644 (file)
@@ -38,7 +38,7 @@ Executable ghc-pkg
     if impl(ghc < 6.9)
         Build-Depends: extensible-exceptions
 
     if impl(ghc < 6.9)
         Build-Depends: extensible-exceptions
 
-    Build-Depends: haskell98, filepath, Cabal
+    Build-Depends: haskell98, filepath, Cabal, bin-package-db
     if !os(windows)
         Build-Depends: unix
     if os(windows)
     if !os(windows)
         Build-Depends: unix
     if os(windows)
index 6c582d0..ae15376 100644 (file)
@@ -31,8 +31,8 @@ endif
 else
 
 $(GHC_PKG_INPLACE) : utils/ghc-pkg/dist/build/$(utils/ghc-pkg_dist_PROG)$(exeext) $(MKDIRHIER)
 else
 
 $(GHC_PKG_INPLACE) : utils/ghc-pkg/dist/build/$(utils/ghc-pkg_dist_PROG)$(exeext) $(MKDIRHIER)
-       "$(MKDIRHIER)" $(dir $(INPLACE_PACKAGE_CONF))
-       echo "[]" > $(INPLACE_PACKAGE_CONF)
+       "$(MKDIRHIER)" $(INPLACE_PACKAGE_CONF)
+       "$(RM)" $(RM_OPTS) $(INPLACE_PACKAGE_CONF)/*
 ifeq "$(Windows)" "YES"
        cp $< $@
 else
 ifeq "$(Windows)" "YES"
        cp $< $@
 else
@@ -59,7 +59,10 @@ utils/ghc-pkg/dist/build/$(utils/ghc-pkg_dist_PROG)$(exeext): utils/ghc-pkg/Main
               -ilibraries/Cabal \
               -ilibraries/filepath \
               -ilibraries/extensible-exceptions \
               -ilibraries/Cabal \
               -ilibraries/filepath \
               -ilibraries/extensible-exceptions \
-              -ilibraries/hpc
+              -ilibraries/hpc \
+              -ilibraries/binary/src \
+              -ilibraries/bin-package-db \
+
 
 utils/ghc-pkg/Version.hs: mk/project.mk
        "$(RM)" $(RM_OPTS) $@
 
 utils/ghc-pkg/Version.hs: mk/project.mk
        "$(RM)" $(RM_OPTS) $@
@@ -78,7 +81,7 @@ $(eval $(call clean-target,utils/ghc-pkg,dist,\
 
 utils/ghc-pkg_dist-install_PROG = ghc-pkg
 utils/ghc-pkg_dist-install_MODULES = Main Version
 
 utils/ghc-pkg_dist-install_PROG = ghc-pkg
 utils/ghc-pkg_dist-install_MODULES = Main Version
-utils/ghc-pkg_dist-install_DEPS = Cabal
+utils/ghc-pkg_dist-install_DEPS = Cabal terminfo bin-package-db
 utils/ghc-pkg_dist-install_SHELL_WRAPPER = YES
 utils/ghc-pkg_dist-install_INSTALL_SHELL_WRAPPER = YES
 utils/ghc-pkg_dist-install_INSTALL_SHELL_WRAPPER_NAME = ghc-pkg-$(ProjectVersion)
 utils/ghc-pkg_dist-install_SHELL_WRAPPER = YES
 utils/ghc-pkg_dist-install_INSTALL_SHELL_WRAPPER = YES
 utils/ghc-pkg_dist-install_INSTALL_SHELL_WRAPPER_NAME = ghc-pkg-$(ProjectVersion)