I'm trying to create NuGet package for a .Net assembly which does pinvoke to a native win32 dll. I need to pack both the assembly and the native dll with the assembly added to the project references (no problem at this part) and the native dll should be copied into the project output directory or some other relative directory.
My questions are:
- How do I pack the native dll without visual studio trying to add it into the references list?
- Do I have to write an install.ps1 for copying the native dll? If so how can I access the package content for copying it?
Using the
Copy
target in the targets file to copy required libraries won't copy those files to other projects which reference the project, resulting in aDllNotFoundException
. This can be done with a much simpler targets file though, using aNone
element, as MSBuild will copy allNone
files to referencing projects.Add the targets file to the
build
directory of the nuget package along with the required native libraries. The targets file will include alldll
files in all child directories of thebuild
directory. So to add anx86
andx64
version of a native library used by anAny CPU
managed assembly you would end up with a directory structure similar to the following:The same
x86
andx64
directories will be created in the project's output directory when built. If you don't need subdirectories then the**
and the%(RecursiveDir)
can be removed and instead include the required files in thebuild
directory directly. Other required content files can also be added in the same way.The files added as
None
in the targets file won't be shown in the project when open in Visual Studio. If you are wondering why I don't use theContent
folder in the nupkg it's because there's no way to set theCopyToOutputDirectory
element without using a powershell script (which will only be run inside Visual Studio, not from the command prompt, on build servers or in other IDEs, and is not supported in project.json / xproj DNX projects) and I prefer to use aLink
to the files rather than having an additional copy of the files within the project.Update: Although this should also work with
Content
rather thanNone
it appears that there's a bug in msbuild so files won't be copied to referencing projects more than one step removed (e.g. proj1 -> proj2 -> proj3, proj3 won't get the files from proj1's NuGet package but proj2 will).This is an old question, but I have the same problem now, and I found a turnaround that is a little tricky but very simple and effective: create in the Nuget standard Content folder the following structure with one subfolder for each configuration:
When you pack the nuspec file, you will receive the following message for each native library in the Debug and Release folders:
We don't need such "solution" because this is just our goal: that native libraries aren't added as NET Assemblies references.
The advantages are:
The disadvantages are:
Here is an alternative that uses the
.targets
to inject the native DLL in the project with the following properties.Build action
=None
Copy to Output Directory
=Copy if newer
The main benefit of this technique is that the native DLL is copied into the
bin/
folder of dependent projects transitively.See the layout of the
.nuspec
file:Here is the
.targets
file:This inserts the
MyNativeLib.dll
as if it was part of the original project (but curiously the file is not visible in Visual Studio).Notice the
<Link>
element that sets the destination file name in thebin/
folder.I had recently the same problem when I tried to build an EmguCV NuGet package including both managed assemblies and non-managed shared liraries (which also had to be placed in a
x86
subdirectory) which had to be copied automatically to the build output directory after each build.Here is a solution I came up with, that relies on only NuGet and MSBuild:
Place the managed assemblies in the
/lib
directory of the package (obvious part) and the non-managed shared libraries and related files (e.g. .pdb packages) in the/build
subdirectory (as described in the NuGet docs).Rename all non-managed
*.dll
file endings to something different, for example*.dl_
to prevent NuGet from moaning about alleged assemblies being placed at a wrong place ("Problem: Assembly outside lib folder.").Add a custom
<PackageName>.targets
file in the/build
subdirectory with something like the following contents (see below for a description):The above
.targets
file will be injected on an installation of the NuGet package in the target project file and is responsible for copying the native libraries to the output directory.<AvailableItemName Include="NativeBinary" />
adds a new item "Build Action" for the project (which also becomes available in the "Build Action" dropdown inside of Visual Studio).<NativeBinary Include="...
adds the native libraries placed in/build/x86
to current project and makes them accessible to the custom target which copies those files to the output directory.<TargetPath>x86</TargetPath>
adds custom metadata to the files and tells the custom target to copy the native files to thex86
subdirectory of the actual output directory.The
<PrepareForRunDependsOn ...
block adds the custom target to the list of targets the build depends on, see the Microsoft.Common.targets file for details.The custom target,
CopyNativeBinaries
, contains two copy tasks. The first one is responsible for copying any*.dl_
files to the output directory while changing their extension back to to the original*.dll
. The second one simply copies the rest (for example any*.pdb
files) to the same location. This could be replaced by a single copy task and an install.ps1 script which had to rename all*.dl_
files to*.dll
during package installation.However, this solution still would not copy the native binaries to the output directory of another project referencing the one which initially includes the NuGet package. You still have to reference the NuGet package in your "final" project as well.
If anyone else stumbles across this.
The
.targets
filename MUST equal the NuGet Package IdAnything else wont work.
Credits go to: https://sushihangover.github.io/nuget-and-msbuild-targets/
I should've read more thoroughly as its actually noted here. Took me ages..
Put it is the content folder
the command
nuget pack [projfile].csproj
will do it for you automatically if you will mark the files as content.then edit the project file as mentioned here adding ItemGroup & NativeLibs & None element
worked for me