conan: Incorrect handling of MSVC versions
I am using Conan v1.7.4.
I’ve created a static library Conan package using VS 2017 15.7 and updated my Visual Studio to 15.8. After the update, Conan failed to detect a change in the compiler version and did not perform recompilation of the static library. Instead, it just downloaded the same package that was built with VS 2017 15.7. However, when linking to that package from VS 2017 15.8, I am getting following linker error:
LINK : fatal error C1047: The object or library file 'C:\Users\dodo\.conan\data\LogAndTimer\1.0.1\microblink\stable\package\8b2772668ccbf879d34a70a9d54c78ce70f38a1b\lib\LogAndTimer.lib' was created with an older compiler than other objects; rebuild old objects and libraries
2>LINK : fatal error LNK1257: code generation failed
However, the debug build does work (details how to reproduce are below).
I think the problem is in how Conan handles MSVC versions - instead of using the actual compiler version (CMake detects MSVC 19.14.26431.0
in VS 2017 15.7 and MSVC 19.15.26729.0
in VS 2017 15.8), it uses the version of Visual Studio (VS 2017 is compiler version 15, according to Conan).
I suggest that Conan should use the MSVC compiler version under compiler.version
setting and additionally have compiler.vs_version
for Visual Studio version. The reason for that is that sometimes libraries built with one version of MSVC are not compatible with later versions of MSVC, especially if you use link time code generation flag (/LTCG
link flag and /GL
compile flag).
How to reproduce
- Install Visual Studio 2017, but not the latest update (at the time of writing, this is 15.8.4). For example, use VS 2017 15.7.x or 15.6.x.
- Create a conan package that contains a single static library. Make sure that
Release
build type of that package is compiled with/GL
compile flag. - Create an application that consumes that static library and make sure it works in
Release
build type (the app should be linked with/LTCG
linker flag). - Update Visual Studio 2017 to the latest version (15.8.4 at the time of writing).
- Build your application
Expected behaviour
conan install
after updating the VS should calculate different hash for the library and should rebuild it, thus making sure application linking will succeed
Actual behaviour
- Conan does not detect version change in the MSVC compiler and uses the incompatible binary, thus causing linker error described above.
Now, the main question is - could this be fixed without breaking existing conan VS packages, or we need to wait for Conan 2.0 (and disable LTCG until this issue gets fixed).
Also, as a side question, is there a possibility to configure Conan to trigger rebuild of libraries even when a minor/bugfix version of the compiler changes, regardless of the compiler being used (e.g. if we update GCC from 8.1.0 to 8.1.1, we would like to rebuild all our binaries, even though they are link-compatible). This would get us the benefits of always having binaries built with the latest compiler to get the latest bugfixes and performance improvements - this benefit is also mentioned in conan documentation:
What happens if we have a library that we can be built with GCC 4.8 and will preserve the ABI compatibility with GCC 4.9? (This kind of compatibility is easier to achieve for example for pure C libraries).
Although it could be argued that it is worth rebuilding with 4.9 too -to get fixes and performance improvements-. Let’s suppose that we don’t want to create 2 different binaries, but just a single built with GCC 4.8 which also needs to be compatible for GCC 4.9 installations.
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Reactions: 20
- Comments: 52 (48 by maintainers)
Oops, Microsoft did it again 🤦♂️
some notes:
probably, using compiler version (
_MSC_VER
or_MSC_FULL_VER
) better aligns with other compilers like GCC, Clang, where we use compiler version as well. it’s better to be consistent. also, in this case compiler.version isn’t misleading and self-descriptive.need to carefully check
LTCG
andLTO
use cases, may users on slack and GitHub reported that even minor version matters for compatibility in these modessome notes from
Stephan T. Lavavej
(Microsoft STL maintainer):AFAIK there are/were ways to install MSVC compiler without Visual Studio, e.g. with DDK (Driver Development Kit), WDK (Windows Driver Kit), Windows SDK, might be some others. I am not sure, if toolsets are available in these cases at all.
I’ve actually described the
settings.yml
I use in this comment above.This seems reasonable for the default VS settings, where LTO is disabled. However, this is then not the
msvc
compiler - it’svisual studio
toolset - that should be different frommsvc
compiler.Here is where I would actually treat that as the separate compiler, for simplicity, not as the
mscver
subsetting. If you choose to usemsvc
compiler instead ofvisual studio
toolset then you should probably have different binaries. Yes, sometimes they might match, but this is similar to how some GCC vs Clang binaries are compatible.Yes, it’s possible and we have multiple
msvc
versions installed on our CI machines. I’ve even described in the comment linked above how you can force conan to select correctmsvc
version.For
msvc
compiler, the versions should definitely be in line with what compiler reports (19.25, 19.26, 19.27, … ). The140
,141
, … versions are in line withvisual studio
toolset and should not be used formsvc
compiler.So, to conclude:
msvc
compiler should have versioning in line with reported version ofcl.exe
and should not be related to version of Visual Studio nor it should require VS installation, imhovisual studio
toolset can have the versioning in line with versions of VS toolsets and can be used by people that don’t want fine-grained control over msvc versionsmsvc
andvisual studio
should in general have different ID and not be treated as compatible, however methods used foricc
may be used to ensure compatibility betweenmsvc
andvisual studio
builds of the same package.If all that seems too complicated (I’d guess most people will get confused by having both
msvc
andvisual studio
“compilers”), and if we just want to have only onemsvc
compiler, then I would definitely vote for the first option (msvc
, notvisual studio
). The versions should then be in sync with versions reported bycl.exe
and for general case (when LTO is not needed), the version should be parsed from major number (i.e. 19.1, 19.2, …) and if someone needs LTO and fine-grained control, they can easily add versions 19.16, 19.17, 19.25, 19.26, …) to theirsettings.yml
.This is not without precedent: the default
settings.yml
, shipped by conan, already has such versioning scheme for GCC: there is version 9, but also versions 9.1, 9.2 and 9.3. Version 9 is OK for general case, but if you use LTO, you need to use 9.1, 9.2 and 9.3.We use the same trick also for Apple Clang - without LTO all compilers shipped with Xcode 11 produce link-compatible binaries, but if you enable LTO, there are actually two different versions of clang in Xcode 11 series: 11.0.0 and 11.0.3, which are not LTO-link compatible.
So, why not doing the same with MSVC as well?
We will look at this before 2.0 to see what can we introduce in 1.X. We understand this is important.
I like the idea - introduce new compiler
msvc
in Conan 1.9.x, but keepVisual Studio
a default choice for Windows with the same behaviour as current. Themsvc
would behave in the same way asgcc
andclang
. Then, in v2.0 switch the windows default fromVisual Studio
tomsvc
, but keepVisual Studio
as a legacy option. Finally, removeVisual Studio
in Conan v3.0 and keep it only as the generator, not as the “compiler”.I think this should be accompanied by appropriate updates in cmake-conan project, but I think this should not be difficult (such a change is even within my capabilities 😛 )
The introduction of
msvc
in v1.9.0 would enable those that have the same problem as we do to circumvent them almost immediately, without waiting for breaking change - although using prebuilt packages from bintray would require some additional code in consumer’s conanfiles** (let’s discuss that as well - I think that would also require some backwards-compatible additions to conanfiles).Then, with v2.0 users that were already using
msvc
instead ofVisual Studio
compiler would not have a breaking change and v2.x series would give a plenty of time to everyone to switch their packages tomsvc
compiler. In v3.0Visual Studio
could be removed just to reduce the code bloat, if necessary. If not, it can remain eternally for backwards compatibility if Conan adopts policies in the way similar to how CMake does***).** Here I generally think of a way for the consumer to “override” the calculation of the package ID of the package being consumed. The idea here is for the consumer to be able to “tell” Conan that if it uses, e.g. compiler
msvc 19.15
that it can safely consume a specific package built forVisual Studio 15
(something like how you tell the versioning schema) - to avoid rebuilding the package for compiler the author of the package didn’t anticipate. This is especially useful for binary-only packages built with earlier versions of Conan.*** I think policies are a good way to keep backward compatibility while providing new features. Yes, it causes bloatware, but I think it is essential for package management software to be as backwards-compatible as possible. Also, as I already mentioned, a recipe should be able to specify the minimum required version of Conan for a recipe to be parsed. This would enable much easier development of packages that support wide range of Conan versions.
OK, I solved this problem for our use cases. The solution (ab)uses the fact that conan will not set vcvars if they are already set and assumes that developers will never manually invoke
conan install
orconan create
on Windows systems and that all packages use CMake build system. This assumption is true for us, as we use cmake helper that automatically invokesconan install
during cmake configuration.Here is how I did it:
First, I’ve added a
msvc
compiler to mysettings.yml
. The entry looks like this:Then, I’ve created several conan profiles:
msvc-generic
:msvc-19.27
:vs-19.27
:and similar for every MSVC version (19.25, 19.26 and 19.27).
The real magic happens here. While configuring the project and deciding about command line parameters to be given to
conan install
, cmake script first obtains the version of MSVC compiler currently used and decides to usevs-<version>
ifVisual Studio
generator is used andmsvc-<version>
if not (in our case it’s Ninja, but it should also work with Makefiles).The idea is the following:
vcvars
are definitely set for the current shell, otherwise, Ninja would not be able to find MSVC compiler. This means that the current build will be definitely built with the correct version of the MSVC compiler, but by usingmsvc-<version>
profile we ensure that conan will also use Ninja generator for building any packages that need to be built by dependency. This abuses the fact that conan will not modify vcvars if they are already set. This then ensures that the entire dependency graph will be built with the same version of MSVC, enabling us to enable stuff like LTO.Visual Studio
generator is used, then we cannot assume thatvcvars
are set for the current shell (they may or may not be). However,Visual Studio
generator will always use the latest version of MSVC, as it does not support selecting an older version of MSVC (it’s a limitation of Visual Studio, not CMake). Therefore, here we will usevs-<version>
profile to ensure that conan will also useVisual Studio
generator for building any packages that need to be built by dependency. By having the samecompiler.version
set in that profile as well we will ensure that correct packages are selected in the dependency graph (regardless if they were already built before or not).Of course, this assumes that developers will not manually invoke
conan install/create
, as invokingconan install/create conanfile.py -pr vs-19.25
while having Visual Studio updated to v16.7 will create invalid packages (the package will be built with MSVC 19.27, but conan package will advertise 19.25, which is wrong).Similar could happen (I didn’t test) if invoking
conan install/create conanfile.py -pr msvc-19.25
from the shell where vcvars are initialized to msvc 19.27 (although this line of code may catch that, but I’m not entirely sure).Fortunately, the profiles I described above don’t have
compiler.runtime
set, and conan automatically fills the runtime information only forVisual Studio
andintel
compilers, so if you accidentally invokeconan install/create
manually, you will get an error that thecompiler.runtime
setting is missing 🎉 . This is hacky, but it works 😛On the other hand, the Jenkins that builds our packages is configured to always use Ninja generator and set correct vcvars environment before invoking any Conan commands.
In general, I would suggest that conan gets native support for MSVC compiler. The implementation may be based on this solution:
ConanException
and fail the buildintel
andVisual Studio
compilersSo, from the twit joke above we have this table, do we all agree the first column should we use? @DoDoENT @sigiesec @psiha @memsharded @claasd
We need to discuss what to do with the
CMake
generator argument if the IDE is not available as a setting, probably we should try to deduce (or try) from the compiler version but let the user override it with an environment variable/conan.conf?Hi all! Thanks for the feedback. About the compatibility between the old Visual Studio setting and the new one, it is not that easy, we would need to think about it carefully but it is not going to be solved in the firsts versions. By the way, it cannot be in 1.8, it is already packaged and struggling to release it, so anyway this will wait for 1.9. Sorry.
I am proposing this:
The
runtime_type
will be filled automatically based onbuild_type
, but the user always has the possibility of explicitly defining it. I am testing it for the CMake toolchain, seem to be a good approach so far.I don’t think we can enforce the combination of
runtime
andbuild_type
. Tons of companies build.dll
s withMT
. It works fine for so many people, they don’t care that it’s not safe or not recommended.I have updated https://github.com/conan-io/conan/pull/8201 to implement:
The mapping to default VS versions (to call cmake -G “Generator”) is done dropping the latest digit if existing. This is basically the same approach done for gcc and clang. Auto-detect (when this happens, Conan 2.0) will resolve to 19.1, 19.2, assuming that binary compatibility, and letting users to opt-in into the full versions for LTO and other binary-incompatible issues.
I still need to figure out the
runtime
and work on the migration plan forpackage_id
compatibility with existing Visual Studio compiler.This sounds reasonable for me as long as there will be a guarantee that each MSVC compiler version will have its own toolset, i.e. that there will be no MSVC compiler updates (at least incompatible ones, in terms of LTO) without changing the toolset version.
Also, we will also need to update cmake-conan to correctly infer toolset from MSVC version detected by the CMake.
We are going to make a proposal following the lines of all that has been discussed here: it should be a new compiler for Conan
v1.x
that will use the toolset version. As it is more common to hear about toolsetv140
,v142
IMO we should choose the toolset over the _MSC_VER variable that is available as a macro provided by the compiler, anyway there is bi-univocal correspondence between them.The first approach (or the
settings.yml
provided by default by Conan) should consider only the first number in the minor version as it should be compatible and it is not so easy to have several minor versions installed at the same time (also this), nevertheless as there are bugs from time to time the implementation should allow an advanced user to use full version number (v14.11
-vs-v14.12
) in their customsettings.yml
. Note.- There is a patch version for toolsets too (as it is_MSC_FULL_VER
, but IMHO it is too much to consider at this moment, although if possible, the implementation should not forbid using it.The profile for this new compiler will contain the toolset version and Conan should be able to go from the toolset version to the compiler that is able to generate the binaries or even to the IDE version if we need to generate the solution using CMake for example. We need to write a POC about this first, but these are some insights to build a POC for this:
Using
vswhere
(at a known path since 15.2) we can ask for an installation using some components:vswhere [-latest] -requires <component-toolset>
, where each toolset has its own requirement name:v141
:Microsoft.VisualStudio.Component.VC.v141.x86.x64
v142
(14.20):Microsoft.VisualStudio.Component.VC.14.20.x86.x64
v142
(14.21):Microsoft.VisualStudio.Component.VC.14.21.x86.x64
v140
:Microsoft.VisualStudio.Component.VC.140
It could be easier, but at least it is documented.
To be able to choose an older toolset (corresponding to
v120
,v100
,…) you need to install the corresponding VS version (need to double-check). This is great, as looking for the compiler/IDE for an old toolset version will require to look for the IDE itself and ⬇️For legacy VS versions
vswhere
(not installed) it is able to find Visual Studio 2010 and newer using-legacy
flag (we cannot use the-requires
flag, but we are able to find a suitable compiler for the toolset requested). At least it retrieves the install path.Also interesting (maybe we can use it from Python): https://github.com/microsoft/vs-setup-samples
With this approach we should be able to go back to VS 2010, older versions could require other approaches, but I haven’t looked for information about them, sorry.
Once we have the IDE (and the toolset version) we know the CMake generator name and we can use the
cmake ... -G "Visual Studio Studio ..." -T <toolset>
or we can call one of thevcvars
scripts with the proper arguments,… or find theMSBuild
…Probably it is not as easy as it looks like, but I think it is feasible and there are good foundations to start building on top of it.
Thanks everyone for the feedback
There is still something else I would like to mention (and probably discard): what about the
Windows SDK
? is it something we want/need to consider? And what about theFramework .NET
version, can it generate incompatible binaries? Probably these are just subsettings that the user building binaries that depend on them should take into account, but not for the defaultsettings.yml
Ping @DoDoENT @sigiesec @claasd @psiha @DaniCybereason for a final round of feedback before we start walking this path. Thanks!
https://twitter.com/Donzanoid/status/1111682600215678976?s=09
To my knowledge, MSVC relies much more on LTCG (and PGO) to generate optimal code than other compilers (in particular gcc and clang) do, even though they also provide comparable features. But as @DoDoENT said, maybe not everyone is aware of that. I have found build files of many open source libraries that were not enabled for LTCG.
MS never guaranteed binary compatibility for .lib files between different toolset versions, but in many cases it worked anyway (or at least it did not obviously not work). However, before Visual Studio 2017 there haven’t been multiple toolset versions for the same major Visual Studio version, or at least they did not exhibit such obvious incompatibilities.
Also, not all users of Visual Studio will have already migrated to Visual Studio 2017. This is also true for us. We have prepared new versions of several components that are built with the VS2017 toolsets already, and during development we already stumbled upon this problem where a mismatch between the toolset used on the CI and locally caused problems. But the products using these components still use VS2015 as of now. I am somewhat scared of what will happen when they start to use VS2017, and get compilation failures because of mismatches in the toolset versions.
Just for information, I am not a coworker of @DoDoENT (since @lasote was asking).
There really is nothing to stabilize here - Visual Studio and the Visual C++ (AKA MSVC) have always been separate products (one is an IDE and the other a compiler - e.g. you can get the compiler w/o the IDE through the Windows SDK) and their separate versioning schemas used currently have been in place for quite some time - it is just plain wrong to ‘guess’ or ‘tie’ the version of the compiler to the version of the IDE (not just because, as DoDoENT already pointed out, you can have multiple toolchains and compiler versions ‘within’ one IDE version). IOW&IMNHO there should be no question of whether this needs to be fixed … only when and how…
With that said, given that it is already creating problems or even showstoppers for MSVC users, it would be nice if you could come up with some kind of a workaround until it gets ‘officially fixed’…
Actually… this could and probably should be discussed earlier. If anything, it could make sense to introduce a new compiler in the setting, to replace the old one, but without replacing. Conan 2.0 would deprecate the previous one (without removing), maybe Conan 3.0 could remove the old one. Lets discuss.