Add native files from NuGet package to project output directory

AlonFStackoverflow picture AlonFStackoverflow · Oct 20, 2013 · Viewed 56.6k times · Source

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:

  1. How do I pack the native dll without visual studio trying to add it into the references list?
  2. 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?

Answer

kjbartel picture kjbartel · May 19, 2015

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 a DllNotFoundException. This can be done with a much simpler targets file though, using a None element, as MSBuild will copy all None files to referencing projects.

<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>

Add the targets file to the build directory of the nuget package along with the required native libraries. The targets file will include all dll files in all child directories of the build directory. So to add an x86 and x64 version of a native library used by an Any CPU managed assembly you would end up with a directory structure similar to the following:

  • build
    • x86
      • NativeLib.dll
      • NativeLibDependency.dll
    • x64
      • NativeLib.dll
      • NativeLibDependency.dll
    • MyNugetPackageID.targets
  • lib
    • net40
      • ManagedAssembly.dll

The same x86 and x64 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 the build 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 the Content folder in the nupkg it's because there's no way to set the CopyToOutputDirectory 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 a Link to the files rather than having an additional copy of the files within the project.

Update: Although this should also work with Content rather than None 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).