concretizer: first rudimentary round-trip with asp-based solver
This commit is contained in:
parent
c7812f7e10
commit
81e187e410
@ -6,6 +6,10 @@
|
|||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import llnl.util.tty as tty
|
||||||
|
|
||||||
import spack
|
import spack
|
||||||
import spack.cmd
|
import spack.cmd
|
||||||
@ -17,9 +21,23 @@
|
|||||||
section = 'developer'
|
section = 'developer'
|
||||||
level = 'long'
|
level = 'long'
|
||||||
|
|
||||||
|
#: output options
|
||||||
|
dump_options = ('asp', 'warnings', 'output', 'solutions')
|
||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
arguments.add_common_arguments(subparser, ['long', 'very_long'])
|
# Solver arguments
|
||||||
|
subparser.add_argument(
|
||||||
|
'--dump', action='store', default=('solutions'),
|
||||||
|
help="outputs: a list with any of: "
|
||||||
|
"%s (default), all" % ', '.join(dump_options))
|
||||||
|
subparser.add_argument(
|
||||||
|
'--models', action='store', type=int, default=1,
|
||||||
|
help="number of solutions to display (0 for all)")
|
||||||
|
|
||||||
|
# Below are arguments w.r.t. spec display (like spack spec)
|
||||||
|
arguments.add_common_arguments(
|
||||||
|
subparser, ['long', 'very_long', 'install_status'])
|
||||||
subparser.add_argument(
|
subparser.add_argument(
|
||||||
'-y', '--yaml', action='store_true', default=False,
|
'-y', '--yaml', action='store_true', default=False,
|
||||||
help='print concrete spec as YAML')
|
help='print concrete spec as YAML')
|
||||||
@ -30,11 +48,6 @@ def setup_parser(subparser):
|
|||||||
subparser.add_argument(
|
subparser.add_argument(
|
||||||
'-N', '--namespaces', action='store_true', default=False,
|
'-N', '--namespaces', action='store_true', default=False,
|
||||||
help='show fully qualified package names')
|
help='show fully qualified package names')
|
||||||
subparser.add_argument(
|
|
||||||
'-I', '--install-status', action='store_true', default=False,
|
|
||||||
help='show install status of packages. packages can be: '
|
|
||||||
'installed [+], missing and needed by an installed package [-], '
|
|
||||||
'or not installed (no annotation)')
|
|
||||||
subparser.add_argument(
|
subparser.add_argument(
|
||||||
'-t', '--types', action='store_true', default=False,
|
'-t', '--types', action='store_true', default=False,
|
||||||
help='show dependency types')
|
help='show dependency types')
|
||||||
@ -43,5 +56,70 @@ def setup_parser(subparser):
|
|||||||
|
|
||||||
|
|
||||||
def solve(parser, args):
|
def solve(parser, args):
|
||||||
|
# these are the same options as `spack spec`
|
||||||
|
name_fmt = '{namespace}.{name}' if args.namespaces else '{name}'
|
||||||
|
fmt = '{@version}{%compiler}{compiler_flags}{variants}{arch=architecture}'
|
||||||
|
install_status_fn = spack.spec.Spec.install_status
|
||||||
|
kwargs = {
|
||||||
|
'cover': args.cover,
|
||||||
|
'format': name_fmt + fmt,
|
||||||
|
'hashlen': None if args.very_long else 7,
|
||||||
|
'show_types': args.types,
|
||||||
|
'status_fn': install_status_fn if args.install_status else None,
|
||||||
|
'hashes': args.long or args.very_long
|
||||||
|
}
|
||||||
|
|
||||||
|
# process dump options
|
||||||
|
dump = re.split(r'\s*,\s*', args.dump)
|
||||||
|
if 'all' in dump:
|
||||||
|
dump = dump_options
|
||||||
|
for d in dump:
|
||||||
|
if d not in dump_options:
|
||||||
|
raise ValueError(
|
||||||
|
"Invalid dump option: '%s'\nchoose from: (%s)"
|
||||||
|
% (d, ', '.join(dump_options + ('all',))))
|
||||||
|
|
||||||
|
models = args.models
|
||||||
|
if models < 0:
|
||||||
|
tty.die("model count must be non-negative: %d")
|
||||||
|
|
||||||
specs = spack.cmd.parse_specs(args.specs)
|
specs = spack.cmd.parse_specs(args.specs)
|
||||||
asp.solve(specs)
|
|
||||||
|
# dump generated ASP program
|
||||||
|
result = asp.solve(specs, dump=dump, models=models)
|
||||||
|
|
||||||
|
# die if no solution was found
|
||||||
|
# TODO: we need to be able to provide better error messages than this
|
||||||
|
if not result.satisfiable:
|
||||||
|
tty.die("Unsatisfiable spec.")
|
||||||
|
|
||||||
|
# dump generated ASP program
|
||||||
|
if 'asp' in dump:
|
||||||
|
tty.msg('ASP program:')
|
||||||
|
sys.stdout.write(result.asp)
|
||||||
|
|
||||||
|
# dump any warnings generated by the solver
|
||||||
|
if 'warnings' in dump:
|
||||||
|
if result.warnings:
|
||||||
|
tty.msg('Clingo gave the following warnings:')
|
||||||
|
sys.stdout.write(result.warnings)
|
||||||
|
else:
|
||||||
|
tty.msg('No warnings.')
|
||||||
|
|
||||||
|
# dump the raw output of the solver
|
||||||
|
if 'output' in dump:
|
||||||
|
tty.msg('Clingo output:')
|
||||||
|
sys.stdout.write(result.output)
|
||||||
|
|
||||||
|
# dump the solutions as concretized specs
|
||||||
|
if 'solutions' in dump:
|
||||||
|
for i, answer in enumerate(result.answers):
|
||||||
|
tty.msg("Answer %d" % (i + 1))
|
||||||
|
for spec in specs:
|
||||||
|
answer_spec = answer[spec.name]
|
||||||
|
if args.yaml:
|
||||||
|
out = answer_spec.to_yaml()
|
||||||
|
else:
|
||||||
|
out = answer_spec.tree(
|
||||||
|
color=sys.stdout.isatty(), **kwargs)
|
||||||
|
sys.stdout.write(out)
|
||||||
|
@ -42,7 +42,6 @@ def setup_parser(subparser):
|
|||||||
subparser.add_argument(
|
subparser.add_argument(
|
||||||
'-N', '--namespaces', action='store_true', default=False,
|
'-N', '--namespaces', action='store_true', default=False,
|
||||||
help='show fully qualified package names')
|
help='show fully qualified package names')
|
||||||
|
|
||||||
subparser.add_argument(
|
subparser.add_argument(
|
||||||
'-t', '--types', action='store_true', default=False,
|
'-t', '--types', action='store_true', default=False,
|
||||||
help='show dependency types')
|
help='show dependency types')
|
||||||
|
@ -6,25 +6,63 @@
|
|||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
|
import re
|
||||||
|
import tempfile
|
||||||
import types
|
import types
|
||||||
|
from six import string_types
|
||||||
|
|
||||||
|
import llnl.util.tty as tty
|
||||||
|
|
||||||
import spack
|
import spack
|
||||||
import spack.cmd
|
import spack.cmd
|
||||||
import spack.spec
|
import spack.spec
|
||||||
import spack.package
|
import spack.package
|
||||||
|
import spack.repo
|
||||||
|
from spack.util.executable import which
|
||||||
|
from spack.version import ver
|
||||||
|
|
||||||
|
|
||||||
def title(name):
|
#: generate the problem space, establish cardinality constraints
|
||||||
print()
|
_generate = """\
|
||||||
print("%% %s" % name)
|
% One version, arch, etc. per package
|
||||||
print("% -----------------------------------------")
|
{ version(P, V) : version(P, V) } = 1 :- node(P).
|
||||||
|
{ arch_platform(P, A) : arch_platform(P, A) } = 1 :- node(P).
|
||||||
|
{ arch_os(P, A) : arch_os(P, A) } = 1 :- node(P).
|
||||||
|
{ arch_target(P, T) : arch_target(P, T) } = 1 :- node(P).
|
||||||
|
|
||||||
|
% one variant value for single-valued variants.
|
||||||
|
{ variant_value(P, V, X) : variant_value(P, V, X) } = 1
|
||||||
|
:- node(P), variant(P, V), not variant_single_value(P, V).
|
||||||
|
"""
|
||||||
|
|
||||||
def section(name):
|
#: define the rules of Spack concretization
|
||||||
print()
|
_define = """\
|
||||||
print("%")
|
% dependencies imply new nodes.
|
||||||
print("%% %s" % name)
|
node(D) :- node(P), depends_on(P, D).
|
||||||
print("%")
|
|
||||||
|
% propagate platform, os, target downwards
|
||||||
|
arch_platform(D, A) :- node(D), depends_on(P, D), arch_platform(P, A).
|
||||||
|
arch_os(D, A) :- node(D), depends_on(P, D), arch_os(P, A).
|
||||||
|
arch_target(D, A) :- node(D), depends_on(P, D), arch_target(P, A).
|
||||||
|
|
||||||
|
% if a variant is set to anything, it is considered "set".
|
||||||
|
variant_set(P, V) :- variant_set(P, V, _).
|
||||||
|
|
||||||
|
% variant_set is an explicitly set variant value. If it's not "set",
|
||||||
|
% we revert to the default value. If it is set, we force the set value
|
||||||
|
variant_value(P, V, X) :- node(P), variant(P, V), variant_set(P, V, X).
|
||||||
|
"""
|
||||||
|
|
||||||
|
#: what parts of the model to display to read models back in
|
||||||
|
_display = """\
|
||||||
|
#show node/1.
|
||||||
|
#show depends_on/2.
|
||||||
|
#show version/2.
|
||||||
|
#show variant_value/3.
|
||||||
|
#show arch_platform/2.
|
||||||
|
#show arch_os/2.
|
||||||
|
#show arch_target/2.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
def _id(thing):
|
def _id(thing):
|
||||||
@ -33,7 +71,7 @@ def _id(thing):
|
|||||||
|
|
||||||
|
|
||||||
def issequence(obj):
|
def issequence(obj):
|
||||||
if isinstance(obj, basestring):
|
if isinstance(obj, string_types):
|
||||||
return False
|
return False
|
||||||
return isinstance(obj, (collections.Sequence, types.GeneratorType))
|
return isinstance(obj, (collections.Sequence, types.GeneratorType))
|
||||||
|
|
||||||
@ -97,7 +135,30 @@ def __str__(self):
|
|||||||
return "not %s" % self.arg
|
return "not %s" % self.arg
|
||||||
|
|
||||||
|
|
||||||
class AspBuilder(object):
|
class AspFunctionBuilder(object):
|
||||||
|
def __getattr__(self, name):
|
||||||
|
return AspFunction(name)
|
||||||
|
|
||||||
|
|
||||||
|
fn = AspFunctionBuilder()
|
||||||
|
|
||||||
|
|
||||||
|
class AspGenerator(object):
|
||||||
|
def __init__(self, out):
|
||||||
|
self.out = out
|
||||||
|
self.func = AspFunctionBuilder()
|
||||||
|
|
||||||
|
def title(self, name):
|
||||||
|
self.out.write('\n')
|
||||||
|
self.out.write("%% %s\n" % name)
|
||||||
|
self.out.write("% -----------------------------------------\n")
|
||||||
|
|
||||||
|
def section(self, name):
|
||||||
|
self.out.write("\n")
|
||||||
|
self.out.write("%\n")
|
||||||
|
self.out.write("%% %s\n" % name)
|
||||||
|
self.out.write("%\n")
|
||||||
|
|
||||||
def _or(self, *args):
|
def _or(self, *args):
|
||||||
return AspOr(*args)
|
return AspOr(*args)
|
||||||
|
|
||||||
@ -107,192 +168,257 @@ def _and(self, *args):
|
|||||||
def _not(self, arg):
|
def _not(self, arg):
|
||||||
return AspNot(arg)
|
return AspNot(arg)
|
||||||
|
|
||||||
def _fact(self, head):
|
def fact(self, head):
|
||||||
"""ASP fact (a rule without a body)."""
|
"""ASP fact (a rule without a body)."""
|
||||||
print("%s." % head)
|
self.out.write("%s.\n" % head)
|
||||||
|
|
||||||
def _rule(self, head, body):
|
def rule(self, head, body):
|
||||||
"""ASP rule (an implication)."""
|
"""ASP rule (an implication)."""
|
||||||
print("%s :- %s." % (head, body))
|
self.out.write("%s :- %s.\n" % (head, body))
|
||||||
|
|
||||||
def _constraint(self, body):
|
def constraint(self, body):
|
||||||
"""ASP integrity constraint (rule with no head; can't be true)."""
|
"""ASP integrity constraint (rule with no head; can't be true)."""
|
||||||
print(":- %s." % body)
|
self.out.write(":- %s.\n" % body)
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def pkg_version_rules(self, pkg):
|
||||||
return AspFunction(name)
|
pkg = packagize(pkg)
|
||||||
|
self.rule(
|
||||||
|
self._or(fn.version(pkg.name, v) for v in pkg.versions),
|
||||||
|
fn.node(pkg.name))
|
||||||
|
|
||||||
|
def spec_versions(self, spec):
|
||||||
|
spec = specify(spec)
|
||||||
|
|
||||||
asp = AspBuilder()
|
if spec.concrete:
|
||||||
|
self.rule(fn.version(spec.name, spec.version),
|
||||||
|
fn.node(spec.name))
|
||||||
def pkg_version_rules(pkg):
|
|
||||||
pkg = packagize(pkg)
|
|
||||||
asp._rule(
|
|
||||||
asp._or(asp.version(pkg.name, v) for v in pkg.versions),
|
|
||||||
asp.node(pkg.name))
|
|
||||||
|
|
||||||
|
|
||||||
def spec_versions(spec):
|
|
||||||
spec = specify(spec)
|
|
||||||
|
|
||||||
if spec.concrete:
|
|
||||||
asp._rule(asp.version(spec.name, spec.version),
|
|
||||||
asp.node(spec.name))
|
|
||||||
else:
|
|
||||||
version = spec.versions
|
|
||||||
impossible, possible = [], []
|
|
||||||
for v in spec.package.versions:
|
|
||||||
if v.satisfies(version):
|
|
||||||
possible.append(v)
|
|
||||||
else:
|
|
||||||
impossible.append(v)
|
|
||||||
|
|
||||||
if impossible:
|
|
||||||
asp._rule(
|
|
||||||
asp._and(asp._not(asp.version(spec.name, v))
|
|
||||||
for v in impossible),
|
|
||||||
asp.node(spec.name))
|
|
||||||
if possible:
|
|
||||||
asp._rule(
|
|
||||||
asp._or(asp.version(spec.name, v) for v in possible),
|
|
||||||
asp.node(spec.name))
|
|
||||||
|
|
||||||
|
|
||||||
def pkg_rules(pkg):
|
|
||||||
pkg = packagize(pkg)
|
|
||||||
|
|
||||||
# versions
|
|
||||||
pkg_version_rules(pkg)
|
|
||||||
|
|
||||||
# variants
|
|
||||||
for name, variant in pkg.variants.items():
|
|
||||||
asp._rule(asp.variant(pkg.name, name),
|
|
||||||
asp.node(pkg.name))
|
|
||||||
|
|
||||||
single_value = not variant.multi
|
|
||||||
single = asp.variant_single_value(pkg.name, name)
|
|
||||||
if single_value:
|
|
||||||
asp._rule(single, asp.node(pkg.name))
|
|
||||||
asp._rule(asp.variant_value(pkg.name, name, variant.default),
|
|
||||||
asp._not(asp.variant_set(pkg.name, name)))
|
|
||||||
else:
|
else:
|
||||||
asp._rule(asp._not(single), asp.node(pkg.name))
|
version = spec.versions
|
||||||
defaults = variant.default.split(',')
|
impossible, possible = [], []
|
||||||
for val in defaults:
|
for v in spec.package.versions:
|
||||||
asp._rule(asp.variant_value(pkg.name, name, val),
|
if v.satisfies(version):
|
||||||
asp._not(asp.variant_set(pkg.name, name)))
|
possible.append(v)
|
||||||
|
else:
|
||||||
|
impossible.append(v)
|
||||||
|
|
||||||
# dependencies
|
if impossible:
|
||||||
for name, conditions in pkg.dependencies.items():
|
self.rule(
|
||||||
for cond, dep in conditions.items():
|
self._and(self._not(fn.version(spec.name, v))
|
||||||
asp._fact(asp.depends_on(dep.pkg.name, dep.spec.name))
|
for v in impossible),
|
||||||
|
fn.node(spec.name))
|
||||||
|
if possible:
|
||||||
|
self.rule(
|
||||||
|
self._or(fn.version(spec.name, v) for v in possible),
|
||||||
|
fn.node(spec.name))
|
||||||
|
|
||||||
|
def pkg_rules(self, pkg):
|
||||||
|
pkg = packagize(pkg)
|
||||||
|
|
||||||
|
# versions
|
||||||
|
self.pkg_version_rules(pkg)
|
||||||
|
|
||||||
|
# variants
|
||||||
|
for name, variant in pkg.variants.items():
|
||||||
|
self.rule(fn.variant(pkg.name, name),
|
||||||
|
fn.node(pkg.name))
|
||||||
|
|
||||||
|
single_value = not variant.multi
|
||||||
|
single = fn.variant_single_value(pkg.name, name)
|
||||||
|
if single_value:
|
||||||
|
self.rule(single, fn.node(pkg.name))
|
||||||
|
self.rule(fn.variant_value(pkg.name, name, variant.default),
|
||||||
|
self._not(fn.variant_set(pkg.name, name)))
|
||||||
|
else:
|
||||||
|
self.rule(self._not(single), fn.node(pkg.name))
|
||||||
|
defaults = variant.default.split(',')
|
||||||
|
for val in defaults:
|
||||||
|
self.rule(fn.variant_value(pkg.name, name, val),
|
||||||
|
self._not(fn.variant_set(pkg.name, name)))
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
for name, conditions in pkg.dependencies.items():
|
||||||
|
for cond, dep in conditions.items():
|
||||||
|
self.fact(fn.depends_on(dep.pkg.name, dep.spec.name))
|
||||||
|
|
||||||
|
def spec_rules(self, spec):
|
||||||
|
self.fact(fn.node(spec.name))
|
||||||
|
self.spec_versions(spec)
|
||||||
|
|
||||||
|
# seed architecture at the root (we'll propagate later)
|
||||||
|
# TODO: use better semantics.
|
||||||
|
arch = spack.spec.ArchSpec(spack.architecture.sys_type())
|
||||||
|
spec_arch = spec.architecture
|
||||||
|
if spec_arch:
|
||||||
|
if spec_arch.platform:
|
||||||
|
arch.platform = spec_arch.platform
|
||||||
|
if spec_arch.os:
|
||||||
|
arch.os = spec_arch.os
|
||||||
|
if spec_arch.target:
|
||||||
|
arch.target = spec_arch.target
|
||||||
|
self.fact(fn.arch_platform(spec.name, arch.platform))
|
||||||
|
self.fact(fn.arch_os(spec.name, arch.os))
|
||||||
|
self.fact(fn.arch_target(spec.name, arch.target))
|
||||||
|
|
||||||
|
# TODO
|
||||||
|
# dependencies
|
||||||
|
# compiler
|
||||||
|
# external_path
|
||||||
|
# external_module
|
||||||
|
# compiler_flags
|
||||||
|
# namespace
|
||||||
|
|
||||||
|
def generate_asp_program(self, specs):
|
||||||
|
"""Write an ASP program for specs.
|
||||||
|
|
||||||
|
Writes to this AspGenerator's output stream.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
specs (list): list of Specs to solve
|
||||||
|
"""
|
||||||
|
# get list of all possible dependencies
|
||||||
|
pkg_names = set(spec.fullname for spec in specs)
|
||||||
|
pkgs = [spack.repo.path.get_pkg_class(name) for name in pkg_names]
|
||||||
|
pkgs = list(set(spack.package.possible_dependencies(*pkgs))
|
||||||
|
| set(pkg_names))
|
||||||
|
|
||||||
|
self.title("Generate")
|
||||||
|
self.out.write(_generate)
|
||||||
|
|
||||||
|
self.title("Define")
|
||||||
|
self.out.write(_define)
|
||||||
|
|
||||||
|
self.title("Package Constraints")
|
||||||
|
for pkg in pkgs:
|
||||||
|
self.section(pkg)
|
||||||
|
self.pkg_rules(pkg)
|
||||||
|
|
||||||
|
self.title("Spec Constraints")
|
||||||
|
for spec in specs:
|
||||||
|
self.section(str(spec))
|
||||||
|
self.spec_rules(spec)
|
||||||
|
|
||||||
|
self.title("Display")
|
||||||
|
self.out.write(_display)
|
||||||
|
self.out.write('\n\n')
|
||||||
|
|
||||||
|
|
||||||
def spec_rules(spec):
|
class ResultParser(object):
|
||||||
asp._fact(asp.node(spec.name))
|
"""Class with actions that can re-parse a spec from ASP."""
|
||||||
spec_versions(spec)
|
def __init__(self):
|
||||||
|
self._result = None
|
||||||
|
|
||||||
# seed architecture at the root (we'll propagate later)
|
def node(self, pkg):
|
||||||
# TODO: use better semantics.
|
if pkg not in self._specs:
|
||||||
arch = spack.spec.ArchSpec(spack.architecture.sys_type())
|
self._specs[pkg] = spack.spec.Spec(pkg)
|
||||||
spec_arch = spec.architecture
|
|
||||||
if spec_arch:
|
def _arch(self, pkg):
|
||||||
if spec_arch.platform:
|
arch = self._specs[pkg].architecture
|
||||||
arch.platform = spec_arch.platform
|
if not arch:
|
||||||
if spec_arch.os:
|
arch = spack.spec.ArchSpec()
|
||||||
arch.os = spec_arch.os
|
self._specs[pkg].architecture = arch
|
||||||
if spec_arch.target:
|
return arch
|
||||||
arch.target = spec_arch.target
|
|
||||||
asp._fact(asp.arch_platform(spec.name, arch.platform))
|
def arch_platform(self, pkg, platform):
|
||||||
asp._fact(asp.arch_os(spec.name, arch.os))
|
self._arch(pkg).platform = platform
|
||||||
asp._fact(asp.arch_target(spec.name, arch.target))
|
|
||||||
|
def arch_os(self, pkg, os):
|
||||||
|
self._arch(pkg).os = os
|
||||||
|
|
||||||
|
def arch_target(self, pkg, target):
|
||||||
|
self._arch(pkg).target = target
|
||||||
|
|
||||||
|
def variant_value(self, pkg, name, value):
|
||||||
|
pkg_class = spack.repo.path.get_pkg_class(pkg)
|
||||||
|
variant = pkg_class.variants[name].make_variant(value)
|
||||||
|
self._specs[pkg].variants[name] = variant
|
||||||
|
|
||||||
|
def version(self, pkg, version):
|
||||||
|
self._specs[pkg].versions = ver([version])
|
||||||
|
|
||||||
|
def depends_on(self, pkg, dep):
|
||||||
|
self._specs[pkg]._add_dependency(
|
||||||
|
self._specs[dep], ('link', 'run'))
|
||||||
|
|
||||||
|
def parse(self, stream, result):
|
||||||
|
for line in stream:
|
||||||
|
match = re.match(r'SATISFIABLE', line)
|
||||||
|
if match:
|
||||||
|
result.satisfiable = True
|
||||||
|
continue
|
||||||
|
|
||||||
|
match = re.match(r'UNSATISFIABLE', line)
|
||||||
|
if match:
|
||||||
|
result.satisfiable = False
|
||||||
|
continue
|
||||||
|
|
||||||
|
match = re.match(r'Answer: (\d+)', line)
|
||||||
|
if not match:
|
||||||
|
continue
|
||||||
|
|
||||||
|
answer_number = int(match.group(1))
|
||||||
|
assert answer_number == len(result.answers) + 1
|
||||||
|
|
||||||
|
answer = next(stream)
|
||||||
|
tty.debug("Answer: %d" % answer_number, answer)
|
||||||
|
|
||||||
|
self._specs = {}
|
||||||
|
for m in re.finditer(r'(\w+)\(([^)]*)\)', answer):
|
||||||
|
name, arg_string = m.groups()
|
||||||
|
args = re.findall(r'"([^"]*)"', arg_string)
|
||||||
|
|
||||||
|
action = getattr(self, name)
|
||||||
|
assert action and callable(action)
|
||||||
|
action(*args)
|
||||||
|
|
||||||
|
result.answers.append(self._specs)
|
||||||
|
|
||||||
|
|
||||||
|
class Result(object):
|
||||||
|
def __init__(self, asp):
|
||||||
|
self.asp = asp
|
||||||
|
self.satisfiable = None
|
||||||
|
self.warnings = None
|
||||||
|
self.answers = []
|
||||||
|
|
||||||
# TODO
|
|
||||||
# dependencies
|
|
||||||
# compiler
|
|
||||||
# external_path
|
|
||||||
# external_module
|
|
||||||
# compiler_flags
|
|
||||||
# namespace
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# These are handwritten parts for the Spack ASP model.
|
# These are handwritten parts for the Spack ASP model.
|
||||||
#
|
#
|
||||||
|
def solve(specs, dump=None, models=1):
|
||||||
|
|
||||||
#: generate the problem space, establish cardinality constraints
|
|
||||||
_generate = """\
|
|
||||||
% One version, arch, etc. per package
|
|
||||||
{ version(P, V) : version(P, V) } = 1 :- node(P).
|
|
||||||
{ arch_platform(P, A) : arch_platform(P, A) } = 1 :- node(P).
|
|
||||||
{ arch_os(P, A) : arch_os(P, A) } = 1 :- node(P).
|
|
||||||
{ arch_target(P, T) : arch_target(P, T) } = 1 :- node(P).
|
|
||||||
|
|
||||||
% one variant value for single-valued variants.
|
|
||||||
{ variant_value(P, V, X) : variant_value(P, V, X) } = 1
|
|
||||||
:- node(P), variant(P, V), not variant_single_value(P, V).
|
|
||||||
"""
|
|
||||||
|
|
||||||
#: define the rules of Spack concretization
|
|
||||||
_define = """\
|
|
||||||
% dependencies imply new nodes.
|
|
||||||
node(D) :- node(P), depends_on(P, D).
|
|
||||||
|
|
||||||
% propagate platform, os, target downwards
|
|
||||||
arch_platform(D, A) :- node(D), depends_on(P, D), arch_platform(P, A).
|
|
||||||
arch_os(D, A) :- node(D), depends_on(P, D), arch_os(P, A).
|
|
||||||
arch_target(D, A) :- node(D), depends_on(P, D), arch_target(P, A).
|
|
||||||
|
|
||||||
% if a variant is set to anything, it is considered "set".
|
|
||||||
variant_set(P, V) :- variant_set(P, V, _).
|
|
||||||
|
|
||||||
% variant_set is an explicitly set variant value. If it's not "set",
|
|
||||||
% we revert to the default value. If it is set, we force the set value
|
|
||||||
variant_value(P, V, X) :- node(P), variant(P, V), variant_set(P, V, X).
|
|
||||||
"""
|
|
||||||
|
|
||||||
#: what parts of the model to display to read models back in
|
|
||||||
_display = """\
|
|
||||||
#show node/1.
|
|
||||||
#show depends_on/2.
|
|
||||||
#show version/2.
|
|
||||||
#show variant_value/3.
|
|
||||||
#show arch_platform/2.
|
|
||||||
#show arch_os/2.
|
|
||||||
#show arch_target/2.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def solve(specs):
|
|
||||||
"""Solve for a stable model of specs.
|
"""Solve for a stable model of specs.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
specs (list): list of Specs to solve.
|
specs (list): list of Specs to solve.
|
||||||
|
dump (tuple): what to dump
|
||||||
|
models (int): number of satisfying specs to find (default: 1)
|
||||||
"""
|
"""
|
||||||
|
clingo = which('clingo', required=True)
|
||||||
|
parser = ResultParser()
|
||||||
|
|
||||||
# get list of all possible dependencies
|
with tempfile.TemporaryFile("w+") as program:
|
||||||
pkg_names = set(spec.fullname for spec in specs)
|
generator = AspGenerator(program)
|
||||||
pkgs = [spack.repo.path.get_pkg_class(name) for name in pkg_names]
|
generator.generate_asp_program(specs)
|
||||||
pkgs = spack.package.possible_dependencies(*pkgs)
|
program.seek(0)
|
||||||
|
|
||||||
title("Generate")
|
result = Result(program.read())
|
||||||
print(_generate)
|
program.seek(0)
|
||||||
|
|
||||||
title("Define")
|
with tempfile.TemporaryFile("w+") as output:
|
||||||
print(_define)
|
with tempfile.TemporaryFile() as warnings:
|
||||||
|
clingo(
|
||||||
|
'--models=%d' % models,
|
||||||
|
input=program,
|
||||||
|
output=output,
|
||||||
|
error=warnings,
|
||||||
|
fail_on_error=False)
|
||||||
|
|
||||||
title("Package Constraints")
|
output.seek(0)
|
||||||
for pkg in pkgs:
|
result.output = output.read()
|
||||||
section(pkg)
|
|
||||||
pkg_rules(pkg)
|
|
||||||
|
|
||||||
title("Spec Constraints")
|
output.seek(0)
|
||||||
for spec in specs:
|
parser.parse(output, result)
|
||||||
section(str(spec))
|
|
||||||
spec_rules(spec)
|
|
||||||
|
|
||||||
title("Display")
|
warnings.seek(0)
|
||||||
print(_display)
|
result.warnings = warnings.read()
|
||||||
print()
|
|
||||||
print()
|
return result
|
||||||
|
Loading…
Reference in New Issue
Block a user