main
cs 278 lines 10 KB
Raw
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
3 namespace WixToolset.Harvesters
4 {
5 using System;
6 using System.Collections.Generic;
7 using System.Globalization;
8 using System.IO;
9 using System.Runtime.InteropServices;
10 using System.Threading;
11 using System.Threading.Tasks;
12 using System.Xml;
13 using WixToolset.Data;
14 using WixToolset.Extensibility;
15 using WixToolset.Extensibility.Data;
16 using WixToolset.Extensibility.Services;
17 using WixToolset.Harvesters.Extensibility;
18 using Wix = WixToolset.Harvesters.Serialize;
19
20 internal class HeatCommand : BaseCommandLineCommand
21 {
22 private bool showLogo;
23
24 public HeatCommand(string harvestType, IList<IHeatExtension> extensions, IServiceProvider serviceProvider)
25 {
26 this.Extensions = extensions;
27 this.Messaging = serviceProvider.GetService<IMessaging>();
28 this.ServiceProvider = serviceProvider;
29
30 this.ExtensionType = harvestType;
31 this.ExtensionOptions.Add(harvestType);
32 }
33
34 public override bool ShowLogo => this.showLogo;
35
36 private string ExtensionArgument { get; set; }
37
38 private List<string> ExtensionOptions { get; } = new List<string>();
39
40 private string ExtensionType { get; }
41
42 private IList<IHeatExtension> Extensions { get; }
43
44 private int Indent { get; set; } = 4;
45
46 private IMessaging Messaging { get; }
47
48 private string OutputFile { get; set; }
49
50 private IServiceProvider ServiceProvider { get; }
51
52 public override CommandLineHelp GetCommandLineHelp()
53 {
54 return null;
55 }
56
57 public override Task<int> ExecuteAsync(CancellationToken cancellationToken)
58 {
59 var exitCode = this.Harvest();
60 return Task.FromResult(exitCode);
61 }
62
63 public override bool TryParseArgument(ICommandLineParser parser, string arg)
64 {
65 if (this.ExtensionArgument == null)
66 {
67 this.ExtensionArgument = arg;
68 }
69 else if ('-' == arg[0] || '/' == arg[0])
70 {
71 var parameter = arg.Substring(1);
72 if ("nologo" == parameter)
73 {
74 this.showLogo = false;
75 }
76 else if ("o" == parameter || "out" == parameter)
77 {
78 this.OutputFile = parser.GetNextArgumentAsFilePathOrError(arg, "output source file");
79
80 if (String.IsNullOrEmpty(this.OutputFile))
81 {
82 return false;
83 }
84 }
85 else if ("swall" == parameter)
86 {
87 this.Messaging.Write(WarningMessages.DeprecatedCommandLineSwitch("swall", "sw"));
88 this.Messaging.SuppressAllWarnings = true;
89 }
90 else if (parameter.StartsWith("sw"))
91 {
92 var paramArg = parameter.Substring(2);
93 try
94 {
95 if (0 == paramArg.Length)
96 {
97 this.Messaging.SuppressAllWarnings = true;
98 }
99 else
100 {
101 var suppressWarning = Convert.ToInt32(paramArg, CultureInfo.InvariantCulture.NumberFormat);
102 if (0 >= suppressWarning)
103 {
104 this.Messaging.Write(ErrorMessages.IllegalSuppressWarningId(paramArg));
105 }
106
107 this.Messaging.SuppressWarningMessage(suppressWarning);
108 }
109 }
110 catch (FormatException)
111 {
112 this.Messaging.Write(ErrorMessages.IllegalSuppressWarningId(paramArg));
113 }
114 catch (OverflowException)
115 {
116 this.Messaging.Write(ErrorMessages.IllegalSuppressWarningId(paramArg));
117 }
118 }
119 else if ("wxall" == parameter)
120 {
121 this.Messaging.Write(WarningMessages.DeprecatedCommandLineSwitch("wxall", "wx"));
122 this.Messaging.WarningsAsError = true;
123 }
124 else if (parameter.StartsWith("wx"))
125 {
126 var paramArg = parameter.Substring(2);
127 try
128 {
129 if (0 == paramArg.Length)
130 {
131 this.Messaging.WarningsAsError = true;
132 }
133 else
134 {
135 var elevateWarning = Convert.ToInt32(paramArg, CultureInfo.InvariantCulture.NumberFormat);
136 if (0 >= elevateWarning)
137 {
138 this.Messaging.Write(ErrorMessages.IllegalWarningIdAsError(paramArg));
139 }
140
141 this.Messaging.ElevateWarningMessage(elevateWarning);
142 }
143 }
144 catch (FormatException)
145 {
146 this.Messaging.Write(ErrorMessages.IllegalWarningIdAsError(paramArg));
147 }
148 catch (OverflowException)
149 {
150 this.Messaging.Write(ErrorMessages.IllegalWarningIdAsError(paramArg));
151 }
152 }
153 else if ("v" == parameter)
154 {
155 this.Messaging.ShowVerboseMessages = true;
156 }
157 else if ("indent" == parameter)
158 {
159 try
160 {
161 this.Indent = Int32.Parse(parser.GetNextArgumentOrError(arg), CultureInfo.InvariantCulture);
162 }
163 catch
164 {
165 throw new ArgumentException("Invalid numeric argument.", parameter);
166 }
167 }
168 }
169
170 this.ExtensionOptions.Add(arg);
171 return true;
172 }
173
174 private int Harvest()
175 {
176 try
177 {
178 if (String.IsNullOrEmpty(this.ExtensionArgument))
179 {
180 this.Messaging.Write(ErrorMessages.HarvestSourceNotSpecified());
181 }
182 else if (String.IsNullOrEmpty(this.OutputFile))
183 {
184 this.Messaging.Write(ErrorMessages.OutputTargetNotSpecified());
185 }
186
187 // exit if there was an error parsing the core command line
188 if (this.Messaging.EncounteredError)
189 {
190 return this.Messaging.LastErrorNumber;
191 }
192
193 if (this.ShowLogo)
194 {
195 HelpCommand.DisplayToolHeader();
196 }
197
198 var heatCore = new HeatCore(this.ServiceProvider, this.ExtensionArgument);
199
200 // parse the extension's command line arguments
201 var extensionOptionsArray = this.ExtensionOptions.ToArray();
202 foreach (var heatExtension in this.Extensions)
203 {
204 heatExtension.Core = heatCore;
205 heatExtension.ParseOptions(this.ExtensionType, extensionOptionsArray);
206 }
207
208 // exit if there was an error parsing the command line (otherwise the logo appears after error messages)
209 if (this.Messaging.EncounteredError)
210 {
211 return this.Messaging.LastErrorNumber;
212 }
213
214 // harvest the output
215 var wix = heatCore.Harvester.Harvest(this.ExtensionArgument);
216 if (null == wix)
217 {
218 return this.Messaging.LastErrorNumber;
219 }
220
221 // mutate the output
222 if (!heatCore.Mutator.Mutate(wix))
223 {
224 return this.Messaging.LastErrorNumber;
225 }
226
227 var xmlSettings = new XmlWriterSettings();
228 xmlSettings.Indent = true;
229 xmlSettings.IndentChars = new string(' ', this.Indent);
230 xmlSettings.OmitXmlDeclaration = true;
231
232 string wixString;
233 using (var stringWriter = new StringWriter())
234 {
235 using (var xmlWriter = XmlWriter.Create(stringWriter, xmlSettings))
236 {
237 wix.OutputXml(xmlWriter);
238 }
239
240 wixString = stringWriter.ToString();
241 }
242
243 var mutatedWixString = heatCore.Mutator.Mutate(wixString);
244 if (String.IsNullOrEmpty(mutatedWixString))
245 {
246 return this.Messaging.LastErrorNumber;
247 }
248
249 Directory.CreateDirectory(Path.GetDirectoryName(this.OutputFile));
250
251 using (var streamWriter = new StreamWriter(this.OutputFile, false, System.Text.Encoding.UTF8))
252 {
253 using (var xmlWriter = XmlWriter.Create(streamWriter, xmlSettings))
254 {
255 xmlWriter.WriteStartDocument();
256 xmlWriter.Flush();
257 }
258
259 streamWriter.Write(mutatedWixString);
260 }
261 }
262 catch (WixException we)
263 {
264 this.Messaging.Write(we.Error);
265 }
266 catch (Exception e)
267 {
268 this.Messaging.Write(ErrorMessages.UnexpectedException(e));
269 if (e is NullReferenceException || e is SEHException)
270 {
271 throw;
272 }
273 }
274
275 return this.Messaging.LastErrorNumber;
276 }
277 }
278 }