我有一个包含一些类库和一个控制台项目的解决方案。每个库在
Inputs
文件旁边都有一个 .csproj
目录,其中包含一堆文件。
我目前正在每个库项目中执行此操作,以便不同的文件最终被复制到控制台项目输出目录。它按预期工作:
<ItemGroup>
<None Update="Inputs\*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
现在我尝试通过执行以下操作将该功能提取到
Directory.Build.props
:
<ItemGroup Condition="$(MSBuildProjectName.StartsWith('AoC')) AND !$(MSBuildProjectName.EndsWith('Test'))" >
<None Update="$(MSBuildProjectDirectory)\Inputs\*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
最终没有文件被复制到输出目录(无论是库文件还是运行程序)。
有趣的是:
Inputs
中有一个 \bin\Debug\net8.0
目录,其中包含从项目中的原始 ItemGroup
创建的文件,那么如果我只保留 ItemGroup
文件中的“通用”Directory.Build.props
,这些文件就会被删除。Inputs
目录,则根本就没有创建。此外,
Directory.Build.props
中的此目标显示了Input
文件夹所在的预期项目路径:
<Target Name="PrintDirs" BeforeTargets="Build" Condition="$(MSBuildProjectName.StartsWith('AoC')) AND !$(MSBuildProjectName.EndsWith('Test'))">
<Message Importance="High" Text="||---> $(MSBuildProjectDirectory)" />
</Target>
<..>\AoCMultiYear> dotnet build
Versión de MSBuild 17.8.3+195e7f5a3 para .NET
Determinando los proyectos que se van a restaurar...
Todos los proyectos están actualizados para la restauración.
Common -> <..>\AoCMultiYear\src\Common\bin\Debug\net8.0\Common.dll
AoC2020 -> <..>\AoCMultiYear\src\AoC2020\bin\Debug\net8.0\AoC2020.dll
AoC2021 -> <..>\AoCMultiYear\src\AoC2021\bin\Debug\net8.0\AoC2021.dll
||---> <..>\AoCMultiYear\src\AoC2020
||---> <..>\AoCMultiYear\src\AoC2021
AoC2020.Test -> <..>\AoCMultiYear\tests\AoC2020.Test\bin\Debug\net8.0\AoC2020.Test.dll
AoC2022 -> <..>\AoCMultiYear\src\AoC2022\bin\Debug\net8.0\AoC2022.dll
||---> <..>\AoCMultiYear\src\AoC2022
Runner -> <..>\AoCMultiYear\src\Runner\bin\Debug\net8.0\Runner.dll
我在这里遗漏了
msbuild
拼图的哪一块? :)
MSBuild 项目文件有两种类型:遗留样式和SDK 样式。旧项目用于仅适用于旧版 Windows 的 .NET Framework。 SDK 项目是针对 .NET(又名 .NET Core)开发的,可以同时支持 .NET 和 .NET Framework。
MSBuild 项目文件是 XML。如果
Project
元素具有 SDK
属性,则它是 SDK 样式项目。
例如
<Project Sdk="Microsoft.NET.Sdk">
...
</Project>
SDK 风格的项目将大量内容从实际项目文件中移出,并实现了一些新功能。但随之而来的是一些行为改变。
遗留项目通常会明确指定属于项目一部分的文件。 SDK 项目有一组默认的文件包含。与问题相关,项目文件夹和子文件夹中不是
Compile
或资源项或其他一些特殊类型的文件都包含在 None
项集合中。
Directory.Build.props
文件和 Directory.Build.targets
文件之间的区别在于文件导入的时间。这两个文件都可以包含任何有效的 MSBuild 内容。 (.props
文件不限于属性,.targets
文件不限于目标。)
Directory.Build.props
文件很早就导入了,在项目文件本身的内容之前以及在定义许多标准属性之前。
Directory.Build.targets
文件是在项目文件内容之后导入的。
根据经验,将内容放入
Directory.Build.targets
文件中。当需要更改标准属性和项目的定义行为时,请使用 Directory.Build.props
文件。例如,有一组用于更改默认项目包含的属性。设置这些属性应在 Directory.Build.props
文件中完成。项目文件本身(和 Directory.Build.targets
文件)太晚了。
当从一组项目文件中分解通用功能(其中该功能当前位于项目文件中并且正常工作)时,请将通用代码移至
Directory.Build.targets
文件。本质上,这就像将代码移动到项目文件的末尾。由于 Directory.Build.props
文件的特殊早期导入,您不会破坏代码。
Inputs
文件夹的方法假设是一个 SDK 风格的项目,创建一个包含以下内容的
Directory.Build.targets
文件:
<Project>
<PropertyGroup>
<InputsDirectoryName Condition="'$(InputsDirectoryName)' == ''">Inputs</InputsDirectoryName>
<!-- EnableCopyInputs: default if not set; normalize to always be either 'true' or 'false' -->
<EnableCopyInputs Condition="'$(EnableCopyInputs)' == ''">true</EnableCopyInputs>
<EnableCopyInputs Condition="'$(EnableCopyInputs)' != 'true' and '$(EnableCopyInputs)' != 'false'">false</EnableCopyInputs>
</PropertyGroup>
<ItemGroup Condition="$(EnableCopyInputs) and Exists('$(InputsDirectoryName)')">
<None Update="$(InputsDirectoryName)\*" >
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<Target Name="DisplayCopyInputsSettings">
<Message Text="EnableCopyInputs: $(EnableCopyInputs)" />
<Message Text="InputsDirectoryName: $(InputsDirectoryName)" />
<PropertyGroup>
<InputsDirectoryNameExists>false</InputsDirectoryNameExists>
<InputsDirectoryNameExists Condition="Exists('$(InputsDirectoryName)')">false</InputsDirectoryNameExists>
</PropertyGroup>
<Message Text="Directory '$(InputsDirectoryName)' Exists: $(InputsDirectoryNameExists)" />
<Message Text="@(None->'%(Identity), %(CopyToOutputDirectory)', '%0d%0a')" />
</Target>
</Project>
注释
Directory.Build.targets
文件。通用逻辑在每个单独项目的上下文中运行。InputsDirectoryName
) 中,并且可以被覆盖。 (这可以在每个项目的基础上完成。)EnableCopyInputs
)。
false
和/或可以根据 Directory.Build.targets
文件中的项目名称进行设置。ItemGroup
条件要求启用复制功能并且目录存在。如果项目没有适当的现有目录,则不会进行任何更改。Upgrade
用于修改现有项目的元数据。它们是现有项目,因为 SDK 逻辑将自动包含它们。
Upgrade
无法在 Directory.Build.props
文件中使用,因为尚未执行 SDK 的自动包含。DisplayCopyInputsSettings
目标会报告相关属性和项目。它可以用于项目,例如dotnet msbuild -t:DisplayCopyInputsSettings myproject.csproj
。