How can I wrap C# DLLs into a NuGet package on Ubuntu? - c#

On Ubuntu I'd like to wrap a few C# DLL files into a NuGet package. On Windows one would use the NuGet package explorer or nuget.exe + manually edited *.csproj.nuspec. In summary when manually editing the *.nuspec file one may add DLLs via the <files> section:
<files>
<file src="some\Path\YourDll.dll" target="lib"></file>
</files>
On Ubuntu I'd like to use dotnet pack instead. However it seems like it's not able to operate on a *.csproj.nuspec file:
Usage: dotnet pack [options] <PROJECT | SOLUTION>
Arguments:
<PROJECT | SOLUTION> The project or solution file to operate on. If a file is not specified, the command will search the current directory for one.
Options:
-h, --help Show command line help.
-o, --output <OUTPUT_DIR> The output directory to place built packages in.
--no-build Do not build the project before packing. Implies --no-restore.
--include-symbols Include packages with symbols in addition to regular packages in output directory.
--include-source Include PDBs and source files. Source files go into the 'src' folder in the resulting nuget package.
-c, --configuration <CONFIGURATION> The configuration to use for building the package. The default for most projects is 'Debug'.
--version-suffix <VERSION_SUFFIX> Set the value of the $(VersionSuffix) property to use when building the project.
-s, --serviceable Set the serviceable flag in the package. See https://aka.ms/nupkgservicing for more information.
--nologo Do not display the startup banner or the copyright message.
--interactive Allows the command to stop and wait for user input or action (for example to complete authentication).
--no-restore Do not restore the project before building.
-v, --verbosity <LEVEL> Set the MSBuild verbosity level. Allowed values are q[uiet], m[inimal], n[ormal], d[etailed], and diag[nostic].
--runtime <RUNTIME_IDENTIFIER> The target runtime to restore packages for.
--no-dependencies Do not restore project-to-project references and only restore the specified project.
--force Force all dependencies to be resolved even if the last restore was successful.
This is equivalent to deleting project.assets.json.
Can I wrap C# DLLs into a NuGet package on Ubuntu using the dotnet CLI? Or do I have to use the nuget CLI (apt-get install nuget) instead with the approach like on Windows?

I would add this into the .csproj file:
<ItemGroup>
<Content Include="some.dll" PackageCopyToOutput="true">
<pack>true</pack>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
It's not pretty but it works.

Related

Auto-add PackageReferences to NuGet package

In my project I reference some packages with PackageReference like that:
But, if I run pack command, it doesn't include them unless I manually add them to .nuspec file.
I want to automatically add packages from PackageReference in my C# project to NuGet packages.
I pack my project using nuget pack ProjectFile on Build or manually.
Current nuspec with dependencies:
P.S. AFAIK references from packages.config are automatically added, but I don't want to use this file. I want to use PackageReferences instead.
Actually, you don't need .nuspec file anymore. All information you can specify in csproj file like:
<PropertyGroup>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<PackageId>BinaryTree</PackageId>
<Version>5.1.0.0</Version>
<Authors>RMarusyk</Authors>
<Description>Simple Binary Tree implementation</Description>
<PackageProjectUrl>https://github.com/Marusyk/BinaryTree</PackageProjectUrl>
<PackageTags>binarytree</PackageTags>
</PropertyGroup>
Then I've added this line into csproj.
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
To build it I use:
dotnet build -c Release src/Solution.sln
dotnet pack -c Release src/Solution.sln
dotnet nuget push ..
It works in my case without define any dependencies

Msbuild v15 can't resolve the variables of metadata of nuspec file

I know Since the release of msbuild 15 (vs 2017) that NuGet is now fully integrated into MSBuild.
I have a nuspec file with defining variables of package properties like:
<metadata>
<id>$id$</id>
<version>$version$</version>
<authors>$authors$</authors>
...
</metadata>
The nuspec file is located in the same folder of the project.
When using nuget tool to create the package , it works fine.
nuget pack
When using msbuild v15, it raise an exception.
run the command:
msbuild -version
Microsoft (R) Build Engine version 15.8.168+ga8fba1ebd7 for .NET Framework
15.8.168.64424
msbuild /t:pack /p:configuration=release /p:NuspecFile=mylib.nuspec
raise exception:
C:\Program Files\dotnet\sdk\2.1.402\Sdks\NuGet.Build.Tasks.Pack\build\NuGet.Build.Tasks.Pack.targets(199,5): error : Value cannot be null or an empty string.
The strange is that dotnet sdk version 2.1.402 raises the exception.
I tried msbuild installed with vs2017 with its path and also it raises the same exception.
When i substitute the variables with its values, msbuild is working fine.
The question
Is this a bug in msbuild version 15.8.168.64424 or i missed something ?
In other words, Can msbuild support using the metadata variables of the package?.
As has been mentioned in the comments, you no longer need a Nuspec file as most aspects can be controlled via properties in the csproj file or additional metadata on items (e.g. if you need additional content).
If you do need a nuspec file for some reason, you need to provide the variables for substitution yourself. You can do this in a target inside the csproj file like this:
<Target Name="SetNuspecProperties" BeforeTargets="GenerateNuspec">
<PropertyGroup>
<NuspecProperties>$(NuspecProperties);id=$(AssemblyName)</NuspecProperties>
<NuspecProperties>$(NuspecProperties);config=$(Configuration)</NuspecProperties>
<NuspecProperties>$(NuspecProperties);version=$(PackageVersion)</NuspecProperties>
<NuspecProperties>$(NuspecProperties);description=$(Description)</NuspecProperties>
<NuspecProperties>$(NuspecProperties);authors=$(Authors)</NuspecProperties>
</PropertyGroup>
</Target>

Nuget package does not restore all DLLs and Dependencies

I have created a Nuget package using Nuget Package Explorer. The package has some third party dlls that I use in my code. I dlls are included in the Nuget package and not referenced directly in the project references. The Nuget Package has the following.
- thirdPartyAAA.dll
- thirdPartyAAA.xml (Needed by thirdPartyAAA.dll)
- thirdPartyBBB.dll (needed by thirdPartyAAA.dll
- thirdPartyBBB.dll.config (used by thirdPartyBBB.dll)
- Dependency on HtmlAgilityPack nuget (Needed by thirdPartyAAA.dll)
- Dependency om RestSharp nuget (Needed by thirdPartyAAA.dll)
The problem is: when I reference this Nuget package in the code and compile the code I only get aaa.dll in the bin output folder. the following files are missing from the bin folder:
- thirdPartyAAA.xml
- thirdPartyBBB.dll
- thirdPartyBBB.dll.config
- All dlls from HtmlAgilityPack nuget
- All dll from RestSharp nuget
In my code I directly reference thirdPartyAAA.dll.
Is there a way - either during creating the Nuget Package or when referencing the package - to force the Nuget Package to restore all its contents and its dependencies? I need all the files that's included in the Nuget Package to be restored regardless if they are directly references in the code or not.
thank you all for your help.
Here is the manifest of the package if it helps.
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>MyPackage</id>
<version>1.0.0</version>
<title></title>
<authors>Dev</authors>
<owners>Dev</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>My package description.</description>
<dependencies>
<dependency id="HtmlAgilityPack" version="1.4.9" />
<dependency id="RestSharp" version="105.0.1" />
</dependencies>
</metadata>
<files>
<file src="content\thirdPartyAAA.chm" target="content\thirdPartyAAA.chm" />
<file src="content\thirdPartyAAA.XML" target="content\thirdPartyAAA.XML" />
<file src="content\thirdPartyBBB.dll.config" target="content\thirdPartyBBB.dll.config" />
<file src="lib\thirdPartyAAA.dll" target="lib\thirdPartyAAA.dll" />
<file src="lib\thirdPartyBBB.dll" target="lib\thirdPartyBBB.dll" />
</files>
</package>
The problem is: when I reference this Nuget package in the code and compile the code I only get aaa.dll in the bin output folder. the following files are missing from the bin folder:
- thirdPartyAAA.xml
- thirdPartyBBB.dll
- thirdPartyBBB.dll.config
- All dlls from HtmlAgilityPack nuget
- All dll from RestSharp nuget
First, for the thirdPartyBBB.dll, you should make sure the target framework version of project is higher than your dll's target framework. For example, If the target framework version of your project is .net 4.6.2, the target framework version of your thirdPartyBBB.dll should be lower than .net 4.6.2. Otherwise, you are referring a dll file with higher version target framework to the project with lower version target framework. This is incompatible.
Besides, for the dependencies of this package, you can check if those dependencies added to the project, and the properties Copy Local of those dlls are set to True. It works fine on my side.
Second, for the content file, you should add a .targets file in the build folder in your package with following code to copy those content files to the bin folder:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<None Include="$(ProjectDir)thirdPartyAAA.chm">
<Link>thirdPartyAAA.chm</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CustomToolNamespace></CustomToolNamespace>
</None>
<None Include="$(ProjectDir)thirdPartyAAA.XML">
<Link>thirdPartyAAA.XML</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CustomToolNamespace></CustomToolNamespace>
</None>
<None Include="$(ProjectDir)thirdPartyBBB.dll.config">
<Link>thirdPartyBBB.dll.config</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CustomToolNamespace></CustomToolNamespace>
</None>
</ItemGroup>
</Project>
For some details info, please check this thread.
Alternatively, you can use Install.ps1 file to change the property, the script looks like:
param($installPath, $toolsPath, $package, $project)
function MarkDirectoryAsCopyToOutputRecursive($item)
{
$item.ProjectItems | ForEach-Object { MarkFileASCopyToOutputDirectory($_) }
}
function MarkFileASCopyToOutputDirectory($item)
{
Try
{
Write-Host Try set $item.Name
$item.Properties.Item("CopyToOutputDirectory").Value = 2
}
Catch
{
Write-Host RecurseOn $item.Name
MarkDirectoryAsCopyToOutputRecursive($item)
}
}
#Now mark everything in the a directory as "Copy to newer"
MarkDirectoryAsCopyToOutputRecursive($project.ProjectItems.Item("TheFolderOfYourContentFiles"))
You can check similar issue for details.
In addition, I have created a test nuget package, you can check if it works for you, test it with .net framework project with target framework 4.6 and above.
https://1drv.ms/u/s!Ai1sp_yvodHf1QJriMiGQWdYveRm
Hope this helps.
Go to your solution explorer ,
on Ur project references,
right click on the DLL which is missing from the bin folder. Select properties, then make "copy local " to true. This will copy the DLL to the build path after compiling.
Go to your solution explorer ,
on Ur project references,
right click on the DLL which is missing from the bin folder. Select properties, then make "copy local " to true. This will copy the DLL to the build path after compiling.
For nuget restoring please refer the below link
https://learn.microsoft.com/en-us/nuget/consume-packages/package-restore-troubleshooting
I recently ran into almost exactly the same problem.
After 2 days, I discovered several posts that clarified that the contents folder are ONLY deployed during the +initial+ installation of the nuget.
What this means is that any files deployed as CONTENT are intended to be checked into your version control system, just like any other code in your project.
They are specifically NOT deployed when VS runs a "package restore" as it does when you build a project and some packages are missing.

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>

Updating local nuget package on post-build event

I have my local nuget library repository separately both for my personal and work releted class libraries.
I have created some of the nuget packages for the libraries which are no longer in development. I did this only for them because I do not know how to update them automatically as soon as my project builds.
I have figured that all the work is being done by nuget command line with Visual Studio Command Prompt. So I can easily do the work I needed (of course I would know commands perfectly and I do not !)
Basically I want the following tasks to execute on the post-build event of my project.
On project build:
copying project dll into a specific folder (lib folder of the nuget package)
updating nuspec file for new file version (my project is increasing the file version on every build)
creating new nupkg file with new file version
Phil Haack shows some of this feature but it is still a prototype as far as I can tell.
So my requirement is the above. Has anyone else accomplished this?
The selected solution looks like it would work but it seems like there is a simpler solution for your requirements.
You can create a nuspec file that will read data from the project's metadata. You only need to do this once with this command:
C:\<Path to project>\nuget spec
This creates 'tokens' in the spec file that will will be replaced by the project's metadata when you create the nuget package. This includes the file version. You will want to replace and because all projects are technically suppose to have them.
More details can be found here: http://docs.nuget.org/docs/creating-packages/Creating-and-Publishing-a-Package#From_a_project
Then...
For .Net Framework (old-school) projects, in you project's Post build events you can do this:
nuget pack "$(ProjectPath)"
xcopy "$(TargetDir)*.nupkg" "<path where you are hosting your local nuget repo>" /C /Y
(assuming nuget.exe is available on your system PATH).
For .Net Core and Standard projects, nuget can't pack them (see https://github.com/NuGet/Home/issues/4491). Instead, use this as your post-build step:
dotnet pack "$(ProjectPath)" --no-build --include-source --include-symbols --output "<path where you are hosting your local nuget repo>"
Of course you can adjust the options to meet your needs. See https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-pack?tabs=netcore2x for dotnet pack command options.
Just in case someone else (like me) come across this ancient question - in the current era (VS2017, SDK/NugetReference format/ .NET Core / .NET Standard / Multi-target projects), where creation of NuGet package is an option in the Project Properties - so, only issue of local repository has to be solved:
If you have single-target project, to copy your .nupkg file to local NuGet repository, add Post-build event (Project properties > Build Events > Post-build event command line):
xcopy $(TargetDir)*.nupkg [path to your local nuget repository] /s
Like:
xcopy $(TargetDir)*.nupkg G:\imbVelesOpenSource\LocalNuGet\imbVelesSecondGeneration\ /s
If you have multi-target project, to copy your .nupkg file to local NuGet repository:
add Post-build event (Project properties > Build Events > Post-build event command line):
xcopy $(TargetDir)..*.nupkg [path to your local nuget repository] /s
Like:
xcopy $(TargetDir)..*.nupkg G:\imbVelesOpenSource\LocalNuGet\imbVelesSecondGeneration\ /s
Update:
Forget post-build events, there is a cleaner way (xcopy approach works strange for multi-target projects), just add this in project XML:
<Target Name="CopyPackage" AfterTargets="Pack">
<Copy SourceFiles="$(OutputPath)$(PackageId).$(PackageVersion).nupkg" DestinationFolder="G:\imbVelesOpenSource\LocalNuGet\imbVelesSecondGeneration\" />
</Target>
Update
For newer NuGet versions, which cut off the last 0 (patch from a 0.0.0.0 version notation), you will need to regex the PackageVersion:
<Target Name="CopyPackage" AfterTargets="Pack">
<Copy SourceFiles="$(OutputPath)$(PackageId).$([System.Text.RegularExpressions.Regex]::Replace("$(PackageVersion)", "^(.+?)(\.0+)$", "$1")).nupkg" DestinationFolder="G:\imbVelesOpenSource\LocalNuGet\imbVelesSecondGeneration\" />
</Target>
Jeremy Skinner has written a blog post on how he performs automated builds of packages and uploads them to the NuGet Gallery. I think that it matches your requirements.
Basically he uses MsBuild to apply the version (with the MsBuild Community Extensions UpdateXml task) to the nuspec file and invoke the nuget.exe to package it up.
I recently published a solution for this that actually creates/updates the nuspec files during the build, so don't have to do so manually, then creates the nupkg files.
All you will have to add is a post-build event for copying the packages to their destination (or add it as an optional stage to my solution).
You can find an article with a walk-through guide here and the source code + binary here.
With the latest nuget package format using "PackageReference" tags you can use the following simple postbuild event in your csproj to update nuspec file with latest dependencies.
<Target Name="AfterBuild">
<WriteLinesToFile File="dependencies.xml" Overwrite="true" Lines=""/>
<WriteLinesToFile File="dependencies.xml" Overwrite="false" Lines="<dependencies>"/>
<WriteLinesToFile File="dependencies.xml" Overwrite="false" Lines="<dependency id="%(PackageReference.Identity)" version="%(PackageReference.Version)" />" />
<WriteLinesToFile File="dependencies.xml" Overwrite="false" Lines="</dependencies >"/>
<Exec Command="powershell -NonInteractive -executionpolicy Unrestricted -command "$xml = [xml] (Get-Content Project.nuspec); $xml.package.metadata.RemoveChild($xml.package.metadata.dependencies); $dependencies = [xml](Get-Content dependencies.xml); $xml.Package.Metadata.AppendChild($xml.ImportNode($dependencies.Dependencies, $true)); $xml.Save('Project.nuspec')""/>
<Delete Files="dependencies.xml" />
</Target>
Only premise for this is that you have a nuspec file with rest of the metadata mentioned in the project directory. Here is a sample nuspec file:
<?xml version="1.0"?>
<package>
<metadata>
<id>Package Id</id>
<version>1.0.0</version>
<authors>Author name</authors>
<owners>Owner name</owners>
<description>Description</description>
<contentFiles>
<files include="**/content.zip" buildAction="None" copyToOutput="true" flatten="false" />
</contentFiles>
<dependencies>
</dependencies>
</metadata>
<files>
<file src="bin\Release\Project.dll" target="lib\net462" />
<file src="bin\Release\Project.pdb" target="lib\net462" />
<file src="bin\Release\file.zip" target="Content" />
</files>
</package>
I solved it by using a command line
Let's assume you have added the location of NuGet to your path environment or copies a stable version to a given directory (this is what I did)
I have my NuGet.exe as well as my certificate in a folder named D:\Build, you may need to update this to fit your path.
Then let's assume you have a codesign certificate file named: "CodeSignCertificate.pfx"
with a password: pa$$w0rd
You can get a simple codesign certificate for 17 euro from Ascertia. they also have free trial certificates that are valid for 10 days. the link https://account.ascertia.com/onlineCA/default
then as a one liner I use :
for %f in (X:\Packages\*.nupkg) do D:\Build\nuget sign %f -CertificatePath D:\Build\CodeSignCertificate.pfx -Timestamper http://timestamp.digicert.com -CertificatePassword pa$$w0rd
This sign all my packages in my output directory
if you only want to sign the lastest the NuGet package as this might be the one you just compiled then you can use something like this.
set Path="X:\ASP-WAF\DLL"
for /f "tokens=*" %%a in ('dir /A:-D /B /O:-D /S %Path%') do set NEW=%%a&& goto:n
:n
sign %NEW% -CertificatePath D:\Build\CodeSignCertificate.pfx -Timestamper http://timestamp.digicert.com -CertificatePassword pa$$w0rd
it's not a one-line but you can put it in a bat file and call that in your post build.

Categories

Resources