Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NuGet v4 contentFiles not getting copied to output

I'm having trouble with NuGet copying files to output, using NuGet v4 CLI.

My directory structure looks like this:

repo
repo\CodeAnalyzer.nuspec
repo\CodeAnalyzer.props
repo\contentFiles\any\any\StyleCop.ruleset

Here's my CodeAnalyzer.props file:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <PackageName>CodeAnalyzer</PackageName>
    <PackageVersion>0.1.0</PackageVersion>
  </PropertyGroup>
  <PropertyGroup>
      <CodeAnalysisRuleSet>StyleCop.ruleset</CodeAnalysisRuleSet>
  </PropertyGroup>
</Project>

And CodeAnalyzer.nuspec file:

<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
  <metadata>
    <id>CodeAnalyzer</id>
    <description>Provides standard code analyzer tooling and customisation.</description>
    <authors>Me</authors>
    <version>0.1.0</version>
    <dependencies>
      <dependency id="StyleCop.Analyzers" version="1.1.118" />
    </dependencies>
    <contentFiles>
      <files include="any/any/*" buildAction="Content" copyToOutput="true" flatten="false" />
    </contentFiles>
  </metadata>
  <files>
    <file src="contentFiles\any\any\StyleCop.ruleset" target="contentFiles" />
    <file src="CodeAnalyzer.props" target="build" />
  </files>
</package>

On CLI I am packing using:

.\nuget.exe pack .\CodeAnalyzer.nuspec

In the resulting .nupkg, there is a contentFiles directory with the StyleCop.ruleset file in, so that works.

If I then install the package in a project, it picks up on the StyleCop.Analyzers dependency fine, and it looks for the StyleCop.ruleset file, so the .props file is fine.

But the StyleCop.ruleset file isn't placed anywhere.

Feel like I've tried a lot of changing buildActions to None/Content, copyToOutput to true/false, different paths for the contentFiles.file element and the file.file element.

Edit: the consuming project is using PackageReferences for NuGet packages, not packages.config.

like image 602
jacob21 Avatar asked Nov 08 '19 13:11

jacob21


2 Answers

contentFiles with PackageReference works fundamentally differently to content with packages.config. With packages.config, you must use tooling (Visual Studio) to install packages, and at install-time, NuGet will copy content to the project directory and whatever other changes it needs to (like telling the Project System to modify the csproj). With packages.config restore is conceptually different to install, and all restore does is unzip the package in the expected location so that the lib/<tfm>/*.dll assemblies are available at compile-time.

With PackageReference it's possible to hand edit the csproj without tooling having any knowledge this happened. This means when NuGet does a restore, it has no idea if this is the first time a particular package was restored for the project, or just the first time on this computer (in fact, NuGet doesn't even check the existing state of the project on the machine, so currently it has no idea if the package has been previously restored in the project on the current computer). Although you can still use tooling (Visual Studio, or dotnet add package), this is nothing more than a convenient way to edit the csproj. This is a long way to say that the concept of install vs restore no longer exists with PackageReference. There is now only restore.

Since the concept of install no longer exists with PackageReference, there's a big question about how to do content files, if NuGet were to copy files to the consuming project's directory. If NuGet copies the file every restore, it means if the developers of the project modify the file, they lose their changes on the next NuGet restore. If NuGet doesn't overwrite files, then when the project changes the package version, the new content from the new package will not be updated.

So, the way that contentFiles works is that NuGet tells the build system about the files, and they're used as links from the global packages folder. At build time for .NET Framework TFMs, or at publish time for .NET Core TFMs, the files are copied to the output directory. But at no time are they ever copied to the project directory. If using Visual Studio, they should appear in Solution Explorer, but if you click a file to open it, it opens in read-only mode, since it would be bad to make project-specific changes to the global packages folder, which is used by all projects on the computer. So, conceptually it's similar to copying the file on every build, and in order to discourage developers of the consuming project from making changes, it doesn't bother copying the files to the directory. It also means there's less to check into source control.

Since the content file you're using is a build-time asset, not a runtime asset, I suggest that contentFiles is not the right place to put your ruleset file. I'd just put it in the build/ directory next to your props file. Since your props file doesn't specify a path to your ruleset file, I believe it tells MSBuild to look in the props file's directory, so it'll just work. Worst case you change it to $(MSBuildThisFileDirectory)StyleCop.ruleset. This is under the assumption you're fine with the consuming projects not being able to modify the ruleset file. If you do wish them to be able to modify the file, then NuGet is not an appropriate delivery mechanism, and I suggest you look at dotnet new item templates instead.

like image 58
zivkan Avatar answered Sep 18 '22 16:09

zivkan


I was have this issue, when i had turned lot of changes and nothing worked. And finally solved by using PackageReferences

packages.config projects use content/ PackageReference projects use contentFiles/

You can find more about immutable content here:

ContentFilesExample

nuget-contentFiles-demystified

  1. Existing project with nuget references via packages.config
  2. Installed NuGet package with content files
  3. Build project
  4. No content files in output directory

  5. Conversion of packages.config to PackageReferences
  6. Build project
  7. Content files have been copied to output directory

IDE is Visual Studio 2017. The project is an application project which means it is in the old csproj format.

Updated Answer

The GeneratePathProperty attribute was added to PackageReference

<PackageReference Include="EO.Pdf" GeneratePathProperty="true">
    <Version>19.0.83</Version>
</PackageReference>

Check your \obj\.csproj.nuget.g.props to verify the variables are generated

Variables are prefixed with Pkg. It sounds like they are automatically generated for all nuget packages with a tools folder.

like image 41
Vignesh Kumar A Avatar answered Sep 20 '22 16:09

Vignesh Kumar A