Skip to content

Commit

Permalink
Fix cppstd_compat migration for 2.12 (#17647)
Browse files Browse the repository at this point in the history
* Remove cppstdcompat if guarded, warn otherwise, move to main compatibility file

* Conan config clean is better if it regenerates default files

* Add migration test for compatibility

* Fix conan config clean test

* Remove initial try

* Franchu was right

* Move to its own RP

* wip

* fix test

* fixed tests

---------

Co-authored-by: memsharded <[email protected]>
  • Loading branch information
AbrilRBS and memsharded authored Jan 28, 2025
1 parent e90cfa2 commit bd03e22
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 40 deletions.
85 changes: 47 additions & 38 deletions conans/client/graph/compatibility.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,49 @@
import os
from collections import OrderedDict

from conan.api.output import ConanOutput
from conan.internal.cache.home_paths import HomePaths
from conans.client.graph.compute_pid import run_validate_package_id
from conans.client.loader import load_python_file
from conan.internal.errors import conanfile_exception_formatter, scoped_traceback
from conan.errors import ConanException
from conans.client.migrations import CONAN_GENERATED_COMMENT

# TODO: Define other compatibility besides applications
from conans.util.files import load, save

_default_compat = """\
# This file was generated by Conan. Remove this comment if you edit this file or Conan
# will destroy your changes.
from cppstd_compat import cppstd_compat
from conan.tools.build import supported_cppstd, supported_cstd
from conan.errors import ConanException
def cppstd_compat(conanfile):
# It will try to find packages with all the cppstd versions
extension_properties = getattr(conanfile, "extension_properties", {})
compiler = conanfile.settings.get_safe("compiler")
compiler_version = conanfile.settings.get_safe("compiler.version")
cppstd = conanfile.settings.get_safe("compiler.cppstd")
if not compiler or not compiler_version:
return []
factors = [] # List of list, each sublist is a potential combination
if cppstd is not None and extension_properties.get("compatibility_cppstd") is not False:
cppstd_possible_values = supported_cppstd(conanfile)
if cppstd_possible_values is None:
conanfile.output.warning(f'No cppstd compatibility defined for compiler "{compiler}"')
else: # The current cppst must be included in case there is other factor
factors.append([{"compiler.cppstd": v} for v in cppstd_possible_values])
cstd = conanfile.settings.get_safe("compiler.cstd")
if cstd is not None and extension_properties.get("compatibility_cstd") is not False:
cstd_possible_values = supported_cstd(conanfile)
if cstd_possible_values is None:
conanfile.output.warning(f'No cstd compatibility defined for compiler "{compiler}"')
else:
factors.append([{"compiler.cstd": v} for v in cstd_possible_values if v != cstd])
return factors
def compatibility(conanfile):
Expand Down Expand Up @@ -52,47 +84,24 @@ def _factors_combinations(factors):
"""


_default_cppstd_compat = """\
# This file was generated by Conan. Remove this comment if you edit this file or Conan
# will destroy your changes.
from conan.tools.build import supported_cppstd, supported_cstd
from conan.errors import ConanException
def cppstd_compat(conanfile):
# It will try to find packages with all the cppstd versions
extension_properties = getattr(conanfile, "extension_properties", {})
compiler = conanfile.settings.get_safe("compiler")
compiler_version = conanfile.settings.get_safe("compiler.version")
cppstd = conanfile.settings.get_safe("compiler.cppstd")
if not compiler or not compiler_version:
return []
factors = [] # List of list, each sublist is a potential combination
if cppstd is not None and extension_properties.get("compatibility_cppstd") is not False:
cppstd_possible_values = supported_cppstd(conanfile)
if cppstd_possible_values is None:
conanfile.output.warning(f'No cppstd compatibility defined for compiler "{compiler}"')
else: # The current cppst must be included in case there is other factor
factors.append([{"compiler.cppstd": v} for v in cppstd_possible_values])
cstd = conanfile.settings.get_safe("compiler.cstd")
if cstd is not None and extension_properties.get("compatibility_cstd") is not False:
cstd_possible_values = supported_cstd(conanfile)
if cstd_possible_values is None:
conanfile.output.warning(f'No cstd compatibility defined for compiler "{compiler}"')
else:
factors.append([{"compiler.cstd": v} for v in cstd_possible_values if v != cstd])
return factors
"""


def migrate_compatibility_files(cache_folder):
from conans.client.migrations import update_file
compatible_folder = HomePaths(cache_folder).compatibility_plugin_path
compatibility_file = os.path.join(compatible_folder, "compatibility.py")
cppstd_compat_file = os.path.join(compatible_folder, "cppstd_compat.py")
update_file(compatibility_file, _default_compat)
update_file(cppstd_compat_file, _default_cppstd_compat)

def _should_migrate_file(file_path):
if not os.path.exists(file_path):
return True
content = load(file_path)
first_line = content.lstrip().split("\n", 1)[0]
return CONAN_GENERATED_COMMENT in first_line

if _should_migrate_file(compatibility_file) and _should_migrate_file(cppstd_compat_file):
if os.path.exists(compatibility_file) and load(compatibility_file) != _default_compat:
ConanOutput().success("Migration: Successfully updated compatibility.py}")
save(compatibility_file, _default_compat)
if os.path.exists(cppstd_compat_file):
os.remove(cppstd_compat_file)


class BinaryCompatibility:
Expand Down
44 changes: 42 additions & 2 deletions test/integration/test_migrations.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ def _drop_lru_column(db_folder):

@pytest.mark.parametrize(["plugin_path", "string_replace", "new_string"],
[("profile.py", "msvc", "EME_ESE_VC"),
("compatibility/compatibility.py", "conanfile", "conian_file"),
("compatibility/cppstd_compat.py", "conanfile", "conian_file")])
("compatibility/compatibility.py", "conanfile", "conian_file")])
def test_migration_profile_checker_plugin(plugin_path, string_replace, new_string):
t = TestClient()
# Any command that checks the package cache generates the DB
Expand Down Expand Up @@ -151,3 +150,44 @@ def test_back_default_compatibility_migration():
with patch('conan.api.conan_api.ClientMigrator', new=lambda *args, **kwargs: migrator):
t.run("-v") # Fire the backward migration
assert f"WARN: Downgrading cache from Conan {conan_version} to 2.3.2" in t.out


class TestMigrationCppstdCompat:
def test_migration(self):
t = TestClient()
t.run("-v")
cppstd_compat_path = "extensions/plugins/compatibility/cppstd_compat.py"
compatibility_path = "extensions/plugins/compatibility/compatibility.py"

# both files exist and not modified
t.save_home({"version.txt": "2.11"})
t.run("-v")
assert "def cppstd_compat(conanfile)" in t.load_home(compatibility_path)
assert not os.path.exists(os.path.join(t.cache_folder, cppstd_compat_path))

def test_cppstd_modified(self):
t = TestClient()
t.run("-v")
cppstd_compat_path = "extensions/plugins/compatibility/cppstd_compat.py"
compatibility_path = "extensions/plugins/compatibility/compatibility.py"
# cppstd_compat modified
t.save_home({"version.txt": "2.11",
compatibility_path: "# This file was generated by Conan",
cppstd_compat_path: "custom file content"})
t.run("-v")
assert t.load_home(cppstd_compat_path) == "custom file content"
# compatibility not migrated, keeps the old content
assert "def cppstd_compat(conanfile)" not in t.load_home(compatibility_path)

def test_compatibility_modified(self):
t = TestClient()
t.run("-v")
cppstd_compat_path = "extensions/plugins/compatibility/cppstd_compat.py"
compatibility_path = "extensions/plugins/compatibility/compatibility.py"
t.save_home({"version.txt": "2.11",
cppstd_compat_path: "# This file was generated by Conan POTATO",
compatibility_path: "Modified file"})
t.run("-v")
assert t.load_home(compatibility_path) == "Modified file"
# not Removed because compatibility was modified
assert "POTATO" in t.load_home(cppstd_compat_path)

0 comments on commit bd03e22

Please sign in to comment.