如何使 MSBuild 在 .NET 解决方案出现错误时不停止

How to make MSBuild to NOT stop on error on .NET solution

我正在根据团队的需要定制构建服务器设置。 它应该会自动构建我们的 .net 解决方案。 我尽力不更改 .sln 或项目文件中的任何内容。

这项工作的明显构建工具是 msbuild。我已经成功地从 .sln 文件编译了我们的解决方案。

下一步是测试某些解决方案的项目被破坏时的场景。在下面的情况下,我在 devenvmsbuild 之间遇到了奇怪的行为差异。有 3 个项目,其中 2nd 依赖于 1st,而 3rd 是独立且损坏的。 devenv 编译第 1 次和第 2 次,第 3 次失败,如预期的那样; msbuild 第一次编译,第三次失败,而且 - 出乎意料 - 没有尝试第二次。

示例解决方案(4 个文件:Complete.sln、1.csproj、2.csproj、3.csproj)

--- 1.csproj
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <FileUpgradeFlags>
    </FileUpgradeFlags>
    <UpgradeBackupLocation>
    </UpgradeBackupLocation>
    <OldToolsVersion>2.0</OldToolsVersion>
  </PropertyGroup>
  <Target Name="Build">
    <Message Importance="High" Text="Project 1" />
  </Target>
</Project>
--- 2.csproj
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <FileUpgradeFlags>
    </FileUpgradeFlags>
    <UpgradeBackupLocation>
    </UpgradeBackupLocation>
    <OldToolsVersion>2.0</OldToolsVersion>
  </PropertyGroup>
  <Target Name="Build">
    <Message Importance="High" Text="Project 2" />
  </Target>
</Project>
--- 3.csproj
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <FileUpgradeFlags>
    </FileUpgradeFlags>
    <UpgradeBackupLocation>
    </UpgradeBackupLocation>
    <OldToolsVersion>2.0</OldToolsVersion>
  </PropertyGroup>
  <Target Name="Build">
    <Message Importance="High" Text="Project 3" />
    <Error Text="Build failed" />
  </Target>
</Project>
--- Complete.sln

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29709.97
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "1", "1.csproj", "{AA88BE9A-5006-4CCE-8871-A67F413DBADF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "2", "2.csproj", "{BAF51480-4B90-454A-96F5-E63A05A486EF}"
    ProjectSection(ProjectDependencies) = postProject
        {AA88BE9A-5006-4CCE-8871-A67F413DBADF} = {AA88BE9A-5006-4CCE-8871-A67F413DBADF}
    EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "3", "3.csproj", "{12C30CFB-7387-4CD2-81D4-87E14209C408}"
EndProject
Global
    GlobalSection(SolutionConfigurationPlatforms) = preSolution
        Debug|x86 = Debug|x86
    EndGlobalSection
    GlobalSection(ProjectConfigurationPlatforms) = postSolution
        {AA88BE9A-5006-4CCE-8871-A67F413DBADF}.Debug|x86.ActiveCfg = Debug|x86
        {AA88BE9A-5006-4CCE-8871-A67F413DBADF}.Debug|x86.Build.0 = Debug|x86
        {BAF51480-4B90-454A-96F5-E63A05A486EF}.Debug|x86.ActiveCfg = Debug|x86
        {BAF51480-4B90-454A-96F5-E63A05A486EF}.Debug|x86.Build.0 = Debug|x86
        {12C30CFB-7387-4CD2-81D4-87E14209C408}.Debug|x86.ActiveCfg = Debug|x86
        {12C30CFB-7387-4CD2-81D4-87E14209C408}.Debug|x86.Build.0 = Debug|x86
    EndGlobalSection
    GlobalSection(SolutionProperties) = preSolution
        HideSolutionNode = FALSE
    EndGlobalSection
    GlobalSection(ExtensibilityGlobals) = postSolution
        SolutionGuid = {75E95C15-489D-4D3F-82CA-735FB7B88910}
    EndGlobalSection
EndGlobal

"Developer Command Prompt for VS 2019" 的控制台输出如下所示:

e>devenv /Build Debug Complete.sln

Microsoft Visual Studio 2019 Version 16.4.3.
Copyright (C) Microsoft Corp. All rights reserved.
1>------ Build started: Project: 1, Configuration: Debug x86 ------
2>------ Build started: Project: 3, Configuration: Debug x86 ------
1>  Project 1
2>  Project 3
2>C:\temp\msbuild\TestSolution\Complete.csproj(11,2): error : Build failed
3>------ Build started: Project: 2, Configuration: Debug x86 ------
3>  Project 2
========== Build: 2 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
>msbuild Complete.sln /p:Configuration=Debug /p:Platform=x86 /m
Microsoft (R) Build Engine version 16.4.0+e901037fe for .NET Framework
Copyright (C) Microsoft Corporation. All rights reserved.

Build started 23.01.2020 11:10:38.
     1>Project "c:\temp\msbuild\TestSolution\Complete\Complete.sln" on node 1 (default targets).
     1>ValidateSolutionConfiguration:
         Building solution configuration "Debug|x86".
     1>Project "c:\temp\msbuild\TestSolution\Complete\Complete.sln" (1) is building "c:\temp\msbuild\TestSolution\Complete.cs
       proj" (2) on node 1 (default targets).
     2>Build:
         Project 1
     2>Done Building Project "c:\temp\msbuild\TestSolution\Complete.csproj" (default targets).
     1>Project "c:\temp\msbuild\TestSolution\Complete\Complete.sln" (1) is building "c:\temp\msbuild\TestSolution\Complete.cs
       proj" (3) on node 2 (default targets).
     3>Build:
         Project 3
     3>c:\temp\msbuild\TestSolution\Complete.csproj(11,5): error : Build failed
     3>Done Building Project "c:\temp\msbuild\TestSolution\Complete.csproj" (default targets) -- FAILED.
     1>Done Building Project "c:\temp\msbuild\TestSolution\Complete\Complete.sln" (default targets) -- FAILED.

Build FAILED.

       "c:\temp\msbuild\TestSolution\Complete\Complete.sln" (default target) (1) ->
       "c:\temp\msbuild\TestSolution\Complete.csproj" (default target) (3) ->
       (Build target) ->
         c:\temp\msbuild\TestSolution\Complete.csproj(11,5): error : Build failed

    0 Warning(s)
    1 Error(s)

Time Elapsed 00:00:00.65

请注意 msbuild 输出中缺少 Project 2 消息。

是否有(非侵入式)方式让msbuild继续编译其他项目?

到目前为止我想出的最好的办法是在 Complete.sln(参见 msdn)向上的某个文件夹中创建一个 Directory.Solution.targets,内容如下。

<Project>
  <Target Name="SetSkip">
    <ItemGroup>
      <ProjectReference Update="*">
        <SkipNonexistentProjects>Build</SkipNonexistentProjects>
      </ProjectReference>
    </ItemGroup>
  </Target>
  <Target Name="BuildAll" DependsOnTargets="SetSkip">
    <CallTarget Targets="Build"/>
  </Target>
</Project>

注意 SkipNonexistentProjects 设置为 Build,而不是 True。当设置为 True 时,第二个项目被 Skipping project "2.csproj.metaproj" because it was not found..

跳过

然后用 /t:BuildAll 而不是 /t:Build 调用 msbuild。但是我需要为 每个可能的解决方案和 每个可能的目标执行此操作,我真的很想要一些更好的方法,也许是我还找不到的命令行开关(/p:ContinueOnError=true;StopOnFirstFailure=false;SkipNonexistentProjects=true 没有帮助)。

更新 改进了我的解决方法:现在只需要根文件夹中的一个附加文件。

How to make MSBuild to NOT stop on error on .NET solution

答案是 因为它看起来像是一个关于 msbuild 的问题。所以我们只能有解决方法而不是解决方案...

我可以在我的机器上用 VS2019 和 msbuild 16.0 重现同样的问题。由于使用 devenv 一切正常,我认为您可以 post 一期 here 到 Msbuild 团队。 (我尝试了几个 msbuild 属性,但它们不起作用,所以我认为这个问题)

这里有一些临时解决方法:

1.If 你在这个解决方案中没有太多项目,你可以使用 target 开关以特定顺序构建特定项目,检查 this document.

对我来说,我使用像 msbuild Library.sln -target:LibraryA;LibraryB;LibraryC /p:Configuration=Debug /p:Platform="Any CPU" -m 这样的命令,然后 msbuild 将构建 LibraryB

2.If 您在同一个解决方案中有很多项目,您可以在解决方案文件夹中创建一个自定义构建脚本 build.proj 文件,内容如下:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Target Name="CustomBuild">
<MSBuild Projects="LibraryA\LibraryA.vbproj" Properties="Configuration=$(Configuration);Platform=$(Platform)"/>
<MSBuild Projects="LibraryB\LibraryB.vbproj" Properties="Configuration=$(Configuration);Platform=$(Platform)"/>
<MSBuild Projects="LibraryC\LibraryC.vbproj" Properties="Configuration=$(Configuration);Platform=$(Platform)"/>

然后使用像 msbuild build.proj /p:Configuration=xx;Platform=xx 这样的命令来构建这些项目。

您可以暂时尝试这些解决方法,或者您可以使用 devenv 命令进行构建。但是对于问题本身,如果我没有误解的话,解决方案应该是 msbuild 团队的一个修复......希望以上所有内容都有帮助:)

这个答案在问题本身中引用了我的解决方法,以提高其可见性(如 对另一个答案的建议)。

到目前为止,我最好的办法是在 Complete.sln(参见 msdn)向上的某个文件夹中创建一个 Directory.Solution.targets,内容如下。

<Project>
  <Target Name="SetSkip">
    <ItemGroup>
      <ProjectReference Update="*">
        <SkipNonexistentProjects>Build</SkipNonexistentProjects>
      </ProjectReference>
    </ItemGroup>
  </Target>
  <Target Name="BuildAll" DependsOnTargets="SetSkip">
    <CallTarget Targets="Build"/>
  </Target>
  <Target Name="RebuildAll" DependsOnTargets="SetSkip">
    <CallTarget Targets="Rebuild"/>
  </Target>
  <Target Name="CleanAll" DependsOnTargets="SetSkip">
    <CallTarget Targets="Clean"/>
  </Target>
</Project>

注意 SkipNonexistentProjects 设置为 Build,而不是 True。当设置为 True 时,第二个项目被 Skipping project "2.csproj.metaproj" because it was not found. 跳过。

然后使用 /t:BuildAll 而不是 /t:Build 调用 msbuild 会按预期构建它所能构建的一切。

这对我来说 足够好 解决方案,尽管我仍然更喜欢不定义替代 Build/Rebuild 目标的方法。