How ad-hoc aliasing of NuGet-referenced assemblies work? - msbuild

We had issues in our build with some of our dependencies forcing really old version of a library we were using ourselves at a newer version. This got slightly worse with transitioning towards the new style of project files that no longer explicitly mention DLL resources brought in by NuGet packages, because we lost the ability to mark such assemblies by aliases.
Now, I found out a solution of basically the same problem at NuGet's GitHub. I adapted it for our needs in the following way:
<Target Name="AliasLog4Net" BeforeTargets="FindReferenceAssembliesForReferences;ResolveReferences">
<ItemGroup>
<ReferencePath Condition="'%(FileName)' == 'log4net'">
<Aliases>l4n</Aliases>
</ReferencePath>
</ItemGroup>
</Target>
It works magically. I want to know why though.
I can't find documentation for ReferencePath. Specifically, I would love to know what can I test for in the Condition attribute, apart from %(FileName)?
How can I log this logic? Is there a way to write something out for every ad-hoc application of the alias in this way?

Related

How does msbuild resolve assembly from a multi-framework nuget package?

I have a solution with a console app ConsoleApp-net461 that references a library ClassLibrary-net452.
Both projects reference a multi-framework nuget package NuGetPackage-net452-net461.
When I build, I end up with the nuget package's net461 dll in the output folder. At runtime, I am getting errors like:
System.TypeLoadException: Inheritance security rules violated by type: 'ClassA'.
Derived types must either match the security accessibility of the base type or be less accessible.
I am suspecting this is because my ClassLibrary-net452 ends up having a dependency on the nuget package dll with the net461 framework.
Is this normal behavior? What are the best ways to ensure the net452 dll is resolved from the nuget package? BindingRedirects don't seem to let you choose a target framework.
Not 100% sure this will work, but might be worth trying:
(Relies on using SDK csproj format, this link might work for packages.config, but I know less about the area.)
It looks like what you want is something like this. However the dotnet/SDK team haven't got round to implementing it yet.
The suggested work around is:
- Grab the dll path from your package reference
- Tell the package reference to not copy compile assets (dlls).
- Use the dll path to generate a similar path for a non-package reference that redirects to the Net452 version of the package.
Explanatory links here and here.
Summary is that your csproj should contain something like this:
<ItemGroup>
<PackageReference Include="*MyPackageName*" ExcludeAssets="Compile" GeneratePathProperty="true">
<Version>x.y.z</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Reference Include="*NameYouWantToGiveThisReference(suggest dll name)*">
<HintPath>$(Pkg*MyPackageName*)lib\net452\*DLLname*.dll</HintPath>
</Reference>
Everything surrounded by ** needs to be named according to whatever your projects are actually called.
The two MyPackageNames must match up (with the caveat that all "."s in the first one should be replaced with "_" in the second one).

PackageReference condition is ignored for old project format

I'm trying to centralize some of our project configurations by using the Directory.Builds.props/Directory.Build.targets files. Unfortunately we have a mixture of the old and new (sdk-style) project format. And not all of our projects can be converted to the new format due to some features not available in in the new format.
The issues that I'm running into is that I would like to have all our test projects automatically reference certain nuget packages. For projects in the new format, this works fine. However projects in the old format seem to ignore any conditions for the PackageReferences that I specify. It doesn't seem to matter what I use for the condition.
Here is an example of a very simple Directory.Build.Targets file:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PackageReference Include="Moq" Version="4.12.0" Condition="False" />
</ItemGroup>
</Project>
In this case I should never see the Moq package included in any project.
For all the projects in the old format it is included regardless.
I have also tried to use the condition on:
ItemGroup itself
Choose/When block
Putting a condition on a property group or property works as expected on the other hand.
I haven't see any mention in the documentation that conditions are not supported for ItemGroup or PackageReference.
Is there something I'm missing?
Thanks
I don't believe non-SDK projects support PackageReference; I suspect they are being ignored regardless of any condition you specify. Check for a packages.config file in the same directory as the project file-- if I am right it is present and references the package(s) in question.
The Nuget docs currently state:
You can use a condition to control whether a package is included, where conditions can use any MSBuild variable or a variable defined in the targets or props file. However, at presently, only the TargetFramework variable is supported.
My experience was that the Condition="" was ignored by Visual Studio, but respected by the msbuild command line. However, in my testing the <Choose><When Condition=""> block did seem to be respected by both tools.

CopyLocalLockFileAssemblies filtering

CopyLocalLockFileAssemblies (set in true) copes perfectly with the copying of nuget dependencies *.dll to the output folder. But there are many redundant libraries (Microsoft, System etc...) after build. I need some sort of a filtering to handle this mess. Is there elegant solution to solve the problem or only workarounds?
If the libraries are truly redundant, they would not be copied to the output. Many System.* and Microsoft.* libraries are really needed, especially if you get newer versions from NuGet than are in the framework you are running on (e.g. .NET Core 1.0 with dependencies on newer libraries).
If you really want to do this you can add the following to your csproj file to filter out an assembly:
<Target Name="FilterCopyLocalItems" AfterTargets="ResolveLockFileCopyLocalProjectDeps">
<ItemGroup>
<ReferenceCopyLocalPaths Remove="#(ReferenceCopyLocalPaths)" Condition="'%(Filename)' == 'Newtonsoft.Json'" />
</ItemGroup>
</Target>
You can use any MSBuild condition here, e.g. string methods:
Condition="$([System.String]::Copy('%(Filename)').Contains('HttpSys'))"

Unnecessary dlls in dotnet core console app?

I'm just trying build an example dotnet-core 2.0 console app which should be published as an execute file. This requires me to add an RuntimeIdentifier in the csproj file. After publishing my sample application for win-x64, I get a output directory which contains around 200 dlls and my executable. I have the feeling that's too much - only to print a simple Hello World to the console.
Is there a way to reduce the number of dlls? In this old (and now surely outdated document) named reducing package dependencies a manual approach is proposed for libraries.
Is there a way to reduce the dependencies in dotnet-core 2.0? Or isn't this an issue after all and I shouldn't care?
Just for completeness, here is my example project definition:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<RuntimeIdentifiers>Portable;win-x64</RuntimeIdentifiers>
</PropertyGroup>
</Project>
All the dependencies are useful in a certain way (some classes of each are used to make your app work), so when you say "unnecessary" you are wrong.
So far, there is no better tool than the newly announced IL Linker to shrink the size of deployment,
https://github.com/dotnet/announcements/issues/30

.net core (csproj) global.json 'projects' equivalent

With .net core (project.json) I used to switch between nuget packages and source code by adding the path to source code to the projects field in the global.json. After I did that it would add all the projects that it could find in that path that could replace the nuget packages I referenced.
I used this feature alot because I have my own nuget packages that I use, but I want to test the changes in my other project before I publish. But once I switched to Sdk 1.0.0/VS 2017/csproj .net core that feature seemed to disappear.
The alternative is just manually adding each project, switch the references manually (since they are broken up into project, nuget and sdk references), and then after switch it all back.
Any thoughts or advice would be great.
UPDATE:
Sounds like there is no equivalent in csproj (as expected), but there are msbuild workarounds for now (As of the initial VS 2017/.NET Core SDK 1.0.0 release)
Yes, I too had gotten used to this functionality and built my workflow around it. I am still looking for a solution but I'm currently playing with the idea of using conditional logic in the csproj files. Since it's now msbuild, you can do things like this:
<Choose>
<When Condition="Exists('..\..\..\MyProject')">
<ItemGroup>
<ProjectReference Include="..\..\..\MyProject\src\MyProject\MyProject.csproj" />
</ItemGroup>
</When>
<Otherwise>
<ItemGroup>
<PackageReference Include="MyProject" Version="1.0.0" />
</ItemGroup>
</Otherwise>
</Choose>
This replaces the hard reference to a package with a conditional that uses a project reference if it can find the source code (in this case the directory), and a package reference if can't.
So by default you would be referencing the package, but if you want to debug one of your projects, you check it out in the location that the conditional checks, and add the project to your solution.
This way you only need to change your solution file (by adding the project) when you want to include source code, instead of rewiring all your project references.
For others that are interested in attempting to emulate with Global.json did, I worked around this for now using a couple powershell scripts and a custom json file that mimics it. Check out my answer here:
https://stackoverflow.com/a/43795974/5504245