dotnet core nuget package copying content files on restore
Asked Answered
W

4

10

So I feel like I have come to the end of the rope here, but hoping someone knows more than I do here. I have some Typescript files, though that is mostly irrelevant as I am having this problem with all content files.

I am able to generate a nuget, or more precisely dotnet pack, nuget package that includes my content files in the package by using this in the .csproj of my parent project:

<ItemGroup>
  <Content Include="Scripts\Utility.ts">
    <Pack>true</Pack>
    <PackagePath>contentFiles\Scripts\;content\Scripts</PackagePath>
  </Content>
</ItemGroup>

I can browse the generated .nupkg and see that indeed the file was added to the package in both the content\Scripts and contentFiles\Scripts locations

The problem is that whenver I consume this package in my 'child' project, that Typescript never gets copied into any folder of the child project, though I can see it extracted in the .nuget\packages\parent\... folders.

At first I thought it was something with my initial settings in the parent project, and it may be, but after trying what seems like everything in the book, that fails to copy the content files to the child project. I then tried going the dark path of trying to use Init.ps1 in the tools folder of my package, and though it was impossible to debug, it also seemed to run sporatically (I completely unistalled and reinstalled the package and it still failed to run most of the time.) This could be the way but I don't know why I can't get it to output to the Package Manager Console... maybe there's still hope with Init.ps1 but I can't seem to figure it out. Finally I see some potential with a nuget .targets file but I can's seem to grasp how to use it for my purpose either! I would love some feedback as to how to get this done.

Wellmannered answered 22/3, 2018 at 9:32 Comment(0)
B
6

From: Announcing NuGet 3.1 with Support for Universal Windows Platform

Importing content from a Nuget package was depreciated for projects using a project.json file in Nuget v3.1. Since then the project.json file has been dropped in favour of the new .csproj format. Importing content from a Nuget package should still work though if you're using the packages.config file instead.

Also mentioned is the fact that there are other package managers available for delivering content.

It looks to me like the answer in the new world is to create a node module containing utility.js and let npm deliver it to your project.

Possible Workaround:

I've looked at .targets to copy files and got this working, but it does run on each build - which may or may not be a problem for you. I can't do what I want with it.

image

In [PackageId].targets:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <!-- Either do this for all scripts in the Scripts/js folder -->
    <Target Name="CopyScriptsToProject" BeforeTargets="Build">
        <Message Text="Copying scripts to project" />
        <ItemGroup>
            <SourceScripts Include="$(MSBuildThisFileDirectory)..\..\content\Scripts\js\**\*.*"/>
        </ItemGroup>
        <Copy SourceFiles="@(SourceScripts)" DestinationFiles="@(SourceScripts -> '$(MSBuildProjectDirectory)\wwwroot\js\%(RecursiveDir)%(Filename)%(Extension)')" Condition="!Exists('$(MSBuildProjectDirectory)\wwwroot\js\%(RecursiveDir)%(Filename)%(Extension)')" />
    </Target>
    <!-- Or do this for the individual script -->
    <Target Name="CopyUtilityScriptToProject" BeforeTargets="Build">
        <Copy SourceFiles="$(MSBuildThisFileDirectory)..\..\content\Scripts\js\Utility.js" DestinationFiles="$(MSBuildProjectDirectory)\wwwroot\js\Utility.js" Condition="!Exists('$(MSBuildProjectDirectory)\wwwroot\js\Utility.js')" />
    </Target>
</Project>
<!-- Note: condition can be removed from either if you want it to overwrite each build -->

and in the .csproj file (replacing [PackageId] with the name of your package):

<Project Sdk="Microsoft.NET.Sdk">
    ... any Globals for source control stuff ...
    <PropertyGroup>
        <TargetFramework>netcoreapp2.0</TargetFramework>
        <Version>7.0.0</Version>
        <PackageId>[PackageId]</PackageId>
    </PropertyGroup>
    ... any PackageReference stuff ...
    <ItemGroup Label="Packaging">
        <Content Include="build\netcoreapp2.0\[PackageId].targets" PackagePath="build\netcoreapp2.0\[PackageId].targets" />
            <!-- Either -->
            <Content Include="Scripts\js\**\*.*" PackagePath="content\Scripts\js;contentFiles\Scripts\js" />
            <!-- or -->
            <Content Include="Scripts\js\Utility.js" PackagePath="content\Scripts\js;contentFiles\Scripts\js" />
        </ItemGroup>
</Project> 

There seemed to be a bug whereby when the <PackageId>[PackageId]</PackageId> wasn't set explicitly in the .csproj, the build targets didn't work. Although that may well be an issue with my development environment.

Burgess answered 30/3, 2018 at 13:18 Comment(4)
I would bring the copy of your Possible Workaround here, although unlikely with GitHub, it's possible that it could become a bad link over time. Once you do (and I test that it actually works), I'll accept yours as the Accepted AnswerWellmannered
I've added the workaround here as suggested. Hopefully you'll have some success with the workaround. I think I'm going down the npm route.Burgess
After testing with the help of the NuGet team: github.com/NuGet/Home/issues/6743 we finally got it working, but you were on the right track with your solution. In case anyone is interested, you can find a copy of a working solution here: github.com/NuGet/Home/issues/6743Wellmannered
Does this work in .net 5? I have tried to copy your example but it's not working for me.Thermoluminescence
W
6

Apparently you need the any\any in the path (learn more) as well as to include <PackageCopyToOutput>true</PackageCopyToOutput>, like this:

<ItemGroup>
  <Content Include="Scripts\js\Utility.js">
    <Pack>true</Pack>

    <PackagePath>contentFiles\any\any\wwwroot\js\;content\any\any\wwwroot\js\</PackagePath>

    <PackageCopyToOutput>true</PackageCopyToOutput>
  </Content>
</ItemGroup>

You'll also need to precompile your TypeScript before including the .js files in the package

However, this still doesn't create a file there, just some strange reference to it.

In the end, we got it working with a .targets file, you can find a working repo here: https://github.com/NuGet/Home/issues/6743

Wellmannered answered 22/3, 2018 at 21:14 Comment(0)
B
6

From: Announcing NuGet 3.1 with Support for Universal Windows Platform

Importing content from a Nuget package was depreciated for projects using a project.json file in Nuget v3.1. Since then the project.json file has been dropped in favour of the new .csproj format. Importing content from a Nuget package should still work though if you're using the packages.config file instead.

Also mentioned is the fact that there are other package managers available for delivering content.

It looks to me like the answer in the new world is to create a node module containing utility.js and let npm deliver it to your project.

Possible Workaround:

I've looked at .targets to copy files and got this working, but it does run on each build - which may or may not be a problem for you. I can't do what I want with it.

image

In [PackageId].targets:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <!-- Either do this for all scripts in the Scripts/js folder -->
    <Target Name="CopyScriptsToProject" BeforeTargets="Build">
        <Message Text="Copying scripts to project" />
        <ItemGroup>
            <SourceScripts Include="$(MSBuildThisFileDirectory)..\..\content\Scripts\js\**\*.*"/>
        </ItemGroup>
        <Copy SourceFiles="@(SourceScripts)" DestinationFiles="@(SourceScripts -> '$(MSBuildProjectDirectory)\wwwroot\js\%(RecursiveDir)%(Filename)%(Extension)')" Condition="!Exists('$(MSBuildProjectDirectory)\wwwroot\js\%(RecursiveDir)%(Filename)%(Extension)')" />
    </Target>
    <!-- Or do this for the individual script -->
    <Target Name="CopyUtilityScriptToProject" BeforeTargets="Build">
        <Copy SourceFiles="$(MSBuildThisFileDirectory)..\..\content\Scripts\js\Utility.js" DestinationFiles="$(MSBuildProjectDirectory)\wwwroot\js\Utility.js" Condition="!Exists('$(MSBuildProjectDirectory)\wwwroot\js\Utility.js')" />
    </Target>
</Project>
<!-- Note: condition can be removed from either if you want it to overwrite each build -->

and in the .csproj file (replacing [PackageId] with the name of your package):

<Project Sdk="Microsoft.NET.Sdk">
    ... any Globals for source control stuff ...
    <PropertyGroup>
        <TargetFramework>netcoreapp2.0</TargetFramework>
        <Version>7.0.0</Version>
        <PackageId>[PackageId]</PackageId>
    </PropertyGroup>
    ... any PackageReference stuff ...
    <ItemGroup Label="Packaging">
        <Content Include="build\netcoreapp2.0\[PackageId].targets" PackagePath="build\netcoreapp2.0\[PackageId].targets" />
            <!-- Either -->
            <Content Include="Scripts\js\**\*.*" PackagePath="content\Scripts\js;contentFiles\Scripts\js" />
            <!-- or -->
            <Content Include="Scripts\js\Utility.js" PackagePath="content\Scripts\js;contentFiles\Scripts\js" />
        </ItemGroup>
</Project> 

There seemed to be a bug whereby when the <PackageId>[PackageId]</PackageId> wasn't set explicitly in the .csproj, the build targets didn't work. Although that may well be an issue with my development environment.

Burgess answered 30/3, 2018 at 13:18 Comment(4)
I would bring the copy of your Possible Workaround here, although unlikely with GitHub, it's possible that it could become a bad link over time. Once you do (and I test that it actually works), I'll accept yours as the Accepted AnswerWellmannered
I've added the workaround here as suggested. Hopefully you'll have some success with the workaround. I think I'm going down the npm route.Burgess
After testing with the help of the NuGet team: github.com/NuGet/Home/issues/6743 we finally got it working, but you were on the right track with your solution. In case anyone is interested, you can find a copy of a working solution here: github.com/NuGet/Home/issues/6743Wellmannered
Does this work in .net 5? I have tried to copy your example but it's not working for me.Thermoluminescence
T
2

Serj Sagan's answer got me on the right track, but it wasn't sufficient to deploy the content file to the bin directory (as he noted). I was able to get the file to be deployed by changing the package reference options in the consuming project's .csproj file, as follows:

<PackageReference Include="MyNuGetPackage" Version="0.0.0.1">
  <IncludeAssets>all</IncludeAssets>
  <PrivateAssets>analyzers;build</PrivateAssets>
</PackageReference>

It seems like the default for PrivateAssets is contentfiles;analyzers;build (documentation), which is not what we want in this case.

Tobietobin answered 17/9, 2019 at 23:30 Comment(0)
S
0

Simplified code and explanation from @PurplePiranha

TL;DR:

Basic .NET6 simplified sample code on Github

Step by Step guide

Solution explorer

  1. Selection of the files

First we need to select all the files that needs to get into the nuget package. Add this to the <LibraryPackageName>.csproj:

<Project Sdk="Microsoft.NET.Sdk">
  ...
  <ItemGroup Label="Packaging">
    <Content Include="<Your directory path>\<your file(s)>" />
  </ItemGroup>

Multiple content lines are allowed.

  1. Write a target

Make a target file to copy the files before (or after) the build to the bin directory:

The location and name of this file is important: <root>\build\<LibraryPackageName>.targets

Now, make sure that it will get executed by referencing it in the <LibraryPackageName>.csproj by adding a content line:

<Project Sdk="Microsoft.NET.Sdk">
  ...
  <ItemGroup Label="Packaging">
    <Content Include="build\<LibraryPackageName>.targets" PackagePath="build\<LibraryPackageName>.targets" />

    <Content Include="filesToAdd\*.txt">
      <Pack>true</Pack>
    </Content>
  </ItemGroup>

Eg: From the code in github:

<Project Sdk="Microsoft.NET.Sdk">
  ...
  <ItemGroup Label="Packaging">
    <Content Include="build\PackageToGenerateFile.targets" PackagePath="build\PackageToGenerateFile.targets" />

    <Content Include="filesToAdd/*.txt">
      <Pack>true</Pack>
    </Content>
  </ItemGroup>

NOTE: By copying the files to the bin directory, the files are not part of your version control, but your package is!

  1. Build and pack

In Visual Studio, right-click on the package name and select "Pack". A new nuget package should be created in the bin directory of your library.

  1. Use the nuget package

Install the nuget package now in your destination package.

Notice that the files are in the solution explorer, but not in a directory on your disk. They have a shortcut symbol.

  1. Build the destination package

Check the bin directory. The files should be copied to the location mentioned in the targets.

Selmner answered 14/1, 2022 at 16:39 Comment(1)
The files are copied as a link, and visible only via visual studio but not physically copy.Hackney

© 2022 - 2024 — McMap. All rights reserved.