22

I've read a handful of posts (see references below) and have yet to find a guide on best practices that is specific to my tech stack.

The goal: Create a single NuGet package targeting multiple .NET frameworks built from a single .csproj file via TeamCity using MSBuild and NuGet.

The constraints:

  1. Pull the code from the VCS only once.
  2. All compiled assemblies should be versioned the same.
  3. Single .csproj (not one per target framework).

I have two approaches in mind:

  1. Create a single build configuration. It would contain three build steps: compile .NET 3.5, compile .NET 4.0, pack with NuGet. Each build step would be contingent upon success of the last. The only real problem I see with this approach (and hopefully there's a solution that I'm not aware of) is that each build step would require its own set of build parameters (e.g., system.TargetFrameworkVersion and system.OutputPath) to designate the unique location for the DLL to sit (e.g., bin\release\v3.5 and bin\release\v4.0) so that the NuGet pack step would be able to do its thing based upon the Files section in the .nuspec file.

  2. Create multiple build configurations. One build configuration per the build steps outlined above. With this approach, it is easy to solve the TargetFrameworkVersion and OutputPath build parameters issue but I now have to create snapshot dependencies and share the assembly version number across the builds. It also eats up build configuration slots which is ok (but not optimal) for us since we do have an Enterprise license.

Option #1 seems like the obvious choice. Options #2 feels dirty.

So my two questions are:

  1. Is it possible to create parameters that are unique to a build step?
  2. Is there a third, better approach?

References:

  1. Multi-framework NuGet build with symbols for internal dependency management
  2. Nuget - packing a solution with multiple projects (targeting multiple frameworks)
  3. http://lostechies.com/joshuaflanagan/2011/06/23/tips-for-building-nuget-packages/
  4. http://msdn.microsoft.com/en-us/library/hh264223.aspx
  5. https://stackoverflow.com/a/1083362/607701
  6. http://confluence.jetbrains.com/display/TCD7/Configuring+Build+Parameters
  7. http://docs.nuget.org/docs/creating-packages/creating-and-publishing-a-package
Community
  • 1
  • 1
David Peden
  • 17,596
  • 6
  • 52
  • 72
  • I have achieved solutions following both approaches and will post separate answers soon (as time allows). – David Peden Apr 15 '13 at 21:31
  • I await with baited breath ;-) Coincidentally I'm just having the same requirement. – Tim Long Apr 17 '13 at 02:00
  • Ha ha, Tim. I recognize your name from the assembly info youtrack discussion (http://youtrack.jetbrains.com/issue/TW-27596). You and I are following the same issue. I'll try to post my two answers this week. ;) – David Peden Apr 17 '13 at 02:52

2 Answers2

25

Here is my preferred solution (Option #1):

The magic relies on an unfortunate workaround. If you're willing to make this compromise, this solution does work. If you are not, you can follow the issue that I opened on JetBrains' issue tracker.

The single build configuration looks like this:

enter image description here

Note the name of the first two build steps. They are, in fact, named explicitly as the TargetFrameworkVersion values for .NET 3.5 and 4.0, respectively.

Then, in the Build Parameters section, I have configured the following parameters:

enter image description here

And finally, the Nuget Pack step does the file path translation according to my .nuspec's files section:

<files>
    <file src="bin\release\v3.5\*.*" target="lib\net35" />
    <file src="bin\release\v4.0\*.*" target="lib\net40" />
</files>
David Peden
  • 17,596
  • 6
  • 52
  • 72
  • 1
    +1 for both answers because you took the time to write a fully fledged manual – stijn Jul 12 '13 at 09:46
  • Here's another way that doesn't involve any TeamCity parameters and is entirely done using Visual Studio project build configuration, nuspec and Team City build: http://stackoverflow.com/questions/40046710/teamcity-nuget-package-build-for-different-net-frameworks/40111795 – Robin French Oct 26 '16 at 18:02
14

Here is a solution taking the second approach:

The project contains the following build configurations and template:

enter image description here

The Shared Build Number Generator is the first build in the chain. It does nothing other than create a build number that the dependent builds will share. I am using the TeamCity-referenced plugin Shared Build Number by Nicholas Williams.

And here are the notable configurations in the build template:

enter image description here

Note the build number is coming from the build ID of the Shared Build Number Generator mentioned above. So, in my case, that build's ID is 14. Also note the variable %TargetFrameworkVersion% in the artifact path. Fortunately, TeamCity supports variable interpolation almost everywhere.

In order for the template to leverage the build number, it must have a snapshot dependency on that build configuration:

enter image description here

And, finally (regarding the template), the build parameters are nearly identical to the parameters in my preferred solution. Note the additional Configuration Parameter, however. This is what will be overridden by the inheriting build configurations:

enter image description here

Then, in the dependent builds, you must wire up a snapshot dependency so that the build number (inherited from the template) actually works by also taking a dependency on the shared build number build configuration:

enter image description here

And, of course, you need to set the actual targeted framework:

enter image description here

With the actual builds configured, you can now configure the NuGet pack build configuration. You do not need to attach to a VCS root:

enter image description here

But you do need to configure a bunch of dependencies (both snapshot and artifact):

enter image description here

And, finally, you're done.

David Peden
  • 17,596
  • 6
  • 52
  • 72
  • 1
    I have the same problem, and used a version of your solution. However, I am having problems when it comes to the component I'm building is using NuGet packages. I target v4.0 and v4.5, and I have a dependency to EntityFramework 6.x. When I build my 4.0 version of MY package, I want it to use the same version of EF. I haven't gotten as far as trying out different reinstall scenarios yet, but from my research they seem to be flawed. – bigfoot Feb 17 '14 at 13:02