aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Data.WindowsInstaller/Output.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/WixToolset.Data.WindowsInstaller/Output.cs')
-rw-r--r--src/WixToolset.Data.WindowsInstaller/Output.cs342
1 files changed, 342 insertions, 0 deletions
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 @@
1// 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.
2
3namespace WixToolset.Data
4{
5 using System;
6 using System.Collections.Generic;
7 using System.Globalization;
8 using System.IO;
9 using System.Linq;
10 using System.Xml;
11
12 /// <summary>
13 /// Output is generated by the linker.
14 /// </summary>
15 public sealed class Output
16 {
17 public const string XmlNamespaceUri = "http://wixtoolset.org/schemas/v4/wixout";
18 private static readonly Version CurrentVersion = new Version("4.0.0.0");
19
20 /// <summary>
21 /// Creates a new empty output object.
22 /// </summary>
23 /// <param name="sourceLineNumbers">The source line information for the output.</param>
24 public Output(SourceLineNumber sourceLineNumbers)
25 {
26 this.SourceLineNumbers = sourceLineNumbers;
27 this.SubStorages = new List<SubStorage>();
28 this.Tables = new TableIndexedCollection();
29 }
30
31 /// <summary>
32 /// Gets the type of the output.
33 /// </summary>
34 /// <value>Type of the output.</value>
35 public OutputType Type { get; set; }
36
37 /// <summary>
38 /// Gets or sets the codepage for this output.
39 /// </summary>
40 /// <value>Codepage of the output.</value>
41 public int Codepage { get; set; }
42
43 /// <summary>
44 /// Gets the source line information for this output.
45 /// </summary>
46 /// <value>The source line information for this output.</value>
47 public SourceLineNumber SourceLineNumbers { get; private set; }
48
49 /// <summary>
50 /// Gets the substorages in this output.
51 /// </summary>
52 /// <value>The substorages in this output.</value>
53 public ICollection<SubStorage> SubStorages { get; private set; }
54
55 /// <summary>
56 /// Gets the tables contained in this output.
57 /// </summary>
58 /// <value>Collection of tables.</value>
59 public TableIndexedCollection Tables { get; private set; }
60
61 /// <summary>
62 /// Gets the output type corresponding to a given output filename extension.
63 /// </summary>
64 /// <param name="extension">Case-insensitive output filename extension.</param>
65 /// <returns>Output type for the extension.</returns>
66 public static OutputType GetOutputType(string extension)
67 {
68 if (extension.Equals(".exe", StringComparison.OrdinalIgnoreCase))
69 {
70 return OutputType.Bundle;
71 }
72 if (extension.Equals(".msi", StringComparison.OrdinalIgnoreCase))
73 {
74 return OutputType.Product;
75 }
76 else if (extension.Equals(".msm", StringComparison.OrdinalIgnoreCase))
77 {
78 return OutputType.Module;
79 }
80 else if (extension.Equals(".msp", StringComparison.OrdinalIgnoreCase))
81 {
82 return OutputType.Patch;
83 }
84 else if (extension.Equals(".mst", StringComparison.OrdinalIgnoreCase))
85 {
86 return OutputType.Transform;
87 }
88 else if (extension.Equals(".pcp", StringComparison.OrdinalIgnoreCase))
89 {
90 return OutputType.PatchCreation;
91 }
92 else
93 {
94 return OutputType.Unknown;
95 }
96 }
97
98 /// <summary>
99 /// Gets the filename extension corresponding to a given output type.
100 /// </summary>
101 /// <param name="type">One of the WiX output types.</param>
102 /// <returns>Filename extension for the output type, for example ".msi".</returns>
103 public static string GetExtension(OutputType type)
104 {
105 switch (type)
106 {
107 case OutputType.Bundle:
108 return ".exe";
109 case OutputType.Product:
110 return ".msi";
111 case OutputType.Module:
112 return ".msm";
113 case OutputType.Patch:
114 return ".msp";
115 case OutputType.Transform:
116 return ".mst";
117 case OutputType.PatchCreation:
118 return ".pcp";
119 default:
120 return ".wix";
121 }
122 }
123
124 /// <summary>
125 /// Loads an output from a path on disk.
126 /// </summary>
127 /// <param name="path">Path to output file saved on disk.</param>
128 /// <param name="suppressVersionCheck">Suppresses wix.dll version mismatch check.</param>
129 /// <returns>Output object.</returns>
130 public static Output Load(string path, bool suppressVersionCheck)
131 {
132 using (FileStream stream = File.OpenRead(path))
133 using (FileStructure fs = FileStructure.Read(stream))
134 {
135 if (FileFormat.Wixout != fs.FileFormat)
136 {
137 throw new WixUnexpectedFileFormatException(path, FileFormat.Wixout, fs.FileFormat);
138 }
139
140 Uri uri = new Uri(Path.GetFullPath(path));
141 using (XmlReader reader = XmlReader.Create(fs.GetDataStream(), null, uri.AbsoluteUri))
142 {
143 try
144 {
145 reader.MoveToContent();
146 return Output.Read(reader, suppressVersionCheck);
147 }
148 catch (XmlException xe)
149 {
150 throw new WixCorruptFileException(path, fs.FileFormat, xe);
151 }
152 }
153 }
154 }
155
156 /// <summary>
157 /// Saves an output to a path on disk.
158 /// </summary>
159 /// <param name="path">Path to save output file to on disk.</param>
160 public void Save(string path)
161 {
162 Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(path)));
163
164 using (FileStream stream = File.Create(path))
165 using (FileStructure fs = FileStructure.Create(stream, FileFormat.Wixout, null))
166 using (XmlWriter writer = XmlWriter.Create(fs.GetDataStream()))
167 {
168 writer.WriteStartDocument();
169 this.Write(writer);
170 writer.WriteEndDocument();
171 }
172 }
173
174 /// <summary>
175 /// Processes an XmlReader and builds up the output object.
176 /// </summary>
177 /// <param name="reader">Reader to get data from.</param>
178 /// <param name="suppressVersionCheck">Suppresses wix.dll version mismatch check.</param>
179 /// <returns>The Output represented by the Xml.</returns>
180 internal static Output Read(XmlReader reader, bool suppressVersionCheck)
181 {
182 if (!reader.LocalName.Equals("wixOutput"))
183 {
184 throw new XmlException();
185 }
186
187 bool empty = reader.IsEmptyElement;
188 Output output = new Output(SourceLineNumber.CreateFromUri(reader.BaseURI));
189 SectionType sectionType = SectionType.Unknown;
190 Version version = null;
191
192 while (reader.MoveToNextAttribute())
193 {
194 switch (reader.LocalName)
195 {
196 case "codepage":
197 output.Codepage = Convert.ToInt32(reader.Value, CultureInfo.InvariantCulture.NumberFormat);
198 break;
199 case "type":
200 switch (reader.Value)
201 {
202 case "Bundle":
203 output.Type = OutputType.Bundle;
204 sectionType = SectionType.Bundle;
205 break;
206 case "Module":
207 output.Type = OutputType.Module;
208 sectionType = SectionType.Module;
209 break;
210 case "Patch":
211 output.Type = OutputType.Patch;
212 break;
213 case "PatchCreation":
214 output.Type = OutputType.PatchCreation;
215 sectionType = SectionType.PatchCreation;
216 break;
217 case "Product":
218 output.Type = OutputType.Product;
219 sectionType = SectionType.Product;
220 break;
221 case "Transform":
222 output.Type = OutputType.Transform;
223 break;
224 default:
225 throw new XmlException();
226 }
227 break;
228 case "version":
229 version = new Version(reader.Value);
230 break;
231 }
232 }
233
234 if (!suppressVersionCheck && null != version && !Output.CurrentVersion.Equals(version))
235 {
236 throw new WixException(WixDataErrors.VersionMismatch(SourceLineNumber.CreateFromUri(reader.BaseURI), "wixOutput", version.ToString(), Output.CurrentVersion.ToString()));
237 }
238
239 // loop through the rest of the xml building up the Output object
240 TableDefinitionCollection tableDefinitions = null;
241 List<Table> tables = new List<Table>();
242 if (!empty)
243 {
244 bool done = false;
245
246 // loop through all the fields in a row
247 while (!done && reader.Read())
248 {
249 switch (reader.NodeType)
250 {
251 case XmlNodeType.Element:
252 switch (reader.LocalName)
253 {
254 case "subStorage":
255 output.SubStorages.Add(SubStorage.Read(reader));
256 break;
257 case "table":
258 if (null == tableDefinitions)
259 {
260 throw new XmlException();
261 }
262 tables.Add(Table.Read(reader, tableDefinitions));
263 break;
264 case "tableDefinitions":
265 tableDefinitions = TableDefinitionCollection.Read(reader);
266 break;
267 default:
268 throw new XmlException();
269 }
270 break;
271 case XmlNodeType.EndElement:
272 done = true;
273 break;
274 }
275 }
276
277 if (!done)
278 {
279 throw new XmlException();
280 }
281 }
282
283 output.Tables = new TableIndexedCollection(tables);
284 return output;
285 }
286
287 /// <summary>
288 /// Ensure this output contains a particular table.
289 /// </summary>
290 /// <param name="tableDefinition">Definition of the table that should exist.</param>
291 /// <param name="section">Optional section to use for the table. If one is not provided, the entry section will be used.</param>
292 /// <returns>The table in this output.</returns>
293 public Table EnsureTable(TableDefinition tableDefinition)
294 {
295 if (!this.Tables.TryGetTable(tableDefinition.Name, out Table table))
296 {
297 table = new Table(tableDefinition);
298 this.Tables.Add(table);
299 }
300
301 return table;
302 }
303
304 /// <summary>
305 /// Persists an output in an XML format.
306 /// </summary>
307 /// <param name="writer">XmlWriter where the Output should persist itself as XML.</param>
308 internal void Write(XmlWriter writer)
309 {
310 writer.WriteStartElement("wixOutput", XmlNamespaceUri);
311
312 writer.WriteAttributeString("type", this.Type.ToString());
313
314 if (0 != this.Codepage)
315 {
316 writer.WriteAttributeString("codepage", this.Codepage.ToString(CultureInfo.InvariantCulture));
317 }
318
319 writer.WriteAttributeString("version", Output.CurrentVersion.ToString());
320
321 // Collect all the table definitions and write them.
322 TableDefinitionCollection tableDefinitions = new TableDefinitionCollection();
323 foreach (Table table in this.Tables)
324 {
325 tableDefinitions.Add(table.Definition);
326 }
327 tableDefinitions.Write(writer);
328
329 foreach (Table table in this.Tables.OrderBy(t => t.Name))
330 {
331 table.Write(writer);
332 }
333
334 foreach (SubStorage subStorage in this.SubStorages)
335 {
336 subStorage.Write(writer);
337 }
338
339 writer.WriteEndElement();
340 }
341 }
342}