Creating multitarget nuget package with .nuspec - continuous-integration

I have specified multitargeting in my .csproj
<TargetFrameworks>netstandard2.0;net472</TargetFrameworks>
When I am using below script, then my package has dependencies and it is expected result:
dotnet pack --output nupkgs $ProjectPath -c:Release
The problem is when I also want to use .nuspec file with some information about package:
dotnet pack --output nupkgs $ProjectPath -c:Release -p:NuspecFile=$NuspecFile
With .nuspec file the package has no dependencies and I can install it only in netstandard2.0...
<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
<metadata>
<id>xyz</id>
<version>1.2011.4</version>
<title>xyz</title>
<authors>xyz</authors>
<projectUrl>xyz</projectUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>xyz</description>
<copyright>xyz</copyright>
</metadata>
</package>
Should I add something to this .nuspec to have a package with target dependencies? I have read about tag, but to be honest I cannot find example how to configure it.

Given that you are using an SDK based projects, I'd strongly discourage using a nuspec unless your scenario cannot be satisfied because of it's complexity.
The automatic package generation with project file through dotnet.exe pack does much of the compatibility work for you.
If you really need to go with a nuspec, you should add dependency groups like defined here:
https://learn.microsoft.com/en-us/nuget/reference/nuspec#dependency-groups.
Make sure you add the relevant framework.
Example
<group targetFramework=".NETFramework4.7.2">
<dependency id="PackageA" version="1.2.3" />
</group>
<group targetFramework=".NETStandard2.0">
<dependency id="PackageB" version="4.5.6" />
</group>
Note that you need to add a dependency group for the specific frameworks you support.

Related

Do 'init.ps1' scripts still works when installing a NuGet package?

This question might be a little outdated, but does 'init.ps1' scripts still works when installing a nupkg?
I was trying to use 'install.ps1' and 'uninstall.ps1', but after a little bit of research I've seen that those files stopped working with Visual Studio 2017 (which is the version of Visual I'm currently using), but I have not found any recent information about 'init.ps1' (being the most recent from 2017 or 2018).
My script is working correctly when executed by itself but seems like it's not getting called on the package install.
In the case that 'init.ps1' still works here's my '.nuspec' file, am I doing something wrong?:
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
<metadata>
<id>NugetTry</id>
<version>0.0.142</version>
<title>NugetTry</title>
<authors>rwpk9</authors>
<description>Description</description>
<copyright>Copyright © 2023</copyright>
<dependencies>
<group targetFramework=".NETFramework4.7.2">
<dependency id="EntityFramework" version="6.4.4" />
<dependency id="Microsoft.AspNet.Mvc.es" version="5.2.9" />
<dependency id="Newtonsoft.Json" version="13.0.2" />
</group>
</dependencies>
</metadata>
<files xmlns="">
<file src="init.ps1" target="." />
</files>
<powershell xmlns="">
<scripts>
<base><![CDATA[init.ps1]]></base>
</scripts>
</powershell>
</package>
My 'init.ps1' is at the same path as the '.nuspec'.
The init.ps1 file needs to be in a tools/ directory, not the package root. So, the zip file's central directory record has a path of tools/init.ps1
I think this no longer works.
I already put the init.ps1 to the tools directory and then configured the .nuspec, after that, package the package.
But if I install the package at the first time in the solution, I didn't see my powershell script run.
From the Nuget Product Team member:
https://github.com/NuGet/Home/issues/4318#issuecomment-343255043
powershel script is deprecated for package reference, we don't
recommend people to use script in their packages.
I think this is deprecated.

How to pass a Version-Range for a Project reference into the nuspec file?

I have a ProjectA that has a ProjectReference LibraryB. Now I want to achieve the Following:
If I Pack ProjectA I want to have the Library B as Dependency with a specific Version Range.
Currently, for packing, I use the Visual-Studio Command "Pack" in Visual-Studio 2019.
My Problem: If I write the Version range into the .csproj file and pack ProjectA, the current Version of LibraryB is written into the nuspec file and not the Version-range I defined in the .csproj-file.
So in my .csproj-file I have the following:
<ProjectReference Include="..\LibraryB\LibraryB.csproj" Version="[2.1.1, 2.1.6)"/>
In the .nuspec-file of the project I want the following text:
<group targetFramework="net5.0-windows7.0">
<dependency id="LibraryB" version="[2.1.1, 2.1.6)" exclude="Build,Analyzers" />
</group>
The Text that I get is:
<group targetFramework="net5.0-windows7.0">
<dependency id="LibraryB" version="2.1.5" exclude="Build,Analyzers" />
</group>
So is there any possibility to get this Version Range into the .nuspec-file out of the csproj-file? For a NuGet-Dependency, it's working fine.

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>

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.

Resources