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?
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.
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.
after all, building a xamarin project is too slow,
the demo source is in github, it has four project:
ConflictLib
ProjectReference
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.
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)
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:
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:
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
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:
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.
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):
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!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With