MSBuild:CopyToOutputDirectory 不会将本机 DLL 复制到输出

问题描述 投票:0回答:1

我有一个包含 2 个项目的解决方案:

MSBuildQuestion.csproj
NativeLibrary.vcxproj

MSBuildQuestion.csproj
产生
MSBuildQuestion.exe
NativeLibrary.vcxproj
产生
NativeLibrary.dll

MSBuildQuestion.csproj
通过
NativeLibrary.vcxproj
引用
ProjectReference
,并通过
NativeLibrary.dll
CopyToOutputDirectory
复制到其输出:

<ItemGroup>
    <ProjectReference Include="..\NativeLibrary\NativeLibrary.vcxproj" />
    <Content Include="..\$(Platform)\$(Configuration)\*.dll">
        <Link>NativeLibrary.dll</Link>
        <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Content>
</ItemGroup>

当我清理(通过删除所有输出文件夹)然后通过 MSBuild 从命令行构建解决方案时,它不会将

NativeLibrary.dll
复制到
MSBuildQuestion.csproj
的输出,尽管
CopyToOutputDirectory
明确告诉它这样做。当我尝试运行时,这当然会导致“无法加载 DLL 'NativeLibrary.dll'”错误
MSBuildQuestion.exe

我是否遗漏了一些东西,而这不是应该从

*.csproj
引用本机DLL的方式?或者这是 MSBuild 中的一个错误?

这就是我清理、构建和运行解决方案的方式:

Get-ChildItem . -include bin,obj,x64 -Recurse | ForEach-Object ($_) { Remove-Item $_.FullName -Force -Recurse }
nuget restore
msbuild /p:Platform=x64 /p:Configuration=Release
.\MSBuildQuestion\bin\x64\Release\net6.0\MSBuildQuestion.exe

重现问题的项目是here


一个观察:如果我运行

msbuild /p:Platform=x64 /p:Configuration=Release
两次,
NativeLibrary.dll
会被复制,然后一切正常。就好像 MSBuild 违反了项目必须构建的顺序一样。虽然如果我检查 MSBuild 日志,它似乎并没有违反顺序,因为下一行是按预期顺序记录的:

Done Building Project "...\NativeLibrary.vcxproj"
Done Building Project "...\MSBuildQuestion.csproj"
Done Building Project "...\MSBuildQuestion.sln"
.net msbuild
1个回答
0
投票

NativeLibrary.dll
不是依赖项

托管代码是写入 .NET CLR(.NET 或 .NET Framework 实现)的代码。托管代码项目可以具有对其他托管代码程序集的编译依赖性。但本机代码程序集不能是编译时依赖项。托管代码调用本机代码的互操作性是在运行时实现的。

C# 项目可以有一组

ProjectReference
项。
ProjectReference
被解析为编译依赖项的程序集。本机程序集不能是编译依赖项。
NativeLibrary.dll
不是您的 C# 项目的依赖项,不是直接的,也不是传递性的。

NativeLibrary.dll
是代码中
DllImport
属性的依赖项,但那是在运行时。并且构建对运行时依赖一无所知。

建立订单

项目的构建顺序由跨项目的

ProjectReference
项以及解决方案文件中设置的
Project Dependencies
确定。

在解决方案中设置项目依赖项只会影响构建顺序。它不提供参考或“依赖”。

MSBuild 阶段

MSBuild 在处理项目时有两个阶段:评估和执行。在评估阶段,“顶级”项目组得到解决。

MSBuildQuestion.sln 示例

MSBuildQuestion.csproj
包含:

<Project Sdk="Microsoft.NET.Sdk">

    ...

    <ItemGroup>
        <ProjectReference Include="..\NativeLibrary\NativeLibrary.vcxproj" />
        <Content Include="..\$(Platform)\$(Configuration)\*.dll">
            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
        </Content>
    </ItemGroup>

</Project>

在评估阶段,MSBuild 将查看并处理

ProjectReference
Content

ProjectReference
NativeLibrary.vcxproj
影响构建顺序。 MSBuild 将知道在
NativeLibrary.vcxproj
的执行阶段之前构建
MSBuildQuestion.csproj
。因为它是一个本机项目,所以没有可供编译的托管程序集。

Content
将被评估,除非有先前版本中的文件,否则不会匹配任何内容。

在执行阶段,有一个步骤会复制添加到

Content
的文件。除非先前版本留下的文件在评估阶段满足通配符,否则
NativeLibrary.dll
将不会出现在
Content
中。

(顺便说一句,如果您想要增量构建,请使用

PreserveNewest
而不是
Always
。)

要使此示例正常工作:

  • <ProjectReference Include="..\NativeLibrary\NativeLibrary.vcxproj" />
    中删除
    MSBuildQuestion.csproj
  • 修改解决方案文件,使
    MSBuildQuestion.csproj
    具有对
    NativeLibrary.vcxproj
    的项目依赖。

该解决方案将在

NativeLibrary.vcxproj
之前构建
MSBuildQuestion.csproj
,确保
MSBuildQuestion.csproj
的评估阶段将看到
NativeLibrary.vcxproj
的当前构建文件。
NativeLibrary.dll
将成为
Content
的一部分,并将被复制为
Content
的一部分。

MSBuildQuestion2.sln 示例

在此示例中:

  • MSBuildQuestion2.csproj
    构建依赖于
    NativeLibraryWrapper.dll
    的 .exe 程序集。
  • NativeLibraryWrapper.csproj
    构建一个 .dll 程序集,其
    DllImport
    NativeLibrary.dll
  • NativeLibrary.vcxproj
    构建本机 DLL
    NativeLibrary.dll

当项目引用解析为对托管程序集的引用时,将复制传递托管依赖项。将复制与正在复制的程序集名称匹配的文件(例如 .pdb 文件)。

NativeLibrary.dll
这些都不是。

这可以像前面的例子一样解决:

  • 修改解决方案文件以设置
    MSBuildQuestion2.csproj
    依赖于
    NativeLibrary.vcxproj
    的项目依赖项。
  • NativeLibrary.dll
    添加为
    Content

是的,这并不干燥,而且很容易忘记。

另一种方法是让

NativeLibraryWrapper.csproj
发布文件夹或包。用托管代码编写的消费者将使用已发布的文件夹或包,而不是直接引用项目。

© www.soinside.com 2019 - 2024. All rights reserved.