如何在MSBuild中排除(禁用)PackageReference的(传递)依赖性?

时间:2019-12-27 09:42:46

标签: xamarin.forms msbuild dependencies nuget

我使用的软件包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>

但是它们都不起作用!

MSBuild将始终使用Trasitive依赖项Xamarin.Forms.4.1.0并忽略我自己的构建类(在其中我添加了新类并在主项目中使用它们,因此链接失败指示已选择是旧的)。

那么排除传递依赖的正确方法是什么?

1 个答案:

答案 0 :(得分:0)

我花了一整天的时间研究这个问题,最后得到了一个合理的答案。

所以我想用我可怜的英语在stackoverflow中发布我的第一个长答案。

TL'DR:

MSBuild的{​​{1}}的{​​{1}}目标做了邪恶的事情,创建了一个自定义目标以将其还原,并从底部查找任务代码。


学习故事

首先,我为研究此问题制作了类似但更简单的副本。

毕竟,建立一个Xamarin项目太慢了,

演示源位于github中, 它有四个项目:

  • ConflictLib:另一个库和主应用程序同时使用的库
  • DirectLib:主应用程序使用的库,并且正在使用nuget plugin
  • MyAppDev:主应用程序,以上两个库为ResolveNuGetPackageAssets
  • MyAppConsumer:另一个应用程序,使用ConflictLib的{​​{1}}和ProjectReference作为DirectLib。为了测试这种情况,我将ConflictLib和DirectLib推送到PackageReference,然后对ConflictLib的本地版本进行了修改,以便可以验证使用哪个版本。

这些项目及其关系与我的起源问题非常相似,关键点是:当应用程序同时使用具有两个不同版本的库时,本地(ProjectReference或HintPath)(应该)会赢吗?

对于我的原始案例xamarin项目,它是ConflictLib,所以我来研究它。

对于一个测试用例,一个dotnet核心控制台项目,它是ProjectReference,因此在构建过程中一定会有一些神秘之处:nuget.org,这是一个庞大的系统,但是现在我要去进行深入研究。

然后,我需要一个检查工具来找出No在构建项目时的工作。

简单的工具只是在命令行中调用它,它将显示所有执行的YesMSBuild类似于MSBuild,而目标中的targetsmsbuild target类似,该概念在诸如gradle之类的许多其他系统中以稍有不同的术语存在,因此容易理解。

但是,目标和任务太多了,它们都依赖于其他目标并且通过a target in makefiletasks进行交互,因此很难从文本日志中了解哪个目标违反了我的需求。

幸运的是,有一个用于检查commands in makefile中所有内容的高级工具:它叫做MSBuild structured log viewer,我从here中学到了。

现在使用Property选项构建项目,它将生成一个包含完整信息的二进制日志文件,由上述查看器打开:(我的原始xamarin项目的构建日志)

enter image description here

很明显,Items目标更改了MSBuild项,从而决定了最终的链接库程序集。

但是为什么,它在测试用例中没有做出错误的决定?让我们查看其日志: enter image description here

有区别吗? -没有/bl目标!

ResolveNuGetPackageAssetsReference,但在nuget部分有所区别。

ResolveNuGetPackageAssets上双击的同时,查看器将打开其中定义目标的ResolveReferences

  

C:\ Program Files(x86)\ Microsoft Visual Studio \ 2019 \ Community \ MSBuild \ Current \ Bin \ Microsoft.Common.CurrentVersion.targets

两种情况仍然相同:

enter image description here

ResolveAssemblyReferences不依赖于ResolveAssemblyReferences,那么后者在哪里呢?只需单击它,文件就会打开:

  

C:\ Program Files(x86)\ Microsoft Visual Studio \ 2019 \ Community \ MSBuild \ Microsoft \ NuGet \ 16.0 \ Microsoft.NuGet.targets

enter image description here

它会覆盖targets file,并将ResolveAssemblyReferences添加到ResolveNuGetPackageAssets的依存关系中。

最后一个问题:为什么上述ResolveAssemblyReferencesDependsOn文件没有出现在测试用例中?观看者的ResolveNuGetPackageAssets部分仍可以回答:

enter image description here

显然,由于属性ResolveAssemblyReferences设置为true,因此不会导入该文件。经过简单的搜索后,我确认它是测试用例的默认值:已在Microsoft.NET.Sdk.targets中设置。但是在xamarin情况下,它没有设置并且表示NuGet.targets,所以所有事情都发生了。

最后,我必须找出一些解决问题的方法。

首先,我不会将Evaluation属性添加到xamarin项目中,因为我认为这是一个框架设计,并且可能会对其他方面产生很大的影响,所以我只想解决一个特定的问题。

我决定在SkipImportNuGetBuildTargets之后立即添加自定义目标,删除false并添加自己的目标-只需还原SkipImportNuGetBuildTargets的内容即可。

任务代码很简单(仅在写完之后,实际上花了我很多时间来搜索语法/内置函数/等并进行测试):

enter image description here

注意ResolveAssemblyReferences(请参阅msbuild doc)的工作方式(但不起作用:注释行),我仍然不太了解它,但是它确实起作用了!