使用MsBuild和dotnet pack命令将Nuget软件包中的文件复制到输出目录

21
上次我必须找出如何从Nuget包中提取一些文件,这至少花了我6个月的时间,但最终我成功找到了解决方案
问题是,该解决方案假设我有一个`.nupkg`文件并手动添加一个`.targets`文件来执行提取过程。
现在情况不同了:
  1. 我没有任何`.nupgk`文件,我们在VSTS服务器上使用`dotnet pack`命令自动生成一个。 然后我们从我们的Nuget服务器消耗这个包
  2. 我们无法再花费6个月的时间找到解决方案
这是我的`ProjectName.csproj`:
<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <TargetFramework>netstandard2.0</TargetFramework>
        <RestoreProjectStyle>PackageReference</RestoreProjectStyle>
        <Authors>Jérôme MEVEL</Authors>
        <Version>1.0.3</Version>
        <GeneratePackageOnBuild>true</GeneratePackageOnBuild>

        <!-- This answer got many votes on a Github thread so I tried just in case -->
        <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>

        <!-- Just trying that too-->
        <IncludeBuildOutput>true</IncludeBuildOutput>
        <IncludeContentInPack>true</IncludeContentInPack>

        <!-- I wanted to see the full generated Nuget package -->
        <IncludeSource>true</IncludeSource>

        <!-- desperate attempt -->     
        <TargetsForTfmSpecificBuildOutput>GetMyPackageFiles</TargetsForTfmSpecificBuildOutput>
    </PropertyGroup>
    <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">

            <!-- Let's try this too just in case-->
            <IncludeAssets>all</IncludeAssets>
        </PackageReference>
        <PackageReference Include="NLog.Extensions.Logging" Version="1.2.1">
            <IncludeAssets>all</IncludeAssets>
        </PackageReference>
        <PackageReference Include="NLog.Web.AspNetCore" Version="4.6.0">
            <IncludeAssets>all</IncludeAssets>
        </PackageReference>
        <PackageReference Include="System.Data.Common" Version="4.3.0" />
        <PackageReference Include="System.Data.SqlClient" Version="4.5.1" />
    </ItemGroup>
    <ItemGroup>

        <!-- Added the file to the root folder in case <IncludeAssets>all</IncludeAssets> is looking there -->
        <Content Include="NLog.config">
            <Pack>true</Pack>
            <PackagePath>NLog;;</PackagePath>
            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
        </Content>

        <!-- My desired path to look for the file -->
        <Content Include="NLog\NLog.config">
          <Pack>true</Pack>
          <PackagePath>NLog;;</PackagePath>
          <CopyToOutputDirectory>Always</CopyToOutputDirectory>
        </Content>
    </ItemGroup>

    <!-- This is the default setting, perfectly working when I reference the project instead of installing the Nuget package -->
    <ItemGroup>
        <None Update="NLog\NLog.config">
            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
        </None>
    </ItemGroup>

    <!-- desperate attempt -->
    <Target Name="GetMyPackageFiles">
        <ItemGroup>
            <BuildOutputInPackage Include="NLog/Nlog.config">
                <TargetPath>NLog</TargetPath>
            </BuildOutputInPackage>
        </ItemGroup>
    </Target>
</Project>

正如您所看到的,我尝试了几种不同的设置。这个MsBuild会在Nuget包文件的根目录中包含一个名为NLog的文件夹下的NLog.config文件。

enter image description here

在我不同的尝试过程中,根据我设置的配置,我能够最终得到位于src/ProjectName.Logging/NLog/NLog.config甚至是lib/netstandard2.0/Nlog.config位置的NLog.config文件。

因此,我的文件肯定包含在Nuget包文件中,但没有复制到使用该包的项目的输出目录中。

我尝试指定使用带有dotnet pack生成我的包的.nuspec文件,就像这里描述的那样,但我从未能够获得期望的结果(要么只包括我的NLog.config在Nuget包中,要么包括所有源文件)。此外,这有几个缺点,比如覆盖.csproj文件中的配置或添加无用的复杂性。我相信可以在不使用.nuspec文件的情况下完成我想要实现的目标(也许我是错的)。

我注意到我的包中缺少build/ProjectName.targets文件,这可能是缺失的部分。那么如何在不手动修改包的情况下添加此.targets文件?

是否还有其他方法将我的配置文件从Nuget包中复制到输出目录中?

我真的希望有人能够帮助我解决这个问题。这是我第二次想进行相同的操作,但稍有不同,再一次证明这很难做到。

非常感谢

3个回答

23

如果您按照这里所述,从.csproj文件创建您的NuGet包,则可以在没有.nuspec文件的情况下复制文件。

要将文件从NuGet复制到输出目录,请创建一个名为ProjectName.targets的文件,并包含以下内容:

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

在你的.csproj文件中添加:
<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>

路径和名称当然可以自由选择。
这应该会将所有的 .config 文件复制到一个名为 CopiedLogFiles 的文件夹中,该文件夹位于输出目录中!

2
谢谢你的回答。我刚刚测试了一下,使用 dotnet pack 命令,无论是在 PackageReference 还是 Packages.config 方式下都能正常工作。与我的解决方案略有不同的是:你的方法在 Visual Studio 的解决方案资源管理器中不会创建符号链接,但我认为这并不是什么坏事。 - Jérôme MEVEL
3
我接受这个答案,因为我认为它比我的解决方案更好。使用新的PackageReference,在Visual Studio浏览器中不要出现指向Nuget缓存中实际位置的符号链接是更好的选择。否则,在Visual Studio内更改此文件将同时更改引用相同Nuget包和版本的计算机上的其他项目,这很可能不是开发人员想要的行为。 - Jérôme MEVEL
在创建此目标文件时选择哪种模板/项目类型?(例如,在项目中右键单击,然后选择添加->新项,然后选择什么) - KBNanda
1
我认为目标文件没有模板!它们是普通的 XML 文件。这里提供了一些示例。 - wmorian

9

好的,我终于找到了解决方案,其中包括一个 .nuspec 文件和一个 .targets 文件。

ProjectName.csproj 只需要包含这个即可。

<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>

ProjectName.nuspec中,您可以放置与Nuget包相关的所有内容。
<?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>

最后是ProjectName.targets文件。注意!该文件位于机器的Nuget缓存中。您将能够在Visual Studio项目的根目录中看到它,但在Windows资源管理器中却看不到(至少在Windows上是这样)。因此,如果您在Visual Studio中修改该文件,则会修改引用相同Nuget包(和相同版本)的所有其他项目在此机器上的该文件。

<?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>

我使用nuget pack命令生成Nuget包(现在我有更多经验,我知道dotnet pack在使用.nuspec文件时存在几个bug)。

enter image description here

最后,我只需要安装我的包,在构建过程中,Nlog.config文件将从Nuget缓存中复制到我的项目的输出目录中。

8
我认为如何在使用 .Net Standard 库 .csproj 时将 nuget contentFiles CopyToOutput 值设置为 true?提供了更好的答案。 https://github.com/NuGet/NuGet.Client/pull/1450 您可以在 .csproj 中将PackageCopyToOutput设置为 true,以声明 nuget 内容文件为“CopyToOutput=true”。这样,引用 nuget 的任何项目都会在其引用的 csproj 文件中将内容文件标记为<CopyToOutput>true</CopyToOutput>,指示 msbuild 将内容文件复制到输出目录:
在 nuget 项目的 .csproj 中:
<Content Include="...">
    <PackageCopyToOutput>true</PackageCopyToOutput>
</Content>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接