/// <summary>
        /// Write the latest version of the XML definition file.
        /// Always refer to the schema location as a relative path to the bam executable.
        /// Always reference the default namespace in the first element.
        /// Don't use a namespace prefix on child elements, as the default namespace covers them.
        /// </summary>
        public void Write()
        {
            if (System.IO.File.Exists(this.XMLFilename))
            {
                var attributes = System.IO.File.GetAttributes(this.XMLFilename);
                if (0 != (attributes & System.IO.FileAttributes.ReadOnly))
                {
                    throw new Exception("File '{0}' cannot be written to as it is read only", this.XMLFilename);
                }
            }

            this.Dependents.Sort();

            var document = new System.Xml.XmlDocument();
            var namespaceURI = "http://www.buildamation.com";
            var packageDefinition = document.CreateElement("PackageDefinition", namespaceURI);
            {
                var xmlns = "http://www.w3.org/2001/XMLSchema-instance";
                var schemaAttribute = document.CreateAttribute("xsi", "schemaLocation", xmlns);
                var mostRecentSchemaRelativePath = "./Schema/BamPackageDefinitionV1.xsd";
                schemaAttribute.Value = System.String.Format("{0} {1}", namespaceURI, mostRecentSchemaRelativePath);
                packageDefinition.Attributes.Append(schemaAttribute);
                packageDefinition.SetAttribute("name", this.Name);
                if (null != this.Version)
                {
                    packageDefinition.SetAttribute("version", this.Version);
                }
            }
            document.AppendChild(packageDefinition);

            // package description
            if (!string.IsNullOrEmpty(this.Description))
            {
                var descriptionElement = document.CreateElement("Description", namespaceURI);
                descriptionElement.InnerText = this.Description;
                packageDefinition.AppendChild(descriptionElement);
            }

            // package repositories
            var packageRepos = new StringArray(this.PackageRepositories);
            // TODO: could these be marked as transient?
            // don't write out the repo that this package resides in
            packageRepos.Remove(this.GetPackageRepository());
            // nor an associated repo for tests
            var associatedRepo = this.GetAssociatedPackageDirectoryForTests();
            if (null != associatedRepo)
            {
                packageRepos.Remove(associatedRepo);
            }
            if (packageRepos.Count > 0)
            {
                var packageRootsElement = document.CreateElement("PackageRepositories", namespaceURI);
                var bamDir = this.GetBamDirectory() + System.IO.Path.DirectorySeparatorChar; // slash added to make it look like a directory
                foreach (string repo in packageRepos)
                {
                    var relativePackageRepo = RelativePathUtilities.GetPath(repo, bamDir);
                    if (OSUtilities.IsWindowsHosting)
                    {
                        // standardize on non-Windows directory separators
                        relativePackageRepo = relativePackageRepo.Replace(System.IO.Path.DirectorySeparatorChar, System.IO.Path.AltDirectorySeparatorChar);
                    }

                    var rootElement = document.CreateElement("Repo", namespaceURI);
                    rootElement.SetAttribute("dir", relativePackageRepo);
                    packageRootsElement.AppendChild(rootElement);
                }

                packageDefinition.AppendChild(packageRootsElement);
            }

            if (this.Dependents.Count > 0)
            {
                var dependentsEl = document.CreateElement("Dependents", namespaceURI);
                foreach (var package in this.Dependents)
                {
                    var packageName = package.Item1;
                    var packageVersion = package.Item2;
                    var packageIsDefault = package.Item3;
                    System.Xml.XmlElement packageElement = null;

                    {
                        var node = dependentsEl.FirstChild;
                        while (node != null)
                        {
                            var attributes = node.Attributes;
                            var nameAttribute = attributes["Name"];
                            if ((null != nameAttribute) && (nameAttribute.Value == packageName))
                            {
                                packageElement = node as System.Xml.XmlElement;
                                break;
                            }

                            node = node.NextSibling;
                        }
                    }

                    if (null == packageElement)
                    {
                        packageElement = document.CreateElement("Package", namespaceURI);
                        packageElement.SetAttribute("name", packageName);
                        if (null != packageVersion)
                        {
                            packageElement.SetAttribute("version", packageVersion);
                        }
                        if (packageIsDefault.HasValue)
                        {
                            packageElement.SetAttribute("default", packageIsDefault.Value.ToString().ToLower());
                        }
                        dependentsEl.AppendChild(packageElement);
                    }
                }
                packageDefinition.AppendChild(dependentsEl);
            }

            if (this.BamAssemblies.Count > 0)
            {
                var requiredAssemblies = document.CreateElement("BamAssemblies", namespaceURI);
                foreach (var assembly in this.BamAssemblies)
                {
                    var assemblyElement = document.CreateElement("BamAssembly", namespaceURI);
                    assemblyElement.SetAttribute("name", assembly.Name);
                    if (assembly.MajorVersion.HasValue)
                    {
                        assemblyElement.SetAttribute("major", assembly.MajorVersion.ToString());
                        if (assembly.MinorVersion.HasValue)
                        {
                            assemblyElement.SetAttribute("minor", assembly.MinorVersion.ToString());
                            if (assembly.PatchVersion.HasValue)
                            {
                                // honour any existing BamAssembly with a specific patch version
                                assemblyElement.SetAttribute("patch", assembly.PatchVersion.ToString());
                            }
                        }
                    }
                    requiredAssemblies.AppendChild(assemblyElement);
                }
                packageDefinition.AppendChild(requiredAssemblies);
            }

            if (this.DotNetAssemblies.Count > 0)
            {
                var requiredDotNetAssemblies = document.CreateElement("DotNetAssemblies", namespaceURI);
                foreach (var desc in this.DotNetAssemblies)
                {
                    var assemblyElement = document.CreateElement("DotNetAssembly", namespaceURI);
                    assemblyElement.SetAttribute("name", desc.Name);
                    if (null != desc.RequiredTargetFramework)
                    {
                        assemblyElement.SetAttribute("requiredTargetFramework", desc.RequiredTargetFramework);
                    }
                    requiredDotNetAssemblies.AppendChild(assemblyElement);
                }
                packageDefinition.AppendChild(requiredDotNetAssemblies);
            }

            // supported platforms
            {
                var supportedPlatformsElement = document.CreateElement("SupportedPlatforms", namespaceURI);

                if (EPlatform.Windows == (this.SupportedPlatforms & EPlatform.Windows))
                {
                    var platformElement = document.CreateElement("Platform", namespaceURI);
                    platformElement.SetAttribute("name", "Windows");
                    supportedPlatformsElement.AppendChild(platformElement);
                }
                if (EPlatform.Linux == (this.SupportedPlatforms & EPlatform.Linux))
                {
                    var platformElement = document.CreateElement("Platform", namespaceURI);
                    platformElement.SetAttribute("name", "Linux");
                    supportedPlatformsElement.AppendChild(platformElement);
                }
                if (EPlatform.OSX == (this.SupportedPlatforms & EPlatform.OSX))
                {
                    var platformElement = document.CreateElement("Platform", namespaceURI);
                    platformElement.SetAttribute("name", "OSX");
                    supportedPlatformsElement.AppendChild(platformElement);
                }

                packageDefinition.AppendChild(supportedPlatformsElement);
            }

            // definitions
            this.Definitions.Remove(this.GetPackageDefinitionName());
            if (this.Definitions.Count > 0)
            {
                var definitionsElement = document.CreateElement("Definitions", namespaceURI);

                foreach (string define in this.Definitions)
                {
                    var defineElement = document.CreateElement("Definition", namespaceURI);
                    defineElement.SetAttribute("name", define);
                    definitionsElement.AppendChild(defineElement);
                }

                packageDefinition.AppendChild(definitionsElement);
            }

            var xmlWriterSettings = new System.Xml.XmlWriterSettings();
            xmlWriterSettings.Indent = true;
            xmlWriterSettings.CloseOutput = true;
            xmlWriterSettings.OmitXmlDeclaration = false;
            xmlWriterSettings.NewLineOnAttributes = false;
            xmlWriterSettings.NewLineChars = "\n";
            xmlWriterSettings.ConformanceLevel = System.Xml.ConformanceLevel.Document;
            xmlWriterSettings.Encoding = new System.Text.UTF8Encoding(false);

            using (var xmlWriter = System.Xml.XmlWriter.Create(this.XMLFilename, xmlWriterSettings))
            {
                document.WriteTo(xmlWriter);
                xmlWriter.WriteWhitespace(xmlWriterSettings.NewLineChars);
            }

            if (this.validate)
            {
                this.Validate();
            }
        }