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..2d69eaba97d
--- /dev/null
+++ b/conan/tools/build/cstd.py
@@ -0,0 +1,176 @@
+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):
+ if version < "4.7":
+ return ["99", "gnu99"]
+ if version < "8":
+ return ["99", "gnu99", "11", "gnu11"]
+ if version < "14":
+ return ["99", "gnu99", "11", "gnu11", "17", "gnu17"]
+ return ["99", "gnu99", "11", "gnu11", "17", "gnu17", "23", "gnu23"]
+
+
+def _msvc_supported_cstd(version):
+ if version < "192":
+ return []
+ return ["11", "17"]
+
+
+def _clang_supported_cstd(version):
+ if version < "3":
+ return ["99", "gnu99"]
+ if version < "6":
+ return ["99", "gnu99", "11", "gnu11"]
+ if version < "18":
+ return ["99", "gnu99", "11", "gnu11", "17", "gnu17"]
+ return ["99", "gnu99", "11", "gnu11", "17", "gnu17", "23", "gnu23"]
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/cmake/toolchain/blocks.py b/conan/tools/cmake/toolchain/blocks.py
index 26cb7ba0707..34021ece8e5 100644
--- a/conan/tools/cmake/toolchain/blocks.py
+++ b/conan/tools/cmake/toolchain/blocks.py
@@ -249,24 +249,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/conan/tools/gnu/autotoolstoolchain.py b/conan/tools/gnu/autotoolstoolchain.py
index c0a8655d317..f76edb7d1bb 100644
--- a/conan/tools/gnu/autotoolstoolchain.py
+++ b/conan/tools/gnu/autotoolstoolchain.py
@@ -5,8 +5,7 @@
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
+ 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
@@ -49,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")
@@ -130,7 +130,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/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 127eaf77c0b..743d38cbb25 100644
--- a/conan/tools/meson/toolchain.py
+++ b/conan/tools/meson/toolchain.py
@@ -109,6 +109,9 @@ class MesonToolchain(object):
{% if cpp_std %}
cpp_std = '{{cpp_std}}'
{% endif %}
+ {% if c_std %}
+ c_std = '{{c_std}}'
+ {% endif %}
{% if backend %}
backend = '{{backend}}'
{% endif %}
@@ -188,6 +191,9 @@ def __init__(self, conanfile, backend=None, native=False):
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)
@@ -505,6 +511,7 @@ def _context(self):
"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/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/conanfile/configure.py b/conans/client/conanfile/configure.py
index 116a879a820..4b0aa99f1eb 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):
@@ -15,6 +16,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)
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")
diff --git a/conans/client/conf/__init__.py b/conans/client/conf/__init__.py
index 9dfe94a1b04..23b5e28f533 100644
--- a/conans/client/conf/__init__.py
+++ b/conans/client/conf/__init__.py
@@ -100,6 +100,7 @@
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, 194]
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, v144]
+ 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 1d67e7cf5e6..a23669c8870 100644
--- a/conans/client/graph/compatibility.py
+++ b/conans/client/graph/compatibility.py
@@ -23,7 +23,7 @@ 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
@@ -44,6 +44,14 @@ def cppstd_compat(conanfile):
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])
+
if compiler == "msvc":
msvc_fallback = {"194": "193"}.get(compiler_version)
if msvc_fallback:
diff --git a/conans/client/profile_loader.py b/conans/client/profile_loader.py
index 6b1ba23e81a..cc4763fba8e 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/model/conan_file.py b/conans/model/conan_file.py
index 96e819ff128..7fcdbf8ac23 100644
--- a/conans/model/conan_file.py
+++ b/conans/model/conan_file.py
@@ -47,6 +47,7 @@ class ConanFile:
default_options = None
default_build_options = None
package_type = None
+ languages = []
implements = []
@@ -91,6 +92,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,
@@ -134,6 +137,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/test/integration/command_v2/test_inspect.py b/test/integration/command_v2/test_inspect.py
index 129ee376181..8de7c470ac1 100644
--- a/test/integration/command_v2/test_inspect.py
+++ b/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:',
diff --git a/test/integration/package_id/test_cache_compatibles.py b/test/integration/package_id/test_cache_compatibles.py
index 35fe89e9295..74f1c364c46 100644
--- a/test/integration/package_id/test_cache_compatibles.py
+++ b/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``
diff --git a/test/integration/toolchains/meson/test_mesontoolchain.py b/test/integration/toolchains/meson/test_mesontoolchain.py
index 588873848a3..d9ca23ea9e4 100644
--- a/test/integration/toolchains/meson/test_mesontoolchain.py
+++ b/test/integration/toolchains/meson/test_mesontoolchain.py
@@ -196,6 +196,29 @@ def test_correct_quotes():
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
+
+
def test_deactivate_nowrap():
# https://github.com/conan-io/conan/issues/10671
t = TestClient()
diff --git a/test/unittests/client/toolchain/autotools/autotools_toolchain_test.py b/test/unittests/client/toolchain/autotools/autotools_toolchain_test.py
index 5ea435a3ffa..e5afa10d744 100644
--- a/test/unittests/client/toolchain/autotools/autotools_toolchain_test.py
+++ b/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/test/unittests/tools/microsoft/test_msbuild.py b/test/unittests/tools/microsoft/test_msbuild.py
index 9f0e5b1bf8a..3fc98453183 100644
--- a/test/unittests/tools/microsoft/test_msbuild.py
+++ b/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():