Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix cppstd_compat migration for 2.12 #17647

Merged
Merged
2 changes: 1 addition & 1 deletion conan/api/subapi/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ def clean(self):
packages_folder = self.global_conf.get("core.cache:storage_path") or os.path.join(self.home(), "p")
for content in contents:
content_path = os.path.join(self.home(), content)
if content_path == packages_folder or content == "version.txt":
if content_path == packages_folder:
AbrilRBS marked this conversation as resolved.
Show resolved Hide resolved
continue
ConanOutput().debug(f"Removing {content_path}")
if os.path.isdir(content_path):
Expand Down
72 changes: 35 additions & 37 deletions conans/client/graph/compatibility.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,47 @@
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 remove_if_guarded

# TODO: Define other compatibility besides applications
_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 @@ -51,48 +81,16 @@ def _factors_combinations(factors):
return combinations
"""


_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)
if not remove_if_guarded(cppstd_compat_file):
ConanOutput().warning('The "cppstd_compat.py" file is deprecated. Its contents have been moved '
'to the "compatibility.py" file. Please remove it from your project, '
'carrying over any modifications to the new location.')


class BinaryCompatibility:
Expand Down
24 changes: 23 additions & 1 deletion conans/client/migrations.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from conan.internal.default_settings import migrate_settings_file
from conans.migrations import Migrator
from conans.util.dates import timestamp_now
from conans.util.files import load, save
from conans.util.files import load, save, remove

CONAN_GENERATED_COMMENT = "This file was generated by Conan"

Expand All @@ -33,6 +33,28 @@ def update_file(file_path, new_content):
save(file_path, new_content)
out.success(f"Migration: Successfully updated {file_name}")

def remove_if_guarded(file_path):
"""
Remove the file if it contains the ``CONAN_GENERATED_COMMENT``.

:param file_path: ``str`` path to the file.
"""
out = ConanOutput()
file_name = os.path.basename(file_path)

if not os.path.exists(file_path):
return True

content = load(file_path)

first_line = content.lstrip().split("\n", 1)[0]

if CONAN_GENERATED_COMMENT in first_line:
remove(file_path)
out.success(f"Migration: Successfully removed {file_name}")
return True
return False


class ClientMigrator(Migrator):

Expand Down
3 changes: 2 additions & 1 deletion test/integration/command/config_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,8 @@ def test_config_clean(storage_path):
assert "bar" not in tc.out
tc.run("config show core.upload:retry")
assert "7" not in tc.out
assert not os.path.exists(os.path.join(tc.cache_folder, "extensions"))
assert os.path.exists(os.path.join(tc.cache_folder, "extensions"))
assert not os.path.exists(os.path.join(tc.cache_folder, "extensions", "compatibility", "mycomp.py"))
AbrilRBS marked this conversation as resolved.
Show resolved Hide resolved
assert os.path.exists(absolut_storage_path)


Expand Down
26 changes: 26 additions & 0 deletions test/integration/test_migrations.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,29 @@ 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

def test_migration_cppstd_compat_different_file():
t = TestClient()
t.run("-v")
assert 'WARN: The "cppstd_compat.py" file is deprecated' not in t.out

version_txt_file_path = os.path.join(t.cache_folder, "version.txt")
save(version_txt_file_path, "2.11")

cppstd_compat_file = os.path.join(t.cache_folder, "extensions", "plugins", "compatibility", "cppstd_compat.py")
save(cppstd_compat_file, "custom file content")

t.run("-v")
assert 'WARN: The "cppstd_compat.py" file is deprecated' in t.out
# Exists because it was not the default one
assert os.path.exists(cppstd_compat_file)

save(version_txt_file_path, "2.11")
cppstd_compat_file = os.path.join(t.cache_folder, "extensions", "plugins", "compatibility",
"cppstd_compat.py")
save(cppstd_compat_file, "# This file was generated by Conan. Remove this comment if you edit this file or Conan")

t.run("-v")
assert 'Migration: Successfully removed cppstd_compat.py' in t.out
# Removed because it was the default
assert not os.path.exists(cppstd_compat_file)
Loading