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 <path> specify a binder path to locate all files
57
+ /// (default: current directory)
58
+ /// prefix the path with 'name=' where 'name' is the name of your
59
+ /// named bindpath.
60
+ /// -cc <path> path to cache built cabinets (will not be deleted after linking)
61
+ /// -ct <N> number of threads to use when creating cabinets
62
+ /// (default: %NUMBER_OF_PROCESSORS%)
63
+ /// -cub <file.cub> additional .cub file containing ICEs to run
64
+ /// -dcl:level set default cabinet compression l [rest of string was truncated]";.
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 <path> 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 <path> path to cache built cabinets (will not be deleted after linking)
126
+ -ct <N> number of threads to use when creating cabinets
127
+ (default: %NUMBER_OF_PROCESSORS%)
128
+ -cub <file.cub> 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:<ICE> run a specific internal consistency evaluator (ICE)
133
+ -pdbout <output.wixpdb> 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:<ICE> suppress an internal consistency evaluator (ICE)
139
+ -sl suppress layout
140
+ -spdb suppress outputting the WixPdb
141
+ -sval suppress MSI/MSM validation
142
+ -cultures:<cultures> 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<name>[=<value>] define a wix variable, with or without a value.
146
+ -ext <extension> extension assembly or "class, assembly"
147
+ -loc <loc.wxl> 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 <output.xml> 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>