我花了一整天的时间研究这个问题,终于得到了合理的答案。
所以我想用我蹩脚的英语在 stackoverflow 上发布我的第一个长答案。
TL'DR:
the MSBuild
's nuget plugin
's ResolveNuGetPackageAssets
目标做了邪恶的事情,创建一个自定义目标来恢复它,转到底部查看任务代码。
学习故事
首先,我对这个问题做了一个类似但更简单的副本来学习。
毕竟构建一个xamarin项目太慢了,
演示源位于github https://github.com/fatfatson/my.issue.nuget.conflict.git,
它有四个项目:
- ConflictLib:另一个库和主应用程序同时使用的库
- DirectLib:主应用程序使用的库,正在使用
ConflictLib
- MyAppDev:主应用程序,上面两个库为
ProjectReference
- MyAppConsumer:其他应用程序,使用
DirectLib
by PackageReference
, and ConflictLib
as ProjectReference
。为了测试这种情况,我将 ConflictLib 和 DirectLib 推送到nuget.org
,然后对本地版本的 ConflictLib 进行修改,这样我就可以验证正在使用哪个版本。
这些项目及其关系与我的原始问题非常相似,关键点是:当应用程序同时使用具有两个不同版本的库时,本地版本(ProjectReference 或 HintPath)会(应该)获胜吗?
对于我的原始案例,xamarin 项目,它是No
,所以我来研究一下。
对于测试用例,一个 dotnet core 控制台项目,它是Yes
,所以构建过程中一定有什么神秘的东西:MSBuild
,这是一个庞大的系统,但现在我要深入研究它。
然后,我需要一个检查工具来找出什么MSBuild
构建项目时会执行此操作。
这个简单的工具只需在命令行中调用它,它就会显示所有targets
被执行。 Amsbuild target
是这样的a target in makefile
,以及tasks
目标类似于commands in makefile
,这个概念在许多其他系统(例如 gradle)中存在,但术语略有不同,因此很容易理解。
但是,有这么多的目标和任务,而且它们都依赖于其他人并且是互动的Property
and Items
,很难从文本日志中了解哪个目标破坏了我的需求。
幸运的是,有一个先进的工具可以检查所有内容MSBuild
: 这叫MSBuild structured log viewer http://msbuildlog.com/,我从中学习here https://github.com/Microsoft/msbuild/blob/master/documentation/wiki/Home.md.
现在构建项目/bl
选项,它将生成一个包含完整信息的二进制日志文件,由上面提到的查看器打开它:(我的原始 xamarin 项目的构建日志)
显然,ResolveNuGetPackageAssets
目标改变了Reference
items,决定了最终的链接库组装。
but why it doesn't make the wrong decision in the test case? let's view its log:
有什么区别吗? ——没有ResolveNuGetPackageAssets
target!
是一样的ResolveReferences
to ResolveAssemblyReferences
,但在 nuget 部分有所不同。
双击时ResolveAssemblyReferences
,观众将打开targets file
其中定义了目标。
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\Microsoft.Common.CurrentVersion.targets
两种情况仍然相同:
ResolveAssemblyReferences
不依赖于ResolveNuGetPackageAssets
,那么后者从哪里来呢?只需单击它,文件就会打开:
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Microsoft\NuGet\16.0\Microsoft.NuGet.targets
它覆盖ResolveAssemblyReferencesDependsOn
并添加ResolveNuGetPackageAssets
依赖于ResolveAssemblyReferences
.
最后一个问题:为什么会出现上述情况NuGet.targets
文件没有出现在测试用例中?仍然可以由观众来回答Evaluation
部分:
显然,该文件未导入,因为属性SkipImportNuGetBuildTargets
设置为 true。经过简单的搜索后,我确认它是测试用例中的默认值:它设置在Microsoft.NET.Sdk.targets https://github.com/dotnet/sdk/blob/055027b26dc18cf4d192140c5fc8047c3d2dea57/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets#L17。但在 xamarin 的情况下,它没有设置并且意味着false
,所以所有的事情都发生了。
最后,我必须想出一些措施来解决这个问题。
首先,我不会添加SkipImportNuGetBuildTargets
属性给xamarin项目,因为我认为它是一个框架设计,可能对其他人有很大的影响,我只是想修复一点具体的问题。
我决定在之后立即添加自定义目标ResolveAssemblyReferences
, 去除Nuget's Xamarin.Forms
并添加我自己的——只需恢复什么ResolveNuGetPackageAssets
does.
任务代码很简单(我刚写完,实际上花了我很多时间来搜索语法/内置函数/等等并测试):
注意如何Remove Item
(see 微软构建文档 https://learn.microsoft.com/en-us/visualstudio/msbuild/item-element-msbuild?view=vs-2019)有效(而不有效:注释行),我仍然不完全理解它,但它确实有效!