From 8c263876b83ec3bbc8189cac62a3f1aeeb40dc16 Mon Sep 17 00:00:00 2001 From: memsharded Date: Tue, 29 Oct 2024 17:01:22 +0100 Subject: [PATCH 1/5] lock upgrade feature --- conan/cli/commands/lock.py | 41 +++++++++++++++++++ .../lockfile/test_user_overrides.py | 33 +++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/conan/cli/commands/lock.py b/conan/cli/commands/lock.py index 6b4f2cf548f..eb5ecbaa33d 100644 --- a/conan/cli/commands/lock.py +++ b/conan/cli/commands/lock.py @@ -179,3 +179,44 @@ def lock_update(conan_api, parser, subparser, *args): lockfile.update(requires=args.requires, build_requires=args.build_requires, python_requires=args.python_requires, config_requires=args.config_requires) conan_api.lockfile.save_lockfile(lockfile, args.lockfile_out) + + + + +@conan_subcommand() +def lock_upgrade(conan_api, parser, subparser, *args): + """ + Update requires, build-requires or python-requires from an existing lockfile. + References that matches the arguments package names will be replaced by the arguments. + References can be supplied with and without revisions like "--requires=pkg/version", + """ + subparser.add_argument('--requires', action="append", help='Update references to lockfile.') + subparser.add_argument('--build-requires', action="append", + help='Update build-requires from lockfile') + subparser.add_argument('--python-requires', action="append", + help='Update python-requires from lockfile') + subparser.add_argument('--config-requires', action="append", + help='Update config-requires from lockfile') + common_graph_args(subparser) + subparser.add_argument("--build-require", action='store_true', default=False, + help='Whether the provided reference is a build-require') + args = parser.parse_args(*args) + + # parameter validation + validate_common_graph_args(args) + + cwd = os.getcwd() + path = conan_api.local.get_conanfile_path(args.path, cwd, py=None) if args.path else None + remotes = conan_api.remotes.list(args.remote) if not args.no_remote else [] + overrides = eval(args.lockfile_overrides) if args.lockfile_overrides else None + lockfile = conan_api.lockfile.get_lockfile(lockfile=args.lockfile, conanfile_path=path, + cwd=cwd, partial=True, overrides=overrides) + profile_host, profile_build = conan_api.profiles.get_profiles_from_args(args) + + +args = parser.parse_args(*args) + + lockfile = conan_api.lockfile.get_lockfile(lockfile=args.lockfile, partial=True) + lockfile.update(requires=args.requires, build_requires=args.build_requires, + python_requires=args.python_requires, config_requires=args.config_requires) + conan_api.lockfile.save_lockfile(lockfile, args.lockfile_out) diff --git a/test/integration/lockfile/test_user_overrides.py b/test/integration/lockfile/test_user_overrides.py index e3e033969a3..b02dab3563d 100644 --- a/test/integration/lockfile/test_user_overrides.py +++ b/test/integration/lockfile/test_user_overrides.py @@ -360,3 +360,36 @@ def test_lock_update(self, kind, old, new): lock = c.load("conan.lock") assert old not in lock assert new in lock + + +class TestLockUpgrade: + @pytest.mark.parametrize("kind, old, new", [ + ("requires", "math/*", "math/1.1"), + ("build-requires", "cmake/1.0", "cmake/1.1"), + ("python-requires", "mytool/1.0", "mytool/1.1"), + ]) + def test_lock_update(self, kind, old, new): + c = TestClient(light=True) + lock = textwrap.dedent("""\ + { + "version": "0.5", + "requires": [ + "math/1.0#85d927a4a067a531b1a9c7619522c015%1702683583.3411012", + "math/1.0#12345%1702683584.3411012", + "engine/1.0#fd2b006646a54397c16a1478ac4111ac%1702683583.3544693" + ], + "build_requires": [ + "cmake/1.0#85d927a4a067a531b1a9c7619522c015%1702683583.3411012", + "ninja/1.0#fd2b006646a54397c16a1478ac4111ac%1702683583.3544693" + ], + "python_requires": [ + "mytool/1.0#85d927a4a067a531b1a9c7619522c015%1702683583.3411012", + "othertool/1.0#fd2b006646a54397c16a1478ac4111ac%1702683583.3544693" + ] + } + """) + c.save({"conan.lock": lock}) + c.run(f"lock upgrade --requires={old}") + lock = c.load("conan.lock") + assert old not in lock + assert new in lock From 5bfc541a9109a23f53d51539078f181cd8b1d07d Mon Sep 17 00:00:00 2001 From: PerseoGI Date: Mon, 13 Jan 2025 18:35:24 +0100 Subject: [PATCH 2/5] WIP lock upgrade --- conan/cli/commands/lock.py | 44 ++++++++++++++----- .../lockfile/test_user_overrides.py | 2 +- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/conan/cli/commands/lock.py b/conan/cli/commands/lock.py index eb5ecbaa33d..a3fa1b2a5df 100644 --- a/conan/cli/commands/lock.py +++ b/conan/cli/commands/lock.py @@ -190,16 +190,18 @@ def lock_upgrade(conan_api, parser, subparser, *args): References that matches the arguments package names will be replaced by the arguments. References can be supplied with and without revisions like "--requires=pkg/version", """ - subparser.add_argument('--requires', action="append", help='Update references to lockfile.') - subparser.add_argument('--build-requires', action="append", + common_graph_args(subparser) + subparser.add_argument('--update-requires', action="append", + help='Update requires from lockfile') + subparser.add_argument('--update-build-requires', action="append", help='Update build-requires from lockfile') - subparser.add_argument('--python-requires', action="append", + subparser.add_argument('--update-python-requires', action="append", help='Update python-requires from lockfile') - subparser.add_argument('--config-requires', action="append", + subparser.add_argument('--update-config-requires', action="append", help='Update config-requires from lockfile') - common_graph_args(subparser) subparser.add_argument("--build-require", action='store_true', default=False, help='Whether the provided reference is a build-require') + args = parser.parse_args(*args) # parameter validation @@ -212,11 +214,31 @@ def lock_upgrade(conan_api, parser, subparser, *args): lockfile = conan_api.lockfile.get_lockfile(lockfile=args.lockfile, conanfile_path=path, cwd=cwd, partial=True, overrides=overrides) profile_host, profile_build = conan_api.profiles.get_profiles_from_args(args) + lockfile = conan_api.lockfile.get_lockfile(lockfile=args.lockfile, partial=True) + # Remove the lockfile entries that will be updated + lockfile = conan_api.lockfile.remove_lockfile(lockfile, + requires=args.update_requires, + python_requires=args.update_python_requires, + build_requires=args.update_build_requires, + config_requires=args.update_config_requires) + # Resolve new graph + if path: + graph = conan_api.graph.load_graph_consumer(path, args.name, args.version, + args.user, args.channel, + profile_host, profile_build, lockfile, + remotes, args.update, # TODO: args.update should be True?? + is_build_require=args.build_require) + else: + graph = conan_api.graph.load_graph_requires(args.requires, args.tool_requires, + profile_host, profile_build, lockfile, + remotes, args.update) + print_graph_basic(graph) + graph.report_graph_error() + conan_api.graph.analyze_binaries(graph, args.build, remotes=remotes, update=args.update, + lockfile=lockfile) + print_graph_packages(graph) -args = parser.parse_args(*args) - - lockfile = conan_api.lockfile.get_lockfile(lockfile=args.lockfile, partial=True) - lockfile.update(requires=args.requires, build_requires=args.build_requires, - python_requires=args.python_requires, config_requires=args.config_requires) - conan_api.lockfile.save_lockfile(lockfile, args.lockfile_out) + lockfile = conan_api.lockfile.update_lockfile(lockfile, graph, args.lockfile_packages, + clean=args.lockfile_clean) + conan_api.lockfile.save_lockfile(lockfile, args.lockfile_out or LOCKFILE) diff --git a/test/integration/lockfile/test_user_overrides.py b/test/integration/lockfile/test_user_overrides.py index b02dab3563d..0da17d6b8f4 100644 --- a/test/integration/lockfile/test_user_overrides.py +++ b/test/integration/lockfile/test_user_overrides.py @@ -368,7 +368,7 @@ class TestLockUpgrade: ("build-requires", "cmake/1.0", "cmake/1.1"), ("python-requires", "mytool/1.0", "mytool/1.1"), ]) - def test_lock_update(self, kind, old, new): + def test_lock_upgrade(self, kind, old, new): c = TestClient(light=True) lock = textwrap.dedent("""\ { From bab0eb88e1694327e069862b9f68db073ca80d11 Mon Sep 17 00:00:00 2001 From: PerseoGI Date: Tue, 14 Jan 2025 12:33:39 +0100 Subject: [PATCH 3/5] WIP lock upgrade: added tests --- conan/cli/commands/lock.py | 13 ++-- .../lockfile/test_user_overrides.py | 78 +++++++++++++------ 2 files changed, 61 insertions(+), 30 deletions(-) diff --git a/conan/cli/commands/lock.py b/conan/cli/commands/lock.py index a3fa1b2a5df..5b52e208764 100644 --- a/conan/cli/commands/lock.py +++ b/conan/cli/commands/lock.py @@ -8,6 +8,7 @@ from conan.cli.printers.graph import print_graph_packages, print_graph_basic from conan.internal.model.lockfile import Lockfile, LOCKFILE from conan.internal.model.recipe_ref import RecipeReference +from conan.errors import ConanException @conan_command(group="Consumer") @@ -59,7 +60,7 @@ def lock_create(conan_api, parser, subparser, *args): clean=args.lockfile_clean) conanfile_path = os.path.dirname(graph.root.path) \ if graph.root.path and args.lockfile_out is None else cwd - conan_api.lockfile.save_lockfile(lockfile, args.lockfile_out or "conan.lock", conanfile_path) + conan_api.lockfile.save_lockfile(lockfile, args.lockfile_out or LOCKFILE, conanfile_path) @conan_subcommand() @@ -186,9 +187,8 @@ def lock_update(conan_api, parser, subparser, *args): @conan_subcommand() def lock_upgrade(conan_api, parser, subparser, *args): """ - Update requires, build-requires or python-requires from an existing lockfile. - References that matches the arguments package names will be replaced by the arguments. - References can be supplied with and without revisions like "--requires=pkg/version", + Upgrade requires, build-requires or python-requires from an existing lockfile given a conanfile + or a reference. """ common_graph_args(subparser) subparser.add_argument('--update-requires', action="append", @@ -207,6 +207,10 @@ def lock_upgrade(conan_api, parser, subparser, *args): # parameter validation validate_common_graph_args(args) + if not any([args.update_requires, args.update_build_requires, args.update_python_requires, args.update_config_requires]): + raise ConanException("At least one of --update-requires, --update-build-requires, " + "--update-python-requires or --update-config-requires should be specified") + cwd = os.getcwd() path = conan_api.local.get_conanfile_path(args.path, cwd, py=None) if args.path else None remotes = conan_api.remotes.list(args.remote) if not args.no_remote else [] @@ -214,7 +218,6 @@ def lock_upgrade(conan_api, parser, subparser, *args): lockfile = conan_api.lockfile.get_lockfile(lockfile=args.lockfile, conanfile_path=path, cwd=cwd, partial=True, overrides=overrides) profile_host, profile_build = conan_api.profiles.get_profiles_from_args(args) - lockfile = conan_api.lockfile.get_lockfile(lockfile=args.lockfile, partial=True) # Remove the lockfile entries that will be updated lockfile = conan_api.lockfile.remove_lockfile(lockfile, requires=args.update_requires, diff --git a/test/integration/lockfile/test_user_overrides.py b/test/integration/lockfile/test_user_overrides.py index 0da17d6b8f4..37aa764c579 100644 --- a/test/integration/lockfile/test_user_overrides.py +++ b/test/integration/lockfile/test_user_overrides.py @@ -363,33 +363,61 @@ def test_lock_update(self, kind, old, new): class TestLockUpgrade: - @pytest.mark.parametrize("kind, old, new", [ - ("requires", "math/*", "math/1.1"), - ("build-requires", "cmake/1.0", "cmake/1.1"), - ("python-requires", "mytool/1.0", "mytool/1.1"), + @pytest.mark.parametrize("kind, pkg, old, new", [ + ("requires", "math", "math/1.0", "math/1.1"), + # ("build-requires", "cmake", "cmake/1.0", "cmake/1.1"), # TODO there is not a --build-requires + # ("python-requires", "mytool", "mytool/1.0", "mytool/1.1"), # TODO nor a --python-requires ]) - def test_lock_upgrade(self, kind, old, new): + def test_lock_upgrade(self, kind, pkg, old, new): c = TestClient(light=True) - lock = textwrap.dedent("""\ + c.save({f"{pkg}/conanfile.py": GenConanfile(pkg)}) + + c.run(f"export {pkg} --version=1.0") + rev0 = c.exported_recipe_revision() + c.run(f"lock create --{kind}={pkg}/[*]") + lock = c.load("conan.lock") + assert f"{old}#{rev0}" in lock + + c.run(f"export {pkg} --version=1.1") + rev1 = c.exported_recipe_revision() + c.run(f"lock upgrade --{kind}={pkg}/[*] --update-{kind}={pkg}/[*]") + lock = c.load("conan.lock") + assert f"{old}#{rev0}" not in lock + assert f"{new}#{rev1}" in lock + + + def test_lock_upgrade_path(self): + c = TestClient(light=True) + c.save({"liba/conanfile.py": GenConanfile("liba"), + "libb/conanfile.py": GenConanfile("libb"), + "libc/conanfile.py": GenConanfile("libc")}) + c.run(f"export liba --version=1.0") + c.run(f"export libb --version=1.0") + c.run(f"export libc --version=1.0") + c.save( { - "version": "0.5", - "requires": [ - "math/1.0#85d927a4a067a531b1a9c7619522c015%1702683583.3411012", - "math/1.0#12345%1702683584.3411012", - "engine/1.0#fd2b006646a54397c16a1478ac4111ac%1702683583.3544693" - ], - "build_requires": [ - "cmake/1.0#85d927a4a067a531b1a9c7619522c015%1702683583.3411012", - "ninja/1.0#fd2b006646a54397c16a1478ac4111ac%1702683583.3544693" - ], - "python_requires": [ - "mytool/1.0#85d927a4a067a531b1a9c7619522c015%1702683583.3411012", - "othertool/1.0#fd2b006646a54397c16a1478ac4111ac%1702683583.3544693" - ] + f"conanfile.py": GenConanfile() + .with_requires(f"liba/[>=1.0 <2]") + .with_requires("libb/[<1.2]") + .with_tool_requires("libc/1.0") } - """) - c.save({"conan.lock": lock}) - c.run(f"lock upgrade --requires={old}") + ) + + c.run("lock create .") lock = c.load("conan.lock") - assert old not in lock - assert new in lock + assert "liba/1.0" in lock + assert "libb/1.0" in lock + assert "libc/1.0" in lock + + c.run(f"export liba --version=1.9") + c.run(f"export libb --version=1.1") + c.run(f"export libb --version=1.2") + c.run(f"export libc --version=1.1") + c.run("lock upgrade . --update-requires=liba/* --update-requires=libb/[*] --update-build-requires=libc/[*]") + # TODO not working with --update-requires=libb/1.1 should it? + lock = c.load("conan.lock") + assert "liba/1.9" in lock + assert "libb/1.1" in lock + assert "libc/1.0" in lock + + From 214b30b27ffc11b60ec60ed9e2c30651f727add4 Mon Sep 17 00:00:00 2001 From: PerseoGI Date: Thu, 16 Jan 2025 10:05:29 +0100 Subject: [PATCH 4/5] Updated based on threads --- conan/cli/commands/lock.py | 6 +-- .../lockfile/test_user_overrides.py | 44 ++++++++++++++++++- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/conan/cli/commands/lock.py b/conan/cli/commands/lock.py index 5b52e208764..5dc13b84b58 100644 --- a/conan/cli/commands/lock.py +++ b/conan/cli/commands/lock.py @@ -60,7 +60,7 @@ def lock_create(conan_api, parser, subparser, *args): clean=args.lockfile_clean) conanfile_path = os.path.dirname(graph.root.path) \ if graph.root.path and args.lockfile_out is None else cwd - conan_api.lockfile.save_lockfile(lockfile, args.lockfile_out or LOCKFILE, conanfile_path) + conan_api.lockfile.save_lockfile(lockfile, args.lockfile_out or "conan.lock", conanfile_path) @conan_subcommand() @@ -187,7 +187,7 @@ def lock_update(conan_api, parser, subparser, *args): @conan_subcommand() def lock_upgrade(conan_api, parser, subparser, *args): """ - Upgrade requires, build-requires or python-requires from an existing lockfile given a conanfile + (Experimental) Upgrade requires, build-requires or python-requires from an existing lockfile given a conanfile or a reference. """ common_graph_args(subparser) @@ -244,4 +244,4 @@ def lock_upgrade(conan_api, parser, subparser, *args): lockfile = conan_api.lockfile.update_lockfile(lockfile, graph, args.lockfile_packages, clean=args.lockfile_clean) - conan_api.lockfile.save_lockfile(lockfile, args.lockfile_out or LOCKFILE) + conan_api.lockfile.save_lockfile(lockfile, args.lockfile_out or "conan.lock") diff --git a/test/integration/lockfile/test_user_overrides.py b/test/integration/lockfile/test_user_overrides.py index 37aa764c579..0152eb1e862 100644 --- a/test/integration/lockfile/test_user_overrides.py +++ b/test/integration/lockfile/test_user_overrides.py @@ -4,7 +4,7 @@ import pytest from conan.test.assets.genconanfile import GenConanfile -from conan.test.utils.tools import TestClient +from conan.test.utils.tools import NO_SETTINGS_PACKAGE_ID, TestClient def test_user_overrides(): @@ -420,4 +420,46 @@ def test_lock_upgrade_path(self): assert "libb/1.1" in lock assert "libc/1.0" in lock + def test_lock_upgrade_transitives(self): + c = TestClient(light=True) + c.save({"liba/conanfile.py": GenConanfile("liba"), + "libb/conanfile.py": GenConanfile("libb").with_requires("libc/[<2.0]"), + "libc/conanfile.py": GenConanfile("libc")}) + c.run(f"export liba --version=1.0") + c.run(f"export libc --version=1.0") + c.run(f"create libb --version=1.0 --build=missing") + c.save( + { + f"conanfile.py": GenConanfile() + .with_requires(f"liba/[>=1.0 <2]") + .with_requires("libb/[<1.2]") + } + ) + c.run("lock create .") + lock = c.load("conan.lock") + assert "liba/1.0" in lock + assert "libb/1.0" in lock + assert "libc/1.0" in lock + + c.run(f"export libc --version=1.1") + c.save({"libb/conanfile.py": GenConanfile("libb").with_requires("libc/[>1.0]")}) + c.run(f"create libb --version=1.1 --build=missing") + c.run("lock upgrade . --update-requires=libb/[*]") + lock = c.load("conan.lock") + assert "liba/1.0" in lock + assert "libb/1.1" in lock + assert "libc/1.1" in lock # TODO + + c.save({"libb/conanfile.py": GenConanfile("libb").with_requires("libc/[<2.0]").with_requires("libd/[<2.0]"), + "libd/conanfile.py": GenConanfile("libd")}) + + c.run(f"export libd --version=1.0") + c.run(f"create libb --version=1.1 --build=missing") + c.run("lock upgrade . --update-requires=libb/[*]") + + lock = c.load("conan.lock") + assert "liba/1.0" in lock + assert "libb/1.1" in lock + assert "libc/1.0" in lock + assert "libd/1.0" in lock From 36b7aa0b8e012444488a6c68e7ae88ee2f9e74a2 Mon Sep 17 00:00:00 2001 From: PerseoGI Date: Thu, 16 Jan 2025 13:54:55 +0100 Subject: [PATCH 5/5] Added --transitive options and many tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Abril Rincón Blanco --- conan/cli/commands/lock.py | 88 ++++++++++++----- .../lockfile/test_user_overrides.py | 96 ++++++++++++++++--- 2 files changed, 143 insertions(+), 41 deletions(-) diff --git a/conan/cli/commands/lock.py b/conan/cli/commands/lock.py index 5dc13b84b58..efb623b8aa2 100644 --- a/conan/cli/commands/lock.py +++ b/conan/cli/commands/lock.py @@ -1,3 +1,4 @@ +from collections import defaultdict import os from conan.api.output import ConanOutput @@ -9,6 +10,8 @@ from conan.internal.model.lockfile import Lockfile, LOCKFILE from conan.internal.model.recipe_ref import RecipeReference from conan.errors import ConanException +from conan.internal.model.version import Version +from conan.internal.model.version_range import VersionRange @conan_command(group="Consumer") @@ -190,18 +193,23 @@ def lock_upgrade(conan_api, parser, subparser, *args): (Experimental) Upgrade requires, build-requires or python-requires from an existing lockfile given a conanfile or a reference. """ - common_graph_args(subparser) - subparser.add_argument('--update-requires', action="append", - help='Update requires from lockfile') - subparser.add_argument('--update-build-requires', action="append", - help='Update build-requires from lockfile') - subparser.add_argument('--update-python-requires', action="append", - help='Update python-requires from lockfile') - subparser.add_argument('--update-config-requires', action="append", - help='Update config-requires from lockfile') - subparser.add_argument("--build-require", action='store_true', default=False, - help='Whether the provided reference is a build-require') + """ + TODOs: + - [ ] args.update should always be True? + - [x] manage input version ranges + - [ ] Consider a new option to enable transitive dependencies on different kinds + (eg. --transitive=all, --transitive=python-requires, ...) + - [ ] Improve the detection of range versions + """ + + common_graph_args(subparser) + subparser.add_argument('--update-requires', action="append", help='Update requires from lockfile') + subparser.add_argument('--update-build-requires', action="append", help='Update build-requires from lockfile') + subparser.add_argument('--update-python-requires', action="append", help='Update python-requires from lockfile') + subparser.add_argument('--update-config-requires', action="append", help='Update config-requires from lockfile') + subparser.add_argument('--build-require', action='store_true', default=False, help='Whether the provided reference is a build-require') + subparser.add_argument('--transitives', action='store_true', default=False, help='Upgrade also transitive dependencies') args = parser.parse_args(*args) # parameter validation @@ -218,24 +226,52 @@ def lock_upgrade(conan_api, parser, subparser, *args): lockfile = conan_api.lockfile.get_lockfile(lockfile=args.lockfile, conanfile_path=path, cwd=cwd, partial=True, overrides=overrides) profile_host, profile_build = conan_api.profiles.get_profiles_from_args(args) + + def expand_graph(): + if path: + return conan_api.graph.load_graph_consumer(path, args.name, args.version, + args.user, args.channel, + profile_host, profile_build, lockfile, + remotes, args.update, + is_build_require=args.build_require) + return conan_api.graph.load_graph_requires(args.requires, args.tool_requires, + profile_host, profile_build, lockfile, + remotes, args.update) + requested_update = {"requires": args.update_requires or [], + "build_requires": args.update_build_requires or [], + "python_requires": args.update_python_requires or [], + "config_requires": args.update_config_requires or []} + if args.transitives: + def is_version_range(ref: str): + # TODO: use conan's built in + return ref.find('[') != -1 or ref.find('*') != -1 + + def match_version(ref, node): + if is_version_range(ref): + return VersionRange(str(RecipeReference.loads(ref).version)).contains(Version(node.ref), resolve_prerelease=True) + else: + return node.ref.matches(ref, is_consumer=None) + updatable_deps = defaultdict(list) + for node in expand_graph().nodes: + if not node.ref: + continue + for kind, deps in requested_update.items(): + if any(match_version(ref, node) for ref in deps): + updatable_deps[kind].append(node.ref.repr_notime()) + for dep in node.conanfile.dependencies.values(): + # TODO: dependencies.build.values() -> + # dep.context # host or build require (?) + updatable_deps[kind].append(dep.ref.repr_notime()) + else: + updatable_deps = requested_update # Remove the lockfile entries that will be updated lockfile = conan_api.lockfile.remove_lockfile(lockfile, - requires=args.update_requires, - python_requires=args.update_python_requires, - build_requires=args.update_build_requires, - config_requires=args.update_config_requires) + requires=updatable_deps["requires"], + python_requires=updatable_deps["python_requires"], + build_requires=updatable_deps["build_requires"], + config_requires=updatable_deps["config_requires"]) # Resolve new graph - if path: - graph = conan_api.graph.load_graph_consumer(path, args.name, args.version, - args.user, args.channel, - profile_host, profile_build, lockfile, - remotes, args.update, # TODO: args.update should be True?? - is_build_require=args.build_require) - else: - graph = conan_api.graph.load_graph_requires(args.requires, args.tool_requires, - profile_host, profile_build, lockfile, - remotes, args.update) - + graph = expand_graph() print_graph_basic(graph) graph.report_graph_error() conan_api.graph.analyze_binaries(graph, args.build, remotes=remotes, update=args.update, diff --git a/test/integration/lockfile/test_user_overrides.py b/test/integration/lockfile/test_user_overrides.py index 0152eb1e862..a48a695f1a5 100644 --- a/test/integration/lockfile/test_user_overrides.py +++ b/test/integration/lockfile/test_user_overrides.py @@ -365,7 +365,7 @@ def test_lock_update(self, kind, old, new): class TestLockUpgrade: @pytest.mark.parametrize("kind, pkg, old, new", [ ("requires", "math", "math/1.0", "math/1.1"), - # ("build-requires", "cmake", "cmake/1.0", "cmake/1.1"), # TODO there is not a --build-requires + ("build-requires", "cmake", "cmake/1.0", "cmake/1.1"), # TODO there is not a --build-requires # ("python-requires", "mytool", "mytool/1.0", "mytool/1.1"), # TODO nor a --python-requires ]) def test_lock_upgrade(self, kind, pkg, old, new): @@ -374,14 +374,18 @@ def test_lock_upgrade(self, kind, pkg, old, new): c.run(f"export {pkg} --version=1.0") rev0 = c.exported_recipe_revision() - c.run(f"lock create --{kind}={pkg}/[*]") + kind_create = "tool-requires" if "build-requires" == kind else kind + c.run(f"lock create --{kind_create}={pkg}/[*]") lock = c.load("conan.lock") assert f"{old}#{rev0}" in lock c.run(f"export {pkg} --version=1.1") rev1 = c.exported_recipe_revision() - c.run(f"lock upgrade --{kind}={pkg}/[*] --update-{kind}={pkg}/[*]") + c.run(f"lock upgrade --{kind_create}={pkg}/[*] --update-{kind}={pkg}/[*]") + # c.run(f"lock upgrade --{kind_create}={pkg}/[*] --update-{kind}={pkg}/[*] --transitives") # TODO this is failing because of [] + # c.run(f"lock upgrade --{kind_create}={pkg}/[*] --update-{kind}={old} --transitives") lock = c.load("conan.lock") + print(lock) assert f"{old}#{rev0}" not in lock assert f"{new}#{rev1}" in lock @@ -390,16 +394,19 @@ def test_lock_upgrade_path(self): c = TestClient(light=True) c.save({"liba/conanfile.py": GenConanfile("liba"), "libb/conanfile.py": GenConanfile("libb"), - "libc/conanfile.py": GenConanfile("libc")}) + "libc/conanfile.py": GenConanfile("libc"), + "libd/conanfile.py": GenConanfile("libd")}) c.run(f"export liba --version=1.0") c.run(f"export libb --version=1.0") c.run(f"export libc --version=1.0") + c.run(f"export libd --version=1.0") c.save( { f"conanfile.py": GenConanfile() .with_requires(f"liba/[>=1.0 <2]") .with_requires("libb/[<1.2]") - .with_tool_requires("libc/1.0") + .with_tool_requires("libc/[>=1.0]") + .with_python_requires("libd/[>=1.0 <1.2]") } ) @@ -408,17 +415,27 @@ def test_lock_upgrade_path(self): assert "liba/1.0" in lock assert "libb/1.0" in lock assert "libc/1.0" in lock + assert "libd/1.0" in lock + # Check versions are updated accordingly c.run(f"export liba --version=1.9") c.run(f"export libb --version=1.1") c.run(f"export libb --version=1.2") c.run(f"export libc --version=1.1") - c.run("lock upgrade . --update-requires=liba/* --update-requires=libb/[*] --update-build-requires=libc/[*]") - # TODO not working with --update-requires=libb/1.1 should it? + c.run(f"export libd --version=1.1") + c.run("lock upgrade . --update-requires=liba/1.0 --update-requires=libb/[*] --update-build-requires=libc/[*] --update-python-requires=libd/1.0") lock = c.load("conan.lock") assert "liba/1.9" in lock assert "libb/1.1" in lock - assert "libc/1.0" in lock + assert "libc/1.1" in lock + assert "libd/1.1" in lock + + # Check version conanfile version range is respected + c.run(f"export libd --version=1.2") + c.run("lock upgrade . --update-python-requires=libd/*") + lock = c.load("conan.lock") + assert "libd/1.1" in lock + assert "libd/1.2" not in lock def test_lock_upgrade_transitives(self): c = TestClient(light=True) @@ -435,31 +452,80 @@ def test_lock_upgrade_transitives(self): .with_requires("libb/[<1.2]") } ) - c.run("lock create .") lock = c.load("conan.lock") assert "liba/1.0" in lock assert "libb/1.0" in lock assert "libc/1.0" in lock + # Check if transitive dependencies of libb (libc) also gets updated c.run(f"export libc --version=1.1") - c.save({"libb/conanfile.py": GenConanfile("libb").with_requires("libc/[>1.0]")}) c.run(f"create libb --version=1.1 --build=missing") - c.run("lock upgrade . --update-requires=libb/[*]") + c.run("lock upgrade . --update-requires=libb/1.0 --transitives") lock = c.load("conan.lock") assert "liba/1.0" in lock assert "libb/1.1" in lock - assert "libc/1.1" in lock # TODO + assert "libc/1.1" in lock + # Check if new transitive dependency is added -> this should work even without --transitive c.save({"libb/conanfile.py": GenConanfile("libb").with_requires("libc/[<2.0]").with_requires("libd/[<2.0]"), "libd/conanfile.py": GenConanfile("libd")}) - c.run(f"export libd --version=1.0") c.run(f"create libb --version=1.1 --build=missing") - c.run("lock upgrade . --update-requires=libb/[*]") + c.run("lock upgrade . --update-requires=libb/1.1") + lock = c.load("conan.lock") + assert "liba/1.0" in lock + assert "libb/1.1" in lock + assert "libc/1.1" in lock + assert "libd/1.0" in lock + # Check double transitive upgrade + c.save({"libb/conanfile.py": GenConanfile("libb").with_requires("libc/[<2.0]"), + "libc/conanfile.py": GenConanfile("libc").with_requires("libd/[<2.0]"), + "libd/conanfile.py": GenConanfile("libd")}) + c.run(f"export libd --version=1.0") + c.run(f"create libc --version=1.1 --build=missing") + c.run(f"create libb --version=1.1 --build=missing") + c.run("lock upgrade . --update-requires=libb/1.1 --transitives") lock = c.load("conan.lock") assert "liba/1.0" in lock assert "libb/1.1" in lock - assert "libc/1.0" in lock + assert "libc/1.1" in lock assert "libd/1.0" in lock + c.run(f"export libd --version=1.1") + c.run("lock upgrade . --update-requires=libb/1.1 --transitives") + lock = c.load("conan.lock") + assert "liba/1.0" in lock + assert "libb/1.1" in lock + assert "libc/1.1" in lock + assert "libd/1.1" in lock + + def test_lock_upgrade_build_transitives(self): + c = TestClient(light=True) + c.save({"liba/conanfile.py": GenConanfile("liba"), + "libb/conanfile.py": GenConanfile("libb").with_build_requires("libc/[<2.0]"), + "libc/conanfile.py": GenConanfile("libc").with_requires("libd/[<2.0]"), + "libd/conanfile.py": GenConanfile("libd")}) + c.run(f"export libd --version=1.0") + c.run(f"create libc --version=1.0 --build=missing") + c.run(f"create libb --version=1.0 --build=missing") + c.run(f"export liba --version=1.0") + c.save( + { + f"conanfile.py": GenConanfile() + .with_requires(f"liba/[>=1.0 <2]") + .with_build_requires("libb/[<1.2]") + } + ) + c.run("lock create .") + c.run(f"export libd --version=1.1") + c.run(f"create libc --version=1.1 --build=missing") + c.run(f"create libb --version=1.1 --build=missing") + c.run("lock upgrade . --update-build-requires=libb/1.0 --transitives") + lock = c.load("conan.lock") + print(c.out) + print(lock) + assert "liba/1.0" in lock + assert "libb/1.1" in lock + assert "libc/1.1" in lock + # assert "libd/1.1" in lock # TODO is failing -> we are not getting right the build_requires transitives