NuGet: Two packages with targets files lead to incorrectly copied DLLs - nuget

I have created two NuGet packages that contain Native Libraries. The first one has two subfolders (amd64 and x86) the second one includes the DLL flat under the build directory. The NativeLibraries of the the first package are supposed to be copied into subfolders of the OutputPath. The only DLL in the second package should be copied flat under the OutputPath. I used the following stackoverflow entry as a guide for creating the package: https://stackoverflow.com/a/30316946/4496150
The first NuGet package folder structure looks like this (amd64 and x86 subfolder under build):
build
amd64
DLL1.dll
DLL2.dll
x86
DLL1.dll
DLL2.dll
packagename.targets
First targets file:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<NativeLibs Include="$(MSBuildThisFileDirectory)**\*.dll" />
<None Include="#(NativeLibs)">
<Link>%(RecursiveDir)%(FileName)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
Please notice $(MSBuildThisFileDirectory)** as NativeLibs Include and %(RecursiveDir) as part of Link attribute.
The second NuGet package structure looks like this (no subfolders under build):
build
DLL1.dll
packagename.targets
Second targets file:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<NativeLibs Include="$(MSBuildThisFileDirectory)\*.dll" />
<None Include="#(NativeLibs)">
<Link>%(FileName)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
Please notice $(MSBuildThisFileDirectory) (without **) as NativeLibs Include and missing %(RecursiveDir) in Link attribute.
If you reference both packages, the DLLs from the first package are additionally copied flat into the output folder, which is not desired. I suspect this is because the second include step also reads the DLLs from the first package, but then does not use %(RecursiveDir).
A workaround is to also include in the second package both.
and NativeLibs Include="$(MSBuildThisFileDirectory)***.dll" /> exactly as in the first package.
However, I would prefer to understand why the second package ensures that the DLLs from the first are copied flat.
If I read https://learn.microsoft.com/de-de/visualstudio/msbuild/msbuild-reserved-and-well-known-properties?view=vs-2019 correctly, $(MSBuildThisFileDirectory) points to the folder in my NuGet cache where the targets file of the NuGet package is located. So actually everything would be correct. But still (probably) wrong DLLs are copied.
Edit
I added the following snippet in both targets files to get some output:
<Target Name="OutputPackage1NativeLibs" BeforeTargets="Build">
<Message Text="Package1 %(NativeLibs.Identity)" Importance="high"/>
</Target>
For the seconds targets file I changed the target name to OutputPackage2NativeLibs and started the text Output with Packag2.
When I clean my NuGet Package Cache and rebuild the solution everything is fine. But after the third or forth rebuild operation the DLLs of the first package are copied flat under the Output path and I get the following output:
Package1 C:\Users\USERNAME\.nuget\packages\PACKAGENAME1\1.2.3.4\build\amd64\DLL1.dll
Package1 C:\Users\USERNAME\.nuget\packages\PACKAGENAME1\1.2.3.4\build\amd64\DLL2.dll
Package1 C:\Users\USERNAME\.nuget\packages\PACKAGENAME1\1.2.3.4\build\x86\DLL1.dll
Package1 C:\Users\USERNAME\.nuget\packages\PACKAGENAME1\1.2.3.4\build\x86\DLL2.dll
Package1 C:\Users\USERNAME\.nuget\packages\PACKAGENAME2\1.0.0.0\build\DLL1.dll
Package2 C:\Users\USERNAME\.nuget\packages\PACKAGENAME1\1.2.3.4\build\amd64\DLL1.dll
Package2 C:\Users\USERNAME\.nuget\packages\PACKAGENAME1\1.2.3.4\build\amd64\DLL2.dll
Package2 C:\Users\USERNAME\.nuget\packages\PACKAGENAME1\1.2.3.4\build\x86\DLL1.dll
Package2 C:\Users\USERNAME\.nuget\packages\PACKAGENAME1\1.2.3.4\build\x86\DLL2.dll
Package2 C:\Users\USERNAME\.nuget\packages\PACKAGENAME2\1.0.0.0\build\DLL1.dll
So NativeLibs are added from the other NuGet package apparently after the third or fourth rebuild.

I think you are using Packages.config nuget package format with non-sdk net framework projects. If so, that could be explained.
For packages.config nuget management format, it imports the target files directly under csproj file. In my side, I created two nuget packages called flat 1.0.0 and faltt 1.0.0 nuget packages.
You can check my csproj file:
If so, the first import targets file flat.targets file is the same as your first nuget package's targets file:
It includes the x86 and amd64 folder files into output folder\x86 and output folder\amd64 folder, that is right as we excepted.
However, under pakckages.config, since the two targets file are in the same csproj file, they can access each other(they have the same life cycle in the same CSProj), when msbuild reads faltt.targets file after flat.targets file, also you did not change NativeLibs item for the second targets file, itself has the file:
C:\Users\xxx\source\repos\flat\packages\flat.1.0.0\build\amd64\Dll1.dll
C:\Users\xxx\source\repos\flat\packages\flat.1.0.0\build\amd64\Dll2.dll
C:\Users\xxx\source\repos\flat\packages\flat.1.0.0\build\x86\Dll1.dll
C:\Users\xxx\source\repos\flat\packages\flat.1.0.0\build\x86\Dll1.dll
When reads the flatt.targets file, it also includes C:\Users\xxx\source\repos\flat\packages\flatt.1.0.0\build\x86\Dll1.dll,
So it has five files under NativeLibs item.
And then it executes <Link>%(FileName)%(Extension)</Link>, the first nuget package's dlls will be executed under the second nuget packages's node, output to the output root folder.
Because of the special nature of Packages.config import targets files, they are interlaced.
You should note that
Under PackageReference nuget management format, the import targets files are stored under obj\xxx.csproj.nuget.g.targets file, like this:
<ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<Import Project="$(NuGetPackageRoot)flatt\1.0.0\build\flatt.targets" Condition="Exists('$(NuGetPackageRoot)flatt\1.0.0\build\flatt.targets')" />
<Import Project="$(NuGetPackageRoot)flat\1.0.0\build\flat.targets" Condition="Exists('$(NuGetPackageRoot)flat\1.0.0\build\flat.targets')" />
</ImportGroup>
Since the content is not in csproj file and the particularity of the file xxx.csproj.nuget.g.targets, it will read the targets files in each nuget package separately. Each file is a separate life cycle, so it does not Will affect each other. This is also one of the advantages of the latest PackagesReference nuget management format.
Sorry for not telling you at the beginning that I was using packagesReference and I didn't notice packages.config.
So if you want to get the right behavior to separate them, try the two approaches:
1) do not change the two nuget packages, right-click on the packages.config file of the main project which installs the two nuget packages-->click Migrate packages.config to PackageReference option
After that, click Rebuild option to get that.
2) modify the nuget packages--> change the NativeLibs item's name to another for the second nuget package targets file:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<NativeLibs1 Include="$(MSBuildThisFileDirectory)*.dll" />
<None Include="#(NativeLibs1)">
<Link>%(FileName)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
When re-pack your project, uninstall them first, delete all cache files under C:\Users\xxx\.nuget\packages. And then re-install the packages again.

Related

Nuget package don't copies my own dlls to target project folder after install success

I try to build and install nuget pack with c# proxy for c++ dll.
So, when I install my pack to target project with Visual Studio 2019, it seems, that install is success. But I not see my c++ dll copied from nuget content to in project folder.
My first version of nuget pack used build/Install.ps1, and it copied success my c++ dll to target project folder, but.. not for all projects. For example, when in VS2019 Nuget Package Manager I checked mark for target project with name like ProjectName - all is OK, Install.ps1 execute. But sometime projects in Nuget Package Manager have names like ProjectName\ProjectName.csproj - unfortunately, Install.ps1 not executed for such projects. This why I should to return to use targets files for copying.
Ok, I created target file, pack my nuget packet and install it with VS2019 Nuget Package Manager. C++ dll not copied, but, repeat, packet was installed success. This is content of installed packed in solution package dir:
- Comp.Dep.NetClient.2.3.1
- build
- netstandard2.0
- Comp.Dep.NetClient.targets
- Comp.Dep.NetClient.props
- c++.dll (need to be copy to target project folder)
- lib
- netstandard2.0
- c#.dll (client proxy, added to project references)
- Comp.Dep.NetClient.2.3.1.nupkg
I tried many kinds of Comp.Dep.NetClient.targets content. I try to use Comp.Dep.NetClient.props instaed. Nothing help :) dll not copied. Now, my Comp.Dep.NetClient.targets and Comp.Dep.NetClient.props has next content:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<NativeLibs Include="$(MSBuildThisFileDirectory)**\*.dll" />
<None Include="#(NativeLibs)">
<Link>%(RecursiveDir)%(FileName)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
I used different kinds from this
https://qastack.ru/programming/19478775/add-native-files-from-nuget-package-to-project-output-directory
..and from this
How can I set the 'copy to output directory' property in my nuspec file?
..and others :) nothing work..
For example, my target project structure after install pack is next:
Solution dir
packages
Comp.Dep.NetClient.2.3.1
..(see structure on previous example)
MyApp.sln
MyApp (project dir)
MyApp.csproj
...other files, but not c++ dll
I expect that after install my nuget to MyApp project is success, my c++ dll will be copied to MyApp (project dir). But it not happens.
Why??!!

NuGet package: Copy DLLs to subdirectories in buildoutput

I want to create a NuGet package that copies the DLLs into a subdirectory of the output directory.
This is what the folder structure of my package looks like:
Package root
lib
net48
x64
First.dll
Second.dll
x86
First.dll
Second.dll
The DLLs should be copied to the respective subdirectory in the output folder as follows:
Output directory
x64
First.dll
Second.dll
x86
First.dll
Second.dll
I have tried the files attribute in the nuspec file:
<files>
<file src="lib\net48\amd64\*.dll" target="amd64\" />
<file src="lib\net48\x86\*.dll" target="x86\" />
</files>
When building the package the following warning appears for each DLL I use:
WARNING: NU5100: The assembly 'amd64\First.dll' is not inside
the 'lib' folder and hence it won't be added as a reference when the
package is installed into a project. Move it into the 'lib' folder if
it needs to be referenced.
Neither the subfolders are created nor the DLLs are copied into the output folder after I restored all packages.
How can I configure the nuspec file so that the DLLs are copied into the subfolders? Do I need an install.ps1 script?
I'm using .NET Core (and .NET 5) projects. And packaging one component with multiple project DLLs. Can do this by modifying prject files.
So if you also have .NET Core projects add the following section to your .csproj file (to the project which should be packaged) to package DLL, change the DLL names and path as you need it.
<ItemGroup>
<_PackageFiles Include="$(OutputPath)\First.dll">
<BuildAction>None</BuildAction>
<PackagePath>x64</PackagePath>
</_PackageFiles>
<_PackageFiles Include="$(OutputPath)\First.dll">
<BuildAction>None</BuildAction>
<PackagePath>x86</PackagePath>
</_PackageFiles>
</ItemGroup>

VSTS Automated Build NuGet Packager/Publisher

My package builds successfully and is uploaded to the Packages feed in VSTS however I can't seem to figure out how to edit the Description and Author of the package so that my set values show in the Package feed.
From what I read I put my content in the NuGet Packager under additional build properties and when I look at the log file I see this:
...NuGet.exe pack "...csproj" -OutputDirectory "..." -Properties Configuration=release;Description="My Description";Authors="Me";Owners="My Company"
From the documentation I believe I did this right(but clearly I did not). It does seem a bit confusing as to what goes in "Additional build properties" vs NuGet Arguments.
Again my goal is get the Description and Author that I set to be viewable from the NuGet Package Manager within Visual Studio.
You could create a package according to the .nuspec file.
Steps:
Generate .nuspec file for your project (command: nugget spec).
For example: (Include author and description token)
<?xml version="1.0"?>
<package >
<metadata>
<id>CommLib1</id>
<version>1.0.0.6</version>
<title>CommLib1</title>
<authors>$author$</authors>
<owners>$author$</owners>
<licenseUrl>http://LICENSE_URL_HERE_OR_DELETE_THIS_LINE</licenseUrl>
<projectUrl>http://PROJECT_URL_HERE_OR_DELETE_THIS_LINE</projectUrl>
<iconUrl>http://ICON_URL_HERE_OR_DELETE_THIS_LINE</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>$description$</description>
<releaseNotes>Summary of changes made in this release of the package.</releaseNotes>
<copyright>Copyright 2016</copyright>
<tags>Tag1 Tag2</tags>
</metadata>
</package>
Include this file to source control
Specify Nuget Arguments (token in step 1) of Nuget Packager build step
Update1:
In general, you just need to update AssemblyInfo.cs file of your project (Author=>AssemblyCompany; Description=>AssemblyDescription; Version=>AssemblyVersion), it creates package according to this data unless it can't retrieve metadata from your assembly (I have a project has this issue).
So, steps:
Make sure nuget could retrieve necessary metadata by creating package through nuget.exe command directly in your local/build machine (nuget pack [XX].csproj)
Create a build definition (1. Visual Studio Build 2. Nuget Packager with default value 3. Nuget Publisher)
If it's building the package then there are no problems with your NuGet Packager build step. Two things need to change though.
In order to specify properties like you are doing there MUST be a tokenized *.nuspec file in the same directory as the solution file with the same name and of course the *.nuspec file needs to be checked in to VSTS/TFS.
The token name for description can't be Description.
For more details on the *.nuspec file please see the solution here:
Nuget.exe pack WARNING: Description was not specified. Using 'Description'

NuGet Assembly outside lib folder

I'm going to bang out a couple of questions here...first, with NuGet is it possible to create a package from a few DLLs? There is no visual studio project, just the command line and a couple of pre-compiled DLL files.
Second, assuming that is possible, why do I continuously get the "Assembly outside of the lib folder" warning? I've tried everything I can think of to get associated assemblies to add themselves as references inside of the NuGet package.
My file structure looks like this
Root
- File1.dll
- lib
- File2.dll
- File3.dll
When I tell NuGet to pack it using a .nuspec like this
<?xml version="1.0"?>
<package >
<metadata>
<id>File1.dll</id>
<version>1.0.0</version>
<authors>thisguy</authors>
<owners>thisguysmom</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>This is some library</description>
<releaseNotes>Porting to NuGet</releaseNotes>
<copyright>Copyright 2012</copyright>
<references>
<reference file="File2.dll" />
<reference file="File3.dll" />
</references>
</metadata>
</package>
I receive that warning. From what I'm reading, I shouldn't even have to define the references node in any of my projects, as the lib folder items should automatically be added as references?
Does anyone out there understand this NuGet mess?
I just ran into this problem. Nuget is expecting a structure that looks like:
root
- lib
- net40
- File1.dll
- File2.dll
- File3.dll
net40 or net20 or net45 as appropriate to your .net version.
run
nuget pack yourlibrary.nuspec
to package it up.
That will pack up the dir wholesale and put it in the nupkg. The error messages will disappear at that point.
Any dll that you want referenced should be under the lib folder. The warning is because file1.dll is outside lib and will be ignored during package installation. (Other special folder are "content" and "tools")
I'd used this structure :
Root
- lib
- File1.dll
- File2.dll
- File3.dll
See : http://docs.nuget.org/docs/creating-packages/creating-and-publishing-a-package#Package_Conventions for additional details.
With the version of NuGet that is current as of this post (and I assume later versions as well), you can use the .csproj file, in tandem with the .nuspec file to create the package. What we did was make a .nuspec file (using nuget spec and then customizing it) and include it in the project.
With the customized .nuspec file, we used the command:
nuget pack sample.csproj -IncludeReferencedProjects
At that point it built the .nupkg and did not emit issues. The .nupkg file showed up in the normal output folder (in my default case, bin\debug).
You may add references to another dll by adding below inside tag in nuspec file
<package>
<metadata>
...
</metadata>
<files>
<file src="..\ReferencedFolder\*.*" target="lib\net40\" />
</files>
</package>
Alexandre is referring to the "lib" folder that gets created when you create a NuGet package. You can open the .nupkg just like you would any zip file. In there, you will see a lib\netXX folder where XX is the version of the .NET framework you're targeting. So when you're packing your NuGet file, make sure File1.dll is inside the lib folder.
I used Prof Von Lemongargle' solution, and for me was a great solution.
Important:
Include spec file (with right-click-> Include in project) in the project
Give to spec file THE SAME FILENAME of your project (myproject.csproj, myproject.nuspec)
This work perfectly in Visual Studio 2012.
They get into the "lib" folder by being included in your bin\debug or bin\release folder in .NET. So you need to get the project compile to copy local on the external DLLs so it includes them in the bin folder on compile.
If dependencies or libraries have been changed, old files affect the packaging operation.
Remove obj and bin folders from project.
Run dotnet restore
Run nuget pack yournuspecfile.nuspec -properties Configuration=Release -IncludeReferencedProjects or your command whatever.

NuGet - install.ps1 does not get called

I'm trying to create my first NuGet package. I don't know why my install.ps1 script does not get called. This is directory structure
--Package
|
- MyPackage.nuspec
- tools
|
- Install.ps1
- some_xml_file
I build package using this command line
nuget.exe pack MyPackage.nuspec
When I Install-Package from VS Package Manager Console install.ps1 does not get called.
I thought that maybe I had some errors in script and that's the reason so I commented out everything but
param($installPath, $toolsPath, $package, $project)
"ECHO"
But I don't see ECHO appearing in Package Manager Console. What can be wrong?
Install.ps will only be invoked if there is something in the \lib and/or \content folder, not for a "tools only" package, though. See here:
The package must have files in the content or lib folder for Install.ps1 to run. Just having something in the tools folder will not kick this off.
Use the Init.ps1 instead (however, this will be executed every time the solution is opened).
Install.ps1 (and Uninstall.ps1) are no longer called in v3, but you can use Init.ps1. See here:
Powershell script support was modified to no longer execute install
and uninstall scripts, but init scripts are still executed. Some of
the reasoning for this is the inability to determine which package
scripts need to be run when not all packages are directly referenced
by a project.
An alternative to the install script can sometimes be a package targets file. This targets file is automatically weaved into the project file (csproj, ...) and gets called with a build.
To allow Nuget to find this targets file and to weave it in, these two things are mandatory:
the name of the targets file must be <package-name>.targets
it must be saved in the folder build at the top level of the package
If you like to copy something to the output folder (e.g. some additional binaries, like native DLLs) you can put these binaries into the package under folder binaries and use this fragment in the targets file for the copying:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="CopyBinaries" BeforeTargets="BeforeBuild">
<CreateItem Include="$(MSBuildThisFileDirectory)..\binaries\**\*.*">
<Output TaskParameter="Include" ItemName="PackageBinaries" />
</CreateItem>
<Copy SourceFiles="#(PackageBinaries)"
DestinationFolder="$(OutputPath)\%(RecursiveDir)"
SkipUnchangedFiles="true"
OverwriteReadOnlyFiles="true"
/>
</Target>
</Project>