Tooling or method to re-add 'content' to projects utilizing "Package References"

463 Views Asked by At
  1. A project (original VS format) is converted to use Package References. Great.

  2. Project References "don't support content". Questionable choice, although not debated here.

  3. Some used packages contain "content" and cannot or will not be updated to use "content files". Hmm..

What semi-automated tooling / method can be used to copy the "content" from NuGet dependencies that "don't support contentFiles"+? Only applying to direct packages is fine.


+Naturally, one could manually open up each individual NuGet file and copy the contents. This question isn't about "why" the switch was made and/or any merits or trade-offs and/or how how packages "should" be authored. The question is about an automated or semi-automated method to be able to restore the "content" into a project's source tree.

2

There are 2 best solutions below

1
On BEST ANSWER

It is possible to use "content"-based NuGet packages with original Visual Studio / MSBuild projects that have been converted to use Package References.

The presented solution can likely be amended for SDK-style projects as well. The feat is accomplished by utilizing the GeneratePathProperty attribute of PackageReference+. Using the generated Pkg* path properties ensures valid paths to the referenced packages. (Note: if a package name contains ., replace it with _ in the Pkg* property name.)

First, add a GeneratePathProperty to all the packages with content to copy or link.

<PackageReference Include="bootstrap" GeneratePathProperty="true">
  <Version>Don't ask..</Version>
</PackageReference>
<PackageReference Include="AngularJS.Core" GeneratePathProperty="true">
  <Version>..it's not great</Version>
</PackageReference>

"Get the Content"

Depending on needs there are two different approaches presented here. While these approaches can be used together, neither approach should be applied to "content files"-based NuGet packages.

Approach #1: Copy Content to Project Source

With this approach it's expected that the files are added to source control. The following lines in the project can thus be un-commented (and re-commented) when updating packages to ensure that the newest NuGet package content.

It uses the Copy Task to, well, copy the the content.

<ItemGroup>
  <NugetContentToRestore Include="
                         $(Pkgbootstrap)\Content\**\*.*;
                         $(PkgAngularJS_Core)\Content\**\*.*;
                         " />
</ItemGroup>
<Target Name="BeforeBuild">
  <Copy SourceFiles="@(NugetContentToRestore)"
        DestinationFiles="@(NugetContentToRestore->'$(ProjectDir)\%(RecursiveDir)%(Filename)%(Extension)')" />
</Target>

If the ItemGroup is not commented out during normal development, the items will be included in the the project’s solution explorer root, which is a bit ugly. It’s also not possible to detect when "content" has been removed.

Approach #2: Link in Content

If none of the content is to be copied and/or checked into source control along with the project, it can also be linked in. This approach has the benefit of always linking in the latest content and does not pollute the solution explorer.

A local file explicitly included in the project will take precedence over the linked resources.

For a Normal Project (and bin-deployed files)

For a normal project, <CopyToOutputDirectory> is sufficient and the linked files will go in the 'bin' output. However, this does not work for a Web Project which only understands files which are physically present in the project tree.

<ItemGroup>
  <Content Include="$(Pkgbootstrap)\Content\**\*.*">
    <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  </Content>
  <Content Include="$(PkgAngularJS_Core)\Content\**\*.*">
    <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  </Content>
</ItemGroup>

For a Web Project (requires local files)

For a Web Project, these links must be copied to local files: <CopyToOutputDirectory> is not sufficient or required. Packages with content that should go into the bin output should use the approach above.

<ItemGroup>
  <Content Include="$(Pkgbootstrap)\Content\**\*.*">
    <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
  </Content>
  <Content Include="$(PkgAngularJS_Core)\Content\**\*.*">
    <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
  </Content>
</ItemGroup>

Then add a target to copy the linked files so they are picked up by the Web Project tooling. See Copying linked content files at each build using MSBuild. It may be relevant so skip certain links and add source control ignore entries.

<Target Name="CopyLinkedContentFiles" BeforeTargets="Build">
  <Copy SourceFiles="%(Content.Identity)"
        DestinationFiles="%(Content.Link)"
        SkipUnchangedFiles='true'
        OverwriteReadOnlyFiles='true'
        Condition="'%(Content.Link)' != ''" />
</Target>

+This works in an up-to-date Visual Studio 2019 environment. It may not work in certain older MSBuild/NuGet configurations as Pkg* property generation is required.

5
On

Tooling or method to re-add 'content' to projects utilizing “Package References”

I am afraid that there is no such tool to make files from content folder into projects with PackageReference nuget management format.

As the user of the package, there is currently no tool to copy the files of the content folder to the projects with PackageReference.

And actually, if there is such a tool, it also violates the mechanism of nuget. All of them are depended on the author of the nuget package rather than the users unless the package is authored by us

Suggestion

So you have to manually unzip the nuget package and copy the files into your project.

And it seems to be a bit complex and if you still want to get a easy way to get what you want, I suggest you could suggest a feature on our User Voice Forum.

enter image description here

And after that, you could share the link here and anyone who is interested in it will vote it so that it will get more Microsoft's attention. All of these will help get what you want as soon as possible.