46
votes

Earlier versions of MSBuild could be found here: %programfiles(x86)%\msbuild\<version>\bin\msbuild.exe.

But for Visual Studio 2017RC the path is %programfiles(x86)%\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\msbuild.exe. The type of install: enteprise, community or professional, seems to be part of the path. And that makes it difficult to specify the correct path to msbuild in a BAT file that should work on many different computers with different versions of Visual Studio 2017.

What is the best way to call Visual Studio 2017 RC's version of Msbuild from a bat file? Or from PowerShell?

9
Wouldn't it be best to run vcvars as it usually includes the path to msbuild, or has that changed for VS2017 as well?stijn
vcvars is also below the "enteprise", "community" or "professional" path. so calling vcvars is equally undefined as calling msbuildArve
And there's no VS150COMNTOOLS or similar env var resolving the 'enterpirse' thingie?stijn
Maybe they did it that way because it's possible to install enterprise/community/... on one machine? In any case, this kinda sucks. Hopefully something changes by the time the release comes.stijn
The problem is: the user may install either edition (Community/Professional/Enterprise) but the directory name depends on editionlinquize

9 Answers

25
votes

The supported way

VS2017's setup engine has an interop API that allows you to query information about which instance(s) of VS2017 are installed. There's a NuGet package for it, and even a sample of how to query, including the installation path. For consumption in a batch or PS file, you might just rewrite the sample app and call it from your script to output the information you need.

The unsupported way

The VS setup engine keeps a store of its data under %ProgramData%\Microsoft\VisualStudio\Packages\_Instances. There will be a folder for each instance of VS2017, and inside that folder is a state.json file that contains information about that installation, including the install path.

Since you need to extract the info from the .json file, you have the option to either write an app that you call from your script file, or come up with some parsing logic directly in your script. This will obviously be fragile as the JSON schema or file location may change.

The brute force method

Assuming you are using the default installation path, you could just recursively search for msbuild.exe under %ProgramFiles(x86)%\Microsoft Visual Studio\ (it appears that there will be 32-bit and 64-bit msbuild.exe for each VS instance). This would probably be the easiest to do within your script file, but does rely on the default installation path (or whatever hardcoded path you wish to search under).

Change your dev environment requirements

The last thing you could do is require that devs use (or somehow invoke) the vsdevcmd.bat to use the VS dev environment. This will get them MSBuild, and additionally any other tools from the VS environment, onto their %PATH%. This does place a requirement on your dev team, but will always be an officially supported way to find msbuild.exe.

18
votes

Microsoft have created a tool to find the location of Visual Studio 2017 and newer https://github.com/Microsoft/vswhere

That option and newer are decribed in this blog post.

8
votes

Simplest way...

Right at the bottom of the download page on VS

  • Profit; the build tools will install to a separate location thats standard across everywhere you install it regardless of your VS SKU (professional / enterprise / community all install to different paths)
  • New location C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\MSBuild\15.0\Bin\msbuild.exe
8
votes

Posting this for folks still trying to locate the newest version of msbuild on a machine and fall back to an older one if that is availble instead. I find this snippet of powershell does the trick, which can easily be invoked from a bat file.

Function Find-MsBuild([int] $MaxVersion = 2017)
{
    $agentPath = "$Env:programfiles (x86)\Microsoft Visual Studio\2017\BuildTools\MSBuild\15.0\Bin\msbuild.exe"
    $devPath = "$Env:programfiles (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\msbuild.exe"
    $proPath = "$Env:programfiles (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin\msbuild.exe"
    $communityPath = "$Env:programfiles (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\msbuild.exe"
    $fallback2015Path = "${Env:ProgramFiles(x86)}\MSBuild\14.0\Bin\MSBuild.exe"
    $fallback2013Path = "${Env:ProgramFiles(x86)}\MSBuild\12.0\Bin\MSBuild.exe"
    $fallbackPath = "C:\Windows\Microsoft.NET\Framework\v4.0.30319"

    If ((2017 -le $MaxVersion) -And (Test-Path $agentPath)) { return $agentPath } 
    If ((2017 -le $MaxVersion) -And (Test-Path $devPath)) { return $devPath } 
    If ((2017 -le $MaxVersion) -And (Test-Path $proPath)) { return $proPath } 
    If ((2017 -le $MaxVersion) -And (Test-Path $communityPath)) { return $communityPath } 
    If ((2015 -le $MaxVersion) -And (Test-Path $fallback2015Path)) { return $fallback2015Path } 
    If ((2013 -le $MaxVersion) -And (Test-Path $fallback2013Path)) { return $fallback2013Path } 
    If (Test-Path $fallbackPath) { return $fallbackPath } 

    throw "Yikes - Unable to find msbuild"
}


$msbuildPath = Find-MsBuild 2017
4
votes

Now that VS 2017 has been released, thought it might be useful to add my 2 cents:

Creating a batch file:

Open Notepad

Copy the following and paste into notepad, replacing the solution you want to build with [My Solution Name Here]:

@echo off

:variables
SET msBuildLocation="C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\msbuild.exe"
SET solutionDirectory=[My Solution Location Here]
SET batchFileLocation=[BatchFileLocation Here]

cd %solutionDirectory%

echo Building MS files for [My Solution Name Here].

call %msBuildLocation% [My Solution Name].sln /p:Configuration=Debug /m:4 /v:M /fl /flp:LogFile=msbuild.log;Verbosity=Normal /nr:false  /consoleloggerparameters:Summary;ShowTimestamp;ShowEventId;PerformanceSummary
echo -
echo --------Done building [My solution name here].--------------
echo -

cd %batchFileLocation%

Save your batch file as whatever you want with the .bat extension. Be sure to select all files instead of .txt or else it won't work. Call the batch file by issuing the cd command where you have it saved at, and it should work. Or, just double-click. Done.

Please note that my version of visual studio is community; you'll need to replace the path with the appropriate version.

3
votes

I also run the build tools from a bat file on my CI server: The new location I've found is this:

%programfiles(x86)%\Microsoft Visual Studio\2017\BuildTools\MSBuild\15.0\Bin\msbuild.exe

1
votes

If you have installed the .NET Core tools preview 3 or later, the dotnet CLI should be available on the %PATH% by default (even if you're not building a .NET Core project), so you could try dotnet msbuild. Ref https://github.com/dotnet/docs/blob/master/docs/core/preview3/tools/dotnet-msbuild.md

1
votes

I maintain a very simple PowerShell script (gist) to run CI/CLI builds with properly configured Visual Studio environment variables. It uses vswhere.exe and VsDevCmd.bat. It works well on build agents as a part of Azure DevOps pipelines. Particularly for C++ projects, it's vital to set INCLUDE and LIB properly, that's what VsDevCmd.bat does.

E.g, run a build with MSBuild for the solution in the current folder:

powershell -f _invoke-build.ps1 -buildCommand "msbuild ."

Copy VS' INCLUDE environment variable into clipboard (for fun):

powershell -f _invoke-build.ps1 -buildCommand "set INCLUDE | clip"

The current version of the script:

<#
  .Synopsis
    Find Visual Studio and run its VsDevCmd.bat, then run a build command.

  .Example
    powershell -f _invoke-build.ps1 -buildCommand "msbuild ."

  .Link
    https://gist.github.com/noseratio/843bb4d9c410c42081fac9d8b7a33b5e
#>

#Requires -Version 5.0
# by @noseratio

param([Parameter(Mandatory = $true)] [string] $buildCommand)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

echo "Building via '$buildCommand' ..."

$vs_not_found = "Visual Studio hasn't been detected!"

$vswhere = "${env:ProgramFiles(x86)}/Microsoft Visual Studio/Installer/vswhere.exe"
if (!(Test-Path $vswhere)) { throw $vs_not_found }

$vs_ide_path = $(& $vswhere -format value -property productPath)
if (!(Test-Path $vs_ide_path)) { throw $vs_not_found }

$vs_ide_folder = "$(Split-Path $vs_ide_path)"
$vs_dev_cmd = "$vs_ide_folder/../Tools/VsDevCmd.bat"
$invoke = "call ""$vs_dev_cmd"" && cd ""$pwd"" && $buildCommand"

# we need to run via CMD for proper propagation of Visual Studio environment vars
& $env:comspec /s /c ""$invoke""
if (!$?) { throw "Failed with error code: $LastExitCode" }

-1
votes

SO I have Community edition 2017, for me path is: C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\

Other solutions on this thread throwed me an exception.