From 238286f076bcc9f5a067278ca4aa838f670a2773 Mon Sep 17 00:00:00 2001 From: Kristen Kozak Date: Sat, 19 May 2018 21:09:09 -0700 Subject: [PATCH] Solver: Check whether packages are buildable in the current environment. This commit handles the most common case of issue #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. --- .../Solver/Modular/IndexConversion.hs | 88 ++++++++++++++++++- .../Distribution/Solver/Modular/Message.hs | 1 + .../Distribution/Solver/Modular/Tree.hs | 1 + .../PackageTests/NewBuild/T3978/cabal.out | 9 +- 4 files changed, 93 insertions(+), 6 deletions(-) diff --git a/cabal-install/Distribution/Solver/Modular/IndexConversion.hs b/cabal-install/Distribution/Solver/Modular/IndexConversion.hs index 4a0f3f4edd1..75610dd4f81 100644 --- a/cabal-install/Distribution/Solver/Modular/IndexConversion.hs +++ b/cabal-install/Distribution/Solver/Modular/IndexConversion.hs @@ -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 + -> (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 diff --git a/cabal-install/Distribution/Solver/Modular/Message.hs b/cabal-install/Distribution/Solver/Modular/Message.hs index 494d6897970..2278aa8b5a2 100644 --- a/cabal-install/Distribution/Solver/Modular/Message.hs +++ b/cabal-install/Distribution/Solver/Modular/Message.hs @@ -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)" diff --git a/cabal-install/Distribution/Solver/Modular/Tree.hs b/cabal-install/Distribution/Solver/Modular/Tree.hs index b7c03541a0c..f82765a2780 100644 --- a/cabal-install/Distribution/Solver/Modular/Tree.hs +++ b/cabal-install/Distribution/Solver/Modular/Tree.hs @@ -106,6 +106,7 @@ data FailReason = UnsupportedExtension Extension | CannotReinstall | Shadowed | Broken + | NotBuildable | GlobalConstraintVersion VR ConstraintSource | GlobalConstraintInstalled ConstraintSource | GlobalConstraintSource ConstraintSource diff --git a/cabal-testsuite/PackageTests/NewBuild/T3978/cabal.out b/cabal-testsuite/PackageTests/NewBuild/T3978/cabal.out index 5aa2db6bdb7..0a85b37dae1 100644 --- a/cabal-testsuite/PackageTests/NewBuild/T3978/cabal.out +++ b/cabal-testsuite/PackageTests/NewBuild/T3978/cabal.out @@ -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)