summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/spack/docs/conf.py1
-rw-r--r--lib/spack/spack/cmd/solve.py16
-rw-r--r--lib/spack/spack/solver/asp.py134
-rw-r--r--lib/spack/spack/test/concretize.py4
-rwxr-xr-xshare/spack/spack-completion.bash2
5 files changed, 79 insertions, 78 deletions
diff --git a/lib/spack/docs/conf.py b/lib/spack/docs/conf.py
index c29951154f..e139ac7774 100644
--- a/lib/spack/docs/conf.py
+++ b/lib/spack/docs/conf.py
@@ -200,6 +200,7 @@ nitpick_ignore = [
("py:class", "_io.BufferedReader"),
("py:class", "unittest.case.TestCase"),
("py:class", "_frozen_importlib_external.SourceFileLoader"),
+ ("py:class", "clingo.Control"),
# Spack classes that are private and we don't want to expose
("py:class", "spack.provider_index._IndexBase"),
("py:class", "spack.repo._PrependFileLoader"),
diff --git a/lib/spack/spack/cmd/solve.py b/lib/spack/spack/cmd/solve.py
index 48f0226022..fddb3cc4e2 100644
--- a/lib/spack/spack/cmd/solve.py
+++ b/lib/spack/spack/cmd/solve.py
@@ -42,13 +42,6 @@ def setup_parser(subparser):
" solutions models found by asp program\n"
" all all of the above",
)
- subparser.add_argument(
- "--models",
- action="store",
- type=int,
- default=0,
- help="number of solutions to search (default 0 for all)",
- )
# Below are arguments w.r.t. spec display (like spack spec)
arguments.add_common_arguments(subparser, ["long", "very_long", "install_status"])
@@ -170,10 +163,6 @@ def solve(parser, args):
% (d, ", ".join(show_options + ("all",)))
)
- models = args.models
- if models < 0:
- tty.die("model count must be non-negative: %d")
-
# Format required for the output (JSON, YAML or None)
required_format = args.format
@@ -195,7 +184,6 @@ def solve(parser, args):
result = solver.solve(
specs,
out=output,
- models=models,
timers=args.timers,
stats=args.stats,
setup_only=setup_only,
@@ -204,9 +192,7 @@ def solve(parser, args):
_process_result(result, show, required_format, kwargs)
else:
for idx, result in enumerate(
- solver.solve_in_rounds(
- specs, out=output, models=models, timers=args.timers, stats=args.stats
- )
+ solver.solve_in_rounds(specs, out=output, timers=args.timers, stats=args.stats)
):
if "solutions" in show:
tty.msg("ROUND {0}".format(idx))
diff --git a/lib/spack/spack/solver/asp.py b/lib/spack/spack/solver/asp.py
index a50b10133f..43a3d341f0 100644
--- a/lib/spack/spack/solver/asp.py
+++ b/lib/spack/spack/solver/asp.py
@@ -56,6 +56,35 @@ ASTType = None
parse_files = None
+#: Data class that contain configuration on what a
+#: clingo solve should output.
+#:
+#: Args:
+#: timers (bool): Print out coarse timers for different solve phases.
+#: stats (bool): Whether to output Clingo's internal solver statistics.
+#: out: Optional output stream for the generated ASP program.
+#: setup_only (bool): if True, stop after setup and don't solve (default False).
+OutputConfiguration = collections.namedtuple(
+ "OutputConfiguration", ["timers", "stats", "out", "setup_only"]
+)
+
+#: Default output configuration for a solve
+DEFAULT_OUTPUT_CONFIGURATION = OutputConfiguration(
+ timers=False, stats=False, out=None, setup_only=False
+)
+
+
+def default_clingo_control():
+ """Return a control object with the default settings used in Spack"""
+ control = clingo.Control()
+ control.configuration.configuration = "tweety"
+ control.configuration.solve.models = 0
+ control.configuration.solver.heuristic = "Domain"
+ control.configuration.solve.parallel_mode = "1"
+ control.configuration.solver.opt_strategy = "usc,one"
+ return control
+
+
# backward compatibility functions for clingo ASTs
def ast_getter(*names):
def getter(node):
@@ -520,6 +549,12 @@ class PyclingoDriver(object):
self.out = llnl.util.lang.Devnull()
self.cores = cores
+ # These attributes are part of the object, but will be reset
+ # at each call to solve
+ self.control = None
+ self.backend = None
+ self.assumptions = None
+
def title(self, name, char):
self.out.write("\n")
self.out.write("%" + (char * 76))
@@ -556,43 +591,31 @@ class PyclingoDriver(object):
if choice:
self.assumptions.append(atom)
- def solve(
- self,
- setup,
- specs,
- nmodels=0,
- reuse=None,
- timers=False,
- stats=False,
- out=None,
- setup_only=False,
- ):
+ def solve(self, setup, specs, reuse=None, output=None, control=None):
"""Set up the input and solve for dependencies of ``specs``.
Arguments:
- setup (SpackSolverSetup): An object to set up the ASP problem.
- specs (list): List of ``Spec`` objects to solve for.
- nmodels (int): Number of models to consider (default 0 for unlimited).
- reuse (None or list): list of concrete specs that can be reused
- timers (bool): Print out coarse timers for different solve phases.
- stats (bool): Whether to output Clingo's internal solver statistics.
- out: Optional output stream for the generated ASP program.
- setup_only (bool): if True, stop after setup and don't solve (default False).
+ setup (SpackSolverSetup): An object to set up the ASP problem.
+ specs (list): List of ``Spec`` objects to solve for.
+ reuse (None or list): list of concrete specs that can be reused
+ output (None or OutputConfiguration): configuration object to set
+ the output of this solve.
+ control (clingo.Control): configuration for the solver. If None,
+ default values will be used
+
+ Return:
+ A tuple of the solve result, the timer for the different phases of the
+ solve, and the internal statistics from clingo.
"""
+ output = output or DEFAULT_OUTPUT_CONFIGURATION
# allow solve method to override the output stream
- if out is not None:
- self.out = out
+ if output.out is not None:
+ self.out = output.out
timer = spack.util.timer.Timer()
# Initialize the control object for the solver
- self.control = clingo.Control()
- self.control.configuration.configuration = "tweety"
- self.control.configuration.solve.models = nmodels
- self.control.configuration.solver.heuristic = "Domain"
- self.control.configuration.solve.parallel_mode = "1"
- self.control.configuration.solver.opt_strategy = "usc,one"
-
+ self.control = control or default_clingo_control()
# set up the problem -- this generates facts and rules
self.assumptions = []
with self.control.backend() as backend:
@@ -622,8 +645,8 @@ class PyclingoDriver(object):
parse_files([path], visit)
# If we're only doing setup, just return an empty solve result
- if setup_only:
- return Result(specs)
+ if output.setup_only:
+ return Result(specs), None, None
# Load the file itself
self.control.load(os.path.join(parent_dir, "concretize.lp"))
@@ -682,18 +705,21 @@ class PyclingoDriver(object):
# record the number of models the solver considered
result.nmodels = len(models)
+ # record the possible dependencies in the solve
+ result.possible_dependencies = setup.pkgs
+
elif cores:
result.control = self.control
result.cores.extend(cores)
- if timers:
+ if output.timers:
timer.write_tty()
print()
- if stats:
+ if output.stats:
print("Statistics:")
pprint.pprint(self.control.statistics)
- return result
+ return result, timer, self.control.statistics
class SpackSolverSetup(object):
@@ -732,6 +758,9 @@ class SpackSolverSetup(object):
# If False allows for input specs that are not solved
self.concretize_everything = True
+ # Set during the call to setup
+ self.pkgs = None
+
def pkg_version_rules(self, pkg):
"""Output declared versions of a package.
@@ -964,7 +993,8 @@ class SpackSolverSetup(object):
# virtual preferences
self.virtual_preferences(
- pkg.name, lambda v, p, i: self.gen.fact(fn.pkg_provider_preference(pkg.name, v, p, i))
+ pkg.name,
+ lambda v, p, i: self.gen.fact(fn.pkg_provider_preference(pkg.name, v, p, i)),
)
def condition(self, required_spec, imposed_spec=None, name=None, msg=None):
@@ -1063,7 +1093,8 @@ class SpackSolverSetup(object):
self.gen.h2("Default virtual providers")
assert self.possible_virtuals is not None
self.virtual_preferences(
- "all", lambda v, p, i: self.gen.fact(fn.default_provider_preference(v, p, i))
+ "all",
+ lambda v, p, i: self.gen.fact(fn.default_provider_preference(v, p, i)),
)
def external_packages(self):
@@ -1798,7 +1829,7 @@ class SpackSolverSetup(object):
if missing_deps:
raise spack.spec.InvalidDependencyError(spec.name, missing_deps)
- pkgs = set(possible)
+ self.pkgs = set(possible)
# driver is used by all the functions below to add facts and
# rules to generate an ASP program.
@@ -1835,7 +1866,7 @@ class SpackSolverSetup(object):
self.flag_defaults()
self.gen.h1("Package Constraints")
- for pkg in sorted(pkgs):
+ for pkg in sorted(self.pkgs):
self.gen.h2("Package rules: %s" % pkg)
self.pkg_rules(pkg, tests=self.tests)
self.gen.h2("Package preferences: %s" % pkg)
@@ -2215,7 +2246,6 @@ class Solver(object):
self,
specs,
out=None,
- models=0,
timers=False,
stats=False,
tests=False,
@@ -2225,7 +2255,6 @@ class Solver(object):
Arguments:
specs (list): List of ``Spec`` objects to solve for.
out: Optionally write the generate ASP program to a file-like object.
- models (int): Number of models to search (default: 0 for unlimited).
timers (bool): Print out coarse fimers for different solve phases.
stats (bool): Print out detailed stats from clingo.
tests (bool or tuple): If True, concretize test dependencies for all packages.
@@ -2237,22 +2266,14 @@ class Solver(object):
reusable_specs = self._check_input_and_extract_concrete_specs(specs)
reusable_specs.extend(self._reusable_specs())
setup = SpackSolverSetup(tests=tests)
- return self.driver.solve(
- setup,
- specs,
- nmodels=models,
- reuse=reusable_specs,
- timers=timers,
- stats=stats,
- out=out,
- setup_only=setup_only,
- )
+ output = OutputConfiguration(timers=timers, stats=stats, out=out, setup_only=setup_only)
+ result, _, _ = self.driver.solve(setup, specs, reuse=reusable_specs, output=output)
+ return result
def solve_in_rounds(
self,
specs,
out=None,
- models=0,
timers=False,
stats=False,
tests=False,
@@ -2267,7 +2288,6 @@ class Solver(object):
Arguments:
specs (list): list of Specs to solve.
- models (int): number of models to search (default: 0)
out: Optionally write the generate ASP program to a file-like object.
timers (bool): print timing if set to True
stats (bool): print internal statistics if set to True
@@ -2281,16 +2301,10 @@ class Solver(object):
setup.concretize_everything = False
input_specs = specs
+ output = OutputConfiguration(timers=timers, stats=stats, out=out, setup_only=False)
while True:
- result = self.driver.solve(
- setup,
- input_specs,
- nmodels=models,
- reuse=reusable_specs,
- timers=timers,
- stats=stats,
- out=out,
- setup_only=False,
+ result, _, _ = self.driver.solve(
+ setup, input_specs, reuse=reusable_specs, output=output
)
yield result
diff --git a/lib/spack/spack/test/concretize.py b/lib/spack/spack/test/concretize.py
index d4f417239f..1560a085dd 100644
--- a/lib/spack/spack/test/concretize.py
+++ b/lib/spack/spack/test/concretize.py
@@ -1700,7 +1700,7 @@ class TestConcretize(object):
with spack.config.override("concretizer:reuse", True):
solver = spack.solver.asp.Solver()
setup = spack.solver.asp.SpackSolverSetup()
- result = solver.driver.solve(setup, [root_spec], reuse=reusable_specs, out=sys.stdout)
+ result, _, _ = solver.driver.solve(setup, [root_spec], reuse=reusable_specs)
# The result here should have a single spec to build ('a')
# and it should be using b@1.0 with a version badness of 2
# The provenance is:
@@ -1731,7 +1731,7 @@ class TestConcretize(object):
with spack.config.override("concretizer:reuse", True):
solver = spack.solver.asp.Solver()
setup = spack.solver.asp.SpackSolverSetup()
- result = solver.driver.solve(setup, [root_spec], reuse=reusable_specs, out=sys.stdout)
+ result, _, _ = solver.driver.solve(setup, [root_spec], reuse=reusable_specs)
concrete_spec = result.specs[0]
assert concrete_spec.satisfies("%gcc@4.5.0")
assert concrete_spec.satisfies("os=debian6")
diff --git a/share/spack/spack-completion.bash b/share/spack/spack-completion.bash
index 14d981936d..bd41619a09 100755
--- a/share/spack/spack-completion.bash
+++ b/share/spack/spack-completion.bash
@@ -1670,7 +1670,7 @@ _spack_restage() {
_spack_solve() {
if $list_options
then
- SPACK_COMPREPLY="-h --help --show --models -l --long -L --very-long -I --install-status -y --yaml -j --json -c --cover -N --namespaces -t --types --timers --stats -U --fresh --reuse"
+ SPACK_COMPREPLY="-h --help --show -l --long -L --very-long -I --install-status -y --yaml -j --json -c --cover -N --namespaces -t --types --timers --stats -U --fresh --reuse"
else
_all_packages
fi