Search code examples
c#visual-studionugetcsprojmultitargeting

Creating a NuGet package with .exe file as content file


I have been trying to include the 7za.exe file from the installed 7ZipCLI NuGet package into a project, of which I want a NuGet package to be created. The NuGet package targets both.NET Standard2.0 and .NET Framework v4.6.1+. I am using the Net Standard capability of creating NuGet packages on Build.

The Fonts copy perfectly to the NuGet package, but the .exe does not want to copy.

Can someone please help me get the 7za.exe to be placed in the package's content folder? It does not need to be the content folder, I just want it copied to the consuming project's output directory on Installing of the NuGet package.

Here is the .csproj file:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFrameworks>netstandard2.0;net461</TargetFrameworks>
    <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
    <Version>1.1.13</Version>
    <TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);IncludeAssemblyReferences</TargetsForTfmSpecificBuildOutput>
    <Platforms>x86</Platforms>
    <PackageId>DocumentGeneration</PackageId>
    <Authors>Me</Authors>
    <Company>Me</Company>
    <Product>DocumentGeneration</Product>
    <Description>A library for creating PDF documents.</Description>
    <PackageTags>Report Certificate Generator iText PDF</PackageTags>
    <AssemblyVersion>1.0.0</AssemblyVersion>
    <FileVersion>1.0.0.0</FileVersion>
    <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
  </PropertyGroup>

  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'">
    <OutputPath>$(SolutionDir)Output\</OutputPath>
    <DocumentationFile>$(SolutionDir)Output\DocumentGeneration.xml</DocumentationFile>
    <GenerateResourceUsePreserializedResources>true</GenerateResourceUsePreserializedResources>
    <DebugType>full</DebugType>
    <DebugSymbols>true</DebugSymbols>
  </PropertyGroup>

  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'">
    <OutputPath>$(SolutionDir)Output\</OutputPath>
    <DocumentationFile>$(SolutionDir)Output\DocumentGeneration.xml</DocumentationFile>
    <GenerateResourceUsePreserializedResources>true</GenerateResourceUsePreserializedResources>
    <DebugType>embedded</DebugType>
    <DebugSymbols>true</DebugSymbols>
  </PropertyGroup>

  <ItemGroup>
    <Content Include="Resources\Arial.ttf">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
      <PackageCopyToOutput>true</PackageCopyToOutput>
      <CopyToPublishDirectory>true</CopyToPublishDirectory>
    </Content>
    <Content Include="Resources\HelveticaNarrow.otf">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
      <PackageCopyToOutput>true</PackageCopyToOutput>
      <CopyToPublishDirectory>true</CopyToPublishDirectory>
    </Content>
  </ItemGroup>
  
  <ItemGroup Condition="'$(TargetFramework)' == 'net461'">
    <PackageReference Include="iTextSharp" Version="5.5.13.2" />
  </ItemGroup>
  <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0'">
    <PackageReference Include="itextsharp.netstandard" Version="5.5.13.2" GeneratePathProperty="true" />
    <PackageReference Include="System.Drawing.Common" Version="5.0.0" GeneratePathProperty="true" />
    <PackageReference Include="System.Resources.Extensions" Version="5.0.0" />
    <PackageReference Include="System.Resources.ResourceManager" Version="4.3.0" />
  </ItemGroup>
  
  <ItemGroup>
    <PackageReference Include="7ZipCLI" Version="9.20.0" GeneratePathProperty="true" IncludeAssets="all" />
    <PackageReference Include="System.Text.Encoding" Version="4.3.0" />
    <PackageReference Include="System.Text.Encoding.CodePages" Version="5.0.0" />
  </ItemGroup>

  <ItemGroup>
    <None Include="readme.txt" pack="true" PackagePath="." />
  </ItemGroup>
  
  <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
    <None Include="$(Pkgitextsharp_netstandard)\lib\netstandard2.0\itextsharp.netstandard.dll">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
      <PackageCopyToOutput>true</PackageCopyToOutput>
      <Visible>false</Visible>
    </None>
    <Content Include="$(Pkg7ZipCLI)\tools\7za.exe">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
      <PackageCopyToOutput>true</PackageCopyToOutput>
      <PackagePath>content\Resources;contentFiles\any\any\Resources</PackagePath>
      <Visible>false</Visible>
    </Content>
  </ItemGroup>

  <ItemGroup Condition="'$(TargetFramework)' == 'net461'">
    <Content Include="$(Pkg7ZipCLI)\tools\7za.exe">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
      <PackageCopyToOutput>true</PackageCopyToOutput>
      <PackagePath>content\Resources;contentFiles\any\any\Resources</PackagePath>
      <Visible>false</Visible>
    </Content>
  </ItemGroup>
    
  <ItemGroup>
    <Compile Update="Properties\Resources.Designer.cs">
      <DesignTime>True</DesignTime>
      <AutoGen>True</AutoGen>
      <DependentUpon>Resources.resx</DependentUpon>
    </Compile>
  </ItemGroup>

  <ItemGroup>
    <EmbeddedResource Update="Properties\Resources.resx">
      <Generator>PublicResXFileCodeGenerator</Generator>
      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
    </EmbeddedResource>
  </ItemGroup>
    
  <Target Name="IncludeAssemblyReferences">
    <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
      <BuildOutputInPackage Include="$(Pkgitextsharp_netstandard)\lib\netstandard2.0\itextsharp.netstandard.dll">
        <Visible>false</Visible>
      </BuildOutputInPackage>
    </ItemGroup>
  </Target>

</Project>

Also here is what the NuGet package looks like currently:

Current NuGet Package


Solution

  • If you just want the file be output into the bin folder under packages.config, you should have to use <packages_id>.props file and <PackageCopyToOutput>true</PackageCopyToOutput> is only for PackageReference.

    Also, <CopyToOutputDirectory>Always</CopyToOutputDirectory> is only for local file under the local project rather than the nuget package. And it does not work for nuget package.

    Solution

    1) add a file called DocumentGeneration.props(<packages_id>.props and your packageID is DocumentGeneration) under build folder

    enter image description here

    2) modify your DocumentGeneration.csproj file:

    .....
    
          <Content Include="$(Pkg7ZipCLI)\tools\7za.exe">
               <CopyToOutputDirectory>Always</CopyToOutputDirectory>
               <PackageCopyToOutput>true</PackageCopyToOutput> 
               <PackagePath>content\Resources;contentFiles\any\any\Resources</PackagePath>
               <Visible>false</Visible>
           </Content>
    
            <None Include="build\*.*">
               <Pack>true</Pack>
               <PackagePath>build</PackagePath>
            </None>
        
        
    .....
    

    3) add these on the DocumentGeneration.props file:

    <Project>
        <ItemGroup>
    
            <None Include="$(ProjectDir)Resources\*.*">
                <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>           
            </None> 
        </ItemGroup>
    
    </Project>
    

    Or

    <Project>
            <None Include="$(MSBuildThisFileDirectory)..\content\**\*.*">
                <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
                <Link>Resources\%(FileName)%(Extension)</Link>
            </None>
     </Project>
    

    4) repack your lib project. When you install this new release version, you should first clean nuget caches or delete all cache files under C:\Users\xxx(current user)\.nuget\packages.

    Besides, there is a similar issue.

    Update

    I think it is an issue for multi-targetframeworks feature. And if you use GeneratePathProperty for the pkg, the property will lost on the other targetframework. It is quite strange and when you change to use <TargetFramework>, it works well. Since the issue is under TargetFrameworks with GeneratePathProperty=true. I have no other good idea for that and cannot find the good solution to fix them.

    enter image description here

    And you should report it on DC Forum or raise an issue on github. And when you finish it, you could share it here.

    1) Also, since you use multi-targetframeoworks, you should also use buildCrossTargeting folder on the nupkg to enable the props file. See this similar issue.

    2) Second, when I use these to distinguish between them, nuget also cannot pack it. And it is so strange, and I have to remove that condition.

    <ItemGroup  Condition="'$(TargetFramework)'=='net461'">
    ...
    <None Include="7za.exe" Pack="true" PackagePath="content\Resources;contentFiles\any\any\Resources">
    ...
    </ItemGroup>
    
    
    <ItemGroup  Condition="'$(TargetFramework)'=='netstandard2.0'">
    ...
    <None Include="7za.exe" Pack="true" PackagePath="content\Resources;contentFiles\any\any\Resources">
    ...
    </ItemGroup>
    

    My thought is that content and contentFiles node target to the whole project and you cannot specify a condition with targetframeworks during packing them as content. And what I know is that content and contentFiles are targets to the whole and cannot target to a targetframework under multi-targetframeworks.

    You should use these:

    <Project Sdk="Microsoft.NET.Sdk">
    
        <PropertyGroup>
            <TargetFrameworks>netstandard2.0;net461</TargetFrameworks>
        </PropertyGroup>
    
    ...
        <ItemGroup>
            <PackageReference Include="7ZipCLI" Version="9.20.0" GeneratePathProperty="true">
            </PackageReference>
        </ItemGroup>
    
    ...
    
        <Target Name="FunCopy" BeforeTargets="PrePareForBuild">
            <Copy SourceFiles="$(Pkg7ZipCLI)\tools\7za.exe" DestinationFolder="$(ProjectDir)"></Copy>
        </Target>
    
        
        <ItemGroup>
            <None Include="$(ProjectDir)7za.exe" Pack="true" PackagePath="content\Resources;contentFiles\any\any\Resources">
                <PackageCopyToOutput>true</PackageCopyToOutput>
                <Visible>false</Visible>
                <CopyToOutputDirectory>Always</CopyToOutputDirectory>
            </None>
        </ItemGroup>
        
        <ItemGroup>
    
            <None Include="build\*.*" Pack="true" PackagePath="build;buildCrossTargeting"></None>
            
        </ItemGroup>
            
    ...
    </Project>