1

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

Mr Qian
  • 21,064
  • 1
  • 31
  • 41
  • Did you install the new package on non-sdk project or a new-sdk project? – Mr Qian Jan 27 '21 at 10:04
  • If I understand you correctly: I would like the project to be able to be installed in a project still using the pacakges.config, as well as PackageReference, but also be installed in .NET Standard and .NET Core. – Stephan V.d Westhuizen Jan 27 '21 at 11:49

1 Answers1

4

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>
Mr Qian
  • 21,064
  • 1
  • 31
  • 41
  • I cannot add the 7za.exe as Content outside of the Item Groups with Conditions. If I add it in the same place as I add the Fonts, the $(Pkg7ZipCLI) variable has not yet been set and it points to C:\tools. Also after I did everything in the suggested solution, the 7za.exe did not appear in the NuGet package and was also not copied to either Consuming project (.NET Standard and .NET Framework with package.config) – Stephan V.d Westhuizen Jan 28 '21 at 09:13
  • you can ignore that.What you should do is to add a props file and then pack the props file into build folder of nupkg. That is the improvement. – Mr Qian Jan 28 '21 at 11:32
  • I did this exactly as you stated, this does still not work, as the $(Pkg7ZipCLI) is not defined, and it throws an error at buil time, so the NuGet project cant build, thus the NuGet package is never created. If I do not include the 7za.exe as Contnet, it is not copied over even if I use the .props file. (A hardcoded reference to the .NuGet folder works however, but that is not a valid aproach) – Stephan V.d Westhuizen Jan 28 '21 at 13:47
  • Wait for a moment and I have a workaround for you. – Mr Qian Jan 29 '21 at 02:07
  • I have updated my answer and you can check it. – Mr Qian Jan 29 '21 at 03:33
  • Thanks a lot!!! This worked perfectly thank you. I just need to add the copied 7za.exe to my .gitnore, as it is copied upon build, but works perfectly in the CI/CD. If I install the package into a project using packages.config, the resources are not marked as Copy always/if newer, is it possible to do that in the .props file? – Stephan V.d Westhuizen Jan 29 '21 at 06:45
  • 1
    Actually, it could be done. The function of props file works for non-sdk project with `packages.config`. The file is for that. And although the `7za.exe` is not set to `Copy Always` as the UI shows, it is actually in the output folder. – Mr Qian Jan 29 '21 at 06:51