NET 5 核心项目上的 MSBuild 生成不同的 bin/x64 和 bin/Debug 文件夹结构

MSBuild on NET 5 Core projects produces different bin/x64 and bin/Debug folder structures

我很难理解 MSBuild 如何与 Windows 上的 NET Core 项目文件 (csproj) 一起工作。我有 71 个 NET Core C# 项目文件(可执行文件、库、测试项目)。它们都在 Visual Studio 下或使用在命令行上构建并发布到发布文件夹的 msbuild 批处理作业正确编译和 运行。

在本地构建会生成不同的 BIN/* 文件夹

但是当我 运行 MSBuild(使用相同的命令行参数)在 71 个项目上进行本地构建(不在本地发布)时,一些项目会产生不同的输出文件夹。

其中一些创建 bin/Debug/* 文件夹,一些创建 bin/x64/Debug/ 文件夹,一些项目同时生成这两种类型的文件夹。 “*”部分是framework/runtime,在我的系统上是net5.0-windows/win-x64。

这是我的典型 NET 5.0 项目文件的样子:

<PropertyGroup>
      <Platforms>AnyCPU</Platforms>
      <PlatformTarget>AnyCPU</PlatformTarget>
      <TargetFramework>net5.0-windows7.0</TargetFramework>
      
      <SelfContained>false</SelfContained>
      <IsPackable>false</IsPackable>
      <IsPublishable>false</IsPublishable>
      // The IsPublishable setting makes no difference.
      // The build operation is not a publishable operation.
</PropertyGroup>

这是我的 MSBuild 命令对于所有 71 个项目的样子

MSBuild /t:Restore;Build /p:Configuration=Debug /p:Platform=x64 /p:RuntimeIdentifier=win-x64 csproj-file-pathname

我做了很多实验,包括在 运行 启用 MSBuild 命令行之前完全删除 obj 和 bin 树,但没有任何效果。我看不到项目文件、项目类型(lib 或 exe 或应用程序)和 MSBuild 命令行参数之间的任何可辨别的关系,这些参数可以解释不同的本地生成输出文件夹。

如果项目文件相同且 MSBuild 参数相同,有谁知道什么会使某些项目创建不同的输出文件夹结构(和内容)?

有谁确切确切地知道上面显示的 msbuild 参数应该生成哪些 bin/* 输出文件夹?我花了几个小时来解决这个问题,但没有成功。谢谢。

MSBuild Structured Log Viewer 将成为您的朋友。每当我需要 确切地 了解 MSBuild 正在做什么时,我都会打开这个工具。

我做了几个图书馆项目来向你展示发生了什么

这是 x64 的解决方案配置

我使用您正在使用的相同属性构建解决方案。

我非常熟悉 MSBuild,而且我知道 属性 要查找最终副本的内容。所以我搜索 $property OutDir。这意味着我正在寻找名称包含 OutDir.

的 MSBuild 属性

如您所见,我们遇到的情况与您描述的情况几乎相同。有些有 bin\x64\Debug 而有些只有 bin\Debug。它们都附加了目标框架,但也附加了运行时标识符。

如果我们想要完全按照 MSBuild 的方式查看项目。我们可以展开评估文件夹并从上下文菜单中单击“预处理”。这将是一个怪物xml。在从 .NET SDK 和您的存储库导入所有 .props 和 .targets 之后,这实际上是您的 csproj 的完全扩展荣耀。

您也可以使用 MSBuild -preprocess[:file] 选项从 cli 获取此文件。

有很多事情正在进行,但实际上我们只是在寻找指导输出的东西。我知道 OutDir 中使用了 OutputPath,所以我搜索 OutputPath 并跳转。我发现控制默认输出的特殊 .targets 文件称为 Microsoft.NET.DefaultOutputPaths.targets。这是相关部分:

    <Configuration Condition="'$(Configuration)'==''">Debug</Configuration>
    <Platform Condition="'$(Platform)'==''">AnyCPU</Platform>
    <PlatformName Condition="'$(PlatformName)' == ''">$(Platform)</PlatformName>

    <BaseOutputPath Condition="'$(BaseOutputPath)' == ''">bin\</BaseOutputPath>
    <BaseOutputPath Condition="!HasTrailingSlash('$(BaseOutputPath)')">$(BaseOutputPath)\</BaseOutputPath>
    <OutputPath Condition="'$(OutputPath)' == '' and '$(PlatformName)' == 'AnyCPU'">$(BaseOutputPath)$(Configuration)\</OutputPath>
    <OutputPath Condition="'$(OutputPath)' == '' and '$(PlatformName)' != 'AnyCPU'">$(BaseOutputPath)$(PlatformName)$(Configuration)\</OutputPath>
    <OutputPath Condition="!HasTrailingSlash('$(OutputPath)')">$(OutputPath)\</OutputPath>

你的第一部分终于可以回答了。对于在 AnyCPU 解决方案下构建的项目(即使您指定 x64),您不会在输出中获得 $(Platform)。你可以在我的例子中看到:只有 net6libOutDir.

中有 x64

那么运行时标识符呢,我在这个 .targets 文件中没有看到它。它实际上稍后在另一个文件中进行了扩展:Microsoft.NET.RuntimeIdentifierInference.targets.

相关部分加评论

  <!--
    Append $(RuntimeIdentifier) directory to output and intermediate paths to prevent bin clashes between
    targets.

    But do not append the implicit default runtime identifier for .NET Framework apps as that would
    append a RID the user never mentioned in the path and do so even in the AnyCPU case.
   -->
  <PropertyGroup Condition="'$(AppendRuntimeIdentifierToOutputPath)' == 'true' and '$(RuntimeIdentifier)' != '' and '$(_UsingDefaultRuntimeIdentifier)' != 'true'">
    <IntermediateOutputPath>$(IntermediateOutputPath)$(RuntimeIdentifier)\</IntermediateOutputPath>
    <OutputPath>$(OutputPath)$(RuntimeIdentifier)\</OutputPath>
  </PropertyGroup>

这是输出部分的最后一部分。 $(TargetFramework) 也被插入到我提到的前一个目标和这个目标之间,但你已经知道了。

这里一点也不疯狂。 MSBuild 完全按照指令执行。

希望这对一些人有所帮助!