How do I create a NuGet package for a C++/CLI using MSBuild? - msbuild

I've read these instructions for creating NuGet packages for C++/CLI .vcxproj projects using a .nuspec file. However, I'd like to use MSBuild to create my package instead of using a .nuspec file. I've seen these instructions for packaging .NET Framework projects using MSBuild, but I haven't been able to find anything for doing the same with .vcxproj files.

MSBuild can pack a Visual Studio project into a NuGet package, but only if that project uses PackageReference instead of packages.config.
As of this writing, the Visual Studio UI doesn't allow you to choose PackageReference instead of packages.config as the NuGet package configuration mechanic for VCXPROJ files. However, you can manually edit the .vcxproj file and convert it to PackageReference format.
Edit .vcxproj
By default, .vcxproj files are not SDK-style projects (I don't know if they can even be converted to SDK-style projects). To build NuGet packages from non-SDK-style projects, you'll need to reference NuGet.Build.Tasks.Pack:
Add the following to your .vcxproj (inside the Project element):
<ItemGroup>
<PackageReference Include="NuGet.Build.Tasks.Pack">
<Version>5.11.0</Version>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
Add any other packages your project depends on. Notice the <PrivateAssets>all</PrivateAssets> element. You need NuGet.Build.Tasks.Pack to pack your project, but you don't want it as a dependency when others try and install your NuGet package. Adjust the version for NuGet.Build.Tasks.Pack as appropriate.
If your project references static libraries, you don't want those included as NuGet package dependencies. Use the PrivateAssets element to exclude them. I.e:
<ItemGroup>
<ProjectReference Include="..\vendor\static-lib-build\static-lib\static-lib.vcxproj">
<Project>{8aa99891-3ff7-3fa7-b1b5-131664298a26}</Project>
<PrivateAssets>all</PrivateAssets>
</ProjectReference>
</ItemGroup>
Also add the following inside the Project element"
<PropertyGroup>
<PackageId>Your.Package.Id</PackageId>
<Authors>Your Name</Authors>
<BuildOutputTargetFolder>lib</BuildOutputTargetFolder>
<PackageDescription>A description of this package.</PackageDescription>
<TargetFramework>net461</TargetFramework>
</PropertyGroup>
What's in the TargetFramework element should correspond to what's in the TargetFrameworkVersion element elsewere in your project.
Note that I haven't covered building a cross-platform NuGet package. By its very nature, C++/CLI projects are targeted to a specific processor / architecture. You can include native libraries for multiple architectures in the runtime directory of your NuGet package, but I don't cover that here.
Restore, Build, and Pack Using MSBuild
This line will restore your NuGet packages, build the project, and then pack it:
"c:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin\MSBuild.exe" path\to\the_project.vcxproj /p:Platform="Any CPU" /p:ProductVersion=1.0.0.69 /t:Restore;Build /t:pack
Change the command line options to suit your needs. You may need to run this command twice if NuGet.Build.Tasks.Pack hasn't already been restored.

Related

Consuming nuget package containing .targets file via PackageReference

I have .NET452 project - lets call it Consumer.csproj that I want to consume nuget lets call it SharedTargets that contained some custom targets files (SharedTargets.targets) from msbuild.
I'm using PackageReference format and now (compared to what it used to be) nuget packages are being restored to shared folder (%userprofile%.nuget\packages), and I'm not sure if it is good idea to reference it via that (doesn't feel right).
Eg:
<PackageReference Include="SharedTargets">
<Version>1.0</Version>
</PackageReference>
<Import
Project="$(USERPROFILE)\.nuget\packages\SharedTargets\1.0\SharedTargets.targets"
/>
Also this works only in VS, running this from command line (msbuild) I'm getting chicken-egg problem:
Confirm that the path in the <Import> declaration is correct, and that
the file exists on disk.
Obviously since I need to restore nuget first before I can use it :)
So question:
is there some more elegant way how to resolve path to the nuget package inside project file
is there a way how to make msbuild succeed (i.e. restore packages before SharedTargets.target is imported)
You shouldn't try to manually import targets distributed via NuGet.
Put your .targets file inside a build subfolder inside the package and name it SharedTargets.targets (package id + .targets) and NuGet will automatically include the targets - for packages.config projects it will modify the project file on install and for PackageReference projects the targets will be imported by modifying an implicitly generated targets file in the obj\ directory.

MSBuild /t:Pack with a .nuspec file - does it support token replacement? [duplicate]

I know Since the release of msbuild 15 (vs 2017) that NuGet is now fully integrated into MSBuild.
I have a nuspec file with defining variables of package properties like:
<metadata>
<id>$id$</id>
<version>$version$</version>
<authors>$authors$</authors>
...
</metadata>
The nuspec file is located in the same folder of the project.
When using nuget tool to create the package , it works fine.
nuget pack
When using msbuild v15, it raise an exception.
run the command:
msbuild -version
Microsoft (R) Build Engine version 15.8.168+ga8fba1ebd7 for .NET Framework
15.8.168.64424
msbuild /t:pack /p:configuration=release /p:NuspecFile=mylib.nuspec
raise exception:
C:\Program Files\dotnet\sdk\2.1.402\Sdks\NuGet.Build.Tasks.Pack\build\NuGet.Build.Tasks.Pack.targets(199,5): error : Value cannot be null or an empty string.
The strange is that dotnet sdk version 2.1.402 raises the exception.
I tried msbuild installed with vs2017 with its path and also it raises the same exception.
When i substitute the variables with its values, msbuild is working fine.
The question
Is this a bug in msbuild version 15.8.168.64424 or i missed something ?
In other words, Can msbuild support using the metadata variables of the package?.
As has been mentioned in the comments, you no longer need a Nuspec file as most aspects can be controlled via properties in the csproj file or additional metadata on items (e.g. if you need additional content).
If you do need a nuspec file for some reason, you need to provide the variables for substitution yourself. You can do this in a target inside the csproj file like this:
<Target Name="SetNuspecProperties" BeforeTargets="GenerateNuspec">
<PropertyGroup>
<NuspecProperties>$(NuspecProperties);id=$(AssemblyName)</NuspecProperties>
<NuspecProperties>$(NuspecProperties);config=$(Configuration)</NuspecProperties>
<NuspecProperties>$(NuspecProperties);version=$(PackageVersion)</NuspecProperties>
<NuspecProperties>$(NuspecProperties);description=$(Description)</NuspecProperties>
<NuspecProperties>$(NuspecProperties);authors=$(Authors)</NuspecProperties>
</PropertyGroup>
</Target>

Is it possible to utilize OctoPack with the new PackageReference NuGet format?

We recently upgraded our assemblies to use the PackageReference format instead of the packages.config for our NuGet dependencies. One of the packages, OctoPack, stopped working after doing this. Is there any way to get OctoPack to work while still using the PackageReference format?
Yes, but you have to use Octo.exe pack (same process as for ASP.NET Core applications)
Yes. Recent Nuget versions will autogenerate imports for the MSBuild tasks in the obj/xxx.csproj.nuget.g.targets file.
If it doesn't...
When using packages.config, Nuget will add something like this to your csproj.
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\OctoPack.3.6.4\build\OctoPack.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\OctoPack.3.6.4\build\OctoPack.targets'))" />
and
<Import Project="..\packages\OctoPack.3.6.4\build\OctoPack.targets" Condition="Exists('..\packages\OctoPack.3.6.4\build\OctoPack.targets')" />
The EnsureNuGetPackageBuildImports target is probably already there, even before.
If you add this to your csproj (or keep it, if you converted from packages.config) it will sort of still work. The problem is that packages are not stored in "..\packages".
This is easily fixed by replacing e.g "..\packages\OctoPack.3.6.4" with "$(NuGetPackageRoot)\octopack\3.6.4". Of course you need the PackageReference to octopack. It will be enough to make a restore operation put the package in $(NuGetPackageRoot).
YMMV, but it works for me, both locally and in CI (TeamCity)

VS2017 msbuild / nuget pack

I am having a problem with nuget (version 4.3.0.4406) and msbuild (version 15.3.409.57025). I am using VS2017 to create class library. Using the pack capability of VS2017 i can successfully create a nuget package (that i can install in another solution). Now i want to add an install.ps1 script to the package in the tools folder that runs when the nuget is installed.
In the csproj file i am specifying multiple target frameworks:
<TargetFrameworks>net45;net452</TargetFrameworks>
I cannot figure out how to do this. I've created a nuspec file using the nuget -spec command which generates a simple nuspec file. When i use the msbuild command with the /t:pack and /p:Nuspecfile=path.to.nuspec I get the following errors:
NuGet.Build.Tasks.Pack.targets(141,5): error : Value cannot be null or an empty string.
I have nuspec files from other projects (from VS2015 solutions) that work without problem, and the structure of the one i am using now is basically the same. Can anyone let me know whether i am trying something that cannot be done?
You can pack any item by updating its metadata in the csproj file:
<ItemGroup>
<None Update="install.ps1" CopyToOutputDirectory="PreserveNewest" Pack="true" PackagePath="\tools" />
</ItemGroup>
Note that the ps1 file is only run for projects using packages.config to reference the NuGet package and you should investigate alternative ways to accomplish what you are trying to do with the script as PackageReference is now more likely to be used instead.

How to exclude some stylecop rule in visual studio 2013

I'm using visual studio 2013.
I install stylecop using NuGet Package follow these steps:
https://www.nuget.org/packages/StyleCop.MSBuild/
Previously when I using visual studio 2010, I usually put my custom rules set called Setting.StyleCop file to my solution or project.
How should I implement my custom rules set in VS 2013?
Settings.StyleCop still works as it did previously.
The StyleCop.MsBuild package performs a couple of actions:
1. Stores an instance of StyleCop.exe in the packages directory, this makes it portable between machines, build servers etc.. Namely \packages\StyleCop.MSBuild.4.7.49.1\tools this is pretty much the same you would get with installing it, but its not in program files.
2. Makes changes to the csproj, namely:
<Import Project="..\..\packages\StyleCop.MSBuild.4.7.49.1\build\StyleCop.MSBuild.Targets" Condition="Exists('..\..\packages\StyleCop.MSBuild.4.7.49.1\build\StyleCop.MSBuild.Targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\packages\StyleCop.MSBuild.4.7.49.1\build\StyleCop.MSBuild.Targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\StyleCop.MSBuild.4.7.49.1\build\StyleCop.MSBuild.Targets'))" />
</Target>
All this basically does is point at the targets file so that it runs stylecop and adds in some requires for the package to be there before building.
What you need to do to use StyleCop.Settings
Copy it into the root of your solution, or the project file, then it should get picked up as usual. The easiest way is just to copy it from \packages\StyleCop.MSBuild.4.7.49.1\tools