Conan: [feature] generic tool to check for minumum compiler support

Created on 4 Nov 2020  ยท  9Comments  ยท  Source: conan-io/conan

There is this piece of boiler plate that get's copied a LOT in CCI, but it's also something large project that migrate to newer compilers need to handle as well...

Would this be something that could be added to conans.tools ?

    @property
    def _minimum_cpp_standard(self):
        return 14

    @property
    def _minimum_compilers_version(self):
        return {
            "Visual Studio": "15",
            "gcc": "5",
            "clang": "3.4",
            "apple-clang": "5.1",
        }

    def configure(self):
        if self.settings.compiler.get_safe("cppstd"):
            tools.check_min_cppstd(self, self._minimum_cpp_standard)
        min_version = self._minimum_compilers_version.get(str(self.settings.compiler))
        if not min_version:
            self.output.warn("{} recipe lacks information about the {} compiler support.".format(
                self.name, self.settings.compiler))
        else:
            if tools.Version(self.settings.compiler.version) < min_version:
                raise ConanInvalidConfiguration("{} requires C++{} support. The current compiler {} {} does not support it.".format(
                    self.name, self._minimum_cpp_standard, self.settings.compiler, self.settings.compiler.version))
queue look into

Most helpful comment

I agree that not all consumers have cppstd in their profiles. We don't so far. It seems to me that the C++ standard belongs to the recipe, not the profile. I use one profile to build packages with different C++ standard requirements. Some require C++11, others C++14, others C++17.

All 9 comments

as this is quite a repeating patter in conan-center-index (right now, 10 occurrences for _minimum_compilers_version), I support to make a tool for it to help recipe authors.

depending which substring you look for you get different lists! With different variations!

if tools.Version(self.settings.compiler.version) < min_version:

$ grep -r 'if tools.Version(self.settings.compiler.version) < min_version:'
recipes/cppcmd/all/conanfile.py:            if tools.Version(self.settings.compiler.version) < min_version:
recipes/cxxopts/all/conanfile.py:            if tools.Version(self.settings.compiler.version) < min_version:
recipes/folly/all/conanfile.py:            if tools.Version(self.settings.compiler.version) < min_version:
recipes/json-schema-validator/all/conanfile.py:            if tools.Version(self.settings.compiler.version) < min_version:
recipes/namedtype/all/conanfile.py:            if tools.Version(self.settings.compiler.version) < min_version:
recipes/skyr-url/all/conanfile.py:            if tools.Version(self.settings.compiler.version) < min_version:
recipes/tomlplusplus/all/conanfile.py:            if tools.Version(self.settings.compiler.version) < min_version:
recipes/twitch-native-ipc/all/conanfile.py:            if tools.Version(self.settings.compiler.version) < min_version:
recipes/twitchtv-libsoundtrackutil/all/conanfile.py:            if tools.Version(self.settings.compiler.version) < min_version:

_minimum_compilers_version

$ grep -r '_minimum_compilers_version'
recipes/capnproto/all/conanfile.py:    def _minimum_compilers_version(self):
recipes/capnproto/all/conanfile.py:        minimum_version = self._minimum_compilers_version.get(str(self.settings.compiler), False)
recipes/cpp-sort/all/conanfile.py:    def _minimum_compilers_version(self):
recipes/cpp-sort/all/conanfile.py:            min_version = self._minimum_compilers_version[str(compiler)]
recipes/cppcmd/all/conanfile.py:    def _minimum_compilers_version(self):
recipes/cppcmd/all/conanfile.py:        min_version = self._minimum_compilers_version.get(
recipes/cxxopts/all/conanfile.py:    def _minimum_compilers_version(self):
recipes/cxxopts/all/conanfile.py:        min_version = self._minimum_compilers_version.get(str(self.settings.compiler))
recipes/eastl/all/conanfile.py:    def _minimum_compilers_version(self):
recipes/eastl/all/conanfile.py:        mininum_compiler_version = self._minimum_compilers_version.get(str(self.settings.compiler))
recipes/folly/all/conanfile.py:    def _minimum_compilers_version(self):
recipes/folly/all/conanfile.py:        min_version = self._minimum_compilers_version.get(
recipes/namedtype/all/conanfile.py:    def _minimum_compilers_version(self):
recipes/namedtype/all/conanfile.py:        min_version = self._minimum_compilers_version.get(str(self.settings.compiler))
recipes/pfr/all/conanfile.py:    def _minimum_compilers_version(self):
recipes/pfr/all/conanfile.py:            min_version = self._minimum_compilers_version[str(compiler)]
recipes/skyr-url/all/conanfile.py:    def _minimum_compilers_version(self):
recipes/skyr-url/all/conanfile.py:        min_version = self._minimum_compilers_version.get(
recipes/tomlplusplus/all/conanfile.py:    def _minimum_compilers_version(self):
recipes/tomlplusplus/all/conanfile.py:        min_version = self._minimum_compilers_version.get(

minimum_required_compiler_version

$ grep -r 'minimum_required_compiler_version'
recipes/argparse/all/conanfile.py:            minimum_required_compiler_version = self._compiler_required_cpp17[str(self.settings.compiler)]
recipes/argparse/all/conanfile.py:            if tools.Version(self.settings.compiler.version) < minimum_required_compiler_version:
recipes/bitserializer/0.10/conanfile.py:            minimum_required_compiler_version = self._supported_compilers[str(self.settings.compiler)]
recipes/bitserializer/0.10/conanfile.py:            if tools.Version(self.settings.compiler.version) < minimum_required_compiler_version:

minimal_version

grep -r 'minimal_version = {'
recipes/bx/all/conanfile.py:        minimal_version = {
recipes/cmake/3.x.x/conanfile.py:        minimal_version = {
recipes/cpp-jwt/all/conanfile.py:        minimal_version = {
recipes/cpp-taskflow/all/conanfile.py:        minimal_version = {
recipes/dataframe/all/conanfile.py:        minimal_version = {
recipes/di/all/conanfile.py:        minimal_version = {
recipes/entt/3.x.x/conanfile.py:        minimal_version = {
recipes/frozen/all/conanfile.py:        minimal_version = {
recipes/fruit/all/conanfile.py:        minimal_version = {
recipes/function2/all/conanfile.py:        minimal_version = {
recipes/godot-cpp/all/conanfile.py:        minimal_version = {
recipes/libpqxx/all/conanfile.py:        minimal_version = {
recipes/restinio/all/conanfile.py:        minimal_version = {
recipes/sigslot/all/conanfile.py:        minimal_version = {
recipes/svgwrite/all/conanfile.py:        minimal_version = {
recipes/tabulate/all/conanfile.py:        minimal_version = {
recipes/taskflow/all/conanfile.py:        minimal_version = {
recipes/tl/all/conanfile.py:        minimal_version = {
recipes/uwebsockets/all/conanfile.py:        minimal_version = {

โ— I think this search is the best representative of the problem

$ grep -r 'self.output.warn(' | grep 'compiler' | wc -l
37

self.output.warn( + compiler

$ grep -r 'self.output.warn(' | grep 'compiler'
recipes/argparse/all/conanfile.py:            self.output.warn("This recipe has no support for the current compiler. Please consider adding it.")
recipes/bitserializer/0.10/conanfile.py:            self.output.warn("This recipe has no support for the current compiler. Please consider adding it.")
recipes/capnproto/all/conanfile.py:            self.output.warn("Cap'n Proto requires C++14. Your compiler is unknown. Assuming it supports C++14.")
recipes/celero/all/conanfile.py:            self.output.warn("celero requires C++14. Your compiler is unknown. Assuming it supports C++14.")
recipes/cpp-taskflow/all/conanfile.py:            self.output.warn("%s recipe lacks information about the %s compiler"
recipes/cppbenchmark/all/conanfile.py:            self.output.warn("cppbenchmark requires C++17. Your compiler is unknown. Assuming it supports C++17.")
recipes/cppcmd/all/conanfile.py:            self.output.warn("{} recipe lacks information about the {} compiler support.".format(
recipes/cppcommon/all/conanfile.py:            self.output.warn("cppcommon requires C++17. Your compiler is unknown. Assuming it supports C++17.")
recipes/cxxopts/all/conanfile.py:            self.output.warn("{} recipe lacks information about the {} compiler support.".format(
recipes/elfutils/all/conanfile.py:            self.output.warn("Compiler %s is not gcc." % self.settings.compiler)
recipes/folly/all/conanfile.py:            self.output.warn("{} recipe lacks information about the {} compiler support.".format(
recipes/h5pp/all/conanfile.py:            self.output.warn("h5pp requires C++17. Your compiler is unknown. Assuming it supports C++17.")
recipes/hana/all/conanfile.py:            self.output.warn("This recipe might not support the compiler. Consider adding it.")
recipes/json-schema-validator/all/conanfile.py:            self.output.warn("{} recipe lacks information about the {} compiler support.".format(
recipes/magic_enum/all/conanfile.py:            self.output.warn("magic_enum requires C++17. Your compiler is unknown. Assuming it supports C++17.")
recipes/morton-nd/all/conanfile.py:            self.output.warn("morton-nd requires C++14. Your compiler is unknown. Assuming it supports C++14.")
recipes/ms-gsl/all/conanfile.py:            self.output.warn("ms-gsl requires C++14. Your compiler is unknown. Assuming it supports C++14.")
recipes/namedtype/all/conanfile.py:            self.output.warn("{} recipe lacks information about the {} compiler support.".format(
recipes/nanodbc/all/conanfile.py:            self.output.warn("nanodbc requires c++14, but is unknown to this recipe. Assuming your compiler supports c++14.")
recipes/openblas/all/conanfile.py:            self.output.warn("Building with lapack support requires a Fortran compiler.")
recipes/packio/all/conanfile.py:            self.output.warn("packio requires C++17. Your compiler is unknown. Assuming it supports C++17.")
recipes/pprint/all/conanfile.py:            self.output.warn("pprint needs a c++17 capable compiler")
recipes/quill/all/conanfile.py:            self.output.warn("Quill requires C++14. Your compiler is unknown. Assuming it supports C++14.")
recipes/simdjson/all/conanfile.py:            self.output.warn("{} requires C++17. Your compiler is unknown. Assuming it supports C++17.".format(self.name))
recipes/skyr-url/all/conanfile.py:            self.output.warn("{} recipe lacks information about the {} compiler support.".format(
recipes/sol2/3.x.x/conanfile.py:            self.output.warn("sol2 requires C++17. Your compiler is unknown. Assuming it supports C++17.")
recipes/spy/all/conanfile.py:            self.output.warn("Spy requires C++17. Your compiler is unknown. Assuming it supports C++17.")
recipes/structopt/all/conanfile.py:            self.output.warn("{} recipe lacks information about the {} compiler standard version support".format(self.name, compiler))
recipes/svgwrite/all/conanfile.py:            self.output.warn("{} recipe lacks information about the {} compiler"
recipes/swig/all/conanfile.py:            self.output.warn("Visual Studio compiler cannot create ccache-swig. Disabling ccache-swig.")
recipes/tabulate/all/conanfile.py:            self.output.warn("%s recipe lacks information about the %s compiler"
recipes/taocpp-json/all/conanfile.py:            self.output.warn("taocpp-json requires C++17. Your compiler is unknown. Assuming it supports C++17.")
recipes/taocpp-taopq/all/conanfile.py:            self.output.warn("taocpp-taopq requires C++17. Your compiler is unknown. Assuming it supports C++17.")
recipes/tomlplusplus/all/conanfile.py:            self.output.warn("{} recipe lacks information about the {} compiler support.".format(
recipes/twitch-native-ipc/all/conanfile.py:            self.output.warn("unknown compiler, assuming C++17 support")
recipes/twitchtv-libsoundtrackutil/all/conanfile.py:            self.output.warn("unknown compiler, assuming C++17 support")
recipes/zxing-cpp/all/conanfile.py:            self.output.warn("This recipe might not support the compiler. Consider adding it.")

Recently we noticed the version compare was failing when settings.compiler.version = 6 and was minimum_version = 6.4

Now everywhere where the aforementioned code exists could benefit from the following blob

https://github.com/conan-io/conan-center-index/pull/3472/files#diff-
f262a4dd3c2a23c9985dbbcf5ed0ae18bc3b8bca3fa0f80ffa8dcad9c075b421R32-R38

IMHO, we already have this functionality implemented in the check_min_cppstd tool. This necessity arises because we are not using profiles with different cppstd values but the default ones, don't you think so?

Yes, I think that would cover the majority of use cases.

There's a very slim number which require minimum versions because the CXX standard in some compilers is only partial, just because you set C++17 does not mean it's all there.

EDIT: This happens https://github.com/conan-io/conan-center-index/pull/4631

Having a check_min_compiler_version (or something like that) could still prevent this boilerplate in such cases. CCI had quite some recipe bugs because of bugs in these checks

And not all consumers have the cppstd in their profiles either

I agree that not all consumers have cppstd in their profiles. We don't so far. It seems to me that the C++ standard belongs to the recipe, not the profile. I use one profile to build packages with different C++ standard requirements. Some require C++11, others C++14, others C++17.

I am currently working on a recipe where some versions of the package require C++11 and later versions require C++17. It would be good if this tool supported that - for example if _minimum_compilers_version() took a cppstd parameter instead of using a hardcoded _minimum_cpp_standard().

For example:

def _minimum_compilers_version(self, cppstd):
    standards = {
        "11": {
            "Visual Studio": "15",
            "gcc": "4.8",
            "clang": "4",
            "apple-clang": "9",
        },
        "17": {
            "Visual Studio": "16",
            "gcc": "7",
            "clang": "5",
            "apple-clang": "10",
        },
    }
    return standards.get(cppstd) or {}

def validate(self):
    cppstd = "11" if Version(self.version) <= "0.17.6" else "17"
    if self.settings.compiler.get_safe("cppstd"):
        tools.check_min_cppstd(self, cppstd)
    min_version = self._minimum_compilers_version(cppstd).get(str(self.settings.compiler))
    if not min_version:
        self.output.warn("{} recipe lacks information about the {} compiler support.".format(
            self.name, self.settings.compiler))
    else:
        if tools.Version(self.settings.compiler.version) < min_version:
            raise ConanInvalidConfiguration("{} requires C++{} support. The current compiler {} {} does not support it.".format(
                self.name, cppstd, self.settings.compiler, self.settings.compiler.version))
Was this page helpful?
0 / 5 - 0 ratings