Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to exclude(disable) PackageReference's (transitive)dependency in MSBuild?

I'm using a package Xamanimation which has a dependency of Xamarin.Forms 4.1.0( written in its nuspec file):

    <dependencies>
      <group targetFramework=".NETStandard2.0">
        <dependency id="Xamarin.Forms" version="4.1.0.581479" exclude="Build,Analyzers" />
      </group>
    </dependencies>

but I have built the Xamarin.Froms for my own and added the output dll files into my project's reference:

  <Reference Include="Xamarin.Forms.Xaml">
      <HintPath>..\thirdparty\xforms\Xamarin.Forms.Xaml.dll</HintPath>
    </Reference>

according to the nuget's doc, I add the ExcludeAssets attribute(and other tests) to the section of PackageReference of Xamanimation:

    <PackageReference Include="Xamanimation">
      <IncludeAssets>compile</IncludeAssets>
      <!-- <ExcludeAssets>compile</ExcludeAssets> -->
      <!-- <PrivateAssets>all</PrivateAssets> -->
      <!-- <ExcludeAssets>buildtransitive</ExcludeAssets> -->
      <Version>1.3.0</Version>
    </PackageReference>

but none of them work!

MSBuild will always use the trasitive dependency Xamarin.Forms.4.1.0 and ignore my own build ones( in which I have add new classes and use them in the main project so the linking failure is indicating the choosen is the old one).

so what's the correct method to exclude the transitive dependency?

like image 941
fatfatson Avatar asked Mar 04 '23 00:03

fatfatson


1 Answers

I spend a whole day learning into this question and finally got a reasonable answer.

so I'd like to post my first LONG answer in stackoverflow by my poor english.

TL'DR:

the MSBuild's nuget plugin's ResolveNuGetPackageAssets target do the evil thing, make a custom target to revert it, go to bottom for the task code.


Studying story

first, I made a similar but simpler copy of this problem for studying.

after all, building a xamarin project is too slow,

the demo source is in github, it has four project:

  • ConflictLib: the library used both by another lib and main application
  • DirectLib: the library used by main application, and is using ConflictLib
  • MyAppDev: the main application, with above two library as ProjectReference
  • MyAppConsumer: the other application, using DirectLib by PackageReference, and ConflictLib as ProjectReference. to test this situation, I pushed the ConflictLib and DirectLib to nuget.org, then made modify to local version of ConflictLib, so I can verify which one is using.

these projects and their relationship are very similar to my origin problem, the key point is : when application use a library with two different version simutanously, will(should) the local one(ProjectReference or HintPath) win?

for my origin case, xamarin project, it's No, so I come to study it.

for the test case, a dotnet core console project, it's Yes, so there must be something mysterious in the building process: MSBuild, which is a huge system, but now I'm going to dig into it.

then, I need a inspecting tool to find out what MSBuild does when building a project.

the simple tool is just invoke it in command line, it will display all the targets executed. a msbuild target is something like a target in makefile, and the tasks in target are similar to commands in makefile, the concept exist with slightly different term in many other system such as gradle, so it's easy to understand.

but, there are so many targets and tasks, and all they depend on others and interactive though Property and Items, it's hard to learn which target break my need from the text log.

fortunately, there's an advanced tool for inspecting everything in MSBuild: it's called MSBuild structured log viewer, I learn it from here.

now build the project with /bl option, it will produce a binary log file with full info, open it by the viewer mentioned above:(my origin xamarin project's build log)

enter image description here

obvious, the ResolveNuGetPackageAssets target changes the Reference items, which decides the final linked library assembly.

but why it doesn't make the wrong decision in the test case? let's view its log: enter image description here

got the difference? -- there's no ResolveNuGetPackageAssets target!

it's same from ResolveReferences to ResolveAssemblyReferences, but diff in the nuget part.

while double clicking at ResolveAssemblyReferences, the viewer will open the targets file in which the target be defined.

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

it's still same for both case:

enter image description here

ResolveAssemblyReferences doesn't depend on ResolveNuGetPackageAssets, so where does the later come? just click at it, the file opens:

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

enter image description here

it overrides ResolveAssemblyReferencesDependsOn and add the ResolveNuGetPackageAssets to dependency of ResolveAssemblyReferences.

the last question: why the above NuGet.targets file not appear in the test case? it could still answered by the viewer's Evaluation section:

enter image description here

clearly, this file is not imported because a property SkipImportNuGetBuildTargets set to true. after a simple search, I confirmed it's the default value in test case: it's set in Microsoft.NET.Sdk.targets. but in xamarin case, it's not set and means false, so all the things happened.

finally, I have to figure out some measures to fix the problem.

first of all, I won't add the SkipImportNuGetBuildTargets property to xamarin project, because I think it's a framework design and maybe has a great impact on others, I just want to fix a little, specific problem.

I decide to add a custom target immediately after the ResolveAssemblyReferences, remove the Nuget's Xamarin.Forms and add my own ones -- just revert what ResolveNuGetPackageAssets does.

the task code is simple (only just after I have written, actually it cost me lot of time to search for grammar/builtin function/etc and test):

enter image description here

notice how the Remove Item(see msbuild doc) works (and not work: the commented lines), I still don't understand it exactly, but it did work!

like image 107
fatfatson Avatar answered Mar 10 '23 10:03

fatfatson