49
votes

When building a project or solution using a specific version of msbuild I can select an earlier .net toolchain by using the /toolsversion or /tv switch:

"C:\Program Files (x86)\MSBuild\14.0\bin\msbuild" /tv:12.0 amazing.sln

This Just Works for all versions of msbuild, and the version of csc.exe etc. is correctly chosen based on the above:

> "C:\Program Files (x86)\MSBuild\14.0\bin\msbuild" /tv:4.0 amazing.sln
...
CoreCompile:
  C:\Windows\Microsoft.NET\Framework\v4.0.30319\Csc.exe ...
...

> "C:\Program Files (x86)\MSBuild\14.0\bin\msbuild" /tv:12.0 amazing.sln
...
CoreCompile:
  C:\Program Files (x86)\MSBuild\12.0\bin\Csc.exe ...
...

If I don't specify /tv, then depending on which version of msbuild I'm using and a number of environment variables, I may get any of:

  • The ToolsVersion specified in the top-level element in the project file
  • The ToolsVersion corresponding to the version of msbuild.exe I'm using
  • A value from msbuild.exe.config
  • A value from the registry

(See the different versions of the Overriding ToolsVersion Settings page on MSDN).

So, in order to have builds that have consistent results on the build server and on my local machine, I use /tv when running msbuild.exe (in fact, this is enforced in a psake script, which also ensures it uses the corresponding version of msbuild.exe).

However I cannot use the /tv switch when building with Visual Studio. Instead, Visual Studio 2013 and up will use the .net toolchain that shipped with that version of Visual Studio unless:

  • The environment variable MSBUILDLEGACYDEFAULTTOOLSVERSION is set and...
  • ...all the project files have the ToolsVersion attribute set to the version I want to use.

This is so baroque that I cannot believe anyone is actually doing it. My questions are thus:

  • Is anyone doing the MSBUILDLEGACYDEFAULTTOOLSVERSION thing?
  • If not, is there another way to make Visual Studio use a specific ToolsVersion short of using the version of Visual Studio that shipped with that ToolsVersion? Something that could be stored in version control (so in a project or some other settings file) would be ideal.

And lastly:

  • Should I even care? Given that each successive version of the C# compiler should be able to handle previous versions' input, and I can set the target .net framework and C# language level in the project file, is this enough to ensure repeatable builds?

(My prejudice is that I should care, since:

  • I want builds in the IDE and on the build server to be the same (of course)
  • I want to be able to use VS2015 (and future versions) because it's a better IDE than previous versions, but I don't want to be obliged to use the new toolchain until I decide to.

Perhaps I want too much...)

For a concrete example of the problem, please see my msbuild-vs-vs2015-toolsversion repository on github.


Some background: I'm asking this because we recently had a CI build error when one of my colleagues submitted C# 6.0 code that compiled fine with Roslyn on their copy of Visual Studio 2015, but failed in CI because that uses the previous release of the .net toolchain (they'd used an automatic property with no setter, which is fine in Roslyn but not in earlier versions). We will be updating the CI build to Roslyn, but I wanted to see if we could prevent this sort of thing happening in the future.

4
Fixed :D great question, btwalexandrul
@alexandrul: Thank you. I know it's quite long, but I think the detail is important. Happy to make any suggested improvements.Guy Bolton King
I asked the same question with no working answer yet. This seems to me a microsoft bug as none of the described ways work. stackoverflow.com/questions/33791934/…user1029883
Ah I see Guy you tried to answer my question, but it does not work. Try it out using ONLY VS2015 installed, it always defaults to 14.0.user1029883

4 Answers

6
votes

I solved this by writing a Visual Studio extension that temporarily sets the environment variable MSBUILDDEFAULTTOOLSVERSION for the duration of a build; the value to be used is read from a file .toolsversion in the same directory as the .sln file. The psake script reads the same .toolsversion file and passes the value to the /tv switch.

The code for the extension can be found here: https://github.com/guyboltonking/set-toolsversion-extension. Sadly, I'm not working with C++, or indeed with Visual Studio, at the moment, so I can't provide any support for it (but I can tell you I used it with no issues at all for several months).

Kudos to @efaruk for reminding me about the existence of MSBUILDDEFAULTTOOLSVERSION.

Edit: Thanks to @mbadawi23, it's now possible to use the extension with both VS2015 and VS2017.

2
votes

To force a specific C# version in Visual Studio 2015, you can go into the project properties -> Build -> Advanced -> Language Version.

If you set this to 5, the compiler will complain about C# 6 features with: Feature '...' is not available in C# 5. Please use language version 6 or greater.

Alternativly ReSharper also has some tools for this.

2
votes

Note: You can always create an msbuild file to build your project from using it or changing your project it self and you can decide your tool version conditionally (https://msdn.microsoft.com/en-us/library/7z253716.aspx) (.csproj is also a structured msbuild script with different extension and it will be also compatible with VS).

Regards...

Edit:

https://msdn.microsoft.com/en-us/library/bb383985.aspx

by setting the $(ProjectToolsVersion) property on a project within a solution. This lets you build a project in a solution with a Toolset version that differs from that of the other projects.

So, I think you have got your answer ;)

-2
votes

What you see within Visual Studio (the tools etc) and the code behind them are not what is included in the compiled data they are merely a visual/readable representation, when compiling them as an earlier version of VS you are making the executable of that version.

Please keep in mind that if compiling as a previous .NET version you will potentially lose functionality such as async functionality.