Support for abi-depends for computing shadowing.
authorEdward Z. Yang <ezyang@cs.stanford.edu>
Wed, 14 Dec 2016 09:28:43 +0000 (01:28 -0800)
committerEdward Z. Yang <ezyang@cs.stanford.edu>
Wed, 21 Dec 2016 16:49:06 +0000 (08:49 -0800)
Summary:
This is a complete fix based off of
ed7af26606b3a605a4511065ca1a43b1c0f3b51d for handling
shadowing and out-of-order -package-db flags simultaneously.

The general strategy is we first put all databases together,
overriding packages as necessary.  Once this is done, we successfully
prune out broken packages, including packages which depend on a package
whose ABI differs from the ABI we need.

Our check gracefully degrades in the absence of abi-depends, as
we only check deps which are recorded in abi-depends.

Contains time and Cabal submodule update.

Signed-off-by: Edward Z. Yang <ezyang@cs.stanford.edu>
Test Plan: validate

Reviewers: niteria, austin, bgamari

Subscribers: thomie

Differential Revision: https://phabricator.haskell.org/D2846

GHC Trac Issues: #12485

21 files changed:
compiler/backpack/DriverBkp.hs
compiler/ghc.cabal.in
compiler/main/Packages.hs
ghc/ghc-bin.cabal.in
libraries/Cabal
libraries/ghc-boot/GHC/PackageDb.hs
libraries/hpc
libraries/time
testsuite/driver/extra_files.py
testsuite/tests/cabal/Makefile
testsuite/tests/cabal/T12485/Makefile
testsuite/tests/cabal/T12485/all.T
testsuite/tests/cabal/T12485a.stdout [new file with mode: 0644]
testsuite/tests/cabal/T1750.stderr
testsuite/tests/cabal/all.T
testsuite/tests/cabal/shadow1.pkg
testsuite/tests/cabal/shadow2.pkg
testsuite/tests/cabal/shadow3.pkg
testsuite/tests/perf/haddock/all.T
utils/ghc-cabal/Main.hs
utils/ghc-pkg/Main.hs

index cdbe06d..fc46ce1 100644 (file)
@@ -302,6 +302,7 @@ buildUnit session cid insts lunit = do
                                 $ deps ++ [ moduleUnitId mod
                                           | (_, mod) <- insts
                                           , not (isHoleModule mod) ],
+            abiDepends = [],
             ldOptions = case session of
                             TcSession -> []
                             _ -> obj_files,
index 99bb463..a7d380a 100644 (file)
@@ -55,7 +55,7 @@ Library
                    process    >= 1   && < 1.5,
                    bytestring >= 0.9 && < 0.11,
                    binary     == 0.8.*,
-                   time       >= 1.4 && < 1.7,
+                   time       >= 1.4 && < 1.8,
                    containers >= 0.5 && < 0.6,
                    array      >= 0.1 && < 0.6,
                    filepath   >= 1   && < 1.5,
index b6b5e3c..5f1a7d5 100644 (file)
@@ -83,6 +83,7 @@ import System.Directory
 import System.FilePath as FilePath
 import qualified System.FilePath.Posix as FilePath.Posix
 import Control.Monad
+import Data.Graph (stronglyConnComp, SCC(..))
 import Data.Char ( toUpper )
 import Data.List as List
 import Data.Map (Map)
@@ -95,7 +96,6 @@ import qualified Data.Semigroup as Semigroup
 #endif
 import qualified Data.Map as Map
 import qualified Data.Map.Strict as MapStrict
-import qualified FiniteMap as Map
 import qualified Data.Set as Set
 
 -- ---------------------------------------------------------------------------
@@ -1024,14 +1024,30 @@ updateVisibilityMap wiredInMap vis_map = foldl' f vis_map (Map.toList wiredInMap
 
 -- ----------------------------------------------------------------------------
 
-type IsShadowed = Bool
+-- | The reason why a package is unusable.
 data UnusablePackageReason
-  = IgnoredWithFlag
-  | MissingDependencies IsShadowed [InstalledUnitId]
+  = -- | We ignored it explicitly using @-ignore-package@.
+    IgnoredWithFlag
+    -- | This package transitively depends on a package that was never present
+    -- in any of the provided databases.
+  | BrokenDependencies   [InstalledUnitId]
+    -- | This package transitively depends on a package involved in a cycle.
+    -- Note that the list of 'InstalledUnitId' reports the direct dependencies
+    -- of this package that (transitively) depended on the cycle, and not
+    -- the actual cycle itself (which we report separately at high verbosity.)
+  | CyclicDependencies   [InstalledUnitId]
+    -- | This package transitively depends on a package which was ignored.
+  | IgnoredDependencies  [InstalledUnitId]
+    -- | This package transitively depends on a package which was
+    -- shadowed by an ABI-incompatible package.
+  | ShadowedDependencies [InstalledUnitId]
+
 instance Outputable UnusablePackageReason where
     ppr IgnoredWithFlag = text "[ignored with flag]"
-    ppr (MissingDependencies b uids) =
-        brackets (if b then text "shadowed" else empty <+> ppr uids)
+    ppr (BrokenDependencies uids)   = brackets (text "broken" <+> ppr uids)
+    ppr (CyclicDependencies uids)   = brackets (text "cyclic" <+> ppr uids)
+    ppr (IgnoredDependencies uids)  = brackets (text "ignored" <+> ppr uids)
+    ppr (ShadowedDependencies uids) = brackets (text "shadowed" <+> ppr uids)
 
 type UnusablePackages = Map InstalledUnitId
                             (PackageConfig, UnusablePackageReason)
@@ -1040,13 +1056,28 @@ pprReason :: SDoc -> UnusablePackageReason -> SDoc
 pprReason pref reason = case reason of
   IgnoredWithFlag ->
       pref <+> text "ignored due to an -ignore-package flag"
-  MissingDependencies is_shadowed deps ->
-      pref <+> text "unusable due to"
-           <+> (if is_shadowed then text "shadowed"
-                               else text "missing or recursive")
-           <+> text "dependencies:" $$
+  BrokenDependencies deps ->
+      pref <+> text "unusable due to missing dependencies:" $$
+        nest 2 (hsep (map ppr deps))
+  CyclicDependencies deps ->
+      pref <+> text "unusable due to cyclic dependencies:" $$
+        nest 2 (hsep (map ppr deps))
+  IgnoredDependencies deps ->
+      pref <+> text "unusable due to ignored dependencies:" $$
+        nest 2 (hsep (map ppr deps))
+  ShadowedDependencies deps ->
+      pref <+> text "unusable due to shadowed dependencies:" $$
         nest 2 (hsep (map ppr deps))
 
+reportCycles :: DynFlags -> [SCC PackageConfig] -> IO ()
+reportCycles dflags sccs = mapM_ report sccs
+  where
+    report (AcyclicSCC _) = return ()
+    report (CyclicSCC vs) =
+        debugTraceMsg dflags 2 $
+          text "these packages are involved in a cycle:" $$
+            nest 2 (hsep (map (ppr . unitId) vs))
+
 reportUnusable :: DynFlags -> UnusablePackages -> IO ()
 reportUnusable dflags pkgs = mapM_ report (Map.toList pkgs)
   where
@@ -1057,36 +1088,60 @@ reportUnusable dflags pkgs = mapM_ report (Map.toList pkgs)
 
 -- ----------------------------------------------------------------------------
 --
--- Detect any packages that have missing dependencies, and also any
--- mutually-recursive groups of packages (loops in the package graph
--- are not allowed).  We do this by taking the least fixpoint of the
--- dependency graph, repeatedly adding packages whose dependencies are
--- satisfied until no more can be added.
+-- Utilities on the database
 --
-findBroken :: IsShadowed
-           -> [PackageConfig]
-           -> Map InstalledUnitId PackageConfig
-           -> UnusablePackages
-findBroken is_shadowed pkgs pkg_map0 = go [] pkg_map0 pkgs
- where
-   go avail pkg_map not_avail =
-     case partitionWith (depsAvailable pkg_map) not_avail of
-        ([], not_avail) ->
-            Map.fromList [ (unitId p, (p, MissingDependencies is_shadowed deps))
-                         | (p,deps) <- not_avail ]
-        (new_avail, not_avail) ->
-            go (new_avail ++ avail) pkg_map' (map fst not_avail)
-            where pkg_map' = Map.insertList
-                             [ (unitId p, p) | p <- new_avail ]
-                             pkg_map
-
-   depsAvailable :: InstalledPackageIndex
+
+-- | A reverse dependency index, mapping an 'InstalledUnitId' to
+-- the 'InstalledUnitId's which have a dependency on it.
+type RevIndex = Map InstalledUnitId [InstalledUnitId]
+
+-- | Compute the reverse dependency index of a package database.
+reverseDeps :: InstalledPackageIndex -> RevIndex
+reverseDeps db = Map.foldl' go Map.empty db
+  where
+    go r pkg = foldl' (go' (unitId pkg)) r (depends pkg)
+    go' from r to = Map.insertWith (++) to [from] r
+
+-- | Given a list of 'InstalledUnitId's to remove, a database,
+-- and a reverse dependency index (as computed by 'reverseDeps'),
+-- remove those packages, plus any packages which depend on them.
+-- Returns the pruned database, as well as a list of 'PackageConfig's
+-- that was removed.
+removePackages :: [InstalledUnitId] -> RevIndex
+               -> InstalledPackageIndex
+               -> (InstalledPackageIndex, [PackageConfig])
+removePackages uids index m = go uids (m,[])
+  where
+    go [] (m,pkgs) = (m,pkgs)
+    go (uid:uids) (m,pkgs)
+        | Just pkg <- Map.lookup uid m
+        = case Map.lookup uid index of
+            Nothing    -> go uids (Map.delete uid m, pkg:pkgs)
+            Just rdeps -> go (rdeps ++ uids) (Map.delete uid m, pkg:pkgs)
+        | otherwise
+        = go uids (m,pkgs)
+
+-- | Given a 'PackageConfig' from some 'InstalledPackageIndex',
+-- return all entries in 'depends' which correspond to packages
+-- that do not exist in the index.
+depsNotAvailable :: InstalledPackageIndex
                  -> PackageConfig
-                 -> Either PackageConfig (PackageConfig, [InstalledUnitId])
-   depsAvailable pkg_map pkg
-        | null dangling = Left pkg
-        | otherwise     = Right (pkg, dangling)
-        where dangling = filter (not . (`Map.member` pkg_map)) (depends pkg)
+                 -> [InstalledUnitId]
+depsNotAvailable pkg_map pkg = filter (not . (`Map.member` pkg_map)) (depends pkg)
+
+-- | Given a 'PackageConfig' from some 'InstalledPackageIndex'
+-- return all entries in 'abiDepends' which correspond to packages
+-- that do not exist, OR have mismatching ABIs.
+depsAbiMismatch :: InstalledPackageIndex
+                -> PackageConfig
+                -> [InstalledUnitId]
+depsAbiMismatch pkg_map pkg = map fst . filter (not . abiMatch) $ abiDepends pkg
+  where
+    abiMatch (dep_uid, abi)
+        | Just dep_pkg <- Map.lookup dep_uid pkg_map
+        = abiHash dep_pkg == abi
+        | otherwise
+        = False
 
 -- -----------------------------------------------------------------------------
 -- Ignore packages
@@ -1102,6 +1157,98 @@ ignorePackages flags pkgs = Map.fromList (concatMap doit flags)
         -- because a common usage is to -ignore-package P as
         -- a preventative measure just in case P exists.
 
+-- ----------------------------------------------------------------------------
+--
+-- Merging databases
+--
+
+-- | Given a list of databases, merge them together, where
+-- packages with the same unit id in later databases override
+-- earlier ones.  This does NOT check if the resulting database
+-- makes sense (that's done by 'validateDatabase').
+mergeDatabases :: DynFlags -> [(FilePath, [PackageConfig])]
+               -> IO InstalledPackageIndex
+mergeDatabases dflags = foldM merge Map.empty
+  where
+    merge pkg_map (db_path, db) = do
+      debugTraceMsg dflags 2 $
+          text "loading package database" <+> text db_path
+      forM_ (Set.toList override_set) $ \pkg ->
+          debugTraceMsg dflags 2 $
+              text "package" <+> ppr pkg <+>
+              text "overrides a previously defined package"
+      return pkg_map'
+     where
+      db_map = mk_pkg_map db
+      mk_pkg_map = Map.fromList . map (\p -> (unitId p, p))
+
+      -- The set of UnitIds which appear in both db and pkgs.  These are the
+      -- ones that get overridden.  Compute this just to give some
+      -- helpful debug messages at -v2
+      override_set :: Set InstalledUnitId
+      override_set = Set.intersection (Map.keysSet db_map)
+                                      (Map.keysSet pkg_map)
+
+      -- Now merge the sets together (NB: in case of duplicate,
+      -- first argument preferred)
+      pkg_map' :: InstalledPackageIndex
+      pkg_map' = Map.union db_map pkg_map
+
+-- | Validates a database, removing unusable packages from it
+-- (this includes removing packages that the user has explicitly
+-- ignored.)  Our general strategy:
+--
+-- 1. Remove all broken packages (dangling dependencies)
+-- 2. Remove all packages that are cyclic
+-- 3. Apply ignore flags
+-- 4. Remove all packages which have deps with mismatching ABIs
+--
+validateDatabase :: DynFlags -> InstalledPackageIndex
+                 -> (InstalledPackageIndex, UnusablePackages, [SCC PackageConfig])
+validateDatabase dflags pkg_map1 =
+    (pkg_map5, unusable, sccs)
+  where
+    ignore_flags = reverse (ignorePackageFlags dflags)
+
+    -- Compute the reverse dependency index
+    index = reverseDeps pkg_map1
+
+    -- Helper function
+    mk_unusable mk_err dep_matcher m uids =
+      Map.fromList [ (unitId pkg, (pkg, mk_err (dep_matcher m pkg)))
+                   | pkg <- uids ]
+
+    -- Find broken packages
+    directly_broken = filter (not . null . depsNotAvailable pkg_map1)
+                             (Map.elems pkg_map1)
+    (pkg_map2, broken) = removePackages (map unitId directly_broken) index pkg_map1
+    unusable_broken = mk_unusable BrokenDependencies depsNotAvailable pkg_map2 broken
+
+    -- Find recursive packages
+    sccs = stronglyConnComp [ (pkg, unitId pkg, depends pkg)
+                            | pkg <- Map.elems pkg_map2 ]
+    getCyclicSCC (CyclicSCC vs) = map unitId vs
+    getCyclicSCC (AcyclicSCC _) = []
+    (pkg_map3, cyclic) = removePackages (concatMap getCyclicSCC sccs) index pkg_map2
+    unusable_cyclic = mk_unusable CyclicDependencies depsNotAvailable pkg_map3 cyclic
+
+    -- Apply ignore flags
+    directly_ignored = ignorePackages ignore_flags (Map.elems pkg_map3)
+    (pkg_map4, ignored) = removePackages (Map.keys directly_ignored) index pkg_map3
+    unusable_ignored = mk_unusable IgnoredDependencies depsNotAvailable pkg_map4 ignored
+
+    -- Knock out packages whose dependencies don't agree with ABI
+    -- (i.e., got invalidated due to shadowing)
+    directly_shadowed = filter (not . null . depsAbiMismatch pkg_map4)
+                               (Map.elems pkg_map4)
+    (pkg_map5, shadowed) = removePackages (map unitId directly_shadowed) index pkg_map4
+    unusable_shadowed = mk_unusable ShadowedDependencies depsAbiMismatch pkg_map5 shadowed
+
+    unusable = directly_ignored `Map.union` unusable_ignored
+                                `Map.union` unusable_broken
+                                `Map.union` unusable_cyclic
+                                `Map.union` unusable_shadowed
+
 -- -----------------------------------------------------------------------------
 -- When all the command-line options are in, we can process our package
 -- settings and populate the package state.
@@ -1124,25 +1271,24 @@ mkPackageState dflags dbs preload0 = do
 
     1. We want to build a single, unified package database based
        on all of the input databases, which upholds the invariant that
-       there is only one package per any UnitId, and that there are no
-       dangling dependencies. We'll do this by successively merging each
-       input database into this unified database:
+       there is only one package per any UnitId and there are no
+       dangling dependencies.  We'll do this by merging, and
+       then successively filtering out bad dependencies.
 
-       a) if an input database defines unit ID that is already in
+       a) Merge all the databases together.
+          If an input database defines unit ID that is already in
           the unified database, that package SHADOWS the existing
-          package in the current unified database
-            * for every such shadowed package, we remove it and any
-              packages which transitively depend on it from the
-              unified datbase
+          package in the current unified database.
 
-       b) remove packages selected by -ignore-package from input database
+       b) Remove all packages with missing dependencies, or
+          mutually recursive dependencies.
 
-       c) remove any packages with missing dependencies or mutually recursive
-          dependencies from the input database
+       b) Remove packages selected by -ignore-package from input database
 
-       d) report (with -v) any packages that were removed by steps 1-3
+       c) Remove all packages which depended on packages that are now
+          shadowed by an ABI-incompatible package
 
-       e) merge the input database into the unified database
+       d) report (with -v) any packages that were removed by steps 1-3
 
     2. We want to look at the flags controlling package visibility,
        and build a mapping of what module names are in scope and
@@ -1170,75 +1316,23 @@ mkPackageState dflags dbs preload0 = do
 -}
 
   let other_flags = reverse (packageFlags dflags)
-      ignore_flags = reverse (ignorePackageFlags dflags)
   debugTraceMsg dflags 2 $
       text "package flags" <+> ppr other_flags
 
-  let merge (pkg_map, prev_unusable) (db_path, db) = do
-            debugTraceMsg dflags 2 $
-                text "loading package database" <+> text db_path
-            forM_ (Set.toList shadow_set) $ \pkg ->
-                debugTraceMsg dflags 2 $
-                    text "package" <+> ppr pkg <+>
-                    text "shadows a previously defined package"
-            reportUnusable dflags unusable
-            -- NB: an unusable unit ID can become usable again
-            -- if it's validly specified in a later package stack.
-            -- Keep unusable up-to-date!
-            return (pkg_map', (prev_unusable `Map.difference` pkg_map')
-                                    `Map.union` unusable)
-        where -- The set of UnitIds which appear in both
-              -- db and pkgs (to be shadowed from pkgs)
-              shadow_set :: Set InstalledUnitId
-              shadow_set = foldr ins Set.empty db
-                where ins pkg s
-                        -- If the package from the upper database is
-                        -- in the lower database, and the ABIs don't
-                        -- match...
-                        | Just old_pkg <- Map.lookup (unitId pkg) pkg_map
-                        , abiHash old_pkg /= abiHash pkg
-                        -- ...add this unit ID to the set of unit IDs
-                        -- which (transitively) should be shadowed from
-                        -- the lower database.
-                        = Set.insert (unitId pkg) s
-                        | otherwise
-                        = s
-              -- Remove shadow_set from pkg_map...
-              shadowed_pkgs0 :: [PackageConfig]
-              shadowed_pkgs0 = filter (not . (`Set.member` shadow_set) . unitId)
-                                      (Map.elems pkg_map)
-              -- ...and then remove anything transitively broken
-              -- this way.
-              shadowed = findBroken True shadowed_pkgs0 Map.empty
-              shadowed_pkgs :: [PackageConfig]
-              shadowed_pkgs = filter (not . (`Map.member` shadowed) . unitId)
-                                     shadowed_pkgs0
-
-              -- Apply ignore flags to db (TODO: could extend command line
-              -- flag format to support per-database ignore now!  More useful
-              -- than what we have now.)
-              ignored = ignorePackages ignore_flags db
-              db2 = filter (not . (`Map.member` ignored) . unitId) db
-
-              -- Look for broken packages (either from ignore, or possibly
-              -- because the db was broken to begin with)
-              mk_pkg_map = Map.fromList . map (\p -> (unitId p, p))
-              broken = findBroken False db2 (mk_pkg_map shadowed_pkgs)
-              db3 = filter (not . (`Map.member` broken) . unitId) db2
-
-              unusable = shadowed `Map.union` ignored
-                                  `Map.union` broken
-
-              -- Now merge the sets together (NB: later overrides
-              -- earlier!)
-              pkg_map' :: Map InstalledUnitId PackageConfig
-              pkg_map' = mk_pkg_map (shadowed_pkgs ++ db3)
-
-  (pkg_map1, unusable) <- foldM merge (Map.empty, Map.empty) dbs
+  -- Merge databases together, without checking validity
+  pkg_map1 <- mergeDatabases dflags dbs
+
+  -- Now that we've merged everything together, prune out unusable
+  -- packages.
+  let (pkg_map2, unusable, sccs) = validateDatabase dflags pkg_map1
+
+  reportCycles dflags sccs
+  reportUnusable dflags unusable
+
   -- Apply trust flags (these flags apply regardless of whether
   -- or not packages are visible or not)
   pkgs1 <- foldM (applyTrustFlag dflags unusable)
-                 (Map.elems pkg_map1) (reverse (trustFlags dflags))
+                 (Map.elems pkg_map2) (reverse (trustFlags dflags))
   let prelim_pkg_db = extendPackageConfigMap emptyPackageConfigMap pkgs1
 
   --
index b9babfe..dce6142 100644 (file)
@@ -52,7 +52,7 @@ Executable ghc
             deepseq        == 1.4.*,
             ghci           == @ProjectVersionMunged@,
             haskeline      == 0.7.*,
-            time           == 1.6.*,
+            time           == 1.7.*,
             transformers   == 0.5.*
         CPP-Options: -DGHCI
         GHC-Options: -fno-warn-name-shadowing
index 034b441..09865f6 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 034b44191740214c9e691439b604a8ac95ee9946
+Subproject commit 09865f60caa55a7b02880f2a779c9dd8e1be5ac0
index 0999109..9b2889f 100644 (file)
@@ -66,7 +66,8 @@ import System.Directory
 
 
 -- | This is a subset of Cabal's 'InstalledPackageInfo', with just the bits
--- that GHC is interested in.
+-- that GHC is interested in.  See Cabal's documentation for a more detailed
+-- description of all of the fields.
 --
 data InstalledPackageInfo compid srcpkgid srcpkgname instunitid unitid modulename mod
    = InstalledPackageInfo {
@@ -78,6 +79,9 @@ data InstalledPackageInfo compid srcpkgid srcpkgname instunitid unitid modulenam
        packageVersion     :: Version,
        abiHash            :: String,
        depends            :: [instunitid],
+       -- | Like 'depends', but each dependency is annotated with the
+       -- ABI hash we expect the dependency to respect.
+       abiDepends         :: [(instunitid, String)],
        importDirs         :: [FilePath],
        hsLibraries        :: [String],
        extraLibraries     :: [String],
@@ -159,6 +163,7 @@ emptyInstalledPackageInfo =
        packageVersion     = Version [] [],
        abiHash            = "",
        depends            = [],
+       abiDepends         = [],
        importDirs         = [],
        hsLibraries        = [],
        extraLibraries     = [],
@@ -307,7 +312,7 @@ instance (RepInstalledPackageInfo a b c d e f g) =>
   put (InstalledPackageInfo
          unitId componentId instantiatedWith sourcePackageId
          packageName packageVersion
-         abiHash depends importDirs
+         abiHash depends abiDepends importDirs
          hsLibraries extraLibraries extraGHCiLibraries
          libraryDirs libraryDynDirs
          frameworks frameworkDirs
@@ -325,6 +330,7 @@ instance (RepInstalledPackageInfo a b c d e f g) =>
              instantiatedWith)
     put abiHash
     put (map toStringRep depends)
+    put (map (\(k,v) -> (toStringRep k, v)) abiDepends)
     put importDirs
     put hsLibraries
     put extraLibraries
@@ -355,6 +361,7 @@ instance (RepInstalledPackageInfo a b c d e f g) =>
     instantiatedWith   <- get
     abiHash            <- get
     depends            <- get
+    abiDepends         <- get
     importDirs         <- get
     hsLibraries        <- get
     extraLibraries     <- get
@@ -383,6 +390,7 @@ instance (RepInstalledPackageInfo a b c d e f g) =>
               (fromStringRep packageName) packageVersion
               abiHash
               (map fromStringRep depends)
+              (map (\(k,v) -> (fromStringRep k, v)) abiDepends)
               importDirs
               hsLibraries extraLibraries extraGHCiLibraries
               libraryDirs libraryDynDirs
index 8625c1c..9267329 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 8625c1c0550719437acad89d49401cf048990084
+Subproject commit 92673292ab7ce7878e982d0a02df3e548ef15b52
index 52e0f5e..b6098be 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 52e0f5e85ffbaab77b155d48720fb216021c8a73
+Subproject commit b6098be8a4facfa854c633f2a3a82ab8e72962ef
index c2cb401..8b0f99b 100644 (file)
@@ -88,6 +88,7 @@ extra_src_files = {
   'T12035j': ['T12035.hs', 'T12035a.hs', 'T12035.hs-boot'],
   'T12042': ['T12042.hs', 'T12042a.hs', 'T12042.hs-boot'],
   'T12485': ['a.pkg', 'b.pkg', 'Main.hs'],
+  'T12485a': ['shadow1.pkg', 'shadow2.pkg', 'shadow3.pkg'],
   'T12733': ['p/', 'q/', 'Setup.hs'],
   'T1372': ['p1/', 'p2/'],
   'T1407': ['A.c'],
index 45fb6eb..64034d4 100644 (file)
@@ -136,12 +136,14 @@ LOCAL_GHC_PKGSHADOW13 = '$(GHC_PKG)' --no-user-package-db -f $(PKGCONFSHADOW1) -
 
 # Test package shadowing behaviour.
 #
-#     localshadow1.package.conf:  shadowdep-1-XXX <- shadow-1-XXX
-#     localshadow2.package.conf:                     shadow-1-XXX
+# The general principle is that we shadow in order of declarations,
+# but we determine what gets overridden based on ABI dependencies.
 #
-# If the ABI hash of boths shadow-1s are the same, we'll just accept
-# the later shadow version.  However, if the ABIs are different, we
-# should complain!
+# Here is the structure of our databases (unitid=abi):
+#
+#     localshadow1.package.conf:  shadowdep-1-XXX=ddd -> shadow-1-XXX=aaa
+#     localshadow2.package.conf:                         shadow-1-XXX=bbb
+#     localshadow3.package.conf:                         shadow-1-XXX=aaa
 shadow:
        rm -rf $(PKGCONFSHADOW1) $(PKGCONFSHADOW2) $(PKGCONFSHADOW3) shadow.hs shadow.o shadow.hi shadow.out shadow.hs shadow.hi
        $(LOCAL_GHC_PKGSHADOW1) init $(PKGCONFSHADOW1)
@@ -164,8 +166,8 @@ shadow:
        if '$(TEST_HC)' $(TEST_HC_OPTS) -package-db $(PKGCONFSHADOW1) -package-db $(PKGCONFSHADOW2) -package shadowdep -c shadow.hs -fno-code; then false; else true; fi
 #
 # Reversing the orders of the configs fixes the problem, because now
-# the shadow-1-XXX defined in the same DB as shadowdep shadows
-# shadow-1-XXX in localshadow2.package.conf
+# we prefer the shadow-1 from the first database, which has the correct
+# ABI hash for shadowdep-1.
 #
        @echo "should SUCCEED:"
        '$(TEST_HC)' $(TEST_HC_OPTS) -package-db $(PKGCONFSHADOW2) -package-db $(PKGCONFSHADOW1) -package shadowdep -c shadow.hs -fno-code
@@ -175,6 +177,31 @@ shadow:
        @echo "should SUCCEED:"
        '$(TEST_HC)' $(TEST_HC_OPTS) -package-db $(PKGCONFSHADOW3) -package-db $(PKGCONFSHADOW1) -package shadowdep -c shadow.hs -fno-code
 
+# Test that order we pass databases doesn't matter
+#
+#     1. shadow-1-XXX=aaa
+#     2. shadowdep-1-XXX=ddd (shadow-1-XXX=aaa)
+#     3. shadow-1-XXX=bbb
+.PHONY: T12485a
+T12485a:
+       rm -rf T12485a.package.conf T12485b.package.conf T12485c.package.conf
+       '$(GHC_PKG)' --no-user-package-db init T12485a.package.conf
+       '$(GHC_PKG)' --no-user-package-db init T12485b.package.conf
+       '$(GHC_PKG)' --no-user-package-db init T12485c.package.conf
+       '$(GHC_PKG)' --no-user-package-db -f T12485a.package.conf register -v0 --force shadow1.pkg
+       '$(GHC_PKG)' --no-user-package-db -f T12485b.package.conf register -v0 --force shadow2.pkg
+       '$(GHC_PKG)' --no-user-package-db -f T12485c.package.conf register -v0 --force shadow3.pkg
+       echo "main = return ()" > T12485a.hs
+       # Normal test
+       @echo "should SUCCEED"
+       '$(TEST_HC)' $(TEST_HC_OPTS) -package-db T12485a.package.conf -package-db T12485b.package.conf -package shadowdep -c T12485a.hs -fno-code
+       # Reversed test
+       @echo "should SUCCEED"
+       '$(TEST_HC)' $(TEST_HC_OPTS) -package-db T12485b.package.conf -package-db T12485a.package.conf -package shadowdep -c T12485a.hs -fno-code
+       # Shadow OK, as long as correct one is chosen eventually, even when reversed
+       @echo "should SUCCEED"
+       '$(TEST_HC)' $(TEST_HC_OPTS) -package-db T12485b.package.conf -package-db T12485c.package.conf -package-db T12485a.package.conf -package shadowdep -c T12485a.hs -fno-code
+
 # If we pass --global, we should ignore instances in the user database
 T5442a:
        @rm -rf package.conf.T5442a.global package.conf.T5442a.user
index fc8e992..2ff0c3c 100644 (file)
@@ -9,6 +9,6 @@ T12485 :
        '$(GHC_PKG)' init b.db
        '$(GHC_PKG)' -f a.db/ -f b.db/ register b.pkg # register b.pkg in b.db
        # -package-db in dependency order
-       '$(TEST_HC)' -XNoImplicitPrelude -fforce-recomp -hide-all-packages -no-user-package-db -package-db a.db -package-db b.db -package-id a-1-XXX -package-id b-1-XXX Main.hs
+       '$(TEST_HC)' $(TEST_HC_OPTS) -XNoImplicitPrelude -fforce-recomp -hide-all-packages -no-user-package-db -package-db a.db -package-db b.db -package-id a-1-XXX -package-id b-1-XXX Main.hs
        # -package-db in reverse dependency order
-       '$(TEST_HC)' -XNoImplicitPrelude -fforce-recomp -hide-all-packages -no-user-package-db -package-db b.db -package-db a.db -package-id a-1-XXX -package-id b-1-XXX Main.hs
+       '$(TEST_HC)' $(TEST_HC_OPTS) -XNoImplicitPrelude -fforce-recomp -hide-all-packages -no-user-package-db -package-db b.db -package-db a.db -package-id a-1-XXX -package-id b-1-XXX Main.hs
index 63f6d6a..be817eb 100644 (file)
@@ -1,5 +1,4 @@
 test('T12485',
-     [extra_clean(['a.db', 'b.db', 'Main.o', 'Main', 'Main.hi']),
-      expect_broken(12485)],
+     [extra_clean(['a.db', 'b.db', 'Main.o', 'Main', 'Main.hi'])],
      run_command,
      ['$MAKE -s --no-print-directory T12485'])
diff --git a/testsuite/tests/cabal/T12485a.stdout b/testsuite/tests/cabal/T12485a.stdout
new file mode 100644 (file)
index 0000000..ee83ab2
--- /dev/null
@@ -0,0 +1,3 @@
+should SUCCEED
+should SUCCEED
+should SUCCEED
index 1809d5b..53c5671 100644 (file)
@@ -1,5 +1,5 @@
 WARNING: there are broken packages.  Run 'ghc-pkg check' for more details.
 <command line>: cannot satisfy -package T1750A: 
-    T1750A-1-XXX is unusable due to missing or recursive dependencies:
+    T1750A-1-XXX is unusable due to cyclic dependencies:
       T1750B-1-XXX
     (use -v for more information)
index cc874c7..64f2639 100644 (file)
@@ -101,3 +101,10 @@ test('shadow',
                   'local1shadow2.package.conf',
                   'local1shadow2.package.conf.old']),
      run_command, ['$MAKE -s --no-print-directory shadow'])
+
+test('T12485a',
+     extra_clean(['T12485a.hi', 'T1750.out',
+                  'T12485a.package.conf',
+                  'T12485b.package.conf',
+                  'T12485c.package.conf']),
+     run_command, ['$MAKE -s --no-print-directory T12485a'])
index 1e39602..246d62b 100644 (file)
@@ -4,3 +4,4 @@ id: shadow-1-XXX
 key: shadow-1-XXX
 abi: aaa
 depends:
+abi-depends:
index 5cd54cc..9f6410b 100644 (file)
@@ -1,5 +1,7 @@
 name: shadowdep
 version: 1
+abi: ddd
 id: shadowdep-1-XXX
 key: shadowdep-1-XXX
 depends: shadow-1-XXX
+abi-depends: shadow-1-XXX=aaa
index 6640e9d..04cfb41 100644 (file)
@@ -4,3 +4,4 @@ id: shadow-1-XXX
 key: shadow-1-XXX
 abi: bbb
 depends:
+abi-depends:
index dee39fc..8ec02ce 100644 (file)
@@ -52,7 +52,7 @@ test('haddock.base',
 test('haddock.Cabal',
      [unless(in_tree_compiler(), skip), req_haddock
      ,stats_num_field('bytes allocated',
-          [(wordsize(64), 23706190072, 5)
+          [(wordsize(64), 25478853176 , 5)
             # 2012-08-14: 3255435248 (amd64/Linux)
             # 2012-08-29: 3324606664 (amd64/Linux, new codegen)
             # 2012-10-08: 3373401360 (amd64/Linux)
@@ -91,6 +91,7 @@ test('haddock.Cabal',
             # 2016-10-01: 20619433656 (amd64/Linux) - Cabal update
             # 2016-10-03: 21554874976 (amd64/Linux) - Cabal update
             # 2016-10-06: 23706190072 (amd64/Linux) - Cabal update
+            # 2016-12-20: 25478853176 (amd64/Linux) - Cabal update
 
           ,(platform('i386-unknown-mingw32'), 3293415576, 5)
             # 2012-10-30:                     1733638168 (x86/Windows)
index 3b55fe7..12699a7 100644 (file)
@@ -316,7 +316,7 @@ generate directory distdir dll0Modules config_args
           do cwd <- getCurrentDirectory
              let ipid = mkUnitId (display (packageId pd))
              let installedPkgInfo = inplaceInstalledPackageInfo cwd distdir
-                                        pd (mkAbiHash "") lib lbi clbi
+                                        pd (mkAbiHash "inplace") lib lbi clbi
                  final_ipi = mangleIPI directory distdir lbi $ installedPkgInfo {
                                  Installed.installedUnitId = ipid,
                                  Installed.compatPackageKey = display (packageId pd),
index 290993f..53f5f9d 100644 (file)
@@ -1107,6 +1107,7 @@ convertPackageInfoToCacheFormat pkg =
        GhcPkg.packageName        = packageName pkg,
        GhcPkg.packageVersion     = Version.Version (versionNumbers (packageVersion pkg)) [],
        GhcPkg.depends            = depends pkg,
+       GhcPkg.abiDepends         = map (\(AbiDependency k v) -> (k,unAbiHash v)) (abiDepends pkg),
        GhcPkg.abiHash            = unAbiHash (abiHash pkg),
        GhcPkg.importDirs         = importDirs pkg,
        GhcPkg.hsLibraries        = hsLibraries pkg,