views:

1891

answers:

3

In our .NET software development shop, we have CruiseControl.NET set up to build 28 projects, some of which are interdependent. Each project approximately represents a Visual Studio projects or Flex library paired with unit tests. What I'm annoyed at is that I haven't found a good way to ensure that projects build in an order representing the dependencies.

Here's what I am doing:

  1. Everything is in the same build queue. I did this so that builds are done sequentially and so I can avoid needing multiple working directories.
  2. I set queue priorities on the projects, which I thought would influence their build order. But after reading the documentation more carefully, I found that it just controls prioritization when there are multiple build requests in a queue.
  3. In addition to using interval triggers to kick off a build, I use project triggers so that when dependencies successfully build, their dependents also build.

In a way, this setup works. The main problem is if somebody commits changes to the code in both project A and project B, where project B is dependent on project A. Sometimes, project B will build before project A. Because project A hasn't been build yet, this can sometimes cause project B to break. This is temporary, since the interval trigger causes project A to build later on, and its successful build triggers project B to rebuild and get fixed. What I want to avoid is project B building before project A so that intermediate breakage cannot happen.

Which practices do you use to properly manage interdependencies on a CruiseControl.NET server? At this point, I'm not willing to change to a non-free continuous integration package like TeamCity.

+2  A: 

I suggest that you don't cross the concept of a CruiseControl project with a visual studio project! I generally set up a CC project that encompasses a body of work. For me this means that the CC project will run a NAnt script. The NAnt script has very fine grained control over what I do and when. This means that I can build my solution (which knows which project to build first!), run my unit tests, reset a database, deploy some code, send out some emails, do some code analysis (NDepend and NCover are great!), etc. This means that I have one project show up in CCTray and this keeps things more true to what they actually are. I then can create a new project in CC to control when I push from DEV to STAGING and from STAGING to PROD so that we can "push button" this task. But this yields only 3 projects in cruise control and is considerably more user friendly.

Andrew Siemer
Agree. The concept of a VS solution file is much more analogous to what our CC builds are. Our CC projects each represent a "product" and each product has many, many .csproj files.
womp
The main reason I don't do that is because I have unit tests that "belong to" each VS project, and I don't want to run the same unit tests for each solution that shares the project.
Jacob
+6  A: 

Don't put the dependency in the CC.NET project settings. You need to control the project build order via the NAnt script. You don't have to build on a solution level, you can build on an individual project level.

<target name="Project1" depends="Projects2" description="Builds project 1">
     <msbuild>
        <executable>C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\MSBuild.exe</executable>
        <workingDirectory>C:\dev\ccnet</workingDirectory>
        <projectFile>CCNet.sln</projectFile>
        <buildArgs>/noconsolelogger /p:Configuration=Debug /v:diag</buildArgs>
        <targets>Build;Test</targets>
        <timeout>900</timeout>
        <logger>C:\Program Files\CruiseControl.NET\server\ThoughtWorks.CruiseControl.MsBuild.dll</logger>
     </msbuild>
</target>

<target name="Project2" depends="Projects3" description="Builds project 2">
     <msbuild>
        <executable>C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\MSBuild.exe</executable>
        <workingDirectory>C:\dev\ccnet</workingDirectory>
        <projectFile>CCNet.sln</projectFile>
        <buildArgs>/noconsolelogger /p:Configuration=Debug /v:diag</buildArgs>
        <targets>Build;Test</targets>
        <timeout>900</timeout>
        <logger>C:\Program Files\CruiseControl.NET\server\ThoughtWorks.CruiseControl.MsBuild.dll</logger>
     </msbuild>
</target>

<target name="Project3" description="Builds Project 3">
     <msbuild>
            <executable>C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\MSBuild.exe</executable>
            <workingDirectory>C:\dev\ccnet</workingDirectory>
            <projectFile>CCNet.sln</projectFile>
            <buildArgs>/noconsolelogger /p:Configuration=Debug /v:diag</buildArgs>
            <targets>Build;Test</targets>
            <timeout>900</timeout>
            <logger>C:\Program Files\CruiseControl.NET\server\ThoughtWorks.CruiseControl.MsBuild.dll</logger>
     </msbuild>
</target>

You can run your unit tests on an individual project level, so you shouldn't need to make duplicate runs over multiple projects.

The Matt
Doing it this way would mean that I would have to duplicate the build instructions for each dependency project into each dependent project. But I guess if it turns out that I can't control the build order in CC.NET, I'll have to be redundant. At least I can use CC.NET's support for XML Entities to encapsulate a project's build steps.
Jacob
Not necessarily. Use a single cruise.build file to store the NAnt script. Share this single cruise.build file across the projects, that way they all adhere to the dependency hierarchy and you're not duplicating the script.
The Matt
Although it sucks that the build box would still have to do duplicate building, it's looking like there's no good way around it. This answer looks like the best one.
Jacob
Unfortunately probably so. Although you may be able to work some magic with msbuild and the compiled storage location to skip the building of DLL's that have already been compiled, thereby avoiding some of the overhead.
The Matt
A: 

Although you have already accepted an answer, I would suggest something else: you shouldn't have direct dependencies between two projects. By "direct" I mean that every time binaries in the project A change, this shouldn't mean you automatically use them to build the project B.

The process of updating these references should be controlled (by you), otherwise you will inevitably end up with a lot of broken builds for the project B.

I tend to keep all external binaries under lib directory (and subdirectories) under source control and update them only when I decide to do so. And by "external" I mean both 3rd party libraries and those from other projects in my company (example: http://code.google.com/p/projectpilot/source/browse/#svn/trunk/lib)

Igor Brejc