Copy files from Nuget package to output directory with MsBuild in .csproj and dotnet pack command

It is possible to copy files without the .nuspec file, if you create your nuget from the .csproj file as described here.

And to copy files from nuget to output directory, create a ProjectName.targets file with the following content:

<ItemGroup>
    <LogFiles Include="$(MSBuildThisFileDirectory)\..\contentFiles\LogFiles\*.config" />
</ItemGroup>
<Target Name="CopyLogFiles" BeforeTargets="Build">
    <Copy SourceFiles="@(LogFiles)" DestinationFolder="$(TargetDir)CopiedLogFiles\" />
</Target>

In your .csproj file add:

<ItemGroup Label="FilesToCopy">
   <Content Include="ProjectName.targets" PackagePath="build/ProjectName.targets" />
   <Content Include="LogFiles\*.config" Pack="true" PackagePath="contentFiles\LogFiles">
     <PackageCopyToOutput>true</PackageCopyToOutput>
   </Content>
</ItemGroup>

The paths and names can of course be freely chosen.

This should copy all .config files to a folder called CopiedLogFiles in the output directory!


Ok I finally found the solution and that includes a .nuspec file and a .targets file as well.

The ProjectName.csproj just needs to include this

<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <TargetFramework>netstandard2.0</TargetFramework>
        <RestoreProjectStyle>PackageReference</RestoreProjectStyle>
        <NuspecFile>ProjectName.Logging.nuspec</NuspecFile>  
    </PropertyGroup>

    <!-- This is just for some projects referencing this project directly instead of the Nuget package -->
    <ItemGroup>
        <Content Include="NLog\NLog.config">
            <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
        </Content>
    </ItemGroup>

    <ItemGroup>
        <PackageReference Include="Dapper" Version="1.50.5" />
        <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.1.1" />
        <PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.1" />
        <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.1.1" />
        <PackageReference Include="NLog" Version="4.5.8" />
        <PackageReference Include="NLog.Extensions.Logging" Version="1.2.1" />
        <PackageReference Include="NLog.Web.AspNetCore" Version="4.6.0" />
        <PackageReference Include="System.Data.Common" Version="4.3.0" />
        <PackageReference Include="System.Data.SqlClient" Version="4.5.1" />
    </ItemGroup>
</Project>

In the ProjectName.nuspec you put everything related to the Nuget package

<?xml version="1.0"?>
<package >
    <metadata>
        <id>ProjectName.Logging</id>
        <version>1.1.0</version>
        <authors>Jérôme MEVEL</authors>
        <description>Just a logging component</description>
        <releaseNotes>Extract the NLog.config file automatically</releaseNotes>
        <dependencies>
            <dependency id="Dapper" version="1.50.5" />
            <dependency id="Microsoft.Extensions.DependencyInjection" version="2.1.1" />
            <dependency id="Microsoft.Extensions.Logging" version="2.1.1" />
            <dependency id="Microsoft.Extensions.Logging.Abstractions" version="2.1.1" />
            <dependency id="NLog" version="4.5.8" />
            <dependency id="NLog.Extensions.Logging" version="1.2.1" />
            <dependency id="NLog.Web.AspNetCore" version="4.6.0" />
            <dependency id="System.Data.Common" version="4.3.0" />
            <dependency id="System.Data.SqlClient" version="4.5.1" />
        </dependencies>
    </metadata>
    <files>
        <file src="bin\Release\netstandard2.0\ProjectName.Logging.dll" target="lib/netstandard2.0/ProjectName.Logging.dll" />    
        <file src="ProjectName.Logging.targets" target="build/ProjectName.Logging.targets" />
        <file src="NLog/Nlog.config" target="content/Nlog.config" />
    </files>
</package>

And finally the ProjectName.targets. Careful! The file is located in the Nuget cache of the machine. You will be able to see it at the root of your project in Visual Studio but not in the Windows Explorer (in Windows at least). So if you modify the file in Visual Studio, it will modify it for ALL other projects on this machine referencing the same Nuget package (and same version).

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <ItemGroup>
        <Content Include="$(MSBuildThisFileDirectory)\..\content/NLog.config">
            <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
        </Content>
    </ItemGroup>
</Project>

I generate the Nuget package using the dotnet pack nuget pack command (now that I've more experience I know that dotnet pack doesn't work well with a .nuspec file, there are several bugs) Here is the result:

enter image description here

Finally I can just install my package and during the build process, the Nlog.config file will be copied out of the Nuget cache to the output directory of my project.


I think that How do you set nuget contentFiles CopyToOutput value to true when using a .Net Standard library .csproj? provides a better answer to this question. https://github.com/NuGet/NuGet.Client/pull/1450

You can set PackageCopyToOutput to true in the .csproj to declare the nuget content file as "CopyToOutput=true". That way any project referencing the nuget will have the content file marked with <CopyToOutput>true</CopyToOutput> in the referring csproj file, instructing msbuild to copy the content file to the ouput directory:

In the .csproj of the nuget project:

<Content Include="...">
    <PackageCopyToOutput>true</PackageCopyToOutput>
</Content>