From 4ce9f6c3524c02e9c6bce9840f6f2151ec78734d Mon Sep 17 00:00:00 2001 From: memsharded Date: Mon, 19 Feb 2024 21:41:55 +0100 Subject: [PATCH 01/15] wip --- conans/client/conanfile/implementations.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/conans/client/conanfile/implementations.py b/conans/client/conanfile/implementations.py index 133ddcea7e5..14cabc76032 100644 --- a/conans/client/conanfile/implementations.py +++ b/conans/client/conanfile/implementations.py @@ -17,3 +17,14 @@ def auto_shared_fpic_configure(conanfile): def auto_header_only_package_id(conanfile): if conanfile.options.get_safe("header_only") or conanfile.package_type is PackageType.HEADER: conanfile.info.clear() + + +def auto_language(conanfile): + if not conanfile.languages: + conanfile.settings.rm_safe("compiler.cstd") + return + if "C" not in conanfile.languages: + conanfile.settings.rm_safe("compiler.cstd") + if "C++" not in conanfile.languages: + conanfile.settings.rm_safe("compiler.cppstd") + conanfile.settings.rm_safe("compiler.libcxx") From a4e18e253e6bb9751b77b48e7ce16ea529be421d Mon Sep 17 00:00:00 2001 From: memsharded Date: Mon, 4 Mar 2024 17:26:16 +0100 Subject: [PATCH 02/15] wip --- conans/client/conanfile/configure.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/conans/client/conanfile/configure.py b/conans/client/conanfile/configure.py index 210e0f5f1bd..fb8f9064304 100644 --- a/conans/client/conanfile/configure.py +++ b/conans/client/conanfile/configure.py @@ -1,7 +1,8 @@ from conans.errors import conanfile_exception_formatter from conans.model.pkg_type import PackageType from conans.model.requires import BuildRequirements, TestRequirements, ToolRequirements -from conans.client.conanfile.implementations import auto_shared_fpic_config_options, auto_shared_fpic_configure +from conans.client.conanfile.implementations import auto_shared_fpic_config_options, \ + auto_shared_fpic_configure, auto_language def run_configure_method(conanfile, down_options, profile_options, ref): @@ -13,6 +14,8 @@ def run_configure_method(conanfile, down_options, profile_options, ref): elif "auto_shared_fpic" in conanfile.implements: auto_shared_fpic_config_options(conanfile) + auto_language(conanfile) # default implementation removes `compiler.cstd` + # Assign only the current package options values, but none of the dependencies is_consumer = conanfile._conan_is_consumer conanfile.options.apply_downstream(down_options, profile_options, ref, is_consumer) From 4ba80494d6254feb08cefad1ef24867b03064a45 Mon Sep 17 00:00:00 2001 From: memsharded Date: Fri, 5 Apr 2024 15:10:16 +0200 Subject: [PATCH 03/15] wip --- conan/tools/build/__init__.py | 2 + conan/tools/build/cstd.py | 165 ++++++++++++++++++ conan/tools/cmake/toolchain/blocks.py | 35 ++-- conans/client/conf/__init__.py | 6 +- conans/client/graph/compatibility.py | 43 +++-- conans/model/conan_file.py | 3 + .../package_id/test_cache_compatibles.py | 31 ++++ 7 files changed, 260 insertions(+), 25 deletions(-) create mode 100644 conan/tools/build/cstd.py diff --git a/conan/tools/build/__init__.py b/conan/tools/build/__init__.py index 9c77a3f4d5a..0ae8bc5f392 100644 --- a/conan/tools/build/__init__.py +++ b/conan/tools/build/__init__.py @@ -6,6 +6,8 @@ from conan.tools.build.flags import cppstd_flag from conan.tools.build.cppstd import check_max_cppstd, check_min_cppstd, \ valid_max_cppstd, valid_min_cppstd, default_cppstd, supported_cppstd +from conan.tools.build.cstd import check_max_cstd, check_min_cstd, \ + valid_max_cstd, valid_min_cstd, supported_cstd from conan.tools.build.cpu import build_jobs from conan.tools.build.cross_building import cross_building, can_run from conan.tools.build.stdcpp_library import stdcpp_library diff --git a/conan/tools/build/cstd.py b/conan/tools/build/cstd.py new file mode 100644 index 00000000000..346707129bd --- /dev/null +++ b/conan/tools/build/cstd.py @@ -0,0 +1,165 @@ +import operator + +from conan.errors import ConanInvalidConfiguration, ConanException +from conans.model.version import Version + + +def check_min_cstd(conanfile, cstd, gnu_extensions=False): + """ Check if current cstd fits the minimal version required. + + In case the current cstd doesn't fit the minimal version required + by cstd, a ConanInvalidConfiguration exception will be raised. + + 1. If settings.compiler.cstd, the tool will use settings.compiler.cstd to compare + 2. It not settings.compiler.cstd, the tool will use compiler to compare (reading the + default from cstd_default) + 3. If not settings.compiler is present (not declared in settings) will raise because it + cannot compare. + 4. If can not detect the default cstd for settings.compiler, a exception will be raised. + + :param conanfile: The current recipe object. Always use ``self``. + :param cstd: Minimal cstd version required + :param gnu_extensions: GNU extension is required (e.g gnu17) + """ + _check_cstd(conanfile, cstd, operator.lt, gnu_extensions) + + +def check_max_cstd(conanfile, cstd, gnu_extensions=False): + """ Check if current cstd fits the maximum version required. + + In case the current cstd doesn't fit the maximum version required + by cstd, a ConanInvalidConfiguration exception will be raised. + + 1. If settings.compiler.cstd, the tool will use settings.compiler.cstd to compare + 2. It not settings.compiler.cstd, the tool will use compiler to compare (reading the + default from cstd_default) + 3. If not settings.compiler is present (not declared in settings) will raise because it + cannot compare. + 4. If can not detect the default cstd for settings.compiler, a exception will be raised. + + :param conanfile: The current recipe object. Always use ``self``. + :param cstd: Maximum cstd version required + :param gnu_extensions: GNU extension is required (e.g gnu17) + """ + _check_cstd(conanfile, cstd, operator.gt, gnu_extensions) + + +def valid_min_cstd(conanfile, cstd, gnu_extensions=False): + """ Validate if current cstd fits the minimal version required. + + :param conanfile: The current recipe object. Always use ``self``. + :param cstd: Minimal cstd version required + :param gnu_extensions: GNU extension is required (e.g gnu17). This option ONLY works on Linux. + :return: True, if current cstd matches the required cstd version. Otherwise, False. + """ + try: + check_min_cstd(conanfile, cstd, gnu_extensions) + except ConanInvalidConfiguration: + return False + return True + + +def valid_max_cstd(conanfile, cstd, gnu_extensions=False): + """ Validate if current cstd fits the maximum version required. + + :param conanfile: The current recipe object. Always use ``self``. + :param cstd: Maximum cstd version required + :param gnu_extensions: GNU extension is required (e.g gnu17). This option ONLY works on Linux. + :return: True, if current cstd matches the required cstd version. Otherwise, False. + """ + try: + check_max_cstd(conanfile, cstd, gnu_extensions) + except ConanInvalidConfiguration: + return False + return True + + +def supported_cstd(conanfile, compiler=None, compiler_version=None): + """ + Get a list of supported ``compiler.cstd`` for the "conanfile.settings.compiler" and + "conanfile.settings.compiler_version" or for the parameters "compiler" and "compiler_version" + if specified. + + :param conanfile: The current recipe object. Always use ``self``. + :param compiler: Name of the compiler e.g: gcc + :param compiler_version: Version of the compiler e.g: 12 + :return: a list of supported ``cstd`` values. + """ + compiler = compiler or conanfile.settings.get_safe("compiler") + compiler_version = compiler_version or conanfile.settings.get_safe("compiler.version") + if not compiler or not compiler_version: + raise ConanException("Called supported_cstd with no compiler or no compiler.version") + + func = {"apple-clang": _apple_clang_supported_cstd, + "gcc": _gcc_supported_cstd, + "msvc": _msvc_supported_cstd, + "clang": _clang_supported_cstd, + }.get(compiler) + if func: + return func(Version(compiler_version)) + return None + + +def _check_cstd(conanfile, cstd, comparator, gnu_extensions): + """ Check if current cstd fits the version required according to a given comparator. + + In case the current cstd doesn't fit the maximum version required + by cstd, a ConanInvalidConfiguration exception will be raised. + + 1. If settings.compiler.cstd, the tool will use settings.compiler.cstd to compare + 2. It not settings.compiler.cstd, the tool will use compiler to compare (reading the + default from cstd_default) + 3. If not settings.compiler is present (not declared in settings) will raise because it + cannot compare. + 4. If can not detect the default cstd for settings.compiler, a exception will be raised. + + :param conanfile: The current recipe object. Always use ``self``. + :param cstd: Required cstd version. + :param comparator: Operator to use to compare the detected and the required cstd versions. + :param gnu_extensions: GNU extension is required (e.g gnu17) + """ + if not str(cstd).isdigit(): + raise ConanException("cstd parameter must be a number") + + def compare(lhs, rhs, comp): + def extract_cpp_version(_cstd): + return str(_cstd).replace("gnu", "") + + def add_millennium(_cstd): + return "19%s" % _cstd if _cstd == "99" else "20%s" % _cstd + + lhs = add_millennium(extract_cpp_version(lhs)) + rhs = add_millennium(extract_cpp_version(rhs)) + return not comp(lhs, rhs) + + current_cstd = conanfile.settings.get_safe("compiler.cstd") + if current_cstd is None: + raise ConanInvalidConfiguration("The compiler.cstd is not defined for this configuration") + + if gnu_extensions and "gnu" not in current_cstd: + raise ConanInvalidConfiguration("The cstd GNU extension is required") + + if not compare(current_cstd, cstd, comparator): + raise ConanInvalidConfiguration( + "Current cstd ({}) is {} than the required C standard ({}).".format( + current_cstd, "higher" if comparator == operator.gt else "lower", cstd)) + + +def _apple_clang_supported_cstd(version): + # TODO: Per-version support + return ["99", "gnu99", "11", "gnu11", "17", "gnu17", "23", "gnu23"] + + +def _gcc_supported_cstd(version): + # TODO: Per-version support + return ["99", "gnu99", "11", "gnu11", "17", "gnu17", "23", "gnu23"] + + +def _msvc_supported_cstd(version): + # TODO: Per-version support + return ["11", "17"] + + +def _clang_supported_cstd(version): + # TODO: Per-version support + return ["99", "gnu99", "11", "gnu11", "17", "gnu17", "23", "gnu23"] diff --git a/conan/tools/cmake/toolchain/blocks.py b/conan/tools/cmake/toolchain/blocks.py index 27a9c226056..b56344bce40 100644 --- a/conan/tools/cmake/toolchain/blocks.py +++ b/conan/tools/cmake/toolchain/blocks.py @@ -244,24 +244,39 @@ def context(self): class CppStdBlock(Block): template = textwrap.dedent(""" + {% if cppstd %} message(STATUS "Conan toolchain: C++ Standard {{ cppstd }} with extensions {{ cppstd_extensions }}") set(CMAKE_CXX_STANDARD {{ cppstd }}) set(CMAKE_CXX_EXTENSIONS {{ cppstd_extensions }}) set(CMAKE_CXX_STANDARD_REQUIRED ON) + {% endif %} + {% if cstd %} + message(STATUS "Conan toolchain: C Standard {{ cstd }} with extensions {{ cstd_extensions }}") + set(CMAKE_C_STANDARD {{ cstd }}) + set(CMAKE_C_EXTENSIONS {{ cstd_extensions }}) + set(CMAKE_C_STANDARD_REQUIRED ON) + {% endif %} """) def context(self): compiler_cppstd = self._conanfile.settings.get_safe("compiler.cppstd") - if compiler_cppstd is None: - return None - - if compiler_cppstd.startswith("gnu"): - cppstd = compiler_cppstd[3:] - cppstd_extensions = "ON" - else: - cppstd = compiler_cppstd - cppstd_extensions = "OFF" - return {"cppstd": cppstd, "cppstd_extensions": cppstd_extensions} + compiler_cstd = self._conanfile.settings.get_safe("compiler.cstd") + result = {} + if compiler_cppstd is not None: + if compiler_cppstd.startswith("gnu"): + result["cppstd"] = compiler_cppstd[3:] + result["cppstd_extensions"] = "ON" + else: + result["cppstd"] = compiler_cppstd + result["cppstd_extensions"] = "OFF" + if compiler_cstd is not None: + if compiler_cstd.startswith("gnu"): + result["cstd"] = compiler_cstd[3:] + result["cstd_extensions"] = "ON" + else: + result["cstd"] = compiler_cstd + result["cstd_extensions"] = "OFF" + return result or None class SharedLibBock(Block): diff --git a/conans/client/conf/__init__.py b/conans/client/conf/__init__.py index 15836051df9..a46f10cd858 100644 --- a/conans/client/conf/__init__.py +++ b/conans/client/conf/__init__.py @@ -94,12 +94,13 @@ "10", "10.1", "10.2", "10.3", "10.4", "10.5", "11", "11.1", "11.2", "11.3", "11.4", "12", "12.1", "12.2", "12.3", - "13", "13.1", "13.2", + "13", "13.1", "13.2", "14", "14.1"] libcxx: [libstdc++, libstdc++11] threads: [null, posix, win32] # Windows MinGW exception: [null, dwarf2, sjlj, seh] # Windows MinGW cppstd: [null, 98, gnu98, 11, gnu11, 14, gnu14, 17, gnu17, 20, gnu20, 23, gnu23] + cstd: [null, 99, gnu99, 11, gnu11, 17, gnu17, 23, gnu23] msvc: version: [170, 180, 190, 191, 192, 193] update: [null, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9] @@ -107,6 +108,7 @@ runtime_type: [Debug, Release] cppstd: [null, 14, 17, 20, 23] toolset: [null, v110_xp, v120_xp, v140_xp, v141_xp] + cstd: [null, 11, 17] clang: version: ["3.3", "3.4", "3.5", "3.6", "3.7", "3.8", "3.9", "4.0", "5.0", "6.0", "7.0", "7.1", @@ -116,11 +118,13 @@ runtime: [null, static, dynamic] runtime_type: [null, Debug, Release] runtime_version: [null, v140, v141, v142, v143] + cstd: [null, 99, gnu99, 11, gnu11, 17, gnu17, 23, gnu23] apple-clang: version: ["5.0", "5.1", "6.0", "6.1", "7.0", "7.3", "8.0", "8.1", "9.0", "9.1", "10.0", "11.0", "12.0", "13", "13.0", "13.1", "14", "14.0", "15", "15.0"] libcxx: [libstdc++, libc++] cppstd: [null, 98, gnu98, 11, gnu11, 14, gnu14, 17, gnu17, 20, gnu20, 23, gnu23] + cstd: [null, 99, gnu99, 11, gnu11, 17, gnu17, 23, gnu23] intel-cc: version: ["2021.1", "2021.2", "2021.3", "2021.4", "2022.1", "2022.2", "2022.3", "2023.0", "2023.1", "2023.2", "2024.0",] diff --git a/conans/client/graph/compatibility.py b/conans/client/graph/compatibility.py index c72c4377c0a..a0a4fd298f2 100644 --- a/conans/client/graph/compatibility.py +++ b/conans/client/graph/compatibility.py @@ -23,32 +23,47 @@ def compatibility(conanfile): _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 +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", {}) - if extension_properties.get("compatibility_cppstd") is False: - return [] 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 or not cppstd: + cstd = conanfile.settings.get_safe("compiler.cstd") + if not compiler or not compiler_version: return [] base = dict(conanfile.settings.values_list) - cppstd_possible_values = supported_cppstd(conanfile) - if cppstd_possible_values is None: - conanfile.output.warning(f'No cppstd compatibility defined for compiler "{compiler}"') - return [] + if cppstd is None or extension_properties.get("compatibility_cppstd") is False: + cppstd_possible_values = [None] + else: + cppstd_possible_values = supported_cppstd(conanfile) + if cppstd_possible_values is None: + conanfile.output.warning(f'No cppstd compatibility defined for compiler "{compiler}"') + cppstd_possible_values = [None] + if cstd is None or extension_properties.get("compatibility_cstd") is False: + cstd_possible_values = [None] + else: + cstd_possible_values = supported_cstd(conanfile) + if cstd_possible_values is None: + conanfile.output.warning(f'No cstd compatibility defined for compiler "{compiler}"') + cstd_possible_values = [None] + + combinations = [(c1, c2) for c1 in cppstd_possible_values for c2 in cstd_possible_values] ret = [] - for _cppstd in cppstd_possible_values: - if _cppstd is None or _cppstd == cppstd: - continue - configuration = base.copy() - configuration["compiler.cppstd"] = _cppstd - ret.append({"settings": [(k, v) for k, v in configuration.items()]}) + for _cppstd, _cstd in combinations: + conf_variation = {} + if _cppstd is not None and _cppstd != cppstd: + conf_variation["compiler.cppstd"] = _cppstd + if _cstd is not None and _cstd != cstd: + conf_variation["compiler.cstd"] = _cstd + if conf_variation: + configuration = base.copy() + configuration.update(conf_variation) + ret.append({"settings": [(k, v) for k, v in configuration.items()]}) return ret """ diff --git a/conans/model/conan_file.py b/conans/model/conan_file.py index 554d0938afa..46789591f56 100644 --- a/conans/model/conan_file.py +++ b/conans/model/conan_file.py @@ -48,6 +48,7 @@ class ConanFile: default_options = None default_build_options = None package_type = None + languages = [] implements = [] @@ -92,6 +93,8 @@ def __init__(self, display_name=""): if isinstance(self.generators, str): self.generators = [self.generators] + if isinstance(self.languages, str): + self.languages = [self.languages] if isinstance(self.settings, str): self.settings = [self.settings] self.requires = Requirements(self.requires, self.build_requires, self.test_requires, diff --git a/conans/test/integration/package_id/test_cache_compatibles.py b/conans/test/integration/package_id/test_cache_compatibles.py index f98dd7fe628..8e63ae18339 100644 --- a/conans/test/integration/package_id/test_cache_compatibles.py +++ b/conans/test/integration/package_id/test_cache_compatibles.py @@ -311,6 +311,37 @@ def package_info(self): assert "valid standard!!" in c.out assert "pkg/0.1: CPPSTD: 17" in c.out + def test_check_min_cstd(self): + """ test that the check_min_cstd works fine wiht compatibility + """ + conanfile = textwrap.dedent(""" + from conan import ConanFile + from conan.tools.build import check_min_cstd + class Pkg(ConanFile): + name = "pkg" + version = "0.1" + settings = "os", "arch", "compiler", "build_type" + languages = "C" + def validate(self): + check_min_cstd(self, "17", False) + self.output.info("valid standard!!") + def package_info(self): + self.output.info("CSTD: {}".format(self.settings.compiler.cstd)) + """) + + c = TestClient() + c.save({"conanfile.py": conanfile}) + settings = "-s compiler=gcc -s compiler.version=9 -s compiler.libcxx=libstdc++11" + c.run(f"create . {settings} -s compiler.cstd=17") + assert "pkg/0.1: valid standard!!" in c.out + assert "pkg/0.1: CSTD: 17" in c.out + c.run(f"install {settings} --requires=pkg/0.1 -s compiler.cstd=11", assert_error=True) + assert "pkg/0.1: Invalid: Current cstd (11) is lower than the required C standard (17)."\ + in c.out + c.run(f"install {settings} --requires=pkg/0.1 -s compiler.cstd=23") + assert "valid standard!!" in c.out + assert "pkg/0.1: CSTD: 17" in c.out + def test_check_min_cppstd_interface(self): """ test that says that compatible binaries are ok, as long as the user defined cppstd>=14. The syntax is a bit forced, maybe we want to improve ``check_min_cppstd`` From 69ee38582d1a00564bf84d7afb8e0abdcdad7f31 Mon Sep 17 00:00:00 2001 From: Ernesto de Gracia Herranz Date: Fri, 5 Apr 2024 13:14:21 +0200 Subject: [PATCH 04/15] Test for BazelDeps in the build context (#16025) * feat add test to bazeldeps * Fixed several bugs. Improved tests coverage * Reverted * Better name --------- Co-authored-by: Francisco Ramirez de Anton --- conan/tools/google/bazeldeps.py | 20 +++-- .../toolchains/google/test_bazeldeps.py | 89 +++++++++++++++++++ 2 files changed, 101 insertions(+), 8 deletions(-) diff --git a/conan/tools/google/bazeldeps.py b/conan/tools/google/bazeldeps.py index 4dffc1c4db7..b2d64f985c5 100644 --- a/conan/tools/google/bazeldeps.py +++ b/conan/tools/google/bazeldeps.py @@ -28,9 +28,9 @@ def _get_package_reference_name(dep): return dep.ref.name -def _get_repository_name(dep): +def _get_repository_name(dep, is_build_require=False): pkg_name = dep.cpp_info.get_property("bazel_repository_name") or _get_package_reference_name(dep) - return f"build-{pkg_name}" if dep.context == "build" else pkg_name + return f"build-{pkg_name}" if is_build_require else pkg_name def _get_target_name(dep): @@ -456,9 +456,11 @@ def generate(self): class _InfoGenerator: - def __init__(self, conanfile, dep): + def __init__(self, conanfile, dep, require): self._conanfile = conanfile self._dep = dep + self._req = require + self._is_build_require = require.build self._transitive_reqs = get_transitive_requires(self._conanfile, dep) def _get_cpp_info_requires_names(self, cpp_info): @@ -491,7 +493,7 @@ def package_info(self): try: req_conanfile = self._transitive_reqs[pkg_ref_name] # Requirements declared in another dependency BUILD file - prefix = f"@{_get_repository_name(req_conanfile)}//:" + prefix = f"@{_get_repository_name(req_conanfile, is_build_require=self._is_build_require)}//:" except KeyError: continue # If the dependency is not in the transitive, might be skipped else: # For instance, dep == "hello/1.0" and req == "hello::cmp1" -> hello == hello @@ -527,7 +529,7 @@ def root_package_info(self): :return: `_BazelTargetInfo` object with the package information """ - repository_name = _get_repository_name(self._dep) + repository_name = _get_repository_name(self._dep, is_build_require=self._is_build_require) pkg_name = _get_target_name(self._dep) # At first, let's check if we have defined some global requires, e.g., "other::cmp1" requires = self._get_cpp_info_requires_names(self._dep.cpp_info) @@ -535,8 +537,10 @@ def root_package_info(self): if not requires: # If no requires were found, let's try to get all the direct dependencies, # e.g., requires = "other_pkg/1.0" - requires = [f"@{_get_repository_name(req)}//:{_get_target_name(req)}" - for req in self._transitive_reqs.values()] + requires = [ + f"@{_get_repository_name(req, is_build_require=self._is_build_require)}//:{_get_target_name(req)}" + for req in self._transitive_reqs.values() + ] cpp_info = self._dep.cpp_info return _BazelTargetInfo(repository_name, pkg_name, requires, cpp_info) @@ -567,7 +571,7 @@ def generate(self): deps_info = [] for require, dep in requirements: # Bazel info generator - info_generator = _InfoGenerator(self._conanfile, dep) + info_generator = _InfoGenerator(self._conanfile, dep, require) root_package_info = info_generator.root_package_info components_info = info_generator.components_info # Generating single BUILD files per dependency diff --git a/conans/test/integration/toolchains/google/test_bazeldeps.py b/conans/test/integration/toolchains/google/test_bazeldeps.py index 62b6059495d..92ccd008d7d 100644 --- a/conans/test/integration/toolchains/google/test_bazeldeps.py +++ b/conans/test/integration/toolchains/google/test_bazeldeps.py @@ -880,3 +880,92 @@ def build(self): "--build=example") c.assert_listed_require({"example/1.0": "Cache"}) c.assert_listed_require({"example/1.0": "Cache"}, build=True) + + +class TestBazelGenerationBuildContext: + """ + https://github.com/conan-io/conan/issues/15764 + """ + def test_bazel_generate(self): + c = TestClient() + tool = textwrap.dedent(""" + import os + from conan import ConanFile + from conan.tools.google import BazelDeps + class Example(ConanFile): + name = "tool" + version = "1.0" + requires = "wayland/1.0" + tool_requires = "wayland/1.0" + def generate(self): + tc = BazelDeps(self) + tc.build_context_activated = ["wayland", "dep"] + tc.generate() + def build(self): + # Build context + assert os.path.exists(os.path.join("build-wayland", "BUILD.bazel")) + # Revisit this after investigation -> build_req = conanfile.dependencies.direct_build + # assert os.path.exists(os.path.join("build-dep", "BUILD.bazel")) + # Host context + assert os.path.exists(os.path.join("wayland", "BUILD.bazel")) + assert os.path.exists(os.path.join("dep", "BUILD.bazel")) + """) + c.save({"dep/conanfile.py": GenConanfile("dep", "1.0").with_package_type("shared-library"), + "wayland/conanfile.py": GenConanfile("wayland", "1.0").with_requires("dep/1.0"), + "tool/conanfile.py": tool, + "app/conanfile.py": GenConanfile().with_tool_requires("tool/1.0")}) + c.run("export dep") + c.run("export wayland") + c.run("export tool") + c.run("install app --build=missing") + assert "Install finished successfully" in c.out # the asserts in build() didn't fail + # Now make sure we can actually build with build!=host context + c.run("install app -s:h build_type=Debug --build=missing") + assert "Install finished successfully" in c.out # the asserts in build() didn't fail + + def test_bazel_generate_components(self): + c = TestClient() + tool = textwrap.dedent(""" + import os + from conan import ConanFile + from conan.tools.google import BazelDeps + class Example(ConanFile): + name = "tool" + version = "1.0" + requires = "wayland/1.0" + tool_requires = "wayland/1.0" + def generate(self): + tc = BazelDeps(self) + tc.build_context_activated = ["wayland", "dep"] + tc.generate() + def build(self): + # Build context + assert os.path.exists(os.path.join("build-wayland", "BUILD.bazel")) + # Revisit this after investigation -> build_req = conanfile.dependencies.direct_build + # assert os.path.exists(os.path.join("build-dep", "BUILD.bazel")) + # Host context + assert os.path.exists(os.path.join("wayland", "BUILD.bazel")) + assert os.path.exists(os.path.join("dep", "BUILD.bazel")) + """) + wayland = textwrap.dedent(""" + from conan import ConanFile + class Pkg(ConanFile): + name = "wayland" + version = "1.0" + requires = "dep/1.0" + def package_info(self): + self.cpp_info.components["client"].libs = [] + self.cpp_info.components["server"].libs = [] + """) + c.save({"dep/conanfile.py": GenConanfile("dep", "1.0").with_package_type("shared-library"), + "wayland/conanfile.py": wayland, + "tool/conanfile.py": tool, + "app/conanfile.py": GenConanfile().with_tool_requires("tool/1.0")}) + c.run("export dep") + c.run("export wayland") + c.run("export tool") + c.run("install app --build=missing") + assert "Install finished successfully" in c.out # the asserts in build() didn't fail + # Now make sure we can actually build with build!=host context + c.run("install app -s:h build_type=Debug --build=missing") + assert "Install finished successfully" in c.out # the asserts in build() didn't fail From c8545d82dd39a83acfb4c6fe6a3c1e549d680ba8 Mon Sep 17 00:00:00 2001 From: James Date: Mon, 8 Apr 2024 12:37:08 +0200 Subject: [PATCH 05/15] copy only if different (#16031) --- conan/tools/files/copy_pattern.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/conan/tools/files/copy_pattern.py b/conan/tools/files/copy_pattern.py index e960ea13c1f..ef0796da0ff 100644 --- a/conan/tools/files/copy_pattern.py +++ b/conan/tools/files/copy_pattern.py @@ -1,3 +1,4 @@ +import filecmp import fnmatch import os import shutil @@ -7,7 +8,7 @@ def copy(conanfile, pattern, src, dst, keep_path=True, excludes=None, - ignore_case=True): + ignore_case=True, overwrite_equal=False): """ Copy the files matching the pattern (fnmatch) at the src folder to a dst folder. @@ -25,6 +26,9 @@ def copy(conanfile, pattern, src, dst, keep_path=True, excludes=None, :param ignore_case: (Optional, defaulted to ``True``) If enabled, it will do a case-insensitive pattern matching. will do a case-insensitive pattern matching when ``True`` + :param overwrite_equal: (Optional, default ``False``). If the file to be copied already exists + in the destination folder, only really copy it if it seems different (different size, + different modification time) :return: list of copied files """ if src == dst: @@ -40,7 +44,7 @@ def copy(conanfile, pattern, src, dst, keep_path=True, excludes=None, files_to_copy, files_symlinked_to_folders = _filter_files(src, pattern, excludes, ignore_case, excluded_folder) - copied_files = _copy_files(files_to_copy, src, dst, keep_path) + copied_files = _copy_files(files_to_copy, src, dst, keep_path, overwrite_equal) copied_files.extend(_copy_files_symlinked_to_folders(files_symlinked_to_folders, src, dst)) if conanfile: # Some usages still pass None copied = '\n '.join(files_to_copy) @@ -108,7 +112,7 @@ def _filter_files(src, pattern, excludes, ignore_case, excluded_folder): return files_to_copy, files_symlinked_to_folders -def _copy_files(files, src, dst, keep_path): +def _copy_files(files, src, dst, keep_path, overwrite_equal): """ executes a multiple file copy from [(src_file, dst_file), (..)] managing symlinks if necessary """ @@ -122,14 +126,17 @@ def _copy_files(files, src, dst, keep_path): os.makedirs(parent_folder, exist_ok=True) if os.path.islink(abs_src_name): - linkto = os.readlink(abs_src_name) # @UndefinedVariable + linkto = os.readlink(abs_src_name) try: os.remove(abs_dst_name) except OSError: pass - os.symlink(linkto, abs_dst_name) # @UndefinedVariable + os.symlink(linkto, abs_dst_name) else: - shutil.copy2(abs_src_name, abs_dst_name) + # Avoid the copy if the file exists and has the exact same signature (size + mod time) + if overwrite_equal or not os.path.exists(abs_dst_name) \ + or not filecmp.cmp(abs_src_name, abs_dst_name): + shutil.copy2(abs_src_name, abs_dst_name) copied_files.append(abs_dst_name) return copied_files From 7dbfb3d452b7140a118cc48321ea3a166b5fb0bf Mon Sep 17 00:00:00 2001 From: James Date: Mon, 8 Apr 2024 12:41:24 +0200 Subject: [PATCH 06/15] allow conf in exports-sources and export (#16034) --- conans/client/cmd/export.py | 1 + conans/test/functional/tools/test_files.py | 26 ++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/conans/client/cmd/export.py b/conans/client/cmd/export.py index 4ff4f40d721..93ae8bc26b6 100644 --- a/conans/client/cmd/export.py +++ b/conans/client/cmd/export.py @@ -25,6 +25,7 @@ def cmd_export(app, global_conf, conanfile_path, name, version, user, channel, g ref.validate_ref(allow_uppercase=global_conf.get("core:allow_uppercase_pkg_names", check_type=bool)) + conanfile.conf = global_conf.get_conanfile_conf(ref, is_consumer=True) conanfile.display_name = str(ref) conanfile.output.scope = conanfile.display_name scoped_output = conanfile.output diff --git a/conans/test/functional/tools/test_files.py b/conans/test/functional/tools/test_files.py index 2aef28f1844..c6f5331661f 100644 --- a/conans/test/functional/tools/test_files.py +++ b/conans/test/functional/tools/test_files.py @@ -86,6 +86,32 @@ def source(self): client.save({"profile": profile}) client.run("create . -pr=profile") + def test_download_export_sources(self): + client = TestClient(light=True) + file_server = TestFileServer() + client.servers["file_server"] = file_server + save(os.path.join(file_server.store, "myfile.txt"), "some content") + save(os.path.join(file_server.store, "myfile2.txt"), "some content") + + conanfile = textwrap.dedent(f""" + import os + from conan import ConanFile + from conan.tools.files import download + + class Pkg(ConanFile): + name = "mypkg" + version = "1.0" + def export(self): + download(self, "{file_server.fake_url}/myfile.txt", "myfile.txt") + assert os.path.exists("myfile.txt") + def export_sources(self): + download(self, "{file_server.fake_url}/myfile2.txt", "myfile2.txt") + assert os.path.exists("myfile2.txt") + """) + + client.save({"conanfile.py": conanfile}) + client.run("create .") + def test_patch(mock_patch_ng): conanfile = textwrap.dedent(""" From d579c0f3602ff82d102bfc7bbb274a11f1658a7b Mon Sep 17 00:00:00 2001 From: James Date: Mon, 8 Apr 2024 15:37:36 +0200 Subject: [PATCH 07/15] refactor apple_min_version_flag() (#16017) * refactor apple_min_version_flag() * Refactored all the apple module and where it was being used (AutotoolsToolchain and MesonToolchain for now) * Fixed bad return * Fixing tests * Keeping legacy behavior in apple_min_version_flag function * Preventing possible breaking change --------- Co-authored-by: Francisco Ramirez de Anton --- conan/tools/apple/apple.py | 84 +++++++++++-------- conan/tools/gnu/autotoolstoolchain.py | 38 ++++----- conan/tools/meson/toolchain.py | 21 ++--- .../integration/tools/test_apple_xcrun.py | 56 +++++++++++++ .../autotools/autotools_toolchain_test.py | 2 +- conans/test/unittests/util/apple_test.py | 52 ++---------- 6 files changed, 137 insertions(+), 116 deletions(-) create mode 100644 conans/test/integration/tools/test_apple_xcrun.py diff --git a/conan/tools/apple/apple.py b/conan/tools/apple/apple.py index 39dafb69df1..52a021817a2 100644 --- a/conan/tools/apple/apple.py +++ b/conan/tools/apple/apple.py @@ -30,11 +30,16 @@ def to_apple_arch(conanfile, default=None): return _to_apple_arch(arch_, default) -def apple_sdk_path(conanfile): +def apple_sdk_path(conanfile, is_cross_building=True): sdk_path = conanfile.conf.get("tools.apple:sdk_path") if not sdk_path: # XCRun already knows how to extract os.sdk from conanfile.settings sdk_path = XCRun(conanfile).sdk_path + if not sdk_path and is_cross_building: + raise ConanException( + "Apple SDK path not found. For cross-compilation, you must " + "provide a valid SDK path in 'tools.apple:sdk_path' config." + ) return sdk_path @@ -48,7 +53,6 @@ def get_apple_sdk_fullname(conanfile): os_ = conanfile.settings.get_safe('os') os_sdk = conanfile.settings.get_safe('os.sdk') os_sdk_version = conanfile.settings.get_safe('os.sdk_version') or "" - if os_sdk: return "{}{}".format(os_sdk, os_sdk_version) elif os_ == "Macos": # it has only a single value for all the architectures @@ -57,39 +61,51 @@ def get_apple_sdk_fullname(conanfile): raise ConanException("Please, specify a suitable value for os.sdk.") -def apple_min_version_flag(os_version, os_sdk, subsystem): +def apple_min_version_flag(conanfile): """compiler flag name which controls deployment target""" - if not os_version or not os_sdk: - return '' - - # FIXME: This guess seems wrong, nothing has to be guessed, but explicit - flag = '' - if 'macosx' in os_sdk: - flag = '-mmacosx-version-min' - elif 'iphoneos' in os_sdk: - flag = '-mios-version-min' - elif 'iphonesimulator' in os_sdk: - flag = '-mios-simulator-version-min' - elif 'watchos' in os_sdk: - flag = '-mwatchos-version-min' - elif 'watchsimulator' in os_sdk: - flag = '-mwatchos-simulator-version-min' - elif 'appletvos' in os_sdk: - flag = '-mtvos-version-min' - elif 'appletvsimulator' in os_sdk: - flag = '-mtvos-simulator-version-min' - elif 'xros' in os_sdk: - # need early return as string did not fit into the "normal" schema - return f'-target arm64-apple-xros{os_version}' - elif 'xrsimulator' in os_sdk: - # need early return as string did not fit into the "normal" schema - return f'-target arm64-apple-xros{os_version}-simulator' - - if subsystem == 'catalyst': - # especial case, despite Catalyst is macOS, it requires an iOS version argument - flag = '-mios-version-min' - - return f"{flag}={os_version}" if flag else '' + os_ = conanfile.settings.get_safe('os') + os_sdk = conanfile.settings.get_safe('os.sdk') + os_sdk = os_sdk or ("macosx" if os_ == "Macos" else None) + os_version = conanfile.settings.get_safe("os.version") + if not os_sdk or not os_version: + # Legacy behavior + return "" + if conanfile.settings.get_safe("os.subsystem") == 'catalyst': + os_sdk = "iphoneos" + return { + "macosx": f"-mmacosx-version-min={os_version}", + "iphoneos": f"-mios-version-min={os_version}", + "iphonesimulator": f"-mios-simulator-version-min={os_version}", + "watchos": f"-mwatchos-version-min={os_version}", + "watchsimulator": f"-mwatchos-simulator-version-min={os_version}", + "appletvos": f"-mtvos-version-min={os_version}", + "appletvsimulator": f"-mtvos-simulator-version-min={os_version}", + "xros": f"-target arm64-apple-xros{os_version}", + "xrsimulator": f"-target arm64-apple-xros{os_version}-simulator", + }.get(os_sdk, "") + + +def resolve_apple_flags(conanfile, is_cross_building=False): + """ + Gets the most common flags in Apple systems. If it's a cross-building context + SDK path is mandatory so if it could raise an exception if SDK is not found. + + :param conanfile: instance. + :param is_cross_building: boolean to indicate if it's a cross-building context. + :return: tuple of Apple flags (apple_min_version_flag, apple_arch, apple_isysroot_flag). + """ + if not is_apple_os(conanfile): + # Keeping legacy defaults + return "", None, None + + apple_arch_flag = apple_isysroot_flag = None + if is_cross_building: + arch = to_apple_arch(conanfile) + sdk_path = apple_sdk_path(conanfile, is_cross_building=is_cross_building) + apple_isysroot_flag = f"-isysroot {sdk_path}" if sdk_path else "" + apple_arch_flag = f"-arch {arch}" if arch else "" + min_version_flag = apple_min_version_flag(conanfile) + return min_version_flag, apple_arch_flag, apple_isysroot_flag class XCRun: diff --git a/conan/tools/gnu/autotoolstoolchain.py b/conan/tools/gnu/autotoolstoolchain.py index 41430e73b25..c81e70abebc 100644 --- a/conan/tools/gnu/autotoolstoolchain.py +++ b/conan/tools/gnu/autotoolstoolchain.py @@ -1,7 +1,7 @@ from conan.internal import check_duplicated_generator from conan.internal.internal_tools import raise_on_universal_arch -from conan.tools.apple.apple import apple_min_version_flag, is_apple_os, to_apple_arch, apple_sdk_path -from conan.tools.apple.apple import get_apple_sdk_fullname +from conan.tools.apple.apple import apple_min_version_flag, is_apple_os, to_apple_arch, \ + apple_sdk_path, resolve_apple_flags from conan.tools.build import cmd_args_to_string, save_toolchain_args from conan.tools.build.cross_building import cross_building from conan.tools.build.flags import architecture_flag, build_type_flags, cppstd_flag, build_type_link_flags, libcxx_flags @@ -59,16 +59,8 @@ def __init__(self, conanfile, namespace=None, prefix="/"): self._build = self._conanfile.conf.get("tools.gnu:build_triplet") self._target = None - self.apple_arch_flag = self.apple_isysroot_flag = None - - os_sdk = get_apple_sdk_fullname(conanfile) - os_version = conanfile.settings.get_safe("os.version") - subsystem = conanfile.settings.get_safe("os.subsystem") - self.apple_min_version_flag = apple_min_version_flag(os_version, os_sdk, subsystem) - - self.sysroot_flag = None - - if cross_building(self._conanfile): + is_cross_building = cross_building(self._conanfile) + if is_cross_building: os_host = conanfile.settings.get_safe("os") arch_host = conanfile.settings.get_safe("arch") os_build = conanfile.settings_build.get_safe('os') @@ -80,17 +72,6 @@ def __init__(self, conanfile, namespace=None, prefix="/"): # Build triplet if not self._build: self._build = _get_gnu_triplet(os_build, arch_build, compiler=compiler) - # Apple Stuff - if os_build == "Macos" and is_apple_os(conanfile): - # SDK path is mandatory for cross-building - sdk_path = apple_sdk_path(self._conanfile) - if not sdk_path: - raise ConanException("You must provide a valid SDK path for cross-compilation.") - apple_arch = to_apple_arch(self._conanfile) - # https://man.archlinux.org/man/clang.1.en#Target_Selection_Options - self.apple_arch_flag = "-arch {}".format(apple_arch) if apple_arch else None - # -isysroot makes all includes for your library relative to the build directory - self.apple_isysroot_flag = "-isysroot {}".format(sdk_path) if sdk_path else None sysroot = self._conanfile.conf.get("tools.build:sysroot") sysroot = sysroot.replace("\\", "/") if sysroot is not None else None @@ -101,6 +82,17 @@ def __init__(self, conanfile, namespace=None, prefix="/"): self._get_triplets() self.autoreconf_args = self._default_autoreconf_flags() self.make_args = [] + # Apple stuff + is_cross_building_osx = (is_cross_building + and conanfile.settings_build.get_safe('os') == "Macos" + and is_apple_os(conanfile)) + min_flag, arch_flag, isysroot_flag = resolve_apple_flags(conanfile, + is_cross_building=is_cross_building_osx) + # https://man.archlinux.org/man/clang.1.en#Target_Selection_Options + self.apple_arch_flag = arch_flag + # -isysroot makes all includes for your library relative to the build directory + self.apple_isysroot_flag = isysroot_flag + self.apple_min_version_flag = min_flag def _get_msvc_runtime_flag(self): flag = msvc_runtime_flag(self._conanfile) diff --git a/conan/tools/meson/toolchain.py b/conan/tools/meson/toolchain.py index b7f945b31e9..2e019d88dfe 100644 --- a/conan/tools/meson/toolchain.py +++ b/conan/tools/meson/toolchain.py @@ -7,7 +7,7 @@ from conan.internal import check_duplicated_generator from conan.internal.internal_tools import raise_on_universal_arch from conan.tools.apple.apple import to_apple_arch, is_apple_os, apple_min_version_flag, \ - apple_sdk_path, get_apple_sdk_fullname + apple_sdk_path, resolve_apple_flags from conan.tools.build.cross_building import cross_building from conan.tools.build.flags import libcxx_flags from conan.tools.env import VirtualBuildEnv @@ -307,21 +307,12 @@ def _get_cpp_info_value(name): def _resolve_apple_flags_and_variables(self, build_env, compilers_by_conf): if not self._is_apple_system: return - # SDK path is mandatory for cross-building - sdk_path = apple_sdk_path(self._conanfile) - if not sdk_path and self.cross_build: - raise ConanException( - "Apple SDK path not found. For cross-compilation, you must " - "provide a valid SDK path in 'tools.apple:sdk_path' config." - ) # Calculating the main Apple flags - os_sdk = get_apple_sdk_fullname(self._conanfile) - arch = to_apple_arch(self._conanfile) - self.apple_arch_flag = ["-arch", arch] if arch else [] - self.apple_isysroot_flag = ["-isysroot", sdk_path] if sdk_path else [] - os_version = self._conanfile.settings.get_safe("os.version") - subsystem = self._conanfile.settings.get_safe("os.subsystem") - self.apple_min_version_flag = [apple_min_version_flag(os_version, os_sdk, subsystem)] + min_flag, arch_flag, isysroot_flag = ( + resolve_apple_flags(self._conanfile, is_cross_building=bool(self.cross_build))) + self.apple_arch_flag = arch_flag.split() if arch_flag else [] + self.apple_isysroot_flag = isysroot_flag.split() if isysroot_flag else [] + self.apple_min_version_flag = [apple_min_version_flag(self._conanfile)] # Objective C/C++ ones self.objc = compilers_by_conf.get("objc", "clang") self.objcpp = compilers_by_conf.get("objcpp", "clang++") diff --git a/conans/test/integration/tools/test_apple_xcrun.py b/conans/test/integration/tools/test_apple_xcrun.py new file mode 100644 index 00000000000..5e978106aed --- /dev/null +++ b/conans/test/integration/tools/test_apple_xcrun.py @@ -0,0 +1,56 @@ +import os +import platform + +import pytest + +from conan.tools.apple.apple import XCRun +from conans.test.utils.mocks import ConanFileMock, MockSettings +from conans.util.runners import conan_run + + +@pytest.mark.skipif(platform.system() != "Darwin", reason="Requires OSX and xcrun tool") +def test_xcrun(): + def _common_asserts(xcrun_): + assert xcrun_.cc.endswith('clang') + assert xcrun_.cxx.endswith('clang++') + assert xcrun_.ar.endswith('ar') + assert xcrun_.ranlib.endswith('ranlib') + assert xcrun_.strip.endswith('strip') + assert xcrun_.find('lipo').endswith('lipo') + assert os.path.isdir(xcrun_.sdk_path) + + conanfile = ConanFileMock( runner=conan_run) + conanfile.settings = MockSettings( + {"os": "Macos", + "arch": "x86"}) + xcrun = XCRun(conanfile) + _common_asserts(xcrun) + + conanfile.settings = MockSettings( + {"os": "iOS", + "arch": "x86"}) + xcrun = XCRun(conanfile, sdk='macosx') + _common_asserts(xcrun) + # Simulator + assert "iPhoneOS" not in xcrun.sdk_path + + conanfile.settings = MockSettings( + {"os": "iOS", + "os.sdk": "iphoneos", + "arch": "armv7"}) + xcrun = XCRun(conanfile) + _common_asserts(xcrun) + assert "iPhoneOS" in xcrun.sdk_path + + conanfile.settings = MockSettings( + {"os": "watchOS", + "os.sdk": "watchos", + "arch": "armv7"}) + xcrun = XCRun(conanfile) + _common_asserts(xcrun) + assert "WatchOS" in xcrun.sdk_path + + # Default one + conanfile.settings = MockSettings({}) + xcrun = XCRun(conanfile) + _common_asserts(xcrun) diff --git a/conans/test/unittests/client/toolchain/autotools/autotools_toolchain_test.py b/conans/test/unittests/client/toolchain/autotools/autotools_toolchain_test.py index d8fa16cf938..6c7dd86b207 100644 --- a/conans/test/unittests/client/toolchain/autotools/autotools_toolchain_test.py +++ b/conans/test/unittests/client/toolchain/autotools/autotools_toolchain_test.py @@ -349,7 +349,7 @@ def test_crossbuild_from_macos_to_non_apple_os(): conanfile.settings = MockSettings({"os": "Android", "arch": "armv8"}) conanfile.settings_build = MockSettings({"os": "Macos", "arch": "armv8"}) be = AutotoolsToolchain(conanfile) - assert be.apple_min_version_flag == '' + assert be.apple_min_version_flag == "" assert be.apple_arch_flag is None assert be.apple_isysroot_flag is None diff --git a/conans/test/unittests/util/apple_test.py b/conans/test/unittests/util/apple_test.py index ee4adb734d5..c1e27624f5b 100644 --- a/conans/test/unittests/util/apple_test.py +++ b/conans/test/unittests/util/apple_test.py @@ -1,16 +1,13 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -import os -import platform import unittest import pytest from conan.tools.apple.apple import _to_apple_arch, apple_min_version_flag, \ - is_apple_os, XCRun + is_apple_os from conans.test.utils.mocks import MockSettings, ConanFileMock -from conans.util.runners import conan_run class FakeSettings(object): @@ -48,11 +45,17 @@ class TestApple: '-mtvos-simulator-version-min=10.1'), ("Macos", "10.1", "macosx", "catalyst", '-mios-version-min=10.1'), ("Solaris", "10.1", None, None, ''), - ("Macos", "10.1", None, None, ''), + ("Macos", "10.1", None, None, '-mmacosx-version-min=10.1'), ("Macos", None, "macosx", None, '') ]) def test_deployment_target_flag_name(self, os_, version, sdk, subsystem, flag): - assert apple_min_version_flag(version, sdk, subsystem) == flag + conanfile = ConanFileMock() + settings = MockSettings({"os": os_, + "os.version": version, + "os.sdk": sdk, + "os.subsystem": subsystem}) + conanfile.settings = settings + assert apple_min_version_flag(conanfile) == flag class AppleTest(unittest.TestCase): @@ -82,40 +85,3 @@ def test_to_apple_arch(self): self.assertEqual(_to_apple_arch('armv8_32'), 'arm64_32') self.assertIsNone(_to_apple_arch('mips')) self.assertEqual(_to_apple_arch('mips', default='mips'), 'mips') - - @pytest.mark.skipif(platform.system() != "Darwin", reason="Requires OSX") - def test_xcrun(self): - - def _common_asserts(xcrun_): - self.assertTrue(xcrun_.cc.endswith('clang')) - self.assertTrue(xcrun_.cxx.endswith('clang++')) - self.assertTrue(xcrun_.ar.endswith('ar')) - self.assertTrue(xcrun_.ranlib.endswith('ranlib')) - self.assertTrue(xcrun_.strip.endswith('strip')) - self.assertTrue(xcrun_.find('lipo').endswith('lipo')) - self.assertTrue(os.path.isdir(xcrun_.sdk_path)) - - conanfile = ConanFileMock(settings=FakeSettings('Macos', 'x86'), runner=conan_run) - xcrun = XCRun(conanfile) - _common_asserts(xcrun) - - conanfile.settings = FakeSettings('iOS', 'x86') - xcrun = XCRun(conanfile, sdk='macosx') - _common_asserts(xcrun) - # Simulator - self.assertNotIn("iPhoneOS", xcrun.sdk_path) - - conanfile.settings = FakeSettings('iOS', 'armv7', os_sdk="iphoneos") - xcrun = XCRun(conanfile) - _common_asserts(xcrun) - self.assertIn("iPhoneOS", xcrun.sdk_path) - - conanfile.settings = FakeSettings('watchOS', 'armv7', os_sdk="watchos") - xcrun = XCRun(conanfile) - _common_asserts(xcrun) - self.assertIn("WatchOS", xcrun.sdk_path) - - # Default one - conanfile.settings = FakeSettings(None, None) - xcrun = XCRun(conanfile) - _common_asserts(xcrun) From ce4f5df94401b40352d864114db8f4653c8b93eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=B6ffner?= Date: Mon, 8 Apr 2024 20:39:18 +0200 Subject: [PATCH 08/15] Allow to unhide git url (#16038) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add hide_url tests for git scm tool * Add hide_url flag to clone and fetch_commit. Resolves #15684 * Update conans/test/functional/tools/scm/test_git.py * Update conans/test/functional/tools/scm/test_git.py --------- Co-authored-by: Rubén Rincón Blanco --- conan/tools/scm/git.py | 18 ++++-- conans/test/functional/tools/scm/test_git.py | 62 ++++++++++++++++++++ 2 files changed, 75 insertions(+), 5 deletions(-) diff --git a/conan/tools/scm/git.py b/conan/tools/scm/git.py index bf2ea8ba064..1c8a12fc06d 100644 --- a/conan/tools/scm/git.py +++ b/conan/tools/scm/git.py @@ -197,13 +197,15 @@ def get_repo_root(self): folder = self.run("rev-parse --show-toplevel") return folder.replace("\\", "/") - def clone(self, url, target="", args=None): + def clone(self, url, target="", args=None, hide_url=True): """ Performs a ``git clone `` operation, where target is the target directory. :param url: URL of remote repository. :param target: Target folder. :param args: Extra arguments to pass to the git clone as a list. + :param hide_url: Hides the URL from the log output to prevent accidental + credential leaks. Can be disabled by passing ``False``. """ args = args or [] if os.path.exists(url): @@ -212,18 +214,24 @@ def clone(self, url, target="", args=None): self._conanfile.output.info("Cloning git repo") target_path = f'"{target}"' if target else "" # quote in case there are spaces in path # Avoid printing the clone command, it can contain tokens - self.run('clone "{}" {} {}'.format(url, " ".join(args), target_path), hidden_output=url) + self.run('clone "{}" {} {}'.format(url, " ".join(args), target_path), + hidden_output=url if hide_url else None) - def fetch_commit(self, url, commit): + def fetch_commit(self, url, commit, hide_url=True): """ - Experimental: does a 1 commit fetch and checkout, instead of a full clone, + Experimental: does a single commit fetch and checkout, instead of a full clone, should be faster. + + :param url: URL of remote repository. + :param commit: The commit ref to checkout. + :param hide_url: Hides the URL from the log output to prevent accidental + credential leaks. Can be disabled by passing ``False``. """ if os.path.exists(url): url = url.replace("\\", "/") # Windows local directory self._conanfile.output.info("Shallow fetch of git repo") self.run('init') - self.run(f'remote add origin "{url}"', hidden_output=url) + self.run(f'remote add origin "{url}"', hidden_output=url if hide_url else None) self.run(f'fetch --depth 1 origin {commit}') self.run('checkout FETCH_HEAD') diff --git a/conans/test/functional/tools/scm/test_git.py b/conans/test/functional/tools/scm/test_git.py index 90cd7025741..bc7bf1b6cbe 100644 --- a/conans/test/functional/tools/scm/test_git.py +++ b/conans/test/functional/tools/scm/test_git.py @@ -289,6 +289,37 @@ def test_clone_checkout(self): assert c.load("source/src/myfile.h") == "myheader!" assert c.load("source/CMakeLists.txt") == "mycmake" + def test_clone_url_not_hidden(self): + conanfile = textwrap.dedent(""" + import os + from conan import ConanFile + from conan.tools.scm import Git + from conan.tools.files import load + + class Pkg(ConanFile): + name = "pkg" + version = "0.1" + + def layout(self): + self.folders.source = "source" + + def source(self): + git = Git(self) + git.clone(url="{url}", target=".", hide_url=False) + """) + folder = os.path.join(temp_folder(), "myrepo") + url, _ = create_local_git_repo(files={"CMakeLists.txt": "mycmake"}, folder=folder) + + c = TestClient(light=True) + c.save({"conanfile.py": conanfile.format(url=url)}) + c.run("create . -v") + # Clone URL is explicitly printed + assert f'pkg/0.1: RUN: git clone "{url}" "."' in c.out + + # It also works in local flow + c.run("source .") + assert f'conanfile.py (pkg/0.1): RUN: git clone "{url}" "."' in c.out + def test_clone_target(self): # Clone to a different target folder # https://github.com/conan-io/conan/issues/14058 @@ -422,6 +453,37 @@ def test_clone_checkout(self): assert c.load("source/src/myfile.h") == "myheader!" assert c.load("source/CMakeLists.txt") == "mycmake" + def test_clone_url_not_hidden(self): + conanfile = textwrap.dedent(""" + import os + from conan import ConanFile + from conan.tools.scm import Git + from conan.tools.files import load + + class Pkg(ConanFile): + name = "pkg" + version = "0.1" + + def layout(self): + self.folders.source = "source" + + def source(self): + git = Git(self) + git.fetch_commit(url="{url}", commit="{commit}", hide_url=False) + """) + folder = os.path.join(temp_folder(), "myrepo") + url, commit = create_local_git_repo(files={"CMakeLists.txt": "mycmake"}, folder=folder) + + c = TestClient(light=True) + c.save({"conanfile.py": conanfile.format(url=url, commit=commit)}) + c.run("create . -v") + # Clone URL is explicitly printed + assert f'pkg/0.1: RUN: git remote add origin "{url}"' in c.out + + # It also works in local flow + c.run("source .") + assert f'conanfile.py (pkg/0.1): RUN: git remote add origin "{url}"' in c.out + class TestGitCloneWithArgs: """ Git cloning passing additional arguments From 2a870159489b287b1b4bb8b1f1183642ed01d2d6 Mon Sep 17 00:00:00 2001 From: memsharded Date: Tue, 9 Apr 2024 20:35:40 +0200 Subject: [PATCH 09/15] wip --- conan/tools/meson/helpers.py | 15 ++++++++++++++- conan/tools/meson/toolchain.py | 5 +++++ conans/model/conan_file.py | 1 + conans/model/conanfile_interface.py | 4 ++++ .../toolchains/meson/test_mesontoolchain.py | 2 ++ 5 files changed, 26 insertions(+), 1 deletion(-) diff --git a/conan/tools/meson/helpers.py b/conan/tools/meson/helpers.py index d0decdf8b1b..ba8e1eb8533 100644 --- a/conan/tools/meson/helpers.py +++ b/conan/tools/meson/helpers.py @@ -2,7 +2,7 @@ from conan.tools.build.flags import cppstd_msvc_flag from conans.model.options import _PackageOption -__all__ = ["to_meson_machine", "to_meson_value", "to_cppstd_flag"] +__all__ = ["to_meson_machine", "to_meson_value", "to_cppstd_flag", "to_cstd_flag"] # https://mesonbuild.com/Reference-tables.html#operating-system-names _meson_system_map = { @@ -126,3 +126,16 @@ def to_cppstd_flag(compiler, compiler_version, cppstd): return 'v%s' % flag if flag else None else: return _cppstd_map.get(cppstd) + + +def to_cstd_flag(cstd): + """ possible values + none, c89, c99, c11, c17, c18, c2x, c23, gnu89, gnu99, gnu11, gnu17, gnu18, gnu2x, gnu23 + """ + _cstd_map = { + '99': "c99", + '11': "c11", + '17': "c17", + '23': "c23", + } + return _cstd_map.get(cstd, cstd) diff --git a/conan/tools/meson/toolchain.py b/conan/tools/meson/toolchain.py index 2e019d88dfe..0ba20b5faf8 100644 --- a/conan/tools/meson/toolchain.py +++ b/conan/tools/meson/toolchain.py @@ -73,6 +73,7 @@ class MesonToolchain(object): {% if b_ndebug %}b_ndebug = {{b_ndebug}}{% endif %} {% if b_staticpic %}b_staticpic = {{b_staticpic}}{% endif %} {% if cpp_std %}cpp_std = '{{cpp_std}}' {% endif %} + {% if c_std %}c_std = '{{c_std}}' {% endif %} {% if backend %}backend = '{{backend}}' {% endif %} {% if pkg_config_path %}pkg_config_path = '{{pkg_config_path}}'{% endif %} {% if build_pkg_config_path %}build.pkg_config_path = '{{build_pkg_config_path}}'{% endif %} @@ -138,6 +139,9 @@ def __init__(self, conanfile, backend=None): cppstd = self._conanfile.settings.get_safe("compiler.cppstd") self._cpp_std = to_cppstd_flag(compiler, compiler_version, cppstd) + cstd = self._conanfile.settings.get_safe("compiler.cstd") + self._c_std = to_cstd_flag(cstd) + self._b_vscrt = None if compiler in ("msvc", "clang"): vscrt = msvc_runtime_flag(self._conanfile) @@ -433,6 +437,7 @@ def _sanitize_format(v): "b_ndebug": to_meson_value(self._b_ndebug), # boolean as string # https://mesonbuild.com/Builtin-options.html#compiler-options "cpp_std": self._cpp_std, + "c_std": self._c_std, "c_args": to_meson_value(self._filter_list_empty_fields(self.c_args)), "c_link_args": to_meson_value(self._filter_list_empty_fields(self.c_link_args)), "cpp_args": to_meson_value(self._filter_list_empty_fields(self.cpp_args)), diff --git a/conans/model/conan_file.py b/conans/model/conan_file.py index 46789591f56..0e16fb8d43e 100644 --- a/conans/model/conan_file.py +++ b/conans/model/conan_file.py @@ -138,6 +138,7 @@ def serialize(self): result["version"] = str(self.version) if self.version is not None else None result["topics"] = list(self.topics) if self.topics is not None else None result["package_type"] = str(self.package_type) + result["languages"] = self.languages settings = self.settings if settings is not None: diff --git a/conans/model/conanfile_interface.py b/conans/model/conanfile_interface.py index 0ebb1b1ffe2..2130ff85be2 100644 --- a/conans/model/conanfile_interface.py +++ b/conans/model/conanfile_interface.py @@ -100,6 +100,10 @@ def is_build_context(self): def package_type(self): return self._conanfile.package_type + @property + def languages(self): + return self._conanfile.languages + @property def info(self): return self._conanfile.info diff --git a/conans/test/integration/toolchains/meson/test_mesontoolchain.py b/conans/test/integration/toolchains/meson/test_mesontoolchain.py index 6ce4b163f13..d2238cd5a7f 100644 --- a/conans/test/integration/toolchains/meson/test_mesontoolchain.py +++ b/conans/test/integration/toolchains/meson/test_mesontoolchain.py @@ -137,6 +137,7 @@ def test_correct_quotes(): compiler=gcc compiler.version=9 compiler.cppstd=17 + compiler.cstd=11 compiler.libcxx=libstdc++11 build_type=Release """) @@ -147,6 +148,7 @@ def test_correct_quotes(): t.run("install . -pr:h=profile -pr:b=profile") content = t.load(MesonToolchain.native_filename) assert "cpp_std = 'c++17'" in content + assert "c_std = 'c11'" in content assert "backend = 'ninja'" in content assert "buildtype = 'release'" in content From c6695766398f440b29c2489070b1498140d4d979 Mon Sep 17 00:00:00 2001 From: memsharded Date: Tue, 9 Apr 2024 23:02:00 +0200 Subject: [PATCH 10/15] wip --- .../toolchains/meson/test_mesontoolchain.py | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/conans/test/integration/toolchains/meson/test_mesontoolchain.py b/conans/test/integration/toolchains/meson/test_mesontoolchain.py index d2238cd5a7f..87944f87be4 100644 --- a/conans/test/integration/toolchains/meson/test_mesontoolchain.py +++ b/conans/test/integration/toolchains/meson/test_mesontoolchain.py @@ -5,6 +5,7 @@ import pytest from conan.tools.meson import MesonToolchain +from conans.test.assets.genconanfile import GenConanfile from conans.test.utils.tools import TestClient @@ -137,7 +138,6 @@ def test_correct_quotes(): compiler=gcc compiler.version=9 compiler.cppstd=17 - compiler.cstd=11 compiler.libcxx=libstdc++11 build_type=Release """) @@ -148,6 +148,28 @@ def test_correct_quotes(): t.run("install . -pr:h=profile -pr:b=profile") content = t.load(MesonToolchain.native_filename) assert "cpp_std = 'c++17'" in content + assert "backend = 'ninja'" in content + assert "buildtype = 'release'" in content + + +def test_c_std(): + profile = textwrap.dedent(""" + [settings] + os=Windows + arch=x86_64 + compiler=gcc + compiler.version=9 + compiler.cstd=11 + build_type=Release + """) + t = TestClient() + t.save({"conanfile.py": GenConanfile().with_settings("os", "compiler", "build_type", "arch") + .with_generator("MesonToolchain") + .with_class_attribute("languages='C'"), + "profile": profile}) + + t.run("install . -pr:h=profile -pr:b=profile") + content = t.load(MesonToolchain.native_filename) assert "c_std = 'c11'" in content assert "backend = 'ninja'" in content assert "buildtype = 'release'" in content From 0845c1f4b750577ab7f0224ae950b1911d2a39e8 Mon Sep 17 00:00:00 2001 From: memsharded Date: Wed, 10 Apr 2024 00:49:44 +0200 Subject: [PATCH 11/15] wip --- conan/tools/build/flags.py | 73 +++++++++++++++++++ conan/tools/gnu/autotoolstoolchain.py | 9 ++- conan/tools/microsoft/toolchain.py | 4 + conans/client/profile_loader.py | 31 ++++++++ .../autotools/autotools_toolchain_test.py | 15 ++++ .../unittests/tools/microsoft/test_msbuild.py | 5 +- 6 files changed, 132 insertions(+), 5 deletions(-) diff --git a/conan/tools/build/flags.py b/conan/tools/build/flags.py index 7dbb1e06b2f..8b20fed3dfe 100644 --- a/conan/tools/build/flags.py +++ b/conan/tools/build/flags.py @@ -482,3 +482,76 @@ def _cppstd_intel_cc(_, cppstd): "20": v20, "gnu20": vgnu20, "23": v23, "gnu23": vgnu23}.get(cppstd) return f'-std={flag}' if flag else None + + +def cstd_flag(conanfile) -> str: + """ + Returns flags specific to the C+standard based on the ``conanfile.settings.compiler``, + ``conanfile.settings.compiler.version`` and ``conanfile.settings.compiler.cstd``. + + It also considers when using GNU extension in ``settings.compiler.cstd``, reflecting it in the + compiler flag. Currently, it supports GCC, Clang, AppleClang, MSVC, Intel, MCST-LCC. + + In case there is no ``settings.compiler`` or ``settings.cstd`` in the profile, the result will + be an **empty string**. + + :param conanfile: The current recipe object. Always use ``self``. + :return: ``str`` with the standard C flag used by the compiler. + """ + compiler = conanfile.settings.get_safe("compiler") + compiler_version = conanfile.settings.get_safe("compiler.version") + cstd = conanfile.settings.get_safe("compiler.cstd") + + if not compiler or not compiler_version or not cstd: + return "" + + func = {"gcc": _cstd_gcc, + "clang": _cstd_clang, + "apple-clang": _cstd_apple_clang, + "msvc": _cstd_msvc}.get(compiler) + flag = None + if func: + flag = func(Version(compiler_version), str(cstd)) + return flag + + +def _cstd_gcc(gcc_version, cstd): + # TODO: Verify flags per version + flag = {"99": "c99", + "11": "c11", + "17": "c17", + "23": "c23"}.get(cstd, cstd) + return f'-std={flag}' if flag else None + + +def _cstd_clang(gcc_version, cstd): + # TODO: Verify flags per version + flag = {"99": "c99", + "11": "c11", + "17": "c17", + "23": "c23"}.get(cstd, cstd) + return f'-std={flag}' if flag else None + + +def _cstd_apple_clang(gcc_version, cstd): + # TODO: Verify flags per version + flag = {"99": "c99", + "11": "c11", + "17": "c17", + "23": "c23"}.get(cstd, cstd) + return f'-std={flag}' if flag else None + + +def cstd_msvc_flag(visual_version, cstd): + if cstd == "17": + if visual_version >= "192": + return "c17" + elif cstd == "11": + if visual_version >= "192": + return "c11" + return None + + +def _cstd_msvc(visual_version, cstd): + flag = cstd_msvc_flag(visual_version, cstd) + return f'/std:{flag}' if flag else None diff --git a/conan/tools/gnu/autotoolstoolchain.py b/conan/tools/gnu/autotoolstoolchain.py index c81e70abebc..2dd177a9b83 100644 --- a/conan/tools/gnu/autotoolstoolchain.py +++ b/conan/tools/gnu/autotoolstoolchain.py @@ -1,10 +1,10 @@ from conan.internal import check_duplicated_generator from conan.internal.internal_tools import raise_on_universal_arch -from conan.tools.apple.apple import apple_min_version_flag, is_apple_os, to_apple_arch, \ - apple_sdk_path, resolve_apple_flags +from conan.tools.apple.apple import is_apple_os, resolve_apple_flags from conan.tools.build import cmd_args_to_string, save_toolchain_args from conan.tools.build.cross_building import cross_building -from conan.tools.build.flags import architecture_flag, build_type_flags, cppstd_flag, build_type_link_flags, libcxx_flags +from conan.tools.build.flags import architecture_flag, build_type_flags, cppstd_flag, \ + build_type_link_flags, libcxx_flags, cstd_flag from conan.tools.env import Environment from conan.tools.gnu.get_gnu_triplet import _get_gnu_triplet from conan.tools.microsoft import VCVars, msvc_runtime_flag, unix_path, check_min_vs, is_msvc @@ -48,6 +48,7 @@ def __init__(self, conanfile, namespace=None, prefix="/"): self.build_type_link_flags = build_type_link_flags(self._conanfile.settings) self.cppstd = cppstd_flag(self._conanfile) + self.cstd = cstd_flag(self._conanfile) self.arch_flag = architecture_flag(self._conanfile.settings) self.libcxx, self.gcc_cxx11_abi = libcxx_flags(self._conanfile) self.fpic = self._conanfile.options.get_safe("fPIC") @@ -127,7 +128,7 @@ def cxxflags(self): @property def cflags(self): fpic = "-fPIC" if self.fpic else None - ret = [self.arch_flag, fpic, self.msvc_runtime_flag, self.sysroot_flag] + ret = [self.cstd, self.arch_flag, fpic, self.msvc_runtime_flag, self.sysroot_flag] apple_flags = [self.apple_isysroot_flag, self.apple_arch_flag, self.apple_min_version_flag] conf_flags = self._conanfile.conf.get("tools.build:cflags", default=[], check_type=list) vs_flag = self._add_msvc_flags(self.extra_cflags) diff --git a/conan/tools/microsoft/toolchain.py b/conan/tools/microsoft/toolchain.py index e5427001cae..5e33d51030c 100644 --- a/conan/tools/microsoft/toolchain.py +++ b/conan/tools/microsoft/toolchain.py @@ -27,6 +27,7 @@ class MSBuildToolchain(object): {{ defines }}%(PreprocessorDefinitions) {{ compiler_flags }} %(AdditionalOptions) {{ runtime_library }} + {% if cstd %}{{ cstd }}{% endif %} {{ cppstd }}{{ parallel }}{{ compile_options }} @@ -69,6 +70,7 @@ def __init__(self, conanfile): self.runtime_library = self._runtime_library(conanfile.settings) #: cppstd value. By default, ``compiler.cppstd`` one. self.cppstd = conanfile.settings.get_safe("compiler.cppstd") + self.cstd = conanfile.settings.get_safe("compiler.cstd") #: VS IDE Toolset, e.g., ``"v140"``. If ``compiler=msvc``, you can use ``compiler.toolset`` #: setting, else, it'll be based on ``msvc`` version. self.toolset = msvs_toolset(conanfile) @@ -139,6 +141,7 @@ def format_macro(key, value): self.ldflags.extend(sharedlinkflags + exelinkflags) cppstd = "stdcpp%s" % self.cppstd if self.cppstd else "" + cstd = f"stdc{self.cstd}" if self.cstd else "" runtime_library = self.runtime_library toolset = self.toolset or "" compile_options = self._conanfile.conf.get("tools.microsoft.msbuildtoolchain:compile_options", @@ -161,6 +164,7 @@ def format_macro(key, value): 'compiler_flags': " ".join(self.cxxflags + self.cflags), 'linker_flags': " ".join(self.ldflags), "cppstd": cppstd, + "cstd": cstd, "runtime_library": runtime_library, "toolset": toolset, "compile_options": compile_options, diff --git a/conans/client/profile_loader.py b/conans/client/profile_loader.py index fc83a734416..1f343529464 100644 --- a/conans/client/profile_loader.py +++ b/conans/client/profile_loader.py @@ -39,6 +39,7 @@ def profile_plugin(profile): except ConanException: pass _check_correct_cppstd(settings) + _check_correct_cstd(settings) def _check_correct_cppstd(settings): from conan.tools.scm import Version @@ -76,6 +77,36 @@ def _error(compiler, cppstd, min_version, version): "14": "190"}.get(cppstd) if mver and version < mver: _error(compiler, cppstd, mver, version) + + +def _check_correct_cstd(settings): + from conan.tools.scm import Version + def _error(compiler, cstd, min_version, version): + from conan.errors import ConanException + raise ConanException(f"The provided compiler.cstd={cstd} requires at least {compiler}" + f">={min_version} but version {version} provided") + cstd = settings.get("compiler.cstd") + version = settings.get("compiler.version") + + if cstd and version: + cstd = cstd.replace("gnu", "") + version = Version(version) + mver = None + compiler = settings.get("compiler") + if compiler == "gcc": + # TODO: right versions + mver = {}.get(cstd) + elif compiler == "clang": + # TODO: right versions + mver = {}.get(cstd) + elif compiler == "apple-clang": + # TODO: Right versions + mver = {}.get(cstd) + elif compiler == "msvc": + mver = {"17": "192", + "11": "192"}.get(cstd) + if mver and version < mver: + _error(compiler, cppstd, mver, version) """ diff --git a/conans/test/unittests/client/toolchain/autotools/autotools_toolchain_test.py b/conans/test/unittests/client/toolchain/autotools/autotools_toolchain_test.py index 6c7dd86b207..5917566e304 100644 --- a/conans/test/unittests/client/toolchain/autotools/autotools_toolchain_test.py +++ b/conans/test/unittests/client/toolchain/autotools/autotools_toolchain_test.py @@ -130,6 +130,21 @@ def test_cppstd(): assert "/std:c++17" in env["CXXFLAGS"] +def test_cstd(): + conanfile = ConanFileMock() + conanfile.settings = MockSettings( + {"build_type": "Release", + "arch": "x86", + "compiler": "gcc", + "compiler.libcxx": "libstdc++11", + "compiler.version": "7.1", + "compiler.cstd": "17"}) + conanfile.settings_build = MockSettings({"os": "Linux", "arch": "x86"}) + be = AutotoolsToolchain(conanfile) + env = be.vars() + assert "-std=c17" in env["CFLAGS"] + + def test_fpic(): conanfile = ConanFileMock() conanfile.settings = MockSettings({"os": "Linux"}) diff --git a/conans/test/unittests/tools/microsoft/test_msbuild.py b/conans/test/unittests/tools/microsoft/test_msbuild.py index 58e3285a6ec..e1d8429c5c5 100644 --- a/conans/test/unittests/tools/microsoft/test_msbuild.py +++ b/conans/test/unittests/tools/microsoft/test_msbuild.py @@ -104,7 +104,8 @@ def test_msbuild_standard(): conanfile.conf.define("tools.microsoft.msbuild:installation_path", ".") conanfile.settings = "os", "compiler", "build_type", "arch" conanfile.settings = Settings({"build_type": ["Release"], - "compiler": {"msvc": {"version": ["193"], "cppstd": ["20"]}}, + "compiler": {"msvc": {"version": ["193"], "cppstd": ["20"], + "cstd": ["17"]}}, "os": ["Windows"], "arch": ["x86_64"]}) conanfile.settings_build = conanfile.settings @@ -112,6 +113,7 @@ def test_msbuild_standard(): conanfile.settings.compiler = "msvc" conanfile.settings.compiler.version = "193" conanfile.settings.compiler.cppstd = "20" + conanfile.settings.compiler.cstd = "17" conanfile.settings.os = "Windows" conanfile.settings.arch = "x86_64" @@ -119,6 +121,7 @@ def test_msbuild_standard(): props_file = os.path.join(test_folder, 'conantoolchain_release_x64.props') msbuild.generate() assert 'stdcpp20' in load(props_file) + assert 'stdc17' in load(props_file) def test_resource_compile(): From 846d78bf178aced568dc857c63f8f00a28f40df8 Mon Sep 17 00:00:00 2001 From: memsharded Date: Wed, 10 Apr 2024 23:35:36 +0200 Subject: [PATCH 12/15] fix --- conans/test/integration/command_v2/test_inspect.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/conans/test/integration/command_v2/test_inspect.py b/conans/test/integration/command_v2/test_inspect.py index 2f186decdc5..b6aa3dfbd78 100644 --- a/conans/test/integration/command_v2/test_inspect.py +++ b/conans/test/integration/command_v2/test_inspect.py @@ -14,6 +14,7 @@ def test_basic_inspect(): ' shared: False', 'generators: []', 'label: ', + 'languages: []', 'name: foo', 'options:', ' shared: False', @@ -83,6 +84,7 @@ def test_normal_inspect(): 'generators: []', 'homepage: ', 'label: ', + 'languages: []', 'license: ', 'name: pkg', 'options:', @@ -126,6 +128,7 @@ class Pkg(ConanFile): tc.run("inspect .") assert ['generators: []', 'label: ', + 'languages: []', "license: ['MIT', 'Apache']", 'options:', 'options_definitions:', From dc43d968b1f5a0e8e0b2cb385d6c1831aa849524 Mon Sep 17 00:00:00 2001 From: memsharded Date: Tue, 21 May 2024 23:41:17 +0200 Subject: [PATCH 13/15] new language cppstd -> C management --- .../templates/target_configuration.py | 8 +- .../cmake/cmakedeps/test_auto_stdcpp.py | 48 ++++++++++ test/functional/toolchains/conftest.py | 89 +++++++++++++++++++ 3 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 test/functional/toolchains/cmake/cmakedeps/test_auto_stdcpp.py diff --git a/conan/tools/cmake/cmakedeps/templates/target_configuration.py b/conan/tools/cmake/cmakedeps/templates/target_configuration.py index 932f39c9513..21218f41cb2 100644 --- a/conan/tools/cmake/cmakedeps/templates/target_configuration.py +++ b/conan/tools/cmake/cmakedeps/templates/target_configuration.py @@ -29,6 +29,8 @@ def context(self): is_win = self.conanfile.settings.get_safe("os") == "Windows" auto_link = self.cmakedeps.get_property("cmake_set_interface_link_directories", self.conanfile) + link_languages = [lang if lang != "C++" else "CXX" for lang in self.conanfile.languages] + link_languages = ";".join(link_languages) return {"pkg_name": self.pkg_name, "root_target_name": self.root_target_name, "config_suffix": self.config_suffix, @@ -36,7 +38,8 @@ def context(self): "deps_targets_names": ";".join(deps_targets_names), "components_names": components_names, "configuration": self.cmakedeps.configuration, - "set_interface_link_directories": auto_link and is_win} + "set_interface_link_directories": auto_link and is_win, + "link_languages": link_languages} @property def template(self): @@ -128,6 +131,9 @@ def template(self): $<$:{{ pkg_var(pkg_name, 'LIB_DIRS', config_suffix) }}>) {%- endif %} + {% if link_languages %} + set_target_properties({{root_target_name}} PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "{{link_languages}}") + {%- endif %} {%- else %} diff --git a/test/functional/toolchains/cmake/cmakedeps/test_auto_stdcpp.py b/test/functional/toolchains/cmake/cmakedeps/test_auto_stdcpp.py new file mode 100644 index 00000000000..04dc65722dd --- /dev/null +++ b/test/functional/toolchains/cmake/cmakedeps/test_auto_stdcpp.py @@ -0,0 +1,48 @@ +import textwrap + + +def test_auto_cppstd(matrix_c_interface_client): + c = matrix_c_interface_client + + consumer = textwrap.dedent(""" + cmake_minimum_required(VERSION 3.15) + project(myapp C CXX) + + find_package(matrix REQUIRED) + + add_executable(app app.c) + target_link_libraries(app PRIVATE matrix::matrix) + """) + + conanfile = textwrap.dedent("""\ + import os + from conan import ConanFile + from conan.tools.cmake import CMake, cmake_layout + + class Recipe(ConanFile): + settings = "os", "compiler", "build_type", "arch" + package_type = "application" + generators = "CMakeToolchain", "CMakeDeps" + requires = "matrix/0.1" + + def layout(self): + cmake_layout(self) + + def build(self): + cmake = CMake(self) + cmake.configure() + cmake.build() + self.run(os.path.join(self.cpp.build.bindir, "app"), env="conanrun") + """) + app = textwrap.dedent(""" + #include "matrix.h" + int main(){ + matrix(); + return 0; + } + """) + c.save({"conanfile.py": conanfile, + "CMakeLists.txt": consumer, + "app.c": app}, clean_first=True) + c.run("build .") + assert "Hello Matrix!" in c.out diff --git a/test/functional/toolchains/conftest.py b/test/functional/toolchains/conftest.py index e1f294b62a6..4179e60aab0 100644 --- a/test/functional/toolchains/conftest.py +++ b/test/functional/toolchains/conftest.py @@ -1,5 +1,6 @@ import os import shutil +import textwrap import pytest @@ -48,3 +49,91 @@ def transitive_libraries(_transitive_libraries): c.cache_folder = os.path.join(temp_folder(), ".conan2") shutil.copytree(_transitive_libraries.cache_folder, c.cache_folder) return c + + +@pytest.fixture(scope="session") +def _matrix_c_interface_client(): + c = TestClient() + matrix_h = textwrap.dedent("""\ + #pragma once + #ifdef __cplusplus + extern "C" { + #endif + void matrix(); + #ifdef __cplusplus + } + #endif + """) + matrix_cpp = textwrap.dedent("""\ + #include "matrix.h" + #include + #include + + void matrix(){ + std::cout<< std::string("Hello Matrix!") < + $ + ) + set_target_properties(matrix PROPERTIES PUBLIC_HEADER "include/matrix.h") + + install(TARGETS matrix EXPORT matrixConfig) + export(TARGETS matrix + NAMESPACE matrix:: + FILE "${CMAKE_CURRENT_BINARY_DIR}/matrixConfig.cmake" + ) + + install(EXPORT matrixConfig + DESTINATION "${CMAKE_INSTALL_PREFIX}/matrix/cmake" + NAMESPACE matrix:: + ) + """) + conanfile = textwrap.dedent("""\ + from conan import ConanFile + from conan.tools.cmake import CMake, cmake_layout + + class Recipe(ConanFile): + name = "matrix" + version = "0.1" + settings = "os", "compiler", "build_type", "arch" + package_type = "static-library" + generators = "CMakeToolchain" + exports_sources = "CMakeLists.txt", "src/*", "include/*" + + languages = "C", "C++" + + def build(self): + cmake = CMake(self) + cmake.configure() + cmake.build() + + def layout(self): + cmake_layout(self) + + def package(self): + cmake = CMake(self) + cmake.install() + + def package_info(self): + self.cpp_info.libs = ["matrix"] + """) + c.save({"include/matrix.h": matrix_h, + "src/matrix.cpp": matrix_cpp, + "conanfile.py": conanfile, + "CMakeLists.txt": cmake}) + c.run("create .") + return c + + +@pytest.fixture() +def matrix_c_interface_client(_matrix_c_interface_client): + c = TestClient() + c.cache_folder = os.path.join(temp_folder(), ".conan2") + shutil.copytree(_matrix_c_interface_client.cache_folder, c.cache_folder) + return c From bdeb3c9aec0820cb691e7f31c4455ff3573cd5bb Mon Sep 17 00:00:00 2001 From: memsharded Date: Sat, 19 Oct 2024 20:19:57 +0200 Subject: [PATCH 14/15] merged --- conan/tools/cmake/toolchain/blocks.py | 1 - conans/model/conan_file.py | 1 - test/integration/package_id/test_cache_compatibles.py | 11 ++--------- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/conan/tools/cmake/toolchain/blocks.py b/conan/tools/cmake/toolchain/blocks.py index 037b641a8ef..8f9a2c85236 100644 --- a/conan/tools/cmake/toolchain/blocks.py +++ b/conan/tools/cmake/toolchain/blocks.py @@ -289,7 +289,6 @@ class CppStdBlock(Block): set(CMAKE_CXX_STANDARD {{ cppstd }}) set(CMAKE_CXX_EXTENSIONS {{ cppstd_extensions }}) set(CMAKE_CXX_STANDARD_REQUIRED ON) - variable_watch(CMAKE_CXX_STANDARD conan_modify_std_watch) {% endif %} {% if cstd %} diff --git a/conans/model/conan_file.py b/conans/model/conan_file.py index 11016afe228..f61cee57d38 100644 --- a/conans/model/conan_file.py +++ b/conans/model/conan_file.py @@ -49,7 +49,6 @@ class ConanFile: package_type = None vendor = False languages = [] - implements = [] provides = None diff --git a/test/integration/package_id/test_cache_compatibles.py b/test/integration/package_id/test_cache_compatibles.py index b13e088e8a0..4591b5d0071 100644 --- a/test/integration/package_id/test_cache_compatibles.py +++ b/test/integration/package_id/test_cache_compatibles.py @@ -316,11 +316,8 @@ def package_info(self): assert "pkg/0.1: CPPSTD: 17" in c.out def test_check_min_cstd(self): -<<<<<<< HEAD - """ test that the check_min_cstd works fine wiht compatibility -======= - """ test that the check_min_cstd works fine with compatibility ->>>>>>> develop2 + """ + test that the check_min_cstd works fine with compatibility """ conanfile = textwrap.dedent(""" from conan import ConanFile @@ -339,11 +336,7 @@ def package_info(self): c = TestClient() c.save({"conanfile.py": conanfile}) -<<<<<<< HEAD - settings = "-s compiler=gcc -s compiler.version=9 -s compiler.libcxx=libstdc++11" -======= settings = "-s compiler=gcc -s compiler.version=14 -s compiler.libcxx=libstdc++11" ->>>>>>> develop2 c.run(f"create . {settings} -s compiler.cstd=17") assert "pkg/0.1: valid standard!!" in c.out assert "pkg/0.1: CSTD: 17" in c.out From f500640d77656cd0befb0ec0701c3ab16818d2e5 Mon Sep 17 00:00:00 2001 From: memsharded Date: Sat, 19 Oct 2024 20:31:34 +0200 Subject: [PATCH 15/15] wip --- conan/tools/cmake/cmakedeps/templates/target_configuration.py | 2 +- test/functional/toolchains/cmake/cmakedeps/test_auto_stdcpp.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/conan/tools/cmake/cmakedeps/templates/target_configuration.py b/conan/tools/cmake/cmakedeps/templates/target_configuration.py index c3bf5f54651..b7f36e350c7 100644 --- a/conan/tools/cmake/cmakedeps/templates/target_configuration.py +++ b/conan/tools/cmake/cmakedeps/templates/target_configuration.py @@ -40,7 +40,7 @@ def context(self): "components_names": components_names, "configuration": self.cmakedeps.configuration, "set_interface_link_directories": auto_link and is_win, - "link_languages": link_languages} + "link_languages": None} @property def template(self): diff --git a/test/functional/toolchains/cmake/cmakedeps/test_auto_stdcpp.py b/test/functional/toolchains/cmake/cmakedeps/test_auto_stdcpp.py index 04dc65722dd..7e41ebb4598 100644 --- a/test/functional/toolchains/cmake/cmakedeps/test_auto_stdcpp.py +++ b/test/functional/toolchains/cmake/cmakedeps/test_auto_stdcpp.py @@ -6,7 +6,7 @@ def test_auto_cppstd(matrix_c_interface_client): consumer = textwrap.dedent(""" cmake_minimum_required(VERSION 3.15) - project(myapp C CXX) + project(myapp C) find_package(matrix REQUIRED)