From 09daa19b7b01c27391cdd7fd015c520651bbfb7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abril=20Rinc=C3=B3n=20Blanco?= Date: Thu, 21 Aug 2025 11:37:32 +0200 Subject: [PATCH 1/5] Add failing test for double build of compatible packages in test-package --- .../integration/package_id/compatible_test.py | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/test/integration/package_id/compatible_test.py b/test/integration/package_id/compatible_test.py index b224f2f8406..ce2ee376640 100644 --- a/test/integration/package_id/compatible_test.py +++ b/test/integration/package_id/compatible_test.py @@ -670,6 +670,39 @@ def validate_build(self): assert pkga["info"]["compatibility_delta"] == {"settings": [["compiler.cppstd", "14"]]} assert pkga["build_args"] == "--requires=liba/0.1 --build=compatible:liba/0.1" + def test_compatible_build_test_package(self): + tc = TestClient() + tc.save({"conanfile.py": textwrap.dedent(""" + from conan import ConanFile + from conan.tools.build import check_min_cppstd + + class Pkg(ConanFile): + name = "pkg" + version = "0.1" + settings = "compiler" + + def validate(self): + check_min_cppstd(self, 17) + """), + "test_package/conanfile.py": textwrap.dedent(""" + from conan import ConanFile + + class TestPkg(ConanFile): + settings = "compiler" + def requirements(self): + self.requires(self.tested_reference_str) + def test(self): + pass + """)}) + + tc.run("create . -s compiler.cppstd=14 --build=compatible") + # Without checking if the compatibles are already in the cache, it will build twice, + # once for the package and once for the test_package + assert tc.out.count("pkg/0.1#2e52358a1684ca0bd32277b2630f0080:0b26ee249577d4b7c1e52c7ce646c0b5eb611dfb - Build") == 1 + tc.run("create . -s compiler.cppstd=17 --build=compatible") + # This would fail because the package revision is now also included + #assert tc.out.count("pkg/0.1#2e52358a1684ca0bd32277b2630f0080:0b26ee249577d4b7c1e52c7ce646c0b5eb611dfb - Cache") == 2 + def test_compatibility_new_setting_forwards_compat(): """ This test tries to reflect the following scenario: From fcbd05ec2cdfc188a35d1e36ad47a29b4d6c817d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abril=20Rinc=C3=B3n=20Blanco?= Date: Thu, 21 Aug 2025 11:57:25 +0200 Subject: [PATCH 2/5] Sketch how a solution might look like --- conan/internal/graph/graph_binaries.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/conan/internal/graph/graph_binaries.py b/conan/internal/graph/graph_binaries.py index 128e796464b..faf0cf2d106 100644 --- a/conan/internal/graph/graph_binaries.py +++ b/conan/internal/graph/graph_binaries.py @@ -194,7 +194,16 @@ def _find_build_compatible_binary(self, node, compatibles): if not compatible.cant_build: node._package_id = pkg_id # Modifying package id under the hood, FIXME self._compatible_found(node.conanfile, pkg_id, compatible) - node.binary = BINARY_BUILD + # We might have the compatible package in the cache, + # check for it before, and then build it if not found + cache_latest_prev = self._cache.get_latest_package_reference(node.pref) + if cache_latest_prev is None: + node.binary = BINARY_BUILD + else: + node.binary = BINARY_CACHE + node.binary_remote = None + node.prev = cache_latest_prev.revision + node.pref_timestamp = cache_latest_prev.timestamp return node.binary = original_binary node._package_id = original_package_id From bd3891b6a4c7d4b134a3a0e5b009981e7a04673e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abril=20Rinc=C3=B3n=20Blanco?= Date: Tue, 7 Oct 2025 01:31:22 +0200 Subject: [PATCH 3/5] Disallow more than compatible for the binary you're building --- conan/internal/graph/build_mode.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/conan/internal/graph/build_mode.py b/conan/internal/graph/build_mode.py index 042652007f8..315b06c0386 100644 --- a/conan/internal/graph/build_mode.py +++ b/conan/internal/graph/build_mode.py @@ -56,6 +56,9 @@ def __init__(self, params): else: self.patterns.append(clean_pattern) + if self._build_compatible_patterns and self._build_compatible_patterns != ["&"]: + raise ConanException("compatible build mode only supports --build=compatible:& syntax for now") + if self.never and (self.missing or self.patterns or self.cascade): raise ConanException("--build=never not compatible with other options") From 80c5b6cda260c01ff829309a80cc32db4159a401 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abril=20Rinc=C3=B3n=20Blanco?= Date: Tue, 7 Oct 2025 01:37:21 +0200 Subject: [PATCH 4/5] Skip old tests, dont remove them so we can go back once that syntaxt is supported againk --- test/integration/command/info/test_info_build_order.py | 3 ++- test/integration/package_id/compatible_test.py | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/test/integration/command/info/test_info_build_order.py b/test/integration/command/info/test_info_build_order.py index 4a39b80e6f9..f0c11d1a13e 100644 --- a/test/integration/command/info/test_info_build_order.py +++ b/test/integration/command/info/test_info_build_order.py @@ -821,6 +821,7 @@ def test_build_order_space_in_options(): assert order["order"][0][0]["build_args"] == '''--requires=dep/1.0 --build=dep/1.0 -o="dep/*:extras=cxx="yes" gnuext='no'" -o="dep/*:flags=define=FOO define=BAR define=BAZ"''' +@pytest.mark.skip(reason="Unsupported --build=compatible syntax") def test_build_order_build_context_compatible(): c = TestClient() foo = textwrap.dedent(""" @@ -887,7 +888,7 @@ def test_info_build_order_editable(): "consumer/conanfile.txt": "[requires]\npkg/0.1"}) c.run("editable add dep") c.run("export pkg") - + c.run("graph build-order consumer --build=missing --build=editable -f=json --order-by=recipe") bo_json = json.loads(c.stdout) pkg = bo_json["order"][0][0]["packages"][0][0] diff --git a/test/integration/package_id/compatible_test.py b/test/integration/package_id/compatible_test.py index ce2ee376640..a8e59b56133 100644 --- a/test/integration/package_id/compatible_test.py +++ b/test/integration/package_id/compatible_test.py @@ -1,6 +1,8 @@ import json import textwrap +import pytest + from conan.test.utils.tools import TestClient, GenConanfile @@ -546,6 +548,7 @@ def validate_build(self): c.run("list *:*") assert "compiler.cppstd: 17" in c.out + @pytest.mark.skip(reason="Unsupported --build=compatible syntax") def test_multi_level_build_compatible(self): c = TestClient() conanfile = textwrap.dedent(""" @@ -583,6 +586,7 @@ def validate(self): c.run("list libb:*") assert "compiler.cppstd: 17" in c.out + @pytest.mark.skip(reason="Unsupported --build=compatible syntax") def test_multi_level_build_compatible_build_order(self): c = TestClient() conanfile = textwrap.dedent(""" @@ -670,6 +674,7 @@ def validate_build(self): assert pkga["info"]["compatibility_delta"] == {"settings": [["compiler.cppstd", "14"]]} assert pkga["build_args"] == "--requires=liba/0.1 --build=compatible:liba/0.1" + @pytest.mark.skip(reason="Unsupported --build=compatible syntax") def test_compatible_build_test_package(self): tc = TestClient() tc.save({"conanfile.py": textwrap.dedent(""" From 6cf625a1263fed9e6b7ff7f9b1c17bbaeadda36a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abril=20Rinc=C3=B3n=20Blanco?= Date: Tue, 7 Oct 2025 01:38:42 +0200 Subject: [PATCH 5/5] Revert fix --- conan/internal/graph/graph_binaries.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/conan/internal/graph/graph_binaries.py b/conan/internal/graph/graph_binaries.py index faf0cf2d106..128e796464b 100644 --- a/conan/internal/graph/graph_binaries.py +++ b/conan/internal/graph/graph_binaries.py @@ -194,16 +194,7 @@ def _find_build_compatible_binary(self, node, compatibles): if not compatible.cant_build: node._package_id = pkg_id # Modifying package id under the hood, FIXME self._compatible_found(node.conanfile, pkg_id, compatible) - # We might have the compatible package in the cache, - # check for it before, and then build it if not found - cache_latest_prev = self._cache.get_latest_package_reference(node.pref) - if cache_latest_prev is None: - node.binary = BINARY_BUILD - else: - node.binary = BINARY_CACHE - node.binary_remote = None - node.prev = cache_latest_prev.revision - node.pref_timestamp = cache_latest_prev.timestamp + node.binary = BINARY_BUILD return node.binary = original_binary node._package_id = original_package_id