如何使用 Visual Studio 2015 (MSBuild 14.0) 在中心位置实施解决方案范围的构建配置

How to implement a solution-wide build configuration in a central location with Visual Studio 2015 (MSBuild 14.0)

在 Visual Studio 2017 和 MSBuild 15.0 中引入了一项新功能,通过放置 Directory.Build.props and/or Directory.Build.targets 解决方案根文件夹中的文件(*sln 文件所在的文件夹)。

不幸的是,这个很棒的功能不适用于旧的 VS 和 MSBuild 版本。有没有办法让全局构建配置在 VS 2015 和 MSBuild 14.0 中以类似于 VS 2017 和 MSBuild 15.0 的方式工作?

在研究了 MSBuild 的工作原理后,我发现 Microsoft.Common.targets 文件位于 MSBuildToolsPath 目录(例如 C:\Program Files (x86)\MSBuild.0\Bin)在每次构建期间由 MSBuild 使用。

当我比较 MSBuild 14.0 和 MSBuild 15.0 的 Microsoft.Common.targets 文件时,发现它们几乎完全相同。主要区别在于,15.0 文件包含一些发现 Directory.Build.targets 文件路径的逻辑,如果存在这样的文件,则此文件中的构建目标也包含在构建中。

Differences of MSBuild 14.0 and 15.0 Microsoft.Common.targets files

* 重要提示 *
以下解决方案更改了部分 MS Visual Studio 安装,可能会导致无法预料的严重后果。一般来说,它很危险,完全不受支持,并且可能会破坏各种东西。 请仅在您完全确定自己在做什么时才继续。

我的解决方案只是将包含 Directory.Build.targets 文件的部分从 MSBuild 15.0 Microsoft.Common.targets 文件粘贴到 MSBuild 14.0 Microsoft.Common.targets 文件中。但是用 15.0 文件替换 14.0 文件也应该是安全的。 Microsoft.Common.targets文件的当前版本可以在here

中找到

为了自动化这个过程并在其他开发机器上重复它,我制作了这个 Windows 批处理文件,它发现 MSBuildTools 路径并用更新的文件替换 Microsoft.Common.targets 文件。我在此批处理脚本中使用的更新文件名为 MSBuil15.Microsoft.Common.targets 并且必须与批处理文件位于同一文件夹中。这个批处理文件是基于MSBuildPath.bat and GotAdmin.bat,我在其中添加了两行代码来替换文件。

@echo off

reg.exe query "HKLM\SOFTWARE\Microsoft\MSBuild\ToolsVersions.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.0" /v MSBuildToolsPath') do SET "MSBUILDDIR=%%B"

echo.
echo MSBuildToolsPath: %MSBUILDDIR%
echo.

IF NOT EXIST "%MSBUILDDIR%" goto MissingMSBuildToolsPath
IF NOT EXIST "%MSBUILDDIR%msbuild.exe" goto MissingMSBuildExe
IF EXIST "%MSBUILDDIR%Microsoft.Common.targets.bak" goto AlreadyPatched


::-------------------------------------
REM  --> Check for permissions
>nul 2>&1 "%SYSTEMROOT%\system32\cacls.exe" "%SYSTEMROOT%\system32\config\system"

REM --> If error flag set, we do not have admin.
if '%errorlevel%' NEQ '0' (
    echo Requesting administrative privileges...
    goto UACPrompt
) else ( goto gotAdmin )

:UACPrompt
    echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\getadmin.vbs"
    echo UAC.ShellExecute "%~s0", "", "", "runas", 1 >> "%temp%\getadmin.vbs"

    "%temp%\getadmin.vbs"
    exit /B

:gotAdmin
    if exist "%temp%\getadmin.vbs" ( del "%temp%\getadmin.vbs" )
    pushd "%CD%"
    CD /D "%~dp0"
::-------------------------------------- 


:: Replace MSBuild 14.0 Microsoft.Common.targets file with MSBuild 15.0 file.
rename "%MSBUILDDIR%Microsoft.Common.targets" "Microsoft.Common.targets.bak"
copy "MSBuil15.Microsoft.Common.targets" "%MSBUILDDIR%Microsoft.Common.targets"


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
:AlreadyPatched
echo The Microsoft.Common.targets file is already patched
goto:eof