Kzu's Today I Learned
GitHubX/TwitterBlog
  • Today I Learned
  • dotnet
    • How to emit descriptions for exported JSON schema using JsonSchemaExporter
    • NuGet
      • Suppress dependencies when packing
      • Hide contentFiles from your nuget packages
      • Packaging transitive analyzers with NuGet
      • How to add search to static nuget feed
      • Populate RepositoryBranch in CI for NuGet Pack
    • Ignore folder from dotnet-format
    • Accessing Tor .onion URLs via HttpClient with .NET6
    • Installing .NET 5.0 on Raspberry Pi 4
    • Quickly check C# compiler and language version
    • Disable diagnostic analyzers for entire folder/submodules
    • Persisting output files from source generators
    • Use C# 9 records in non-net5.0 projects
    • AsyncLocal never leaks and is safe for CallContext-like state
    • Using HashCode in .NETFramework
    • How to locate dotnet
  • testing
    • Conditional unit tests
    • Skip tagged scenarios in SpecFlow with Xunit
  • msbuild
    • How to get user home dir ~ cross-platform
    • Modifying the build for every solution in a repository
    • Detect CI builds for every CI system
    • Modify all command-line builds in entire repo
    • Write entire XML fragments in MSBuild with XmlPoke
    • How to select first item in an ItemGroup
    • How to include commit URL in nuget package description
    • How to include package reference files in your nuget
    • How to build project when content files change
  • azure
    • How to launch multiple Azure Functions apps on different ports
    • C# script function apps beyond Azure portal
    • Publishing function app from GitHub folder
    • Exploring Azure Data with Kusto and Dashboards
    • Shared secret authorization with Azure SignalR Service
    • Using Azure File Copy from DevOps yaml pipeline
    • Code-less redirection with serverless Azure Functions
  • DevOps/CI/CD
    • How to run Azure Storage unit tests in CI
    • How to skip steps or jobs in GitHub Actions for PRs from forks
    • Update version and publish npm from GH
    • Push to protected branch from GitHub actions
Powered by GitBook
On this page
  1. msbuild

How to include package reference files in your nuget

This is a particularly common scenario if you're developing MSBuild tasks or Roslyn code analyzers: all the dependencies you use in your task, analyzer or source generator, need to be included in your nuget package too, alongside your project primary output (i.e. under the properanalyzer, tools or buildfolders). A very convenient way (if not comprehensive, since it won't include with transitive dependencies) to do so is by annotating your PackageReference themselves, like:

<PackageReference Include="Scriban" Version="2.1.2" PrivateAssets="all" Pack="true" />

The Pack metadata value can then be used to include the assets in the package with the following target:

  <!-- For every PackageReference with Pack=true, we include the assemblies from it in the package -->
  <Target Name="AddPackDependencies" 
          Inputs="@(RuntimeCopyLocalItems)" 
          Outputs="%(RuntimeCopyLocalItems.NuGetPackageId)" 
          DependsOnTargets="ResolvePackageAssets"
          BeforeTargets="GenerateNuspec"
          AfterTargets="ResolvePackageAssets">
    <ItemGroup>
      <NuGetPackageId Include="@(RuntimeCopyLocalItems -> '%(NuGetPackageId)')" />
    </ItemGroup>
    <PropertyGroup>
      <NuGetPackageId>@(NuGetPackageId -&gt; Distinct())</NuGetPackageId>
    </PropertyGroup>
    <ItemGroup>
      <PackageReferenceDependency Include="@(PackageReference -&gt; WithMetadataValue('Identity', '$(NuGetPackageId)'))" />
    </ItemGroup>
    <PropertyGroup>
      <NuGetPackagePack>@(PackageReferenceDependency -> '%(Pack)')</NuGetPackagePack>
    </PropertyGroup>
    <ItemGroup Condition="'$(NuGetPackagePack)' == 'true'">
      <_PackageFiles Include="@(RuntimeCopyLocalItems)" PackagePath="$(BuildOutputTargetFolder)/$(TargetFramework)/%(Filename)%(Extension)" />
      <RuntimeCopyLocalItems Update="@(RuntimeCopyLocalItems)" CopyLocal="true" Private="true" />
      <ResolvedFileToPublish Include="@(RuntimeCopyLocalItems)" CopyToPublishDirectory="PreserveNewest" RelativePath="%(Filename)%(Extension)" />
    </ItemGroup>
  </Target>

Quite a few things to note in the above target that aren't too obvious:

  1. We use the RuntimeCopyLocalItems item group which is resolved by ResolvePackageAssets and contains the stuff that is needed for the dependency to run (i.e. the actual binaries, not reference assemblies, if it includes them).

  2. We use Inputs/Outputs on it so we can batch by %(RuntimeCopyLocalItems.NuGetPackageId): this makes processing simpler inside the target, since we'll be dealing with a single NuGetPackageId for each batch, regardless of how many RuntimeCopyLocalItemsthere are.

  3. We next get that package ID as a property, and find the @(PackageReference) with that ID, to determine if it needs to be packed or not.

  4. Note we use property syntax next since we know there can be at most one such @(PackageReferenceDependency), in which case we'd get either an empty value or true for %(Pack).

  5. If we have to pack, we include all the @(RuntimeCopyLocalItems) (in the current batch, MSBuild does this for us for free thanks to the Inputs/Outputs) and use as the package path the same as what the primary project output will use. These are added directly as _PackageFiles which is what the SDK-style project Pack uses.

  6. We update the items metadata so they are also flagged as copy-local

  7. Finally, the ResolvedFileToPublish is useful when creating dotnet tools, since packing those is slightly different and includes a publish operation.

PreviousHow to include commit URL in nuget package descriptionNextHow to build project when content files change

Last updated 3 years ago