Tuesday, November 15, 2011

Branching in TFS 2010: Part V (Sharing Common Libraries)


In the previous four parts of this article (start with Part I here), I covered the theory behind branching and the three main patterns that use in our projects. The only detail to wrap up is how to share common library code between projects. We do this with a combination of build events and branching. Note that this method only works if both projects are in the same collection. You can't branch between two projects if they are in separate collections. So plan accordingly.


Sharing Common Code
In order to share common code, we need to take care of a few preliminaries first. The common library project needs to contain a "Deploy" folder that contains the final binary assembly. This assembly will be stored in the TFS source control repository. We'll make use of pre-build and post-build events to help automate the process of keeping this assembly up-to-date. Then, each End-User project will have a "Lib" folder. When a project needs a common assembly, we will branch from the "Deploy" folder of a particular release branch into the "Lib" folder of the referencing project. TFS keeps track of all this in the file history, so we can see who branched what assembly to what project and when. This is also handy when something needs to be rolled back.

This is how the folder structure looks for the common library, showing the "Deploy" folder:


Once the folder structure is set up, the build events need to be set up. Right-click on the project and choose "Properties". In the Properties window, select the "Build Events" tab. In the "Post-build event command line:" box, enter the following:
copy /Y "$(TargetPath)" "$(SolutionDir)Deploy"
It looks like this:


Make sure you have the "Deploy" folder created, and then build the solution once and make sure it copies the assembly correctly. If not, check your paths and make sure everything is in the right place. Once you get it built, create a solution folder in your solution, and add assembly into it. This will ensure that the assembly gets stored in TFS. Then check everything in.

Now at this point, if you try to build again it will fail because the assembly in the "Deploy" folder is read-only, since it's stored in TFS and checked in. One solution is to remember to check out the assembly each time before you build, but honestly....who wants to do that? Instead, we can automate that task, too.

For this, we're going to use a pre-build event. Into the "Pre-build event command line:" box, enter the following:

For 64-bit Windows, use:
"C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\tf.exe" checkout "$(SolutionDir)Deploy\$(TargetFileName)"

For 32-bit Windows, use:
"C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\tf.exe" checkout "$(SolutionDir)Deploy\$(TargetFileName)"
It should look like this (this screenshot is from the 64-bit version):


This pre-build event will now check out the assembly from TFS before each build, which will allow the post-build event to copy over it after the build is done. It's still up to you to remember to check it in once you're finished.

This is all that's needed on the common library side of things. You do your releases as I've described in the previous parts of this article, and each release branch will then contain the "Deploy" folder with the binary assembly.

On the other side, make sure each project that needs a reference has a "Lib" folder in its solution and references all shared libraries from there. All that's left now is to get the assembly from the "Deploy" folder of the common library project to the "Lib" folder of the End-User project.

Note that you can put your "Lib" folder at the project level, or at the solution level. The choice is yours. Putting it at the solution level will keep you from having duplicate assemblies in each project's separate "Lib" folder. On the other hand, having a "Lib" folder per project gives you the added flexibility of allowing you to have a different version of the same assembly for each project. You need to decide which approach works best for your needs.

Once the "Lib" folder is set up, the only thing left is to branch a specific version of the common library into that "Lib" folder. Do that by right-clicking on the actual assembly (the .dll file) from the "Deploy" folder of a particular release, and branching it into the "Lib" folder of the other project.


Note below that the Source and Target are two different projects. This is the key to this type of sharing.


This method of sharing is superior to simply copying the assemblies manually as needed, because it allows tracking and rolling back of changes through TFS. The version history of the files will show what projects a specific assembly has been branched into, and what versions of various assemblies are included in a given project.

Here is a slideshow that contains the information in this article:
That concludes my overview of branching and sharing code in TFS. I hope it is useful, and feel free to leave feedback on how I can improve this, or future, articles.

12 comments:

  1. Hi John, Great series of articles. TFS branching and merging is now starting to make more sense and less bewildering.

    In part V you discuss whether to put the Lib folder at the project or the solution level but I don't quite follow what you're trying to say. Is there anyway you can elaborate on these 2 different approaches, the pro's and cons.

    Thanks again for a great series of articles.

    ReplyDelete
  2. Sure, Steven.

    Lib Folder at Solution Level
    Pros: single point of contact (only one assembly to update), easier to manage.
    Cons: all projects in the solution are tied to the same version of the assembly.

    Lib Folder at the Project Level
    Pros: Each project can be at a different version of the assembly.
    Cons: Multiple versions of the assembly to update within the solution, more confusing to manage.

    ReplyDelete
  3. Excellent series of articles. We're in the process of migrating from a haphazard SourceSafe approach (single branches, all releases built from the development code, no version control, etc. Scary stuff) to TFS, and this is extremely helpful.

    Hopefully you're still around to answer a simple question about sharing and referencing common code (although the answer is probably staring me in the face): Why use the Lib folders instead of referencing the assemblies directly from their respective Deploy folders?

    ReplyDelete
    Replies
    1. That's a good question. There's no technical reason why you couldn't reference them directly from the other library's Deploy folder. The reason I don't is so I don't have to have the code for the library on my local machine if I don't need to. In order to do that, my solution has to be completely self-contained. If you reference the Deploy folder directly, then you have to "Get Latest" on both TFS projects so Visual Studio can find the referenced assembly when it builds.

      Delete
    2. Ah, I see your point.

      I guess the same reasoning would be applied to the question of why you wouldn't use project references either, plus the fact that from what I can see it's difficult (or impossible) to reference projects between different team projects?

      I'm currently researching pros and cons for keeping all our code in one team project. That would allow for direct project references of shared code, and simple administration (as long as we don't have projects that have different access requirements).

      Thanks for your reply, and for the article! Very helpful.

      Delete
    3. Yes, similar reasons. And you can't reference a project itself from another one, but you can reference a file in a project from another.

      Delete
  4. I keep getting a error everytime i try to branch the dll into another team project. It states the target project already exists. Any ideas

    ReplyDelete
    Replies
    1. When you branch the file, are you putting the filename into the target location? By default, Visual Studio doesn't fill in the filename in the target box. You have to make sure you branch just the file, not the project.

      Delete
  5. I update my "shared" project constantly (DAL) so it seems more sensible to branch it into a "Solution Workspace" and reference it as a project so I can keep it in my solution and build on-demand without extra effort. Then I can merge back to Stable and with other projects on a regular basis. Any reason that would be a bad idea?

    ReplyDelete
    Replies
    1. No reason, besides the manual overhead. But if that works for you (and it sounds like it fits your workflow), then I don't see any reason why not.

      Delete
  6. Hello John,
    I am configuring TFS 2012 in my company, i tried to implement your strategy of sharing common libraries, but i didnt understand how did the dll generated from common library project will be copied to the deploy folder. In fact in my environment i see that while building tfs is copying the project in the build workspace and the copy command that you put in the postbuild event will just copy the dll to the deploy folder of the project located in the build workspace. I wish you could help me understanding this issue!

    ReplyDelete
    Replies
    1. It doesn't automatically copy the assembly from one project to the other. You are correct that the build event copies the final assembly to the Deploy folder. To share it from the Deploy folder, you need to manually branch the assembly from there into the other project.

      The reason for this step being manual is because you wouldn't necessarily want all referencing projects to be updated to the newest version of an assembly, as it could contain breaking changes. This way, each referencing project could be using a different version of the assembly, if needed.

      Delete