<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>Rambling Comments</title>
    <link rel="alternate" type="text/html" href="http://www.lenholgate.com/blog/" />
    <link rel="self" type="application/atom+xml" href="http://www.lenholgate.com/blog/atom.xml" />
    <id>tag:www.lenholgate.com,2010-12-10:/blog//12</id>
    <updated>2010-12-28T13:51:17Z</updated>
    
    <generator uri="http://www.sixapart.com/movabletype/">Movable Type Pro 5.12</generator>

<entry>
    <title>Process management using Jobs on Windows</title>
    <link rel="alternate" type="text/html" href="http://www.lenholgate.com/blog/2008/02/process-management-using-jobs-on-windows.html" />
    <id>tag:www.socketframework.com,2008:/blog//12.815</id>

    <published>2008-02-14T09:25:17Z</published>
    <updated>2010-12-28T13:51:17Z</updated>

    <summary>One of the problems I currently have with CruiseControl.Net is that some of my tests spawn multiple processes; such as server tests which run the development environment, which runs a batch file to start a server (or two) and then...</summary>
    <author>
        <name>Len</name>
        
    </author>
    
        <category term="CC.Net" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="Continuous Integration" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="Geek Speak" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="en-us" xml:base="http://www.lenholgate.com/blog/">
        <![CDATA[<p>One of the problems I currently have with CruiseControl.Net is that some of my tests spawn multiple processes; such as server tests which run the development environment, which runs a batch file to start a server (or two) and then run a test harness which generates network traffic and then run a program to ask the server to shutdown cleanly. When these tests timeout in CC.Net they're forcibly killed. Unfortunately due to how Windows processes work, killing the parent of a tree of processes doesn't kill the children. This is because Windows processes don't know who their children are and partly because it seems that even tools like <a href="http://technet2.microsoft.com/windowsserver/en/library/c1db6dae-41c3-4c94-a038-d87baa0fee091033.mspx?mfr=true">taskkill.exe</a> and <a href="http://technet2.microsoft.com/windowsserver/en/library/950c43ed-6e26-4b12-a63e-2c0b5c057e081033.mspx">kill.exe</a> (which CC.Net uses to try and kill whole process trees) have issues with working out the relationship between processes sometimes; it may be a race condition, process A launches process B, the tool snapshots the process relationships somehow, process B launches process C, the tool walks the snapshot and kills the processes but doesn't know about process C... The end result is that when one of these multiple process tests times out in CC.Net the cleanup doesn't clean up and processes are left hanging around which then interfere with other tests.</p>

<p>There's a solution to this in the shape of <a href="http://www.microsoft.com/msj/0399/jobkernelobj/jobkernelobj.aspx">Windows Job objects</a>, and, just by chance, I happen to have been reading about them this week in <a href="http://www.lenholgate.com/archives/000760.html">Richter's new release</a>. A <a href="http://www.microsoft.com/msj/0399/jobkernelobj/jobkernelobj.aspx">Job object </a>allows you to group together processes and it knows when new child processes are started and it can kill all processes in the job in one go, or you can wait on all processes in the job to terminate, etc. Unfortunately it's not easy to add support for launching CC.Net tasks in a job as jobs currently aren't exposed by .Net (they've only been around since Windows 2000, after all...).</p>

<p>One of the nice thing about jobs is that the notifications from them come in via an IO completion port. This means that you need 0 more threads than you already have to monitor a job (assuming you already have at least one thread monitoring an IOCP). I took my <a href="http://www.lenholgate.com/archives/000325.html">usual approach</a> and spent some time putting together some simple classes to make job management easier and to learn how the API works. Once I had a job object that I could use to manage a set of processes and which I implemented my <code>IWaitable</code> interface so that I could wait for the termination of all of the processes within the job I decided it might be nice to be able to launch and monitor a process and capture its output and error streams all without using a single extra thread in the process that was doing the management...</p>

<p>The standard way to capture a process' standard output and standard error is detailed pretty well in <a href="http://msdn2.microsoft.com/en-us/library/ms682499(VS.85).aspx">this article</a>, but the problem that I have with that is that it uses an anonymous pipe and you can't issue overlapped reads on anonymous pipes which means you need a thread to read the pipes and that spoils my scalability. However, as the documentation for <code><a href="http://msdn2.microsoft.com/en-us/library/aa365152(VS.85).aspx">CreatePipe()</a></code> states, <i>"Anonymous pipes are implemented using a named pipe with a unique name. Therefore, you can often pass a handle to an anonymous pipe to a function that requires a handle to a named pipe."</i> so I set about writing a new implementation of <code>CreatePipe()</code> that allows the pipe to be used with overlapped I/O.</p>

<p>The result is something like this:</p>
<pre class="brush: cpp gutter: false">CProcessOutputPipe::CProcessOutputPipe(
   const DWORD bufferSize,
   const Milliseconds timeout)
   :  m_name(GenerateName()),
      m_pipeServer(CreateServerPipe(m_name, bufferSize, timeout)),
      m_pipeClient(CreateClientPipe(m_name))
{
   Connect(timeout);
}
  
CProcessOutputPipe::CProcessOutputPipe(
   const _tstring &amp;name,
   const DWORD bufferSize,
   const Milliseconds timeout)
   :  m_name(name),
      m_pipeServer(CreateServerPipe(name, bufferSize, timeout)),
      m_pipeClient(CreateClientPipe(name))
{
   Connect(timeout);
}
  
void CProcessOutputPipe::Connect(
   const Milliseconds timeout)
{
   COverlappedWithEvent overlapped;

   if (0 == ::ConnectNamedPipe(m_pipeServer, &amp;overlapped))
   {
      const DWORD lastError = ::GetLastError();
      
      if (lastError == ERROR_IO_PENDING)
      {
         if (!overlapped.Wait(timeout))
         {
            throw CException(
               _T("CProcessOutputPipe::CProcessOutputPipe()"), 
               _T("Failed to connect client and server ends of the pipe."));
         }
      }
      else if (lastError != ERROR_PIPE_CONNECTED)
      {
         throw CWin32Exception(
            _T("CProcessOutputPipe::CProcessOutputPipe() - ConnectNamedPipe"), 
            lastError);
      }
   }
}
  
CProcessOutputPipe::~CProcessOutputPipe()
{
}
  
HANDLE CProcessOutputPipe::DetachWriteHandle()
{
   return m_pipeClient.Detach();
}
  
HANDLE CProcessOutputPipe::DetachReadHandle()
{
   return m_pipeServer.Detach();
}
  
static _tstring GenerateName()
{
   return CreateGUIDAsString();
}
  
static HANDLE CreateServerPipe(
   const _tstring &amp;name,
   const DWORD bufferSize,
   const Milliseconds defaultTimeout)
{
   const _tstring fullName = _T("\\\\.\\pipe\\") + name;
  
   CSecurityAttributes securityAttributes(false);
  
   CSmartHandle handle(::CreateNamedPipe(
      fullName.c_str(),
      PIPE_ACCESS_INBOUND |      // data flow from client to server
      FILE_FLAG_OVERLAPPED,      // overlapped mode 
      PIPE_TYPE_BYTE |           // byte-type pipe 
      PIPE_READMODE_BYTE |       // byte-read mode 
      PIPE_WAIT,                 // blocking mode 
      1,                         // number of instances 
      0,                         // output buffer size - no data flows this way
      bufferSize,                // input buffer size 
      defaultTimeout,            // client time-out 
      &amp;securityAttributes));     // dont allow handle inheritance
  
   if (handle == INVALID_HANDLE_VALUE)
   {
      const DWORD lastError = ::GetLastError();
  
      throw CWin32Exception(
         _T("CProcessOutputPipe::CreateServerPipe()"), 
         lastError);
   }
  
   return handle.Detach();
}
  
static HANDLE CreateClientPipe(
   const _tstring &amp;name)
{
   static const _tstring server = _T(".");
  
   const _tstring fullName = _T("\\\\") + server + _T("\\pipe\\") + name;
  
   CSecurityAttributes securityAttributes(true);
  
   CSmartHandle handle(::CreateFile( 
         fullName.c_str(),   
         GENERIC_WRITE, 
         0,                      // no sharing 
         &amp;securityAttributes,    // allow handle inheritance
         OPEN_EXISTING,          // opens existing pipe 
         FILE_FLAG_OVERLAPPED,   // default attributes 
         NULL));                 // no template file 
  
   if (handle == INVALID_HANDLE_VALUE)
   {
      const DWORD lastError = ::GetLastError();
  
      throw CWin32Exception(
         _T("CProcessOutputPipe::CreateClientPipe() - CreateFile"), 
         lastError);
   }
  
   DWORD dwMode = PIPE_READMODE_BYTE; 
  
   if (0 == ::SetNamedPipeHandleState( 
      handle,   // pipe handle 
      &amp;dwMode,  // new pipe mode 
      NULL,     // don't set maximum bytes 
      NULL))    // don't set maximum time 
   {
      const DWORD lastError = ::GetLastError();
  
      throw CWin32Exception(
         _T("CProcessOutputPipe::CreateClientPipe() - SetNamedPipeHandleState"), 
         lastError);
   }
  
   return handle.Detach();
}
</pre>

<p>Once the write end of this pipe is wired up to a process' standard output or error stream and an instance of <code>CAsyncFileReader()</code> is plugged in on the read end I can monitor the process and read the pipe using the threads in my IOCP's pool. Firing off a process and capturing/processing its output is now reliable (I can terminate it and any processes it creates) and scalable (I don't need any new threads in my process to monitor a new process tree). </p>

Of course this doesn't help me directly with my problems with CC.Net but I should be able to write a process launcher that I can spawn from within a CC.Net task and which runs my multi-process tests in a job and which can therefore clean up after then correctly when it's killed off. I'm also one step closer to a scalable C++ task execution engine; but that's another story.]]>
        
    </content>
</entry>

<entry>
    <title>Another CCNet patch</title>
    <link rel="alternate" type="text/html" href="http://www.lenholgate.com/blog/2008/02/another-ccnet-patch.html" />
    <id>tag:www.socketframework.com,2008:/blog//12.814</id>

    <published>2008-02-11T18:02:03Z</published>
    <updated>2010-12-28T13:52:59Z</updated>

    <summary>Here is another patch for CruiseControl.Net. This patch provides support for a Robocopy SourceControl provider. This gives a significant performance increase over the FileSystem SourceControl provider. To integrate this I needed to adjust how CCNet determined if an executable had...</summary>
    <author>
        <name>Len</name>
        
    </author>
    
        <category term="CC.Net" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="Continuous Integration" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="en-us" xml:base="http://www.lenholgate.com/blog/">
        <![CDATA[<p>Here is another patch for CruiseControl.Net. This patch provides support for a <a href="http://en.wikipedia.org/wiki/Robocopy">Robocopy</a> SourceControl provider. This gives a significant performance increase over the FileSystem SourceControl provider. To integrate this I needed to adjust how CCNet determined if an executable had succeeded, it used to rely on the exit code of the executable being 0 to indicate success, but Robocopy is more complex than that.</p>

<p><a href="http://www.lenholgate.com/patches/ccnet-3684-ProcessExecution-MultipleSuccessExitCodes-Patch.txt">ProcessExecution-MultipleSuccessExitCodes-Patch.txt</a></p>

<p>And here are the source files and test harness for the robocopy source control provider. I've found this to give much better performance than the file system provider and it supports the removal of deleted files and file and directory exlusions.</p>

<p><a href="http://www.lenholgate.com/patches/RobocopySourceControl.cs" onclick="_gaq.push(['_trackEvent', 'Downloads', 'RobocopySourceControl.cs']);">RobocopySourceControl.cs</a><br />
<a href="http://www.lenholgate.com/patches/RobocopyHistoryParser.cs" onclick="_gaq.push(['_trackEvent', 'Downloads', 'RobocopyHistoryParser.cs']);">RobocopyHistoryParser.cs</a><br />
<a href="http://www.lenholgate.com/patches/RobocopyHistoryParserTest.cs" onclick="_gaq.push(['_trackEvent', 'Downloads', 'RobocopyHistoryParserTest.cs']);">RobocopyHistoryParserTest.cs</a></p>

<p>The required source control block looks something like this:</p>

<pre class="brush: text gutter: false">        &lt;robocopy&gt;
          &lt;executable&gt;C:\Windows\System32\Robocopy.exe&lt;/executable&gt;
          &lt;autoGetSource&gt;true&lt;/autoGetSource&gt;
          &lt;repositoryRoot&gt;E:\CC-Build\Build\5.2.1\VS2005-x86\Deploy\JetByteTools\Admin&lt;/repositoryRoot&gt;
          &lt;workingDirectory&gt;Admin&lt;/workingDirectory&gt;
          &lt;additionalArguments&gt;/XD Output /XF *.idb&lt;/additionalArguments&gt;    
        &lt;/robocopy&gt;
</pre>

<p><code>&lt;executable&gt;</code> is optional and defaults to C:\Windows\System32\Robocopy.exe which is where the exe lives on vista, if you're using the XP then you need to supply the path to where the resource kit was installed...</p>

<p><code>&lt;additionalArguments&gt;</code> allows you to pass in any additional robocopy arguments that you want to supply. This is handy for <code>/XD</code> and <code>/XF</code> to exclude directories and files from the comparison and the copy.</p>

<p>By default the following arguments are used:</p>

<ul>
<li><code>/MIR - </code>MIRror a directory tree (equivalent to <code>/E</code> plus <code>/PURGE</code>).</li>
<li><code>/NP  - </code>No Progress - don't display % copied.</li>
<li><code>/X   - </code>Report all eXtra files, not just those selected.</li>
<li><code>/TS  - </code>Include source file Time Stamps in the output.</li>
<li><code>/FP  - </code>Include Full Pathname of files in the output.</li>
<li><code>/NDL - </code>No Directory List - don't log directory names.</li>
<li><code>/NS  - </code>No Size - don't log file sizes.</li>
<li><code>/NJH - </code>No Job Header.</li>
<li><code>/NJS - </code>No Job Summary.</li>
</ul>

<p>with the addition of "<code>/L</code>" being specified for the get modifications call; I don't think you can break the output format by providing additional arguments that I'm not expecting, but let me know if the history parser barfs with certain arguments...</p>

If someone can improve the history parser then that would be good, I get the feeling that it should be possible to use a single regex; which may or may not be an improvement...]]>
        
    </content>
</entry>

<entry>
    <title>CC.Net and Robocopy</title>
    <link rel="alternate" type="text/html" href="http://www.lenholgate.com/blog/2008/02/ccnet-and-robocopy.html" />
    <id>tag:www.socketframework.com,2008:/blog//12.812</id>

    <published>2008-02-02T13:15:03Z</published>
    <updated>2010-12-28T13:48:15Z</updated>

    <summary>I&apos;m continuing to tune my continuous integration system. Today I switched the &apos;deploy&apos; projects from using a CC.Net file system source control task to do the deployment to using Robocopy. This has sped things up nicely and made the deployments...</summary>
    <author>
        <name>Len</name>
        
    </author>
    
        <category term="CC.Net" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="Continuous Integration" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="en-us" xml:base="http://www.lenholgate.com/blog/">
        <![CDATA[<p>I'm continuing to tune my continuous integration system. Today I switched the 'deploy' projects from using a CC.Net file system source control task to do the deployment to using <a href="http://en.wikipedia.org/wiki/Robocopy">Robocopy</a>. This has sped things up nicely and made the deployments more configurable. One problem that I had was that the CC.Net "executable" task (the one that lets you run arbitrary executables) assumed that only exit codes of 0 were 'success' and Robocopy has a more complex strategy for exit codes (see <a href="http://kerpau.net/robocopy-exit-codes/">here</a>). Obviously you can wrap anything in a script that adjust the exit codes so that you can conform to CC.Net's idea, but, well, I found it neater to add to the executable task object so that it can be told which codes should be considered as successful completion. </p>

I think the next thing to do is write a file system source control provider that uses Robocopy to do all of the work; this would fix ALL of my issues with the existing CC.Net file system source control provider.]]>
        
    </content>
</entry>

<entry>
    <title>CC.Net File System Source Control speed up</title>
    <link rel="alternate" type="text/html" href="http://www.lenholgate.com/blog/2008/02/ccnet-file-system-source-control-speed-up.html" />
    <id>tag:www.socketframework.com,2008:/blog//12.811</id>

    <published>2008-02-01T11:22:16Z</published>
    <updated>2010-12-28T13:41:00Z</updated>

    <summary>Yesterday I mentioned that the file system source control provider in CC.Net was a little inefficient. I speculated as to how it might be working and how it might be improved. Well, as the saying goes, assume makes an ass...</summary>
    <author>
        <name>Len</name>
        
    </author>
    
        <category term="CC.Net" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="Continuous Integration" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="en-us" xml:base="http://www.lenholgate.com/blog/">
        <![CDATA[<p><a href="http://www.lenholgate.com/archives/000757.html">Yesterday</a> I mentioned that the file system source control provider in CC.Net was a little inefficient. I speculated as to how it might be working and how it might be improved. Well, as the saying goes, assume makes an ass out of you and me...</p>

<p>The file system source control provider is actually far far simpler than I expected. It scans the 'repository' file system tree and simply looks for files that have been written to since the last check. If it finds these files it builds modification details for them and then returns. When asked to "check out the changes" it simply copies the entire source tree to the destination... I've already added functionality to allow you to say that you'd like to remove deleted files, but I hadn't really tested it and don't actually use it, and it's broken as the file system provider wont spot a single delete as it only works on newer files... :( </p>

Anyway... The provider can be made slightly more efficient by checking the sub directory write times as well as just file write times, with that in place you can skip most of the file checking if a directory says that it hasn't been modified. I've done that and also added the ability to give it sub directories that it should completely ignore (useful for CVS 'state' directories and build output directories), I know that I can do this using the filter provider but it's more efficient to skip these directories during the file scan phase rather than after...]]>
        
    </content>
</entry>

<entry>
    <title>Living with continuous integration</title>
    <link rel="alternate" type="text/html" href="http://www.lenholgate.com/blog/2008/01/living-with-continuous-integration.html" />
    <id>tag:www.socketframework.com,2008:/blog//12.810</id>

    <published>2008-01-31T08:48:34Z</published>
    <updated>2010-12-28T13:40:37Z</updated>

    <summary>Well, it&apos;s about a month since I started running Cruise Control .Net and things have settled down somewhat now and I can almost go a day or two without tweaking my configuration or being tempted to fix issues in Cruise...</summary>
    <author>
        <name>Len</name>
        
    </author>
    
        <category term="CC.Net" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="Continuous Integration" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="Geek Speak" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="Socket Servers" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="en-us" xml:base="http://www.lenholgate.com/blog/">
        <![CDATA[<p>Well, it's about a month since I started running <a href="http://confluence.public.thoughtworks.org/display/CCNET/Welcome+to+CruiseControl.NET">Cruise Control .Net</a> and things have settled down somewhat now and I can almost go a day or two without tweaking my configuration or being tempted to fix issues in Cruise Control itself.</p>

<p>For those of you that haven't been following along:</p>
<ul>
<li><a href="http://www.lenholgate.com/archives/000744.html">First</a> I realised that the latest (1.3) release of Cruise Control .Net wouldn't work for me without some hacking.</li>
<li><a href="http://www.lenholgate.com/archives/000745.html">Then</a> I found that my idea of 'integration' wasn't quite the same as the simplistic situation for which Cruise Control .Net worked best;  I have lots of projects and running CC.Net with lots of project triggers wasn't fun.</li>
<li><a href="http://www.lenholgate.com/archives/000746.html">Later</a> I added even more projects to the system so that I could build all of my servers and all of the library code that they depend on for all of the compilers and architectures that I support.</li>
<li><a href="http://www.lenholgate.com/archives/000747.html">I hacked</a> at some 'low hanging fruit' and seriously questioned the design of CC.Net</li>
<li><a href="http://www.lenholgate.com/archives/000748.html">I finally</a> identified one of the main causes of the lack of scalability that I was seeing.</li>
<li><a href="http://www.lenholgate.com/archives/000754.html">I submitted</a> some of my patches to CC.Net.</li>
</ul>

<p>And now I'm just 'using it', and still complaining. Here are some random thoughts about my experiences so far.</p>

<p>My latest project trigger hacks have been working well. In the end the main scalability issue was the fact that the project trigger would retrieve ALL projects from a server and then search for the ONE project that it actually wanted. It did this using .Net remoting and it did this often. My changes mean that a project trigger now only requests the ONE project that it wants from a server (which means less work on the server that it is requesting from and less work in the trigger itself). You can also configure the trigger to be 'local' in which case it does all of the query work against internally without using .Net remoting. These changes made it possible to run with lots of projects and the hack around the project integrator and the <code>Sleep()</code> were taken out as the whole 'thread per project' design was far more complex than it first appeared. I have the project changes ready to submit as a patch but I'm waiting to get some form of response to my previous patches before doing so (if anyone wants the patches now, just ask).</p>

<p>My aim of being able to rebuild all of my projects for all of my supported compilers hit another snag when I found that the box that I was using to build VC6, VS2002, VS2003 and VS2005 didn't have enough disk space to build everything at once. There's an easy fix for that but for now I've cut down the number of compilers that it builds for. Likewise my plan to have the system set up to build multiple branches suffers from the same problem. The VS2008 and VS2005, x64 and x86 build setup on my main development box works nicely.</p>

<p>The whole thing runs too slowly for my liking. This is related to the design of CC.Net and isn't something I intend to try and change. I run with two build queues on each build machine; build and deploy projects are queued in one queue and test projects are queued in another. This is mainly because tests must run sequentially as sometimes (in the case of server tests that start servers and whatever) the test can't run at the same time as another test on the same box. The test queue has to be sequential but the build queue could be processed by multiple threads (i.e. it would be nice to allow 'x' builds to run at once and be fed from a single queue, you can't do that with the current CC.Net design). The 1 thread per project design of CC.Net bugs me, but right now we're scaling OK, 227 threads on the XP build box and 484 on the Vista x64 box. The project trigger polling system is inefficient, but at least with my local project trigger changes it's not a show stopper anymore.</p>

<p>My project dependency design is flawed in that there are only three projects that access the CVS repository and all others use file system source control providers to copy from the local CVS tree to their own build trees. I'm doing things this way because I use CVS modules a lot and I want to make sure that my projects build in an environment where only the code that I say they depend on is available to them. So, for example, I have lib A that depends on libs B and C and the CVS module for A checks out A and B and C which is fine but when you run an update with -d you get everything else that lives in the directory where A and B and C live (such as all of the other library projects in my world). This is BAD for me, so, instead, I have a CVS project which checks out all of the library projects into one place and each build project knows which dependencies to copy locally. It all works nicely but it means that when the CVS tree is updated ALL projects are scheduled for a rebuild and most then don't rebuild (as they havent actually been changed) but it takes a while for the queue to clean out... This is made worse by my abundant use of the file system source code provider; every time a CVS update happens for any project all projects use the file system provider to compare the files they have with the bits of the CVS tree that they use; this touches a LOT of files and is pretty slow. I'd quite like to write a file system source code provider that uses a single manifest file to store the current state of the file system that it's watching (file and directory details and an MD5 hash of contents) so that it only has to scan one half of the files that it currently scans (but that's work for another day).</p>

<p>The program that I have that generates the config files for my projects works great. It was well worth writing. It's still "a small step" away from being something that you can just run on a series of .sln files but it's easy to add projects and configure dependencies, etc. It's also very easy to tweak how the project configs are generated. The config file for the 5.2.1 branch of my server framework for VS2005 and VS2008 (x86 and x64 built separately) is <a href="http://www.lenholgate.com/patches/ccnet.config">here</a>.</p>

<p>When you're running with this many projects the 'polling' nature of the CC.Net task tray monitoring system becomes a performance issue and the fact that you can't group projects into folders for easy display makes knowing what's going on more complex that it needs to be.</p>

<p>I'm currently contemplating the next stage which is to build with different environment variables to allow me to compile against different versions of third party code (STLPort, OpenSSL and platform SDKs). This requires a new CC.Net task (one that sets up the environment for the tasks that it contains) but right now this is on hold as the increase in projects and disk space required don't make it feasible.</p>

<p>Continuous integration has worked well to flush out some subtle bugs in some of the more complex unit tests and, in some cases, has flushed out bugs in the code too. Running your tests over and over again on various machines with the code built by various compilers and when the machines are running other code and so are 'randomly busy' is a great way to force race conditions and threading bugs out into the open. The fact that the failures then prevent further projects from building then gives an incentive to fix them. This means that the 5.2.1 release will contain slightly more bug fixes than originally planned... It's also very useful to have all supported compilers building all changes straight away, it makes it easier to work around VC6 template issues if the latest changes that you just made in VS2008 are built NOW rather than in two months time when you do the next release...</p>

All in all I'm pleased with the results, but I can't help viewing it as a stepping stone to a system that actually works the way that I want it to...]]>
        
    </content>
</entry>

<entry>
    <title>CruiseControl.Net patches</title>
    <link rel="alternate" type="text/html" href="http://www.lenholgate.com/blog/2008/01/cruisecontrolnet-patches.html" />
    <id>tag:www.socketframework.com,2008:/blog//12.807</id>

    <published>2008-01-14T14:25:19Z</published>
    <updated>2010-12-28T13:37:44Z</updated>

    <summary>Hacking CruiseControl.Net to work better for my specific circumstances (lots of projects that depend on lots of projects) has resulted in the following patches to revision 3607 of ccnet which I&apos;m just about to submit to the developers. These patches...</summary>
    <author>
        <name>Len</name>
        
    </author>
    
        <category term="CC.Net" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="Continuous Integration" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="Geek Speak" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="en-us" xml:base="http://www.lenholgate.com/blog/">
        <![CDATA[<p>Hacking CruiseControl.Net to work better for my specific circumstances (lots of projects that depend on lots of projects) has resulted in the following patches to revision 3607 of ccnet which I'm just about to submit to the developers.</p>

<p>These patches are 'supporting patches' to the main ProjectTrigger and integrator changes, I thought I'd start with the simple ones... None of these patches should change existing functionality, all require new, optional, properties to be specified to activate the new code. All patches include updated and or new tests.</p>

<p>1) IntervalTrigger - Added <code>initialIntervalSeconds</code>.</p>

<p><code>initialIntervalSeconds</code> is a new property that controls when the trigger fires the first time. The idea being that you might want to have a trigger that fires every 'X' seconds but you dont want to have to wait a full 'X' seconds for it to fire when the server first starts up. If <code>initialIntervalSeconds</code> is set then the trigger will first fire after this number of seconds and will then switch to firing every <code>intervalSeconds</code> as normal. </p>

<p>I find this useful when running ccnet on machines which aren't dedicated build machines (i.e. machines that don't have ccnet running all the time) for projects that I don't want to run very often but, when I start ccnet, I want them to be checked...</p>

<p><a href="http://www.lenholgate.com/patches/ccnet-3607-IntervalTrigger-InitialInterval-Patch.txt" onclick="_gaq.push(['_trackEvent', 'Downloads', 'ccnet-3607-IntervalTrigger-InitialInterval-Patch.txt']);">ccnet-3607-IntervalTrigger-InitialInterval-Patch.txt</a></p>

<p>2)  <code>IFileSystem</code> changes - Added some more functionality to support the tests for the changes in patches 3 and 4.</p>

<p>Added <code>CreateDirectory()</code> and <code>DeleteDirectory()</code>.</p>

<p><a href="http://www.lenholgate.com/patches/ccnet-3607-IFileSystem-Patch.txt" onclick="_gaq.push(['_trackEvent', 'Downloads', 'ccnet-3607-IFileSystem-Patch.txt']);">ccnet-3607-IFileSystem-Patch.txt</a></p>

<p>3) CVS source control - allow non existent working directory to be created.</p>

<p>Changes to <code>DoesCvsDirectoryExist()</code> and <code>NewProcessInfoWithArgs()</code> to allow for creation of working directory if required for checkout or update only. </p>

<p><a href="http://www.lenholgate.com/patches/ccnet-3607-CVS-CreateWorkingDirectory-Patch.txt" onclick="_gaq.push(['_trackEvent', 'Downloads', 'ccnet-3607-CVS-CreateWorkingDirectory-Patch.txt']);">ccnet-3607-CVS-CreateWorkingDirectory-Patch.txt</a></p>

<p>4) FileSourceControl - allow working directory to be specified and added a property to force deletion of remove files during <code>GetSources</code>.</p>

<p>I've added a working directory property so that you can have a FileSourceControl source control block that checks files out to a sub directory of the project's working directory. This is useful if you are accumulating files from multiple source control blocks into a directory tree and want to place some of the files in subdirectories.</p>

<p>The second change forces the working directory to be removed before an update occurs. This is a brute force method of removing files which no longer exists in the repository from the working copy.</p>

<p><a href="http://www.lenholgate.com/patches/ccnet-3607-FileSourceControl-Patch.txt" onclick="_gaq.push(['_trackEvent', 'Downloads', 'ccnet-3607-FileSourceControl-Patch.txt']);">ccnet-3607-FileSourceControl-Patch.txt</a></p>

<p>5) Visual C++ version 6 "MsDev" build task.</p>

<p>Added a new build task that works with Visual studio 6.</p>

<p><a href="http://www.lenholgate.com/patches/ccnet-3607-MsDevTask.cs" onclick="_gaq.push(['_trackEvent', 'Downloads', 'ccnet-3607-MsDevTask.cs']);">ccnet-3607-MsDevTask.cs</a><br />
<a href="http://www.lenholgate.com/patches/ccnet-3607-MsDevTaskTest.cs" onclick="_gaq.push(['_trackEvent', 'Downloads', 'ccnet-3607-MsDevTaskTest.cs']);">ccnet-3607-MsDevTaskTest.cs</a></p>

<p>6) Allow you to specify which version of Visual Studio is used if mutliple versions are installed without having to know the executable path. Although you can currently select the version of visual studio that is used you need to do it by providing the full path to the executable this patch allows you to simply say "I'd like Vs2003 please" and then used the registry query code to work out where it is installed. This is enabled by specifying a Version property with a value of one of the following: "9.0", "8.0", "7.1", "7.0", "VS2008", VS2005", "VS2003" or "VS2002". I considered, and rejected, having the DevenvTask read the solution file and work out the appropriate version as unecessarilly complex.</p>

<p>This removes the need to embed potentially machine specific information in a config file just to select which version of VS you want to use; though, unfortunately it doesn't help me on Vista x64 as the InstallDir value isnt present in the registry :(</p>

<p><a href="http://www.lenholgate.com/patches/ccnet-3607-DevEnvTask-VersionSelect-Patch.txt" onclick="_gaq.push(['_trackEvent', 'Downloads', 'ccnet-3607-DevEnvTask-VersionSelect-Patch.txt']);">ccnet-3607-DevEnvTask-VersionSelect-Patch.txt</a></p>

<p>With any luck these will be approved and incorporated into the subversion ccnet repository soon, but, if they're not you can get them from here.</p>

<p>I'm currently working on tidying up the ProjectTrigger changes that are mentioned <a href="http://www.lenholgate.com/archives/000748.html">here</a> and these will be released later. The <code>Sleep()</code> changes to the <a href="http://www.lenholgate.com/archives/000747.html">ProjectIntegrator</a> were more complex than expected (mainly due to the design of the integration thread), although my hacked version works for me it's not good for prime-time so I'm working on a better fix.</p>]]>
        
    </content>
</entry>

<entry>
    <title>Local project trigger...</title>
    <link rel="alternate" type="text/html" href="http://www.lenholgate.com/blog/2008/01/local-project-trigger.html" />
    <id>tag:www.socketframework.com,2008:/blog//12.801</id>

    <published>2008-01-04T18:45:31Z</published>
    <updated>2010-12-28T13:26:00Z</updated>

    <summary>I hacked another fix into CruiseControl.Net today and we now almost have an acceptable level of performance in my particular (some may say warped) circumstances. The trick was to hack the project trigger to be a &quot;local project&quot; trigger. By...</summary>
    <author>
        <name>Len</name>
        
    </author>
    
        <category term="CC.Net" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="Continuous Integration" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="en-us" xml:base="http://www.lenholgate.com/blog/">
        I hacked another fix into CruiseControl.Net today and we now almost have an acceptable level of performance in my particular (some may say warped) circumstances. The trick was to hack the project trigger to be a &quot;local project&quot; trigger. By default the project trigger uses .Net remoting to talk to the CruiseControl.Net server where the project is hosted. In my case this meant that all of my project triggers (and there are lots!) were using remoting to talk to the server that they were running in; this looked like it could be a triffle inefficient so I hacked in a way for them to check the local server&apos;s projects directly. I&apos;m now down to CC.Net taking between 0 and 4% of my CPU rather than 65-90%...
        
    </content>
</entry>

<entry>
    <title>Thread.Sleep(100); // sleep for a short while, to avoid hammering CPU</title>
    <link rel="alternate" type="text/html" href="http://www.lenholgate.com/blog/2008/01/threadsleep100-sleep-for-a-short-while-to-avoid-hammering-cpu.html" />
    <id>tag:www.socketframework.com,2008:/blog//12.800</id>

    <published>2008-01-03T22:41:20Z</published>
    <updated>2010-12-28T13:25:30Z</updated>

    <summary>I am intending to check out some of the other build servers that people have been suggesting, but today I was too busy with real work so I just left a cut down version of my latest CruiseControl.Net configuration running...</summary>
    <author>
        <name>Len</name>
        
    </author>
    
        <category term="CC.Net" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="Continuous Integration" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="Geek Speak" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="en-us" xml:base="http://www.lenholgate.com/blog/">
        <![CDATA[<p>I am intending to check out some of the other build servers that people have been suggesting, but today I was too busy with real work so I just left a cut down version of my latest CruiseControl.Net configuration running on one of my boxes and fixed a few issues whilst doing proper work most of the time... This evening I decided to go and look at why CruiseControl.Net scales so poorly and the first issue that I came across is the title of this blog posting...</p>

<p>In the <code>ProjectIntegrator</code> class there's a <code>Run()</code> method (because, of course, all projects have their own threads...) and at the bottom of the run loop there's this piece of code <code>Thread.Sleep(100);</code> along with, what I assume is a wishful thinking comment... Of course with 100s of projects (each with its own thread) a 100ms sleep from each of them before they go off and do busy work is a nightmare scenario. Every thread wakes up and gets scheduled every 100ms... No wonder the system staggers around like a drunken monkey and brings even beefy development boxes to their knees.</p>

<p>The strange thing is that the project knows how long it is until it needs to check again (and run through the loop again). The triggers that trigger the project have a "next time to run" time set on them, the project just doesn't bother to query them and use that as the basis of how long it should go to sleep for... A five minute hack job and my version of CC.Net does this and I'm able to run my 'all compilers' build again. It's still not the kind of performance that I'd like from a system that's basically doing nothing (albeit lots of nothing) but it's considerably better than what went before...</p>

<p>The next target on my search for 'the bleeding obvious' (I think 'low hanging fruit' is the polite term...) is the corresponding 200ms sleep that happens during a busy loop when the project is in the integration queue... I expect that I'll fix it in a similar way for now, but, really, it should be waiting on a request to terminate the thread (and not just a boolean flag, this is what auto reset events are for!) and, likewise, waiting on the project being removed from the queue...</p>

Of couse, what I'd really like to do is remove at least 90% of the threads and most of the polling and have the completion of a project actively trigger the projects that depend on it, but I think the simplistic design of CC.Net is too ingrained for me to achieve much in a reasonable time-frame.]]>
        
    </content>
</entry>

<entry>
    <title>More Cruise Control .Net woes</title>
    <link rel="alternate" type="text/html" href="http://www.lenholgate.com/blog/2008/01/more-cruise-control-net-woes.html" />
    <id>tag:www.socketframework.com,2008:/blog//12.799</id>

    <published>2008-01-02T18:46:34Z</published>
    <updated>2010-12-28T13:17:27Z</updated>

    <summary>I&apos;ve almost got something that works out of CruiseControl.Net. Once I&apos;d hacked in some fixes for the project triggers and other stuff that didn&apos;t work the way I wanted it to I concentrated on generating the config files that I...</summary>
    <author>
        <name>Len</name>
        
    </author>
    
        <category term="CC.Net" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="Continuous Integration" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="Geek Speak" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="en-us" xml:base="http://www.lenholgate.com/blog/">
        <![CDATA[<p>I've almost got something that works out of CruiseControl.Net. Once I'd <a href="http://www.lenholgate.com/archives/000745.html">hacked in some fixes</a> for the project triggers and other stuff that didn't work the way I wanted it to I concentrated on generating the config files that I required and testing the system. </p>

<p>The good news is that I now have an integration system that works, it can build, test and deploy libraries in such a way that dependent libraries and applications are rebuilt as new versions of deployed artifacts become available. The config file generator can generate files for all of the build machines that I'm currently using and builds projects per machine, per compiler and per branch so that I can have several compilers building several branches on several machines and can see what's what from the CCTray application (it would be nice if that allowed you to group projects...).</p>

<p>The bad news is that CruiseControl.Net just isn't really designed for this kind of scalability... I have lots of projects (each library consists of 4 projects (build, build tests, run tests and deploy)), my most basic server application needs 6 libraries and the exe projects, so that's almost 30 CC.Net projects (per branch, per compiler, and (optionally) per architecture). Adding a server that doesn't use any new libraries means 2 more projects (servers have build and test projects)... </p>

<p>It's better than not having an integration system but on my old dev box which does the builds for VC6, VS2002, VS2003 and VS2005 I currently have a CC.Net server running with 190 threads which is thrashing the box with pointless busy work as it polls itself to determine if the projects need to be rebuilt. My main x64 dev box is almost as bad, it's building for VS2005 and VS2008 for both x86 and x64 (so total projects X 4 for each branch that I want to build)...  And this is with the whole system building using two queues, so there's only ever two concurrent actions in progress (a build and a test run can run at the same time). </p>

Still, it's free and it has flushed out some project file configuration issues which prevented random sequences of x86 and x64 builds from working (but these probably wouldn't have bitten real users of the code) and it's also flushed out a race condition bug that was causing a test run to fail every so often.]]>
        
    </content>
</entry>

<entry>
    <title>Fighting with CruiseControl.Net...</title>
    <link rel="alternate" type="text/html" href="http://www.lenholgate.com/blog/2007/12/fighting-with-cruisecontrolnet.html" />
    <id>tag:www.socketframework.com,2007:/blog//12.798</id>

    <published>2007-12-21T21:55:06Z</published>
    <updated>2010-12-28T13:25:06Z</updated>

    <summary>I&apos;ve been trying to get my code to build with CruiseControl.Net this week. It&apos;s taken longer than I&apos;d hoped, but I&apos;m almost there. It became easier when I switched from assuming various parts of CruiseControl.Net would &quot;work as I expected...</summary>
    <author>
        <name>Len</name>
        
    </author>
    
        <category term="CC.Net" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="Continuous Integration" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="Geek Speak" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="en-us" xml:base="http://www.lenholgate.com/blog/">
        <![CDATA[<p>I've been trying to get my code to build with CruiseControl.Net this week. It's taken longer than I'd hoped, but I'm almost there. It became easier when I switched from assuming various parts of CruiseControl.Net would "work as I expected them to" to assuming that I'd have to delve into the source and change things...</p>

<p>On the whole I'm a bit disappointed in CruiseControl.Net. I'm sure it works very nicely for simple situations, such as where you pull everything out of your repository and build it with a single project, but, when you're trying to do more complex things it seems to be a bit fragile. Part of this fragility is probably due to me; I sort of assume things should work how I'd expect them to work ;) So, for example, when I saw that there was support for combining triggers in 'and' and 'or' chains I assumed that I could do that with all of the other triggers... However, it seems that if you try and combine project triggers then you don't really get what you want; project triggers reset themselves after they've 'fired' rather than waiting until they're told that an integration has been completed (which is when some of the other triggers reset themselves...). So, if you have several project triggers ANDed together then you might find that you're waiting a hell of a long time for all of them to fire at the same time... And since the "multiple trigger" trigger applies a "C programmer predictability" to how it deals with its ANDing (it short-circuits, so if the first trigger hasn't fired yet then the rest don't get called at all), but it doesn't do the same to how it works out when its triggers will next change state (it scans all of the triggers and takes the lowest of the "when are you due to go off next" times), you can find that your dashboard is showing that some of your projects will next be built in the past... Oh, and project triggers only fire when a project changes state, so if you're waiting for project A to become successful and it does then you'll run, but if the server has just started and it's already successful then, well, you'll wait until it builds again... </p>

Of course, the good thing about Open Source is that you have the source and you can change things. Well, at least I <i>can</i> change things... I've been changing things...]]>
        <![CDATA[<p>I admit that the way I'm trying to structure my build is probably a bit complex.<br />
</p>
<ul>
<li>My code tends to be broken up into separate libraries depending on function, so I have a series of libs that I link with any exe project that I'm building.</li>
<li>Each library project depends on 0 or more other library projects.</li>
<li>Each library project has a test harness, which builds as an exe from a different project, and there also tend to be mock libraries which provide mocks for the interfaces that the library contains. A library's test may or may not use the library's mocks, but it generally does.</li>
<li>I don't want to overly stress my CVS server.</li>
<li>My code is structured as CVS modules, but CVS is a bit broken with respect to doing an update on a module that is in a subdirectory of the repository if you decide to tell it to 'create any missing directories' with the <code>-d</code> option.</li>
<li>I'd like the code to build in isolation, that is, with only the dependencies that I think it should actually have.</li>
<li>I don't want to overly stress my build machine, after all, it could be the machine I'm working on, or a virtual machine running on the machine I'm working on, etc.</li>
</ul>
<p>What I've ended up with is a series of CruiseControl projects per library. Most libraries have a Build, Build Tests, Run Tests and Deploy project. There's a central "Get Sources" project that grabs the whole of the directory structure where all of the libraries live, this prevents/works around the problem that CVS has with updating modules in isolation. Since this project gets a whole tree I then use dependent projects which trigger off of the "Get Sources" project to copy just the parts of the tree that each project needs. These use the file system source control provider.</p>

<p>This is where I had to make my first changes. By default, with the build of the code that I have, CruiseControl.Net 1.3.0.2918, has a file system source control provider that can only copy to your project's working directory. So, if you want to chain a series of file system source control providers together and have them move files into a series of sub directories then you're out of luck... This is an easy fix, just give the provider its own working directory property and 'do the right thing' when you decide where to copy your files. My next problem was that combining a file system source control provider with the CVS provider didn't work quite how I wanted it to. When the CVS provider updates its tree it fiddles with the timestamps on the files in the CVS directories, the file system source control provider considers that a 'change' and therefore tells CruiseControl.Net that the source is modified, which triggers a build every time the CVS dump is updated... A quick hack so that the file system provider ignores anything in a directory called CVS and things start to work how I'd like them to (and yes, this can be generalised into a property with a string of 'ignore directories'). Finally there's the problem that, when it has decided that there are changes and is then asked to update from the repository, the file system provider just copies everything in its source directory into its destination directory. This, effectively, merges the new directory with the old; if anything has been deleted from the new directory it's still in the updated directory... Right now the fix is to delete the target directory before updating (but after checking for changes!), this works but causes more code to be recompiled on a change; I've made it optional.</p>

<p>So, now I have a source control system that works; I pull a directory out of my repository and have a project that gets updates for this as they happen. I then have dependent projects that use file system providers to get just the bits that they need into their build directories. This enables each library to build with only the code that should be available.</p>

<p>At around this point the aforementioned holes in the multiple trigger and the project trigger come to light. More changes to the code leave me with a multiple trigger that, er, well, works, and a project trigger that is much more configurable. For starters I want to restrict when things try to build to when they're likely to be able to build. This means that if project A depends on project B and C then it shouldn't even try to build until project B and C have both built successfully. However, once they have built then changes to either should cause A to rebuild. This isn't possible with CruiseControl.Net out of the box. A change to the project trigger so that it can be set to trigger if the project has ever built successfully means that the first part of project A's trigger can be an ANDing together of project B and C being available. Next we can and that trigger with a trigger which looks for changes in either the 'Get Sources' project, A or B. This gives the required amount of control; A doesn't ever get scheduled to build until B and C are built, but once they are it will rebuild if new source arrives or if A or B rebuilds... Oh, and then there's the "project triggers don't trigger on the first integration of the projects that they monitor" issue... Er, mine can, enough said. This was a bit of a killer really, it was another "if the server was shutdown before you built but all your dependencies were built then you'll never built until they're changed" issues...</p>

<p>By this stage I have some code that writes the project files based on some hard coded dependencies; I've hand crafted it to build some of the base libraries to make sure that things work. Once things work, and we're nearly there, then I'll add in the Visual Studio project parsing code and have build the CruiseControl projects from the Visual Studio projects. Since each step is quite hard to test I've found myself doing clean starts of the integration server quite a lot. This led to another change, the file state object requires that the directory in which you're storing state exists; for me that's a pain, I like to blow away everything from a single root and then run the server again and have it 'just work' and create all of the directories that it needs. My file state object doesn't barf if the target directory doesn't exist, it creates it. </p>

<p>It was at this point that I began to wish that the CruiseControl.Net "pluggin" architecture was a bit more "plug in" rather than "plugged in". It seems that all of the various 'providers' and 'tasks' etc are all compiled into the same assembly. Sure you can implement another, but I'd prefer to be able to do that in my own assembly so that I can, er, well, plug it in. I'd be more than happy for someone to correct me here and tell me to go and read some web page to find out how to do it... </p>

<p>My final change was just for convenience. My "Get Sources" project runs every 10 mins, however, I don't really like the fact that when I start the server up it takes 10 mins before it runs this task for the first time. So, I added a property to the <code>IntervalTrigger</code> that allows you to specify a different 'initial timeout'. </p>

<p>I'm not quite where I want to be, but I'm a lot closer. I have code that generates my CruiseControl projects, which makes changing things easy, it has already allowed me to generate different projects for different compilers and to optionally have the x64 builds build and test as well as the Win32 builds. I've found quite a few build bugs, as it's becoming easier to build everything in every way each time I change anything. This is a good thing. I just wish that CruiseControl.Net had done it all straight out of the zip...</p>]]>
    </content>
</entry>

<entry>
    <title>CruiseControl.Net</title>
    <link rel="alternate" type="text/html" href="http://www.lenholgate.com/blog/2007/12/cruisecontrolnet.html" />
    <id>tag:www.socketframework.com,2007:/blog//12.797</id>

    <published>2007-12-18T17:11:29Z</published>
    <updated>2010-12-28T13:16:14Z</updated>

    <summary><![CDATA[I'm currently working on two new releases of The&nbsp;Server&nbsp;Framework, the first is a simple release to add support for VS2008 and the second is the next "feature" release. Due to the number of projects and example servers and supported compilers...]]></summary>
    <author>
        <name>Len</name>
        
    </author>
    
        <category term="CC.Net" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="Continuous Integration" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="Geek Speak" scheme="http://www.sixapart.com/ns/types#category" />
    
    
    <content type="html" xml:lang="en-us" xml:base="http://www.lenholgate.com/blog/">
        <![CDATA[<p>I'm currently working on two new releases of <a href="http://www.serverframework.com/">The&nbsp;Server&nbsp;Framework</a>, the first is a simple release to add support for VS2008 and the second is the next "feature" release. Due to the number of projects and example servers and supported compilers that I test with it can take some time to finalise a release; each example server has to be checked out, built in each build configuration with each supported compiler and then the tests need to be run... Last time I did this I had planned to put aside some time to sort out an automatic build to do it for me, this time I might actually do it...</p>

<p>I've been playing with CruiseControl.Net and it works reasonably well and since the source is available I've been able to patch up a couple of things that didn't work quite how I needed... I can see that I'm going to need to write something that writes a config file for me as most of the examples will have similar project definitions and I figure I should manage the duplication in one place... It's a pity that some nodes can't logically 'cascade' down the XML tree...</p>

<p>So far my project structure has been giving me a few problems but I've fixed them by hacking the cruise control code (oh the double edged sword of open source software)... The first issue was that the CVS source countrol provider required that the directory that you wanted to check out into (its working directory) existed. Probably not a problem most of the time but I wanted to run multiple CVS source control providers and have them check out different modules into different directories. Next the file system source code provider would only copy into the main project working directory; likewirse I needed to chain multiple ones of these together and so wanted each one to copy into a directory that I could specify...</p>

Still, I'm getting there. I'm slowly developing a structure that puts minimal strain on the CVS server and allows projects to be built and their tests to be run and that causes dependent projects to only rebuild if their dependencies are changed...]]>
        
    </content>
</entry>

</feed>



