15

I have created a C++/CLI (mixed) assembly which has a managed wrapper class around some unmanaged C++ code. The managed part targets .NET 4.6.1, I got a file entry.cpp with just this line to do that:

[assembly:System::Runtime::Versioning::TargetFrameworkAttribute(L".NETFramework,Version=v4.6.1", FrameworkDisplayName = L".NET Framework 4.6.1")];

When I now manually include the compiled assembly in a .NET 4.6.1 project I can use the managed class as expected.

This project can be build four ways: x86 or x64 as either debug or release build. It has no managed dependencies.

Now I want one (or if required multiple) NuGet packages which I can upload to my feed and use the wrapper assembly easily in every .NET 4.6.1 compatible project I would like. How do I achieve this?


So far I tried two approaches:

First, I created a .autopkg file which is according to this blog post the way to provide native DLLs. The files section of that file looks like this:

files {
  // include: { *.h }; 
  [x86,v120,release] {
     symbols: { ..\Release\*.pdb; }
     bin:     { ..\Release\*.dll; }
  };
  [x86,v120,debug] {
     symbols: { ..\Debug\*.pdb; }
     bin:     { ..\Debug\*.dll; }
  };
};

This process results in three .nupkg files which I can upload to my feed. But when I try to install that package to a .NET 4.6.1 project I get this error message:

Could not install package 'MyCppCliWrapper.redist 1.0.0.2'. You are trying to install this package into a project that targets '.NETFramework,Version=v4.6.1', but the package does not contain any assembly references or content files that are compatible with that framework. For more information, contact the package author.


So I rethought if I should not use the way for managed assembly to create the .nupkg because the assembly has a managed class I want to use from managed code. I created a .nuspec (using nuget spec) and provided the metadata. Then I try to create my package like this:

nuget pack MyCppCliWrapper.nuspec -Prop Configuration=Release -Prop Platform=x86 -Build

But that results in a package which contains the whole project with all source files and temporary files, just like a zip file of that folder.

Obviously there is also missing the meta information about targeted framework.

When I try to use the project file to create the package (like with C# assemblies) this fails too:

Please specify a nuspec, project.json, or project file to use

The C++ project files, .vcxproj, seem to be unsupported by NuGet (I am using the NuGet 3.5.0.1938 command line utility).

Will I need to build manually and provide all files in the files section of the .nuspec? If yes, how would he know from this line which DLL is for which .NET framework plus platform?

<file src="bin\**\*.dll" target="lib" />

I believe Hans Passant is right, this is just a regular managed nuget package but the packager does not handle the .vcxproj files so I made up my own .nuspec:

<?xml version="1.0"?>
<package >
  <metadata>
  ...
  </metadata>
  <files>
    <file src="readme.txt" target="" />
    <file src="bin\Win32\Release\*.dll" target="lib\net461" />
    <file src="bin\Win32\Release\*.pdb" target="lib\net461" />
  </files>
</package>

The package generated this ways works.

There is one question remaining: This way, do I have to do two packages, one for 32bit and one for 64bit - or is it possible to include them in one package (which I would prefer) and have the consuming project use one or another depending on the target architecture (any-cpu is mostly 32bit)?

ZoolWay
  • 5,411
  • 6
  • 42
  • 76
  • 1
    Possible duplicate of [How can I make my managed NuGet package support C++/CLI projects?](http://stackoverflow.com/questions/18694947/how-can-i-make-my-managed-nuget-package-support-c-cli-projects) – Hans Passant Nov 30 '16 at 17:09
  • @HansPassant I tend to say no as I do not have a normal managed assembly in my package, just the C++/CLI with the managed class. But I try out some things from that post as well and report back. – ZoolWay Dec 01 '16 at 07:13
  • 2
    You just followed the wrong tutorial, it only applies to pure native C++ projects. You can't add a reference to a native DLL in the IDE either. That a C++/CLI assembly is still a .NET assembly and has metadata like any other .NET assembly matters a lot more. Working around the limitation that the Nuget packager can't deal with a .vcxproj file is what it takes. – Hans Passant Dec 01 '16 at 07:28
  • @HansPassant Yeah, looks like you are right. I edited the question with my `.nuspec` - can I have one package for 32 and 64 bit architectures? – ZoolWay Dec 01 '16 at 09:16
  • @ZoolWay - "There is one question remaining: This way, do I have to do two packages, one for 32bit and one for 64bit" - did you ever figure this out or get this answered? – Dave Thieben Mar 21 '18 at 14:10

1 Answers1

10

I don't know if this could still help you, but I've managed to pack both x64 and x86 C++ code, and a C# wrapper that was compiled on AnyCPU.

On My C# project, I have two Platforms: "x86" and "x64".

On my Nuget folder, I have the following structure:

\Project
    \Project.1.0.nuspec
    \build
        \x64
            \*.dll
            \*.pdb
        \x86
            \*.dll
            \*.pdb
        \Project.targets
    \lib
        \net452
            \Wrapper.dll
            \Wrapper.pdb

Project.nuspec:

<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
    <metadata>
        <id>Project</id>
        <version>1.0</version>
        <authors>nilsonneto</authors>
        <owners>nilsonneto</owners>
        <requireLicenseAcceptance>false</requireLicenseAcceptance>
        <description>Example.</description>
        <references>
            <reference file="Wrapper.dll" />
        </references>
    </metadata>
    <files>
        <file src="build\Project.targets" target="build\Project.targets" />

        <file src="build\x64\**" target="build\x64" />
        <file src="build\x86\**" target="build\x86" />

        <file src="lib\net452\Wrapper.dll" target="lib\net452\Wrapper.dll" />
        <file src="lib\net452\Wrapper.pdb" target="lib\net452\Wrapper.pdb" />
    </files>
</package>

Project.targets:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
 <ItemGroup>
    <NativeLibs Include="$(MSBuildThisFileDirectory)\$(Platform)\*.*" />
    <Content Include="@(NativeLibs)">
      <Link>%(RecursiveDir)%(FileName)%(Extension)</Link>
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
  </ItemGroup>
</Project>

Notice the $(Platform), which is where the name of the Platform being build on Visual Studio will be placed, which is why I separated the C++ DLLs in folders with the same name as the Platforms in Visual Studio.

And according to the documentation (https://learn.microsoft.com/en-us/nuget/create-packages/native-packages), all native DLLs have to be placed in the \build directory.

Native NuGet packages targeting native then provide files in \build, \content, and \tools folders; \lib is not used in this case (NuGet cannot directly add references to a C++ project). A package may also include targets and props files in \build that NuGet will automatically import into projects that consume the package. Those files must be named the same as the package ID with the .targets and/or .props extensions.

So, just adjust the folder names based on the Platforms you support on the .NET project and your set.

nilsonneto
  • 475
  • 9
  • 18
  • 1
    If there were some translation files (language dependent) where would they go in the nuget path? in my scenario, i have few xml files under English and German. For my native library/app. I am putting my app which is x64 based under build\native\x64\*.dll so where should the translation files go? Thanks – Zenwalker Nov 16 '18 at 10:49
  • 1
    Is this native c++ or cli? could you tell me more about the Wrapper? – Jakub Pawlinski Jan 14 '19 at 10:54
  • how would you support consumers on AnyCPU platform – Jakub Pawlinski Jan 14 '19 at 17:07
  • @Zenwalker I believe you can place these files on content\* According to the [documentation for the .nuspec](https://learn.microsoft.com/en-us/nuget/create-packages/creating-a-package) anything place there "are copied to the project root. Think of the content folder as the root of the target application that ultimately consumes the package. To have the package add an image in the application's /images folder, place it in the package's content/images folder." – nilsonneto Feb 21 '19 at 19:08
  • @JakubPawlinski My wrapper was built on C# for a C++ DLL, with the platform set to AnyCPU, since the what controls the DLLs it will access is the .nuspec. As for the support for AnyCPU platform, I am not quite sure what you mean, since it is basically the same as x86 (See here for more: http://blogs.microsoft.co.il/sasha/2012/04/04/what-anycpu-really-means-as-of-net-45-and-visual-studio-11/) – nilsonneto Feb 21 '19 at 19:21
  • This isnt using C++/CLI so it really isnt the answer the poster was looking for. There is some unstated magic going on inside the wrapper to account for the different architectures and the Debug/Release differences. – Andrew Robinson Nov 02 '22 at 23:10