Hi folks and welcome to Part II.
Before I start, I'd just like to say Happy Independence Day from the heart of New England in the USA!
Ok, with that out of the way, I’ll give you all an overview of where I’m at in my quest to inject a custom task into a TFS Team Build while standing on my head juggling 6 live Carp…er…strike that last part about juggling Carp (although I am standing on my head most of my workday). Actually it’s injecting a custom task into a Team Build just prior to compilation of each project. As both of my faithful readers will remember, I was having a bit of a go around with Microsoft PSS. They wanted me to override Team Build’s CoreCompile target to get this thing to work even when the Team Build devs felt that it was a mistake to do so. Well, I have to agree with the Team Build devs. My managers did also…they felt that the risk of mucking with CoreCompile was too great and sent me down a different path to get my stuff to work. It’s soooo nice when management and I agree on stuff.
So basically my answer to you on the “Can I do a custom task just prior to each project compilation” is NO, don’t do it. Don't override CoreCompile or anything else in Microsoft.TeamBuild.targets. Don't muck around with the Visual Basic or C-Sharp project level targets either. If you have a small number of projects or a lot of time, you can modify your individual project files to perform your custom task. I have neither. I say this to you primarily because I have been told that the CoreCompile target WILL be changing in v.2 of TFS.
So what the heck am I doing now to complete this never-ending task? Glad you asked! I’m performing a 2–pass compile. As you may (or may not, I don’t really care which) remember, the real need was to run code generation before compiling because we have projects later in the build cycle whose code generation performs reflection over earlier assemblies. So I really need to do the following:
For each project in solution
Check out code generation-centric code files
Code Gen (if needed)
Compile code
Check in code generation-centric code files
Next project
If Successful, label source code in repository
The good thing is that we already have this running MSBuild under CruiseControl.NET and NAnt hitting our VSS repository. So now you are scratching your head, that way you do in meetings when management starts talking, and say “But Steve, this doesn’t get you anything more. It doesn’t get you code coverage metrics or test statistics or work items references. It doesn’t even touch TFS!” Well you are correct grasshopper, but it does get me a starting point for the first pass of my new and improved (well it’s new) Team Build. So I start by taking this existing work and with slight modification, have it hit TFS. Once I have it running against the new repository, I can then fire this process at the beginning of the Team Build by EXECing (is that a word?) to NAnt (cutting out CruiseControl.NET). Once this first pass is completed successfully, we let the Team Build run as normal doing it’s whole “Lets compile everything”, “Lets label everything”, “Lets test everything” process.
So now you’re saying “But Steve, Isn’t this just a different type of kludge??” Why Yes, it is. But it is a maintainable kludge since we already have in-house devs that know how to work with NAnt and more importantly, NAnt can already do this! The key point here is that I get my code generation to work and ensure that it compiles in NAnt prior to letting the Team Build fire against it. So if pass 1 completes successfully then I know that the generated code that is in TFVC compiles and is ready for testing and such. When Team Build fires, I get logging, metrics, code coverage and all of the really great TFS reporting stuff. If the first pass fails, Team Build notifies me and logs that as well. Best of both worlds! Also, in the future (maybe v.2?) the Team Build may support what I need. When that happens, I can just remove the EXEC to NAnt and integrate the code generation task right into the JustBeforeWeActuallyCompileTheProject target (or whatever name Microsoft comes up with, although I truly believe that my name reflects the true nature of the target).
Ok great, so here’s what my build looks like now:
Begin TeamBuild
Begin NAnt build
For each project in solution
Check out code generation-centric files
Run code generation Compile project using MSBuild
If compile succeeds then
Check in all files checked out earlier
Return success
Else
Undo check out on all files checked out earlier
Return failure
End If
Next project
If all projects succeeded then
Label repository at file versions in workspace
Return success to TeamBuild
Else
Return failure to TeamBuild
End If
End NAnt build
Skip workspace create
Skip clean
Skip get
If NAnt build completed successfully then
Begin Solution Build
Compile code
Run Tests
Run code coverage
Report to Team Foundation Server
End Solution Build
End TeamBuild
Ok, another really really long post. Glad you've kept with me. As Brain says to Pinky; "Are you pondering what I'm pondering?" You: "Uh...yeah, Steve, but won't the monkeys get tired?" Ummmm..no. Actually we are pondering if this actually works. As you all know, or will in a moment, I don't know a whole lot of things that work in Team Build, BUT I do know a whole lot of things that don't work...and this is one of them.
It seems that when you get to the Compile project using MSBuild step, the MSBuild process just hangs. This is strange because when I run the TeamBuild or the NAnt build separately, each works 100%. To diagnose the issue, I checked the Processes tab in Task Manager to see what was going on. In Task Manager, I saw MSBuild and NAnt both sitting there taking up no more than 1% CPU (and usually less), but nothing being written to the logs. I explicitly terminated the MSBuild task and the NAnt task ran to completion which included spinning up MSBuild processes for each project in the solution, but the TeamBuild returned a failure. Quite an interesting dilemma, No? Yes, it seems like the MSBuild process spawned by the TeamBuild doesn't like it when an EXEC'd process fires up an MSBuild process. I didn't dig too much further into the whys and wherefores of it, but I did notice that there wasn't a second MSBuild process in Task Manager, just the first one fired by TeamBuild. Could this mean that the second instance of MSBuild is running in the first instance's process space? Could be, I don't know, but if anyone out there figures this out, please let me know...I'm kinda curious.
I have now shown you a number of things that don't work. Let's look at one that does. To get beyond the hanging MSBuild problem we need a solution that will be maintainable, easy to implement and actually works. To get this thing to work, we need to reverse the flow of control in this scenario. Instead of having the TeamBuild start an MSBuild that fires up NAnt which spawns MSBuild tasks (ouch...my head hurts), we will have CruiseControl.NET start NAnt that will fire of MSBuilds for each project and then spin up a TeamBuild on the newly labeled code. Here's the flow in pseudocode:
CruiseControl.NET detects check-in to TFVC
Begin NAnt build
Create workspace
Clean source code folders
Get source code from TFS
For each Project in Solution
Check out code generation-centric files
Run code generation
Compile project using MSBuild
If compile succeeds then
Check in all files checked out earlier using well-know label
Return success
Else
Undo check out on all files checked out earlier
Return failure
End If
Next Project
If NAnt build completed successfully then
Label all source code in workspace
Call TFS Server to request a TeamBuild (TFSBuild.exe)
Begin Team Build
Skip workspace create
Skip clean Skip get
Begin Solution Build
Compile code
Run Tests
Run code coverage
Report to Team Foundation Server
End Solution Build
End Team Build
End Call to TFS Server
End If
End NAnt build
We utilize the VSTS plug-in for CruiseControl.NET to determine when a check-in has occurred to kick off the build. In my case, I also need to figure out a way to stop the check-in that occurs during the build from triggering another build in CruiseControl.NET. I will be looking into this in the near future and will add another blog entry with whatever I find out. I'm sure that the answer will revolve around checking-in files using a specific label and filtering out those check-ins based on that.
Phew...that was a long one. If you have any questions or would like more detail, please leave me a comment and I'll do my best to accommodate you.

1 comments:
Dammit! A few minutes too late. Well... I tried to get it up on the 4th. Just my luck, burned again.
Post a Comment