Create nuget containing shared project properties - automatic references - visual-studio

We would like to create a nuget that contains an msbuild properties (.props) file. We do this by creating a nuspec which as the following MIL (most important line) :
<files>
<file src="SharedProperties.props" target="build\SharedProperties.props" />
</files>
How can we change our .nuspec definition so that a project (.csproj) that references this nuget will automatically include the property file ("like" line 3 in this snippet):
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\Shared\SharedProperties.props" />
(Is this even possible ?)

That is a feature of the nuget package design. And nuget has the automatic import targets mechanism. See this document.
The tip is that you should name the targets or props file to <package_id>.props or targets and then pack the file into build folder. That is all.
For an example, I created a lib project and then use this nuspec file to pack:
<?xml version="1.0" encoding="utf-8"?>
<package >
<metadata>
<id>test_A</id>
<version>1.0.0</version>
<title>me</title>
<authors>me</authors>
......
</metadata>
<files>
<file src="test_A.props" target="build" />
</files>
</package>
If my package is called test_A.1.0.0.nupkg, the file should be named as test_A.props file.
Then, when you install the nuget package into a new project, you can check the <new project>.csproj file, the props file is added automatically during the nuget installation.
If you use PackageReference nuget management format to install the nuget package, the file is added under obj\xxx.csproj.nuget.g.props or obj\xxx.csproj.nuget.g.targets file:
For new-sdk project, that also work. If you create a new-sdk class library project, you could use this into csproj file to pack it:
<None Include="<packages_id>.props" Pack="true" PackagePath="build">
When you finish it, install the new package into new-sdk main project, you will find that the props file has imported automatically under obj\xxx.csproj.nuget.g.props file.

According to the documentation the ".props" file should be automatically added to the beginning of a .csproj as an import (a .targets file should go to the end). However, for the new sdk-style projects this doesn't seem to work?

Related

NuGet to copy files into the project directory

I've created a .nuspec file which packages a bunch of .proto files for sharing between projects. This is great. Unfortunately, for the .proto files to be built, they need to be actually copied over to the project directory, not just referenced. Note that this is a .NET Core project.
Right now my definition creates a package that, when used, references the files in the project, but these files still reside in the original .Nuget folder, and that's not really what I need.
Here's the .nuspec definition that I've got right now:
<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>GRPCProtoFiles</id>
<version>1.0.0</version>
<authors>Author</authors>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>.proto files NuGet package.</description>
<dependencies>
<dependency id="Google.Protobuf" version="3.14.0"/>
<dependency id="Grpc" version="2.35.0"/>
</dependencies>
<contentFiles>
<files include="any/any/Protos/**/*.proto" buildAction="None" copyToOutput="false" />
</contentFiles>
</metadata>
<files>
<file src="**\*.proto" target="contentFiles\any\any\Protos" exclude="google\**" />
</files>
</package>
I know that "build action" is currently set to "none", but I can get that fixed once the files land in the correct folder upon package installation. As is, if I try to set the build action for the proto files to the correct one (which is Protobuf Compiler) then I get an error, because the files aren't where they're supposed to be.
What am I doing wrong?
EDIT:
OK, I've read up a bit on what might be happening and why. Specifically this answer gave me a lot of insight. However I'm left wondering how I can actually do what I need to do. Right now it seems that what I want to do is impossible with NuGet and I'm using the wrong tool for the job. But besides this one limitation NuGet seems ideal - it's able to pick just the .proto files from the source project and just publish that rather then pre-built libraries for various target systems.
I could, possibly, add a pre-build action to copy the files to the project dir, but I've no idea how to reference the source NuGet package folder (especially if the version changes). Any idea if this problem even has a solution?
I really know your ideas. First, make these proto files be packed into nupkg with Build Action None. And then use pre-build event under the main project to handle the files from the nupkg. The question is that how to use msbuild property to get files from nupkg since Net Core projects use Link properties to references the files under the main project rather than copy the real nuget files into the main project folder.
And under the Link properties, you could not change the Build Action of that file because it actually does not exist under the main project folder and the main project has no duty to handle the file.
There are two functions to solve it:
=============================================
Function one
You could did this directly under nupkg itself and after that, the proto files will be copied automatically during build process with the nuget package. Try the following steps and you should not add any copy task from another main project which install the nuget package.
You have to use <packages_id>.props/targets file into nupkg to get what you want. Refer to this official document.
1) create a file called <packages_id>.props file. In your side, it should be named as GRPCProtoFiles.props so that it will work.
2) add these on the GRPCProtoFiles.props file:
<Project>
<Target Name="CopyFiles" BeforeTargets="Build">
<ItemGroup>
<File Include="$(MSBuildThisFileDirectory)..\contentFiles\**\*.*"></File>
</ItemGroup>
<Copy SourceFiles="#(File)" DestinationFolder="$(ProjectDir)\Protos"></Copy>
</Target>
</Project>
3) add these on the GRPCProtoFiles.nuspec file to include the props file into nupkg:
<file src="build\*.props" target="build" />
The whole GRPCProtoFiles.nuspec file should be like this:
<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>GRPCProtoFiles</id>
<version>1.0.0</version>
<authors>Author</authors>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>.proto files NuGet package.</description>
<dependencies>
<dependency id="Google.Protobuf" version="3.14.0"/>
<dependency id="Grpc" version="2.35.0"/>
</dependencies>
<contentFiles>
<files include="any/any/Protos/**/*.proto" buildAction="None" copyToOutput="false" />
</contentFiles>
</metadata>
<files>
<file src="xxx.proto" target="contentFiles\any\any\Protos" exclude="google\**" />
<file src="build\*.props" target="build" />
</files>
</package>
4) then re-pack the new version of nuget project. Before you install the new release version, please clean nuget caches first or just delete all files under C:\Users\xxx\.nuget\packages
After the installation of the nuget package, please click Build button to execute the target.
More similar to this issue.
====================================================
Function Two
You could not do any internal steps for nupkg itself. You could directly copy the file from the external main project.
1) do not do any steps under Function One for your nuget package. Use your old nuget package.
Modify the Project A's csproj file which has installed GRPCProtoFiles nuget package.
Add <GeneratePathProperty>true</GeneratePathProperty> for PackageReference GRPCProtoFiles to generate an exclusive property as $(PkgGRPCProtoFiles) to get the path of the nupkg's content.
The whole is like this:
<ItemGroup>
<PackageReference Include="GRPCProtoFiles" Version="1.0.0">
<GeneratePathProperty>true</GeneratePathProperty>
</PackageReference>
</ItemGroup>
Then, right-click on the Project A-->Properties-->Build Events-->add these under Pre-build event Command Line:
xcopy /s /e /y /i $(PkgGRPCProtoFiles)\contentFiles\any\any\Protos $(ProjectDir)\Protos
Or, add this target into ProjectA.csproj file:
<Target Name="CopyFiles" BeforeTargets="PrepareForBuild">
<ItemGroup>
<File Include="$(PkgGRPCProtoFiles)\contentFiles\**\*.*"></File>
</ItemGroup>
<Copy SourceFiles="#(File)" DestinationFolder="$(ProjectDir)\Protos"></Copy>
</Target>

Prevent content files to be added on Nuget restore

We have some executables which we need to create our setups. So we have packed
the external dependencies which are some .exe files into a nuget package. But on NuGet restore they are added to project root.
How can we achieve this ?
Have searched around but haven't found any solution so far.
Since we use nuspec file, this is what i have it as:
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>VCRedistributable</id>
<version>$version$</version>
<title>VCRedistributable</title>
<authors>--</authors>
<owners>--</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>InstallVCRedistributable assemblies</description>
<contentFiles>
<files include="**" exclude="**" buildAction="None" copyToOutput="false"
/>
</contentFiles>
</metadata>
<files>
<file src="VC\x86\*.*" target="content\x86" />
<file src="VC\x64\*.*" target="content\x64" />
</files>
Any ideas ?
Prevent content files to be added on Nuget restore
You should target to the tools folder instead of content folder.
So, your .nupsec file should be:
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>VCRedistributable</id>
<version>$version$</version>
<title>VCRedistributable</title>
<authors>--</authors>
<owners>--</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>InstallVCRedistributable assemblies</description>
</metadata>
<files>
<file src="VC\x86\*.*" target="tools\x86" />
<file src="VC\x64\*.*" target="tools\x64" />
</files>
</package>
That because the content directory is a convention-based working directory, which contents are copied to the project root:
Convention-based working directory:
Besides, if you nuget package just include external some .exe files, you do not have to add the contentFiles label, this label is used for the content file for packagereference.
<contentFiles>
<files include="**" exclude="**" buildAction="None" copyToOutput="false"
/>
</contentFiles>
If you are interested in, you can check this document for some more details.
Update:
Is it good convention to create our own folder structure other than
NuGet defined since based on the tools folder description from above
it seems they will be accessible via Package Manager Console.
Of course, you can use your own folder structure other than NuGet defined. But you need to notice that there will be a limit to do this. You can NOT just include your own folder structure, you need also need add a NuGet defined folder structure in your .nuspec, otherwise, nuget will install failed with the error like:
Could not install package 'MyCustomPackage 1.0.0'. You are trying to
install this package into a project that targets
'.NETFramework,Version=v4.6.1', but the package does not contain any
assembly references or content files that are compatible with that
framework.
Because nuget did not detect that you added assembly references or content files to the project.
Hope this helps.

Automatically add project dependency metadata to manually created nuspec file

When I nuget pack a web project I want to specify custom unpack locations for content and maintain project dependency metadata.
Given the following manually created example nuspec file:
<?xml version="1.0"?>
<package>
<metadata>
<id>Web.MyApp</id>
<version>1.0</version>
<title>Web.MyApp</title>
<authors>Chris</authors>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Nuget package containing files for Web.MyApp</description>
<releaseNotes>release notes</releaseNotes>
<copyright>Copyright Chris 2017</copyright>
<tags />
<dependencies />
</metadata>
<files>
<file src="bin\**\*.*" target="bin" />
<file src="views\**\*.*" target="views" />
<file src="content\" target="content" />
<file src="scripts\" target="scripts" />
<file src="Global.asax" target="" />
<file src="*.config" target="" />
</files>
</package>
This allows me to specify custom unpack locations for bin, content, sprint folders etc but I want the project dependency metadata to be automatically maintained. I don't want to edit the nupsec each time a new dependency is referenced.
As an attempt to resolve this problem I tried to nuget pack the csproj file instead of the nuspec. This maintained the dependency metadata however it made specifying content unpack locations much trickier. I can do the following:
<Content Include="Content\dist\images\brand-logo.svg">
<Pack>true</Pack>
<PackagePath>Content\Content\dist</PackagePath>
</Content>
But I couldn't find an elegant solution for the bin folder. I just feel like I'm doing something fundamentally wrong.
So my question is, how can I automatically maintain project dependency metadata when creating a nuget package from a manually created nuspec file?
The pack command I am using:
..\tools\nuget\nuget.exe pack $project + ".nuspec" -IncludeReferencedProjects -
Properties Configuration=Release -Verbosity quiet -NonInteractive -
OutputDirectory "$packagedirectory" -Version $buildVersion -Symbols
Automatically add project dependency metadata to manually created nuspec file
If I understand you correct, I am afraid you have already automatically add project dependency metadata to manually created .nuspec file. You just need to rebuild the project and re-pack the .nuspec file.
When you include the referenced files with wildcard, it will contain the new added project references:
<file src="bin\**\*.*" target="bin" />
Add a new project reference to the project, then re-build the project, the dll file of referenced project will be copied to the \bin folder, So we just need to re-pack the .nuspec file, the referenced project metadata will included in the new created package.
For example, add a Atest reference project to Web.MyApp project, then rebuild the project and re-pack the .nuspec file:
If I misunderstand you, please let me know for free.

nuspec contentFiles not added to a project

I have a web project (mvc5) with a project.json inside.
Also, I have a nuget package. Inside this package (besides the dll reference) I have some Content files (cshtml files, css, javascript etc).
There are 2 goals to achieve:
After installing the package to the project I want to get Content Files included to the project.
After Building the project I want nuget to restore the content files
The nuspec file:
<?xml version="1.0"?>
<package>
<metadata>
/.../
<dependencies>
<group targetFramework="net461">
/.../
</group>
</dependencies>
<contentFiles>
<files include="**/*.*" flatten="false" copyToOutput="false" buildAction="Content" />
</contentFiles>
</metadata>
<files>
/.../
<file src="..\src\Content\**\*.*" target="contentFiles\any\any\Content" />
<file src="..\src\Views\**\*.*" target="contentFiles\any\any\Views" />
</files>
</package>
Well, Visual Studio is adding nuget package with no issues. The reference is also included. But the content is always missing. The content files are not copied from the /packages/ folder and are not included to the project.
The same is when I build the project - it just copies the dll and does not touch content files from the package at all.
It only works if I have copyToOutput="true", but all content goes to bin folder, of course.
The reason why I need to restore the content files is that the files from the nuget package are ignored by a tfs 'ignore file' (.tfignore). I don't want to have these content files to be a part of the web project in TFS. However, of course, TFS build server fails to build the project, because some content files are missing (the files from the package). So I want nuget on the build server to restore the content files before it stats building the project.
Any idea how to get this work if it is possible? Or maybe this is not possible and I must have content files to be included to the project?
you could add a msbuild target to your package. in this target you can execute the nuget restore command...
example:
<?xml version="1.0"?>
<package >
<metadata>
<!-- ... -->
</metadata>
<files>
<!-- Include your MSBuild target to \build -->
<file src="build\myNuGetRestoreTarget.targets" target="build" />
</files>
</package>
see:
https://learn.microsoft.com/en-us/nuget/create-packages/creating-a-package#including-msbuild-props-and-targets-in-a-package
This is by design. NuGet packages are no longer supposed to modify the project's source but only add to its built output or build process (in addition to .NET libraries). The content folder for adding sources to the project is only continued to be supported for packages.config based projects since it would be a breaking change for existing projects, but projects using project.json or PackageReference (VS 2017) get the new behaviour.
I was in the situation. Here's how I handled it.
Don't keep your files as content, content files are added to project files. Rename the target to something else. I kept it as drop. Your nuspec file should be like:
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
<metadata>
<id>ID</id>
<version>1.0</version>
<title>Title</title>
<authors>J</authors>
<owners>J</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Binary Files such as images, fonts, icons and svgs</description>
<copyright>J</copyright>
</metadata>
<files>
<file src="..\..\Internal.Resources.targets" target="build" />
<file src="..\..\**\*.*" target="drop" exclude="**\*.targets"/>
</files>
</package>
Keep a target file as one of your drop file. Like following:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<RequiredFiles Include="$(MSBuildThisFileDirectory)..\drop\**\*.*" />
<None Include="#(RequiredFiles)">
<Link>%(RecursiveDir)%(FileName)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
Now your nuget install will just extract the content in packages folder, add target to your project. When you build, the targets will fetch files from package directory and put in build directory.

Update Post-Build with custom nuget package

I need help to be able to update the post-build event of a project thanks to a custom nuget package.
I've created a package thanks to a nuspec file that include a .targets file :
<file src="*.targets" target="build"/>
Here is the .targets file :
<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="AfterBuild" AfterTargets="Build" >
<Message Text="Hello World" Importance="high" />
</Target>
</Project>
Actually, the file is read when i install the package (if i delete some '>', i've an error).
But the .csproj isn't updated (so, nothing in the post-build event textbox).
Did i miss something ?
I agree with Matt Ward. From NuGet 2.5, NuGet recognizes a new top-level folder: \build.
Within the \build folder, you can provide a “.props” file and/or a
“.targets” file that will be automatically imported into the project.
For this convention, the file name must match your package id with
either a “.props” or “.targets” extension.
Please refer to the MSBuild Integration part in following document:
http://blog.nuget.org/20130426/native-support.html
And after install the package, you will see a import node in .csproj which import the package.targets file. Then when build your project, you will see "Hello World" text in output window.

Resources