conan-io / conan

Conan - The open-source C and C++ package manager
https://conan.io
MIT License
8.17k stars 974 forks source link

[feature] generic tool to check for minumum compiler support #8002

Closed prince-chrismc closed 5 months ago

prince-chrismc commented 3 years ago

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 validate(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))
SSE4 commented 3 years ago

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.

prince-chrismc commented 3 years ago

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

if tools.Version(self.settings.compiler.version) < min_version: ```sh $ 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 ```sh $ 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 ```sh $ 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 ```sh 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 ```sh $ 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.") ```
prince-chrismc commented 3 years ago

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

jgsogo commented 3 years ago

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?

prince-chrismc commented 3 years ago

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

Croydon commented 3 years ago

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

sourcedelica commented 3 years ago

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.

sourcedelica commented 3 years ago

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().

sourcedelica commented 3 years ago

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))
Minimonium commented 3 years ago

I have a PR open for quite a long time with a comprehensive implementation. While it provides a lot of different things - you can salvage a smaller scope tool.

https://github.com/conan-io/conan/pull/6713

Minimonium commented 3 years ago

Also related to https://github.com/conan-io/conan-center-index/issues/54

AbrilRBS commented 5 months ago

Closing as not planned - As CCI reviewers, we're ok with recipes repeating this pattern where necessary. Note that in CCI, it's mostly used for cppstd checks in Conan 1. Conan 2 models this property way better, so that the check becomes irrelevant in most cases.

The cases that would still need it would not benefit from having this logic abstracted out, because as commented here, there are lots of variations of the expected behaviour

Thanks everyone for your input!