using System.Xml; namespace ReleaseBuilder; /// /// Implemenation of the "heat" command line tool from Wix /// public static class WixHeatBuilder { /// /// Creates a Wix filelist from a directory /// /// The source folder to create the filelist from /// The name of the directory reference /// The name of the component group /// A function to generate file IDs. /// The wix file xml contents public static string CreateWixFilelist(string sourceFolder, string folderPrefix = "$(var.HarvestPath)", string directoryRefName = "INSTALLLOCATION", string componentGroupId = "DUPLICATIBIN", Func? fileIdGenerator = null) { var itemIds = new Dictionary(); fileIdGenerator ??= (x) => Path.GetRelativePath(sourceFolder, x).Replace("\\", "_").Replace("/", "_").Replace(":", "_").Replace(" ", "_"); Func pathTransformer = (x) => $"{folderPrefix}{Path.GetRelativePath(sourceFolder, x)}"; var doc = new XmlDocument(); doc.LoadXml(""); var root = doc.DocumentElement!; var fragment = doc.CreateElement("Fragment"); root.AppendChild(fragment); var directoryRef = doc.CreateElement("DirectoryRef"); directoryRef.SetAttribute("Id", directoryRefName); fragment.AppendChild(directoryRef); foreach (var f in Directory.EnumerateFileSystemEntries(sourceFolder)) if (File.Exists(f)) AddFile(doc, directoryRef, f, itemIds, fileIdGenerator, pathTransformer); else if (Directory.Exists(f)) AddDirectory(doc, directoryRef, f, itemIds, fileIdGenerator, pathTransformer); var fragment2 = doc.CreateElement("Fragment"); root.AppendChild(fragment2); var componentGroup = doc.CreateElement("ComponentGroup"); componentGroup.SetAttribute("Id", componentGroupId); fragment2.AppendChild(componentGroup); foreach (var file in itemIds.Keys) { var componentRef = doc.CreateElement("ComponentRef"); componentRef.SetAttribute("Id", itemIds[file]); componentGroup.AppendChild(componentRef); } return doc.OuterXml; } /// /// Adds a file to the XML document. /// /// The XML document. /// The XML element representing the directory reference. /// The file to be added. /// The dictionary containing the item IDs. /// The function to generate file IDs. private static void AddFile(XmlDocument doc, XmlElement directoryRef, string file, Dictionary itemIds, Func fileIdGenerator, Func pathTransformer) { var id = fileIdGenerator.Invoke(file); itemIds.Add(file, id); var component = doc.CreateElement("Component"); component.SetAttribute("Id", id); component.SetAttribute("Guid", "*"); directoryRef.AppendChild(component); var fileElement = doc.CreateElement("File"); fileElement.SetAttribute("Id", id); fileElement.SetAttribute("KeyPath", "yes"); fileElement.SetAttribute("Source", pathTransformer(file)); component.AppendChild(fileElement); } /// /// Recursively adds a directory and its contents to an XML document. /// /// The XML document to add the directory to. /// The parent directory reference element. /// The directory path to add. /// A dictionary to store the mapping between directory paths and their generated IDs. /// A function to generate file IDs. private static void AddDirectory(XmlDocument doc, XmlElement directoryRef, string dir, Dictionary itemIds, Func fileIdGenerator, Func pathTransformer) { var id = fileIdGenerator.Invoke(dir); var dirName = Path.GetFileName(dir); var directory = doc.CreateElement("Directory"); directory.SetAttribute("Id", id); directory.SetAttribute("Name", dirName); directoryRef.AppendChild(directory); foreach (var file in Directory.GetFiles(dir)) AddFile(doc, directory, file, itemIds, fileIdGenerator, pathTransformer); foreach (var subDir in Directory.GetDirectories(dir)) AddDirectory(doc, directory, subDir, itemIds, fileIdGenerator, pathTransformer); } }