Skip to content

Commit

Permalink
solved bad merge
Browse files Browse the repository at this point in the history
  • Loading branch information
memsharded committed May 12, 2024
1 parent cab4f18 commit bdafa80
Show file tree
Hide file tree
Showing 6 changed files with 269 additions and 23 deletions.
7 changes: 5 additions & 2 deletions conans/client/graph/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ def propagate_downstream(self, require, node, src_node=None):
# print(" +++++Runtime conflict!", require, "with", node.ref)
return True
require.aggregate(existing.require)
if existing.require.override and existing.require.ref != require.ref:
existing.require.overriden_ref = existing.require.ref
existing.require.override_ref = require.ref

assert not require.version_range # No ranges slip into transitive_deps definitions
# TODO: Might need to move to an update() for performance
Expand Down Expand Up @@ -275,8 +278,8 @@ def create(nodes):
overrides = {}
for n in nodes:
for r in n.conanfile.requires.values():
if r.override:
continue
#if r.override:
# continue
if r.overriden_ref and not r.force:
overrides.setdefault(r.overriden_ref, set()).add(r.override_ref)
else:
Expand Down
10 changes: 7 additions & 3 deletions conans/client/graph/graph_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,11 @@ def _expand_require(self, require, node, graph, profile_host, profile_build, gra

prev_ref = prev_node.ref if prev_node else prev_require.ref
if prev_require.force or prev_require.override: # override
require.overriden_ref = require.ref # Store that the require has been overriden
require.override_ref = prev_ref
require.ref = prev_ref
require.overriden_ref = require.overriden_ref or require.ref.copy() # Old one
# require.override_ref can be !=None if lockfile-overrides defined
require.override_ref = (require.override_ref or prev_require.override_ref
or prev_require.ref.copy()) # New one
require.ref = prev_ref # New one, maybe resolved with revision
else:
self._conflicting_version(require, node, prev_require, prev_node,
prev_ref, base_previous, self._resolve_prereleases)
Expand Down Expand Up @@ -194,6 +196,8 @@ def _initialize_requires(self, node, graph, graph_lock, profile_build, profile_h
# if partial, we might still need to resolve the alias
if not resolved:
self._resolve_alias(node, require, alias, graph)
if graph_lock:
graph_lock.resolve_overrides(require)
self._resolve_replace_requires(node, require, profile_build, profile_host, graph)
node.transitive_deps[require] = TransitiveRequirement(require, node=None)

Expand Down
18 changes: 10 additions & 8 deletions conans/model/graph_lock.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,6 @@ def resolve_locked(self, node, require, resolve_prereleases):
locked_refs = self._conf_requires.refs()
else:
locked_refs = self._requires.refs()
self._resolve_overrides(require)
try:
self._resolve(require, locked_refs, resolve_prereleases)
except ConanException:
Expand All @@ -273,13 +272,16 @@ def resolve_locked(self, node, require, resolve_prereleases):
ConanOutput().error(msg, error_type="exception")
raise

def _resolve_overrides(self, require):
existing = self._overrides.get(require.ref)
if existing is not None and len(existing) == 1:
require.overriden_ref = require.ref # Store that the require has been overriden
ref = next(iter(existing))
require.ref = ref
require.override_ref = ref
def resolve_overrides(self, require):
if not self._overrides:
return

overriden = self._overrides.get(require.ref)
if overriden and len(overriden) == 1:
override_ref = next(iter(overriden))
require.overriden_ref = require.overriden_ref or require.ref.copy()
require.override_ref = override_ref
require.ref = override_ref

def resolve_prev(self, node):
if node.context == CONTEXT_BUILD:
Expand Down
4 changes: 4 additions & 0 deletions conans/model/recipe_ref.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ def __init__(self, name=None, version=None, user=None, channel=None, revision=No
self.revision = revision
self.timestamp = timestamp

def copy(self):
return RecipeReference(self.name, self.version, self.user, self.channel, self.revision,
self.timestamp)

def __repr__(self):
""" long repr like pkg/0.1@user/channel#rrev%timestamp """
result = self.repr_notime()
Expand Down
10 changes: 0 additions & 10 deletions conans/model/requires.py
Original file line number Diff line number Diff line change
Expand Up @@ -518,16 +518,6 @@ def build_require(self, ref, raise_if_duplicated=True, package_id_mode=None, vis
raise ConanException("Duplicated requirement: {}".format(ref))
self._requires[req] = req

def override(self, ref):
req = Requirement(ref)
old_requirement = self._requires.get(req)
if old_requirement is not None:
req.force = True
self._requires[req] = req
else:
req.override = True
self._requires[req] = req

def test_require(self, ref, run=None, options=None, force=None):
"""
Represent a testing framework like gtest
Expand Down
243 changes: 243 additions & 0 deletions conans/test/integration/lockfile/test_ci_overrides.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
import json

from conans.model.recipe_ref import RecipeReference
from conans.test.assets.genconanfile import GenConanfile
from conans.test.utils.tools import TestClient


def test_graph_build_order_override_error():
"""
libc -> libb -> liba -> zlib/1.2
|--------------/
|-----override------> zlib/1.3
"""
c = TestClient()
c.save({"zlib/conanfile.py": GenConanfile("zlib"),
"liba/conanfile.py": GenConanfile("liba", "0.1").with_requires("zlib/1.0"),
"libb/conanfile.py": GenConanfile("libb", "0.1").with_requires("liba/0.1", "zlib/2.0"),
"libc/conanfile.py": GenConanfile("libc", "0.1").with_requirement("libb/0.1")
.with_requirement("zlib/3.0",
override=True)
})
c.run("export zlib --version=2.0")
c.run("export zlib --version=3.0")
c.run("export liba")
c.run("export libb")
c.run("export libc")
c.run("graph info --requires=libc/0.1 --lockfile-out=output.lock")

c.run("graph build-order --requires=libc/0.1 --lockfile=output.lock --order-by=configuration "
"--build=missing --format=json")

to_build = json.loads(c.stdout)
for level in to_build["order"]:
for package in level:
binary = package["binary"]
if binary != "Build":
continue
build_args = package["build_args"]
c.run(f"install {build_args} --lockfile=output.lock")
ref = RecipeReference.loads(package["ref"])
assert f"{ref}: Building from source"

c.run("install --requires=libc/0.1 --lockfile=output.lock")
# All works, all binaries exist now
assert "zlib/3.0: Already installed!" in c.out
assert "liba/0.1: Already installed!" in c.out
assert "libb/0.1: Already installed!" in c.out
assert "libc/0.1: Already installed!" in c.out


def test_single_config_decentralized_overrides():
r""" same scenario as "test_single_config_centralized()", but distributing the build in
different build servers, using the "build-order"
Now with overrides
pkga -> toola/1.0 -> toolb/1.0 -> toolc/1.0
\------override-----> toolc/2.0
pkgb -> toola/2.0 -> toolb/1.0 -> toolc/1.0
\------override-----> toolc/3.0
pkgc -> toola/3.0 -> toolb/1.0 -> toolc/1.0
"""
c = TestClient()
c.save({"toolc/conanfile.py": GenConanfile("toolc"),
"toolb/conanfile.py": GenConanfile("toolb").with_requires("toolc/1.0"),
"toola/conanfile.py": GenConanfile("toola", "1.0").with_requirement("toolb/1.0")
.with_requirement("toolc/2.0",
override=True),
"toola2/conanfile.py": GenConanfile("toola", "2.0").with_requirement("toolb/1.0")
.with_requirement("toolc/3.0",
override=True),
"toola3/conanfile.py": GenConanfile("toola", "3.0").with_requirement("toolb/1.0"),
"pkga/conanfile.py": GenConanfile("pkga", "1.0").with_tool_requires("toola/1.0"),
"pkgb/conanfile.py": GenConanfile("pkgb", "1.0").with_requires("pkga/1.0")
.with_tool_requires("toola/2.0"),
"pkgc/conanfile.py": GenConanfile("pkgc", "1.0").with_requires("pkgb/1.0")
.with_tool_requires("toola/3.0"),
})
c.run("export toolc --version=1.0")
c.run("export toolc --version=2.0")
c.run("export toolc --version=3.0")

c.run("export toolb --version=1.0")

c.run("export toola")
c.run("export toola2")
c.run("export toola3")

c.run("export pkga")
c.run("export pkgb")
c.run("lock create pkgc")
lock = json.loads(c.load("pkgc/conan.lock"))
requires = "\n".join(lock["build_requires"])
assert "toolc/3.0" in requires
assert "toolc/2.0" in requires
assert "toolc/1.0" in requires
assert len(lock["overrides"]) == 1
assert set(lock["overrides"]["toolc/1.0"]) == {"toolc/3.0", "toolc/2.0", None}

c.run("graph build-order pkgc --lockfile=pkgc/conan.lock --format=json --build=missing")
to_build = json.loads(c.stdout)
for level in to_build:
for elem in level:
for package in elem["packages"][0]: # assumes no dependencies between packages
binary = package["binary"]
if binary != "Build":
continue
build_args = package["build_args"]
c.run(f"install {build_args} --lockfile=pkgc/conan.lock")

c.run("install pkgc --lockfile=pkgc/conan.lock")
# All works, all binaries exist now
assert "pkga/1.0: Already installed!" in c.out
assert "pkgb/1.0: Already installed!" in c.out


def test_single_config_decentralized_overrides_nested():
r""" same scenario as "test_single_config_centralized()", but distributing the build in
different build servers, using the "build-order"
Now with overrides
pkga -> toola/1.0 -> libb/1.0 -> libc/1.0 -> libd/1.0 -> libe/1.0 -> libf/1.0
\ \-----------override---------> libf/2.0
\--------------------override--------------------------> libf/3.0
"""
c = TestClient()
c.save({"libf/conanfile.py": GenConanfile("libf"),
"libe/conanfile.py": GenConanfile("libe", "1.0").with_requires("libf/1.0"),
"libd/conanfile.py": GenConanfile("libd", "1.0").with_requires("libe/1.0"),
"libc/conanfile.py": GenConanfile("libc", "1.0").with_requirement("libd/1.0")
.with_requirement("libf/2.0",
override=True),
"libb/conanfile.py": GenConanfile("libb", "1.0").with_requires("libc/1.0"),
"toola/conanfile.py": GenConanfile("toola", "1.0").with_requirement("libb/1.0")
.with_requirement("libf/3.0",
override=True),
"pkga/conanfile.py": GenConanfile("pkga", "1.0").with_tool_requires("toola/1.0"),
})

c.run("export libf --version=3.0")
c.run("export libe")
c.run("export libd")
c.run("export libc")
c.run("export libb")
c.run("export toola")

c.run("lock create pkga")
lock = json.loads(c.load("pkga/conan.lock"))
assert lock["overrides"] == {"libf/1.0": ["libf/3.0"],
"libf/2.0": ["libf/3.0"]}

c.run("graph build-order pkga --lockfile=pkga/conan.lock --format=json --build=missing")
to_build = json.loads(c.stdout)
for level in to_build:
for elem in level:
ref = elem["ref"]
if "libc" in ref:
pass
for package in elem["packages"][0]: # assumes no dependencies between packages
binary = package["binary"]
if binary != "Build":
continue
build_args = package["build_args"]
c.run(f"install {build_args} --lockfile=pkga/conan.lock")

c.run("install pkga --lockfile=pkga/conan.lock")
# All works, all binaries exist now
assert "Install finished successfully" in c.out


def test_single_config_decentralized_overrides_multi():
r""" same scenario as "test_single_config_centralized()", but distributing the build in
different build servers, using the "build-order"
Now with overrides
pkga -> toola/1.0 -> libb/1.0 -> libc/1.0 -> libd/1.0 -> libe/1.0 -> libf/1.0
| \ \-----------override--------> libf/2.0
| \--------------------override-------------------------> libf/3.0
pkgb -> toola/1.1 -> libb/1.0 -> libc/1.0 -> libd/1.0 -> libe/1.0 -> libf/1.0
| \ \-----------override--------> libf/2.0
| \--------------------override-------------------------> libf/4.0
pkgc -> toola/1.2 -> libb/1.0 -> libc/1.0 -> libd/1.0 -> libe/1.0 -> libf/1.0
\-----------override--------> libf/2.0
"""
c = TestClient()
c.save({"libf/conanfile.py": GenConanfile("libf"),
"libe/conanfile.py": GenConanfile("libe", "1.0").with_requires("libf/1.0"),
"libd/conanfile.py": GenConanfile("libd", "1.0").with_requires("libe/1.0"),
"libc/conanfile.py": GenConanfile("libc", "1.0").with_requirement("libd/1.0")
.with_requirement("libf/2.0",
override=True),
"libb/conanfile.py": GenConanfile("libb", "1.0").with_requires("libc/1.0"),
"toola/conanfile.py": GenConanfile("toola", "1.0").with_requirement("libb/1.0")
.with_requirement("libf/3.0",
override=True),
"toola1/conanfile.py": GenConanfile("toola", "1.1").with_requirement("libb/1.0")
.with_requirement("libf/4.0",
override=True),
"toola2/conanfile.py": GenConanfile("toola", "1.2").with_requirement("libb/1.0"),
"pkga/conanfile.py": GenConanfile("pkga", "1.0").with_tool_requires("toola/1.0"),
"pkgb/conanfile.py": GenConanfile("pkgb", "1.0").with_requires("pkga/1.0")
.with_tool_requires("toola/1.1"),
"pkgc/conanfile.py": GenConanfile("pkgc", "1.0").with_requires("pkgb/1.0")
.with_tool_requires("toola/1.2"),
})

c.run("export libf --version=2.0")
c.run("export libf --version=3.0")
c.run("export libf --version=4.0")
c.run("export libe")
c.run("export libd")
c.run("export libc")
c.run("export libb")

c.run("export toola")
c.run("export toola1")
c.run("export toola2")

c.run("export pkga")
c.run("export pkgb")
c.run("lock create pkgc")
lock = json.loads(c.load("pkgc/conan.lock"))
assert len(lock["overrides"]) == 2
assert set(lock["overrides"]["libf/1.0"]) == {"libf/4.0", "libf/2.0", "libf/3.0"}
assert set(lock["overrides"]["libf/2.0"]) == {"libf/4.0", "libf/3.0", None}

c.run("graph build-order pkgc --lockfile=pkgc/conan.lock --format=json --build=missing")
to_build = json.loads(c.stdout)
for level in to_build:
for elem in level:
ref = elem["ref"]
if "libc" in ref:
pass
for package in elem["packages"][0]: # assumes no dependencies between packages
binary = package["binary"]
if binary != "Build":
continue
build_args = package["build_args"]
c.run(f"install {build_args} --lockfile=pkgc/conan.lock")

c.run("install pkgc --lockfile=pkgc/conan.lock")
# All works, all binaries exist now
assert "pkga/1.0: Already installed!" in c.out
assert "pkgb/1.0: Already installed!" in c.out

0 comments on commit bdafa80

Please sign in to comment.