217

How can I programatically get the path to MSBuild from a machine where my .exe is running?

I can get the .NET version from the Environment but is there a way of getting the correct folder for a .NET version?

Lukasz Lysik
  • 10,462
  • 3
  • 51
  • 72
dagda1
  • 26,856
  • 59
  • 237
  • 450

23 Answers23

161

Poking around the registry, it looks like

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSBuild\ToolsVersions\2.0
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSBuild\ToolsVersions\3.5
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSBuild\ToolsVersions\4.0

may be what you're after; fire up regedit.exe and have a look.

Query via command line (per Nikolay Botev)

reg.exe query "HKLM\SOFTWARE\Microsoft\MSBuild\ToolsVersions\4.0" /v MSBuildToolsPath

Query via PowerShell (per MovGP0)

dir HKLM:\SOFTWARE\Microsoft\MSBuild\ToolsVersions\
jpaugh
  • 6,634
  • 4
  • 38
  • 90
Brian
  • 117,631
  • 17
  • 236
  • 300
  • 7
    I have installed Visual Studio 2017 RC and starting the Developer Command Prompt, the MSBuild version is 15.+, but this version doesn't show in the registry. How do I get access to the same MSBuild that the Dev Cmd Prompt is using? – SuperJMN Jan 07 '17 at 12:54
  • 6
    MSBuild 15 is located at `C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\amd64\` – nZeus Mar 03 '17 at 23:14
  • 2
    Only if you installed VS2017 there, I couldn't find a single entry point in the registry for MsBuildToolsPath for the 15.0 toolset – Paciv Mar 08 '17 at 11:15
  • 1
    As per David Moore on https://developercommunity.visualstudio.com/content/problem/2813/cant-find-registry-entries-for-visual-studio-2017.html there is a way through the registry : HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS\VS7 for base VS2017 installation path – Paciv Mar 08 '17 at 15:26
  • 14
    https://learn.microsoft.com/en-us/visualstudio/msbuild/what-s-new-in-msbuild-15-0 "MSBuild is now installed in a folder under each version of Visual Studio. For example, C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild" and "ToolsVersion values are no longer set in the registry" – Hulvej May 08 '17 at 13:47
  • 1
    @Hulvej: So, what's the solution? How can external build scripts find MSBuild? – O. R. Mapper Oct 19 '17 at 11:24
  • I guess, either ensure that msbuild is set in the $PATH or knowing what version of MS build tools in installed. Or use Docker :) – Hulvej Oct 19 '17 at 12:10
  • 3
    @O.R.Mapper Microsoft offers [a project on GitHub](https://github.com/Microsoft/vswhere) for determining paths of *Visual Studio 2017/msbuild 15.x* instances. It is a single executable which can be used by your build software/scripts. – Roi Danton Dec 22 '17 at 11:24
146

You can also print the path of MSBuild.exe to the command line:

reg.exe query "HKLM\SOFTWARE\Microsoft\MSBuild\ToolsVersions\4.0" /v MSBuildToolsPath
Nikolay Botev
  • 1,588
  • 1
  • 9
  • 6
  • 2
    Please note that if you want to build a windows phone app, that needs the 32 bits msbuild. Querying the registry gives only the 64 bit msbuild on a 64 bit machine. – Victor Ionescu Jul 03 '14 at 11:39
  • 3
    @VictorIonescu: You can use `/reg:32` or `/reg:64` on both bitnessess of `cmd` (or whatever process you are running) to explicitly get that path. – Simon Buchan Apr 20 '16 at 01:53
  • this will give you the path to an old (4.0) location - the one you probably want is actually elsewhere see https://stackoverflow.com/questions/32007871/how-to-upgrade-msbuild-to-c-sharp-6 – JonnyRaa Apr 30 '18 at 10:29
  • In my case it was under `Computer\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\MSBuild\ToolsVersions\4.0\MSBuildToolsPath` – Sen Jacob Jul 23 '19 at 02:55
79

Instructions for finding MSBuild:

  • PowerShell: &"${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -prerelease -products * -requires Microsoft.Component.MSBuild -find MSBuild\**\Bin\MSBuild.exe
  • CMD: "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -latest -prerelease -products * -requires Microsoft.Component.MSBuild -find MSBuild\**\Bin\MSBuild.exe

Instructions for finding VSTest:

  • PowerShell: &"${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -prerelease -products * -requires Microsoft.VisualStudio.PackageGroup.TestTools.Core -find Common7\IDE\CommonExtensions\Microsoft\TestWindow\vstest.console.exe
  • CMD: "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -latest -prerelease -products * -requires Microsoft.VisualStudio.PackageGroup.TestTools.Core -find Common7\IDE\CommonExtensions\Microsoft\TestWindow\vstest.console.exe

(Note that the instructions above are slightly modified from Microsoft's official ones. In particular, I've included the -prerelease flag to allow Preview and RC installations to be picked up, and the -products * to detect Visual Studio Build Tools installations. Configure as required by your use case.)


It only took over two years but finally in 2019, Microsoft has listened and given us a way to find these vital executables! If you have Visual Studio 2017 or newer installed, the vswhere utility can be queried for the location of MSBuild et al. Since vswhere is guaranteed by Microsoft to be located at %ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe, there is no bootstrapping, no path hardcoding, and no registry probing required any more.

The magic is the -find parameter, added in version 2.6.2. You can determine the version you have installed by running vswhere, or checking its file properties. If you have an older version, you can simply download the latest one and overwrite the existing %ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe.

vswhere.exe is a standalone executable, so you can download and run it from anywhere you have an internet connection. That means your build scripts can check if the environment they're running on is setup correctly, to name one option.

If you have Chocolatey installed, you can also use the relevant vswhere package.

Ian Kemp
  • 28,293
  • 19
  • 112
  • 138
  • There are already 3 answers that mention vswhere, including your comment to that effect under one of them. Adding this answer just makes the answer soup worse. – jpaugh Nov 15 '18 at 15:59
  • 10
    4 now. I found this answer helpful, whereas I did not find the other vswhere answers helpful. – cowlinator Jun 20 '19 at 00:03
  • 1
    Worth noting that just because VSWHERE says that is the msbuild.exe to use, it does not mean that if you type `msbuild` at the command line (especially the Visual Studio command line if using that), that is the one that will get used. To see what gets used if you type `msbuild` at the command line, do this: `where msbuild`. If that's not reporting the same as VSWHERE says the latest and greatest is, then either you have to do a full path to the `msbuild.exe` you want to use, or make adjustments to your PATH variables to suit. – Jinlye Jul 11 '19 at 14:25
  • 1
    So the next question is how do you find the path to vswhere.... – BJury Oct 10 '19 at 08:41
  • 1
    @BJury Microsoft guarantees that it will always be at "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe". That's your starting point. Alternatively, as noted in the last 2 paragraphs, you can download `vswhere.exe` and use it directly. – Ian Kemp Nov 02 '20 at 14:28
  • it picks up only BuildTools MSBuild in my case (i.e. not those ones installed with a full Visual Studio) – Random Oct 19 '22 at 02:04
  • The PowerShell version should use `VSSetup` cmdlet to simplify, https://github.com/microsoft/vssetup.powershell – Lex Li Dec 18 '22 at 22:45
37

If you want to use MSBuild for .Net 4 then you can use the following PowerShell command to get the executable's path. If you want version 2.0 or 3.5 then just change the $dotNetVersion variable.

To run the executable you'll need to prepend the $msbuild variable with &. That will execute the variable.

# valid versions are [2.0, 3.5, 4.0]
$dotNetVersion = "4.0"
$regKey = "HKLM:\software\Microsoft\MSBuild\ToolsVersions\$dotNetVersion"
$regProperty = "MSBuildToolsPath"

$msbuildExe = join-path -path (Get-ItemProperty $regKey).$regProperty -childpath "msbuild.exe"

&$msbuildExe
AllenSanborn
  • 871
  • 1
  • 8
  • 13
  • 2
    works also for `$dotNetVersion` 12.0 (vs 2013) and 14.0 (vs 2015) (if installed of course) – Julian Nov 10 '16 at 11:12
  • 4
    [Does not work for VS 2017, which does not add a value under the `HKLM:\software\Microsoft\MSBuild\ToolsVersions` key.](https://learn.microsoft.com/en-us/visualstudio/msbuild/what-s-new-in-msbuild-15-0) Instead you need to get the VS2017 install dir from `HKLM:\SOFTWARE\WOW6432Node\Microsoft\VisualStud‌​io\SxS\VS7\15.0`, then append `MSBuild\15.0\Bin\MSBuild.exe` to get the MSBuild EXE location. – Ian Kemp May 11 '17 at 10:59
31

For cmd shell scripting in Windows 7, I use the following fragment in my batch file to find MSBuild.exe in the .NET Framework version 4. I assume version 4 is present, but don't assume the sub-version. This isn't totally general-purpose, but for quick scripts it may be helpful:

set msbuild.exe=
for /D %%D in (%SYSTEMROOT%\Microsoft.NET\Framework\v4*) do set msbuild.exe=%%D\MSBuild.exe

For my uses I'm exiting the batch file with an error if that didn't work:

if not defined msbuild.exe echo error: can't find MSBuild.exe & goto :eof
if not exist "%msbuild.exe%" echo error: %msbuild.exe%: not found & goto :eof
yoyo
  • 8,310
  • 4
  • 56
  • 50
  • @yoyo What's `set bb.build.msbuild.exe=` for? Is it required or just an artifact of your setup? – Elisée May 17 '16 at 11:16
  • @Elisée Oops, sorry, that's a copy/paste typo. In my environment I call the variable bb.build.msbuild.exe, I neglected to fix that instance when I pasted into the answer. Fixed now, thanks for pointing that out. – yoyo May 17 '16 at 16:56
30

You can use this very trial PowerShell Command to get the MSBuildToolsPath from the registry.

PowerShell (from registry)

Resolve-Path HKLM:\SOFTWARE\Microsoft\MSBuild\ToolsVersions\* | 
Get-ItemProperty -Name MSBuildToolsPath

Output

MSBuildToolsPath : C:\Program Files (x86)\MSBuild\12.0\bin\amd64\
PSPath           : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSBuild\ToolsVersions\12.0
PSParentPath     : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSBuild\ToolsVersions
PSChildName      : 12.0
PSDrive          : HKLM
PSProvider       : Microsoft.PowerShell.Core\Registry

MSBuildToolsPath : C:\Program Files (x86)\MSBuild\14.0\bin\amd64\
PSPath           : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSBuild\ToolsVersions\14.0
PSParentPath     : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSBuild\ToolsVersions
PSChildName      : 14.0
PSDrive          : HKLM
PSProvider       : Microsoft.PowerShell.Core\Registry

MSBuildToolsPath : C:\Windows\Microsoft.NET\Framework64\v2.0.50727\
PSPath           : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSBuild\ToolsVersions\2.0
PSParentPath     : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSBuild\ToolsVersions
PSChildName      : 2.0
PSDrive          : HKLM
PSProvider       : Microsoft.PowerShell.Core\Registry

MSBuildToolsPath : C:\Windows\Microsoft.NET\Framework64\v3.5\
PSPath           : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSBuild\ToolsVersions\3.5
PSParentPath     : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSBuild\ToolsVersions
PSChildName      : 3.5
PSDrive          : HKLM
PSProvider       : Microsoft.PowerShell.Core\Registry

MSBuildToolsPath : C:\Windows\Microsoft.NET\Framework64\v4.0.30319\
PSPath           : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSBuild\ToolsVersions\4.0
PSParentPath     : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSBuild\ToolsVersions
PSChildName      : 4.0
PSDrive          : HKLM
PSProvider       : Microsoft.PowerShell.Core\Registry

or from the filesystem

PowerShell (from file system)

Resolve-Path "C:\Program Files (x86)\MSBuild\*\Bin\amd64\MSBuild.exe"
Resolve-Path "C:\Program Files (x86)\MSBuild\*\Bin\MSBuild.exe"

Output

Path
----
C:\Program Files (x86)\MSBuild\12.0\Bin\amd64\MSBuild.exe
C:\Program Files (x86)\MSBuild\14.0\Bin\amd64\MSBuild.exe
C:\Program Files (x86)\MSBuild\12.0\Bin\MSBuild.exe
C:\Program Files (x86)\MSBuild\14.0\Bin\MSBuild.exe
hdev
  • 6,097
  • 1
  • 45
  • 62
  • 2
    The best answer on this topic. – Teoman shipahi Feb 28 '17 at 19:58
  • 1
    Unfortunately this is no longer best because newer versions are delivered within VisualStudio and not registered here – Dmitry Gusarov Apr 03 '21 at 22:33
  • @hdev Thanks for this answer. If anyone is looking for MSBuild.exe with Visual Studio - this works for VS2019 & VS2022 PowerShell (from file system) ```powershell Resolve-Path 'C:\Program Files*\Microsoft Visual Studio\*\Professional\Msbuild\Current\bin\MSBuild.exe' ``` – Sir CodesALot Jan 20 '22 at 19:16
17

@AllenSanborn has a great powershell version, but some folks have a requirement to use only batch scripts for builds.

This is an applied version of what @bono8106 answered.

msbuildpath.bat

@echo off

reg.exe query "HKLM\SOFTWARE\Microsoft\MSBuild\ToolsVersions\14.0" /v MSBuildToolsPath > nul 2>&1
if ERRORLEVEL 1 goto MissingMSBuildRegistry

for /f "skip=2 tokens=2,*" %%A in ('reg.exe query "HKLM\SOFTWARE\Microsoft\MSBuild\ToolsVersions\14.0" /v MSBuildToolsPath') do SET "MSBUILDDIR=%%B"

IF NOT EXIST "%MSBUILDDIR%" goto MissingMSBuildToolsPath
IF NOT EXIST "%MSBUILDDIR%msbuild.exe" goto MissingMSBuildExe

exit /b 0

goto:eof
::ERRORS
::---------------------
:MissingMSBuildRegistry
echo Cannot obtain path to MSBuild tools from registry
goto:eof
:MissingMSBuildToolsPath
echo The MSBuild tools path from the registry '%MSBUILDDIR%' does not exist
goto:eof
:MissingMSBuildExe
echo The MSBuild executable could not be found at '%MSBUILDDIR%'
goto:eof

build.bat

@echo off
call msbuildpath.bat
"%MSBUILDDIR%msbuild.exe" foo.csproj /p:Configuration=Release

For Visual Studio 2017 / MSBuild 15, Aziz Atif (the guy who wrote Elmah) wrote a batch script

build.cmd Release Foo.csproj

https://github.com/linqpadless/LinqPadless/blob/master/build.cmd

@echo off
setlocal
if "%PROCESSOR_ARCHITECTURE%"=="x86" set PROGRAMS=%ProgramFiles%
if defined ProgramFiles(x86) set PROGRAMS=%ProgramFiles(x86)%
for %%e in (Community Professional Enterprise) do (
    if exist "%PROGRAMS%\Microsoft Visual Studio\2017\%%e\MSBuild\15.0\Bin\MSBuild.exe" (
        set "MSBUILD=%PROGRAMS%\Microsoft Visual Studio\2017\%%e\MSBuild\15.0\Bin\MSBuild.exe"
    )
)
if exist "%MSBUILD%" goto :restore
set MSBUILD=
for %%i in (MSBuild.exe) do set MSBUILD=%%~dpnx$PATH:i
if not defined MSBUILD goto :nomsbuild
set MSBUILD_VERSION_MAJOR=
set MSBUILD_VERSION_MINOR=
for /f "delims=. tokens=1,2,3,4" %%m in ('msbuild /version /nologo') do (
    set MSBUILD_VERSION_MAJOR=%%m
    set MSBUILD_VERSION_MINOR=%%n
)
if not defined MSBUILD_VERSION_MAJOR goto :nomsbuild
if not defined MSBUILD_VERSION_MINOR goto :nomsbuild
if %MSBUILD_VERSION_MAJOR% lss 15    goto :nomsbuild
if %MSBUILD_VERSION_MINOR% lss 1     goto :nomsbuild
:restore
for %%i in (NuGet.exe) do set nuget=%%~dpnx$PATH:i
if "%nuget%"=="" (
    echo WARNING! NuGet executable not found in PATH so build may fail!
    echo For more on NuGet, see https://github.com/nuget/home
)
pushd "%~dp0"
nuget restore ^
 && call :build Debug   %* ^
 && call :build Release %*
popd
goto :EOF

:build
setlocal
"%MSBUILD%" /p:Configuration=%1 /v:m %2 %3 %4 %5 %6 %7 %8 %9
goto :EOF

:nomsbuild
echo Microsoft Build version 15.1 (or later) does not appear to be
echo installed on this machine, which is required to build the solution.
exit /b 1
JJS
  • 6,431
  • 1
  • 54
  • 70
  • 2
    Note: Since VS2017/msbuild 15.x doesn't use the registry for their paths, [vswhere](https://github.com/Microsoft/vswhere) is an alternative to determine the msbuild path. – Roi Danton Dec 22 '17 at 11:27
  • 1
    Also, AzizAtif is the man. Take a gander at this for 15.1 builds - https://github.com/linqpadless/LinqPadless/blob/master/build.cmd – JJS Jan 31 '18 at 23:11
  • 2
    Also, vswhere can be installed via Chocolatey: https://chocolatey.org/packages/vswhere – cowlinator May 04 '18 at 21:34
8

This works for Visual Studio 2015 and 2017:

function Get-MSBuild-Path {

    $vs14key = "HKLM:\SOFTWARE\Microsoft\MSBuild\ToolsVersions\14.0"
    $vs15key = "HKLM:\SOFTWARE\wow6432node\Microsoft\VisualStudio\SxS\VS7"

    $msbuildPath = ""

    if (Test-Path $vs14key) {
        $key = Get-ItemProperty $vs14key
        $subkey = $key.MSBuildToolsPath
        if ($subkey) {
            $msbuildPath = Join-Path $subkey "msbuild.exe"
        }
    }

    if (Test-Path $vs15key) {
        $key = Get-ItemProperty $vs15key
        $subkey = $key."15.0"
        if ($subkey) {
            $msbuildPath = Join-Path $subkey "MSBuild\15.0\bin\amd64\msbuild.exe"
        }
    }

    return $msbuildPath

}
Raman Zhylich
  • 3,537
  • 25
  • 23
  • 4
    For VS2017, see also: https://github.com/Microsoft/vswhere, https://github.com/Microsoft/vssetup.powershell, and https://github.com/deadlydog/Invoke-MsBuild – Ian Kemp May 17 '17 at 10:44
  • 3
    For Build Tools, use `vswhere -products *`, as specified in https://github.com/Microsoft/vswhere/wiki/Find-MSBuild. – TN. May 17 '17 at 15:53
  • 1
    For vswhere you should know the path where it is located. And of course you should have power-shell available for your build system. Just one question: why amd64? Does it have anything specific for building? – Maxim Aug 22 '17 at 18:41
  • Upvoted because this solution essentially just uses a registry key for MSBuild 15 too, not a third party library or script. Out of curiosity, what does "SxS\VS7" refer to? Will that stay valid across VS versions? – Lazlo Dec 21 '18 at 20:51
6

An one-liner based on @dh_cgn's answer:

(Resolve-Path ([io.path]::combine(${env:ProgramFiles(x86)}, 'Microsoft Visual Studio', '*', '*', 'MSBuild', '*' , 'bin' , 'msbuild.exe'))).Path

It selects all existing paths paths of eg. C:\Program Files (x86)\Microsoft Visual Studio\*\*\MSBuild\*\bin\msbuild.exe.

The wildcards stars are:

  • the year (2017)
  • the visual studio edition (community, professional, enterprise)
  • the tools version (15.0)

Be aware that this command is selecting the first path that matches the expression ordered by alphabet. To narrow it down just replace the wildcards with specific elements eg. the year or tools version.

jpaugh
  • 6,634
  • 4
  • 38
  • 90
SeriousM
  • 3,374
  • 28
  • 33
  • Add `amd64` to find the 64-bit ones: ``` (Resolve-Path ([io.path]::combine(${env:ProgramFiles(x86)}, 'Microsoft Visual Studio', '*', '*', 'MSBuild', '*' , 'bin' , 'amd64', 'msbuild.exe'))).Path ``` – Ray Depew May 25 '21 at 19:34
5

The Registry locations

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSBuild\ToolsVersions\2.0
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSBuild\ToolsVersions\3.5

give the location for the executable.

But if you need the location where to save the Task extensions, it's on

%ProgramFiles%\MSBuild
Paulo Santos
  • 11,285
  • 4
  • 39
  • 65
  • 4
    It's pretty old, I know - but anyway: on x64-Systems, the MSBuild-Folder is located in ProgramFiles(x86) – Sascha May 27 '11 at 23:21
4

easiest way might be to open PowerShell and enter

dir HKLM:\SOFTWARE\Microsoft\MSBuild\ToolsVersions\
MovGP0
  • 7,267
  • 3
  • 49
  • 42
4

On Windows 2003 and later, type this command in cmd:

cmd> where MSBuild
Sample result: C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe

If nothing appears, it means that .NET framework is not included in the system PATH. The MSBuild should be in the .NET installation folder, along with .NET compilers (vbc.exe, csc.exe)

draganicimw
  • 87
  • 1
  • 1
  • This answer doesn't add much over other answers. It's less robust than [this answer](https://stackoverflow.com/a/15446618/712526) – jpaugh Nov 15 '18 at 15:49
4

Starting with MSBuild 2017 (v15), MSBuild is now installed in a folder under each version of Visual Studio

Here are some examples of where MSBuild.exe is found on my machine:

C:\windows\Microsoft.NET\Framework\v2.0.50727\MSBuild.exe  (v2.0.50727.8745  32-bit)
C:\windows\Microsoft.NET\Framework64\v2.0.50727\MSBuild.exe  (v2.0.50727.8745  64-bit)
C:\Windows\Microsoft.NET\Framework\v3.5\MSBuild.exe  (v3.5.30729.8763 32-bit)
C:\Windows\Microsoft.NET\Framework64\v3.5\MSBuild.exe  (v3.5.30729.8763 64-bit)
C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe  (v4.7.2053.0 32-bit)
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe  (v4.7.2053.0 64-bit)
C:\Program Files (x86)\MSBuild\12.0\Bin\MSBuild.exe  (v12.0.21005.1 32-bit)
C:\Program Files (x86)\MSBuild\12.0\Bin\amd64\MSBuild.exe (v12.0.21005.1 64-bit)
C:\Program Files (x86)\MSBuild\14.0\Bin\MSBuild.exe  (v14.0.25420.1 32-bit)
C:\Program Files (x86)\MSBuild\14.0\Bin\amd64\MSBuild.exe  (v14.0.25420.1 64-bit)
C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\MSBuild\15.0\Bin\MSBuild.exe  (v15.1.1012+g251a9aec17 32-bit)
C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\MSBuild\15.0\Bin\amd64\MSBuild.exe (v15.1.1012+g251a9aec17 64-bit)
C:\Program Files (x86)\Microsoft Visual Studio\2017\{LicenceName}\MSBuild\Bin\MSBuild.exe (v15.1.1012.6693 32-bit)
C:\Program Files (x86)\Microsoft Visual Studio\2017\{LicenceName}\MSBuild\Bin\amd64\MSBuild.exe (v15.1.1012.6693 64-bit)
cowlinator
  • 7,195
  • 6
  • 41
  • 61
  • According to a [previous answer](https://stackoverflow.com/a/43623213/712526), 2017 *does* in fact store this information in the registry. – jpaugh Nov 15 '18 at 15:53
  • Tools like `vswhere` and `VSSetup` were created along with VS2017 to assist locating MSBuild, so even in year 2017 this isn't a good enough answer. – Lex Li Dec 18 '22 at 22:52
3

There are many correct answers. However, here a One-Liner in PowerShell I use to determine the MSBuild path for the most recent version:

Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\MSBuild\ToolsVersions\' | 
    Get-ItemProperty -Name MSBuildToolsPath | 
    Sort-Object PSChildName | 
    Select-Object -ExpandProperty MSBuildToolsPath -first 1
Martin Brandl
  • 56,134
  • 13
  • 133
  • 172
  • 2
    +1 Really useful! But in my answer I use `-last 1` (instead of `-first 1` in order to get the latest version) and also concatenate the file name (to properly get the full path and not only the folder). – Mariano Desanze Sep 13 '19 at 21:56
2

To retrieve path of msbuild 15 (Visual Studio 2017) with batch from registry w/o additional tools:

set regKey=HKLM\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS\VS7
set regValue=15.0
for /f "skip=2 tokens=3,*" %%A in ('reg.exe query %regKey% /v %regValue% 2^>nul') do (
    set vs17path=%%A %%B
)
set msbuild15path = %vs17path%\MSBuild\15.0\Bin\MSBuild.exe

Better available tools:

Roi Danton
  • 7,933
  • 6
  • 68
  • 80
  • 1
    you saved my life – Hakan Fıstık Jul 18 '18 at 10:25
  • There is already a [PowerShell](https://stackoverflow.com/a/43623213/712526) version of this. Circa 2017, is there any reason to avoid learning Powershell? – jpaugh Nov 15 '18 at 15:54
  • 2
    @jpaugh Not every build system has PowerShell available. – Roi Danton Nov 15 '18 at 21:52
  • Every supported Windows release in 2017 has PowerShell preinstalled, so "Not every build system has PowerShell available" isn't true. – Lex Li Dec 18 '22 at 22:57
  • PowerShell may be installed, but restricted. You may need to run: "powershell.exe -Command Set-ExecutionPolicy -ExecutionPolicy RemoteSigned" for anything more than a one-liner. Do I really have to wrap every ps script in a cmd to Set-ExecutionPolicy, just in case? Or maybe I just don't understand PS. – Andrew Dennison Mar 02 '23 at 05:01
1

This powershell method gets the path to msBuild from multiple sources. Trying in order:

  1. First using vswhere (because Visual Studio seems to have more up to date versions of msBuild) e.g.

    C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin\MSBuild.exe
    
  2. If not found trying the registry (framework version) e.g.

    C:\Windows\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe
    

Powershell code:

Function GetMsBuildPath {

    Function GetMsBuildPathFromVswhere {
        # Based on https://github.com/microsoft/vswhere/wiki/Find-MSBuild/62adac8eb22431fa91d94e03503d76d48a74939c
        $vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
        $path = & $vswhere -latest -prerelease -products * -requires Microsoft.Component.MSBuild -property installationPath
        if ($path) {
            $tool = join-path $path 'MSBuild\Current\Bin\MSBuild.exe'
            if (test-path $tool) {
                return $tool
            }
            $tool = join-path $path 'MSBuild\15.0\Bin\MSBuild.exe'
            if (test-path $tool) {
                return $tool
            }
        }
    }

    Function GetMsBuildPathFromRegistry {
        # Based on Martin Brandl's answer: https://stackoverflow.com/a/57214958/146513
        $msBuildDir = Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\MSBuild\ToolsVersions\' |
            Get-ItemProperty -Name MSBuildToolsPath |
            Sort-Object PSChildName |
            Select-Object -ExpandProperty MSBuildToolsPath -last 1
        $msBuildPath = join-path $msBuildDir 'msbuild.exe'
        if (test-path $msBuildPath) {
            return $msBuildPath
        }
    }

    $msBuildPath = GetMsBuildPathFromVswhere
    if (-Not $msBuildPath) {
        $msBuildPath = GetMsBuildPathFromRegistry
    }
    return $msBuildPath
}
Mariano Desanze
  • 7,847
  • 7
  • 46
  • 67
0

For Visual Studio 2017 without knowing the exact edition you could use this in a batch script:

FOR /F "tokens=* USEBACKQ" %%F IN (`where /r "%PROGRAMFILES(x86)%\Microsoft Visual 
Studio\2017" msbuild.exe ^| findstr /v /i "amd64"`) DO (SET msbuildpath=%%F)

The findstr command is to ignore certain msbuild executables (in this example the amd64).

Ernstjan Freriks
  • 622
  • 6
  • 12
0

add vswhere branch for https://github.com/linqpadless/LinqPadless/blob/master/build.cmd, works fine in my computer, and the vswhere branch works on my mate's computer. May be, the vswhere branch should move forward as the first check.

@echo off
setlocal
if "%PROCESSOR_ARCHITECTURE%"=="x86" set PROGRAMS=%ProgramFiles%
if defined ProgramFiles(x86) set PROGRAMS=%ProgramFiles(x86)%
for %%e in (Community Professional Enterprise) do (
    if exist "%PROGRAMS%\Microsoft Visual Studio\2017\%%e\MSBuild\15.0\Bin\MSBuild.exe" (
        set "MSBUILD=%PROGRAMS%\Microsoft Visual Studio\2017\%%e\MSBuild\15.0\Bin\MSBuild.exe"
    )
)
if exist "%MSBUILD%" goto :build

for /f "usebackq tokens=1* delims=: " %%i in (`"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -latest -requires Microsoft.Component.MSBuild`) do (
  if /i "%%i"=="installationPath" set InstallDir=%%j
)

if exist "%InstallDir%\MSBuild\15.0\Bin\MSBuild.exe" (
  set "MSBUILD=%InstallDir%\MSBuild\15.0\Bin\MSBuild.exe"
)
if exist "%MSBUILD%" goto :build
set MSBUILD=
for %%i in (MSBuild.exe) do set MSBUILD=%%~dpnx$PATH:i
if not defined MSBUILD goto :nomsbuild
set MSBUILD_VERSION_MAJOR=
set MSBUILD_VERSION_MINOR=
for /f "delims=. tokens=1,2,3,4" %%m in ('msbuild /version /nologo') do (
    set MSBUILD_VERSION_MAJOR=%%m
    set MSBUILD_VERSION_MINOR=%%n
)
echo %MSBUILD_VERSION_MAJOR% %MSBUILD_VERSION_MINOR%
if not defined MSBUILD_VERSION_MAJOR goto :nomsbuild
if not defined MSBUILD_VERSION_MINOR goto :nomsbuild
if %MSBUILD_VERSION_MAJOR% lss 15    goto :nomsbuild
if %MSBUILD_VERSION_MINOR% lss 1     goto :nomsbuild
:restore
for %%i in (NuGet.exe) do set nuget=%%~dpnx$PATH:i
if "%nuget%"=="" (
    echo WARNING! NuGet executable not found in PATH so build may fail!
    echo For more on NuGet, see https://github.com/nuget/home
)
pushd "%~dp0"
popd
goto :EOF

:build
setlocal
"%MSBUILD%" -restore -maxcpucount %1 /p:Configuration=%2 /v:m %3 %4 %5 %6 %7 %8 %9
goto :EOF

:nomsbuild
echo Microsoft Build version 15.1 (or later) does not appear to be
echo installed on this machine, which is required to build the solution.
exit /b 1
Shamork.Fu
  • 53
  • 7
0

You wouldn't think there's much to add here, but perhaps it's time for a unified way of doing this across all versions. I've combined the registry-query approach (VS2015 and below) with use of vswhere (VS2017 and above) to come up with this:

function Find-MsBuild {
    Write-Host "Using VSWhere to find msbuild..."
    $path = & $vswhere -latest -requires Microsoft.Component.MSBuild -find MSBuild\**\Bin\MSBuild.exe | select-object -first 1

    if (!$path) {
        Write-Host "No results from VSWhere, using registry key query to find msbuild (note this will find pre-VS2017 versions)..."
        $path = Resolve-Path HKLM:\SOFTWARE\Microsoft\MSBuild\ToolsVersions\* |
                    Get-ItemProperty -Name MSBuildToolsPath |
                    sort -Property @{ Expression={ [double]::Parse($_.PSChildName) }; Descending=$true } |
                    select -exp MSBuildToolsPath -First 1 |
                    Join-Path -ChildPath "msbuild.exe"
    }

    if (!$path) {
        throw "Unable to find path to msbuild.exe"
    }

    if (!(Test-Path $path)) {
        throw "Found path to msbuild as $path, but file does not exist there"
    }

    Write-Host "Using MSBuild at $path..."
    return $path
}
Neil Barnwell
  • 41,080
  • 29
  • 148
  • 220
  • Compared to other answers, you ignored the path of `vswhere` or how to acquire it, so this isn't a complete answer. – Lex Li Dec 18 '22 at 22:50
  • You're right. I really should complete the spoonfeeding process by including' nugent install Vswhere' – Neil Barnwell Dec 19 '22 at 23:35
0

My goal is to set the path to MsBuild once as an environment variable, rather than in every script I might write. Sometimes I prefer to not be upgraded tp a new version of a tool, as a result of installing a new version of VS. A one-time script does that. Then I refer to msbuild as

"%MsBuildPath%" arguments here...

In the past I have created a one-line cmd file in my tools folder which is on my path that starts the exe.

"%C:\Program Files\Microsoft Visual Studio\2022\Professional\MSBuild\Current\Bin\MSBuild.exe%" %*

That works but the output may get written to another window, unless you use Start /B

You could also put a symbolic link there. Or you could use pathman to save the parent of the result to the PATH variable.

The advantage of the one-line file is that can be copied by backup or sync tools like dropbox. Symbolic and hard links do not travel across the cloud well.

Building on the better answers that use vswhere, I retrofitted it into a cmd file.

If you know Unix backticks and environment export this will make your eyes hurt, but it is the idiom (idiot?) way Windows forces on you, since PowerShell may be restricted, such as on a build machine:

Rem set MsBuildPath env variable
setx MsBuildPath ""
REM delims=* to avoid parsing spaces filepaths since * is not legal in a filename
FOR /F "usebackq delims=*" %%I IN (
    `"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -latest -prerelease -products * -requires Microsoft.Component.MSBuild -find MSBuild\**\Bin\MSBuild.exe`
        ) do setx MsBuildPath "%%I"

REM Note "SET MsBuildPath" returns nothing until you open a new cmd/process

Expected output is something like (ignore the SO wrapping):

setx MsBuildPath "C:\Program Files\Microsoft Visual Studio\2022\Professional\MSBuild\Current\Bin\MSBuild.exe"

SUCCESS: Specified value was saved.

I wish

dotnet msbuild

worked on 4.8 and older projects. Someday when I have switched my 150 personal C# projects to Core and SDK projects I will be able to use dotnet msbuild; in others maybe never.

Andrew Dennison
  • 1,069
  • 11
  • 9
-1

If you are adventurous you can also get the source code and latest release of MsBuild from GitHub now at https://github.com/Microsoft/msbuild/releases/

Dan Diplo
  • 25,076
  • 4
  • 67
  • 89
  • Does not answer either of the OP's questions – Alan Macdonald Jul 29 '20 at 11:49
  • Seems a bit heavy handed when you can create link to MsBuild.exe Or just copy the one of the existing copies to where ever you like, Or just add it to your PATH. (I already have 20 copies of MS spam on a machine I have been using less than 3 months. So this would not be my first choice) – Andrew Dennison Mar 02 '23 at 05:33
-1

Get latest version of MsBuild. Best way, for all types of msbuild installation, for different processor architecture (Power Shell):

function Get-MsBuild-Path
{
    $msbuildPathes = $null
    $ptrSize = [System.IntPtr]::Size
    switch ($ptrSize) {
        4 {
            $msbuildPathes =
            @(Resolve-Path "${Env:ProgramFiles(x86)}\Microsoft Visual Studio\*\*\MSBuild\*\Bin\msbuild.exe" -ErrorAction SilentlyContinue) +
            @(Resolve-Path "${Env:ProgramFiles(x86)}\MSBuild\*\Bin\MSBuild.exe" -ErrorAction SilentlyContinue) +
            @(Resolve-Path "${Env:windir}\Microsoft.NET\Framework\*\MSBuild.exe" -ErrorAction SilentlyContinue)
        }
        8 {
            $msbuildPathes =
            @(Resolve-Path "${Env:ProgramFiles(x86)}\Microsoft Visual Studio\*\*\MSBuild\*\Bin\amd64\msbuild.exe" -ErrorAction SilentlyContinue) +
            @(Resolve-Path "${Env:ProgramFiles(x86)}\MSBuild\*\Bin\amd64\MSBuild.exe" -ErrorAction SilentlyContinue) +
            @(Resolve-Path "${Env:windir}\Microsoft.NET\Framework64\*\MSBuild.exe" -ErrorAction SilentlyContinue)
        }
        default {
            throw ($msgs.error_unknown_pointersize -f $ptrSize)
        }
    }

    $latestMSBuildPath = $null
    $latestVersion = $null
    foreach ($msbuildFile in $msbuildPathes)
    {
        $msbuildPath = $msbuildFile.Path
        $versionOutput = & $msbuildPath -version
        $fileVersion = (New-Object System.Version($versionOutput[$versionOutput.Length - 1]))
        if (!$latestVersion -or $latestVersion -lt $fileVersion)
        {
            $latestVersion = $fileVersion
            $latestMSBuildPath = $msbuildPath
        }
    }

    Write-Host "MSBuild version detected: $latestVersion" -Foreground Yellow
    Write-Host "MSBuild path: $latestMSBuildPath" -Foreground Yellow

    return $latestMSBuildPath;
}
Stas BZ
  • 1,184
  • 1
  • 17
  • 36
-2

If You want to compile a Delphi project, look at "ERROR MSB4040 There is no target in the project" when using msbuild+Delphi2009

Correct answer there are said: "There is a batch file called rsvars.bat (search for it in the RAD Studio folder). Call that before calling MSBuild, and it will setup the necessary environment variables. Make sure the folders are correct in rsvars.bat if you have the compiler in a different location to the default."

This bat will not only update the PATH environment variable to proper .NET folder with proper MSBuild.exe version, but also registers other necessary variables.

Nashev
  • 490
  • 4
  • 10
  • That answer is not Delphi-related and not more robust for Delphi users. – Nashev Nov 22 '18 at 11:14
  • 3
    Sorry for being terse. I meant more robust as in, works for more than just Delphi. There might be an easier way to do it in Delphi, but the OP didn't ask about Delphi, and this thread has some 18 answers that few will ever see. If it's important to you that others see this, I'd recommend that you create a new question specific to Delphi, and self-answer. If we got down to 6 or fewer answers that covered every version of MSBuild, I'd be very happy – jpaugh Nov 26 '18 at 15:46