@joebigelow / wix / commits / ecf3a0cc

Reintroduce binder extensions and light.exe for binding .wixouts

Rob Mensching committed Dec 22, 2017 at 15:53 UTC ecf3a0cca5a424a91ab98557d963d2535963d582
31 files changed +1956 -560
src/WixToolset.BuildTasks/WixToolTask.cs
+1 -1
@@ -4,7 +4,6 @@ namespace WixToolset.BuildTasks
4 {
5 using System;
6 using System.Collections.Generic;
7 - using System.Diagnostics;
7 using System.Diagnostics.CodeAnalysis;
8 using System.Globalization;
9 using System.IO;
@@ -14,6 +13,7 @@ namespace WixToolset.BuildTasks
13
14 using Microsoft.Build.Framework;
15 using Microsoft.Build.Utilities;
16 + using WixToolset.Core.CommandLine;
17
18 /// <summary>
19 /// Base class for WiX tool tasks; executes tools in-process
src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
+2 -2
@@ -22,7 +22,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
22 // As outlined in RFC 4122, this is our namespace for generating name-based (version 3) UUIDs.
23 internal static readonly Guid WixComponentGuidNamespace = new Guid("{3064E5C6-FB63-4FE9-AC49-E446A792EFA5}");
24
25 - public BindDatabaseCommand(WixToolset.Extensibility.IBindContext context, IEnumerable<IWindowsInstallerBackendExtension> backendExtension, Validator validator)
25 + public BindDatabaseCommand(IBindContext context, IEnumerable<IWindowsInstallerBackendExtension> backendExtension, Validator validator)
26 {
27 this.TableDefinitions = WindowsInstallerStandardInternal.GetTableDefinitions();
28
@@ -49,7 +49,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
49
50 private string CabCachePath { get; }
51
52 - private CompressionLevel DefaultCompressionLevel { get; }
52 + private CompressionLevel? DefaultCompressionLevel { get; }
53
54 public IEnumerable<IDelayedField> DelayedFields { get; }
55
src/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs
+2 -2
@@ -54,7 +54,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
54 /// Sets the default compression level to use for cabinets
55 /// that don't have their compression level explicitly set.
56 /// </summary>
57 - public CompressionLevel DefaultCompressionLevel { private get; set; }
57 + public CompressionLevel? DefaultCompressionLevel { private get; set; }
58
59 public IEnumerable<IWindowsInstallerBackendExtension> BackendExtensions { private get; set; }
60
@@ -99,7 +99,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
99 {
100 var mediaTuple = entry.Key;
101 IEnumerable<FileFacade> files = entry.Value;
102 - CompressionLevel compressionLevel = this.DefaultCompressionLevel;
102 + CompressionLevel compressionLevel = this.DefaultCompressionLevel ?? CompressionLevel.Medium;
103
104 string mediaLayoutFolder = null;
105
src/WixToolset.Core/BindContext.cs
+2 -12
@@ -27,13 +27,13 @@ namespace WixToolset.Core
27
28 public int Codepage { get; set; }
29
30 - public CompressionLevel DefaultCompressionLevel { get; set; }
30 + public CompressionLevel? DefaultCompressionLevel { get; set; }
31
32 public IEnumerable<IDelayedField> DelayedFields { get; set; }
33
34 public IEnumerable<IExpectedExtractFile> ExpectedEmbeddedFiles { get; set; }
35
36 - public IExtensionManager ExtensionManager { get; set; }
36 + public IEnumerable<IBinderExtension> Extensions { get; set; }
37
38 public IEnumerable<IFileSystemExtension> FileSystemExtensions { get; set; }
39
@@ -50,15 +50,5 @@ namespace WixToolset.Core
50 public IEnumerable<string> SuppressIces { get; set; }
51
52 public bool SuppressValidation { get; set; }
53 -
54 - public IBindVariableResolver WixVariableResolver { get; set; }
55 -
56 - public string ContentsFile { get; set; }
57 -
58 - public string OutputsFile { get; set; }
59 -
60 - public string BuiltOutputsFile { get; set; }
61 -
62 - public string WixprojectFile { get; set; }
53 }
54 }
src/WixToolset.Core/Binder.cs
+65 -396
@@ -5,10 +5,8 @@ namespace WixToolset.Core
5 using System;
6 using System.Collections.Generic;
7 using System.Diagnostics;
8 - using System.IO;
8 using System.Linq;
9 using System.Reflection;
11 - using WixToolset.Core.Bind;
10 using WixToolset.Data;
11 using WixToolset.Data.Bind;
12 using WixToolset.Data.Tuples;
@@ -20,334 +18,110 @@ namespace WixToolset.Core
18 /// </summary>
19 public sealed class Binder
20 {
23 - //private BinderCore core;
24 - //private List<IBinderExtension> extensions;
25 - //private List<IBinderFileManager> fileManagers;
26 -
27 - public Binder()
21 + public Binder(IServiceProvider serviceProvider)
22 {
29 - //this.DefaultCompressionLevel = CompressionLevel.High;
30 -
31 - //this.BindPaths = new List<BindPath>();
32 - //this.TargetBindPaths = new List<BindPath>();
33 - //this.UpdatedBindPaths = new List<BindPath>();
34 -
35 - //this.extensions = new List<IBinderExtension>();
36 - //this.fileManagers = new List<IBinderFileManager>();
37 - //this.inspectorExtensions = new List<InspectorExtension>();
38 -
39 - //this.Ices = new List<string>();
40 - //this.SuppressIces = new List<string>();
23 + this.ServiceProvider = serviceProvider;
24 }
25
43 - private IBindContext Context { get; set; }
44 -
45 - //private TableDefinitionCollection TableDefinitions { get; }
26 + public int CabbingThreadCount { get; set; }
27
47 - //public IEnumerable<IBackendFactory> BackendFactories { get; set; }
28 + public string CabCachePath { get; set; }
29
49 - //public string ContentsFile { private get; set; }
30 + public int Codepage { get; set; }
31
51 - //public string OutputsFile { private get; set; }
32 + public CompressionLevel? DefaultCompressionLevel { get; set; }
33
53 - //public string BuiltOutputsFile { private get; set; }
34 + public IEnumerable<IDelayedField> DelayedFields { get; set; }
35
55 - //public string WixprojectFile { private get; set; }
36 + public IEnumerable<IExpectedExtractFile> ExpectedEmbeddedFiles { get; set; }
37
57 - /// <summary>
58 - /// Gets the list of bindpaths.
59 - /// </summary>
60 - //public List<BindPath> BindPaths { get; private set; }
38 + public IEnumerable<string> Ices { get; set; }
39
62 - /// <summary>
63 - /// Gets the list of target bindpaths.
64 - /// </summary>
65 - //public List<BindPath> TargetBindPaths { get; private set; }
40 + public string IntermediateFolder { get; set; }
41
67 - /// <summary>
68 - /// Gets the list of updated bindpaths.
69 - /// </summary>
70 - //public List<BindPath> UpdatedBindPaths { get; private set; }
42 + public Intermediate IntermediateRepresentation { get; set; }
43
72 - /// <summary>
73 - /// Gets or sets the option to enable building binary delta patches.
74 - /// </summary>
75 - /// <value>The option to enable building binary delta patches.</value>
76 - public bool DeltaBinaryPatch { get; set; }
44 + public string OutputPath { get; set; }
45
78 - /// <summary>
79 - /// Gets or sets the cabinet cache location.
80 - /// </summary>
81 - public string CabCachePath { get; set; }
46 + public string OutputPdbPath { get; set; }
47
83 - /// <summary>
84 - /// Gets or sets the number of threads to use for cabinet creation.
85 - /// </summary>
86 - /// <value>The number of threads to use for cabinet creation.</value>
87 - public int CabbingThreadCount { get; set; }
48 + public IEnumerable<string> SuppressIces { get; set; }
49
89 - /// <summary>
90 - /// Gets or sets the default compression level to use for cabinets
91 - /// that don't have their compression level explicitly set.
92 - /// </summary>
93 - //public CompressionLevel DefaultCompressionLevel { get; set; }
94 -
95 - /// <summary>
96 - /// Gets and sets the location to save the WixPdb.
97 - /// </summary>
98 - /// <value>The location in which to save the WixPdb. Null if the the WixPdb should not be output.</value>
99 - //public string PdbFile { get; set; }
100 -
101 - //public List<string> Ices { get; private set; }
102 -
103 - //public List<string> SuppressIces { get; private set; }
104 -
105 - /// <summary>
106 - /// Gets and sets the option to suppress resetting ACLs by the binder.
107 - /// </summary>
108 - /// <value>The option to suppress resetting ACLs by the binder.</value>
109 - public bool SuppressAclReset { get; set; }
110 -
111 - /// <summary>
112 - /// Gets and sets the option to suppress creating an image for MSI/MSM.
113 - /// </summary>
114 - /// <value>The option to suppress creating an image for MSI/MSM.</value>
115 - public bool SuppressLayout { get; set; }
116 -
117 - /// <summary>
118 - /// Gets and sets the option to suppress MSI/MSM validation.
119 - /// </summary>
120 - /// <value>The option to suppress MSI/MSM validation.</value>
121 - /// <remarks>This must be set before calling Bind.</remarks>
50 public bool SuppressValidation { get; set; }
51
124 - /// <summary>
125 - /// Gets and sets the option to suppress adding _Validation table rows.
126 - /// </summary>
127 - public bool SuppressAddingValidationRows { get; set; }
128 -
129 - /// <summary>
130 - /// Gets or sets the localizer.
131 - /// </summary>
132 - /// <value>The localizer.</value>
133 - public Localizer Localizer { get; set; }
134 -
135 - /// <summary>
136 - /// Gets or sets the temporary path for the Binder. If left null, the binder
137 - /// will use %TEMP% environment variable.
138 - /// </summary>
139 - /// <value>Path to temp files.</value>
140 - public string TempFilesLocation { get; set; }
141 -
142 - /// <summary>
143 - /// Gets or sets the Wix variable resolver.
144 - /// </summary>
145 - /// <value>The Wix variable resolver.</value>
146 - internal WixVariableResolver WixVariableResolver { get; set; }
147 -
148 - public BindResult Bind(IBindContext context)
149 - {
150 - this.Context = context;
52 + public bool DeltaBinaryPatch { get; set; }
53
152 - this.WriteBuildInfoTable(this.Context.IntermediateRepresentation, this.Context.OutputPath);
54 + public IServiceProvider ServiceProvider { get; }
55
154 - var bindResult = this.BackendBind();
155 - return bindResult;
156 - }
157 -
158 -//// private ResolveResult Resolve()
159 -//// {
160 -//// var buildingPatch = this.Context.IntermediateRepresentation.Sections.Any(s => s.Type == SectionType.Patch);
161 -
162 -//// var filesWithEmbeddedFiles = new ExtractEmbeddedFiles();
163 -
164 -//// IEnumerable<DelayedField> delayedFields;
165 -//// {
166 -//// var command = new ResolveFieldsCommand();
167 -//// command.Messaging = this.Context.Messaging;
168 -//// command.BuildingPatch = buildingPatch;
169 -//// command.BindVariableResolver = this.Context.WixVariableResolver;
170 -//// command.BindPaths = this.Context.BindPaths;
171 -//// command.Extensions = this.Context.Extensions;
172 -//// command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles;
173 -//// command.IntermediateFolder = this.Context.IntermediateFolder;
174 -//// command.Intermediate = this.Context.IntermediateRepresentation;
175 -//// command.SupportDelayedResolution = true;
176 -//// command.Execute();
177 -
178 -//// delayedFields = command.DelayedFields;
179 -//// }
180 -
181 -////#if REVISIT_FOR_PATCHING
182 -//// if (this.Context.IntermediateRepresentation.SubStorages != null)
183 -//// {
184 -//// foreach (SubStorage transform in this.Context.IntermediateRepresentation.SubStorages)
185 -//// {
186 -//// var command = new ResolveFieldsCommand();
187 -//// command.BuildingPatch = buildingPatch;
188 -//// command.BindVariableResolver = this.Context.WixVariableResolver;
189 -//// command.BindPaths = this.Context.BindPaths;
190 -//// command.Extensions = this.Context.Extensions;
191 -//// command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles;
192 -//// command.IntermediateFolder = this.Context.IntermediateFolder;
193 -//// command.Intermediate = this.Context.IntermediateRepresentation;
194 -//// command.SupportDelayedResolution = false;
195 -//// command.Execute();
196 -//// }
197 -//// }
198 -////#endif
199 -
200 -//// var expectedEmbeddedFiles = filesWithEmbeddedFiles.GetExpectedEmbeddedFiles();
201 -
202 -//// return new ResolveResult
203 -//// {
204 -//// ExpectedEmbeddedFiles = expectedEmbeddedFiles,
205 -//// DelayedFields = delayedFields,
206 -//// };
207 -//// }
208 -
209 - private BindResult BackendBind()
56 + public BindResult Execute()
57 {
211 - var extensionManager = this.Context.ServiceProvider.GetService<IExtensionManager>();
58 + var context = this.ServiceProvider.GetService<IBindContext>();
59 + context.Messaging = this.ServiceProvider.GetService<IMessaging>();
60 + context.CabbingThreadCount = this.CabbingThreadCount;
61 + context.CabCachePath = this.CabCachePath;
62 + context.Codepage = this.Codepage;
63 + context.DefaultCompressionLevel = this.DefaultCompressionLevel;
64 + context.DelayedFields = this.DelayedFields;
65 + context.ExpectedEmbeddedFiles = this.ExpectedEmbeddedFiles;
66 + context.Extensions = this.ServiceProvider.GetService<IExtensionManager>().Create<IBinderExtension>();
67 + context.Ices = this.Ices;
68 + context.IntermediateFolder = this.IntermediateFolder;
69 + context.IntermediateRepresentation = this.IntermediateRepresentation;
70 + context.OutputPath = this.OutputPath;
71 + context.OutputPdbPath = this.OutputPdbPath;
72 + context.SuppressIces = this.SuppressIces;
73 + context.SuppressValidation = this.SuppressValidation;
74 +
75 +
76 + // Prebind.
77 + //
78 + foreach (var extension in context.Extensions)
79 + {
80 + extension.PreBind(context);
81 + }
82
213 - var backendFactories = extensionManager.Create<IBackendFactory>();
83 + // Bind.
84 + //
85 + this.WriteBuildInfoTable(context.IntermediateRepresentation, context.OutputPath, context.OutputPdbPath);
86
215 - var entrySection = this.Context.IntermediateRepresentation.Sections[0];
87 + var bindResult = this.BackendBind(context);
88
217 - foreach (var factory in backendFactories)
89 + if (bindResult != null)
90 {
219 - if (factory.TryCreateBackend(entrySection.Type.ToString(), this.Context.OutputPath, null, out var backend))
91 + // Postbind.
92 + //
93 + foreach (var extension in context.Extensions)
94 {
221 - var result = backend.Bind(this.Context);
222 - return result;
95 + extension.PostBind(bindResult);
96 }
97 }
98
226 - // TODO: messaging that a backend could not be found to bind the output type?
227 -
228 - return null;
99 + return bindResult;
100 }
101
231 - /// <summary>
232 - /// Binds an output.
233 - /// </summary>
234 - /// <param name="output">The output to bind.</param>
235 - /// <param name="file">The Windows Installer file to create.</param>
236 - /// <remarks>The Binder.DeleteTempFiles method should be called after calling this method.</remarks>
237 - /// <returns>true if binding completed successfully; false otherwise</returns>
238 -#if false
239 - public bool Bind(Output output, string file)
102 + private BindResult BackendBind(IBindContext context)
103 {
241 - // Ensure the cabinet cache path exists if we are going to use it.
242 - if (!String.IsNullOrEmpty(this.CabCachePath))
243 - {
244 - Directory.CreateDirectory(this.CabCachePath);
245 - }
246 -
247 - //var fileManagerCore = new BinderFileManagerCore();
248 - //fileManagerCore.CabCachePath = this.CabCachePath;
249 - //fileManagerCore.Output = output;
250 - //fileManagerCore.TempFilesLocation = this.TempFilesLocation;
251 - //fileManagerCore.AddBindPaths(this.BindPaths, BindStage.Normal);
252 - //fileManagerCore.AddBindPaths(this.TargetBindPaths, BindStage.Target);
253 - //fileManagerCore.AddBindPaths(this.UpdatedBindPaths, BindStage.Updated);
254 - //foreach (IBinderFileManager fileManager in this.fileManagers)
255 - //{
256 - // fileManager.Core = fileManagerCore;
257 - //}
258 -
259 - this.core = new BinderCore();
260 - this.core.FileManagerCore = fileManagerCore;
261 -
262 - this.WriteBuildInfoTable(output, file);
263 -
264 - // Initialize extensions.
265 - foreach (IBinderExtension extension in this.extensions)
266 - {
267 - extension.Core = this.core;
268 -
269 - extension.Initialize(output);
270 - }
271 -
272 - // Gather all the wix variables.
273 - //Table wixVariableTable = output.Tables["WixVariable"];
274 - //if (null != wixVariableTable)
275 - //{
276 - // foreach (WixVariableRow wixVariableRow in wixVariableTable.Rows)
277 - // {
278 - // this.WixVariableResolver.AddVariable(wixVariableRow);
279 - // }
280 - //}
281 -
282 - //BindContext context = new BindContext();
283 - //context.CabbingThreadCount = this.CabbingThreadCount;
284 - //context.DefaultCompressionLevel = this.DefaultCompressionLevel;
285 - //context.Extensions = this.extensions;
286 - //context.FileManagerCore = fileManagerCore;
287 - //context.FileManagers = this.fileManagers;
288 - //context.Ices = this.Ices;
289 - //context.IntermediateFolder = this.TempFilesLocation;
290 - //context.IntermediateRepresentation = output;
291 - //context.Localizer = this.Localizer;
292 - //context.OutputPath = file;
293 - //context.OutputPdbPath = this.PdbFile;
294 - //context.SuppressIces = this.SuppressIces;
295 - //context.SuppressValidation = this.SuppressValidation;
296 - //context.WixVariableResolver = this.WixVariableResolver;
297 -
298 - BindResult result = null;
299 -
300 - foreach (var factory in this.BackendFactories)
301 - {
302 - if (factory.TryCreateBackend(output.Type.ToString(), file, null, out var backend))
303 - {
304 - result = backend.Bind(context);
305 - break;
306 - }
307 - }
104 + var extensionManager = context.ServiceProvider.GetService<IExtensionManager>();
105
309 - if (result == null)
310 - {
311 - // TODO: messaging that a backend could not be found to bind the output type?
106 + var backendFactories = extensionManager.Create<IBackendFactory>();
107
313 - return false;
314 - }
108 + var entrySection = context.IntermediateRepresentation.Sections[0];
109
316 - // Layout media
317 - try
318 - {
319 - this.LayoutMedia(result.FileTransfers);
320 - }
321 - finally
110 + foreach (var factory in backendFactories)
111 {
323 - if (!String.IsNullOrEmpty(this.ContentsFile) && result.ContentFilePaths != null)
324 - {
325 - this.CreateContentsFile(this.ContentsFile, result.ContentFilePaths);
326 - }
327 -
328 - if (!String.IsNullOrEmpty(this.OutputsFile) && result.FileTransfers != null)
112 + if (factory.TryCreateBackend(entrySection.Type.ToString(), context.OutputPath, null, out var backend))
113 {
330 - this.CreateOutputsFile(this.OutputsFile, result.FileTransfers, this.PdbFile);
331 - }
332 -
333 - if (!String.IsNullOrEmpty(this.BuiltOutputsFile) && result.FileTransfers != null)
334 - {
335 - this.CreateBuiltOutputsFile(this.BuiltOutputsFile, result.FileTransfers, this.PdbFile);
114 + var result = backend.Bind(context);
115 + return result;
116 }
117 }
118
339 - this.core = null;
119 + // TODO: messaging that a backend could not be found to bind the output type?
120
341 - return Messaging.Instance.EncounteredError;
121 + return null;
122 }
343 -#endif
344 -
345 - /// <summary>
346 - /// Populates the WixBuildInfo table in an output.
347 - /// </summary>
348 - /// <param name="output">The output.</param>
349 - /// <param name="databaseFile">The output file if OutputFile not set.</param>
350 - private void WriteBuildInfoTable(Intermediate output, string outputFile)
123 +
124 + private void WriteBuildInfoTable(Intermediate output, string outputFile, string outputPdbPath)
125 {
126 var entrySection = output.Sections.First(s => s.Type != SectionType.Fragment);
127
@@ -358,117 +132,12 @@ namespace WixToolset.Core
132 buildInfoRow.WixVersion = fileVersion.FileVersion;
133 buildInfoRow.WixOutputFile = outputFile;
134
361 - if (!String.IsNullOrEmpty(this.Context.WixprojectFile))
362 - {
363 - buildInfoRow.WixProjectFile = this.Context.WixprojectFile;
364 - }
365 -
366 - if (!String.IsNullOrEmpty(this.Context.OutputPdbPath))
135 + if (!String.IsNullOrEmpty(outputPdbPath))
136 {
368 - buildInfoRow.WixPdbFile = this.Context.OutputPdbPath;
137 + buildInfoRow.WixPdbFile = outputPdbPath;
138 }
139
140 entrySection.Tuples.Add(buildInfoRow);
141 }
373 -
374 -#if DELETE_THIS_CODE
375 - /// <summary>
376 - /// Binds a bundle.
377 - /// </summary>
378 - /// <param name="bundle">The bundle to bind.</param>
379 - /// <param name="bundleFile">The bundle to create.</param>
380 - private void BindBundle(Output bundle, string bundleFile, out IEnumerable<FileTransfer> fileTransfers, out IEnumerable<string> contentPaths)
381 - {
382 - BindBundleCommand command = new BindBundleCommand();
383 - command.DefaultCompressionLevel = this.DefaultCompressionLevel;
384 - command.Extensions = this.extensions;
385 - command.FileManagerCore = this.fileManagerCore;
386 - command.FileManagers = this.fileManagers;
387 - command.Output = bundle;
388 - command.OutputPath = bundleFile;
389 - command.PdbFile = this.PdbFile;
390 - command.TableDefinitions = this.core.TableDefinitions;
391 - command.TempFilesLocation = this.TempFilesLocation;
392 - command.WixVariableResolver = this.WixVariableResolver;
393 - command.Execute();
394 -
395 - fileTransfers = command.FileTransfers;
396 - contentPaths = command.ContentFilePaths;
397 - }
398 -
399 - /// <summary>
400 - /// Binds a databse.
401 - /// </summary>
402 - /// <param name="output">The output to bind.</param>
403 - /// <param name="databaseFile">The database file to create.</param>
404 - private void BindDatabase(Output output, string databaseFile, out IEnumerable<FileTransfer> fileTransfers, out IEnumerable<string> contentPaths)
405 - {
406 - Validator validator = null;
407 -
408 - // tell the binder about the validator if validation isn't suppressed
409 - if (!this.SuppressValidation && (OutputType.Module == output.Type || OutputType.Product == output.Type))
410 - {
411 - validator = new Validator();
412 - validator.TempFilesLocation = Path.Combine(this.TempFilesLocation, "validate");
413 -
414 - // set the default cube file
415 - string lightDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
416 - string cubePath = (OutputType.Module == output.Type) ? Path.Combine(lightDirectory, "mergemod.cub") : Path.Combine(lightDirectory, "darice.cub");
417 - validator.AddCubeFile(cubePath);
418 -
419 - // by default, disable ICEs that have equivalent-or-better checks in WiX
420 - this.SuppressIces.Add("ICE08");
421 - this.SuppressIces.Add("ICE33");
422 - this.SuppressIces.Add("ICE47");
423 - this.SuppressIces.Add("ICE66");
424 -
425 - // set the ICEs
426 - validator.ICEs = this.Ices.ToArray();
427 -
428 - // set the suppressed ICEs
429 - validator.SuppressedICEs = this.SuppressIces.ToArray();
430 - }
431 -
432 - BindDatabaseCommand command = new BindDatabaseCommand();
433 - command.CabbingThreadCount = this.CabbingThreadCount;
434 - command.Codepage = this.Localizer == null ? -1 : this.Localizer.Codepage;
435 - command.DefaultCompressionLevel = this.DefaultCompressionLevel;
436 - command.Extensions = this.extensions;
437 - command.FileManagerCore = this.fileManagerCore;
438 - command.FileManagers = this.fileManagers;
439 - command.InspectorExtensions = this.inspectorExtensions;
440 - command.Localizer = this.Localizer;
441 - command.PdbFile = this.PdbFile;
442 - command.Output = output;
443 - command.OutputPath = databaseFile;
444 - command.SuppressAddingValidationRows = this.SuppressAddingValidationRows;
445 - command.SuppressLayout = this.SuppressLayout;
446 - command.TableDefinitions = this.core.TableDefinitions;
447 - command.TempFilesLocation = this.TempFilesLocation;
448 - command.Validator = validator;
449 - command.WixVariableResolver = this.WixVariableResolver;
450 - command.Execute();
451 -
452 - fileTransfers = command.FileTransfers;
453 - contentPaths = command.ContentFilePaths;
454 - }
455 -
456 - /// <summary>
457 - /// Binds a transform.
458 - /// </summary>
459 - /// <param name="transform">The transform to bind.</param>
460 - /// <param name="outputPath">The transform to create.</param>
461 - private void BindTransform(Output transform, string outputPath)
462 - {
463 - BindTransformCommand command = new BindTransformCommand();
464 - command.Extensions = this.extensions;
465 - command.FileManagers = this.fileManagers;
466 - command.TableDefinitions = this.core.TableDefinitions;
467 - command.TempFilesLocation = this.TempFilesLocation;
468 - command.Transform = transform;
469 - command.OutputPath = outputPath;
470 - command.Execute();
471 - }
472 -#endif
142 }
143 }
src/WixToolset.Core/CommandLine/BuildCommand.cs
+32 -30
@@ -1,6 +1,6 @@
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.Core
3 +namespace WixToolset.Core.CommandLine
4 {
5 using System;
6 using System.Collections.Generic;
@@ -13,7 +13,7 @@ namespace WixToolset.Core
13
14 internal class BuildCommand : ICommandLineCommand
15 {
16 - public BuildCommand(IServiceProvider serviceProvider, IMessaging messaging, IExtensionManager extensions, IEnumerable<SourceFile> sources, IDictionary<string, string> preprocessorVariables, IEnumerable<string> locFiles, IEnumerable<string> libraryFiles, string outputPath, OutputType outputType, string cabCachePath, IEnumerable<string> cultures, bool bindFiles, IEnumerable<BindPath> bindPaths, string intermediateFolder, string contentsFile, string outputsFile, string builtOutputsFile, string wixProjectFile)
16 + public BuildCommand(IServiceProvider serviceProvider, IMessaging messaging, IExtensionManager extensions, IEnumerable<SourceFile> sources, IDictionary<string, string> preprocessorVariables, IEnumerable<string> locFiles, IEnumerable<string> libraryFiles, string outputPath, OutputType outputType, string cabCachePath, IEnumerable<string> cultures, bool bindFiles, IEnumerable<BindPath> bindPaths, string intermediateFolder, string contentsFile, string outputsFile, string builtOutputsFile)
17 {
18 this.ServiceProvider = serviceProvider;
19 this.Messaging = messaging;
@@ -34,7 +34,6 @@ namespace WixToolset.Core
34 this.ContentsFile = contentsFile;
35 this.OutputsFile = outputsFile;
36 this.BuiltOutputsFile = builtOutputsFile;
37 - this.WixProjectFile = wixProjectFile;
37 }
38
39 public IServiceProvider ServiceProvider { get; }
@@ -73,8 +72,6 @@ namespace WixToolset.Core
72
73 public string BuiltOutputsFile { get; }
74
76 - public string WixProjectFile { get; }
77 -
75 public int Execute()
76 {
77 var intermediates = this.CompilePhase();
@@ -207,7 +204,12 @@ namespace WixToolset.Core
204
205 ResolveResult resolveResult;
206 {
210 - var resolver = new Resolver(this.ServiceProvider, this.BindPaths, output, this.IntermediateFolder, localizations);
207 + var resolver = new Resolver(this.ServiceProvider);
208 + resolver.BindPaths = this.BindPaths;
209 + resolver.IntermediateFolder = this.IntermediateFolder;
210 + resolver.IntermediateRepresentation = output;
211 + resolver.Localizations = localizations;
212 +
213 resolveResult = resolver.Execute();
214 }
215
@@ -224,28 +226,22 @@ namespace WixToolset.Core
226 intermediateFolder = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
227 }
228
227 - var context = this.ServiceProvider.GetService<IBindContext>();
228 - context.Messaging = this.Messaging;
229 - //context.CabbingThreadCount = this.CabbingThreadCount;
230 - context.CabCachePath = this.CabCachePath;
231 - context.Codepage = resolveResult.Codepage;
232 - //context.DefaultCompressionLevel = this.DefaultCompressionLevel;
233 - context.DelayedFields = resolveResult.DelayedFields;
234 - context.ExpectedEmbeddedFiles = resolveResult.ExpectedEmbeddedFiles;
235 - //context.Ices = this.Ices;
236 - context.IntermediateFolder = intermediateFolder;
237 - context.IntermediateRepresentation = resolveResult.IntermediateRepresentation;
238 - context.OutputPath = this.OutputPath;
239 - context.OutputPdbPath = Path.ChangeExtension(this.OutputPath, ".wixpdb");
240 - //context.SuppressIces = this.SuppressIces;
241 - context.SuppressValidation = true; // TODO: set this correctly
242 - context.ContentsFile = this.ContentsFile;
243 - context.OutputsFile = this.OutputsFile;
244 - context.BuiltOutputsFile = this.BuiltOutputsFile;
245 - context.WixprojectFile = this.WixProjectFile;
246 -
247 - var binder = new Binder();
248 - bindResult = binder.Bind(context);
229 + var binder = new Binder(this.ServiceProvider);
230 + //binder.CabbingThreadCount = this.CabbingThreadCount;
231 + binder.CabCachePath = this.CabCachePath;
232 + binder.Codepage = resolveResult.Codepage;
233 + //binder.DefaultCompressionLevel = this.DefaultCompressionLevel;
234 + binder.DelayedFields = resolveResult.DelayedFields;
235 + binder.ExpectedEmbeddedFiles = resolveResult.ExpectedEmbeddedFiles;
236 + binder.Ices = Array.Empty<string>(); // TODO: set this correctly
237 + binder.IntermediateFolder = intermediateFolder;
238 + binder.IntermediateRepresentation = resolveResult.IntermediateRepresentation;
239 + binder.OutputPath = this.OutputPath;
240 + binder.OutputPdbPath = Path.ChangeExtension(this.OutputPath, ".wixpdb");
241 + binder.SuppressIces = Array.Empty<string>(); // TODO: set this correctly
242 + binder.SuppressValidation = true; // TODO: set this correctly
243 +
244 + bindResult = binder.Execute();
245 }
246
247 if (this.Messaging.EncounteredError)
@@ -254,8 +250,14 @@ namespace WixToolset.Core
250 }
251
252 {
257 - // TODO: correctly set SuppressAclReset bool at the end.
258 - var layout = new Layout(this.ServiceProvider, bindResult.FileTransfers, bindResult.ContentFilePaths, this.ContentsFile, this.OutputsFile, this.BuiltOutputsFile, false);
253 + var layout = new Layout(this.ServiceProvider);
254 + layout.FileTransfers = bindResult.FileTransfers;
255 + layout.ContentFilePaths = bindResult.ContentFilePaths;
256 + layout.ContentsFile = this.ContentsFile;
257 + layout.OutputsFile = this.OutputsFile;
258 + layout.BuiltOutputsFile = this.BuiltOutputsFile;
259 + layout.SuppressAclReset = false; // TODO: correctly set SuppressAclReset
260 +
261 layout.Execute();
262 }
263 }
src/WixToolset.Core/CommandLine/CommandLineContext.cs
+1 -1
@@ -1,6 +1,6 @@
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.Core
3 +namespace WixToolset.Core.CommandLine
4 {
5 using System;
6 using WixToolset.Extensibility.Services;
src/WixToolset.Core/CommandLine/CommandLineHelper.cs new
+216
@@ -0,0 +1,216 @@
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.Core.CommandLine
4 +{
5 + using System;
6 + using System.IO;
7 + using WixToolset.Data;
8 + using WixToolset.Extensibility.Services;
9 +
10 + public class CommandLineHelper
11 + {
12 + /// <summary>
13 + /// Validates that a string is a valid directory name, and throws appropriate warnings/errors if not
14 + /// </summary>
15 + /// <param name="commandlineSwitch">The commandline switch we're parsing (for error display purposes).</param>
16 + /// <param name="messageHandler">The messagehandler to report warnings/errors to.</param>
17 + /// <param name="args">The list of strings to check.</param>
18 + /// <param name="index">The index (in args) of the commandline parameter to be parsed.</param>
19 + /// <returns>The string if it is valid, null if it is invalid.</returns>
20 + public static string GetDirectory(string commandlineSwitch, IMessaging messageHandler, string[] args, int index)
21 + {
22 + return GetDirectory(commandlineSwitch, messageHandler, args, index, false);
23 + }
24 +
25 + /// <summary>
26 + /// Validates that a string is a valid directory name, and throws appropriate warnings/errors if not
27 + /// </summary>
28 + /// <param name="commandlineSwitch">The commandline switch we're parsing (for error display purposes).</param>
29 + /// <param name="messageHandler">The messagehandler to report warnings/errors to.</param>
30 + /// <param name="args">The list of strings to check.</param>
31 + /// <param name="index">The index (in args) of the commandline parameter to be parsed.</param>
32 + /// <param name="allowPrefix">Indicates if a colon-delimited prefix is allowed.</param>
33 + /// <returns>The string if it is valid, null if it is invalid.</returns>
34 + public static string GetDirectory(string commandlineSwitch, IMessaging messageHandler, string[] args, int index, bool allowPrefix)
35 + {
36 + commandlineSwitch = String.Concat("-", commandlineSwitch);
37 +
38 + if (!IsValidArg(args, index))
39 + {
40 + messageHandler.Write(ErrorMessages.DirectoryPathRequired(commandlineSwitch));
41 + return null;
42 + }
43 +
44 + if (File.Exists(args[index]))
45 + {
46 + messageHandler.Write(ErrorMessages.ExpectedDirectoryGotFile(commandlineSwitch, args[index]));
47 + return null;
48 + }
49 +
50 + return VerifyPath(messageHandler, args[index], allowPrefix);
51 + }
52 +
53 + /// <summary>
54 + /// Validates that a string is a valid filename, and throws appropriate warnings/errors if not
55 + /// </summary>
56 + /// <param name="commandlineSwitch">The commandline switch we're parsing (for error display purposes).</param>
57 + /// <param name="messageHandler">The messagehandler to report warnings/errors to.</param>
58 + /// <param name="args">The list of strings to check.</param>
59 + /// <param name="index">The index (in args) of the commandline parameter to be parsed.</param>
60 + /// <returns>The string if it is valid, null if it is invalid.</returns>
61 + public static string GetFile(string commandlineSwitch, IMessaging messageHandler, string[] args, int index)
62 + {
63 + commandlineSwitch = String.Concat("-", commandlineSwitch);
64 +
65 + if (!IsValidArg(args, index))
66 + {
67 + messageHandler.Write(ErrorMessages.FilePathRequired(commandlineSwitch));
68 + return null;
69 + }
70 +
71 + if (Directory.Exists(args[index]))
72 + {
73 + messageHandler.Write(ErrorMessages.ExpectedFileGotDirectory(commandlineSwitch, args[index]));
74 + return null;
75 + }
76 +
77 + return VerifyPath(messageHandler, args[index]);
78 + }
79 +
80 + /// <summary>
81 + /// Get a set of files that possibly have a search pattern in the path (such as '*').
82 + /// </summary>
83 + /// <param name="searchPath">Search path to find files in.</param>
84 + /// <param name="fileType">Type of file; typically "Source".</param>
85 + /// <returns>An array of files matching the search path.</returns>
86 + /// <remarks>
87 + /// This method is written in this verbose way because it needs to support ".." in the path.
88 + /// It needs the directory path isolated from the file name in order to use Directory.GetFiles
89 + /// or DirectoryInfo.GetFiles. The only way to get this directory path is manually since
90 + /// Path.GetDirectoryName does not support ".." in the path.
91 + /// </remarks>
92 + /// <exception cref="WixFileNotFoundException">Throws WixFileNotFoundException if no file matching the pattern can be found.</exception>
93 + public static string[] GetFiles(string searchPath, string fileType)
94 + {
95 + if (null == searchPath)
96 + {
97 + throw new ArgumentNullException(nameof(searchPath));
98 + }
99 +
100 + // Convert alternate directory separators to the standard one.
101 + string filePath = searchPath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
102 + int lastSeparator = filePath.LastIndexOf(Path.DirectorySeparatorChar);
103 + string[] files = null;
104 +
105 + try
106 + {
107 + if (0 > lastSeparator)
108 + {
109 + files = Directory.GetFiles(".", filePath);
110 + }
111 + else // found directory separator
112 + {
113 + files = Directory.GetFiles(filePath.Substring(0, lastSeparator + 1), filePath.Substring(lastSeparator + 1));
114 + }
115 + }
116 + catch (DirectoryNotFoundException)
117 + {
118 + // Don't let this function throw the DirectoryNotFoundException. This exception
119 + // occurs for non-existant directories and invalid characters in the searchPattern.
120 + }
121 + catch (ArgumentException)
122 + {
123 + // Don't let this function throw the ArgumentException. This exception
124 + // occurs in certain situations such as when passing a malformed UNC path.
125 + }
126 + catch (IOException)
127 + {
128 + throw new WixFileNotFoundException(searchPath, fileType);
129 + }
130 +
131 + if (null == files || 0 == files.Length)
132 + {
133 + throw new WixFileNotFoundException(searchPath, fileType);
134 + }
135 +
136 + return files;
137 + }
138 +
139 + /// <summary>
140 + /// Validates that a valid string parameter (without "/" or "-"), and returns a bool indicating its validity
141 + /// </summary>
142 + /// <param name="args">The list of strings to check.</param>
143 + /// <param name="index">The index (in args) of the commandline parameter to be validated.</param>
144 + /// <returns>True if a valid string parameter exists there, false if not.</returns>
145 + public static bool IsValidArg(string[] args, int index)
146 + {
147 + if (args.Length <= index || String.IsNullOrEmpty(args[index]) || '/' == args[index][0] || '-' == args[index][0])
148 + {
149 + return false;
150 + }
151 + else
152 + {
153 + return true;
154 + }
155 + }
156 +
157 + /// <summary>
158 + /// Validates that a commandline parameter is a valid file or directory name, and throws appropriate warnings/errors if not
159 + /// </summary>
160 + /// <param name="messageHandler">The messagehandler to report warnings/errors to.</param>
161 + /// <param name="path">The path to test.</param>
162 + /// <returns>The string if it is valid, null if it is invalid.</returns>
163 + public static string VerifyPath(IMessaging messageHandler, string path)
164 + {
165 + return VerifyPath(messageHandler, path, false);
166 + }
167 +
168 + /// <summary>
169 + /// Validates that a commandline parameter is a valid file or directory name, and throws appropriate warnings/errors if not
170 + /// </summary>
171 + /// <param name="messageHandler">The messagehandler to report warnings/errors to.</param>
172 + /// <param name="path">The path to test.</param>
173 + /// <param name="allowPrefix">Indicates if a colon-delimited prefix is allowed.</param>
174 + /// <returns>The full path if it is valid, null if it is invalid.</returns>
175 + public static string VerifyPath(IMessaging messageHandler, string path, bool allowPrefix)
176 + {
177 + string fullPath;
178 +
179 + if (0 <= path.IndexOf('\"'))
180 + {
181 + messageHandler.Write(ErrorMessages.PathCannotContainQuote(path));
182 + return null;
183 + }
184 +
185 + try
186 + {
187 + string prefix = null;
188 + if (allowPrefix)
189 + {
190 + int prefixLength = path.IndexOf('=') + 1;
191 + if (0 != prefixLength)
192 + {
193 + prefix = path.Substring(0, prefixLength);
194 + path = path.Substring(prefixLength);
195 + }
196 + }
197 +
198 + if (String.IsNullOrEmpty(prefix))
199 + {
200 + fullPath = Path.GetFullPath(path);
201 + }
202 + else
203 + {
204 + fullPath = String.Concat(prefix, Path.GetFullPath(path));
205 + }
206 + }
207 + catch (Exception e)
208 + {
209 + messageHandler.Write(ErrorMessages.InvalidCommandLineFileName(path, e.Message));
210 + return null;
211 + }
212 +
213 + return fullPath;
214 + }
215 + }
216 +}
src/WixToolset.Core/CommandLine/CommandLineParser.cs renamed
+14 -78
@@ -1,6 +1,6 @@
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.Core
3 +namespace WixToolset.Core.CommandLine
4 {
5 using System;
6 using System.Collections.Generic;
@@ -22,7 +22,7 @@ namespace WixToolset.Core
22 Bind,
23 }
24
25 - internal class CommandLine : ICommandLine, IParseCommandLine
25 + internal class CommandLineParser : ICommandLine, IParseCommandLine
26 {
27 private IServiceProvider ServiceProvider { get; set; }
28
@@ -54,7 +54,7 @@ namespace WixToolset.Core
54
55 if (!String.IsNullOrEmpty(context.Arguments))
56 {
57 - args = CommandLine.ParseArgumentsToArray(context.Arguments).Union(args).ToArray();
57 + args = CommandLineParser.ParseArgumentsToArray(context.Arguments).Union(args).ToArray();
58 }
59
60 return this.ParseStandardCommandLine(context, args);
@@ -88,13 +88,12 @@ namespace WixToolset.Core
88 var contentsFile = String.Empty;
89 var outputsFile = String.Empty;
90 var builtOutputsFile = String.Empty;
91 - var wixProjectFile = String.Empty;
91
92 this.Parse(context, args, (cmdline, arg) => Enum.TryParse(arg, true, out command), (cmdline, arg) =>
93 {
94 if (cmdline.IsSwitch(arg))
95 {
97 - var parameter = arg.TrimStart(new[] { '-', '/' });
96 + var parameter = arg.Substring(1);
97 switch (parameter.ToLowerInvariant())
98 {
99 case "?":
@@ -127,9 +126,6 @@ namespace WixToolset.Core
126 case "builtoutputsfile":
127 cmdline.GetNextArgumentOrError(ref builtOutputsFile);
128 return true;
130 - case "wixprojectfile":
131 - cmdline.GetNextArgumentOrError(ref wixProjectFile);
132 - return true;
129
130 case "d":
131 case "define":
@@ -181,7 +177,7 @@ namespace WixToolset.Core
177 }
178 else
179 {
184 - files.AddRange(cmdline.GetFiles(arg, "source code"));
180 + files.AddRange(CommandLineHelper.GetFiles(arg, "source code"));
181 return true;
182 }
183 });
@@ -211,7 +207,7 @@ namespace WixToolset.Core
207 var variables = this.GatherPreprocessorVariables(defines);
208 var bindPathList = this.GatherBindPaths(bindPaths);
209 var type = CalculateOutputType(outputType, outputFile);
214 - return new BuildCommand(this.ServiceProvider, this.Messaging, this.ExtensionManager, sourceFiles, variables, locFiles, libraryFiles, outputFile, type, cabCachePath, cultures, bindFiles, bindPathList, intermediateFolder, contentsFile, outputsFile, builtOutputsFile, wixProjectFile);
210 + return new BuildCommand(this.ServiceProvider, this.Messaging, this.ExtensionManager, sourceFiles, variables, locFiles, libraryFiles, outputFile, type, cabCachePath, cultures, bindFiles, bindPathList, intermediateFolder, contentsFile, outputsFile, builtOutputsFile);
211 }
212
213 case Commands.Compile:
@@ -283,7 +279,7 @@ namespace WixToolset.Core
279 }
280 #endif
281
286 - private ICommandLine Parse(ICommandLineContext context, string[] commandLineArguments, Func<CommandLine, string, bool> parseCommand, Func<CommandLine, string, bool> parseArgument)
282 + private ICommandLine Parse(ICommandLineContext context, string[] commandLineArguments, Func<CommandLineParser, string, bool> parseCommand, Func<CommandLineParser, string, bool> parseArgument)
283 {
284 this.FlattenArgumentsWithResponseFilesIntoOriginalArguments(commandLineArguments);
285
@@ -335,7 +331,7 @@ namespace WixToolset.Core
331
332 foreach (var bindPath in bindPaths)
333 {
338 - BindPath bp = BindPath.Parse(bindPath);
334 + var bp = BindPath.Parse(bindPath);
335
336 if (Directory.Exists(bp.Path))
337 {
@@ -350,65 +346,6 @@ namespace WixToolset.Core
346 return result;
347 }
348
353 - /// <summary>
354 - /// Get a set of files that possibly have a search pattern in the path (such as '*').
355 - /// </summary>
356 - /// <param name="searchPath">Search path to find files in.</param>
357 - /// <param name="fileType">Type of file; typically "Source".</param>
358 - /// <returns>An array of files matching the search path.</returns>
359 - /// <remarks>
360 - /// This method is written in this verbose way because it needs to support ".." in the path.
361 - /// It needs the directory path isolated from the file name in order to use Directory.GetFiles
362 - /// or DirectoryInfo.GetFiles. The only way to get this directory path is manually since
363 - /// Path.GetDirectoryName does not support ".." in the path.
364 - /// </remarks>
365 - /// <exception cref="WixFileNotFoundException">Throws WixFileNotFoundException if no file matching the pattern can be found.</exception>
366 - public string[] GetFiles(string searchPath, string fileType)
367 - {
368 - if (null == searchPath)
369 - {
370 - throw new ArgumentNullException(nameof(searchPath));
371 - }
372 -
373 - // Convert alternate directory separators to the standard one.
374 - string filePath = searchPath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
375 - int lastSeparator = filePath.LastIndexOf(Path.DirectorySeparatorChar);
376 - string[] files = null;
377 -
378 - try
379 - {
380 - if (0 > lastSeparator)
381 - {
382 - files = Directory.GetFiles(".", filePath);
383 - }
384 - else // found directory separator
385 - {
386 - files = Directory.GetFiles(filePath.Substring(0, lastSeparator + 1), filePath.Substring(lastSeparator + 1));
387 - }
388 - }
389 - catch (DirectoryNotFoundException)
390 - {
391 - // Don't let this function throw the DirectoryNotFoundException. This exception
392 - // occurs for non-existant directories and invalid characters in the searchPattern.
393 - }
394 - catch (ArgumentException)
395 - {
396 - // Don't let this function throw the ArgumentException. This exception
397 - // occurs in certain situations such as when passing a malformed UNC path.
398 - }
399 - catch (IOException)
400 - {
401 - throw new WixFileNotFoundException(searchPath, fileType);
402 - }
403 -
404 - if (null == files || 0 == files.Length)
405 - {
406 - throw new WixFileNotFoundException(searchPath, fileType);
407 - }
408 -
409 - return files;
410 - }
411 -
349 /// <summary>
350 /// Validates that a valid switch (starts with "/" or "-"), and returns a bool indicating its validity
351 /// </summary>
@@ -449,7 +386,7 @@ namespace WixToolset.Core
386 {
387 if (this.TryGetNextArgumentOrError(out var arg))
388 {
452 - foreach (var path in this.GetFiles(arg, fileType))
389 + foreach (var path in CommandLineHelper.GetFiles(arg, fileType))
390 {
391 args.Add(path);
392 }
@@ -458,13 +395,12 @@ namespace WixToolset.Core
395
396 public bool TryGetNextArgumentOrError(out string arg)
397 {
461 - //if (this.RemainingArguments.TryDequeue(out arg) && !this.IsSwitch(arg))
398 if (TryDequeue(this.RemainingArguments, out arg) && !this.IsSwitch(arg))
399 {
400 return true;
401 }
402
467 - this.ErrorArgument = arg ?? CommandLine.ExpectedArgument;
403 + this.ErrorArgument = arg ?? CommandLineParser.ExpectedArgument;
404
405 return false;
406 }
@@ -489,7 +425,7 @@ namespace WixToolset.Core
425 {
426 if ('@' == arg[0])
427 {
492 - var responseFileArguments = CommandLine.ParseResponseFile(arg.Substring(1));
428 + var responseFileArguments = CommandLineParser.ParseResponseFile(arg.Substring(1));
429 args.AddRange(responseFileArguments);
430 }
431 else
@@ -526,7 +462,7 @@ namespace WixToolset.Core
462 }
463 }
464
529 - private void ProcessRemainingArguments(ICommandLineContext context, Func<CommandLine, string, bool> parseArgument, Func<CommandLine, string, bool> parseCommand)
465 + private void ProcessRemainingArguments(ICommandLineContext context, Func<CommandLineParser, string, bool> parseArgument, Func<CommandLineParser, string, bool> parseCommand)
466 {
467 var extensions = this.ExtensionManager.Create<IExtensionCommandLine>();
468
@@ -593,7 +529,7 @@ namespace WixToolset.Core
529 arguments = reader.ReadToEnd();
530 }
531
596 - return CommandLine.ParseArgumentsToArray(arguments);
532 + return CommandLineParser.ParseArgumentsToArray(arguments);
533 }
534
535 private static List<string> ParseArgumentsToArray(string arguments)
@@ -631,7 +567,7 @@ namespace WixToolset.Core
567 // Add the argument to the list if it's not empty.
568 if (arg.Length > 0)
569 {
634 - argsList.Add(CommandLine.ExpandEnvironmentVariables(arg.ToString()));
570 + argsList.Add(CommandLineParser.ExpandEnvironmentVariables(arg.ToString()));
571 arg.Length = 0;
572 }
573 }
src/WixToolset.Core/CommandLine/CommandLineResponseFile.cs
+1 -1
@@ -1,6 +1,6 @@
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
3 +namespace WixToolset.Core.CommandLine
4 {
5 using System;
6 using System.Collections;
src/WixToolset.Core/CommandLine/CompileCommand.cs
+1 -1
@@ -1,6 +1,6 @@
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.Core
3 +namespace WixToolset.Core.CommandLine
4 {
5 using System;
6 using System.Collections.Generic;
src/WixToolset.Core/CommandLine/HelpCommand.cs
+1 -1
@@ -1,6 +1,6 @@
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.Core
3 +namespace WixToolset.Core.CommandLine
4 {
5 using System;
6 using WixToolset.Extensibility.Services;
src/WixToolset.Core/CommandLine/VersionCommand.cs
+1 -1
@@ -1,6 +1,6 @@
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.Core
3 +namespace WixToolset.Core.CommandLine
4 {
5 using System;
6 using WixToolset.Extensibility.Services;
src/WixToolset.Core/Layout.cs
+10 -16
@@ -9,7 +9,6 @@ namespace WixToolset.Core
9 using WixToolset.Core.Bind;
10 using WixToolset.Data;
11 using WixToolset.Data.Bind;
12 - using WixToolset.Data.Tuples;
12 using WixToolset.Extensibility;
13 using WixToolset.Extensibility.Services;
14
@@ -18,36 +17,31 @@ namespace WixToolset.Core
17 /// </summary>
18 public sealed class Layout
19 {
21 - public Layout(IServiceProvider serviceProvider, IEnumerable<FileTransfer> fileTransfers, IEnumerable<string> contentFilePaths, string contentsFile, string outputsFile, string builtOutputsFile, bool suppressAclReset)
20 + public Layout(IServiceProvider serviceProvider)
21 {
22 this.ServiceProvider = serviceProvider;
24 - this.FileTransfers = fileTransfers;
25 - this.ContentFilePaths = contentFilePaths;
26 - this.ContentsFile = contentsFile;
27 - this.OutputsFile = outputsFile;
28 - this.BuiltOutputsFile = builtOutputsFile;
29 - this.SuppressAclReset = suppressAclReset;
30 - this.Messaging = this.ServiceProvider.GetService<IMessaging>();
23 }
24
25 private IServiceProvider ServiceProvider { get; }
26
35 - private IEnumerable<FileTransfer> FileTransfers { get; }
27 + public IEnumerable<FileTransfer> FileTransfers { get; set; }
28
37 - private IEnumerable<string> ContentFilePaths { get; }
29 + public IEnumerable<string> ContentFilePaths { get; set; }
30
39 - private string ContentsFile { get; }
31 + public string ContentsFile { get; set; }
32
41 - private string OutputsFile { get; }
33 + public string OutputsFile { get; set; }
34
43 - private string BuiltOutputsFile { get; }
35 + public string BuiltOutputsFile { get; set; }
36
45 - private bool SuppressAclReset { get; }
37 + public bool SuppressAclReset { get; set; }
38
47 - private IMessaging Messaging { get; }
39 + private IMessaging Messaging { get; set; }
40
41 public void Execute()
42 {
43 + this.Messaging = this.ServiceProvider.GetService<IMessaging>();
44 +
45 var extensionManager = this.ServiceProvider.GetService<IExtensionManager>();
46
47 var context = this.ServiceProvider.GetService<ILayoutContext>();
src/WixToolset.Core/Resolver.cs
+12 -17
@@ -16,34 +16,31 @@ namespace WixToolset.Core
16 /// </summary>
17 public sealed class Resolver
18 {
19 - public Resolver(IServiceProvider serviceProvider, IEnumerable<BindPath> bindPaths, Intermediate intermediateRepresentation, string intermediateFolder, IEnumerable<Localization> localizations)
19 + public Resolver(IServiceProvider serviceProvider)
20 {
21 this.ServiceProvider = serviceProvider;
22 - this.BindPaths = bindPaths;
23 - this.IntermediateRepresentation = intermediateRepresentation;
24 - this.IntermediateFolder = intermediateFolder;
25 - this.Localizations = localizations;
26 -
27 - this.Messaging = this.ServiceProvider.GetService<IMessaging>();
22 }
23
30 - private IServiceProvider ServiceProvider { get; }
24 + private IServiceProvider ServiceProvider { get; set; }
25
32 - private IEnumerable<BindPath> BindPaths { get; }
26 + public IEnumerable<BindPath> BindPaths { get; set; }
27
34 - private Intermediate IntermediateRepresentation { get; }
28 + public Intermediate IntermediateRepresentation { get; set; }
29
36 - private string IntermediateFolder { get; }
30 + public string IntermediateFolder { get; set; }
31
38 - private IEnumerable<Localization> Localizations { get; }
32 + public IEnumerable<Localization> Localizations { get; set; }
33
40 - private IMessaging Messaging { get; }
34 + private IMessaging Messaging { get; set; }
35
36 public ResolveResult Execute()
37 {
38 + this.Messaging = this.ServiceProvider.GetService<IMessaging>();
39 +
40 var localizer = new Localizer(this.Messaging, this.Localizations);
41
42 var variableResolver = new WixVariableResolver(this.Messaging, localizer);
43 + this.PopulateVariableResolver(variableResolver);
44
45 var context = this.ServiceProvider.GetService<IResolveContext>();
46 context.Messaging = this.Messaging;
@@ -51,7 +48,7 @@ namespace WixToolset.Core
48 context.Extensions = this.ServiceProvider.GetService<IExtensionManager>().Create<IResolverExtension>();
49 context.IntermediateFolder = this.IntermediateFolder;
50 context.IntermediateRepresentation = this.IntermediateRepresentation;
54 - context.WixVariableResolver = this.PopulateVariableResolver(variableResolver);
51 + context.WixVariableResolver = variableResolver;
52
53 // Preresolve.
54 //
@@ -212,7 +209,7 @@ namespace WixToolset.Core
209 }
210 }
211
215 - private WixVariableResolver PopulateVariableResolver(WixVariableResolver resolver)
212 + private void PopulateVariableResolver(WixVariableResolver resolver)
213 {
214 // Gather all the wix variables.
215 var wixVariableTuples = this.IntermediateRepresentation.Sections.SelectMany(s => s.Tuples).OfType<WixVariableTuple>();
@@ -227,8 +224,6 @@ namespace WixToolset.Core
224 this.Messaging.Write(ErrorMessages.WixVariableCollision(tuple.SourceLineNumbers, tuple.WixVariable));
225 }
226 }
230 -
231 - return resolver;
227 }
228 }
229 }
src/WixToolset.Core/WixToolsetServiceProvider.cs
+2 -1
@@ -3,6 +3,7 @@
3 namespace WixToolset.Core
4 {
5 using System;
6 + using WixToolset.Core.CommandLine;
7 using WixToolset.Core.ExtensibilityServices;
8 using WixToolset.Data;
9 using WixToolset.Extensibility;
@@ -64,7 +65,7 @@ namespace WixToolset.Core
65
66 if (serviceType == typeof(ICommandLine))
67 {
67 - return new CommandLine();
68 + return new CommandLineParser();
69 }
70
71 // Singletons.
src/light/App.ico
Binary files /dev/null and b/src/light/App.ico differ
src/light/AssemblyInfo.cs new
+9
@@ -0,0 +1,9 @@
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 +using System;
4 +using System.Reflection;
5 +using System.Runtime.CompilerServices;
6 +using System.Runtime.InteropServices;
7 +
8 +[assembly: AssemblyCulture("")]
9 +[assembly: ComVisible(false)]
src/light/LightCommandLine.cs new
+485
@@ -0,0 +1,485 @@
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.Tools
4 +{
5 + using System;
6 + using System.Collections.Generic;
7 + using System.Globalization;
8 + using System.IO;
9 + using WixToolset.Core.CommandLine;
10 + using WixToolset.Data;
11 + using WixToolset.Extensibility.Services;
12 +
13 + public class LightCommandLine
14 + {
15 + public LightCommandLine(IMessaging messaging)
16 + {
17 + this.Messaging = messaging;
18 + this.ShowLogo = true;
19 + this.Tidy = true;
20 +
21 + this.CubeFiles = new List<string>();
22 + this.SuppressIces = new List<string>();
23 + this.Ices = new List<string>();
24 + this.BindPaths = new List<BindPath>();
25 + this.Extensions = new List<string>();
26 + this.Files = new List<string>();
27 + this.LocalizationFiles = new List<string>();
28 + this.Variables = new Dictionary<string, string>();
29 + }
30 +
31 + public IMessaging Messaging { get; }
32 +
33 + public string PdbFile { get; private set; }
34 +
35 + public CompressionLevel? DefaultCompressionLevel { get; set; }
36 +
37 + public bool SuppressAclReset { get; private set; }
38 +
39 + public bool SuppressLayout { get; private set; }
40 +
41 + public bool SuppressWixPdb { get; private set; }
42 +
43 + public bool SuppressValidation { get; private set; }
44 +
45 + public string IntermediateFolder { get; private set; }
46 +
47 + public string OutputsFile { get; private set; }
48 +
49 + public string BuiltOutputsFile { get; private set; }
50 +
51 + public string WixprojectFile { get; private set; }
52 +
53 + public string ContentsFile { get; private set; }
54 +
55 + public List<string> Ices { get; private set; }
56 +
57 + public string CabCachePath { get; private set; }
58 +
59 + public int CabbingThreadCount { get; private set; }
60 +
61 + public List<string> CubeFiles { get; private set; }
62 +
63 + public List<string> SuppressIces { get; private set; }
64 +
65 + public bool ShowLogo { get; private set; }
66 +
67 + public bool ShowHelp { get; private set; }
68 +
69 + public bool ShowPedanticMessages { get; private set; }
70 +
71 + public bool SuppressLocalization { get; private set; }
72 +
73 + public bool SuppressVersionCheck { get; private set; }
74 +
75 + public string[] Cultures { get; private set; }
76 +
77 + public string OutputFile { get; private set; }
78 +
79 + public bool OutputXml { get; private set; }
80 +
81 + public List<BindPath> BindPaths { get; private set; }
82 +
83 + public List<string> Extensions { get; private set; }
84 +
85 + public List<string> Files { get; private set; }
86 +
87 + public List<string> LocalizationFiles { get; private set; }
88 +
89 + public bool Tidy { get; private set; }
90 +
91 + public string UnreferencedSymbolsFile { get; private set; }
92 +
93 + public IDictionary<string, string> Variables { get; private set; }
94 +
95 + /// <summary>
96 + /// Parse the commandline arguments.
97 + /// </summary>
98 + /// <param name="args">Commandline arguments.</param>
99 + public string[] Parse(string[] args)
100 + {
101 + List<string> unprocessed = new List<string>();
102 +
103 + for (int i = 0; i < args.Length; ++i)
104 + {
105 + string arg = args[i];
106 + if (String.IsNullOrEmpty(arg)) // skip blank arguments
107 + {
108 + continue;
109 + }
110 +
111 + if (1 == arg.Length) // treat '-' and '@' as filenames when by themselves.
112 + {
113 + unprocessed.Add(arg);
114 + }
115 + else if ('-' == arg[0] || '/' == arg[0])
116 + {
117 + string parameter = arg.Substring(1);
118 + if (parameter.Equals("b", StringComparison.Ordinal))
119 + {
120 + if (!CommandLineHelper.IsValidArg(args, ++i))
121 + {
122 + break;
123 + }
124 +
125 + var bindPath = BindPath.Parse(args[i]);
126 +
127 + this.BindPaths.Add(bindPath);
128 + }
129 + else if (parameter.StartsWith("cultures:", StringComparison.Ordinal))
130 + {
131 + string culturesString = arg.Substring(10).ToLower(CultureInfo.InvariantCulture);
132 +
133 + // When null is used treat it as if cultures wasn't specified.
134 + // This is needed for batching over the light task when using MSBuild which doesn't
135 + // support empty items
136 + if (culturesString.Equals("null", StringComparison.OrdinalIgnoreCase))
137 + {
138 + this.Cultures = null;
139 + }
140 + else
141 + {
142 + this.Cultures = culturesString.Split(';', ',');
143 +
144 + for (int c = 0; c < this.Cultures.Length; ++c)
145 + {
146 + // Neutral is different from null. For neutral we still want to do WXL filtering.
147 + // Set the culture to the empty string = identifier for the invariant culture
148 + if (this.Cultures[c].Equals("neutral", StringComparison.OrdinalIgnoreCase))
149 + {
150 + this.Cultures[c] = String.Empty;
151 + }
152 + }
153 + }
154 + }
155 + else if (parameter.StartsWith("dcl:", StringComparison.Ordinal))
156 + {
157 + string defaultCompressionLevel = arg.Substring(5);
158 +
159 + if (String.IsNullOrEmpty(defaultCompressionLevel))
160 + {
161 + break;
162 + }
163 + else if (Enum.TryParse(defaultCompressionLevel, true, out CompressionLevel compressionLevel))
164 + {
165 + this.DefaultCompressionLevel = compressionLevel;
166 + }
167 + }
168 + else if (parameter.StartsWith("d", StringComparison.Ordinal))
169 + {
170 + parameter = arg.Substring(2);
171 + string[] value = parameter.Split("=".ToCharArray(), 2);
172 +
173 + string preexisting;
174 + if (1 == value.Length)
175 + {
176 + this.Messaging.Write(ErrorMessages.ExpectedWixVariableValue(value[0]));
177 + }
178 + else if (this.Variables.TryGetValue(value[0], out preexisting))
179 + {
180 + this.Messaging.Write(ErrorMessages.WixVariableCollision(null, value[0]));
181 + }
182 + else
183 + {
184 + this.Variables.Add(value[0], value[1]);
185 + }
186 + }
187 + else if (parameter.Equals("ext", StringComparison.Ordinal))
188 + {
189 + if (!CommandLineHelper.IsValidArg(args, ++i))
190 + {
191 + this.Messaging.Write(ErrorMessages.TypeSpecificationForExtensionRequired("-ext"));
192 + break;
193 + }
194 +
195 + this.Extensions.Add(args[i]);
196 + }
197 + else if (parameter.Equals("loc", StringComparison.Ordinal))
198 + {
199 + string locFile = CommandLineHelper.GetFile(parameter, this.Messaging, args, ++i);
200 + if (String.IsNullOrEmpty(locFile))
201 + {
202 + break;
203 + }
204 +
205 + this.LocalizationFiles.Add(locFile);
206 + }
207 + else if (parameter.Equals("nologo", StringComparison.Ordinal))
208 + {
209 + this.ShowLogo = false;
210 + }
211 + else if (parameter.Equals("notidy", StringComparison.Ordinal))
212 + {
213 + this.Tidy = false;
214 + }
215 + else if ("o" == parameter || "out" == parameter)
216 + {
217 + this.OutputFile = CommandLineHelper.GetFile(parameter, this.Messaging, args, ++i);
218 + if (String.IsNullOrEmpty(this.OutputFile))
219 + {
220 + break;
221 + }
222 + }
223 + else if (parameter.Equals("pedantic", StringComparison.Ordinal))
224 + {
225 + this.ShowPedanticMessages = true;
226 + }
227 + else if (parameter.Equals("sloc", StringComparison.Ordinal))
228 + {
229 + this.SuppressLocalization = true;
230 + }
231 + else if (parameter.Equals("usf", StringComparison.Ordinal))
232 + {
233 + this.UnreferencedSymbolsFile = CommandLineHelper.GetFile(parameter, this.Messaging, args, ++i);
234 +
235 + if (String.IsNullOrEmpty(this.UnreferencedSymbolsFile))
236 + {
237 + break;
238 + }
239 + }
240 + else if (parameter.Equals("xo", StringComparison.Ordinal))
241 + {
242 + this.OutputXml = true;
243 + }
244 + else if (parameter.Equals("cc", StringComparison.Ordinal))
245 + {
246 + this.CabCachePath = CommandLineHelper.GetDirectory(parameter, this.Messaging, args, ++i);
247 +
248 + if (String.IsNullOrEmpty(this.CabCachePath))
249 + {
250 + break;
251 + }
252 + }
253 + else if (parameter.Equals("ct", StringComparison.Ordinal))
254 + {
255 + if (!CommandLineHelper.IsValidArg(args, ++i))
256 + {
257 + this.Messaging.Write(ErrorMessages.IllegalCabbingThreadCount(String.Empty));
258 + break;
259 + }
260 +
261 + int ct = 0;
262 + if (!Int32.TryParse(args[i], out ct) || 0 >= ct)
263 + {
264 + this.Messaging.Write(ErrorMessages.IllegalCabbingThreadCount(args[i]));
265 + break;
266 + }
267 +
268 + this.CabbingThreadCount = ct;
269 + this.Messaging.Write(VerboseMessages.SetCabbingThreadCount(this.CabbingThreadCount.ToString()));
270 + }
271 + else if (parameter.Equals("cub", StringComparison.Ordinal))
272 + {
273 + string cubeFile = CommandLineHelper.GetFile(parameter, this.Messaging, args, ++i);
274 +
275 + if (String.IsNullOrEmpty(cubeFile))
276 + {
277 + break;
278 + }
279 +
280 + this.CubeFiles.Add(cubeFile);
281 + }
282 + else if (parameter.StartsWith("ice:", StringComparison.Ordinal))
283 + {
284 + this.Ices.Add(parameter.Substring(4));
285 + }
286 + else if (parameter.Equals("intermediatefolder", StringComparison.OrdinalIgnoreCase))
287 + {
288 + this.IntermediateFolder = CommandLineHelper.GetDirectory(parameter, this.Messaging, args, ++i);
289 +
290 + if (String.IsNullOrEmpty(this.IntermediateFolder))
291 + {
292 + break;
293 + }
294 + }
295 + else if (parameter.Equals("contentsfile", StringComparison.Ordinal))
296 + {
297 + this.ContentsFile = CommandLineHelper.GetFile(parameter, this.Messaging, args, ++i);
298 +
299 + if (String.IsNullOrEmpty(this.ContentsFile))
300 + {
301 + break;
302 + }
303 + }
304 + else if (parameter.Equals("outputsfile", StringComparison.Ordinal))
305 + {
306 + this.OutputsFile = CommandLineHelper.GetFile(parameter, this.Messaging, args, ++i);
307 +
308 + if (String.IsNullOrEmpty(this.OutputsFile))
309 + {
310 + break;
311 + }
312 + }
313 + else if (parameter.Equals("builtoutputsfile", StringComparison.Ordinal))
314 + {
315 + this.BuiltOutputsFile = CommandLineHelper.GetFile(parameter, this.Messaging, args, ++i);
316 +
317 + if (String.IsNullOrEmpty(this.BuiltOutputsFile))
318 + {
319 + break;
320 + }
321 + }
322 + else if (parameter.Equals("wixprojectfile", StringComparison.Ordinal))
323 + {
324 + this.WixprojectFile = CommandLineHelper.GetFile(parameter, this.Messaging, args, ++i);
325 +
326 + if (String.IsNullOrEmpty(this.WixprojectFile))
327 + {
328 + break;
329 + }
330 + }
331 + else if (parameter.Equals("pdbout", StringComparison.Ordinal))
332 + {
333 + this.PdbFile = CommandLineHelper.GetFile(parameter, this.Messaging, args, ++i);
334 +
335 + if (String.IsNullOrEmpty(this.PdbFile))
336 + {
337 + break;
338 + }
339 + }
340 + else if (parameter.StartsWith("sice:", StringComparison.Ordinal))
341 + {
342 + this.SuppressIces.Add(parameter.Substring(5));
343 + }
344 + else if (parameter.Equals("sl", StringComparison.Ordinal))
345 + {
346 + this.SuppressLayout = true;
347 + }
348 + else if (parameter.Equals("spdb", StringComparison.Ordinal))
349 + {
350 + this.SuppressWixPdb = true;
351 + }
352 + else if (parameter.Equals("sacl", StringComparison.Ordinal))
353 + {
354 + this.SuppressAclReset = true;
355 + }
356 + else if (parameter.Equals("sval", StringComparison.Ordinal))
357 + {
358 + this.SuppressValidation = true;
359 + }
360 + else if ("sv" == parameter)
361 + {
362 + this.SuppressVersionCheck = true;
363 + }
364 + else if (parameter.StartsWith("sw", StringComparison.Ordinal))
365 + {
366 + string paramArg = parameter.Substring(2);
367 + if (0 == paramArg.Length)
368 + {
369 + this.Messaging.SuppressAllWarnings = true;
370 + }
371 + else
372 + {
373 + int suppressWarning = 0;
374 + if (!Int32.TryParse(paramArg, out suppressWarning) || 0 >= suppressWarning)
375 + {
376 + this.Messaging.Write(ErrorMessages.IllegalSuppressWarningId(paramArg));
377 + }
378 + else
379 + {
380 + this.Messaging.SuppressWarningMessage(suppressWarning);
381 + }
382 + }
383 + }
384 + else if (parameter.StartsWith("wx", StringComparison.Ordinal))
385 + {
386 + string paramArg = parameter.Substring(2);
387 + if (0 == paramArg.Length)
388 + {
389 + this.Messaging.WarningsAsError = true;
390 + }
391 + else
392 + {
393 + int elevateWarning = 0;
394 + if (!Int32.TryParse(paramArg, out elevateWarning) || 0 >= elevateWarning)
395 + {
396 + this.Messaging.Write(ErrorMessages.IllegalWarningIdAsError(paramArg));
397 + }
398 + else
399 + {
400 + this.Messaging.ElevateWarningMessage(elevateWarning);
401 + }
402 + }
403 + }
404 + else if ("v" == parameter)
405 + {
406 + this.Messaging.ShowVerboseMessages = true;
407 + }
408 + else if ("?" == parameter || "help" == parameter)
409 + {
410 + this.ShowHelp = true;
411 + break;
412 + }
413 + else
414 + {
415 + unprocessed.Add(arg);
416 + }
417 + }
418 + else if ('@' == arg[0])
419 + {
420 + string[] parsedArgs = CommandLineResponseFile.Parse(arg.Substring(1));
421 + string[] unparsedArgs = this.Parse(parsedArgs);
422 + unprocessed.AddRange(unparsedArgs);
423 + }
424 + else
425 + {
426 + unprocessed.Add(arg);
427 + }
428 + }
429 +
430 + return unprocessed.ToArray();
431 + }
432 +
433 + public string[] ParsePostExtensions(string[] remaining)
434 + {
435 + List<string> unprocessed = new List<string>();
436 +
437 + for (int i = 0; i < remaining.Length; ++i)
438 + {
439 + string arg = remaining[i];
440 + if (String.IsNullOrEmpty(arg)) // skip blank arguments
441 + {
442 + continue;
443 + }
444 +
445 + if (1 < arg.Length && ('-' == arg[0] || '/' == arg[0]))
446 + {
447 + unprocessed.Add(arg);
448 + }
449 + else
450 + {
451 + this.Files.AddRange(CommandLineHelper.GetFiles(arg, "Source"));
452 + }
453 + }
454 +
455 + if (0 == this.Files.Count)
456 + {
457 + this.ShowHelp = true;
458 + }
459 + else if (String.IsNullOrEmpty(this.OutputFile))
460 + {
461 + if (1 < this.Files.Count)
462 + {
463 + this.Messaging.Write(ErrorMessages.MustSpecifyOutputWithMoreThanOneInput());
464 + }
465 +
466 + // After the linker tells us what the output type actually is, we'll change the ".wix" to the correct extension.
467 + this.OutputFile = Path.ChangeExtension(Path.GetFileName(this.Files[0]), ".wix");
468 +
469 + // Add the directories of the input files as unnamed bind paths.
470 + foreach (string file in this.Files)
471 + {
472 + BindPath bindPath = new BindPath(Path.GetDirectoryName(Path.GetFullPath(file)));
473 + this.BindPaths.Add(bindPath);
474 + }
475 + }
476 +
477 + if (!this.SuppressWixPdb && String.IsNullOrEmpty(this.PdbFile) && !String.IsNullOrEmpty(this.OutputFile))
478 + {
479 + this.PdbFile = Path.ChangeExtension(this.OutputFile, ".wixpdb");
480 + }
481 +
482 + return unprocessed.ToArray();
483 + }
484 + }
485 +}
src/light/LightStrings.Designer.cs new
+104
@@ -0,0 +1,104 @@
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.Tools {
4 + using System;
5 +
6 +
7 + /// <summary>
8 + /// A strongly-typed resource class, for looking up localized strings, etc.
9 + /// </summary>
10 + // This class was auto-generated by the StronglyTypedResourceBuilder
11 + // class via a tool like ResGen or Visual Studio.
12 + // To add or remove a member, edit your .ResX file then rerun ResGen
13 + // with the /str option, or rebuild your VS project.
14 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
15 + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
16 + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
17 + internal class LightStrings {
18 +
19 + private static global::System.Resources.ResourceManager resourceMan;
20 +
21 + private static global::System.Globalization.CultureInfo resourceCulture;
22 +
23 + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
24 + internal LightStrings() {
25 + }
26 +
27 + /// <summary>
28 + /// Returns the cached ResourceManager instance used by this class.
29 + /// </summary>
30 + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
31 + internal static global::System.Resources.ResourceManager ResourceManager {
32 + get {
33 + if (object.ReferenceEquals(resourceMan, null)) {
34 + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WixToolset.Tools.LightStrings", typeof(LightStrings).Assembly);
35 + resourceMan = temp;
36 + }
37 + return resourceMan;
38 + }
39 + }
40 +
41 + /// <summary>
42 + /// Overrides the current thread's CurrentUICulture property for all
43 + /// resource lookups using this strongly typed resource class.
44 + /// </summary>
45 + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
46 + internal static global::System.Globalization.CultureInfo Culture {
47 + get {
48 + return resourceCulture;
49 + }
50 + set {
51 + resourceCulture = value;
52 + }
53 + }
54 +
55 + /// <summary>
56 + /// Looks up a localized string similar to -b &lt;path&gt; specify a binder path to locate all files
57 + /// (default: current directory)
58 + /// prefix the path with &apos;name=&apos; where &apos;name&apos; is the name of your
59 + /// named bindpath.
60 + /// -cc &lt;path&gt; path to cache built cabinets (will not be deleted after linking)
61 + /// -ct &lt;N&gt; number of threads to use when creating cabinets
62 + /// (default: %NUMBER_OF_PROCESSORS%)
63 + /// -cub &lt;file.cub&gt; additional .cub file containing ICEs to run
64 + /// -dcl:level set default cabinet compression l [rest of string was truncated]&quot;;.
65 + /// </summary>
66 + internal static string CommandLineArguments {
67 + get {
68 + return ResourceManager.GetString("CommandLineArguments", resourceCulture);
69 + }
70 + }
71 +
72 + /// <summary>
73 + /// Looks up a localized string similar to The -bf (bind files) option is only applicable with the -xo option..
74 + /// </summary>
75 + internal static string EXP_BindFileOptionNotApplicable {
76 + get {
77 + return ResourceManager.GetString("EXP_BindFileOptionNotApplicable", resourceCulture);
78 + }
79 + }
80 +
81 + /// <summary>
82 + /// Looks up a localized string similar to Cannot link object files (.wixobj) files with an output file (.wixout).
83 + /// </summary>
84 + internal static string EXP_CannotLinkObjFilesWithOutpuFile {
85 + get {
86 + return ResourceManager.GetString("EXP_CannotLinkObjFilesWithOutpuFile", resourceCulture);
87 + }
88 + }
89 +
90 + /// <summary>
91 + /// Looks up a localized string similar to usage: light.exe [-?] [-b bindPath] [-nologo] [-out outputFile] objectFile [objectFile ...] [@responseFile]
92 + ///
93 + ///{0}
94 + ///
95 + ///Environment variables:
96 + /// WIX_TEMP overrides the temporary directory used for cab creation, msm exploding, ....
97 + /// </summary>
98 + internal static string HelpMessage {
99 + get {
100 + return ResourceManager.GetString("HelpMessage", resourceCulture);
101 + }
102 + }
103 + }
104 +}
src/light/LightStrings.resx new
+174
@@ -0,0 +1,174 @@
1 +<?xml version="1.0" encoding="utf-8"?>
2 +<root>
3 + <!--
4 + Microsoft ResX Schema
5 +
6 + Version 2.0
7 +
8 + The primary goals of this format is to allow a simple XML format
9 + that is mostly human readable. The generation and parsing of the
10 + various data types are done through the TypeConverter classes
11 + associated with the data types.
12 +
13 + Example:
14 +
15 + ... ado.net/XML headers & schema ...
16 + <resheader name="resmimetype">text/microsoft-resx</resheader>
17 + <resheader name="version">2.0</resheader>
18 + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
19 + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
20 + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
21 + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
22 + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
23 + <value>[base64 mime encoded serialized .NET Framework object]</value>
24 + </data>
25 + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
26 + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
27 + <comment>This is a comment</comment>
28 + </data>
29 +
30 + There are any number of "resheader" rows that contain simple
31 + name/value pairs.
32 +
33 + Each data row contains a name, and value. The row also contains a
34 + type or mimetype. Type corresponds to a .NET class that support
35 + text/value conversion through the TypeConverter architecture.
36 + Classes that don't support this are serialized and stored with the
37 + mimetype set.
38 +
39 + The mimetype is used for serialized objects, and tells the
40 + ResXResourceReader how to depersist the object. This is currently not
41 + extensible. For a given mimetype the value must be set accordingly:
42 +
43 + Note - application/x-microsoft.net.object.binary.base64 is the format
44 + that the ResXResourceWriter will generate, however the reader can
45 + read any of the formats listed below.
46 +
47 + mimetype: application/x-microsoft.net.object.binary.base64
48 + value : The object must be serialized with
49 + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
50 + : and then encoded with base64 encoding.
51 +
52 + mimetype: application/x-microsoft.net.object.soap.base64
53 + value : The object must be serialized with
54 + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
55 + : and then encoded with base64 encoding.
56 +
57 + mimetype: application/x-microsoft.net.object.bytearray.base64
58 + value : The object must be serialized into a byte array
59 + : using a System.ComponentModel.TypeConverter
60 + : and then encoded with base64 encoding.
61 + -->
62 + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
63 + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
64 + <xsd:element name="root" msdata:IsDataSet="true">
65 + <xsd:complexType>
66 + <xsd:choice maxOccurs="unbounded">
67 + <xsd:element name="metadata">
68 + <xsd:complexType>
69 + <xsd:sequence>
70 + <xsd:element name="value" type="xsd:string" minOccurs="0" />
71 + </xsd:sequence>
72 + <xsd:attribute name="name" use="required" type="xsd:string" />
73 + <xsd:attribute name="type" type="xsd:string" />
74 + <xsd:attribute name="mimetype" type="xsd:string" />
75 + <xsd:attribute ref="xml:space" />
76 + </xsd:complexType>
77 + </xsd:element>
78 + <xsd:element name="assembly">
79 + <xsd:complexType>
80 + <xsd:attribute name="alias" type="xsd:string" />
81 + <xsd:attribute name="name" type="xsd:string" />
82 + </xsd:complexType>
83 + </xsd:element>
84 + <xsd:element name="data">
85 + <xsd:complexType>
86 + <xsd:sequence>
87 + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
88 + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
89 + </xsd:sequence>
90 + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
91 + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
92 + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
93 + <xsd:attribute ref="xml:space" />
94 + </xsd:complexType>
95 + </xsd:element>
96 + <xsd:element name="resheader">
97 + <xsd:complexType>
98 + <xsd:sequence>
99 + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
100 + </xsd:sequence>
101 + <xsd:attribute name="name" type="xsd:string" use="required" />
102 + </xsd:complexType>
103 + </xsd:element>
104 + </xsd:choice>
105 + </xsd:complexType>
106 + </xsd:element>
107 + </xsd:schema>
108 + <resheader name="resmimetype">
109 + <value>text/microsoft-resx</value>
110 + </resheader>
111 + <resheader name="version">
112 + <value>2.0</value>
113 + </resheader>
114 + <resheader name="reader">
115 + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
116 + </resheader>
117 + <resheader name="writer">
118 + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
119 + </resheader>
120 + <data name="CommandLineArguments" xml:space="preserve">
121 + <value> -b &lt;path&gt; specify a binder path to locate all files
122 + (default: current directory)
123 + prefix the path with 'name=' where 'name' is the name of your
124 + named bindpath.
125 + -cc &lt;path&gt; path to cache built cabinets (will not be deleted after linking)
126 + -ct &lt;N&gt; number of threads to use when creating cabinets
127 + (default: %NUMBER_OF_PROCESSORS%)
128 + -cub &lt;file.cub&gt; additional .cub file containing ICEs to run
129 + -dcl:level set default cabinet compression level
130 + (low, medium, high, none, mszip; mszip default)
131 + -eav exact assembly versions (breaks .NET 1.1 RTM compatibility)
132 + -ice:&lt;ICE&gt; run a specific internal consistency evaluator (ICE)
133 + -pdbout &lt;output.wixpdb&gt; save the WixPdb to a specific file
134 + (default: same name as output with wixpdb extension)
135 + -reusecab reuse cabinets from cabinet cache
136 + -sacl suppress resetting ACLs
137 + (useful when laying out image to a network share)
138 + -sice:&lt;ICE&gt; suppress an internal consistency evaluator (ICE)
139 + -sl suppress layout
140 + -spdb suppress outputting the WixPdb
141 + -sval suppress MSI/MSM validation
142 + -cultures:&lt;cultures&gt; semicolon or comma delimited list of localized
143 + string cultures to load from .wxl files and libraries.
144 + Precedence of cultures is from left to right.
145 + -d&lt;name&gt;[=&lt;value&gt;] define a wix variable, with or without a value.
146 + -ext &lt;extension&gt; extension assembly or "class, assembly"
147 + -loc &lt;loc.wxl&gt; read localization strings from .wxl file
148 + -nologo skip printing light logo information
149 + -notidy do not delete temporary files (useful for debugging)
150 + -o[ut] specify output file (default: write to current directory)
151 + -pedantic show pedantic messages
152 + -sloc suppress localization
153 + -sw[N] suppress all warnings or a specific message ID
154 + (example: -sw1009 -sw1103)
155 + -usf &lt;output.xml&gt; unreferenced symbols file
156 + -v verbose output
157 + -wx[N] treat all warnings or a specific message ID as an error
158 + (example: -wx1009 -wx1103)
159 + -xo output wixout format instead of MSI format
160 + -? | -help this help information</value>
161 + </data>
162 + <data name="EXP_CannotLinkObjFilesWithOutpuFile" xml:space="preserve">
163 + <value>Cannot link object files (.wixobj) files with an output file (.wixout)</value>
164 + </data>
165 + <data name="HelpMessage" xml:space="preserve">
166 + <value> usage: light.exe [-?] [-b bindPath] [-nologo] [-out outputFile] objectFile [objectFile ...] [@responseFile]
167 +
168 +{0}
169 +
170 +Environment variables:
171 + WIX_TEMP overrides the temporary directory used for cab creation, msm exploding, ...</value>
172 + <comment>{0} is replaced by a list of light's arguments.</comment>
173 + </data>
174 +</root>
src/light/app.config new
+9
@@ -0,0 +1,9 @@
1 +<?xml version="1.0" encoding="utf-8" ?>
2 +<!-- 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. -->
3 +
4 +
5 +<configuration>
6 + <runtime>
7 + <loadFromRemoteSources enabled="true"/>
8 + </runtime>
9 +</configuration>
src/light/light.cs new
+595
@@ -0,0 +1,595 @@
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.Tools
4 +{
5 + using System;
6 + using System.Collections.Generic;
7 + using System.Globalization;
8 + using System.IO;
9 + using System.Linq;
10 + using System.Runtime.InteropServices;
11 + using System.Text;
12 + using System.Threading;
13 + using WixToolset.Core;
14 + using WixToolset.Data;
15 + using WixToolset.Data.Bind;
16 + using WixToolset.Extensibility;
17 + using WixToolset.Extensibility.Services;
18 +
19 + /// <summary>
20 + /// The main entry point for light.
21 + /// </summary>
22 + public sealed class Light
23 + {
24 + LightCommandLine commandLine;
25 + private IEnumerable<IExtensionData> extensionData;
26 + //private IEnumerable<IBinderExtension> binderExtensions;
27 + //private IEnumerable<IBinderFileManager> fileManagers;
28 +
29 + /// <summary>
30 + /// The main entry point for light.
31 + /// </summary>
32 + /// <param name="args">Commandline arguments for the application.</param>
33 + /// <returns>Returns the application error code.</returns>
34 + [MTAThread]
35 + public static int Main(string[] args)
36 + {
37 + var serviceProvider = new WixToolsetServiceProvider();
38 +
39 + var listener = new ConsoleMessageListener("WIX", "light.exe");
40 +
41 + Light light = new Light();
42 + return light.Run(serviceProvider, listener, args);
43 + }
44 +
45 + /// <summary>
46 + /// Main running method for the application.
47 + /// </summary>
48 + /// <param name="args">Commandline arguments to the application.</param>
49 + /// <returns>Returns the application error code.</returns>
50 + public int Run(IServiceProvider serviceProvider, IMessageListener listener, string[] args)
51 + {
52 + var messaging = serviceProvider.GetService<IMessaging>();
53 + messaging.SetListener(listener);
54 +
55 + try
56 + {
57 + var unparsed = this.ParseCommandLineAndLoadExtensions(serviceProvider, messaging, args);
58 +
59 + if (!messaging.EncounteredError)
60 + {
61 + if (this.commandLine.ShowLogo)
62 + {
63 + AppCommon.DisplayToolHeader();
64 + }
65 +
66 + if (this.commandLine.ShowHelp)
67 + {
68 + PrintHelp();
69 + AppCommon.DisplayToolFooter();
70 + }
71 + else
72 + {
73 + foreach (string arg in unparsed)
74 + {
75 + messaging.Write(WarningMessages.UnsupportedCommandLineArgument(arg));
76 + }
77 +
78 + this.Bind(serviceProvider, messaging);
79 + }
80 + }
81 + }
82 + catch (WixException we)
83 + {
84 + messaging.Write(we.Error);
85 + }
86 + catch (Exception e)
87 + {
88 + messaging.Write(ErrorMessages.UnexpectedException(e.Message, e.GetType().ToString(), e.StackTrace));
89 + if (e is NullReferenceException || e is SEHException)
90 + {
91 + throw;
92 + }
93 + }
94 +
95 + return messaging.LastErrorNumber;
96 + }
97 +
98 + /// <summary>
99 + /// Parse command line and load all the extensions.
100 + /// </summary>
101 + /// <param name="args">Command line arguments to be parsed.</param>
102 + private IEnumerable<string> ParseCommandLineAndLoadExtensions(IServiceProvider serviceProvider, IMessaging messaging, string[] args)
103 + {
104 + this.commandLine = new LightCommandLine(messaging);
105 +
106 + string[] unprocessed = this.commandLine.Parse(args);
107 + if (messaging.EncounteredError)
108 + {
109 + return unprocessed;
110 + }
111 +
112 + // Load extensions.
113 + var extensionManager = CreateExtensionManagerWithStandardBackends(serviceProvider);
114 + foreach (string extension in this.commandLine.Extensions)
115 + {
116 + extensionManager.Load(extension);
117 + }
118 +
119 + // Extension data command line processing.
120 + var context = serviceProvider.GetService<ICommandLineContext>();
121 + context.Arguments = null;
122 + context.ExtensionManager = extensionManager;
123 + context.Messaging = messaging;
124 + context.ParsedArguments = args;
125 +
126 + var commandLineExtensions = extensionManager.Create<IExtensionCommandLine>();
127 + foreach (var extension in commandLineExtensions)
128 + {
129 + extension.PreParse(context);
130 + }
131 +
132 + // Process unproccessed arguments.
133 + List<string> actuallyUnprocessed = new List<string>();
134 + foreach (var arg in unprocessed)
135 + {
136 + if (!this.TryParseCommandLineArgumentWithExtension(arg, commandLineExtensions))
137 + {
138 + actuallyUnprocessed.Add(arg);
139 + }
140 + }
141 +
142 + return this.commandLine.ParsePostExtensions(actuallyUnprocessed.ToArray());
143 + }
144 +
145 + private void Bind(IServiceProvider serviceProvider, IMessaging messaging)
146 + {
147 + var output = this.LoadWixout(messaging);
148 +
149 + if (messaging.EncounteredError)
150 + {
151 + return;
152 + }
153 +
154 + var intermediateFolder = this.commandLine.IntermediateFolder;
155 + if (String.IsNullOrEmpty(intermediateFolder))
156 + {
157 + intermediateFolder = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
158 + }
159 +
160 + var localizations = this.LoadLocalizationFiles(messaging, this.commandLine.LocalizationFiles);
161 +
162 + if (messaging.EncounteredError)
163 + {
164 + return;
165 + }
166 +
167 + ResolveResult resolveResult;
168 + {
169 + var resolver = new Resolver(serviceProvider);
170 + resolver.BindPaths = this.commandLine.BindPaths;
171 + resolver.IntermediateFolder = intermediateFolder;
172 + resolver.IntermediateRepresentation = output;
173 + resolver.Localizations = localizations;
174 +
175 + resolveResult = resolver.Execute();
176 + }
177 +
178 + if (messaging.EncounteredError)
179 + {
180 + return;
181 + }
182 +
183 + BindResult bindResult;
184 + {
185 + var binder = new Binder(serviceProvider);
186 + binder.CabbingThreadCount = this.commandLine.CabbingThreadCount;
187 + binder.CabCachePath = this.commandLine.CabCachePath;
188 + binder.Codepage = resolveResult.Codepage;
189 + binder.DefaultCompressionLevel = this.commandLine.DefaultCompressionLevel;
190 + binder.DelayedFields = resolveResult.DelayedFields;
191 + binder.ExpectedEmbeddedFiles = resolveResult.ExpectedEmbeddedFiles;
192 + binder.Ices = this.commandLine.Ices;
193 + binder.IntermediateFolder = intermediateFolder;
194 + binder.IntermediateRepresentation = resolveResult.IntermediateRepresentation;
195 + binder.OutputPath = this.commandLine.OutputFile;
196 + binder.OutputPdbPath = Path.ChangeExtension(this.commandLine.OutputFile, ".wixpdb");
197 + binder.SuppressIces = this.commandLine.SuppressIces;
198 + binder.SuppressValidation = this.commandLine.SuppressValidation;
199 +
200 + bindResult = binder.Execute();
201 + }
202 +
203 + if (messaging.EncounteredError)
204 + {
205 + return;
206 + }
207 +
208 + {
209 + var layout = new Layout(serviceProvider);
210 + layout.FileTransfers = bindResult.FileTransfers;
211 + layout.ContentFilePaths = bindResult.ContentFilePaths;
212 + layout.ContentsFile = this.commandLine.ContentsFile;
213 + layout.OutputsFile = this.commandLine.OutputsFile;
214 + layout.BuiltOutputsFile = this.commandLine.BuiltOutputsFile;
215 + layout.SuppressAclReset = this.commandLine.SuppressAclReset;
216 +
217 + layout.Execute();
218 + }
219 + }
220 +
221 + private void Run(IMessaging messaging)
222 + {
223 +#if false
224 + // Initialize the variable resolver from the command line.
225 + WixVariableResolver wixVariableResolver = new WixVariableResolver();
226 + foreach (var wixVar in this.commandLine.Variables)
227 + {
228 + wixVariableResolver.AddVariable(wixVar.Key, wixVar.Value);
229 + }
230 +
231 + // Initialize the linker from the command line.
232 + Linker linker = new Linker();
233 + linker.UnreferencedSymbolsFile = this.commandLine.UnreferencedSymbolsFile;
234 + linker.ShowPedanticMessages = this.commandLine.ShowPedanticMessages;
235 + linker.WixVariableResolver = wixVariableResolver;
236 +
237 + foreach (IExtensionData data in this.extensionData)
238 + {
239 + linker.AddExtensionData(data);
240 + }
241 +
242 + // Initialize the binder from the command line.
243 + WixToolset.Binder binder = new WixToolset.Binder();
244 + binder.CabCachePath = this.commandLine.CabCachePath;
245 + binder.ContentsFile = this.commandLine.ContentsFile;
246 + binder.BuiltOutputsFile = this.commandLine.BuiltOutputsFile;
247 + binder.OutputsFile = this.commandLine.OutputsFile;
248 + binder.WixprojectFile = this.commandLine.WixprojectFile;
249 + binder.BindPaths.AddRange(this.commandLine.BindPaths);
250 + binder.CabbingThreadCount = this.commandLine.CabbingThreadCount;
251 + if (this.commandLine.DefaultCompressionLevel.HasValue)
252 + {
253 + binder.DefaultCompressionLevel = this.commandLine.DefaultCompressionLevel.Value;
254 + }
255 + binder.Ices.AddRange(this.commandLine.Ices);
256 + binder.SuppressIces.AddRange(this.commandLine.SuppressIces);
257 + binder.SuppressAclReset = this.commandLine.SuppressAclReset;
258 + binder.SuppressLayout = this.commandLine.SuppressLayout;
259 + binder.SuppressValidation = this.commandLine.SuppressValidation;
260 + binder.PdbFile = this.commandLine.SuppressWixPdb ? null : this.commandLine.PdbFile;
261 + binder.TempFilesLocation = AppCommon.GetTempLocation();
262 + binder.WixVariableResolver = wixVariableResolver;
263 +
264 + foreach (IBinderExtension extension in this.binderExtensions)
265 + {
266 + binder.AddExtension(extension);
267 + }
268 +
269 + foreach (IBinderFileManager fileManager in this.fileManagers)
270 + {
271 + binder.AddExtension(fileManager);
272 + }
273 +
274 + // Initialize the localizer.
275 + Localizer localizer = this.InitializeLocalization(linker.TableDefinitions);
276 + if (messaging.EncounteredError)
277 + {
278 + return;
279 + }
280 +
281 + wixVariableResolver.Localizer = localizer;
282 + linker.Localizer = localizer;
283 + binder.Localizer = localizer;
284 +
285 + // Loop through all the believed object files.
286 + List<Section> sections = new List<Section>();
287 + Output output = null;
288 + foreach (string inputFile in this.commandLine.Files)
289 + {
290 + string inputFileFullPath = Path.GetFullPath(inputFile);
291 + FileFormat format = FileStructure.GuessFileFormatFromExtension(Path.GetExtension(inputFileFullPath));
292 + bool retry;
293 + do
294 + {
295 + retry = false;
296 +
297 + try
298 + {
299 + switch (format)
300 + {
301 + case FileFormat.Wixobj:
302 + Intermediate intermediate = Intermediate.Load(inputFileFullPath, linker.TableDefinitions, this.commandLine.SuppressVersionCheck);
303 + sections.AddRange(intermediate.Sections);
304 + break;
305 +
306 + case FileFormat.Wixlib:
307 + Library library = Library.Load(inputFileFullPath, linker.TableDefinitions, this.commandLine.SuppressVersionCheck);
308 + AddLibraryLocalizationsToLocalizer(library, this.commandLine.Cultures, localizer);
309 + sections.AddRange(library.Sections);
310 + break;
311 +
312 + default:
313 + output = Output.Load(inputFileFullPath, this.commandLine.SuppressVersionCheck);
314 + break;
315 + }
316 + }
317 + catch (WixUnexpectedFileFormatException e)
318 + {
319 + format = e.FileFormat;
320 + retry = (FileFormat.Wixobj == format || FileFormat.Wixlib == format || FileFormat.Wixout == format); // .wixobj, .wixout and .wixout are supported by light.
321 + if (!retry)
322 + {
323 + messaging.OnMessage(e.Error);
324 + }
325 + }
326 + } while (retry);
327 + }
328 +
329 + // Stop processing if any errors were found loading object files.
330 + if (messaging.EncounteredError)
331 + {
332 + return;
333 + }
334 +
335 + // and now for the fun part
336 + if (null == output)
337 + {
338 + OutputType expectedOutputType = OutputType.Unknown;
339 + if (!String.IsNullOrEmpty(this.commandLine.OutputFile))
340 + {
341 + expectedOutputType = Output.GetOutputType(Path.GetExtension(this.commandLine.OutputFile));
342 + }
343 +
344 + output = linker.Link(sections, expectedOutputType);
345 +
346 + // If an error occurred during linking, stop processing.
347 + if (null == output)
348 + {
349 + return;
350 + }
351 + }
352 + else if (0 != sections.Count)
353 + {
354 + throw new InvalidOperationException(LightStrings.EXP_CannotLinkObjFilesWithOutpuFile);
355 + }
356 +
357 + bool tidy = true; // clean up after ourselves by default.
358 + try
359 + {
360 + // only output the xml if its a patch build or user specfied to only output wixout
361 + string outputFile = this.commandLine.OutputFile;
362 + string outputExtension = Path.GetExtension(outputFile);
363 + if (this.commandLine.OutputXml || OutputType.Patch == output.Type)
364 + {
365 + if (String.IsNullOrEmpty(outputExtension) || outputExtension.Equals(".wix", StringComparison.Ordinal))
366 + {
367 + outputExtension = (OutputType.Patch == output.Type) ? ".wixmsp" : ".wixout";
368 + outputFile = Path.ChangeExtension(outputFile, outputExtension);
369 + }
370 +
371 + output.Save(outputFile);
372 + }
373 + else // finish creating the MSI/MSM
374 + {
375 + if (String.IsNullOrEmpty(outputExtension) || outputExtension.Equals(".wix", StringComparison.Ordinal))
376 + {
377 + outputExtension = Output.GetExtension(output.Type);
378 + outputFile = Path.ChangeExtension(outputFile, outputExtension);
379 + }
380 +
381 + binder.Bind(output, outputFile);
382 + }
383 + }
384 + catch (WixException we) // keep files around for debugging IDT issues.
385 + {
386 + if (we is WixInvalidIdtException)
387 + {
388 + tidy = false;
389 + }
390 +
391 + throw;
392 + }
393 + catch (Exception) // keep files around for debugging unexpected exceptions.
394 + {
395 + tidy = false;
396 + throw;
397 + }
398 + finally
399 + {
400 + if (null != binder)
401 + {
402 + binder.Cleanup(tidy);
403 + }
404 + }
405 +
406 + return;
407 +#endif
408 + }
409 +
410 +#if false
411 + private Localizer InitializeLocalization(TableDefinitionCollection tableDefinitions)
412 + {
413 + Localizer localizer = null;
414 +
415 + // Instantiate the localizer and load any localization files.
416 + if (!this.commandLine.SuppressLocalization || 0 < this.commandLine.LocalizationFiles.Count || null != this.commandLine.Cultures || !this.commandLine.OutputXml)
417 + {
418 + List<Localization> localizations = new List<Localization>();
419 +
420 + // Load each localization file.
421 + foreach (string localizationFile in this.commandLine.LocalizationFiles)
422 + {
423 + Localization localization = Localizer.ParseLocalizationFile(localizationFile, tableDefinitions);
424 + if (null != localization)
425 + {
426 + localizations.Add(localization);
427 + }
428 + }
429 +
430 + localizer = new Localizer();
431 + if (null != this.commandLine.Cultures)
432 + {
433 + // Alocalizations in order specified in cultures.
434 + foreach (string culture in this.commandLine.Cultures)
435 + {
436 + foreach (Localization localization in localizations)
437 + {
438 + if (culture.Equals(localization.Culture, StringComparison.OrdinalIgnoreCase))
439 + {
440 + localizer.AddLocalization(localization);
441 + }
442 + }
443 + }
444 + }
445 + else // no cultures specified, so try neutral culture and if none of those add all loc files.
446 + {
447 + bool neutralFound = false;
448 + foreach (Localization localization in localizations)
449 + {
450 + if (String.IsNullOrEmpty(localization.Culture))
451 + {
452 + // If a neutral wxl was provided use it.
453 + localizer.AddLocalization(localization);
454 + neutralFound = true;
455 + }
456 + }
457 +
458 + if (!neutralFound)
459 + {
460 + // No cultures were specified and no neutral wxl are available, include all of the loc files.
461 + foreach (Localization localization in localizations)
462 + {
463 + localizer.AddLocalization(localization);
464 + }
465 + }
466 + }
467 +
468 + // Load localizations provided by extensions with data.
469 + foreach (IExtensionData data in this.extensionData)
470 + {
471 + Library library = data.GetLibrary(tableDefinitions);
472 + if (null != library)
473 + {
474 + // Load the extension's default culture if it provides one and no cultures were specified.
475 + string[] extensionCultures = this.commandLine.Cultures;
476 + if (null == extensionCultures && null != data.DefaultCulture)
477 + {
478 + extensionCultures = new string[] { data.DefaultCulture };
479 + }
480 +
481 + AddLibraryLocalizationsToLocalizer(library, extensionCultures, localizer);
482 + }
483 + }
484 + }
485 +
486 + return localizer;
487 + }
488 +
489 + private void AddLibraryLocalizationsToLocalizer(Library library, string[] cultures, Localizer localizer)
490 + {
491 + foreach (Localization localization in library.GetLocalizations(cultures))
492 + {
493 + localizer.AddLocalization(localization);
494 + }
495 + }
496 +#endif
497 +
498 + private bool TryParseCommandLineArgumentWithExtension(string arg, IEnumerable<IExtensionCommandLine> extensions)
499 + {
500 + foreach (var extension in extensions)
501 + {
502 + // TODO: decide what to do with "IParseCommandLine" argument.
503 + if (extension.TryParseArgument(null, arg))
504 + {
505 + return true;
506 + }
507 + }
508 +
509 + return false;
510 + }
511 +
512 + private IEnumerable<Localization> LoadLocalizationFiles(IMessaging messaging, IEnumerable<string> locFiles)
513 + {
514 + foreach (var loc in locFiles)
515 + {
516 + var localization = Localizer.ParseLocalizationFile(messaging, loc);
517 +
518 + yield return localization;
519 + }
520 + }
521 +
522 + private Intermediate LoadWixout(IMessaging messaging)
523 + {
524 + var path = this.commandLine.Files.Single();
525 +
526 + return Intermediate.Load(path);
527 + }
528 +
529 + private static IExtensionManager CreateExtensionManagerWithStandardBackends(IServiceProvider serviceProvider)
530 + {
531 + var extensionManager = serviceProvider.GetService<IExtensionManager>();
532 +
533 + foreach (var type in new[] { typeof(WixToolset.Core.Burn.WixToolsetStandardBackend), typeof(WixToolset.Core.WindowsInstaller.WixToolsetStandardBackend) })
534 + {
535 + extensionManager.Add(type.Assembly);
536 + }
537 +
538 + return extensionManager;
539 + }
540 +
541 + private static void PrintHelp()
542 + {
543 + string lightArgs = LightStrings.CommandLineArguments;
544 +
545 + Console.WriteLine(String.Format(LightStrings.HelpMessage, lightArgs));
546 + }
547 +
548 + private class ConsoleMessageListener : IMessageListener
549 + {
550 + public ConsoleMessageListener(string shortName, string longName)
551 + {
552 + this.ShortAppName = shortName;
553 + this.LongAppName = longName;
554 +
555 + PrepareConsoleForLocalization();
556 + }
557 +
558 + public string LongAppName { get; }
559 +
560 + public string ShortAppName { get; }
561 +
562 + public void Write(Message message)
563 + {
564 + var filename = message.SourceLineNumbers?.FileName ?? this.LongAppName;
565 + var line = message.SourceLineNumbers?.LineNumber ?? -1;
566 + var type = message.Level.ToString().ToLowerInvariant();
567 + var output = message.Level >= MessageLevel.Warning ? Console.Out : Console.Error;
568 +
569 + if (line > 0)
570 + {
571 + filename = String.Concat(filename, "(", line, ")");
572 + }
573 +
574 + output.WriteLine("{0} : {1} {2}{3:0000}: {4}", filename, type, this.ShortAppName, message.Id, message.ToString());
575 + }
576 +
577 + public void Write(string message)
578 + {
579 + Console.Out.WriteLine(message);
580 + }
581 +
582 + private static void PrepareConsoleForLocalization()
583 + {
584 + Thread.CurrentThread.CurrentUICulture = CultureInfo.CurrentUICulture.GetConsoleFallbackUICulture();
585 +
586 + if (Console.OutputEncoding.CodePage != Encoding.UTF8.CodePage &&
587 + Console.OutputEncoding.CodePage != Thread.CurrentThread.CurrentUICulture.TextInfo.OEMCodePage &&
588 + Console.OutputEncoding.CodePage != Thread.CurrentThread.CurrentUICulture.TextInfo.ANSICodePage)
589 + {
590 + Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");
591 + }
592 + }
593 + }
594 + }
595 +}
src/light/light.csproj new
+21
@@ -0,0 +1,21 @@
1 +<?xml version="1.0" encoding="utf-8"?>
2 +<!-- 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. -->
3 +
4 +<Project Sdk="Microsoft.NET.Sdk">
5 + <PropertyGroup>
6 + <TargetFramework>net461</TargetFramework>
7 + <OutputType>Exe</OutputType>
8 + <Description>Linker</Description>
9 + <Title>WiX Toolset Linker</Title>
10 + </PropertyGroup>
11 +
12 + <ItemGroup>
13 + <ProjectReference Include="..\WixToolset.Core\WixToolset.Core.csproj" />
14 + <ProjectReference Include="..\WixToolset.Core.Burn\WixToolset.Core.Burn.csproj" />
15 + <ProjectReference Include="..\WixToolset.Core.WindowsInstaller\WixToolset.Core.WindowsInstaller.csproj" />
16 + </ItemGroup>
17 +
18 + <ItemGroup>
19 + <PackageReference Include="Nerdbank.GitVersioning" Version="2.0.41" PrivateAssets="all" />
20 + </ItemGroup>
21 +</Project>
src/test/WixToolsetTest.LightIntegration/LightFixture.cs new
+48
@@ -0,0 +1,48 @@
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 WixToolsetTest.LightIntegration
4 +{
5 + using System.IO;
6 + using System.Linq;
7 + using WixToolset.Core;
8 + using WixToolset.Tools;
9 + using WixToolsetTest.LightIntegration.Utility;
10 + using Xunit;
11 +
12 + public class LightFixture
13 + {
14 + [Fact]
15 + public void CanBuildFromWixout()
16 + {
17 + var folder = TestData.Get(@"TestData\Wixout");
18 +
19 + using (var fs = new DisposableFileSystem())
20 + {
21 + var baseFolder = fs.GetFolder();
22 + var intermediateFolder = Path.Combine(baseFolder, "obj");
23 +
24 + var program = new Light();
25 + var result = program.Run(new WixToolsetServiceProvider(), null, new[]
26 + {
27 + Path.Combine(folder, "test.wixout"),
28 + "-loc", Path.Combine(folder, "Package.en-us.wxl"),
29 + "-b", Path.Combine(folder, "data"),
30 + "-intermediateFolder", intermediateFolder,
31 + "-o", Path.Combine(baseFolder, @"bin\test.msi")
32 + });
33 +
34 + Assert.Equal(0, result);
35 +
36 + var binFolder = Path.Combine(baseFolder, @"bin\");
37 + var builtFiles = Directory.GetFiles(binFolder, "*", SearchOption.AllDirectories);
38 +
39 + Assert.Equal(new[]{
40 + "MsiPackage\\test.txt",
41 + "test.msi",
42 + "test.wir",
43 + "test.wixpdb",
44 + }, builtFiles.Select(f => f.Substring(binFolder.Length)).OrderBy(s => s).ToArray());
45 + }
46 + }
47 + }
48 +}
src/test/WixToolsetTest.LightIntegration/TestData/Wixout/Package.en-us.wxl new
+11
@@ -0,0 +1,11 @@
1 +<?xml version="1.0" encoding="utf-8"?>
2 +
3 +<!--
4 +This file contains the declaration of all the localizable strings.
5 +-->
6 +<WixLocalization xmlns="http://wixtoolset.org/schemas/v4/wxl" Culture="en-US">
7 +
8 + <String Id="DowngradeError">A newer version of [ProductName] is already installed.</String>
9 + <String Id="FeatureTitle">MsiPackage</String>
10 +
11 +</WixLocalization>
src/test/WixToolsetTest.LightIntegration/TestData/Wixout/data/test.txt new
+1
@@ -0,0 +1 @@
1 +This is test.txt.
\ No newline at end of file
src/test/WixToolsetTest.LightIntegration/TestData/Wixout/test.wixout
Binary files /dev/null and b/src/test/WixToolsetTest.LightIntegration/TestData/Wixout/test.wixout differ
src/test/WixToolsetTest.LightIntegration/Utility/DisposableFileSystem.cs new
+86
@@ -0,0 +1,86 @@
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 WixToolsetTest.LightIntegration.Utility
4 +{
5 + using System;
6 + using System.Collections.Generic;
7 + using System.IO;
8 +
9 + public class DisposableFileSystem : IDisposable
10 + {
11 + protected bool Disposed { get; private set; }
12 +
13 + private List<string> CleanupPaths { get; } = new List<string>();
14 +
15 + protected string GetFile(bool create = false)
16 + {
17 + var path = Path.GetTempFileName();
18 +
19 + if (!create)
20 + {
21 + File.Delete(path);
22 + }
23 +
24 + this.CleanupPaths.Add(path);
25 +
26 + return path;
27 + }
28 +
29 + public string GetFolder(bool create = false)
30 + {
31 + var path = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
32 +
33 + if (create)
34 + {
35 + Directory.CreateDirectory(path);
36 + }
37 +
38 + this.CleanupPaths.Add(path);
39 +
40 + return path;
41 + }
42 +
43 +
44 + #region // IDisposable
45 +
46 + public void Dispose()
47 + {
48 + this.Dispose(true);
49 + GC.SuppressFinalize(this);
50 + }
51 +
52 + protected virtual void Dispose(bool disposing)
53 + {
54 + if (this.Disposed)
55 + {
56 + return;
57 + }
58 +
59 + if (disposing)
60 + {
61 + foreach (var path in this.CleanupPaths)
62 + {
63 + try
64 + {
65 + if (File.Exists(path))
66 + {
67 + File.Delete(path);
68 + }
69 + else if (Directory.Exists(path))
70 + {
71 + Directory.Delete(path, true);
72 + }
73 + }
74 + catch
75 + {
76 + // Best effort delete, so ignore any failures.
77 + }
78 + }
79 + }
80 +
81 + this.Disposed = true;
82 + }
83 +
84 + #endregion
85 + }
86 +}
src/test/WixToolsetTest.LightIntegration/Utility/TestData.cs new
+17
@@ -0,0 +1,17 @@
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 WixToolsetTest.LightIntegration.Utility
4 +{
5 + using System;
6 + using System.IO;
7 +
8 + public class TestData
9 + {
10 + public static string LocalPath => Path.GetDirectoryName(new Uri(System.Reflection.Assembly.GetExecutingAssembly().CodeBase).LocalPath);
11 +
12 + public static string Get(params string[] paths)
13 + {
14 + return Path.Combine(LocalPath, Path.Combine(paths));
15 + }
16 + }
17 +}
src/test/WixToolsetTest.LightIntegration/WixToolsetTest.LightIntegration.csproj new
+33
@@ -0,0 +1,33 @@
1 +<?xml version="1.0" encoding="utf-8"?>
2 +<!-- 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. -->
3 +
4 +<Project Sdk="Microsoft.NET.Sdk">
5 + <PropertyGroup>
6 + <TargetFramework>net461</TargetFramework>
7 + <IsPackable>false</IsPackable>
8 + </PropertyGroup>
9 + <ItemGroup>
10 + <None Remove="TestData\Wixout\Package.en-us.wxl" />
11 + <None Remove="TestData\Wixout\test.wixout" />
12 + </ItemGroup>
13 +
14 + <ItemGroup>
15 + <Content Include="TestData\Wixout\data\test.txt" CopyToOutputDirectory="PreserveNewest" />
16 + <Content Include="TestData\Wixout\Package.en-us.wxl">
17 + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
18 + </Content>
19 + <Content Include="TestData\Wixout\test.wixout">
20 + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
21 + </Content>
22 + </ItemGroup>
23 +
24 + <ItemGroup>
25 + <ProjectReference Include="..\..\light\light.csproj" />
26 + </ItemGroup>
27 +
28 + <ItemGroup>
29 + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0-preview-20170628-02" />
30 + <PackageReference Include="xunit" Version="2.2.0" />
31 + <PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
32 + </ItemGroup>
33 +</Project>