Here's a strange one relating to a combination of the 'define' and 'include' functionality that the CC.NET preprocessor exposes. We're running CCNet 1.4.4.83, and our ccnet.config
file is structured and split to make best use of common element blocks stored in subfiles that are included by the main config file; we also split out the core project definitions into their own include files too, leaving ccnet.config
as essentially a series of includes thus:
<cruisecontrol xmlns:cb="urn:ccnet.config.builder">
<!-- Configuration root folder - defined so we can use it as a symbol instead of using slightly confusing relative paths -->
<cb:define ccConfigRootFolder="C:\CruiseControl.NET\Config"/>
<!-- Globals - standard or shared build elements common to all projects -->
<cb:include href="$(ccConfigRootFolder)\Globals\globals.xml" xmlns:cb="urn:ccnet.config.builder"/>
<!-- CruiseControl.NET Configuration - refresh configuration if changed -->
<cb:include href="$(ccConfigRootFolder)\CCNet Configuration\ccnet_configuration.xml" xmlns:cb="urn:ccnet.config.builder"/>
<!-- Project #1 -->
<cb:include href="$(ccConfigRootFolder)\Project1\project1.xml" xmlns:cb="urn:ccnet.config.builder"/>
<!-- Project #2 -->
<cb:include href="$(ccConfigRootFolder)\Project2\project2.xml" xmlns:cb="urn:ccnet.config.builder"/>
</cruisecontrol>
This works a treat - the preprocessor correctly includes and parses the <define>
elements in globals.xml
(and recursively parses further files included from globals.xml
too) and the projects included afterwards (which contain references to those defined elements) are parsed correctly.
To further refine ccnet.config
in an attempt to reduce the possibility of mistakes breaking the build process, we altered it to look like this:
<cruisecontrol xmlns:cb="urn:ccnet.config.builder">
<!-- Configuration root folder -->
<cb:define ccConfigRootFolder="C:\CruiseControl.NET\Config"/>
<!-- Project 'include' element definition -->
<cb:define name="ProjectInclude">
<cb:include href="$(ccConfigRootFolder)$(ccIncludePath)" xmlns:cb="urn:ccnet.config.builder"/>
</cb:define>
<!-- Include common gobal elements -->
<cb:ProjectInclude ccIncludePath="\Globals\globals.xml"/>
<!-- Project #1 -->
<cb:ProjectInclude ccIncludePath="\Project1\project1.xml"/>
<!-- Project #2 -->
<cb:ProjectInclude ccIncludePath="\Project2\project2.xml"/>
</cruisecontrol>
As you can see, we embedded the common, repeated, part of the 'include' definition in it's own defined block, and then use that to effect each include using the path as a parameter - the idea being that future modifiers of the file won't have the opportunity to accidentally forget something on their new included project lines (such as the preprocessor URN); as long as their xml file exists and they get the path to it right, the rest is taken care of in the common include definition.
The only problem is that this doesn't work - for some reason, it looks like the globals.xml
file isn't being parsed properly (or maybe even included) because the projects included after it complain of not having elements defined; that is, references to elements defined in the globals file don't appear to have been 'registered', because the projects don't recognise them.
We've tried taking out the nested includes from globals.xml
and including them directly at the top level, to no avail. Commenting-out the first troublesome element reference in the project just causes Validator to complain about the next one, with message "Preprocessing failed loading the XML: Reference to unknown symbol XXXXX". If we embed the body of globals.xml
into ccnet.config
, however, this works. Bizarre though it may sound, it's as though the preprocessor is utterly failing to parse globals.xml
but then happily chewing through the project files, only to then fail because the global references aren't defined.
Validator is failing silently if this is the case, however. And of course because it can't parse the project XML properly, we get nothing in the 'Original' or 'Processed' tabs either. The CruiseControl.NET Service itself fails to start with an unhelpful exception:
Service cannot be started. System.Runtime.Serialization.SerializationException: Type 'ThoughtWorks.CruiseControl.Core.Config.Preprocessor.EvaluationException' in Assembly 'ThoughtWorks.CruiseControl.Core, Version=1.4.4.83, Culture=neutral, PublicKeyToken=null' is not marked as serializable. Server stack trace: at System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter) at System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.Serialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter) at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object graph, Header[] inHeaders, __BinaryWriter serWriter, Boolean fCheck) at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Ser...
All the documentation says this should work, and there's no mention of any incompatibility or inconsistency when using 'include' inside a 'define'. So I'm stumped, and any insight or advice would, at this stage, be highly regarded.