如何在 MSBuild 中排除(禁用)PackageReference 的(传递)依赖性?
how to exclude(disable) PackageReference's (transitive)dependency in MSBuild?
我正在使用一个包 Xamanimation
,它具有 Xamarin.Forms 4.1.0
的依赖项(写在它的 nuspec
文件中):
<dependencies>
<group targetFramework=".NETStandard2.0">
<dependency id="Xamarin.Forms" version="4.1.0.581479" exclude="Build,Analyzers" />
</group>
</dependencies>
但我已经为自己构建了 Xamarin.Froms 并将输出 dll 文件添加到我的项目参考中:
<Reference Include="Xamarin.Forms.Xaml">
<HintPath>..\thirdparty\xforms\Xamarin.Forms.Xaml.dll</HintPath>
</Reference>
根据 nuget's doc,我将 ExcludeAssets
属性(和其他测试)添加到 PackageReference of Xamanimation
的部分:
<PackageReference Include="Xamanimation">
<IncludeAssets>compile</IncludeAssets>
<!-- <ExcludeAssets>compile</ExcludeAssets> -->
<!-- <PrivateAssets>all</PrivateAssets> -->
<!-- <ExcludeAssets>buildtransitive</ExcludeAssets> -->
<Version>1.3.0</Version>
</PackageReference>
但其中 none 有效!
MSBuild
将始终使用传递依赖项 Xamarin.Forms.4.1.0 并忽略我自己构建的(我在其中添加了新的 类 并在主项目中使用它们所以链接失败表明选择的是旧的)。
那么排除传递依赖的正确方法是什么?
我花了一整天的时间研究这个问题,终于得到了一个合理的答案。
所以我想 post 我在 Whosebug 中的第一个长答案是我糟糕的英语。
TL'DR:
MSBuild
的nuget plugin
的ResolveNuGetPackageAssets
目标做坏事,制作一个自定义目标来恢复它,到任务代码的底部。
学习故事
首先,我把这个问题做了一个类似但更简单的副本来学习。
毕竟建一个xamarin项目太慢了,
演示源在github,
它有四个项目:
- ConflictLib:另一个库和主应用程序都使用的库
- DirectLib:主应用程序使用的库,正在使用
ConflictLib
- MyAppDev: 主程序,以上两个库为
ProjectReference
- MyAppConsumer:另一个应用程序,
PackageReference
使用 DirectLib
,ProjectReference
使用 ConflictLib
。为了测试这种情况,我将 ConflictLib 和 DirectLib 推送到 nuget.org
,然后修改了本地版本的 ConflictLib,这样我就可以验证正在使用哪个。
这些项目及其关系与我的原始问题非常相似,关键点是:当应用程序同时使用具有两个不同版本的库时,本地版本(ProjectReference 或 HintPath)是否会(应该)获胜?
我的原生案例,xamarin项目,No
,所以来研究一下
测试用例,一个dotnet core console项目,是Yes
,所以在构建过程中一定有玄机:MSBuild
,这是一个庞大的系统,但是现在我'我要深入研究它。
然后,我需要一个检查工具来找出 MSBuild
在构建项目时做了什么。
这个简单的工具只是在命令行中调用它,它会显示所有执行的targets
。 msbuild target
类似于 a target in makefile
,目标中的 tasks
类似于 commands in makefile
,这个概念在许多其他系统中存在,但术语略有不同,例如 gradle,所以很容易理解。
但是,有这么多的目标和任务,而且它们都依赖于其他人并且通过 Property
和 Items
进行交互,很难从文本日志中了解哪个目标打破了我的需要。
幸运的是,有一个高级工具可以检查 MSBuild
中的所有内容:它叫做 MSBuild structured log viewer
, I learn it from here。
现在使用 /bl
选项构建项目,它将生成一个包含完整信息的二进制日志文件,用上面提到的查看器打开它:(我的原始 xamarin 项目的构建日志)
很明显,ResolveNuGetPackageAssets
目标更改了 Reference
项,这决定了最终的链接库程序集。
但是为什么它在测试用例中没有做出错误的决定呢?让我们查看它的日志:
有区别吗? -- 没有 ResolveNuGetPackageAssets
目标!
从 ResolveReferences
到 ResolveAssemblyReferences
是一样的,但是在 nuget 部分不同。
双击 ResolveAssemblyReferences
,查看器将打开 targets file
,其中定义了目标。
C:\Program Files (x86)\Microsoft Visual Studio19\Community\MSBuild\Current\Bin\Microsoft.Common.CurrentVersion.targets
两种情况仍然相同:
ResolveAssemblyReferences
不依赖于ResolveNuGetPackageAssets
,那么后者从何而来?只需单击它,文件就会打开:
C:\Program Files (x86)\Microsoft Visual Studio19\Community\MSBuild\Microsoft\NuGet.0\Microsoft.NuGet.targets
它覆盖 ResolveAssemblyReferencesDependsOn
并将 ResolveNuGetPackageAssets
添加到 ResolveAssemblyReferences
的依赖项。
最后一个问题:为什么上面的NuGet.targets
文件没有出现在测试用例中?它仍然可以由观众的 Evaluation
部分回答:
很明显,此文件未导入,因为 属性 SkipImportNuGetBuildTargets
设置为 true。经过简单的搜索,我确认这是测试用例中的默认值:它设置在Microsoft.NET.Sdk.targets中。但在 xamarin 的情况下,它没有设置并且意味着 false
,所以所有的事情都发生了。
最后,我得想办法解决这个问题了。
首先,我不会把SkipImportNuGetBuildTargets
属性加入到xamarin项目中,因为我觉得这是一个框架设计,可能对其他人影响很大,我只是想修复一个具体的小问题。
我决定在 ResolveAssemblyReferences
之后立即添加一个自定义目标,删除 Nuget's Xamarin.Forms
并添加我自己的 - 只需恢复 ResolveNuGetPackageAssets
所做的。
任务代码很简单(刚写完,其实花了我很多时间搜索grammar/builtinfunction/etc测试):
注意 Remove Item
(参见 msbuild doc)是如何工作的(而不是工作:注释行),我仍然不完全理解它,但它确实工作了!
我正在使用一个包 Xamanimation
,它具有 Xamarin.Forms 4.1.0
的依赖项(写在它的 nuspec
文件中):
<dependencies>
<group targetFramework=".NETStandard2.0">
<dependency id="Xamarin.Forms" version="4.1.0.581479" exclude="Build,Analyzers" />
</group>
</dependencies>
但我已经为自己构建了 Xamarin.Froms 并将输出 dll 文件添加到我的项目参考中:
<Reference Include="Xamarin.Forms.Xaml">
<HintPath>..\thirdparty\xforms\Xamarin.Forms.Xaml.dll</HintPath>
</Reference>
根据 nuget's doc,我将 ExcludeAssets
属性(和其他测试)添加到 PackageReference of Xamanimation
的部分:
<PackageReference Include="Xamanimation">
<IncludeAssets>compile</IncludeAssets>
<!-- <ExcludeAssets>compile</ExcludeAssets> -->
<!-- <PrivateAssets>all</PrivateAssets> -->
<!-- <ExcludeAssets>buildtransitive</ExcludeAssets> -->
<Version>1.3.0</Version>
</PackageReference>
但其中 none 有效!
MSBuild
将始终使用传递依赖项 Xamarin.Forms.4.1.0 并忽略我自己构建的(我在其中添加了新的 类 并在主项目中使用它们所以链接失败表明选择的是旧的)。
那么排除传递依赖的正确方法是什么?
我花了一整天的时间研究这个问题,终于得到了一个合理的答案。
所以我想 post 我在 Whosebug 中的第一个长答案是我糟糕的英语。
TL'DR:
MSBuild
的nuget plugin
的ResolveNuGetPackageAssets
目标做坏事,制作一个自定义目标来恢复它,到任务代码的底部。
学习故事
首先,我把这个问题做了一个类似但更简单的副本来学习。
毕竟建一个xamarin项目太慢了,
演示源在github, 它有四个项目:
- ConflictLib:另一个库和主应用程序都使用的库
- DirectLib:主应用程序使用的库,正在使用
ConflictLib
- MyAppDev: 主程序,以上两个库为
ProjectReference
- MyAppConsumer:另一个应用程序,
PackageReference
使用DirectLib
,ProjectReference
使用ConflictLib
。为了测试这种情况,我将 ConflictLib 和 DirectLib 推送到nuget.org
,然后修改了本地版本的 ConflictLib,这样我就可以验证正在使用哪个。
这些项目及其关系与我的原始问题非常相似,关键点是:当应用程序同时使用具有两个不同版本的库时,本地版本(ProjectReference 或 HintPath)是否会(应该)获胜?
我的原生案例,xamarin项目,No
,所以来研究一下
测试用例,一个dotnet core console项目,是Yes
,所以在构建过程中一定有玄机:MSBuild
,这是一个庞大的系统,但是现在我'我要深入研究它。
然后,我需要一个检查工具来找出 MSBuild
在构建项目时做了什么。
这个简单的工具只是在命令行中调用它,它会显示所有执行的targets
。 msbuild target
类似于 a target in makefile
,目标中的 tasks
类似于 commands in makefile
,这个概念在许多其他系统中存在,但术语略有不同,例如 gradle,所以很容易理解。
但是,有这么多的目标和任务,而且它们都依赖于其他人并且通过 Property
和 Items
进行交互,很难从文本日志中了解哪个目标打破了我的需要。
幸运的是,有一个高级工具可以检查 MSBuild
中的所有内容:它叫做 MSBuild structured log viewer
, I learn it from here。
现在使用 /bl
选项构建项目,它将生成一个包含完整信息的二进制日志文件,用上面提到的查看器打开它:(我的原始 xamarin 项目的构建日志)
很明显,ResolveNuGetPackageAssets
目标更改了 Reference
项,这决定了最终的链接库程序集。
但是为什么它在测试用例中没有做出错误的决定呢?让我们查看它的日志:
有区别吗? -- 没有 ResolveNuGetPackageAssets
目标!
从 ResolveReferences
到 ResolveAssemblyReferences
是一样的,但是在 nuget 部分不同。
双击 ResolveAssemblyReferences
,查看器将打开 targets file
,其中定义了目标。
C:\Program Files (x86)\Microsoft Visual Studio19\Community\MSBuild\Current\Bin\Microsoft.Common.CurrentVersion.targets
两种情况仍然相同:
ResolveAssemblyReferences
不依赖于ResolveNuGetPackageAssets
,那么后者从何而来?只需单击它,文件就会打开:
C:\Program Files (x86)\Microsoft Visual Studio19\Community\MSBuild\Microsoft\NuGet.0\Microsoft.NuGet.targets
它覆盖 ResolveAssemblyReferencesDependsOn
并将 ResolveNuGetPackageAssets
添加到 ResolveAssemblyReferences
的依赖项。
最后一个问题:为什么上面的NuGet.targets
文件没有出现在测试用例中?它仍然可以由观众的 Evaluation
部分回答:
很明显,此文件未导入,因为 属性 SkipImportNuGetBuildTargets
设置为 true。经过简单的搜索,我确认这是测试用例中的默认值:它设置在Microsoft.NET.Sdk.targets中。但在 xamarin 的情况下,它没有设置并且意味着 false
,所以所有的事情都发生了。
最后,我得想办法解决这个问题了。
首先,我不会把SkipImportNuGetBuildTargets
属性加入到xamarin项目中,因为我觉得这是一个框架设计,可能对其他人影响很大,我只是想修复一个具体的小问题。
我决定在 ResolveAssemblyReferences
之后立即添加一个自定义目标,删除 Nuget's Xamarin.Forms
并添加我自己的 - 只需恢复 ResolveNuGetPackageAssets
所做的。
任务代码很简单(刚写完,其实花了我很多时间搜索grammar/builtinfunction/etc测试):
注意 Remove Item
(参见 msbuild doc)是如何工作的(而不是工作:注释行),我仍然不完全理解它,但它确实工作了!