diff --git a/lib/spack/spack/cmd/solve.py b/lib/spack/spack/cmd/solve.py index 3d6968d2d9..165ede0dbc 100644 --- a/lib/spack/spack/cmd/solve.py +++ b/lib/spack/spack/cmd/solve.py @@ -91,7 +91,6 @@ def setup_parser(subparser): def _process_result(result, show, required_format, kwargs): - result.raise_if_unsat() opt, _, _ = min(result.answers) if ("opt" in show) and (not required_format): tty.msg("Best of %d considered solutions." % result.nmodels) diff --git a/lib/spack/spack/concretize.py b/lib/spack/spack/concretize.py index 86e1eb9735..e1f1170b91 100644 --- a/lib/spack/spack/concretize.py +++ b/lib/spack/spack/concretize.py @@ -749,7 +749,6 @@ def _concretize_specs_together_new(*abstract_specs, **kwargs): result = solver.solve( abstract_specs, tests=kwargs.get("tests", False), allow_deprecated=allow_deprecated ) - result.raise_if_unsat() return [s.copy() for s in result.specs] diff --git a/lib/spack/spack/solver/asp.py b/lib/spack/spack/solver/asp.py index ea2c9ddcfe..5928886bdb 100644 --- a/lib/spack/spack/solver/asp.py +++ b/lib/spack/spack/solver/asp.py @@ -763,7 +763,6 @@ def solve(self, setup, specs, reuse=None, output=None, control=None, allow_depre timer.stop("ground") # With a grounded program, we can run the solve. - result = Result(specs) models = [] # stable models if things go well cores = [] # unsatisfiable cores if they do not @@ -784,6 +783,7 @@ def on_model(model): timer.stop("solve") # once done, construct the solve result + result = Result(specs) result.satisfiable = solve_result.satisfiable if result.satisfiable: @@ -824,6 +824,8 @@ def on_model(model): print("Statistics:") pprint.pprint(self.control.statistics) + result.raise_if_unsat() + if result.satisfiable and result.unsolved_specs and setup.concretize_everything: unsolved_str = Result.format_unsolved(result.unsolved_specs) raise InternalConcretizerError( @@ -3535,9 +3537,14 @@ def solve_in_rounds( if not result.unsolved_specs: break - # This means we cannot progress with solving the input - if not result.satisfiable or not result.specs: - break + if not result.specs: + # This is also a problem: no specs were solved for, which + # means we would be in a loop if we tried again + unsolved_str = Result.format_unsolved(result.unsolved_specs) + raise InternalConcretizerError( + "Internal Spack error: a subset of input specs could not" + f" be solved for.\n\t{unsolved_str}" + ) input_specs = list(x for (x, y) in result.unsolved_specs) for spec in result.specs: diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py index ff3248321a..d63d52a6b1 100644 --- a/lib/spack/spack/spec.py +++ b/lib/spack/spack/spec.py @@ -2968,7 +2968,6 @@ def _new_concretize(self, tests=False): allow_deprecated = spack.config.get("config:deprecated", False) solver = spack.solver.asp.Solver() result = solver.solve([self], tests=tests, allow_deprecated=allow_deprecated) - result.raise_if_unsat() # take the best answer opt, i, answer = min(result.answers) diff --git a/lib/spack/spack/test/concretize.py b/lib/spack/spack/test/concretize.py index f2a104afbb..cd1b189961 100644 --- a/lib/spack/spack/test/concretize.py +++ b/lib/spack/spack/test/concretize.py @@ -1794,6 +1794,22 @@ def test_best_effort_coconcretize_preferences(self, specs, expected_spec, occura counter += 1 assert counter == occurances, concrete_specs + @pytest.mark.only_clingo("Original concretizer cannot concretize in rounds") + def test_solve_in_rounds_all_unsolved(self, monkeypatch, mock_packages, config): + specs = [Spec(x) for x in ["libdwarf%gcc", "libdwarf%clang"]] + solver = spack.solver.asp.Solver() + solver.reuse = False + + simulate_unsolved_property = list((x, None) for x in specs) + monkeypatch.setattr(spack.solver.asp.Result, "unsolved_specs", simulate_unsolved_property) + monkeypatch.setattr(spack.solver.asp.Result, "specs", list()) + + with pytest.raises( + spack.solver.asp.InternalConcretizerError, + match="a subset of input specs could not be solved for", + ): + list(solver.solve_in_rounds(specs)) + @pytest.mark.only_clingo("Use case not supported by the original concretizer") def test_coconcretize_reuse_and_virtuals(self): reusable_specs = []