Team Foundation Service – make CodeContracts work on a hosted build agent

For a new project, I decided to give Team Foundation Service (tfs.visualstudio.com) a chance. Using an ALM solution in the cloud offers a lot of benefits for a small team (actually, I  am the only developer so far working on the new product, but the product owner is off-site). I don’t have to buy licenses, operate servers, think about backups, … I already evaluated TFS Express when it was in beta and RC last year. I had absolutely no experience with TFS so far, but it worked really smooth. Smooth setup, easy configuration, nice web frontend, excellent visual studio integration. At no cost.

But when you are used to a complete ALM solution (I use polarion/subversion/jenkins tool chain at my day job),TFS Express lacks crucial features. Team Foundation Service comes to the rescue. Creating an account and starting with your shiny TFS instance in the cloud is a matter of a few minutes. The features seem more complete (though I have no comparison to an on-premise TFS installation and its feature set). And best of all, it’s free as well (for a team with up to five users). Not enough, they also provide build services.

And these build service caused me a few headaches.

I must confess, I have only little msbuild knowledge and never before saw a TFS build template. So, a big part of my journey solving my concrete problem included learning and understanding all this (read: the relevant)  stuff. Now, as I have figured out the solution, it seems almost trivial to me. I will share what I learned here, because I did not find anything related on the web describing how to make the hosted build work with .NET CodeContracts.

Here is a step-by-step guide describing how to integrate CodeContracts in a visual studio solution that can be built in the cloud.

Step 1:
Download
& install CodeContracts on your development machine (if you have the same problem as I had, you have probably done that already 🙂

Step 2:
Copy the complete CodeContracts installation directory (somewhere in C:\Programs…) to TFS source control (in your application’s workspace, so it gets checked out by the build agent). In my case I added a new folder called CustomBuildBinaries next to my solution and copied all the files into a new subfolder called CodeContracts. Since my solution folder is configured as the $(SourceDir) of my build definition’s workspace, it will get checked out by the build agent.

Step 3:
Edit your csproj-File of the project that uses code contracts and add the following import:

<Import Condition="'$(CodeContractsImported)' != 'true' AND '$(DontImportCodeContracts)' != 'true'" Project="$(CodeContractsInstallDir)\MsBuild\v4.0\Microsoft.CodeContracts.targets" />

In VS TeamExplorer, go to the Builds page. Click on Actions and select Manage Build Controllers. A new window is popping up, from where you then select the Hosted Build Controller (Hosted) and click on Properties. Provide the path to your custom build binaries folder.

Build controller properties

Step 5:
Edit your TFS build template. Think of a strategy that fits your requirements in order to make the ‘CodeContractsInstallationDir’ argument known to the build template. I decided to copy all such 3rd party stuff into a
CustomBuildBinaries folder (as I already mentioned in point 2), so the code contracts would become a subdirectory in there. I will then pass this information via the command line to the msbuild task defined in the build template.

Step 6:
Find the “Try compile…”  activity (Ctrl-F, “Try compile” will do) and look for a sub activity called “Run msbuild for project”

Build template – run msbuild

Edit the msbuild command line arguments For this, go to the property grid and edit the property CommandLineArguments. Modify the expression:

String.Format("/p:SkipInvalidConfigurations=true /p:CustomBuildToolsDirectory={0} /p:CodeContractsInstallDir={1} {2}",
  System.IO.Path.Combine(SourcesDirectory, CustomBuildToolsDirectory),
    System.IO.Path.Combine(
      System.IO.Path.Combine(SourcesDirectory, CustomBuildToolsDirectory),
    CodeContractsSubDirectory),
  MSBuildArguments)

UPDATE: As Chris Eelmaa pointed out in the comments, problems arise when having spaces in the code contracts install dir. If you stumple upon those problems, double-quote the install dir as in this example:

/p:CodeContractsInstallDir=””{0}””

Step 7:
Now popup the Arguments of the template and add these arguments. Via these arguments we will then pass the necessary information to the build process. As you can see in the picture, you will have to add teh
CustomBuildToolsDirectory and the CodeContractsSubDirectory arguments to be In – Parameters of type String. The value you specify in the last column is just a default, you can change it as required. We will see how in a minute.

BuildProcessTemplate - Arguments
BuildProcessTemplate – Arguments

Step 8:
Close the build process template. From the TeamExplorer find your build definition, right click it and select Edit build definition. As shown in the next picture, find your new arguments that already carry the previously specified default values. 

UPDATE: As Chris Eelmaa pointed out in the comments, spaces in the directory paths will cause trouble. The suggested solution is to double all the slashes in order for spaces to work. 

Image of build properties

Step 9:
Check in all the stuff and enjoy. BTW: I was able to use the exact same technique to make OpenAccess work on the hosted build agent as well (you see its argument in the picture already 🙂

So, all in all, that was not too complicated. It took me some time to figure out a way to achieve my goal, but retrospectively its a rather simple solution. I hope anybody having the same problem can benefit from these instructions. If I missed sth., could improve sth. or even perform the same task a lot simpler, please let me know by posting a comment.

12 thoughts on “Team Foundation Service – make CodeContracts work on a hosted build agent

  1. Thanks for article.
    Did you use Runtime checking for target configuration of your build? I tried it, but have errors on build
    C:\a\src\CustomBuildBinaries\CodeContracts\MsBuild\v4.0\Microsoft.CodeContracts.targets (254): The command “”C:\a\src\CustomBuildBinaries\CodeContracts\Bin\ccrewrite.exe” “@…ccrewrite.rsp”” exited with code 3.

    Vuild with ststic checking fails with error
    <C:\a\src\CustomBuildBinaries\CodeContracts\MsBuild\v4.0\Microsoft.CodeContractAnalysis.targets (220): The "Microsoft.Research.CodeContractsAnalysis" task could not be loaded from the assembly C:\a\src\CustomBuildBinaries\CodeContracts\Bin\MsBuildCodeContracts.dll. Could not load file or assembly 'file:///C:\a\src\CustomBuildBinaries\CodeContracts\Bin\MsBuildCodeContracts.dll' or one of its dependencies. The system cannot find the file specified. Confirm that the declaration is correct, that the assembly and all its dependencies are available, and that the task contains a public class that implements Microsoft.Build.Framework.ITask./q>

    I turned off caching results, but this has no effect.

    1. Yes, I use runtime checking, no static checking. I am not sure, what’s wrong in your case. Does ccrewrite work, when you build on your development machine? Did you copy the complete installation folder of CodeContracts to your CustomBuildBinaries folder? I am really sorry, but from the error message you posted, I cannot figure out, what could be wrong. When believing this post, it may be, that the file cannot be found.

  2. As it turns out, step 3b (custom assemblies for the build controller) is not/no longer necessary. Anyway, this is a brilliant blog post and I would never have made it work without your help! Thanks!

    1. I’m so glad you were able to benefit from the post – and even told me :-). Nevertheless, I am really curious on the improvement regarding step 3b. Would you be so kind and tell me, why it is not necessary anymore? Is it finally installed on the build machines? From the website http://listofsoftwareontfshostedbuildserver.azurewebsites.net/ I don’t see this being the case. Please clarify – thanks 🙂

  3. Thank you for your post, I think I am on my way to getting this to work. A real shame that Microsoft does not include this on their build servers.
    I am running into an issue after I followed your steps, I am still getting the error
    System.Diagnostics.Contracts.ContractException: An assembly must be rewritten using the code contracts binary rewriter (CCRewrite) because it is calling Contract.Requires and the CONTRACTS_FULL symbol is defined. Remove any explicit definitions of the CONTRACTS_FULL symbol from your project and rebuild…..
    I am getting a similar error on by dev. box now. Any ideas as to where I should look to fix it?
    Thanks in advance!
    -Igor

    1. Hi Igor,

      sorry for my late reply. I’ve seen the error you describe, but I think it is related to one of your projects not being configured for use of CodeContracts. You have to make sure your msbuild CodeContracts targets are in the relevenat csproj files. If it says an assembly has to be rewritten, it is most probable that at least one of your projects (or projects referencing thos that use code contracts) are not rewritten. It should imho not be related with server build.

  4. Thanks, it helped. There was slight problem with spaces.

    For spaces, you want to use double quotes in order to escape them, that took me a bit time to figure out, since the syntax is VB(aah it should’ve been C# :\)..

    In step6, you want /p:CodeContractsInstallDir=""{0}"", and step8 needs to have all the slashes “doubled” in order for spaces to work, as MSBuild is really picky with spaces,

    as such: Testing\\Contracts with space\\

    Cheers.

    1. Chris, thanks for your feedback and improving this how-to with your insights. I can’t double-check, since I moved away from TFS, but I include a hint to your comment in the original post to save others from having to figure this out on their own.

  5. Sorry. You lost me at Step 6.

    Find the “Try compile…” activity (Ctrl-F, “Try compile” will do) and look for a sub activity called “Run msbuild for project”

    Ctrl F just takes me to “Find in files” and from there I get nothing. I can’t find this ‘activity’ anywhere.

    1. Maybe I should have been more specific: CTRL-F in my VS settings is “Find in the same file”, CTRL-SHIFT-F is “Find in Files”. What I tried to describe in that step is to find the “Run msbiuld for project” activity within the workflow. It worked for me that way at the time I was writing the post. I am pretty sure, the build scripts have changed a little in the meantime. Since I am not using TFS anymore I can’t give you latest information. Nevertheless, the idea is to find the activity in the workflow that is actually calling msbuild, so you can change the command line of this call. Whatever the activity is called today, in the end you just have to find the call to msbuild and change the call. Hope this helps.

Leave a Reply

Your email address will not be published. Required fields are marked *