From 2bb37beda887d120a0ddabf874ad25357101faa1 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Wed, 1 Nov 2017 10:59:45 -0700 Subject: Update to WiX Intermediate Representation --- src/WixToolset.Data.WindowsInstaller/Output.cs | 342 +++++++++++++++++++++++++ 1 file changed, 342 insertions(+) create mode 100644 src/WixToolset.Data.WindowsInstaller/Output.cs (limited to 'src/WixToolset.Data.WindowsInstaller/Output.cs') diff --git a/src/WixToolset.Data.WindowsInstaller/Output.cs b/src/WixToolset.Data.WindowsInstaller/Output.cs new file mode 100644 index 00000000..71faeac7 --- /dev/null +++ b/src/WixToolset.Data.WindowsInstaller/Output.cs @@ -0,0 +1,342 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace WixToolset.Data +{ + using System; + using System.Collections.Generic; + using System.Globalization; + using System.IO; + using System.Linq; + using System.Xml; + + /// + /// Output is generated by the linker. + /// + public sealed class Output + { + public const string XmlNamespaceUri = "http://wixtoolset.org/schemas/v4/wixout"; + private static readonly Version CurrentVersion = new Version("4.0.0.0"); + + /// + /// Creates a new empty output object. + /// + /// The source line information for the output. + public Output(SourceLineNumber sourceLineNumbers) + { + this.SourceLineNumbers = sourceLineNumbers; + this.SubStorages = new List(); + this.Tables = new TableIndexedCollection(); + } + + /// + /// Gets the type of the output. + /// + /// Type of the output. + public OutputType Type { get; set; } + + /// + /// Gets or sets the codepage for this output. + /// + /// Codepage of the output. + public int Codepage { get; set; } + + /// + /// Gets the source line information for this output. + /// + /// The source line information for this output. + public SourceLineNumber SourceLineNumbers { get; private set; } + + /// + /// Gets the substorages in this output. + /// + /// The substorages in this output. + public ICollection SubStorages { get; private set; } + + /// + /// Gets the tables contained in this output. + /// + /// Collection of tables. + public TableIndexedCollection Tables { get; private set; } + + /// + /// Gets the output type corresponding to a given output filename extension. + /// + /// Case-insensitive output filename extension. + /// Output type for the extension. + public static OutputType GetOutputType(string extension) + { + if (extension.Equals(".exe", StringComparison.OrdinalIgnoreCase)) + { + return OutputType.Bundle; + } + if (extension.Equals(".msi", StringComparison.OrdinalIgnoreCase)) + { + return OutputType.Product; + } + else if (extension.Equals(".msm", StringComparison.OrdinalIgnoreCase)) + { + return OutputType.Module; + } + else if (extension.Equals(".msp", StringComparison.OrdinalIgnoreCase)) + { + return OutputType.Patch; + } + else if (extension.Equals(".mst", StringComparison.OrdinalIgnoreCase)) + { + return OutputType.Transform; + } + else if (extension.Equals(".pcp", StringComparison.OrdinalIgnoreCase)) + { + return OutputType.PatchCreation; + } + else + { + return OutputType.Unknown; + } + } + + /// + /// Gets the filename extension corresponding to a given output type. + /// + /// One of the WiX output types. + /// Filename extension for the output type, for example ".msi". + public static string GetExtension(OutputType type) + { + switch (type) + { + case OutputType.Bundle: + return ".exe"; + case OutputType.Product: + return ".msi"; + case OutputType.Module: + return ".msm"; + case OutputType.Patch: + return ".msp"; + case OutputType.Transform: + return ".mst"; + case OutputType.PatchCreation: + return ".pcp"; + default: + return ".wix"; + } + } + + /// + /// Loads an output from a path on disk. + /// + /// Path to output file saved on disk. + /// Suppresses wix.dll version mismatch check. + /// Output object. + public static Output Load(string path, bool suppressVersionCheck) + { + using (FileStream stream = File.OpenRead(path)) + using (FileStructure fs = FileStructure.Read(stream)) + { + if (FileFormat.Wixout != fs.FileFormat) + { + throw new WixUnexpectedFileFormatException(path, FileFormat.Wixout, fs.FileFormat); + } + + Uri uri = new Uri(Path.GetFullPath(path)); + using (XmlReader reader = XmlReader.Create(fs.GetDataStream(), null, uri.AbsoluteUri)) + { + try + { + reader.MoveToContent(); + return Output.Read(reader, suppressVersionCheck); + } + catch (XmlException xe) + { + throw new WixCorruptFileException(path, fs.FileFormat, xe); + } + } + } + } + + /// + /// Saves an output to a path on disk. + /// + /// Path to save output file to on disk. + public void Save(string path) + { + Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(path))); + + using (FileStream stream = File.Create(path)) + using (FileStructure fs = FileStructure.Create(stream, FileFormat.Wixout, null)) + using (XmlWriter writer = XmlWriter.Create(fs.GetDataStream())) + { + writer.WriteStartDocument(); + this.Write(writer); + writer.WriteEndDocument(); + } + } + + /// + /// Processes an XmlReader and builds up the output object. + /// + /// Reader to get data from. + /// Suppresses wix.dll version mismatch check. + /// The Output represented by the Xml. + internal static Output Read(XmlReader reader, bool suppressVersionCheck) + { + if (!reader.LocalName.Equals("wixOutput")) + { + throw new XmlException(); + } + + bool empty = reader.IsEmptyElement; + Output output = new Output(SourceLineNumber.CreateFromUri(reader.BaseURI)); + SectionType sectionType = SectionType.Unknown; + Version version = null; + + while (reader.MoveToNextAttribute()) + { + switch (reader.LocalName) + { + case "codepage": + output.Codepage = Convert.ToInt32(reader.Value, CultureInfo.InvariantCulture.NumberFormat); + break; + case "type": + switch (reader.Value) + { + case "Bundle": + output.Type = OutputType.Bundle; + sectionType = SectionType.Bundle; + break; + case "Module": + output.Type = OutputType.Module; + sectionType = SectionType.Module; + break; + case "Patch": + output.Type = OutputType.Patch; + break; + case "PatchCreation": + output.Type = OutputType.PatchCreation; + sectionType = SectionType.PatchCreation; + break; + case "Product": + output.Type = OutputType.Product; + sectionType = SectionType.Product; + break; + case "Transform": + output.Type = OutputType.Transform; + break; + default: + throw new XmlException(); + } + break; + case "version": + version = new Version(reader.Value); + break; + } + } + + if (!suppressVersionCheck && null != version && !Output.CurrentVersion.Equals(version)) + { + throw new WixException(WixDataErrors.VersionMismatch(SourceLineNumber.CreateFromUri(reader.BaseURI), "wixOutput", version.ToString(), Output.CurrentVersion.ToString())); + } + + // loop through the rest of the xml building up the Output object + TableDefinitionCollection tableDefinitions = null; + List tables = new List
(); + if (!empty) + { + bool done = false; + + // loop through all the fields in a row + while (!done && reader.Read()) + { + switch (reader.NodeType) + { + case XmlNodeType.Element: + switch (reader.LocalName) + { + case "subStorage": + output.SubStorages.Add(SubStorage.Read(reader)); + break; + case "table": + if (null == tableDefinitions) + { + throw new XmlException(); + } + tables.Add(Table.Read(reader, tableDefinitions)); + break; + case "tableDefinitions": + tableDefinitions = TableDefinitionCollection.Read(reader); + break; + default: + throw new XmlException(); + } + break; + case XmlNodeType.EndElement: + done = true; + break; + } + } + + if (!done) + { + throw new XmlException(); + } + } + + output.Tables = new TableIndexedCollection(tables); + return output; + } + + /// + /// Ensure this output contains a particular table. + /// + /// Definition of the table that should exist. + /// Optional section to use for the table. If one is not provided, the entry section will be used. + /// The table in this output. + public Table EnsureTable(TableDefinition tableDefinition) + { + if (!this.Tables.TryGetTable(tableDefinition.Name, out Table table)) + { + table = new Table(tableDefinition); + this.Tables.Add(table); + } + + return table; + } + + /// + /// Persists an output in an XML format. + /// + /// XmlWriter where the Output should persist itself as XML. + internal void Write(XmlWriter writer) + { + writer.WriteStartElement("wixOutput", XmlNamespaceUri); + + writer.WriteAttributeString("type", this.Type.ToString()); + + if (0 != this.Codepage) + { + writer.WriteAttributeString("codepage", this.Codepage.ToString(CultureInfo.InvariantCulture)); + } + + writer.WriteAttributeString("version", Output.CurrentVersion.ToString()); + + // Collect all the table definitions and write them. + TableDefinitionCollection tableDefinitions = new TableDefinitionCollection(); + foreach (Table table in this.Tables) + { + tableDefinitions.Add(table.Definition); + } + tableDefinitions.Write(writer); + + foreach (Table table in this.Tables.OrderBy(t => t.Name)) + { + table.Write(writer); + } + + foreach (SubStorage subStorage in this.SubStorages) + { + subStorage.Write(writer); + } + + writer.WriteEndElement(); + } + } +} -- cgit v1.2.3-55-g6feb