Skip to content

Commit

Permalink
Solver: Check whether packages are buildable in the current environment.
Browse files Browse the repository at this point in the history
This commit handles the most common case of issue haskell#5325 by checking that each
package is buildable in the current environment.  The solver replaces
non-buildable packages with failure nodes.  It allows two types of buildable
packages:

Buildable non-test package:
The package has at least one buildable library or executable.

Buildable test package:
The package has no libraries or executables, but it does have at least one
buildable test or benchmark.

The buildable check can give false-positives, because it ignores all flags, and
it doesn't check whether the intra-package dependencies of a component are
buildable.  The check is also incomplete because it is performed before any
flags are assigned.  It is possible for the solver to later choose a value for a
flag that makes the package unbuildable.
  • Loading branch information
grayjay committed May 20, 2018
1 parent cbdb135 commit 238286f
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 6 deletions.
88 changes: 86 additions & 2 deletions cabal-install/Distribution/Solver/Modular/IndexConversion.hs
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,92 @@ convGPD os arch cinfo strfl solveExes pn
-- forced to, emit a meaningful solver error message).
fr | reqSpecVer > maxSpecVer = Just (UnsupportedSpecVer reqSpecVer)
| otherwise = Nothing
in
PInfo flagged_deps (L.map (ExposedExe . fst) exes ++ [ExposedLib | isJust mlib]) fds fr

-- | Returns true if the package fits either description, in the current
-- environment:
--
-- Buildable non-test package: It has at least one buildable library or exe.
-- Buildable test package: It has no libraries or exes, but it does have at
-- least one buildable test or benchmark.
--
-- Checking whether components may be buildable in the current environment
-- handles the most common case of issue #5325. A more thorough solution
-- would involve tracking which components need to be built and then
-- checking that those components are buildable with the package's flag
-- assignment, as in issue #4087.
isBuildablePackage :: Bool
isBuildablePackage =
or libExeBuildableValues
|| L.null libExeBuildableValues && or testBenchBuildableValues
where
isBuildable = isBuildableComponent os arch cinfo

libExeBuildableValues =
L.map (isBuildable libBuildInfo) (maybeToList mlib)
++ L.map (isBuildable libBuildInfo . snd) sub_libs
++ L.map (isBuildable foreignLibBuildInfo . snd) flibs
++ L.map (isBuildable buildInfo . snd) exes

testBenchBuildableValues =
L.map (isBuildable testBuildInfo . snd) tests
++ L.map (isBuildable benchmarkBuildInfo . snd) benchs

in if isBuildablePackage
then PInfo flagged_deps (L.map (ExposedExe . fst) exes ++ [ExposedLib | isJust mlib]) fds fr
else PInfo [] [] M.empty (Just NotBuildable)

-- | Returns true if the component is buildable in the given environment.
-- This function can give false-positives. For example, it ignores all flags,
-- and it doesn't check whether the intra-package dependencies of a component
-- are buildable.
isBuildableComponent :: OS
-> Arch
-> CompilerInfo

This comment has been minimized.

Copy link
@phadej

phadej May 20, 2018

Can this take flag assigment map too? Would it then solve false-positives problem when buildable: False is hidden behind flag?

This comment has been minimized.

Copy link
@grayjay

grayjay May 21, 2018

Author Owner

This function is called when the package index is converted to the solver-specific format, before the solver has added goal qualifiers or chosen flag values, so the complete flag assignment isn't known. I added a parameter for flag constraints in my PR, though, since those flags can't change.

-> (a -> BuildInfo)
-> CondTree ConfVar [Dependency] a
-> Bool
isBuildableComponent os arch cinfo getInfo tree =
case simplifyCondition $ extractCondition (buildable . getInfo) tree of
Lit False -> False
_ -> True
where
-- Simplify the condition, using the current environment. Most of this
-- function was copied from convBranch and
-- Distribution.Types.Condition.simplifyCondition.
simplifyCondition :: Condition ConfVar -> Condition ConfVar
simplifyCondition (Var (OS os')) = Lit (os == os')
simplifyCondition (Var (Arch arch')) = Lit (arch == arch')
simplifyCondition (Var (Impl cf cvr))
| matchImpl (compilerInfoId cinfo) ||
-- fixme: Nothing should be treated as unknown, rather than empty
-- list. This code should eventually be changed to either
-- support partial resolution of compiler flags or to
-- complain about incompletely configured compilers.
any matchImpl (fromMaybe [] $ compilerInfoCompat cinfo) = Lit True
| otherwise = Lit False
where
matchImpl (CompilerId cf' cv) = cf == cf' && checkVR cvr cv
simplifyCondition (Var v) = Var v
simplifyCondition (Lit b) = Lit b
simplifyCondition (CNot c) =
case simplifyCondition c of
Lit True -> Lit False
Lit False -> Lit True
c' -> CNot c'
simplifyCondition (COr c d) =
case (simplifyCondition c, simplifyCondition d) of
(Lit False, d') -> d'
(Lit True, _) -> Lit True
(c', Lit False) -> c'
(_, Lit True) -> Lit True
(c', d') -> COr c' d'
simplifyCondition (CAnd c d) =
case (simplifyCondition c, simplifyCondition d) of
(Lit False, _) -> Lit False
(Lit True, d') -> d'
(_, Lit False) -> Lit False
(c', Lit True) -> c'
(c', d') -> CAnd c' d'

-- | Create a flagged dependency tree from a list @fds@ of flagged
-- dependencies, using @f@ to form the tree node (@f@ will be
Expand Down
1 change: 1 addition & 0 deletions cabal-install/Distribution/Solver/Modular/Message.hs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ showFR _ CannotInstall = " (only already installed instances
showFR _ CannotReinstall = " (avoiding to reinstall a package with same version but new dependencies)"
showFR _ Shadowed = " (shadowed by another installed package with same version)"
showFR _ Broken = " (package is broken)"
showFR _ NotBuildable = " (not buildable in the current environment)"
showFR _ (GlobalConstraintVersion vr src) = " (" ++ constraintSource src ++ " requires " ++ display vr ++ ")"
showFR _ (GlobalConstraintInstalled src) = " (" ++ constraintSource src ++ " requires installed instance)"
showFR _ (GlobalConstraintSource src) = " (" ++ constraintSource src ++ " requires source instance)"
Expand Down
1 change: 1 addition & 0 deletions cabal-install/Distribution/Solver/Modular/Tree.hs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ data FailReason = UnsupportedExtension Extension
| CannotReinstall
| Shadowed
| Broken
| NotBuildable
| GlobalConstraintVersion VR ConstraintSource
| GlobalConstraintInstalled ConstraintSource
| GlobalConstraintSource ConstraintSource
Expand Down
9 changes: 5 additions & 4 deletions cabal-testsuite/PackageTests/NewBuild/T3978/cabal.out
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# cabal new-build
Resolving dependencies...
Error:
Dependency on unbuildable library from p
In the stanza 'library'
In the inplace package 'q-1.0'
cabal: Could not resolve dependencies:
[__0] next goal: p (user goal)
[__0] rejecting: p-1.0 (not buildable in the current environment)
[__0] fail (backjumping, conflict set: p)
After searching the rest of the dependency tree exhaustively, these were the goals I've had most trouble fulfilling: p (2)

0 comments on commit 238286f

Please sign in to comment.