Skip to content

Commit 9d64a70

Browse files
jappeace-slothjappeace
authored andcommitted
Changes the error message from:
``` Error: The following packages are broken because other packages they depend on are missing. These broken packages must be rebuilt before they can be used. planned package consumer-0.1.0.0 is broken due to missing package framework-0.1.0.0+95RTb42ZWxa9J13cUStM0q ```` To ``` Error: The following packages are broken because other packages they depend on are missing. These broken packages must be rebuilt before they can be used. planned package consumer-0.1.0.0 is broken due to missing package framework-0.1.0.0 (has unfilled signature: App) The package is installed as indefinite. To use it, rebuild it in the same cabal project as the consumer so cabal can fill the signatures. ``` Hopefully it gives more guidence on what's going on to the user. I encountered this when trying to use backpack in a nix setup (for example jappeace-sloth/prrrrrrrrr#2) It keeps on wanting to build everything locally and I couldn't understand why. I still don't fully understand why it can't have partially filled modules upstream. It claims it's because of nix but I don't understand it.
1 parent 9d0dbcf commit 9d64a70

10 files changed

Lines changed: 92 additions & 67 deletions

File tree

Cabal/src/Distribution/Backpack/Configure.hs

Lines changed: 61 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import qualified Distribution.Compat.Graph as Graph
3131
import Distribution.InstalledPackageInfo
3232
( InstalledPackageInfo
3333
, emptyInstalledPackageInfo
34+
, requiredSignatures
3435
)
3536
import qualified Distribution.InstalledPackageInfo as Installed
3637
import Distribution.ModuleName
@@ -261,6 +262,17 @@ toComponentLocalBuildInfos
261262
packageDependsIndex = PackageIndex.fromList (lefts local_graph)
262263
fullIndex = Graph.fromDistinctList local_graph
263264

265+
let -- Map from dependency UnitId to its PackageId, built from includes
266+
-- of all ready components. Used to resolve opaque hashed UnitIds
267+
-- in broken-package error messages.
268+
depPkgMap :: Map UnitId PackageId
269+
depPkgMap = Map.fromList
270+
[ (unDefUnitId (ci_id ci), ci_pkgid ci)
271+
| rc <- graph
272+
, Right instc <- [rc_i rc]
273+
, ci <- instc_includes instc
274+
]
275+
264276
case Graph.broken fullIndex of
265277
[] -> return ()
266278
-- If there are promised dependencies, we don't know what the dependencies
@@ -270,26 +282,25 @@ toComponentLocalBuildInfos
270282
broken
271283
| not (null promisedPkgDeps) -> return ()
272284
| otherwise ->
273-
-- TODO: ppr this
274-
dieProgress . text $
275-
"The following packages are broken because other"
276-
++ " packages they depend on are missing. These broken "
277-
++ "packages must be rebuilt before they can be used.\n"
278-
-- TODO: Undupe.
279-
++ unlines
280-
[ "installed package "
281-
++ prettyShow (packageId pkg)
282-
++ " is broken due to missing package "
283-
++ intercalate ", " (map prettyShow deps)
285+
dieProgress $
286+
text "The following packages are broken because other"
287+
<+> text "packages they depend on are missing. These broken"
288+
<+> text "packages must be rebuilt before they can be used."
289+
$$ nest 2 (vcat $
290+
[ hang (text "installed package" <+> pretty (packageId pkg)) 4
291+
(text "is broken due to missing package"
292+
<+> hsep (punctuate comma (map pretty deps)))
284293
| (Left pkg, deps) <- broken
285294
]
286-
++ unlines
287-
[ "planned package "
288-
++ prettyShow (packageId pkg)
289-
++ " is broken due to missing package "
290-
++ intercalate ", " (map prettyShow deps)
295+
++
296+
[ hang (text "planned package" <+> pretty (packageId pkg)) 4
297+
(vcat $
298+
text "is broken due to missing package"
299+
: [ nest 2 (dispMissingDep installedPackageSet depPkgMap dep)
300+
| dep <- deps
301+
])
291302
| (Right pkg, deps) <- broken
292-
]
303+
])
293304

294305
-- In this section, we'd like to look at the 'packageDependsIndex'
295306
-- and see if we've picked multiple versions of the same
@@ -338,6 +349,39 @@ toComponentLocalBuildInfos
338349
-- forM clbis $ \(clbi,deps) -> info verbosity $ "UNIT" ++ hashUnitId (componentUnitId clbi) ++ "\n" ++ intercalate "\n" (map hashUnitId deps)
339350
return (clbis, packageDependsIndex)
340351

352+
-- | Pretty-print a missing dependency, resolving opaque hashed 'UnitId's
353+
-- to their human-readable package id and signature info when possible.
354+
--
355+
-- When an indefinite Backpack package is installed separately (e.g. via
356+
-- nix callCabal2nix), only the indefinite variant (with unfilled signatures)
357+
-- exists in the package DB. The consumer needs an instantiated variant
358+
-- which was never built. The fix is to add both packages to the same
359+
-- cabal project so cabal can fill the signatures.
360+
dispMissingDep
361+
:: InstalledPackageIndex -- ^ all installed packages
362+
-> Map UnitId PackageId -- ^ dep UnitId to its PackageId (from includes)
363+
-> UnitId -- ^ the missing dependency
364+
-> Doc
365+
dispMissingDep installedPkgSet depPkgMap uid =
366+
case Map.lookup uid depPkgMap of
367+
Just pkgid ->
368+
let ipiSigs = [ sigs
369+
| ipi <- PackageIndex.lookupSourcePackageId installedPkgSet pkgid
370+
, let sigs = requiredSignatures ipi
371+
, not (Set.null sigs)
372+
]
373+
in case ipiSigs of
374+
(sigs : _) ->
375+
pretty pkgid
376+
<+> parens (text "has unfilled"
377+
<+> (if Set.size sigs > 1 then text "signatures:" else text "signature:")
378+
<+> hsep (punctuate comma (map pretty (Set.toList sigs))))
379+
$$ nest 2 (text "The package is installed as indefinite."
380+
$$ text "To use it, rebuild it in the same cabal project as the"
381+
<+> text "consumer so cabal can fill the signatures.")
382+
[] -> pretty pkgid <+> parens (pretty uid)
383+
Nothing -> pretty uid
384+
341385
-- Build ComponentLocalBuildInfo for each component we are going
342386
-- to build.
343387
--

cabal-testsuite/PackageTests/Backpack/Fail5/Fail5.cabal

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,10 @@ name: Fail5
33
version: 0.1.0.0
44
build-type: Simple
55

6-
library framework-utils
7-
exposed-modules: Utils
8-
hs-source-dirs: repo/framework-0.1.0.0/src-utils
9-
build-depends: base
10-
default-language: Haskell2010
11-
visibility: public
12-
136
library framework
147
signatures: App
15-
exposed-modules: Framework
16-
hs-source-dirs: repo/framework-0.1.0.0/src repo/framework-0.1.0.0/src-sig
17-
build-depends: base, Fail5:framework-utils
8+
hs-source-dirs: repo/framework-0.1.0.0/src-sig
9+
build-depends: base
1810
default-language: Haskell2010
1911
visibility: public
2012

@@ -27,6 +19,6 @@ library app-impl
2719

2820
executable consumer
2921
main-is: Main.hs
30-
build-depends: base, Fail5:framework, Fail5:framework-utils, Fail5:app-impl
22+
build-depends: base, Fail5:framework, Fail5:app-impl
3123
hs-source-dirs: repo/consumer-0.1.0.0
3224
default-language: Haskell2010
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import Framework (greet)
2-
import Utils (utilHelper)
1+
import App (appMessage)
32
main :: IO ()
4-
main = putStrLn greet >> putStrLn (utilHelper "direct")
3+
main = putStrLn appMessage

cabal-testsuite/PackageTests/Backpack/Fail5/repo/consumer-0.1.0.0/consumer.cabal

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ build-type: Simple
55

66
executable consumer
77
main-is: Main.hs
8-
build-depends: base, framework, framework:framework-utils, app-impl
8+
build-depends: base, framework, app-impl
99
default-language: Haskell2010

cabal-testsuite/PackageTests/Backpack/Fail5/repo/framework-0.1.0.0/framework.cabal

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,8 @@ name: framework
33
version: 0.1.0.0
44
build-type: Simple
55

6-
library framework-utils
7-
exposed-modules: Utils
8-
hs-source-dirs: src-utils
9-
build-depends: base
10-
default-language: Haskell2010
11-
visibility: public
12-
136
library
147
signatures: App
15-
exposed-modules: Framework
16-
hs-source-dirs: src src-sig
17-
build-depends: base, framework:framework-utils
8+
hs-source-dirs: src-sig
9+
build-depends: base
1810
default-language: Haskell2010

cabal-testsuite/PackageTests/Backpack/Fail5/repo/framework-0.1.0.0/src-utils/Utils.hs

Lines changed: 0 additions & 3 deletions
This file was deleted.

cabal-testsuite/PackageTests/Backpack/Fail5/repo/framework-0.1.0.0/src/Framework.hs

Lines changed: 0 additions & 5 deletions
This file was deleted.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Setup configure
2+
Configuring consumer-0.1.0.0...
3+
Error:
4+
The following packages are broken because other packages they depend on are missing. These broken packages must be rebuilt before they can be used.
5+
planned package consumer-0.1.0.0
6+
is broken due to missing package
7+
framework-0.1.0.0 (has unfilled signature: App)
8+
The package is installed as indefinite.
9+
To use it, rebuild it in the same cabal project as the consumer so cabal can fill the signatures.

cabal-testsuite/PackageTests/Backpack/Fail5/setup-external-fail.test.hs

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,8 @@ main = setupAndCabalTest $ do
99
recordMode DoNotRecord $
1010
withDirectory "repo/app-impl-0.1.0.0" $ setup_install []
1111
-- Configure consumer — should fail because the instantiated
12-
-- framework (with App=app-impl:App) was never built
12+
-- framework (with App=app-impl:App) was never built.
13+
-- The exact error message is checked via the .out file.
1314
withDirectory "repo/consumer-0.1.0.0" $ do
14-
r <- recordMode DoNotRecord . fails $ setup' "configure" []
15-
-- The error should explain the full picture:
16-
--
17-
-- 1. Which package is broken
18-
assertOutputContains "consumer" r
19-
-- 2. Which signature needs filling
20-
assertOutputContains "App" r
21-
-- 3. That the indefinite package exists but the required
22-
-- instantiation was not built — the user installed
23-
-- framework separately, so only the indefinite version
24-
-- (App=<App>) is in the package db. The instantiated
25-
-- variant (App=app-impl:App) doesn't exist.
26-
assertOutputContains "instantiat" r
27-
-- 4. Actionable guidance: rebuild framework together with
28-
-- the consumer so cabal can create the instantiation.
29-
assertOutputContains "rebuild" r
15+
fails $ setup' "configure" []
3016
return ()
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
synopsis: "Improve Backpack broken-package error to show unfilled signatures"
3+
packages: [Cabal]
4+
---
5+
6+
When an indefinite Backpack package is installed separately (e.g. via
7+
nix `callCabal2nix`), configuring a consumer that depends on it would
8+
show an opaque hashed UnitId like
9+
`framework-0.1.0.0+95RTb42ZWxa9J13cUStM0q`. The error now shows the
10+
package name, which signatures are unfilled, and advises rebuilding
11+
in the same cabal project so cabal can fill them.

0 commit comments

Comments
 (0)