conan compiler and compiler version enforcement

Created on 14 Oct 2017  路  10Comments  路  Source: conan-io/conan

Hello,
(sorry, this is going to be long description)
I would like to start discussion about usage of settings compiler and compiler.version. that primary applies to GCC and Clang style compilers (include Apple Clang, MingGW, Cross-Compilers for Android, Raspberry Pi, etc).
my be I don't understand such settings and use-cases completely, but they appear to have some design flaws. lemme explain several use-cases.
they in general apply in two major situations:
1. you have more than one compiler/compiler version in system, for example:
- having _Clang_ together with _GCC_ on Linux machine
- having multiple GCC versions on Linux machine, e.g. _gcc_ invokes _gcc-7_, while _gcc-5_ and _gcc-6_ executable also available
- having _Apple Clang_ from Xcode and _Clang_ installed from brew (which is vanilla LLVM's Clang)
in mentioned case,s you have default conan profile with compiler version auto-detected, but you are going to compile with non-default one.
2. cross-compiling (e.g. for Android or Raspberry Pi).

assume we have GCC as default compiler and want to compile with Clang, we call something like:
conan install -s compiler=clang -s compiler.version=5.0
or we have a profile like:

[settings]
compiler=clang
compiler.version=5.0
  • now most recipes will just trigger configure-style builds using AutoToolsBuildEnvironment helper, which will just use default system compiler. build will be successful, and will have package id matching specified conan compiler and its version, however, actually it will be built using different compiler. user will not have any warning about it.
  • if user will invoke recipes using CMake or test_package, they will likely fail, because conan_basic_setup verifies actual compiler

ok, there is a way to solve it, by specifying environment variables pointing to the actual compiler. users usually do something like:

[settings]
compiler=clang
compiler.version=5.0
[env]
CC=/usr/local/bin/clang
CXX=/usr/local/bin/clang++

again, there are few problems with this approach:

  • there is no guarantee settings match actual compiler in use, e.g. _/usr/local/bin/clang_ can point to _Clang 4.0_. that can easily go out of sync due to the compiler/toolchain update, for example
  • now settings compiler and compiler.version appear absolutely redundant, since we specified compiler in environment variables.
  • it's something non-portable - you can't just share your profile or conan install command line, since other linux machine may have another compiler version (typical situation if your team is using different linux distributions)

then I came up with the following solution for myself and my team (using shell script to invoke conan install):

# script for GCC - detects compiler version and passes to conan
export CC=gcc
export CXX=g++
COMPILER_MAJOR=$(touch test.c && $CC -E -dM test.c  | grep -o '__GNUC__ [[:digit:]]' | cut -d' ' -f2)
COMPILER_MINOR=$(touch test.c && $CC -E -dM test.c  | grep -o '__GNUC_MINOR__ [[:digit:]]' | cut -d' ' -f2)
conan install -s compiler=gcc -s compiler.version=$COMPILER_MAJOR.$COMPILER_MINOR ...
# script for Clang - detects compiler version and passes to conan
export CC=clang
export CXX=clang++
COMPILER_MAJOR=$(touch test.c && $CC -E -dM test.c  | grep -o '__clang_major__ [[:digit:]]' | cut -d' ' -f2)
COMPILER_MINOR=$(touch test.c && $CC -E -dM test.c  | grep -o '__clang_minor__ [[:digit:]]' | cut -d' ' -f2)
conan install -s compiler=clang -s compiler.version=$COMPILER_MAJOR.$COMPILER_MINOR ...

that works like a charm, but _compiler_ and _compiler version_ settings in profiles appear useless after that.

I propose something to improve situation (NOTE : it's not a full solution, but just a piece of puzzle):
1. for AutoToolsBuildEnvironment helper, set also CC, CXX environment variables (and few more for cross-compiling as well: AR, RANLIB, STRIP, etc):
* for GCC, I would set CC=os.environ.get('CC', tools.which('gcc'))
* for Clang- similar, CC=os.environ.get('CC', tools.which('clang'))
* for Apple Clang, CC=$(xcrun -find clang -sdk macosx) (or -sdk iphoneos for iOS, etc)
* for Android GCC (ARM), CC=os.environ.get('CC', tools.which('arm-linux-androideabi-gcc'))
* for Android Clang (ARM), CC=os.environ.get('CC', tools.which('arm-linux-androideabi-clang'))
* others are similarly
2. invoke version detection (like in scrips above) to verify that compiler.version matches actual compiler version in AutoToolsBuildEnvironment helper, and compiler matches actual compiler (so, similar to that CMake conan helpers do), and print warning, or may be even throw an error.

if no objections, I can write PRs for that proposals

if someone run into similar problems, please tell me how do you solve it in your workflows.
feedback is very much appreciated.

feature

Most helpful comment

I have also been caught out by this. I thought the compiler settings will encourage conan into a detection mode for the requested compiler and pass/fail accordingly, but it is not supported yet.

On windows, I use tools.vcvars_command(self.settings) and that is sufficient for utilizing -s compiler.version=xx for msvc. Like @SSE4 mentions, supporting it through AutoToolsBuildEnvironment sounds like a good solution.

Detection should in general at least issue a clear warning the user if the compiler/version requested is not the one used. This would at least alert people to the inconsistency.

I would also go a step further and introduce settings.linker, settings.archiver, etc. That would provide a clear way to override and enforce toolset. For example, I think that setting CC/CXX on macosx with apple-clang and llvm clang installed is not sufficient. LLVM ar and ld are actually llvm-ar and llvm-ld respectively, so despite setting the right compiler, the ar and ld from apple-clang will be used.

I also solve this currently setting the environment up in advance through a build script (something that we should do without, given conan is so awesomely designed).

All 10 comments

@solvingj @sigmoidal your input is very much appreciated

I have also been caught out by this. I thought the compiler settings will encourage conan into a detection mode for the requested compiler and pass/fail accordingly, but it is not supported yet.

On windows, I use tools.vcvars_command(self.settings) and that is sufficient for utilizing -s compiler.version=xx for msvc. Like @SSE4 mentions, supporting it through AutoToolsBuildEnvironment sounds like a good solution.

Detection should in general at least issue a clear warning the user if the compiler/version requested is not the one used. This would at least alert people to the inconsistency.

I would also go a step further and introduce settings.linker, settings.archiver, etc. That would provide a clear way to override and enforce toolset. For example, I think that setting CC/CXX on macosx with apple-clang and llvm clang installed is not sufficient. LLVM ar and ld are actually llvm-ar and llvm-ld respectively, so despite setting the right compiler, the ar and ld from apple-clang will be used.

I also solve this currently setting the environment up in advance through a build script (something that we should do without, given conan is so awesomely designed).

It's obviously a real problem, but one I can't say I've dealt with personally. I've read your proposal and I agree it's likely a good piece to a final solution.

I understand the issue, please let me try to explain a bit the rationale behind the current implementation.

Conan completely separates the concepts of "binary package configuration" and "build environment". The "binary package configuration" is defined by the settings (compiler, compiler.version, arch, etc) and options (shared/static). This is totally irrespective of what compilers could be installed, configured or not in a given machine, you can install a Linux gcc 5.3 package binary in a Windows machine with no installed compiler at all, or with a VS compiler.

This doesn't mean that the current default settings.yml is not subject to improvements, certainly it can be improved and extended, while we keep learning about some reasonable common denominator for a large part of the community.

Then, mismaches can certainly appear when it is necessary to build binary packages from sources. So far, conan has implemented some basic detections in the cmake generator, but that is all. If not using cmake, mismatches can go undetected.

I agree that there is place for improvement here, but I also think that part of the conan flexibility to adapt to many different situations and environment is precisely because it is very agnostic of these things. You define your own settings, define your environment, and if everything is properly declared, it will run smoothly. If you make a mistake and your settings doesn't match the environment, and there will be problems. I still have to read meaningful compiler error messages, like "ey, you are linking with the incorrect build_type", "the libstdc++11 you are using is incompatible with the library libstc++ linkage". Instead I will get weird link errors about some wstring things.

I say this, because we have to be very careful regarding this kind of feature. Implement some checks that will write warnings and nobody will read them, raise Errors, and it will eventually happen that you will be blocking some users, because they have a different environment setup, that happens to be correct, but conan code is misdetecting for some reason as incompatible. I have been there, suffer that with lots of pain, and I prefer to be on the "conan is very dumb, it doesn't detect this mismatch" than "this smart conan is blocking me".

Said that, I believe there are lots of useful information in this thread, and I think some of the above checks could be introduced. Thanks for such detailed info. Maybe learn first, I would say do warnings, opt-in as werrors.

We ran into this issue today (first day of using conan company wide). One of our developers did a conan install and for some reason, the dependencies were built with gcc 4.9, even though the conan profile said 5.4. Then, cmake complained when building the consuming application, because of the version mismatch.

Another problem I see is with Xcode on macOS - since it comes with the compiler, you always have to remember to keep the conan profile in sync with it, making sharing the conan profiles impractical. That's a big problem, since sharing the conan profiles was our solution for supporting cross compiling.

I don't have a good solution for this yet, but I'll update this once we figure out a good workflow for us. I think we'll probably end up doing something like @SSE4 where we specify the settings in a shell script instead of a profile.

Hi @avf

For the first issue, yes, we still have to improve the error checking to catch errors earlier. But still it is a good thing that cmake complained about the mistmatch, indeed it spotted an error.

Regarding the XCode issue and the profiles, I don't fully understand the issue, could you please give me more details? Because you know you can sync profiles with the team with conan config install, right? But I guess you are talking about a different issue? Also profiles can be composed and included. It should be easy to share most of the config in profiles in conan config install, then just the XCode thing in some common profile that could be included. Please tell me a bit more, because it sounds something that could happen to more people, lets see if there is some way to improve it.

@memsharded Yes, it was a problem that CMake complained - because there wasn't actually a version mismatch. The dependencies were built with gcc 4.9 and so was the consuming project. But Conan thought that the dependencies were built with gcc 5.4 (because for whatever reason this was set in the default profile). So the check in conanbuildinfo.cmake failed, even though everything was actually built with the same compiler. I was very confused in the beginning, because I couldn't figure out where the gcc 5.4 setting came from. I think it's a fundamental flaw that you can build a Conan package where Conan believes it was built with a certain compiler/version/architecture, etc. although it was actually built with a different compiler. If anything, the check should happen when building the dependency, not when trying to use the dependency.

The main problem is that you have to keep your profile in sync with the compilers you have installed - if you don't, you get an inconsistent state in your built packages. For example, Xcode comes with its own compiler, so updating Xcode can change the compiler version. A developer using Conan would have to know that they have to update their Conan profile whenever they update Xcode, or at least check if the compiler version changed. That means extra mental overhead and that our entire iOS team would have to know about this specific problem and how to fix it. It's just something that is very easy to forget.

I don't understand why Conan doesn't try to detect the compiler version automatically, but lets you overwrite it manually in profiles/settings.

Yes, it was a problem that CMake complained - because there wasn't actually a version mismatch. The dependencies were built with gcc 4.9 and so was the consuming project. But Conan thought that the dependencies were built with gcc 5.4 (because for whatever reason this was set in the default profile)

Yes, this is a mismatch. Both the packages and the consumers where being built with a different compiler than the one defined in the profile. It is not important that it is built with the same compiler, it is important that the compiler matches the declared one. Because you can totally consume conan packages without using conan in the consumer project, like:

$ conan install . -g cmake #with the default profile that is 5.4
$ CXX=/usr/bin/g++-5.4   cmake . -G "Unix Makefiles"

Of course some extra checks can be added, I am not saying that this cannot be improved. But decoupling the declaration of the configuration (defining the settings, maybe using profiles), from the actual configuration is something that gives a lot of power to users, and most importantly, allows never blocking them, irrespective of what they do.
Let me suggest the following example:

  • One user is working in Windows, with Visual Studio 2017 installed, and maybe a MinGW gcc7.1 too.
  • That user decides to cross-build a package to some embedded device using ARM with an arm-gnu-xxxx toolchain, they have installed somewhere in the system
  • Defines a profile, with settings arch=armv7, os=Linux, compiler=gcc, compiler.version=4.6, and some env-var pointing to the location of their cross-compiler
  • Use Makefiles to build their package, passing the location to the cross compiler in the make invocation

From this use case, which in some of its variants, is quite frequent, it becomes clear that conan cannot directly check the compiler and version and error if it doesn't match. If tries to use the system compilers, they will certainly not match the defined in the profile. If raising an Error, it will block the user. If doing a warning, it passes unnoticed most of the times.

So a general solution has to be done at the build system levels. In many of thems as Makefiles or Visual Studio is plainly impossible. CMake allows to deduce the current settings from its configuration. But this is more than challenging. This is exactly what the cmake-conan project does, and even if it has been continuously improving, it is still far for complete. Check: https://github.com/conan-io/cmake-conan/blob/master/conan.cmake

And even if conanbuildinfo.cmake adds those checks, it can happen that conanbuildinfo.cmake is not used at all (though we suggest that it is good to use it), for example, if the package being created doesn't have transitive dependencies.

Furthermore, and even in CMake, this is still a partial solution, because there are some things that cannot be detected, and also, the settings can be extended. A typical example is when users define custom settings to account for the glibc version, or for different linux distros RH6 vs RH7. It is still their responsability to ensure that they are using a profile that matches their current real configuration, because conan cannot detect that mismatch.

TL;DR: Detecting the compiler version automatically doesn't work for many cases, just the simple ones, and failing to do it corectly results in blocked users. Also, the best that can be done is restricted to CMake (and maybe a some others) build system, when conanbuildinfo.cmake is used (which is not strictly necessary)

I would like to add another corner case, if you have a cross build toolchain as a build require, like the Android NDK, until the dependency graph is computed you will never know the usage to the toolchain, even looking at the standard CXX and CC vars, even the OS setting won't match, and as Diego said, you only could know that something is not matching at the build system level.

I'm closing this as I think we cannot really control that without producing more issues trying to be too smart.

Was this page helpful?
0 / 5 - 0 ratings