Is there a simple way to make Visual Studio 2015 use a specific ToolsVersion?

19,471

Solution 1

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.

Solution 2

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 ;)

Solution 3

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.

Share:
19,471
Guy Bolton King
Author by

Guy Bolton King

Updated on June 06, 2022

Comments

  • Guy Bolton King
    Guy Bolton King almost 2 years

    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.

  • Guy Bolton King
    Guy Bolton King over 8 years
    Thanks, but this doesn't answer the question. I know that compiling against earlier versions of .NET will give me different features, but that's not what the question is about. The version of .NET that we're building against and the ToolsVersion are separate things: I can build against .NET 2.0, 4.0, 4.5.1 etc. using ToolsVersion 12.0 and 14.0. I can control the .NET version using project settings. The question is about how I control the ToolsVersion. I've downvoted your answer because of this.
  • Guy Bolton King
    Guy Bolton King over 8 years
    Also, I do not see how writing your own MSBuild file (spoiler: we have several custom MSBuild files) will allow you to programatically set the ToolsVersion unless you check the current tools version and then re-invoke with the desired tools version if they don't match. Hmm. That feels fairly...ick. I might try that though...
  • efaruk
    efaruk over 8 years
    Did you try to build your project from VS Command Prompt !? Building from vs command prompt and building from vs are same thing (Same behavior) VS it self calls msbuild it self to build your project. There is no another pipeline. Are you meaning something different with saying tool version ?
  • efaruk
    efaruk over 8 years
    Also you should build all project with different/required msbuild version to be sure about compatibility/portability. Another relevant task to do is check portibility with .Net Portability Analyzer: visualstudiogallery.msdn.microsoft.com/…
  • Guy Bolton King
    Guy Bolton King over 8 years
    Thank you for taking the time to reply, but I'm not sure you've fully understood the question. I've clearly shown that I've built from the command prompt. Also, invoking msbuild.exe and building within VS are similar but not identical, the main point being that when building within VS I can't specify any arguments to msbuild, in particular the /tv argument. Finally, portability is important, but so is build reproducibility, which is what the question is concerned with.
  • efaruk
    efaruk over 8 years
    So, create an environment.msbuild file and link it in your .csproj files (or vbproj files, etc.) (Project Import: msdn.microsoft.com/en-us/library/92x05xfs.aspx). How you can say this is fair..ish. This is the solution you are looking for ;). You can have the same behavior for both msbuild and VS (If you can write a good msbuild script)...
  • Guy Bolton King
    Guy Bolton King over 8 years
    Perhaps you could demonstrate (edit your answer) what such a file would look like? Our goal is to make VS2015 compile a .csproj using a ToolsVersion other than 14.0; say 12.0.
  • Guy Bolton King
    Guy Bolton King over 8 years
    Further to an earlier comment I made: reinvoking with the required toolsversion, like this: <MSBuild ToolsVersion="12.0" Condition="'$(MSBuildToolsVersion)' != '12.0'" Projects="$(MSBuildProjectFullPath)"/>, can't be made to work (or at least, not easily) because after invocation the invoker will continue to build as normal with the original undesired ToolsVersion. Besides, this is already getting too baroque, which is something I'd rather avoid.
  • Guy Bolton King
    Guy Bolton King over 8 years
    Thanks: I've read that section in MSDN before, but setting ProjectToolsVersion in the project file itself has no effect: we still get the 14.0 ToolsVersion being used when building in VS2015. Have you tried this? Did it work? If it did, can you please tell me how to set "the $(ProjectToolsVersion) property on a project within a solution"?
  • Guy Bolton King
    Guy Bolton King over 8 years
    I've not been able to make any of your proposed solutions work, and I'm worried we're talking at cross-purposes, so I've set up a repository on github that demonstrates the problem in a vanilla C# console project. If you have time, please show how you'd modify that project to make it build in the VS2015 IDE (I'll ensure that any fixes appear in your answer, which I'll be happy to accept if it works!).
  • efaruk
    efaruk over 8 years
    I don't have vs 2013 on my system so i couldn't able to simulate sorry. But if you refer to same document (msdn.microsoft.com/en-us/library/bb383985(v=vs.140).aspx) you can see another option: 6. If the environment variable MSBUILDLEGACYDEFAULTTOOLSVERSION is not set, then the following steps are used: If the environment variable MSBUILDDEFAULTTOOLSVERSION is set to a ToolsVersion that exists, use it. If DefaultOverrideToolsVersion is set in MSBuild.exe.config, use it. If DefaultOverrideToolsVersion is set in the registry, use it. Otherwise, use the current ToolsVersion.
  • Guy Bolton King
    Guy Bolton King almost 8 years
    Thanks for mentioning MSBUILDDEFAULTTOOLSVERSION; I had forgotten about that, and that led me to the (more involved than I would have liked) solution in my answer.
  • Guy Bolton King
    Guy Bolton King almost 8 years
    Good grief. Another drive-by downvote: can anyone enlighten me as to what merits this?
  • Guy Bolton King
    Guy Bolton King over 7 years
    Sorry, this isn't what the question is about. The problem being addressed is that different versions of the .net toolchain produce different results even with the same version of the language, and there isn't a simple way to enforce toolchain version in the IDE. I've downvoted this answer because it doesn't answer the question.
  • Ryan Rodemoyer
    Ryan Rodemoyer over 7 years
    This was a helpful workaround for me to identify the offending code and produce the same results in VS2015 as we saw on the build server. Obviously not a long term solution but helpful nonetheless.
  • weir
    weir over 7 years
    @GuyBoltonKing - I proposed an edit deleting the incorrect portion of the answer that you pointed out (concerning the .bat files)
  • mbadawi23
    mbadawi23 over 7 years
    I'm attempting to use your extension in VS2015 at the moment. I think I'm using it right, but I'm getting the following behavior: Setting MSBUILDDEFAULTTOOLSVERSION to 12.0 4>Building with tools version "14.0". Of course this results in a failure in my build.
  • Guy Bolton King
    Guy Bolton King over 7 years
    @mbadawi23 I can't be of much help, as I'm no longer working on a platform that can run VS2015. The only thing I can think of is that MSBUILDDEFAULTTOOLSVERSION is the last thing in a long list of things that can set the ToolsVersion; all the other items in the list will override it. You might want to check if you have any of the other environment variables set. Beyond that, I'm very sorry to say that you're on your own :(
  • mbadawi23
    mbadawi23 over 7 years
    Thank you for your response. But I managed to get it to work. I cleaned the solution and restarted VS2015. While I was at it, I upgraded to VS2017 and created a fork from your repository here