Building NuGet packages from Visual Studio - visual-studio

I'm trying to share an internal company assembly via NuGet packages and a private source. This assembly targets .NET Framework 4.6.1. I want these NuGet packages to pack automatically from Visual Studio during the release build. I see I can add <GeneratePackageOnBuild>true</GeneratePackageOnBuild> to .csproj. I'm not sure if this is a .NET Standard-specific property but it seems to partially work. However, when I build, I get
error MSB4044: The "GetPackOutputItemsTask" task was not given a value for the required parameter "PackageOutputPath".
I've been trying to learn how to pass this parameter from within Visual Studio but I don't see a lot of documentation on parameters except when calling it from the command line manually. Is there an easy way to do this from within Visual Studio? Am I going about this wrong?
Edit: This is using a .NET Framework class library. I can run the pack command from the command line giving it the required parameters with /p:PackageOutputPath="path\here". It seems this might have been designed for .NET Core and Standard projects and Visual Studio might not handle packing .NET Framework projects.

To enable on-build packing in a "non SDK" project, using old .NET framework (eg:. 4.5.1) and visual studio 2019 without using custom Target. you need to do the following step:
add a first PropertyGroup tag on the csproj
add minimal tags Authors and PackageOutputPath
check that the same PropertyGroup has GeneratePackageOnBuild
Now the variables will be passed to the internally triggered msbuild -t:Pack command.
Here an example of working configuration, please make sure this will be the first <PropertyGroup> of the .csproj:
<PropertyGroup>
<Title>packageid</Title>
<Description>your description</Description>
<Version>1.1.1</Version>
<ReleaseNotes>New package system</ReleaseNotes>
<Authors>authors</Authors>
<Owners>owners</Owners>
<Copyright>your copyrights</Copyright>
<PackageOutputPath>bin\Package</PackageOutputPath>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
If you are looking for the references, here:
how to create a nuget package with msbuild
all possible tags to set on the csproj on the first PropertyGroup

Am I going about this wrong?
This GeneratePackageOnBuild property is not something for .net framework projects. Create a new .net Standard or .net Core class library project, right-click the project you'll see a Pack command(not available for .net framework).
If we click the button, VS will pack that assembly into a nuget package. And if someone doesn't want to click that button every time manually, go Project=>Properties=>Package we can see the Generate Nuget Packages on build checkbox. Enable it, and then the nuget package will be created after every build.
Actually, enabling that checkbox in VS will add statement <GeneratePackageOnBuild>true</GeneratePackageOnBuild> to xx.csproj, but the Pack button or Package tab in Project=>Properties are not available for .net framework projects.
So I'm afraid the answer is negative, you should't use that property for .net framework projects.(I tested the property in VS2017 and VS2019, it all just did nothing, can't reproduce the partial work mentioned in your question)
Is there an easy way to do this from within Visual Studio?
You need to use nuget pack command to do that as Lex Li says. And to do this in VS automatically, you can consider configuring that command in Post-Build-Event or using custom after-build target to run that command.
Since you want these NuGet packages to pack automatically from Visual Studio during the release build. You can try adding this script into your xx.csproj file:
<Target Name="CustomPack" AfterTargets="build" Condition="'$(Configuration)'=='Release'">
<Message Text="Custom Pack command starts ..." Importance="high"/>
<Exec Command="nuget pack $(MSBuildProjectFile) -Properties Configuration=Release"/>
</Target>
To run this script successfully, you need to download the nuget.exe and add the path of it to Environment Variables. Or use the full path like "C:\SomePath\Nuget.exe" pack ...
If the build in release mode succeeds, you'll see a xx.nupkg file in project folder.
In addition:
1.For more details about nuget pack command please see this document.
2.And don't forget to create a xx.nuspec file in project folder to avoid encountering warnings like NU5115(xxx was not specified). Similar issue see here.

I have a unique situation of needing to Register for Com Interop for and older application but also needing to pack for use in other internal applications and development in our company. I actually got this to work for a .NET Framework project from Visual Studio. Manually adding GeneratePackageOnBuild did attempt to make a package for me in VS2017. I was also able to add other .NET Core project properties such as <Authors>,<Description>, etc. I haven't tried VS2019 yet so maybe that is more restricted but I hope not.
The issue is VS2017 doesn't feed the pack target the output parameter (in this type of project). So then I tried to call pack in the After Build events but that causes a recursive loop because packing also attempts to build (dotnet and nuget both seem to call the msbuild pack target which calls a build). I then found an option -p:NoBuild=true for msbuild that allows me to call the pack target without msbuild actually building the project. Therefore I added the following command to <PostBuildEvent> and it works.
"$(MSBuildBinPath)\msbuild" -t:Pack "$(ProjectPath)" -p:PackageOutputPath="$(SolutionDir)..\packages" -p:NoBuild=true
Edit: I eventually used the following in my csproj. Calling nuget directly worked better because I had a nuspec file that was not getting merged or fully used when calling MSBuild directly.
<Target Name="CustomPack" AfterTargets="Build" Condition="'$(Configuration)'=='Release'">
<Message Text="Custom Pack command starts ..." Importance="high" />
<Exec Command=""nuget" pack "$(ProjectPath)" -OutputDirectory "$(ProjectDir)..\..\packages" -Prop Configuration=Release" />
</Target>

Add this to your proj file to use the pack target on .net framework projects.
<PackageReference Include="NuGet.Build.Tasks.Pack" Version="6.4.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
if you want to run from command line then use this:
msbuild -t:restore

Related

How to modify .nuspec file before NuGet pack in Visual Studio

Visual Studio makes creating NuGet packages easier these days by automating the process of creating the .nuspec file. It means that version dependencies etc. are automatically created and you can simply set up the textual values here:
So, most of the work is already done by the time the NuGet package is built. However, there are external assemblies that I need to add to the .nuspec file before it is built. I know I can add external files in the files collection of the outputted .nuspec file (in obj\Release):
My question is, what build event or otherwise should I use to edit the .nuspec after it is created, but before the pack command is called by msbuild or Visual Studio? Or, should I just run a separate build process afterwards?
Note: a bonus would be to find a tool which I can call that will add the files collection. Without that, I'm guessing I will need to write powershell code to add the files...
You could use Pack element in csproj file: https://learn.microsoft.com/en-us/nuget/reference/msbuild-targets#including-content-in-a-package
Add something like this to your project:
<ItemGroup>
<Content Pack="True"
PackagePath="lib\netcoreapp2.2"
Include="C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All\2.1.2\Microsoft.AI.DependencyCollector.dll" />
</ItemGroup>
This will add a Microsoft.AI.DependencyCollector.dll library to your package:
This way you can add whatever you like to your library.

Reference nuget package project from another nuget package project in same solution

I am creating couple of .NET Standard 1.6 libraries that I want to publish as Nuget packages. They share a common libary that is a 3rd project in the same solution as the first two. The shared library has no value by itself, but I am assuming that if I want people to use both of these two libaries in the same project I should publish the shared library as a Nuget package as well. If I don't I am worried about multiple copies of the same shared library "colliding" or not properly warning when there are version mismatch issues.
Am I correct that the shared library needs to be a Nuget package as well? Is there a way to reference the shared library as Nuget package, but use is as if it was a project reference when developing / debugging the 2 main libraries in this solution? If I had to publish to Nuget.org and wait for the package be propagate through the Nuget.org system before using a changed version in a debug session that is REALLY going to slow down development. Note that these are .NET Standard projects. I found How to reference related projects in the same solution when Nuget packages are the required output but that doesn't seem to work with .NET Standard (getting errors during pack) and I am also not sure if .NET Standard not using nuspec files anymore also would cause a problem.
I am also not sure if .NET Standard not using nuspec files anymore also would cause a problem.
The .NET Standard still using .nuspec files, and using old school nuget pack and a .nuspec will resolve this issue.
As per document dotnet pack:
NuGet dependencies of the packed project are added to the .nuspec
file, so they're properly resolved when the package is installed.
Project-to-project references aren't packaged inside the project.
Currently, you must have a package per project if you have
project-to-project dependencies.
So, to include project-to-project references in NuGet packages, you need manually maintain a .nuspec file and add dependencies. You can refer to the Create .NET Standard packages with Visual Studio 2015 for detail info.
Besides, dasMulli has provided a simpler way to do this by involving adding and hooking up a custom target :
<Project>
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);IncludeP2PAssets</TargetsForTfmSpecificBuildOutput>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\testprivatelib\testprivatelib.csproj" PrivateAssets="All" />
</ItemGroup>
<Target Name="IncludeP2PAssets">
<ItemGroup>
<BuildOutputInPackage Include="$(OutputPath)\testprivatelib.dll" />
</ItemGroup>
</Target>
</Project>
The source code comes from: "donet pack" is not including project references

How do I import a .NET Core project to another .NET Core project in Visual Studio?

I need to use some classes from another project. How can I just import or create a reference to that project in Visual Studio?
Right now if I use "Add reference" in Visual Studio, I get the error:
".NET Core projects only support referencing .NET framework assemblies in this release. <br/>
To reference other assemblies they need to be included in a NuGet package"
.NET Core works with dependencies via NuGet.
If your projects are in the same solution, then yes, you can add a reference using the Visual Studio UI ("Add reference" command). A background reference will be added as a NuGet package.
Manually you can do this by adding <ProjectReference> section to the .csproj file:
<ProjectReference Include="..\ClassLibrary1\ClassLibrary1.csproj" />
Otherwise, you should pack your project into a NuGet package (use the dotnet pack command) and then add it as other NuGet packages.
If you do not use any public NuGet sources, you can host your own NuGet feed.
You have the next error:
".NET Core projects only support referencing .NET framework assemblies in this release.
To reference other assemblies they need to be included in a NuGet package"
Because you are trying to add a .NET project to a .NET Core project or wise versa. Look into this issue for more details:
If you're using netcoreapp then you cannot use .NET 4.x
assemblies/packages
If you're using net4xx then you can use the frameworkAssemblies
section of project.json to reference DLL files that are installed by
.NET Framework (the stuff in the GAC)
I had a .Net core project and i wanted to create another project for services in my solution. After adding the project I added the reference as follows:
Right click Dependencies in your solution.
Select Add Reference option.
In the next window, in Projects dropdown select the project you want to add.
Alternatively, you can add a reference by editing the csproj file of the project in which you want to add the dependency/reference. Open the file and add the following:
<ItemGroup>
<ProjectReference Include="..\PATH\TO_YOUR_NEW PROJECT.csproj" />
</ItemGroup>
Hope this helps someone.
You can add reference by adding your project name in csproj file. if your project in same solution
<ItemGroup>
<ProjectReference Include="..\projectName.csproj" />
<ProjectReference Include="..\ProjectName2.csproj" />
<ProjectReference Include="..\ProjectName3.csproj" />

Does Visual Studio support adding MSBuild tasks to projects?

I'm trying to add some simple MSBuild tasks to a Visual Studio project (VS 2012 Express) - specifically, to create a subdirectory then copy some files to a subdirectory of the output directory ready for packaging.
I see that VS supports custom build steps, which are command-line invocations. However, since VS is based on MSBuild it should be possible to add these directly as MSBuild tasks like the Copy Task in the AfterBuild pre-defined target.
What I can't find is any way to actually add such tasks within the framework of Visual Studio. The documentation only talks about it from an MSBuild perspective, not how it works within Visual Studio's UI. It also doesn't seem to discuss the properties that refer to build output etc there; presumably they're just those used by msbuild its self.
Is there support for MSBuild task management in Visual Studio's UI and it's just crippled out of my Express edition? Or do I have to go hack the project file XML to add MSBuild tasks? Is that supported and the way it's supposed to be done?
I'm used to working with Eclipse and Ant or Maven, where all this is supported within the IDE, though of course you can hack the XML directly. Finding no UI at all for MSBuild task management in Visual Studio is quite confusing. Am I missing the obvious or crippled by using the freebie edition?
For C++ projects, you can use the property
<CppCleanDependsOn>DeleteOutputs;$(CppCleanDependsOn)</CppCleanDependsOn>
instead of defining the BeforeClean target like you did.
From what I read, CallTarget is to be avoided. In your example, you should use DependsOnTargets to do that, as you see in many dummy targets in the MS supplied files. The analogous mechanism of a function where a target just "calls" other targets is done with DependsOnTargets. The flow is not really the same as procedural programming.
Intellisense: I never use it. Is that true for conditional AdditionalIncludeDirectories in the props file only? Go ahead and edit the entry in the proj file where the IDE put it, if you edit the property in the IDE with just one configuration chosen.
(After a bunch more reading I found out how this works):
Visual Studio doesn't seem to expose advanced MSBuild project editing, even though modern vcxproj files are just MSBuild project files with a bunch of extra labeled properties and other entries for Visual Studio IDE specifics. So you have to hack the project XML.
To make it cleaner, only add one line to your actual vcxproj file - an include of a .targets file that contains the rest of your build customisations. e.g, just before the end of the project file, insert:
<Import Project="pg_sysdatetime.targets" />
</Build>
Now create your .targets file with the same structure as any other MSBuild project. Here's mine from the project I've been working on:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- MSBuild extension targets for Visual Studio build -->
<PropertyGroup>
<DistDir>pg_sysdatetime_pg$(PGMAJORVERSION)-$(Configuration)-$(Platform)</DistDir>
</PropertyGroup>
<ItemGroup>
<DocFiles Include="README.md;LICENSE"/>
<ExtensionSourceFiles Include="pg_sysdatetime--1.0.sql;pg_sysdatetime.control"/>
<ExtensionDll Include="$(TargetDir)\pg_sysdatetime.dll"/>
</ItemGroup>
<Target Name="CopyOutputs">
<Message Text="Copying build product to $(DistDir)" Importance="high" />
<Copy
SourceFiles="#(DocFiles)"
DestinationFolder="$(DistDir)"
/>
<Copy
SourceFiles="#(ExtensionDll)"
DestinationFolder="$(DistDir)\lib"
/>
<Copy
SourceFiles="#(ExtensionSourceFiles)"
DestinationFolder="$(DistDir)\share\extension"
/>
</Target>
<Target Name="DeleteOutputs">
<Message Text="Deleting $(DistDir)" Importance="normal" />
<Delete Files="$(DistDir)"/>
</Target>
<!-- Attach to Visual Studio build hooks -->
<Target Name="BeforeClean">
<CallTarget Targets="DeleteOutputs"/>
</Target>
<Target Name="AfterBuild">
<CallTarget Targets="CopyOutputs"/>
</Target>
</Project>
This can contain whatver MSBuild tasks you want, grouped into targets. It can also have property groups, item groups, and whatever else MSBuild supports.
To integrate into Visual Studio you add specially named targets that invoke what you want. Here you can see I've defined the BeforeClean and AfterBuild targets. You can get the supported targets from the VS integration docs.
Now, when I build or rebuild, a new directory containing the product DLL and a bunch of static files is automatically created, ready to zip up. If I wanted I could add the Nuget package for MSBuild Community Extensions and use the Zip task to bundle the whole thing into a zip file at the end too.
BTW, while you can define properties in your .targets files it's better to define them in property sheets instead. That way they're visible in the UI.
I'm using VS2010 Pro, and it doesn't expose the AfterBuild target, at least in C++ projects which is what I'm doing. As you see, it does have the "Events", which according to what I've read are for backward compatibility with converted projects from VSBuild. I agree, a MSBuild task rather than a command script is the way to go.
Forget the UI. It's made to support free editing of the XML files, and continue using the UI too as it respects what you had in there and uses labels for its own stuff so it can find it to update it.
But to keep it neat, you could use a property page; a stand-alone XML file with *.props name, and put what you want in it. Then add that props file to the projects using the UI. You won't hand-edit the project file that the UI is maintaining, and it won't touch the props file unless you go through the property manager view and open it explicitly.
Oh, I also recall seeing additional standard targets something like Package and Publish. Maybe those are not used on your project type, but you could use those entry points anyway.

Visual Studio Build Tasks - TFS Operations

I'm looking to extend some post build tasks to include the checking out and then checking in of a DLL. We are using TFS and I know there are command line tools to do this. What I don't know how to do is to integrate these into my existing post build tasks. Right now my post build tasks are simple and are managed in Visual Studio through the project properties. Eventually I want to break out my custom build tasks into external files and call them in, but that is the subject of another question ;)
Without resorting to custom Build tasks you could try to use the Team Foundation Source Control Command-Line tool (tf.exe).
The example below shows how to use tf.exe to check out a file from TFS.
<PropertyGroup>
<TfCommand>
"C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\tf.exe"
</TfCommand>
</PropertyGroup>
<Target Name="AfterCompile">
<Exec Command="$(TfCommand) get /force /noprompt "$(SolutionRoot)\sources\example.cs""
ContinueOnError="true" />
<Exec Command="$(TfCommand) checkout "$(SolutionRoot)\sources\example.cs""
ContinueOnError="true"/>
</Target>
Include this in your own MSBuild project file.
This example doesn't do anything useful and you need to change it to match your environment, but maybe it gives you a start.
I got this example from tfsbuild.com.
You could use the Team Foundation Server client API. TeamFoundationServer is the base class that should allow you to connect to a server, list and manipulate TFS projects.
Msbuildtasks has some extensions for msbuild with sourcecode (its opensource). You could use this to create your own checkin/checkout functionality. (in combination with what Darin suggests)
http://msbuildtasks.tigris.org/
Take a look at the SDC Tasks Library on CodePlex. It's a set of custom MSBuild tasks that includes Checkin and Checkout tasks (see the Microsoft.Sdc.Tasks.SourceTfs namespace in the accompanying documentation). You can incorporate these tasks in the "AfterBuild" target in your project file.
<SourceTfs.Checkout Path="Path" TfsVersion="tfsVersion"
WorkingDirectory="workingDirectory"/>
<SourceTfs.Checkin Path="Path" Comments="Comments" TfsVersion="tfsVersion"
WorkingDirectory="workingDirectory" Override="overrideText"/>
You would set TfsVersion to "2005" or "2008" as appropriate.
Our team has several small projects which output DLL's used by several other projects. Part of our release is to publish these DLL's. I use the AfterDropBuild target for this. Hopefully the comments in my build script snippet are clear enough to show what I am doing.
<!-- Get a reference to the new release address finalizer DLL and the existing published address finalizer DLL -->
<PropertyGroup>
<ReleaseDLL>$(DropLocation)\$(BuildNumber)\Release\Address_Finalizer.dll</ReleaseDLL>
<PublishedFolder>$(SolutionRoot)\3rd Party\bin\PG File Import</PublishedFolder>
<PublishedDLL>$(PublishedFolder)\Address_Finalizer.dll</PublishedDLL>
</PropertyGroup>
<!-- Check out the published DLL -->
<Exec WorkingDirectory="$(SolutionRoot)" Command='$(TfCommand) checkout /lock:checkout "$(PublishedDLL)"'/>
<!-- Copy release to published -->
<Copy SourceFiles="$(ReleaseDLL)" DestinationFolder="$(PublishedFolder)"/>
<!-- Check in the published DLL -->
<Exec WorkingDirectory="$(SolutionRoot)" Command='$(TfCommand) checkin /override:Automated /noprompt /comment:"$(VersionComment)" "$(PublishedDLL)"'/>

Resources