On the large number of ways of expressing Microsoft Visual C++ compiler versions
When you ask someone what version of the Microsoft Visual C++ compiler they’re using, or if somebody tells you “This feature requires version X of the Microsoft Visual C++ compiler”, you can get the answer is a large number of ways, because the Microsoft Visual C++ compiler has apparently decided that if one version number is good, then more must be better.
The first version number that enters the picture is the name of the Visual Studio product the compiler comes with. This is probably something like “Visual Studio YYYY” for some year, like “Visual Studio 2019”.
The next version number is the product version of that Visual Studio product. For example, “Visual Studio 2019 version 16.11.”
The next version number is the platform toolset that you specify in your project file, like <PlatformToolset>v142</PlatformToolset>
.
Another version number is the actual toolchain version, like “14.29”.
And then there’s the version number reported by the _MSC_VER
predefined macro, like “1929”.
On top of that is the version number reported by the _MSC_FULL_VER
predefined macro, like “192930100”.
And finally, there’s the version number reported by the compiler itself when you type cl /?
.
How are all of these version numbers related?
Product name | ? ⇔ |
Product version | ? ⇔ |
Platform toolset | ? ⇔ |
Toolchain | ? ⇔ |
_MSC_ |
? ⇔ |
_MSC_ |
? ⇔ |
Compiler banner |
Okay, let’s do the easy one first: The last three version numbers are related to each other in a straightforward way. If the compiler’s self-reported version is aa.bb.ccccc.dd
, then the _MSC_VER
is aabb
and the _MSC_FULL_VER
is aabbccccc
. In other words, _MSC_VER = aa * 100 + bb
and _MSC_FULL_VER = aa * 10000000 + bb * 100000 + cc
.
Okay, so that lets us build a relationship among the last three boxes: The compiler banner is the basis for the other two.
The first two boxes are also related, but in a less obvious way: The product name and product major version line up according to this table on Wikipedia. For example, Visual Studio 2019 corresponds to product versions 16.*.
The next two boxes also appear to be related, although I can’t find any official documentation to that effect. The platform toolset appears to be the letter “v”, followed by the toolchain major version, followed by the first digit of the minor version. For example, a toolchain version of “14.29” corresponds to a platform toolset of “v142”.
That leaves three major categories: The Visual Studio product, the toolchain, and the compiler. These three categories follow their own path, so you have to use a cheat sheet to see how they correspond to each other.¹
Let’s build that cheat sheet. I got the raw data from this table on Wikipedia, with gaps filled in from the archived Visual Studio release notes.
Name | Product | Toolset | Toolchain | _MSC_ |
_MSC_ |
Compiler |
---|---|---|---|---|---|---|
Visual Studio 2017 |
15.0 | v141 | 14.1 | 1910 | 1910xxxxx | 19.10.xxxxx |
15.1 | ||||||
15.2 | ||||||
15.3 | 14.11 | 1911 | 1911xxxxx | 19.11.xxxxx | ||
15.4 | ||||||
15.5 | 14.12 | 1912 | 1912xxxxx | 19.12.xxxxx | ||
15.6 | 14.13 | 1913 | 1913xxxxx | 19.13.xxxxx | ||
15.7 | 14.14 | 1914 | 1914xxxxx | 19.14.xxxxx | ||
15.8 | 14.15 | 1915 | 1915xxxxx | 19.15.xxxxx | ||
15.9 | 14.16 | 1916 | 1916xxxxx | 19.16.xxxxx | ||
Visual Studio 2019 |
16.0 | v142 | 14.20 | 1920 | 1920xxxxx | 19.20.xxxxx |
16.1 | 14.21 | 1921 | 1921xxxxx | 19.21.xxxxx | ||
16.2 | 14.22 | 1922 | 1922xxxxx | 19.22.xxxxx | ||
16.3 | 14.23 | 1923 | 1923xxxxx | 19.23.xxxxx | ||
16.4 | 14.24 | 1924 | 1924xxxxx | 19.24.xxxxx | ||
16.5 | 14.25 | 1925 | 1925xxxxx | 19.25.xxxxx | ||
16.6 | 14.26 | 1926 | 1926xxxxx | 19.26.xxxxx | ||
16.7 | 14.27 | 1927 | 1927xxxxx | 19.27.xxxxx | ||
16.8 | 14.28 | 1928 | 1928xxxxx | 19.28.xxxxx | ||
16.9 | ||||||
16.10 | 14.29 | 1929 | 1929xxxxx | 19.29.xxxxx | ||
16.11 | ||||||
Visual Studio 2022 |
17.0 | v143 | 14.30 | 1930 | 1930xxxxx | 19.30.xxxxx |
17.1 | 14.31 | 1931 | 1931xxxxx | 19.31.xxxxx | ||
17.2 | 14.32 | 1932 | 1932xxxxx | 19.32.xxxxx | ||
17.3 | 14.33 | 1933 | 1933xxxxx | 19.33.xxxxx | ||
17.4 | 14.34 | 1934 | 1934xxxxx | 19.34.xxxxx |
But wait, the story isn’t over yet.
Internally, the compiler team delivers periodic compiler updates to the Windows team. These updates are named LKG followed by a number, like “LKG14”, and there is an internal Web site that maps LKG values to compiler version numbers. Fortunately, only people who work at Microsoft need to worry about these LKG version numbers.
Bonus chatter: The term LKG stands for “Last Known Good”, meaning that it is the latest version of the compiler that has been validated against the Windows code base. There is another term FKG, which you think might stand for “First Known Good”, but it doesn’t. It originally stood for “Fast Known Good” because it contained compilers even newer than the Last Known Good. That policy has changed, and now the FKG is used for other purposes, but the name FKG stuck, even though it’s completely wrong.
¹ It appears that starting in Visual Studio 2017, the compiler minor version increases by one each time the toolchain minor version increases by one, so that’s handy. Starting in August 2017, the compiler version is equal to the toolchain version plus five. I don’t know whether this is a rule or a coincidence.
6 comments
There’s also the supported C++ version:
– C++11 (I think gone from VS2017)
– C++14 (the default)
– C++17
– C++20 (supported from VS19 … but which version?)
– Latest
Great info as usual Raymond 🙂
I don’t remember C++11 was really supported.
On the one hand, the first time that MSVC announced standard conformance was around Visual Studio 2015. We can learn from the blog posts that the key features of C++11 were not completely supported, until they were implemented together with C++14 in Visual Studio 2015.
On the other hand, before the introduction of the `
/std
` compiler option (VS 2015 Update 3) and the side-by-side toolset installation (VS 2017), it was not possible to select your language version in MSVC. The first `/std
` switches were `/std:c++14
` and `/std:c++latest
`. C++11 was skipped, which, from my perspective, is practically good, as C++14 and C++17 patched the most of its serious defects.As for the `
/std:c++20
` switch, it was added in Visual Studio 2019 version 16.11.Oh ye gods, yes.
How do the Visual C++ runtime versions on the dll files map into this? I have a cheat sheet here too.
Judging from the required runtime dll (msvcpxx, msvcrtxx) how to find the required Visual C++ runtime. I stopped updating about VS 2015 though.
On a glance it might be the platform major version:
msvcp80.dll VS 2005
msvcp90.dll VS 2008
msvcp100.dll VS 2010
msvcp110.dll VS 2012
msvcp120.dll VS2013
msvcp140.dll VS 2015
Usually pops up when the customer is using any old version of an SDK and I use Dependency Walker to find out the required runtime.
Was there ever any effort started internally to unify these numbering schemes? Since the VS name has the highest number anyway it would seem to be pretty easy to line them up without breaking any backward compatibility. Some schemes would just need to make a little jump every now and then, but they seem to be doing that anyway, so wouldn’t be a problem.
For example:
Visual Studio 2025
Product: 2025.01
Toolset: v250
msvcp: 250
msvcruntime: 250
Toolchain: 25.01
_MSC_VER: 2501
_MSC_FULL_VER: 2501xxxxx
Compiler: 25.01.xxxxx
Or are the owners of all these schemes just not interested enough in making this easier?
I think compiler minor version and toolchain minor version being tied is not a coincidence, but a conscious effort. If I was responsible for this, and I didn’t have any reason to break this convention, then I would keep it for as long as I could.
One thing worth a mention might be the reason why there are only v14x toolset/toolchain versions and 19xx compiler versions mentioned in this blog post. This is because all these versions share ABI. When/if ABI break occurs in the future, we will likely see major version bump to v15x for toolset and 20xx for compiler.
If you go back further on that table on Wikipedia, you can find out why the compiler version is at the large numbers now: it’s an unbroken sequence going all the way back to the DOS-based and often pre-C++ Microsoft C compilers, My first C compiler ever was Microsoft C/C++ Compiler 7.0, which was a DOS executable with DPMI requirements because 640 kB was not enough. That means I mostly launched Windows 3.1 so I could open a DOS prompt so I could run the compiler.
The next highest number is the product version, which is the unbroken line of Visual C++ version numbers. It was simply the C++ compiler version minus 6, but since MS skipped Visual C++ 13.0, that dropped to 5. Then around v14 and VIsual Studio 2015, something happened.
Starting with Visual Studio 2017 and VC++ 15.x, Microsoft stopped breaking binary compatibility with each release. Instead, all new binaries are binary-compatible with VC++ 14.0, which is why the toolset is still 14.x and why the MS Visual C++ runtime DLL is also version 140 (msvcp140.dll). This is a good thing, for those of us who need to deal with one binary release for multiple customers: we can build using the oldest still relevant release and anyone who has a newer version is properly supplied. And it happens outside of Windows and Microsoft products too: think that GCC 12 comes with libstdc++.so.6 (full version is 6.0.30) because that retains compatibility with the first GCC that released libstdc++.so.6.
No, that was not GCC 6. That was 3.4. So having weird version numbers is not exclusive to Microsoft either.
Bonus chatter: you will also not find “6.0.30” anywhere in the GCC sources.