3
votes

I'm attempting to migrate to using MSBuild Pack support for using .csproj to generate a projects NuGet package where during development local .dll's are used to build the project but they need to be replaced/swapped to reference an external NuGet package in the generated .nuspec when Using MSBuild to "pack" the project.

The closest documented example of this use-case I could find is Replacing one library from a restore graph where it suggests you can replace an external NuGet reference:

<PackageReference Include="Newtonsoft.Json" Version="9.0.1">
  <ExcludeAssets>All</ExcludeAssets>
</PackageReference>

Which overrides the package to reference to a local .dll instead:

<Reference Include="Newtonsoft.Json.dll" />

I'm trying to do something similar where the project should build against local .dll's that were injected as an artifact from a dependent TeamCity/CI build:

<Reference Include="..\..\lib\net45\ServiceStack.Interfaces.dll" />
<Reference Include="..\..\lib\net45\ServiceStack.Text.dll" />
<Reference Include="..\..\lib\net45\ServiceStack.Common.dll" />

But when using ExcludeAssets=All as per the documentation:

<PackageReference Include="ServiceStack.Common" Version="5.0.0">
  <ExcludeAssets>All</ExcludeAssets>
</PackageReference>

The PackageReference doesn't get exported in the generated dependency list, e.g:

<group targetFramework=".NETFramework4.5" />

The closest I've come to getting the preferred behavior is to use ExcludeAssets="compile" so my local project doesn't build against it, except this behavior also gets exported in the .nuspec:

<group targetFramework=".NETFramework4.5">
  <dependency id="ServiceStack.Common" version="5.4.0" exclude="Compile,Build,Analyzers" />
</group>

I only want to avoid building against it locally but have it exported as a normal dependency. The other issue with this approach is that I need it to reference a package that hasn't been published to NuGet yet (as all packages are published in lock-step), e.g:

<!-- v5.5.0 is the new version to publish -->
<PackageReference Include="ServiceStack.Common" Version="5.5.0" ExcludeAssets="compile"/>

The build fails that it can't find the unpublished package:

[NU1102] Unable to find package ServiceStack.Common with version (>= 5.5.0)

Effectively I need someway to "inject" the exact dependencies and version I need in the generated .nuspec so it's included in the generated .nuspec dependency list, e.g:

<group targetFramework="net45">
  <dependency id="ServiceStack.Common" version="5.5.0" />
</group>
<group targetFramework=".netstandard2.0">
  <dependency id="ServiceStack.Common" version="5.5.0" />
</group>

How can we inject a custom dependency in MSBuild generated .nuspec?

Is there some way I can manually declare <dependency/> as above so it's only used when MSBuild/NuGet packs the project? Otherwise is there a way to apply some XML transform to the generated .nuspec so I can manipulate the XML in the .nuspec before it's packed/compressed?

Basically I'm only interested in injecting dependencies when the "pack" target is run so it's ignored/inert in all other targets.

3

3 Answers

0
votes

Interesting question, and when I looked around for answers I found Another discussion in Stackoverflow

Where in one of the comments - there was an indication that the pack command only works with framework projects with 4.6 and above - not 4.5.

The .Net Standard 2.0 has to match 4.6.1 based on this blog entry from Microsoft

0
votes

You've probably found another solution for this by now, but I "succeeded" in doing this, by adding some custom targets to the csproj. The solution did leave me with the feeling that maybe I shouldn't have done it.

The steps were

  • Inject a step before the package generation target, that disables the nupkg generation
  • Inject a step after the package generation target that modifies the generated nuspec file, and call the package generation target again.

I've modified my fix below so it injects the dependencies mentioned in the post. I've disabled validation here (NoPackageAnalysis=true). You can put it in a .target file and import it from the csproj.

<Project>

  <!-- Disable nupkg generation before running pack -->
  <Target Name="__DisablePacking" BeforeTargets="GenerateNuspec" Condition="$(NuspecFile) == ''">
    <PropertyGroup>
      <ContinuePackingAfterGeneratingNuspec>false</ContinuePackingAfterGeneratingNuspec>
    </PropertyGroup>
  </Target>

  <!-- Modify the generated nuspec file and rerun the pack target -->
  <Target Name="__EnablePackingAndInjectDependencies" AfterTargets="Pack" Condition="$(NuspecFile) == ''">
    <!-- Get the nuspec file name -->
    <PropertyGroup>
      <_NugetPackOutputAsProperty>@(NuGetPackOutput)</_NugetPackOutputAsProperty>
    </PropertyGroup>
    <ItemGroup>
      <_NugetPackOutputAsItem Remove="@(_NugetPackOutputAsItem)"/>
      <_NugetPackOutputAsItem Include="$(_NugetPackOutputAsProperty.Split(';'))" />
    </ItemGroup>
    <PropertyGroup>
      <__NuspecFileName>%(_NugetPackOutputAsItem.Identity)</__NuspecFileName>
    </PropertyGroup>

    <!-- Create an updated dependencies, with the net46 dependencies copied to a native group -->
    <PropertyGroup>
      <__NuSpecUpdatedDependencies>
        <group targetFramework="net45">
          <dependency id="ServiceStack.Common" version="5.5.0" />
        </group>
        <group targetFramework=".netstandard2.0">
          <dependency id="ServiceStack.Common" version="5.5.0" />
        </group>
      </__NuSpecUpdatedDependencies>
    </PropertyGroup>

    <!-- Poke them back in -->
    <XmlPoke XmlInputPath="$(__NuspecFileName)"
             Value="$(__NuSpecUpdatedDependencies)"
             Query="/n:package/n:metadata/n:dependencies"
             Namespaces="&lt;Namespace Prefix='n' Uri='http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd' /&gt;">
    </XmlPoke>

    <!-- call the pack operation again -->
    <PropertyGroup>
      <ContinuePackingAfterGeneratingNuspec>true</ContinuePackingAfterGeneratingNuspec>
    </PropertyGroup>

    <Msbuild
      Projects="$(MSBuildProjectFullPath)"
      Targets="Pack"
      Properties="NuspecFile=$(__NuspecFileName);NoPackageAnalysis=true">
    </Msbuild>
  </Target>
</Project>

(I was looking to inject a native0.0 framework dependency so my packages could be consumed by c++/cli projects, in case someone should know of an easier way to do it).

-2
votes
<?xml version="1.0"?>
<package >
<metadata>
<id>FIK.DAL</id>
<version>$version$</version>
<title>FIK.DAL .NET</title>
<authors>$author$</authors>
<owners>Imamul Karim</owners>
<license type="expression">MIT</license>
<projectUrl>https://github.com/imamulkarim/FIK.DAL</projectUrl>

<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>A Simple SQL Query Generator Using ADO.NET Provider with SqlClient</description>
<releaseNotes>SQLite Library Bug</releaseNotes>
<copyright>$copyright$</copyright>
<tags>ADO.NET SQL QUERY Generator</tags>
</metadata>
<files>
<file src="bin\Debug\System.Data.SQLite.DLL" target="lib\net40"></file>
</files>
</package>

https://docs.microsoft.com/en-us/nuget/reference/nuspec