Search code examples
c#cmakenugetclrcpack

CPack NuGet Packages


Context

I have a Managed C++/CLR library which is built using CMake 3.17, and packaged into a NuGet package using CPack. The resulting nupkg file cannot be imported into a C# project, as the Package Manager issues the following error: "[snip] the package does not contain any assembly references or content files that are compatible with [.NETFramework,Version=v4.5.2]". However, adding a reference to either the project when added to the solution, or the corresponding library file generated by the build, works as intended.

C++/CLR Details

The code itself is very basic and produces a valid library which can be referenced from another project, when manually adding a reference via Visual Studio 2017 -> Add Reference (either the project or the corresponding library can be added this way and it works all the same).

The code consists of the class itself, and AssemblyInfo.cpp provides attributes which describe the metadata and version information only. The dependencies include only System, System::Runtime::InteropServices, and a raft of pre-built native libraries.

I have not added a .nuspec file, nor a nuget.config file, the latter which I believe is generated by the CPack NuGet generator when the package is built.

CMake / CPack Details

CPack NuGet support is relatively new, and I have been unsuccessful in finding a working example, but I have managed to successfully generate a nupkg file. Firstly CMake is instructed to build a Managed C++ library with the included source files, and the following properties set on the corresponding target ManagedLibrary:

set_target_properties (ManagedLibrary PROPERTIES DOTNET_TARGET_FRAMEWORK_VERSION "v4.5.2")
set_target_properties (ManagedLibrary PROPERTIES COMMON_LANGUAGE_RUNTIME "")

The documentation states that this will generate CLR/Mixed code and works as advertised, so I am able to successfully build against the target framework. The next step was to install the library in what I believe is the correct location:

install (TARGET ManagedLibrary DESTINATION . COMPONENT MixedCLR)

And supporting (native C++) libraries are installed similarly:

install (FILES [various..] DESTINATION . COMPONENT MixedCLR)

I also set CPACK_GENERATOR to 'NuGet', and then run the PACKAGE step from the CLI using cmake --build . --target PACKAGE which successfully produces the nupkg file.

Question

How does NuGet know what libraries to add a reference to?

  1. Is a nuspec file required? If so, what must minimally be included in it, and how do I include it in the target CMakeLists.txt?
  2. Is it acceptable to put the managed library, along with supporting native libraries, in the root of the package? If not, where should they go?
  3. Are any other files generally included in a nupkg file?

Finally, if anyone knows anything about packaging and multi-targeting in C++/CLR to support different framework versions / architectures / build configurations, any notes on that would be highly appreciated.


Solution

  • How does NuGet know what libraries to add a reference to?

    Primarily NuGet infers the libraries to reference from the package structure. Managed assemblies must be put in a directory which is libs/<TFWM> where TFWM is the Target Framework Moniker (eg: .NET Framework 4.5.2 => net452).

    Is a nuspec file required? If so, what must minimally be included in it, and how do I include it in the target CMakeLists.txt?
    

    The nuspec file is automatically generated by CPack at package generation time. The generated file is saved to the output directory, and will preserve the directory structure specified by the install command.

    Is it acceptable to put the managed library, along with supporting native libraries, in the root of the package? If not, where should
    

    they go?

    As already discussed, the managed libraries go in libs/blah. Native libraries, on the other hand, go in runtimes/<RID>/native where RID is the Runtime ID. In my case I wanted to target Windows 64-bit, so the Runtime ID is win-x64.

    Are any other files generally included in a nupkg file?
    

    I bundle the PDB for convenience, but I didn't need to specify any other files or properties.

    Finally, if anyone knows anything about packaging and multi-targeting in C++/CLR to support different framework versions / architectures / build configurations, any notes on that would be highly appreciated.

    If targeting multiple framework versions it's simply a case of creating and installing multiple targets into the respective folders, there's nothing more complex to deal with.

    Finally, my finished package structure looks like the following:

    lib/
      net452/
        ManagedLib.dll
        ManagedLib.pdb
    runtimes/
      win-x64/
        native/
          NativeLib1.dll
          NativeLib2.dll
          ...
    

    I hope this helps someone in the future.