Search code examples
c#.netmsbuildnugetdevenv

How to control output of a nuget package dependencies during build


I would like to support backward compatibility in my application. Simply saying - one app needs to work using different versions of a dll depending on a flag which the app get's during runtime.

I've simplified everything and created a test solution with 2 projects in it. Each project has it's own version of the same nuget package.

test solution

I picked System.Drawing.Common cause it has no dependencies.

ClassLibrary1 contains System.Drawing.Common of version 4.5.0.

ClassLibrary2 contains System.Drawing.Common of version 6.0.0.

Both projects have same output path:

<OutputPath>..\DEBUG\</OutputPath>

When I build my solution I get just one System.Drawing.Common.dll in my output folder: current output

Cause both dlls have one name and only version is different.

The desired behavior on the pictures below:

  1. Distribute the nuget package dependencies into different folders according to versions. desired output

  2. Add suffix to the nuget package dependencies according to versions. desired output

The idea is in controlling output of the nuget package dependencies. Do you have any idea how I can achieve that ?

P.S. all other logic - resolving dependencies according versions etc is out of scope of this question.


Solution

  • It's possible. First you need to add GeneratePathProperty to PackageReference element in csproj file

    <ItemGroup>
        <PackageReference Include="System.Drawing.Common">
            <Version>4.5.0</Version>
            <GeneratePathProperty>true</GeneratePathProperty>
        </PackageReference>
    </ItemGroup>
    

    It allows us using $(PkgSystem_Drawing_Common) variable which contains a path to the nuget package.

    Then we need to create a msbuild targets file

    <?xml version="1.0" encoding="utf-8"?> 
    <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
        <Target Name="CopyNugetDll" BeforeTargets="BeforeCompile" Outputs="System.Drawing.Common.dll">
            <XmlPeek XmlInputPath="$(ProjectPath)" Query="Project/ItemGroup/PackageReference[@Include='System.Drawing.Common']/Version/text()">
                <Output TaskParameter="Result" PropertyName="NugetPackageVersion" />
            </XmlPeek>
            <ItemGroup>
                <NugetrDll Include="$(PkgSystem_Drawing_Common)\lib\net461\System.Drawing.Common.dll" />
            </ItemGroup>
            <Message Text="Copying @(NugetrDll) to $(OutDir)" Importance="high" />
            
            <Exec Command="copy $(PkgSystem_Drawing_Common)\lib\net461\System.Drawing.Common.dll $(OutDir)\System.Drawing.Common.$(NugetPackageVersion).dll" />
        </Target>
    </Project>
    

    Here using xpath we select version from project.assets.json file and save it in NugetPackageVersion variable. Exec copy is used to copy the dll to a specific location with a specific prefix which contains a value from NugetPackageVersion variable.

    Lastly you need to include msbuild targets file to a project

    <Import Project="CopyDll.targets" />