How to package projects and used NuGet packages + files? - c#

I'm working on a .NET Standard 2.0 project called ComputeSharp and I'm having some trouble creating a NuGet package from it. Some info:
The project only targets .NET Standard 2.0
The project uses 2 referenced projects in the same solution, each using some other NuGet packages. Here is a screen of my current solution.
One of the project, ComputeSharp.Shaders also references two .dll files, dxcompiler.dll and dxil.dll. These two are referenced by that project with the following snipped in its .csproj file. I made a PR to DotNetDxc with the update .dll files, so I'll no longer need to manually bundle the two .dll files into my project. So this is no longer an issue ✅
In the folder for the main ComputeSharp project, I also have a .nuspec file with the following structure, which also lists all the NuGet packages used by all 3 projects (note that the path for the *.mustache file goes up one directory because it's inside the ComputeSharp.Shaders project, and not the one from which I'm building the NuGet package, which is just ComputeSharp:
<?xml version="1.0"?>
<package >
<metadata>
<id>ComputeSharp</id>
...
<dependencies>
<dependency id="SharpDX.Direct3D12" version="4.2.1-beta0-gab36f12303" />
...
</dependencies>
<contentFiles>
<files include="..\ComputeSharp.Shaders\Renderer\Templates\*.mustache" buildAction="None" copyToOutput="true" />
</contentFiles>
</metadata>
</package>
In order to create the NuGet package, I first build ComputeSharp in Release mode, then open a cmd in the folder for that project, and run:
nuget pack ComputeSharp.csproj -Prop Configuration=Release -IncludeReferencedProjects
This does create a package that kinda looks ok. It contains the assemblies for all 3 projects, the two .dll files I use and the .mustache content file from the ComputeSharp.Shaders project.
Once uploaded to NuGet I can see the list of all the dependencies for the package, as you can see in this screen. So, that looks is ok as well.
PROBLEM: once I create a test project and install the NuGet package, I notice two things: first get this nice exception when I try to use any APIs, which makes me thing the other NuGet packages haven't been installed correctly at all, and then I can't find any of the APIs from either of those packages (eg. I don't see the SharpDX namespace at all), which makes me thing the previous point is probably correct. I have one last issue: the .mustache file (and relative parent folders) are not created in the build output directory, so I can't load it at runtime. I'm not sure why that is, since I did specify copyToOutputDirectory="true" in the .nuspec file.
I might be missing something obvious here, and I did check both the documentation and countless other SO questions on NuGet packages and whatnot, but I really couldn't figure out what I'm doing wrong here. I do have other NuGet packages uploaded, using the same method of including the referenced NuGet packages in the .nuspec file, and they work just fine when installed.
Thank you for your help!
EDIT #1: the indirect dependencies are now loaded correctly, so the last remaining issue is the content file not being copied to the build output directory (see changes above).
EDIT #2: closing this as it's too broad in scope, and the first part has been resolved already (.dll files and indirect package references). Opening a follow up question just for the issue about the content files not being copied here.

Related

C# - Create dotnet tool with external .exe and call it from parent project

I would like to create a dotnet tool with the purpose of holding external binaries my project depends on, but do not want to reference the binaries directly. In C# code I would start one of the .exe files as a standalone process. My idea is to package the binaries as content so they are not referenced in the project. However I need to keep the binaries from being copied to the bin folder of the parent projects, but still find a way to call the .exe file.
I put this within the .csproj used for creating the dotnet tool and run dotnet pack. The binaries are packaged as content in the tool.
<ItemGroup>
<!--
Include the binaries as content in the nuget package.
Content files are immutable & arbitrary files that will not be referenced in the project.
-->
<Content Include="binaries/*.*" Pack="true">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
<PackageCopyToOutput>true</PackageCopyToOutput>
<Pack>true</Pack>
</Content>
</ItemGroup>
So, I have the nuget packed with 3rd party files and want to use it from a project that consumes it. Is there a way I can access the .exe without copying the binaries to the bin folder of the consuming project?
Although the question is vague about what you want and don't want, together with the comments I think I have an idea now:
You want to distribute an application (exe and accompanying files) via a nuget package.
You want to consume that nuget package in a project.
You want to execute the application from that package at runtime from the project's code.
You do not want to have the application files lying around in the project's output folder.
You already did 1. by adding the application to the package as content.
You also already did 2.
To accomplish 3. you have to be able to access the application file at runtime from a known permanent location. The "package" is a build-time concept. It does not exist at runtime.
During the build (package restore), nuget will download the package and then store it and its contents in a local package cache folder.
From your question in the comments above I understand your plan was to have your project execute the exe in that folder.
That is possible, but highly inadvisable. You cannot rely on the local package cache folder. It is a cache. It can be deleted at any time.
What you can do instead is what Fidor hinted at: add the application files as embedded resource to your project assembly. This will bloat your project's assembly but makes all application files available at runtime without them lying around somewhere.
You do not need ILMerge for that though, because you control the project assembly build.
Creating and using embedded resources from a nuget package
To do that you have to add the application files to the EmbeddedResource item in your project.
For that you need the build-time location of the nuget package contents.
You get the latter by adding GeneratePathProperty="true" to your PackageReference.
This gives you an MSBuild property called Pkg<your_package_name_sanitized containing the root path of the nuget package contents (see the docs).
You can then use that variable to declare a globbed path to the application files within the package, like so:
<PackageReference Include="My.Package" Version="1.0.0" GeneratePathProperty="true" />
<EmbeddedResource Include="$(PkgMy_Package)/contentFiles/any/netstandard2.0/**/*" LinkBase="App" />
This will make all application files available at runtime as embedded resources with the name prefix <project_assembly_root_namespace>.App..
You then get a stream for an embedded file at runtime with
typeof(SomeProjectType).Assembly.GetManifestResourceStream("Project.Root.Namespace.App.MyApplication.exe")
and can e. g. save the files to a location from which they can be executed as their own process.

Nuget restore exception when deleting subproject

I currently have a project in dotnet that has several different projects within it. I am attempting to move out one of the projects to a separate repo and replace it with a git submodule. The issue I'm having is how to remove the package from the original repo. I can delete the directory of the package and I updated the dirs.proj to not contain those proj files.
<Project Sdk="Microsoft.Build.Traversal">
<ItemGroup>
<ProjectFile Include="Proj1\dirs.proj" />
<ProjectFile Include="Proj2\dirs.proj" />
...
// Removed this line <ProjectFile Include="ProjBeingMovedToNewRepo\dirs.proj" />
</ItemGroup>
</Project>
However, when I try to build the resulting solution (msbuild dirs.proj), I get the following exception:
C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\IDE\CommonExtensions\Microsoft\NuGet\NuGet.RestoreEx.targets(19,5): error :
The project file could not be loaded. Could not find a part of the path
'D:\git\Networking-Tycoon\src\ProjBeingMovedToNewRepo\ProjBeingMovedToNewRepo.csproj'. D:\git\Networking-Tycoon\src\ProjBeingMovedToNewRepo\ProjBeingMovedToNewRepo.csproj [D:\git\Networking-Tycoon\dirs.proj]
It isn't clear to me exactly why the nuget restoreEx targets is trying to pull that project that was removed or where I can update those settings. I tried to disable nuget auto restore and to clean the solution before building but so far have not had luck.
One other note is that the project that is being removed is still being referenced in the project elsewhere (I was planning on referencing the submodule for those later) but wanted to see if there was something more simple I was missing first. What would be the steps that would need to be taken to remove a project like this generally?
In Visual Studio, first close the solution.
Then goto Tools, Nuget package manager, Package Manager Settings.
Then Clear all Nuget cache.
That should clear the problem.

Specifying files to add to a nuget package in .csproj file

I am creating a nuget package from some code, but also need to deploy some tools with the package.
In a .nuspec file, I can do this with the <files> element, and this all works well.
However when using a .nuspec file, the packageReferences from the csproj file aren't included, and I am seeing some problems when including them manually (with the <dependencies> element).
The package created also always seems to restore as a .net framework package, even though it is targetting .net, as in this question.
I am hoping that all these problems would go away if I moved to using the .csproj format for specifying the nuget package details, but having read the docs I can't find out how to do it.
Does anyone know how it is done?
If not, can anyone shed any light on created a .net framework / .net core nuget package from a .nuspec file, that restores to the correct target version and respects package dependencies?
It's not easy to find/discover, but NuGet's MSBuild tasks docs page has a section called "including content in a package", which tells you about the PackagePath metadata on MSBuild items, that NuGet uses to copy files into the package.
So, in your csproj, you could have something like this:
<ItemGroup>
<None Include="..\MyTool\Tool.exe" PackagePath="tools" Pack="true" />
</ItemGroup>
and then your package will contain tools\Tool.exe. The Pack="true" attribute is required for None elements.
You can use MSBuild's globbing to copy entire directories, if that's easier. Include="..\MyTool\*". My MSBuild skills are not so advanced, so I don't know how to glob ..\MyTool\**\*, which means all files in all subdirectories, while maintaining the correct directory layout in the PackagePath="???" metadata. So the best I can suggest is one glob per directory.

In VS2019 nuget doesn't consider referenced projects as lib, but as nuget packages

I'm using VS2019 community edition.
I have two .net core projects X, Y, X is referencing Y, and I want to package X as a Nuget package, I'm using the package feature in VS2019.
when I try to add X's Nuget package in another project it searches for Y as a Nuget package and not as DLL should be in X.
How can I change this so Y will be added as DLL in X's package?
I tried to add the following to X's project (.csproj):
<PropertyGroup>
<TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage</TargetsForTfmSpecificBuildOutput>
</PropertyGroup>
<Target Name="CopyProjectReferencesToPackage" DependsOnTargets="ResolveReferences">
<ItemGroup>
<BuildOutputInPackage Include="#(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference'))" />
</ItemGroup>
</Target>
But I still get the same result, nuget.exe tries to restore Y as Nuget package not as DLL comes with X's package.
UPDATE:
Also if Y has another Nuget dependency, it should be considered in X without referencing it directly in X.
I tried the following:
In X's project:
Go to Dependencies->Projects.
Right click on Y's project then Properties.
Set Private Assets to All
Now this will add Y's dll file in the lib folder in the X's nuspec file.
This will work fine if Y doesn't depend on any other Nuget packages that X doesn't know about, because those packages will not be mentioned as dependencies in X's nuspec file.
Finally I settled down on this:
Just reference Y, generate package for it too and live with it :'(
In VS2019 nuget doesn't consider referenced projects as lib, but as
nuget packages
Actually, the new nuget feature which you used and also PackagePath="lib" function will treat the dll as a nuget dependency rather than an assembly dll.
If you simply want Y to be an Assembly DLL rather than a nuget package, these methods won't work. I have tried these methods, and it really made me struggling.
After a deep research, I found that the problem is that dotnet pack will add the referenced project as a nuget pacakge into the main nuget package automatically. Because dotnet pack will automatically generates an Nuspec file to package the project according to its rules, which treat the referenced project as a Nuget package by default. You can open the X.nupkg file and will see like this under X.nuspec file:
However, in this pack mechanism, we do not have the right to change the rule.
Suggestion
1) when you create the X.nupkg file using Pack Button in VS2019, please do some changes to X.nupkg manually.
Use zip to open nuget compressed package, then open X.nuspec file and delete these node:
<dependencies>
<group targetFramework=".NETStandard2.0">
<dependency id="Y" version="1.0.0" exclude="Build,Analyzers" />
</group>
</dependencies>
Save the changes and then use the modified X.nupkg.
2) Or you should create a custom nuspec file based on our needs and rules manually to guidance the nuget pack process.
Try to use this nuspec file:
<?xml version="1.0"?>
<package >
<metadata>
<id>xxx</id>
<version>1.0.0</version>
<title>xx</title>
..................
</metadata>
<files>
<file src="bin\xxx\xxx\Y.dll" target="lib\xxx(targetframework)"/>
</files>
</package>
Also if Y has another Nuget dependency, it should be considered in X
without referencing it directly in X.
So far, Nuget does not have the feature to ask the main project whether to use the dependency package dll of the referenced project.
And the dependencies from the referenced projects always exist in the main project.
Besides, if you still want these, you could report these problems on our Team Forum and I hope the Team will check them carefully and give you a satisfactory reply.

What is the best way to restore nuget packages?

I have a solution that uses custom nuget package sources. For now I specify them in Nuget.config file that is located near my solution file(so that it is checked out from source control):
|- MySoulution.sln
|- MyProjFolder
|- .nuget
|- Nuget.exe
|- Nuget.config
|- Nuget.targets
This works well when building solution from VisualStudio. It manages to read this nuget.config file and successfully restore all packages.
Now I'm configuring my solution to be built from TeamCity. So I've added project configuration and a build step to build it. But TeamCity doesn't restore nuget packages by default. So I've added a separate Nuget installer build step that runs nuget(of specific version) restore for my solution. But the problem is that it doesn't seem to see my custom nuget package sources from Nuget.config file in .nuget folder next to solution file.
I see two possible ways to overcome this:
Configure my custom package sources inside Nuget installer build step.
Configure my custom package sources in Nuget.config in AppData folder on build machine.
I don't like neither of this approaches because they don't provide me single poing of configuration for building both from TeamCity and VisualStudio.
To sum up, the question is: how do I configure my custom package sources so that they would be visible both from TeamCity and VisualStudio without requiring me to configure them several times in different places?
how do I configure my custom package sources so that they would be visible both from TeamCity and VisualStudio without requiring me to configure them several times in different places?
As you know, if you do not want to configure custom nuget sources several times in different places, you can set the custom nuget sources in the NuGet.config and add it to source control. So the key to your problem is why NuGet doesn't respect the your custom nuget package sources from Nuget.config file in .nuget folder next to solution file.
Just as my comment, if you're using NuGet 2.7 or later and have a solution that is still configured for MSBuild-integrated restore, you may have an older version of nuget.exe in the solution's .nuget folder. This will cause builds to fail with an error stating that you have not given consent to restore packages.
To avoid this issue, it's recommended to migrate any project using MSBuild-integrated restore to use the automatic restore capabilities of NuGet 2.7 and above, you can follow the process as below:
Close Visual Studio to avoid file potential file locks and conflicts.
If using TFS:
Remove nuget.exe and NuGet.targets from the solution's .nuget folder and remove those files from the solution workspace.
Retain Nuget.Config with the disableSourceControlIntegration setting as explained in Omitting packages with Team Foundation Version Control.
If not using TFS:
Remove the .nuget folder from the solution and the solution workspace.
Edit each project file in the solution, remove the element, and remove any references to the NuGet.targets file. Those settings generally appear as follows:
After that put NuGet.config next to the solution file with custom NuGet source:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<solution>
<add key="disableSourceControlIntegration" value="true" />
</solution>
<packageSources>
<add key="CustomSource" value="http://CustomSource/nuget" />
</packageSources>
<packageRestore>
<add key="enabled" value="True" />
</packageRestore>
</configuration>
Now, in the NuGet Installer step, there is now a "Package Sources" field that you can fill in to have team city use a custom feed:
You can refer to this document NuGet Package Restore with TeamCity for more detail.
Besides, we can also specify custom feed in the NuGet.targets file in the .nuget folder(I did not verify it yet):
<ItemGroup Condition=" '$(PackageSources)' == '' ">
<PackageSource Include="https://nuget.org/api/v2/" />
<PackageSource Include="\\MyShare" />
<PackageSource Include="http://MyServer/" />
</ItemGroup>

Categories

Resources