Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Feature/language #16028

Open
wants to merge 21 commits into
base: develop2
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions conan/tools/build/__init__.py
Expand Up @@ -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
Expand Down
165 changes: 165 additions & 0 deletions 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"]
73 changes: 73 additions & 0 deletions conan/tools/build/flags.py
Expand Up @@ -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
35 changes: 25 additions & 10 deletions conan/tools/cmake/toolchain/blocks.py
Expand Up @@ -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):
Expand Down
6 changes: 3 additions & 3 deletions conan/tools/gnu/autotoolstoolchain.py
Expand Up @@ -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
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -128,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)
Expand Down
15 changes: 14 additions & 1 deletion conan/tools/meson/helpers.py
Expand Up @@ -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 = {
Expand Down Expand Up @@ -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)
7 changes: 7 additions & 0 deletions conan/tools/meson/toolchain.py
Expand Up @@ -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 %}
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -488,6 +494,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)),
Expand Down