#876 Implementation of module resolver
Merged 6 years ago by jkaluza. Opened 6 years ago by ignatenkobrain.

@@ -0,0 +1,219 @@ 

+ # -*- coding: utf-8 -*-

+ #

+ # Copyright © 2018  Red Hat, Inc.

+ #

+ # Permission is hereby granted, free of charge, to any person obtaining a copy

+ # of this software and associated documentation files (the "Software"), to deal

+ # in the Software without restriction, including without limitation the rights

+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell

+ # copies of the Software, and to permit persons to whom the Software is

+ # furnished to do so, subject to the following conditions:

+ #

+ # The above copyright notice and this permission notice shall be included in all

+ # copies or substantial portions of the Software.

+ #

+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR

+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,

+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE

+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER

+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,

+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE

+ # SOFTWARE.

+ #

+ # Written by Jan Kaluža <jkaluza@redhat.com>

+ #            Igor Gnatenko <ignatenko@redhat.com>

+ 

+ import solv

+ from module_build_service import log

+ 

+ 

+ def _gather_alternatives(pool, favor=None, tested=None, level=1, transactions=None):

+     if tested is None:

+         tested = set()

+     if transactions is None:

+         transactions = []

+     solver = pool.Solver()

+ 

+     jobs = []

+     jobs.extend(pool.Job(solv.Job.SOLVER_FAVOR | solv.Job.SOLVER_SOLVABLE, s.id)

+                 for s in favor or [])

+     jobs.extend(pool.Job(solv.Job.SOLVER_DISFAVOR | solv.Job.SOLVER_SOLVABLE, s.id)

+                 for s in tested)

+     problems = solver.solve(jobs)

+     if problems:

+         raise RuntimeError("Problems were found during solve(): %s" % ", ".join(

+             str(p) for p in problems))

+     newsolvables = solver.transaction().newsolvables()

+     transactions.append(newsolvables)

+     alternatives = solver.all_alternatives()

+     if not alternatives:

+         return transactions

+ 

+     if [alt for alt in alternatives if alt.type != solv.Alternative.SOLVER_ALTERNATIVE_TYPE_RULE]:

+         raise SystemError("Encountered alternative with type != rule")

+ 

+     log.debug("Jobs:")

+     for job in pool.getpooljobs():

+         log.debug("  * %s [pool]", job)

+     for job in jobs:

+         log.debug("  * %s", job)

+     log.debug("Transaction:")

+     for s in newsolvables:

+         log.debug("  * %s", s)

+     log.debug("Alternatives:")

+     auto_minimized = False

+     for alt in alternatives:

+         raw_choices = alt.choices_raw()

+         log.debug("  %d: %s", alt.level, alt)

+         for choice in alt.choices():

+             if choice == alt.chosen:

+                 sign = "+"

+             elif -choice.id in raw_choices:

+                 sign = "-"

+                 auto_minimized = True

+             else:

+                 sign = " "

+             log.debug("  * %s%s", sign, choice)

+     if auto_minimized:

+         raise NotImplementedError("Transaction was auto-minimized")

+ 

+     current_alternatives = [alt for alt in alternatives if alt.level == level]

+     if len(current_alternatives) > 1:

+         raise SystemError("Encountered multiple alternatives on the same level")

+ 

+     alternative = current_alternatives[0]

+     raw_choices = alternative.choices_raw()

+     tested.add(alternative.chosen)

+ 

+     for choice in (choice for choice in alternative.choices() if choice not in tested):

+         _gather_alternatives(pool,

+                              favor=favor,

+                              tested=tested,

+                              level=level,

+                              transactions=transactions)

+ 

+     max_level = max(alt.level for alt in alternatives)

+     if level == max_level:

+         return transactions

+ 

+     next_favor = [alt.chosen for alt in alternatives if alt.level <= level]

+     next_tested = set(alt.chosen for alt in alternatives if alt.level == level + 1)

+     _gather_alternatives(pool,

+                          favor=next_favor,

+                          tested=next_tested,

+                          level=level + 1,

+                          transactions=transactions)

+ 

+     return transactions

+ 

+ 

+ class MMDResolver(object):

+     """

+     Resolves dependencies between Module metadata objects.

+     """

+ 

+     def __init__(self):

+         self.pool = solv.Pool()

+         self.pool.setarch("x86_64")

+         self.build_repo = self.pool.add_repo("build")

+         self.available_repo = self.pool.add_repo("available")

+ 

+     def add_modules(self, mmd):

+         n, s, v, c = mmd.get_name(), mmd.get_stream(), mmd.get_version(), mmd.get_context()

+ 

+         pool = self.pool

+ 

+         solvables = []

+         if c is not None:

+             # Built module

+             deps = mmd.get_dependencies()

+             if len(deps) > 1:

+                 raise ValueError(

+                     "The built module contains different runtime dependencies: %s" % mmd.dumps())

+ 

+             # $n:$s:$c-$v.$a

+             solvable = self.available_repo.add_solvable()

+             solvable.name = "%s:%s:%d:%s" % (n, s, v, c)

+             solvable.evr = str(v)

+             # TODO: replace with real arch

+             solvable.arch = "x86_64"

+ 

+             # Prv: module($n)

+             solvable.add_deparray(solv.SOLVABLE_PROVIDES,

+                                   pool.Dep("module(%s)" % n))

+             # Prv: module($n:$s) = $v

+             solvable.add_deparray(solv.SOLVABLE_PROVIDES,

+                                   pool.Dep("module(%s:%s)" % (n, s)).Rel(

+                                       solv.REL_EQ, pool.Dep(str(v))))

+ 

+             if deps:

+                 # Req: (module($on1:$os1) OR module($on2:$os2) OR …)

+                 for name, streams in deps[0].get_requires().items():

+                     requires = None

+                     for stream in streams.get():

+                         require = pool.Dep("module(%s:%s)" % (name, stream))

+                         if requires is not None:

+                             requires = requires.Rel(solv.REL_OR, require)

+                         else:

+                             requires = require

+                     solvable.add_deparray(solv.SOLVABLE_REQUIRES, requires)

+ 

+             # Con: module($n)

+             solvable.add_deparray(solv.SOLVABLE_CONFLICTS, pool.Dep("module(%s)" % n))

+ 

+             solvables.append(solvable)

+         else:

+             # Input module

+             # Context means two things:

+             # * Unique identifier

+             # * Offset for the dependency which was used

+             for c, deps in enumerate(mmd.get_dependencies()):

+                 # $n:$s:$c-$v.src

+                 solvable = self.build_repo.add_solvable()

+                 solvable.name = "%s:%s:%s:%d" % (n, s, v, c)

+                 solvable.evr = str(v)

+                 solvable.arch = "src"

+ 

+                 # Req: (module($on1:$os1) OR module($on2:$os2) OR …)

+                 for name, streams in deps.get_buildrequires().items():

+                     requires = None

+                     for stream in streams.get():

+                         require = pool.Dep("module(%s:%s)" % (name, stream))

+                         if requires:

+                             requires = requires.Rel(solv.REL_OR, require)

+                         else:

+                             requires = require

+                     solvable.add_deparray(solv.SOLVABLE_REQUIRES, requires)

+ 

+                 solvables.append(solvable)

+ 

+         return solvables

+ 

+     def solve(self, mmd):

+         """

+         Solves dependencies of module defined by `mmd` object. Returns set

+         containing frozensets with all the possible combinations which

+         satisfied dependencies.

+ 

+         :return: set of frozensets of n:s:v:c of modules which satisfied the

+             dependency solving.

+         """

+         solvables = self.add_modules(mmd)

+         if not solvables:

+             raise ValueError("No module(s) found for resolving")

+         self.pool.createwhatprovides()

+ 

+         # XXX: Using SOLVABLE_ONE_OF should be faster & more convenient.

+         # There must be a bug in _gather_alternatives(), possibly when processing l1 alternatives.

+         # Use pool.towhatprovides() to combine solvables.

+ 

+         alternatives = []

+         jobs = self.pool.getpooljobs()

+         for s in solvables:

+             new_job = self.pool.Job(solv.Job.SOLVER_INSTALL | solv.Job.SOLVER_SOLVABLE, s.id)

+             self.pool.setpooljobs(jobs + [new_job])

+             alternatives.extend(_gather_alternatives(self.pool))

+         self.pool.setpooljobs(jobs)

+ 

+         return set(frozenset("%s:%s" % (s.name, s.arch) for s in trans)

+                    for trans in alternatives)

@@ -0,0 +1,322 @@ 

+ # -*- coding: utf-8 -*-

+ #

+ # Copyright © 2018  Red Hat, Inc.

+ #

+ # Permission is hereby granted, free of charge, to any person obtaining a copy

+ # of this software and associated documentation files (the "Software"), to deal

+ # in the Software without restriction, including without limitation the rights

+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell

+ # copies of the Software, and to permit persons to whom the Software is

+ # furnished to do so, subject to the following conditions:

+ #

+ # The above copyright notice and this permission notice shall be included in all

+ # copies or substantial portions of the Software.

+ #

+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR

+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,

+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE

+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER

+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,

+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE

+ # SOFTWARE.

+ #

+ # Written by Jan Kaluža <jkaluza@redhat.com>

+ #            Igor Gnatenko <ignatenko@redhat.com>

+ 

+ import gi

+ gi.require_version("Modulemd", "1.0") # noqa

+ from gi.repository import Modulemd

+ 

+ from module_build_service.mmd_resolver import MMDResolver

+ 

+ 

+ class TestMMDResolver:

+ 

+     def setup_method(self, test_method):

+         self.mmd_resolver = MMDResolver()

+ 

+     def teardown_method(self, test_method):

+         pass

+ 

+     @staticmethod

+     def _make_mmd(nsvc, requires):

+         name, stream, version = nsvc.split(":", 2)

+         mmd = Modulemd.Module()

+         mmd.set_mdversion(2)

+         mmd.set_name(name)

+         mmd.set_stream(stream)

+         mmd.set_summary("foo")

+         mmd.set_description("foo")

+         licenses = Modulemd.SimpleSet()

+         licenses.add("GPL")

+         mmd.set_module_licenses(licenses)

+ 

+         if ":" in version:

Can you add a comment here saying that a module without a context will be the module we are trying to resolve which is why there is special handling of that scenario here?

+             version, context = version.split(":")

+             mmd.set_context(context)

+             add_requires = Modulemd.Dependencies.add_requires

+             requires_list = [requires]

+         else:

+             add_requires = Modulemd.Dependencies.add_buildrequires

+             if not isinstance(requires, list):

+                 requires_list = [requires]

+             else:

+                 requires_list = requires

+         mmd.set_version(int(version))

+ 

+         deps_list = []

+         for reqs in requires_list:

+             deps = Modulemd.Dependencies()

+             for req_name, req_streams in reqs.items():

+                 add_requires(deps, req_name, req_streams)

+             deps_list.append(deps)

+         mmd.set_dependencies(deps_list)

+ 

+         return mmd

+ 

+     @classmethod

+     def _default_mmds(cls):

+         return [

+             cls._make_mmd("gtk:1:0:c2", {"platform": ["f28"]}),

+             cls._make_mmd("gtk:1:0:c3", {"platform": ["f29"]}),

+             cls._make_mmd("gtk:2:0:c4", {"platform": ["f28"]}),

+             cls._make_mmd("gtk:2:0:c5", {"platform": ["f29"]}),

+             cls._make_mmd("foo:1:0:c2", {"platform": ["f28"]}),

+             cls._make_mmd("foo:1:0:c3", {"platform": ["f29"]}),

+             cls._make_mmd("foo:2:0:c4", {"platform": ["f28"]}),

+             cls._make_mmd("foo:2:0:c5", {"platform": ["f29"]}),

+             cls._make_mmd("platform:f28:0:c10", {}),

+             cls._make_mmd("platform:f29:0:c11", {}),

+         ]

+ 

+     @classmethod

+     def _default_mmds_with_multiple_requires(cls):

+         return [

+             cls._make_mmd("gtk:1:0:c2", {"font": ["a", "b"], "platform": ["f28"]}),

+             cls._make_mmd("gtk:1:0:c3", {"font": ["a", "b"], "platform": ["f29"]}),

+             cls._make_mmd("gtk:2:0:c4", {"font": ["a", "b"], "platform": ["f28"]}),

+             cls._make_mmd("gtk:2:0:c5", {"font": ["a", "b"], "platform": ["f29"]}),

+             cls._make_mmd("font:a:0:c6", {"platform": ["f28"]}),

+             cls._make_mmd("font:a:0:c7", {"platform": ["f29"]}),

+             cls._make_mmd("font:b:0:c8", {"platform": ["f28"]}),

+             cls._make_mmd("font:b:0:c9", {"platform": ["f29"]}),

+             cls._make_mmd("platform:f28:0:c10", {}),

+             cls._make_mmd("platform:f29:0:c11", {}),

+         ]

+ 

+     def test_solve_tree(self):

+         for mmd in self._default_mmds_with_multiple_requires():

+             self.mmd_resolver.add_modules(mmd)

+ 

+         app = self._make_mmd("app:1:0", {"gtk": ["1", "2"]})

+         expanded = self.mmd_resolver.solve(app)

+ 

+         expected = set([

+             frozenset(["app:1:0:0:src",

+                        "font:a:0:c6:x86_64",

+                        "gtk:1:0:c2:x86_64",

+                        "platform:f28:0:c10:x86_64"]),

+             frozenset(["app:1:0:0:src",

+                        "font:a:0:c7:x86_64",

+                        "gtk:1:0:c3:x86_64",

+                        "platform:f29:0:c11:x86_64"]),

+             frozenset(["app:1:0:0:src",

+                        "font:b:0:c8:x86_64",

+                        "gtk:1:0:c2:x86_64",

+                        "platform:f28:0:c10:x86_64"]),

+             frozenset(["app:1:0:0:src",

+                        "font:b:0:c9:x86_64",

+                        "gtk:1:0:c3:x86_64",

+                        "platform:f29:0:c11:x86_64"]),

+             frozenset(["app:1:0:0:src",

+                        "font:a:0:c6:x86_64",

+                        "gtk:2:0:c4:x86_64",

+                        "platform:f28:0:c10:x86_64"]),

+             frozenset(["app:1:0:0:src",

+                        "font:b:0:c9:x86_64",

+                        "gtk:2:0:c5:x86_64",

+                        "platform:f29:0:c11:x86_64"]),

+             frozenset(["app:1:0:0:src",

+                        "font:b:0:c8:x86_64",

+                        "gtk:2:0:c4:x86_64",

+                        "platform:f28:0:c10:x86_64"]),

+             frozenset(["app:1:0:0:src",

+                        "font:a:0:c7:x86_64",

+                        "gtk:2:0:c5:x86_64",

+                        "platform:f29:0:c11:x86_64"]),

+         ])

+ 

+         assert expanded == expected

+ 

+     def test_solve_tree_buildrequire_platform(self):

+         for mmd in self._default_mmds_with_multiple_requires():

+             self.mmd_resolver.add_modules(mmd)

+ 

+         app = self._make_mmd("app:1:0", {"gtk": ["1", "2"], "platform": ["f28"]})

+         expanded = self.mmd_resolver.solve(app)

+ 

+         expected = set([

+             frozenset(["app:1:0:0:src",

+                        "font:a:0:c6:x86_64",

+                        "gtk:2:0:c4:x86_64",

+                        "platform:f28:0:c10:x86_64"]),

+             frozenset(["app:1:0:0:src",

+                        "font:b:0:c8:x86_64",

+                        "gtk:1:0:c2:x86_64",

+                        "platform:f28:0:c10:x86_64"]),

+             frozenset(["app:1:0:0:src",

+                        "font:a:0:c6:x86_64",

+                        "gtk:1:0:c2:x86_64",

+                        "platform:f28:0:c10:x86_64"]),

+             frozenset(["app:1:0:0:src",

+                        "font:b:0:c8:x86_64",

+                        "gtk:2:0:c4:x86_64",

+                        "platform:f28:0:c10:x86_64"]),

+         ])

+ 

+         assert expanded == expected

+ 

+     def test_solve_tree_multiple_build_requires(self):

+         for mmd in self._default_mmds():

+             self.mmd_resolver.add_modules(mmd)

+ 

+         app = self._make_mmd("app:1:0", {"gtk": ["1", "2"], "foo": ["1", "2"]})

+         expanded = self.mmd_resolver.solve(app)

+ 

+         expected = set([

+             frozenset(["app:1:0:0:src",

+                        "foo:1:0:c3:x86_64",

+                        "gtk:1:0:c3:x86_64",

+                        "platform:f29:0:c11:x86_64"]),

+             frozenset(["app:1:0:0:src",

+                        "foo:2:0:c5:x86_64",

+                        "gtk:1:0:c3:x86_64",

+                        "platform:f29:0:c11:x86_64"]),

+             frozenset(["app:1:0:0:src",

+                        "foo:1:0:c2:x86_64",

+                        "gtk:2:0:c4:x86_64",

+                        "platform:f28:0:c10:x86_64"]),

+             frozenset(["app:1:0:0:src",

+                        "foo:1:0:c3:x86_64",

+                        "gtk:2:0:c5:x86_64",

+                        "platform:f29:0:c11:x86_64"]),

+             frozenset(["app:1:0:0:src",

+                        "foo:2:0:c5:x86_64",

+                        "gtk:2:0:c5:x86_64",

+                        "platform:f29:0:c11:x86_64"]),

+             frozenset(["app:1:0:0:src",

+                        "foo:2:0:c4:x86_64",

+                        "gtk:2:0:c4:x86_64",

+                        "platform:f28:0:c10:x86_64"]),

+             frozenset(["app:1:0:0:src",

+                        "foo:1:0:c2:x86_64",

+                        "gtk:1:0:c2:x86_64",

+                        "platform:f28:0:c10:x86_64"]),

+             frozenset(["app:1:0:0:src",

+                        "foo:2:0:c4:x86_64",

+                        "gtk:1:0:c2:x86_64",

+                        "platform:f28:0:c10:x86_64"]),

+         ])

+ 

+         assert expanded == expected

+ 

+     def test_solve_multiple_requires_pairs(self):

+         for mmd in self._default_mmds():

+             self.mmd_resolver.add_modules(mmd)

+ 

+         app = self._make_mmd(

+             "app:1:0",

+             [{"gtk": ["1"], "foo": ["1"]},

+              {"gtk": ["2"], "foo": ["1", "2"]}])

+         expanded = self.mmd_resolver.solve(app)

+ 

+         expected = set([

+             frozenset(["app:1:0:1:src",

+                        "foo:1:0:c3:x86_64",

+                        "gtk:2:0:c5:x86_64",

+                        "platform:f29:0:c11:x86_64"]),

+             frozenset(["app:1:0:1:src",

+                        "foo:2:0:c5:x86_64",

+                        "gtk:2:0:c5:x86_64",

+                        "platform:f29:0:c11:x86_64"]),

+             frozenset(["app:1:0:0:src",

+                        "foo:1:0:c3:x86_64",

+                        "gtk:1:0:c3:x86_64",

+                        "platform:f29:0:c11:x86_64"]),

+             frozenset(["app:1:0:1:src",

+                        "foo:1:0:c2:x86_64",

+                        "gtk:2:0:c4:x86_64",

+                        "platform:f28:0:c10:x86_64"]),

+             frozenset(["app:1:0:1:src",

+                        "foo:2:0:c4:x86_64",

+                        "gtk:2:0:c4:x86_64",

+                        "platform:f28:0:c10:x86_64"]),

+             frozenset(["app:1:0:0:src",

+                        "foo:1:0:c2:x86_64",

+                        "gtk:1:0:c2:x86_64",

+                        "platform:f28:0:c10:x86_64"]),

+         ])

+ 

+         assert expanded == expected

+ 

+     def test_solve_multiple_requires_pairs_buildrequire_platform(self):

+         for mmd in self._default_mmds():

+             self.mmd_resolver.add_modules(mmd)

+ 

+         app = self._make_mmd(

+             "app:1:0",

+             [{"gtk": ["1"], "foo": ["1"]},

+              {"gtk": ["2"], "foo": ["1", "2"], "platform": ["f28"]}])

+         expanded = self.mmd_resolver.solve(app)

+ 

+         expected = set([

+             frozenset(["app:1:0:0:src",

+                        "foo:1:0:c3:x86_64",

+                        "gtk:1:0:c3:x86_64",

+                        "platform:f29:0:c11:x86_64"]),

+             frozenset(["app:1:0:1:src",

+                        "foo:1:0:c2:x86_64",

+                        "gtk:2:0:c4:x86_64",

+                        "platform:f28:0:c10:x86_64"]),

+             frozenset(["app:1:0:0:src",

+                        "foo:1:0:c2:x86_64",

+                        "gtk:1:0:c2:x86_64",

+                        "platform:f28:0:c10:x86_64"]),

+             frozenset(["app:1:0:1:src",

+                        "foo:2:0:c4:x86_64",

+                        "gtk:2:0:c4:x86_64",

+                        "platform:f28:0:c10:x86_64"]),

+         ])

+ 

+         assert expanded == expected

+ 

+     def test_solve_multiple_requires_pairs_multiple_requires(self):

+         for mmd in self._default_mmds_with_multiple_requires():

+             self.mmd_resolver.add_modules(mmd)

+ 

+         app = self._make_mmd(

+             "app:1:0",

+             [{"gtk": ["1"], "font": ["a"]},

+              {"gtk": ["2"], "font": ["b"]}])

+         expanded = self.mmd_resolver.solve(app)

+ 

+         expected = set([

+             frozenset(["app:1:0:0:src",

+                        "font:a:0:c7:x86_64",

+                        "gtk:1:0:c3:x86_64",

+                        "platform:f29:0:c11:x86_64"]),

+             frozenset(["app:1:0:1:src",

+                        "font:b:0:c8:x86_64",

+                        "gtk:2:0:c4:x86_64",

+                        "platform:f28:0:c10:x86_64"]),

+             frozenset(["app:1:0:0:src",

+                        "font:a:0:c6:x86_64",

+                        "gtk:1:0:c2:x86_64",

+                        "platform:f28:0:c10:x86_64"]),

+             frozenset(["app:1:0:1:src",

+                        "font:b:0:c9:x86_64",

+                        "gtk:2:0:c5:x86_64",

+                        "platform:f29:0:c11:x86_64"]),

+         ])

+ 

+         assert expanded == expected

file modified
+1
@@ -11,6 +11,7 @@ 

  

  [testenv]

  usedevelop = true

+ sitepackages = true

  deps = -r{toxinidir}/test-requirements.txt

  commands =

      py.test -v {posargs}

no initial comment

3 new commits added

  • mmd_resolver: rewrite function for finding alternatives
  • mmd_resolver: set 'src' arch for build repo solvables
  • Add MMDResolver to resolve deps between modules using libsolv and libmodulemd.
6 years ago

1 new commit added

  • mmd_resolver: HACK: consider alternatives only from whitelist
6 years ago

rebased onto d5ac66553c46b89ebeb146f95c73990c8ecbc4e8

6 years ago

Do you have to set an arch at all or can we assume for the time being that all modules will work on all arches?

I took this code from @jkaluza, for now we just assume that we support only x86_64. In future, we will need to create solvables for each supported arch + src solvables for each input build combination (as you noted in original PR).

I couldn't find any docs about what Job does? Do you have any or could you please add comments of what's going on from here?

rebased onto 697066c4fe48566dd8130c0917db914751baaeed

6 years ago

4 new commits added

  • tests/mmd_resolver: add tests for multi-dependency modules
  • tests/mmd_resolver: deduplicate code for generation test modules
  • tests/mmd_resolver: add support for multi-dependency modules
  • fixup! mmd_resolver: rework solvables generation
6 years ago

1 new commit added

  • fixup! mmd_resolver: rework solvables generation
6 years ago

+1, I would appreciate more comments even for those private methods and even in those methods, but the code itself looks good to me. I'm even OK commenting that myself later. I'm happy you did the hard part finding out how that should work :).

Before merging, just replace asserts with exceptions.

1 new commit added

  • mmd_resolver: replace asserts with meaningful exceptions
6 years ago

rebased onto 799e4318570b402e1a8cc92e9e806cf63697428a

6 years ago

I'm delegating task for writing comments to @jkaluza, but will happily review them for correctness.

@jkaluza, asserts are replaced with exceptions.

Can we remove this multi-line comment and perhaps convert this to a # TODO comment to look into this.

When did Ralph Bean help on this? :)

rebased onto 44890a6ebf8be6cf40db4f6ac412b6c6b3580921

6 years ago

Can you add a comment here saying that a module without a context will be the module we are trying to resolve which is why there is special handling of that scenario here?

Okay, the tests look great. I'll briefly review the code after lunch.

If the mmd is of type Modulemd.Module then it should be s.get_arch() not s.arch.

Yup, just saw that. Disregard my comment.

Could the exception be something like "A modulemd to resolve couldn't be determined"

Maybe something like?

raise ValueError("The built modulemd's dependencies haven't been expanded: {0}".format(mmd.dumps()))

Bike-shedding: I think str(v) is more readable than "%d" % v.

Bike-shedding: I think str(v) is more readable than "%d" % v.

Can you make this a RuntimeError?

Just a few comments but overall +1. I do admit I don't understand parts of the libsolv code but the tests look good so I assume the overall code works as intended.

Thanks for the work on this guys and nice job!

@jkaluza, feel free to merge after you review.

rebased onto 54f2648a645dbe211d920bddfe98515c13d51172

6 years ago

rebased onto a5965a6

6 years ago

Pull-Request has been merged by jkaluza

6 years ago