Update to command-line parsing re-organization
Rob Mensching committed
Oct 24, 2018 at 21:02 UTC
a5c63c90a02665267c11c8bf2c653fd6db8d0107
11 files changed
+824
-715
src/WixToolset.Core.TestPackage/WixRunner.cs
+1
-1
@@ -35,7 +35,7 @@ namespace WixToolset.Core.TestPackage
35
var arguments = serviceProvider.GetService<ICommandLineArguments>();
36
arguments.Populate(args);
37
38
- var commandLine = serviceProvider.GetService<ICommandLineParser>();
38
+ var commandLine = serviceProvider.GetService<ICommandLine>();
39
commandLine.ExtensionManager = CreateExtensionManagerWithStandardBackends(serviceProvider, arguments.Extensions);
40
commandLine.Arguments = arguments;
41
var command = commandLine.ParseStandardCommandLine();
src/WixToolset.Core/CommandLine/BuildCommand.cs
+390
-88
@@ -1,4 +1,4 @@
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.
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
{
@@ -14,83 +14,84 @@ namespace WixToolset.Core.CommandLine
14
15
internal class BuildCommand : ICommandLineCommand
16
{
17
- public BuildCommand(IServiceProvider serviceProvider, IEnumerable<SourceFile> sources, IDictionary<string, string> preprocessorVariables, IEnumerable<string> locFiles, IEnumerable<string> libraryFiles, IEnumerable<string> filterCultures, string outputPath, OutputType outputType, Platform platform, string cabCachePath, bool bindFiles, IEnumerable<BindPath> bindPaths, IEnumerable<string> includeSearchPaths, string intermediateFolder, string contentsFile, string outputsFile, string builtOutputsFile)
17
+ private readonly CommandLine commandLine;
18
+
19
+ public BuildCommand(IServiceProvider serviceProvider)
20
{
21
this.ServiceProvider = serviceProvider;
22
this.Messaging = serviceProvider.GetService<IMessaging>();
23
this.ExtensionManager = serviceProvider.GetService<IExtensionManager>();
22
- this.LocFiles = locFiles;
23
- this.LibraryFiles = libraryFiles;
24
- this.FilterCultures = filterCultures;
25
- this.PreprocessorVariables = preprocessorVariables;
26
- this.SourceFiles = sources;
27
- this.OutputPath = outputPath;
28
- this.OutputType = outputType;
29
- this.Platform = platform;
30
-
31
- this.CabCachePath = cabCachePath;
32
- this.BindFiles = bindFiles;
33
- this.BindPaths = bindPaths;
34
- this.IncludeSearchPaths = includeSearchPaths;
35
-
36
- this.IntermediateFolder = intermediateFolder ?? Path.GetTempPath();
37
- this.ContentsFile = contentsFile;
38
- this.OutputsFile = outputsFile;
39
- this.BuiltOutputsFile = builtOutputsFile;
24
+ this.commandLine = new CommandLine(this.Messaging);
25
}
26
42
- public IServiceProvider ServiceProvider { get; }
27
+ public bool ShowLogo => this.commandLine.ShowLogo;
28
44
- public IMessaging Messaging { get; }
29
+ public bool StopParsing => this.commandLine.ShowHelp;
30
46
- public IExtensionManager ExtensionManager { get; }
31
+ private IServiceProvider ServiceProvider { get; }
32
48
- public IEnumerable<string> FilterCultures { get; }
33
+ private IMessaging Messaging { get; }
34
50
- public IEnumerable<string> IncludeSearchPaths { get; }
35
+ private IExtensionManager ExtensionManager { get; }
36
52
- public IEnumerable<string> LocFiles { get; }
37
+ private string IntermediateFolder { get; set; }
38
54
- public IEnumerable<string> LibraryFiles { get; }
39
+ private OutputType OutputType { get; set; }
40
56
- private IEnumerable<SourceFile> SourceFiles { get; }
41
+ private List<string> IncludeSearchPaths { get; set; }
42
58
- private IDictionary<string, string> PreprocessorVariables { get; }
43
+ private Platform Platform { get; set; }
44
60
- private string OutputPath { get; }
45
+ private string OutputFile { get; set; }
46
62
- private OutputType OutputType { get; }
47
+ private string ContentsFile { get; set; }
48
64
- private Platform Platform { get; }
49
+ private string OutputsFile { get; set; }
50
66
- public string CabCachePath { get; }
51
+ private string BuiltOutputsFile { get; set; }
52
68
- public bool BindFiles { get; }
53
+ public int Execute()
54
+ {
55
+ if (this.commandLine.ShowHelp)
56
+ {
57
+ Console.WriteLine("TODO: Show build command help");
58
+ return -1;
59
+ }
60
70
- public IEnumerable<BindPath> BindPaths { get; }
61
+ this.IntermediateFolder = this.commandLine.CalculateIntermedateFolder();
62
72
- public string IntermediateFolder { get; }
63
+ this.OutputType = this.commandLine.CalculateOutputType();
64
74
- public string ContentsFile { get; }
65
+ this.IncludeSearchPaths = this.commandLine.IncludeSearchPaths;
66
76
- public string OutputsFile { get; }
67
+ this.Platform = this.commandLine.Platform;
68
78
- public string BuiltOutputsFile { get; }
69
+ this.OutputFile = this.commandLine.OutputFile;
70
+
71
+ this.ContentsFile = this.commandLine.ContentsFile;
72
+
73
+ this.OutputsFile = this.commandLine.OutputsFile;
74
+
75
+ this.BuiltOutputsFile = this.commandLine.BuiltOutputsFile;
76
+
77
+ var preprocessorVariables = this.commandLine.GatherPreprocessorVariables();
78
+
79
+ var sourceFiles = this.commandLine.GatherSourceFiles(this.IntermediateFolder);
80
+
81
+ var filterCultures = this.commandLine.CalculateFilterCultures();
82
80
- public int Execute()
81
- {
83
var creator = this.ServiceProvider.GetService<ITupleDefinitionCreator>();
84
84
- this.EvaluateSourceFiles(creator, out var codeFiles, out var wixipl);
85
+ this.EvaluateSourceFiles(sourceFiles, creator, out var codeFiles, out var wixipl);
86
87
if (this.Messaging.EncounteredError)
88
{
89
return this.Messaging.LastErrorNumber;
90
}
91
91
- var wixobjs = this.CompilePhase(codeFiles);
92
+ var wixobjs = this.CompilePhase(preprocessorVariables, codeFiles);
93
93
- var wxls = this.LoadLocalizationFiles().ToList();
94
+ var wxls = this.LoadLocalizationFiles(this.commandLine.LocalizationFilePaths, preprocessorVariables);
95
96
if (this.Messaging.EncounteredError)
97
{
@@ -99,29 +100,29 @@ namespace WixToolset.Core.CommandLine
100
101
if (this.OutputType == OutputType.Library)
102
{
102
- var wixlib = this.LibraryPhase(wixobjs, wxls);
103
+ var wixlib = this.LibraryPhase(wixobjs, wxls, this.commandLine.BindFiles, this.commandLine.BindPaths);
104
105
if (!this.Messaging.EncounteredError)
106
{
106
- wixlib.Save(this.OutputPath);
107
+ wixlib.Save(this.commandLine.OutputFile);
108
}
109
}
110
else
111
{
112
if (wixipl == null)
113
{
113
- wixipl = this.LinkPhase(wixobjs, creator);
114
+ wixipl = this.LinkPhase(wixobjs, this.commandLine.LibraryFilePaths, creator);
115
}
116
117
if (!this.Messaging.EncounteredError)
118
{
119
if (this.OutputType == OutputType.IntermediatePostLink)
120
{
120
- wixipl.Save(this.OutputPath);
121
+ wixipl.Save(this.commandLine.OutputFile);
122
}
123
else
124
{
124
- this.BindPhase(wixipl, wxls);
125
+ this.BindPhase(wixipl, wxls, filterCultures, this.commandLine.CabCachePath, this.commandLine.BindPaths);
126
}
127
}
128
}
@@ -129,13 +130,18 @@ namespace WixToolset.Core.CommandLine
130
return this.Messaging.LastErrorNumber;
131
}
132
132
- private void EvaluateSourceFiles(ITupleDefinitionCreator creator, out List<SourceFile> codeFiles, out Intermediate wixipl)
133
+ public bool TryParseArgument(ICommandLineParser parser, string argument)
134
+ {
135
+ return this.commandLine.TryParseArgument(argument, parser);
136
+ }
137
+
138
+ private void EvaluateSourceFiles(IEnumerable<SourceFile> sourceFiles, ITupleDefinitionCreator creator, out List<SourceFile> codeFiles, out Intermediate wixipl)
139
{
140
codeFiles = new List<SourceFile>();
141
142
wixipl = null;
143
138
- foreach (var sourceFile in this.SourceFiles)
144
+ foreach (var sourceFile in sourceFiles)
145
{
146
var extension = Path.GetExtension(sourceFile.SourcePath);
147
@@ -167,13 +173,13 @@ namespace WixToolset.Core.CommandLine
173
}
174
}
175
170
- private IEnumerable<Intermediate> CompilePhase(IEnumerable<SourceFile> sourceFiles)
176
+ private IEnumerable<Intermediate> CompilePhase(IDictionary<string, string> preprocessorVariables, IEnumerable<SourceFile> sourceFiles)
177
{
178
var intermediates = new List<Intermediate>();
179
180
foreach (var sourceFile in sourceFiles)
181
{
176
- var document = this.Preprocess(sourceFile.SourcePath);
182
+ var document = this.Preprocess(preprocessorVariables, sourceFile.SourcePath);
183
184
if (this.Messaging.EncounteredError)
185
{
@@ -208,11 +214,11 @@ namespace WixToolset.Core.CommandLine
214
return intermediates;
215
}
216
211
- private Intermediate LibraryPhase(IEnumerable<Intermediate> intermediates, IEnumerable<Localization> localizations)
217
+ private Intermediate LibraryPhase(IEnumerable<Intermediate> intermediates, IEnumerable<Localization> localizations, bool bindFiles, IEnumerable<BindPath> bindPaths)
218
{
219
var context = this.ServiceProvider.GetService<ILibraryContext>();
214
- context.BindFiles = this.BindFiles;
215
- context.BindPaths = this.BindPaths;
220
+ context.BindFiles = bindFiles;
221
+ context.BindPaths = bindPaths;
222
context.Extensions = this.ExtensionManager.Create<ILibrarianExtension>();
223
context.Localizations = localizations;
224
context.Intermediates = intermediates;
@@ -230,10 +236,10 @@ namespace WixToolset.Core.CommandLine
236
237
return library;
238
}
233
-
234
- private Intermediate LinkPhase(IEnumerable<Intermediate> intermediates, ITupleDefinitionCreator creator)
239
+
240
+ private Intermediate LinkPhase(IEnumerable<Intermediate> intermediates, IEnumerable<string> libraryFiles, ITupleDefinitionCreator creator)
241
{
236
- var libraries = this.LoadLibraries(creator);
242
+ var libraries = this.LoadLibraries(libraryFiles, creator);
243
244
if (this.Messaging.EncounteredError)
245
{
@@ -251,7 +257,7 @@ namespace WixToolset.Core.CommandLine
257
return linker.Link(context);
258
}
259
254
- private void BindPhase(Intermediate output, IEnumerable<Localization> localizations)
260
+ private void BindPhase(Intermediate output, IEnumerable<Localization> localizations, IEnumerable<string> filterCultures, string cabCachePath, IEnumerable<BindPath> bindPaths)
261
{
262
var intermediateFolder = this.IntermediateFolder;
263
if (String.IsNullOrEmpty(intermediateFolder))
@@ -262,10 +268,10 @@ namespace WixToolset.Core.CommandLine
268
ResolveResult resolveResult;
269
{
270
var context = this.ServiceProvider.GetService<IResolveContext>();
265
- context.BindPaths = this.BindPaths;
271
+ context.BindPaths = bindPaths;
272
context.Extensions = this.ExtensionManager.Create<IResolverExtension>();
273
context.ExtensionData = this.ExtensionManager.Create<IExtensionData>();
268
- context.FilterCultures = this.FilterCultures;
274
+ context.FilterCultures = filterCultures;
275
context.IntermediateFolder = intermediateFolder;
276
context.IntermediateRepresentation = output;
277
context.Localizations = localizations;
@@ -284,7 +290,7 @@ namespace WixToolset.Core.CommandLine
290
{
291
var context = this.ServiceProvider.GetService<IBindContext>();
292
//context.CabbingThreadCount = this.CabbingThreadCount;
287
- context.CabCachePath = this.CabCachePath;
293
+ context.CabCachePath = cabCachePath;
294
context.Codepage = resolveResult.Codepage;
295
//context.DefaultCompressionLevel = this.DefaultCompressionLevel;
296
context.DelayedFields = resolveResult.DelayedFields;
@@ -293,8 +299,8 @@ namespace WixToolset.Core.CommandLine
299
context.Ices = Array.Empty<string>(); // TODO: set this correctly
300
context.IntermediateFolder = intermediateFolder;
301
context.IntermediateRepresentation = resolveResult.IntermediateRepresentation;
296
- context.OutputPath = this.OutputPath;
297
- context.OutputPdbPath = Path.ChangeExtension(this.OutputPath, ".wixpdb");
302
+ context.OutputPath = this.OutputFile;
303
+ context.OutputPdbPath = Path.ChangeExtension(this.OutputFile, ".wixpdb");
304
context.SuppressIces = Array.Empty<string>(); // TODO: set this correctly
305
context.SuppressValidation = true; // TODO: set this correctly
306
@@ -323,41 +329,39 @@ namespace WixToolset.Core.CommandLine
329
}
330
}
331
326
- private IEnumerable<Intermediate> LoadLibraries(ITupleDefinitionCreator creator)
332
+ private IEnumerable<Intermediate> LoadLibraries(IEnumerable<string> libraryFiles, ITupleDefinitionCreator creator)
333
{
334
var libraries = new List<Intermediate>();
335
330
- if (this.LibraryFiles != null)
336
+ foreach (var libraryFile in libraryFiles)
337
{
332
- foreach (var libraryFile in this.LibraryFiles)
338
+ try
339
{
334
- try
335
- {
336
- var library = Intermediate.Load(libraryFile, creator);
340
+ var library = Intermediate.Load(libraryFile, creator);
341
338
- libraries.Add(library);
339
- }
340
- catch (WixCorruptFileException e)
341
- {
342
- this.Messaging.Write(e.Error);
343
- }
344
- catch (WixUnexpectedFileFormatException e)
345
- {
346
- this.Messaging.Write(e.Error);
347
- }
342
+ libraries.Add(library);
343
+ }
344
+ catch (WixCorruptFileException e)
345
+ {
346
+ this.Messaging.Write(e.Error);
347
+ }
348
+ catch (WixUnexpectedFileFormatException e)
349
+ {
350
+ this.Messaging.Write(e.Error);
351
}
352
}
353
354
return libraries;
355
}
356
354
- private IEnumerable<Localization> LoadLocalizationFiles()
357
+ private IEnumerable<Localization> LoadLocalizationFiles(IEnumerable<string> locFiles, IDictionary<string, string> preprocessorVariables)
358
{
356
- var localizer = new Localizer(this.ServiceProvider);
359
+ var localizations = new List<Localization>();
360
+ var localizer = this.ServiceProvider.GetService<ILocalizer>();
361
358
- foreach (var loc in this.LocFiles)
362
+ foreach (var loc in locFiles)
363
{
360
- var document = this.Preprocess(loc);
364
+ var document = this.Preprocess(preprocessorVariables, loc);
365
366
if (this.Messaging.EncounteredError)
367
{
@@ -365,18 +369,20 @@ namespace WixToolset.Core.CommandLine
369
}
370
371
var localization = localizer.ParseLocalizationFile(document);
368
- yield return localization;
372
+ localizations.Add(localization);
373
}
374
+
375
+ return localizations;
376
}
377
372
- private XDocument Preprocess(string sourcePath)
378
+ private XDocument Preprocess(IDictionary<string, string> preprocessorVariables, string sourcePath)
379
{
380
var context = this.ServiceProvider.GetService<IPreprocessContext>();
381
context.Extensions = this.ExtensionManager.Create<IPreprocessorExtension>();
382
context.Platform = this.Platform;
383
context.IncludeSearchPaths = this.IncludeSearchPaths;
384
context.SourcePath = sourcePath;
379
- context.Variables = this.PreprocessorVariables;
385
+ context.Variables = preprocessorVariables;
386
387
XDocument document = null;
388
try
@@ -391,5 +397,301 @@ namespace WixToolset.Core.CommandLine
397
398
return document;
399
}
400
+
401
+ private class CommandLine
402
+ {
403
+ private static readonly char[] BindPathSplit = { '=' };
404
+
405
+ public bool BindFiles { get; private set; }
406
+
407
+ public List<BindPath> BindPaths { get; } = new List<BindPath>();
408
+
409
+ public string CabCachePath { get; private set; }
410
+
411
+ public List<string> Cultures { get; } = new List<string>();
412
+
413
+ public List<string> Defines { get; } = new List<string>();
414
+
415
+ public List<string> IncludeSearchPaths { get; } = new List<string>();
416
+
417
+ public List<string> LocalizationFilePaths { get; } = new List<string>();
418
+
419
+ public List<string> LibraryFilePaths { get; } = new List<string>();
420
+
421
+ public List<string> SourceFilePaths { get; } = new List<string>();
422
+
423
+ public Platform Platform { get; private set; }
424
+
425
+ public bool ShowLogo { get; private set; }
426
+
427
+ public bool ShowHelp { get; private set; }
428
+
429
+ public string IntermediateFolder { get; private set; }
430
+
431
+ public string OutputFile { get; private set; }
432
+
433
+ public string OutputType { get; private set; }
434
+
435
+ public string ContentsFile { get; private set; }
436
+
437
+ public string OutputsFile { get; private set; }
438
+
439
+ public string BuiltOutputsFile { get; private set; }
440
+
441
+ public CommandLine(IMessaging messaging)
442
+ {
443
+ this.Messaging = messaging;
444
+ }
445
+
446
+ private IMessaging Messaging { get; }
447
+
448
+ public bool TryParseArgument(string arg, ICommandLineParser parser)
449
+ {
450
+ if (parser.IsSwitch(arg))
451
+ {
452
+ var parameter = arg.Substring(1);
453
+ switch (parameter.ToLowerInvariant())
454
+ {
455
+ case "?":
456
+ case "h":
457
+ case "help":
458
+ this.ShowHelp = true;
459
+ return true;
460
+
461
+ case "arch":
462
+ case "platform":
463
+ {
464
+ var value = parser.GetNextArgumentOrError(arg);
465
+ if (Enum.TryParse(value, true, out Platform platform))
466
+ {
467
+ this.Platform = platform;
468
+ return true;
469
+ }
470
+ break;
471
+ }
472
+
473
+ case "bindfiles":
474
+ this.BindFiles = true;
475
+ return true;
476
+
477
+ case "bindpath":
478
+ {
479
+ var value = parser.GetNextArgumentOrError(arg);
480
+ if (this.TryParseBindPath(value, out var bindPath))
481
+ {
482
+ this.BindPaths.Add(bindPath);
483
+ return true;
484
+ }
485
+ break;
486
+ }
487
+ case "cc":
488
+ this.CabCachePath = parser.GetNextArgumentOrError(arg);
489
+ return true;
490
+
491
+ case "culture":
492
+ parser.GetNextArgumentOrError(arg, this.Cultures);
493
+ return true;
494
+
495
+ case "contentsfile":
496
+ this.ContentsFile = parser.GetNextArgumentAsFilePathOrError(arg);
497
+ return true;
498
+ case "outputsfile":
499
+ this.OutputsFile = parser.GetNextArgumentAsFilePathOrError(arg);
500
+ return true;
501
+ case "builtoutputsfile":
502
+ this.BuiltOutputsFile = parser.GetNextArgumentAsFilePathOrError(arg);
503
+ return true;
504
+
505
+ case "d":
506
+ case "define":
507
+ parser.GetNextArgumentOrError(arg, this.Defines);
508
+ return true;
509
+
510
+ case "i":
511
+ case "includepath":
512
+ parser.GetNextArgumentOrError(arg, this.IncludeSearchPaths);
513
+ return true;
514
+
515
+ case "intermediatefolder":
516
+ this.IntermediateFolder = parser.GetNextArgumentAsDirectoryOrError(arg);
517
+ return true;
518
+
519
+ case "loc":
520
+ parser.GetNextArgumentAsFilePathOrError(arg, "localization files", this.LocalizationFilePaths);
521
+ return true;
522
+
523
+ case "lib":
524
+ parser.GetNextArgumentAsFilePathOrError(arg, "library files", this.LibraryFilePaths);
525
+ return true;
526
+
527
+ case "o":
528
+ case "out":
529
+ this.OutputFile = parser.GetNextArgumentAsFilePathOrError(arg);
530
+ return true;
531
+
532
+ case "outputtype":
533
+ this.OutputType = parser.GetNextArgumentOrError(arg);
534
+ return true;
535
+
536
+ case "nologo":
537
+ this.ShowLogo = false;
538
+ return true;
539
+
540
+ case "v":
541
+ case "verbose":
542
+ this.Messaging.ShowVerboseMessages = true;
543
+ return true;
544
+
545
+ case "sval":
546
+ // todo: implement
547
+ return true;
548
+
549
+ case "sw":
550
+ case "suppresswarning":
551
+ var warning = parser.GetNextArgumentOrError(arg);
552
+ if (!String.IsNullOrEmpty(warning))
553
+ {
554
+ var warningNumber = Convert.ToInt32(warning);
555
+ this.Messaging.SuppressWarningMessage(warningNumber);
556
+ }
557
+ return true;
558
+ }
559
+
560
+ return false;
561
+ }
562
+ else
563
+ {
564
+ parser.GetArgumentAsFilePathOrError(arg, "source code", this.SourceFilePaths);
565
+ return true;
566
+ }
567
+ }
568
+
569
+ public string CalculateIntermedateFolder()
570
+ {
571
+ return String.IsNullOrEmpty(this.IntermediateFolder) ? Path.GetTempPath() : this.IntermediateFolder;
572
+ }
573
+
574
+ public OutputType CalculateOutputType()
575
+ {
576
+ if (String.IsNullOrEmpty(this.OutputType))
577
+ {
578
+ this.OutputType = Path.GetExtension(this.OutputFile);
579
+ }
580
+
581
+ switch (this.OutputType.ToLowerInvariant())
582
+ {
583
+ case "bundle":
584
+ case ".exe":
585
+ return Data.OutputType.Bundle;
586
+
587
+ case "library":
588
+ case ".wixlib":
589
+ return Data.OutputType.Library;
590
+
591
+ case "module":
592
+ case ".msm":
593
+ return Data.OutputType.Module;
594
+
595
+ case "patch":
596
+ case ".msp":
597
+ return Data.OutputType.Patch;
598
+
599
+ case ".pcp":
600
+ return Data.OutputType.PatchCreation;
601
+
602
+ case "product":
603
+ case "package":
604
+ case ".msi":
605
+ return Data.OutputType.Product;
606
+
607
+ case "transform":
608
+ case ".mst":
609
+ return Data.OutputType.Transform;
610
+
611
+ case "intermediatepostlink":
612
+ case ".wixipl":
613
+ return Data.OutputType.IntermediatePostLink;
614
+ }
615
+
616
+ return Data.OutputType.Unknown;
617
+ }
618
+
619
+ public IEnumerable<string> CalculateFilterCultures()
620
+ {
621
+ var result = new List<string>();
622
+
623
+ if (this.Cultures == null)
624
+ {
625
+ }
626
+ else if (this.Cultures.Count == 1 && this.Cultures[0].Equals("null", StringComparison.OrdinalIgnoreCase))
627
+ {
628
+ // When null is used treat it as if cultures wasn't specified. This is
629
+ // needed for batching in the MSBuild task since MSBuild doesn't support
630
+ // empty items.
631
+ }
632
+ else
633
+ {
634
+ foreach (var culture in this.Cultures)
635
+ {
636
+ // Neutral is different from null. For neutral we still want to do culture filtering.
637
+ // Set the culture to the empty string = identifier for the invariant culture.
638
+ var filter = (culture.Equals("neutral", StringComparison.OrdinalIgnoreCase)) ? String.Empty : culture;
639
+ result.Add(filter);
640
+ }
641
+ }
642
+
643
+ return result;
644
+ }
645
+
646
+ public IDictionary<string, string> GatherPreprocessorVariables()
647
+ {
648
+ var variables = new Dictionary<string, string>();
649
+
650
+ foreach (var pair in this.Defines)
651
+ {
652
+ var value = pair.Split(new[] { '=' }, 2);
653
+
654
+ if (variables.ContainsKey(value[0]))
655
+ {
656
+ this.Messaging.Write(ErrorMessages.DuplicateVariableDefinition(value[0], (1 == value.Length) ? String.Empty : value[1], variables[value[0]]));
657
+ continue;
658
+ }
659
+
660
+ variables.Add(value[0], (1 == value.Length) ? String.Empty : value[1]);
661
+ }
662
+
663
+ return variables;
664
+ }
665
+
666
+
667
+ public IEnumerable<SourceFile> GatherSourceFiles(string intermediateDirectory)
668
+ {
669
+ var files = new List<SourceFile>();
670
+
671
+ foreach (var item in this.SourceFilePaths)
672
+ {
673
+ var sourcePath = item;
674
+ var outputPath = Path.Combine(intermediateDirectory, Path.GetFileNameWithoutExtension(sourcePath) + ".wir");
675
+
676
+ files.Add(new SourceFile(sourcePath, outputPath));
677
+ }
678
+
679
+ return files;
680
+ }
681
+
682
+ private bool TryParseBindPath(string bindPath, out BindPath bp)
683
+ {
684
+ var namedPath = bindPath.Split(BindPathSplit, 2);
685
+ bp = (1 == namedPath.Length) ? new BindPath(namedPath[0]) : new BindPath(namedPath[0], namedPath[1]);
686
+
687
+ if (File.Exists(bp.Path))
688
+ {
689
+ this.Messaging.Write(ErrorMessages.ExpectedDirectoryGotFile("-bindpath", bp.Path));
690
+ return false;
691
+ }
692
+
693
+ return true;
694
+ }
695
+ }
696
}
697
}
src/WixToolset.Core/CommandLine/CommandLine.cs
new
+210
@@ -0,0 +1,210 @@
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.Collections.Generic;
7
+ using WixToolset.Extensibility;
8
+ using WixToolset.Extensibility.Data;
9
+ using WixToolset.Extensibility.Services;
10
+
11
+ internal enum CommandTypes
12
+ {
13
+ Unknown,
14
+ Build,
15
+ Preprocess,
16
+ Compile,
17
+ Link,
18
+ Bind,
19
+ Decompile,
20
+ }
21
+
22
+ internal class CommandLine : ICommandLine
23
+ {
24
+ private static readonly char[] BindPathSplit = { '=' };
25
+
26
+ public CommandLine(IServiceProvider serviceProvider)
27
+ {
28
+ this.ServiceProvider = serviceProvider;
29
+
30
+ this.Messaging = this.ServiceProvider.GetService<IMessaging>();
31
+ }
32
+
33
+ private IServiceProvider ServiceProvider { get; }
34
+
35
+ private IMessaging Messaging { get; set; }
36
+
37
+ public IExtensionManager ExtensionManager { get; set; }
38
+
39
+ public ICommandLineArguments Arguments { get; set; }
40
+
41
+ public static string ExpectedArgument { get; } = "expected argument";
42
+
43
+ public bool ShowHelp { get; private set; }
44
+
45
+ public ICommandLineCommand ParseStandardCommandLine()
46
+ {
47
+ var context = this.ServiceProvider.GetService<ICommandLineContext>();
48
+ context.ExtensionManager = this.ExtensionManager ?? this.ServiceProvider.GetService<IExtensionManager>();
49
+ context.Arguments = this.Arguments;
50
+
51
+ var command = this.Parse(context);
52
+
53
+ if (command.ShowLogo)
54
+ {
55
+ AppCommon.DisplayToolHeader();
56
+ }
57
+
58
+ return command;
59
+ //switch (commandType)
60
+ //{
61
+ //case CommandTypes.Build:
62
+ //{
63
+ // var sourceFiles = GatherSourceFiles(files, outputFolder);
64
+ // var variables = this.GatherPreprocessorVariables(defines);
65
+ // var bindPathList = this.GatherBindPaths(bindPaths);
66
+ // var filterCultures = CalculateFilterCultures(cultures);
67
+ // var type = CalculateOutputType(outputType, outputFile);
68
+ // var platform = CalculatePlatform(platformType);
69
+ // return new BuildCommand(this.ServiceProvider, sourceFiles, variables, locFiles, libraryFiles, filterCultures, outputFile, type, platform, cabCachePath, bindFiles, bindPathList, includePaths, intermediateFolder, contentsFile, outputsFile, builtOutputsFile);
70
+ //}
71
+
72
+ //case CommandTypes.Compile:
73
+ //{
74
+ // var sourceFiles = GatherSourceFiles(files, outputFolder);
75
+ // var variables = this.GatherPreprocessorVariables(defines);
76
+ // var platform = CalculatePlatform(platformType);
77
+ // return new CompileCommand(this.ServiceProvider, sourceFiles, variables, platform);
78
+ //}
79
+
80
+ //case CommandTypes.Decompile:
81
+ //{
82
+ // var sourceFiles = GatherSourceFiles(files, outputFolder);
83
+ // return new DecompileCommand(this.ServiceProvider, sourceFiles, outputFile);
84
+ //}
85
+ //}
86
+
87
+ //return null;
88
+ }
89
+
90
+ private ICommandLineCommand Parse(ICommandLineContext context)
91
+ {
92
+ var extensions = this.ExtensionManager.Create<IExtensionCommandLine>();
93
+
94
+ foreach (var extension in extensions)
95
+ {
96
+ extension.PreParse(context);
97
+ }
98
+
99
+ ICommandLineCommand command = null;
100
+ var parser = context.Arguments.Parse();
101
+
102
+ while (command?.StopParsing != true &&
103
+ String.IsNullOrEmpty(parser.ErrorArgument) &&
104
+ parser.TryGetNextSwitchOrArgument(out var arg))
105
+ {
106
+ if (String.IsNullOrWhiteSpace(arg)) // skip blank arguments.
107
+ {
108
+ continue;
109
+ }
110
+
111
+ // First argument must be the command or global switch (that creates a command).
112
+ if (command == null)
113
+ {
114
+ if (!this.TryParseUnknownCommandArg(arg, parser, out command, extensions))
115
+ {
116
+ parser.ErrorArgument = arg;
117
+ }
118
+ }
119
+ else if (parser.IsSwitch(arg))
120
+ {
121
+ if (!command.TryParseArgument(parser, arg) && !TryParseCommandLineArgumentWithExtension(arg, parser, extensions))
122
+ {
123
+ parser.ErrorArgument = arg;
124
+ }
125
+ }
126
+ else if (!TryParseCommandLineArgumentWithExtension(arg, parser, extensions) && command?.TryParseArgument(parser, arg) == false)
127
+ {
128
+ parser.ErrorArgument = arg;
129
+ }
130
+ }
131
+
132
+ foreach (var extension in extensions)
133
+ {
134
+ extension.PostParse();
135
+ }
136
+
137
+ return command ?? new HelpCommand();
138
+ }
139
+
140
+ private bool TryParseUnknownCommandArg(string arg, ICommandLineParser parser, out ICommandLineCommand command, IEnumerable<IExtensionCommandLine> extensions)
141
+ {
142
+ command = null;
143
+
144
+ if (parser.IsSwitch(arg))
145
+ {
146
+ var parameter = arg.Substring(1);
147
+ switch (parameter.ToLowerInvariant())
148
+ {
149
+ case "?":
150
+ case "h":
151
+ case "help":
152
+ command = new HelpCommand();
153
+ break;
154
+
155
+ case "version":
156
+ case "-version":
157
+ command = new VersionCommand();
158
+ break;
159
+ }
160
+ }
161
+ else
162
+ {
163
+ if (Enum.TryParse(arg, true, out CommandTypes commandType))
164
+ {
165
+ switch (commandType)
166
+ {
167
+ case CommandTypes.Build:
168
+ command = new BuildCommand(this.ServiceProvider);
169
+ break;
170
+
171
+ case CommandTypes.Compile:
172
+ command = new CompileCommand(this.ServiceProvider);
173
+ break;
174
+
175
+ case CommandTypes.Decompile:
176
+ command = new DecompileCommand(this.ServiceProvider);
177
+ break;
178
+ }
179
+ }
180
+ else
181
+ {
182
+ foreach (var extension in extensions)
183
+ {
184
+ if (extension.TryParseCommand(parser, out command))
185
+ {
186
+ break;
187
+ }
188
+
189
+ command = null;
190
+ }
191
+ }
192
+ }
193
+
194
+ return command != null;
195
+ }
196
+
197
+ private static bool TryParseCommandLineArgumentWithExtension(string arg, ICommandLineParser parse, IEnumerable<IExtensionCommandLine> extensions)
198
+ {
199
+ foreach (var extension in extensions)
200
+ {
201
+ if (extension.TryParseArgument(parse, arg))
202
+ {
203
+ return true;
204
+ }
205
+ }
206
+
207
+ return false;
208
+ }
209
+ }
210
+}
src/WixToolset.Core/CommandLine/CommandLineArguments.cs
+2
-2
@@ -41,11 +41,11 @@ namespace WixToolset.Core.CommandLine
41
this.ProcessArgumentsAndParseExtensions(this.OriginalArguments);
42
}
43
44
- public IParseCommandLine Parse()
44
+ public ICommandLineParser Parse()
45
{
46
var messaging = (IMessaging)this.ServiceProvider.GetService(typeof(IMessaging));
47
48
- return new ParseCommandLine(messaging, this.Arguments, this.ErrorArgument);
48
+ return new CommandLineParser(messaging, this.Arguments, this.ErrorArgument);
49
}
50
51
private void FlattenArgumentsWithResponseFilesIntoOriginalArguments(string[] commandLineArguments)
src/WixToolset.Core/CommandLine/CommandLineParser.cs
+173
-341
@@ -1,4 +1,4 @@
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.
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
{
@@ -6,437 +6,269 @@ namespace WixToolset.Core.CommandLine
6
using System.Collections.Generic;
7
using System.IO;
8
using WixToolset.Data;
9
- using WixToolset.Extensibility;
10
- using WixToolset.Extensibility.Data;
9
using WixToolset.Extensibility.Services;
10
13
- internal enum Commands
14
- {
15
- Unknown,
16
- Build,
17
- Preprocess,
18
- Compile,
19
- Link,
20
- Bind,
21
- }
22
-
11
internal class CommandLineParser : ICommandLineParser
12
{
25
- private static readonly char[] BindPathSplit = { '=' };
26
-
27
- public CommandLineParser(IServiceProvider serviceProvider)
28
- {
29
- this.ServiceProvider = serviceProvider;
30
-
31
- this.Messaging = this.ServiceProvider.GetService<IMessaging>();
32
- }
13
+ private const string ExpectedArgument = "expected argument";
14
34
- private IServiceProvider ServiceProvider { get; }
15
+ public string ErrorArgument { get; set; }
16
36
- private IMessaging Messaging { get; set; }
17
+ private Queue<string> RemainingArguments { get; }
18
38
- public IExtensionManager ExtensionManager { get; set; }
19
+ private IMessaging Messaging { get; }
20
40
- public ICommandLineArguments Arguments { get; set; }
41
-
42
- public static string ExpectedArgument { get; } = "expected argument";
43
-
44
- public string ActiveCommand { get; private set; }
21
+ public CommandLineParser(IMessaging messaging, string[] arguments, string errorArgument)
22
+ {
23
+ this.Messaging = messaging;
24
+ this.RemainingArguments = new Queue<string>(arguments);
25
+ this.ErrorArgument = errorArgument;
26
+ }
27
46
- public bool ShowHelp { get; private set; }
28
+ public bool IsSwitch(string arg)
29
+ {
30
+ return !String.IsNullOrEmpty(arg) && ('/' == arg[0] || '-' == arg[0]);
31
+ }
32
48
- public ICommandLineCommand ParseStandardCommandLine()
33
+ public string GetArgumentAsFilePathOrError(string argument, string fileType)
34
{
50
- var context = this.ServiceProvider.GetService<ICommandLineContext>();
51
- context.ExtensionManager = this.ExtensionManager ?? this.ServiceProvider.GetService<IExtensionManager>();
52
- context.Arguments = this.Arguments;
53
-
54
- var next = String.Empty;
55
-
56
- var command = Commands.Unknown;
57
- var showLogo = true;
58
- var showVersion = false;
59
- var outputFolder = String.Empty;
60
- var outputFile = String.Empty;
61
- var outputType = String.Empty;
62
- var platformType = String.Empty;
63
- var verbose = false;
64
- var files = new List<string>();
65
- var defines = new List<string>();
66
- var includePaths = new List<string>();
67
- var locFiles = new List<string>();
68
- var libraryFiles = new List<string>();
69
- var suppressedWarnings = new List<int>();
70
-
71
- var bindFiles = false;
72
- var bindPaths = new List<string>();
73
-
74
- var intermediateFolder = String.Empty;
75
-
76
- var cabCachePath = String.Empty;
77
- var cultures = new List<string>();
78
- var contentsFile = String.Empty;
79
- var outputsFile = String.Empty;
80
- var builtOutputsFile = String.Empty;
81
-
82
- this.Parse(context, (cmdline, arg) => Enum.TryParse(arg, true, out command), (cmdline, parser, arg) =>
35
+ if (!File.Exists(argument))
36
{
84
- if (parser.IsSwitch(arg))
85
- {
86
- var parameter = arg.Substring(1);
87
- switch (parameter.ToLowerInvariant())
88
- {
89
- case "?":
90
- case "h":
91
- case "help":
92
- cmdline.ShowHelp = true;
93
- return true;
94
-
95
- case "arch":
96
- case "platform":
97
- platformType = parser.GetNextArgumentOrError(arg);
98
- return true;
99
-
100
- case "bindfiles":
101
- bindFiles = true;
102
- return true;
103
-
104
- case "bindpath":
105
- parser.GetNextArgumentOrError(arg, bindPaths);
106
- return true;
107
-
108
- case "cc":
109
- cabCachePath = parser.GetNextArgumentOrError(arg);
110
- return true;
111
-
112
- case "culture":
113
- parser.GetNextArgumentOrError(arg, cultures);
114
- return true;
115
- case "contentsfile":
116
- contentsFile = parser.GetNextArgumentAsFilePathOrError(arg);
117
- return true;
118
- case "outputsfile":
119
- outputsFile = parser.GetNextArgumentAsFilePathOrError(arg);
120
- return true;
121
- case "builtoutputsfile":
122
- builtOutputsFile = parser.GetNextArgumentAsFilePathOrError(arg);
123
- return true;
124
-
125
- case "d":
126
- case "define":
127
- parser.GetNextArgumentOrError(arg, defines);
128
- return true;
129
-
130
- case "i":
131
- case "includepath":
132
- parser.GetNextArgumentOrError(arg, includePaths);
133
- return true;
134
-
135
- case "intermediatefolder":
136
- intermediateFolder = parser.GetNextArgumentAsDirectoryOrError(arg);
137
- return true;
138
-
139
- case "loc":
140
- parser.GetNextArgumentAsFilePathOrError(arg, "localization files", locFiles);
141
- return true;
142
-
143
- case "lib":
144
- parser.GetNextArgumentAsFilePathOrError(arg, "library files", libraryFiles);
145
- return true;
146
-
147
- case "o":
148
- case "out":
149
- outputFile = parser.GetNextArgumentAsFilePathOrError(arg);
150
- return true;
151
-
152
- case "outputtype":
153
- outputType = parser.GetNextArgumentOrError(arg);
154
- return true;
155
-
156
- case "nologo":
157
- showLogo = false;
158
- return true;
159
-
160
- case "v":
161
- case "verbose":
162
- verbose = true;
163
- return true;
164
-
165
- case "version":
166
- case "-version":
167
- showVersion = true;
168
- return true;
169
-
170
- case "sval":
171
- // todo: implement
172
- return true;
173
-
174
- case "sw":
175
- case "suppresswarning":
176
- var warning = parser.GetNextArgumentOrError(arg);
177
- if (!String.IsNullOrEmpty(warning))
178
- {
179
- var warningNumber = Convert.ToInt32(warning);
180
- this.Messaging.SuppressWarningMessage(warningNumber);
181
- }
182
- return true;
183
- }
184
-
185
- return false;
186
- }
187
- else
188
- {
189
- parser.GetArgumentAsFilePathOrError(arg, "source code", files);
190
- return true;
191
- }
192
- });
37
+ this.Messaging.Write(ErrorMessages.FileNotFound(null, argument, fileType));
38
+ return null;
39
+ }
40
194
- this.Messaging.ShowVerboseMessages = verbose;
41
+ return argument;
42
+ }
43
196
- if (showVersion)
44
+ public void GetArgumentAsFilePathOrError(string argument, string fileType, IList<string> paths)
45
+ {
46
+ foreach (var path in this.GetFiles(argument, fileType))
47
{
198
- return new VersionCommand();
48
+ paths.Add(path);
49
}
50
+ }
51
201
- if (showLogo)
52
+ public string GetNextArgumentOrError(string commandLineSwitch)
53
+ {
54
+ if (this.TryGetNextNonSwitchArgumentOrError(out var argument))
55
{
203
- AppCommon.DisplayToolHeader();
56
+ return argument;
57
}
58
206
- if (this.ShowHelp)
207
- {
208
- return new HelpCommand(command);
209
- }
59
+ this.Messaging.Write(ErrorMessages.ExpectedArgument(commandLineSwitch));
60
+ return null;
61
+ }
62
211
- switch (command)
212
- {
213
- case Commands.Build:
63
+ public bool GetNextArgumentOrError(string commandLineSwitch, IList<string> args)
64
+ {
65
+ if (this.TryGetNextNonSwitchArgumentOrError(out var arg))
66
{
215
- var sourceFiles = GatherSourceFiles(files, outputFolder);
216
- var variables = this.GatherPreprocessorVariables(defines);
217
- var bindPathList = this.GatherBindPaths(bindPaths);
218
- var filterCultures = CalculateFilterCultures(cultures);
219
- var type = CalculateOutputType(outputType, outputFile);
220
- var platform = CalculatePlatform(platformType);
221
- return new BuildCommand(this.ServiceProvider, sourceFiles, variables, locFiles, libraryFiles, filterCultures, outputFile, type, platform, cabCachePath, bindFiles, bindPathList, includePaths, intermediateFolder, contentsFile, outputsFile, builtOutputsFile);
67
+ args.Add(arg);
68
+ return true;
69
}
70
224
- case Commands.Compile:
71
+ this.Messaging.Write(ErrorMessages.ExpectedArgument(commandLineSwitch));
72
+ return false;
73
+ }
74
+
75
+ public string GetNextArgumentAsDirectoryOrError(string commandLineSwitch)
76
+ {
77
+ if (this.TryGetNextNonSwitchArgumentOrError(out var arg) && this.TryGetDirectory(commandLineSwitch, this.Messaging, arg, out var directory))
78
{
226
- var sourceFiles = GatherSourceFiles(files, outputFolder);
227
- var variables = this.GatherPreprocessorVariables(defines);
228
- var platform = CalculatePlatform(platformType);
229
- return new CompileCommand(this.ServiceProvider, sourceFiles, variables, platform);
230
- }
79
+ return directory;
80
}
81
82
+ this.Messaging.Write(ErrorMessages.ExpectedArgument(commandLineSwitch));
83
return null;
84
}
85
236
- private static IEnumerable<string> CalculateFilterCultures(List<string> cultures)
86
+ public bool GetNextArgumentAsDirectoryOrError(string commandLineSwitch, IList<string> directories)
87
{
238
- var result = new List<string>();
239
-
240
- if (cultures == null)
241
- {
242
- }
243
- else if (cultures.Count == 1 && cultures[0].Equals("null", StringComparison.OrdinalIgnoreCase))
88
+ if (this.TryGetNextNonSwitchArgumentOrError(out var arg) && this.TryGetDirectory(commandLineSwitch, this.Messaging, arg, out var directory))
89
{
245
- // When null is used treat it as if cultures wasn't specified. This is
246
- // needed for batching in the MSBuild task since MSBuild doesn't support
247
- // empty items.
248
- }
249
- else
250
- {
251
- foreach (var culture in cultures)
252
- {
253
- // Neutral is different from null. For neutral we still want to do culture filtering.
254
- // Set the culture to the empty string = identifier for the invariant culture.
255
- var filter = (culture.Equals("neutral", StringComparison.OrdinalIgnoreCase)) ? String.Empty : culture;
256
- result.Add(filter);
257
- }
90
+ directories.Add(directory);
91
+ return true;
92
}
93
260
- return result;
94
+ this.Messaging.Write(ErrorMessages.ExpectedArgument(commandLineSwitch));
95
+ return false;
96
}
97
263
- private static OutputType CalculateOutputType(string outputType, string outputFile)
98
+ public string GetNextArgumentAsFilePathOrError(string commandLineSwitch)
99
{
265
- if (String.IsNullOrEmpty(outputType))
100
+ if (this.TryGetNextNonSwitchArgumentOrError(out var arg) && this.TryGetFile(commandLineSwitch, arg, out var path))
101
{
267
- outputType = Path.GetExtension(outputFile);
102
+ return path;
103
}
104
270
- switch (outputType.ToLowerInvariant())
271
- {
272
- case "bundle":
273
- case ".exe":
274
- return OutputType.Bundle;
275
-
276
- case "library":
277
- case ".wixlib":
278
- return OutputType.Library;
279
-
280
- case "module":
281
- case ".msm":
282
- return OutputType.Module;
283
-
284
- case "patch":
285
- case ".msp":
286
- return OutputType.Patch;
287
-
288
- case ".pcp":
289
- return OutputType.PatchCreation;
290
-
291
- case "product":
292
- case "package":
293
- case ".msi":
294
- return OutputType.Product;
105
+ this.Messaging.Write(ErrorMessages.ExpectedArgument(commandLineSwitch));
106
+ return null;
107
+ }
108
296
- case "transform":
297
- case ".mst":
298
- return OutputType.Transform;
109
+ public bool GetNextArgumentAsFilePathOrError(string commandLineSwitch, string fileType, IList<string> paths)
110
+ {
111
+ if (this.TryGetNextNonSwitchArgumentOrError(out var arg))
112
+ {
113
+ foreach (var path in this.GetFiles(arg, fileType))
114
+ {
115
+ paths.Add(path);
116
+ }
117
300
- case "intermediatepostlink":
301
- case ".wixipl":
302
- return OutputType.IntermediatePostLink;
118
+ return true;
119
}
120
305
- return OutputType.Unknown;
121
+ this.Messaging.Write(ErrorMessages.ExpectedArgument(commandLineSwitch));
122
+ return false;
123
}
124
308
- private static Platform CalculatePlatform(string platformType)
125
+ public bool TryGetNextSwitchOrArgument(out string arg)
126
{
310
- return Enum.TryParse(platformType, true, out Platform platform) ? platform : Platform.X86;
127
+ return TryDequeue(this.RemainingArguments, out arg);
128
}
129
313
- private ICommandLineParser Parse(ICommandLineContext context, Func<CommandLineParser, string, bool> parseCommand, Func<CommandLineParser, IParseCommandLine, string, bool> parseArgument)
130
+ private bool TryGetNextNonSwitchArgumentOrError(out string arg)
131
{
315
- var extensions = this.ExtensionManager.Create<IExtensionCommandLine>();
132
+ var result = this.TryGetNextSwitchOrArgument(out arg);
133
317
- foreach (var extension in extensions)
134
+ if (!result && !this.IsSwitch(arg))
135
{
319
- extension.PreParse(context);
136
+ this.ErrorArgument = arg ?? CommandLineParser.ExpectedArgument;
137
}
138
322
- var parser = context.Arguments.Parse();
323
-
324
- while (!this.ShowHelp &&
325
- String.IsNullOrEmpty(parser.ErrorArgument) &&
326
- parser.TryGetNextSwitchOrArgument(out var arg))
327
- {
328
- if (String.IsNullOrWhiteSpace(arg)) // skip blank arguments.
329
- {
330
- continue;
331
- }
139
+ return result;
140
+ }
141
333
- if (parser.IsSwitch(arg))
334
- {
335
- if (!parseArgument(this, parser, arg) &&
336
- !this.TryParseCommandLineArgumentWithExtension(arg, parser, extensions))
337
- {
338
- parser.ErrorArgument = arg;
339
- }
340
- }
341
- else if (String.IsNullOrEmpty(this.ActiveCommand) && parseCommand != null) // First non-switch must be the command, if commands are supported.
342
- {
343
- if (parseCommand(this, arg))
344
- {
345
- this.ActiveCommand = arg;
346
- }
347
- else
348
- {
349
- parser.ErrorArgument = arg;
350
- }
351
- }
352
- else if (!this.TryParseCommandLineArgumentWithExtension(arg, parser, extensions) &&
353
- !parseArgument(this, parser, arg))
354
- {
355
- parser.ErrorArgument = arg;
356
- }
357
- }
142
+ private static bool IsValidArg(string arg)
143
+ {
144
+ return !(String.IsNullOrEmpty(arg) || '/' == arg[0] || '-' == arg[0]);
145
+ }
146
359
- foreach (var extension in extensions)
147
+ private static bool TryDequeue(Queue<string> q, out string arg)
148
+ {
149
+ if (q.Count > 0)
150
{
361
- extension.PostParse();
151
+ arg = q.Dequeue();
152
+ return true;
153
}
154
364
- return this;
155
+ arg = null;
156
+ return false;
157
}
158
367
- private static IEnumerable<SourceFile> GatherSourceFiles(IEnumerable<string> sourceFiles, string intermediateDirectory)
159
+ private bool TryGetDirectory(string commandlineSwitch, IMessaging messageHandler, string arg, out string directory)
160
{
369
- var files = new List<SourceFile>();
161
+ directory = null;
162
371
- foreach (var item in sourceFiles)
163
+ if (File.Exists(arg))
164
{
373
- var sourcePath = item;
374
- var outputPath = Path.Combine(intermediateDirectory, Path.GetFileNameWithoutExtension(sourcePath) + ".wir");
375
-
376
- files.Add(new SourceFile(sourcePath, outputPath));
165
+ this.Messaging.Write(ErrorMessages.ExpectedDirectoryGotFile(commandlineSwitch, arg));
166
+ return false;
167
}
168
379
- return files;
169
+ directory = this.VerifyPath(arg);
170
+ return directory != null;
171
}
172
382
- private IDictionary<string, string> GatherPreprocessorVariables(IEnumerable<string> defineConstants)
173
+ private bool TryGetFile(string commandlineSwitch, string arg, out string path)
174
{
384
- var variables = new Dictionary<string, string>();
175
+ path = null;
176
386
- foreach (var pair in defineConstants)
177
+ if (!IsValidArg(arg))
178
{
388
- var value = pair.Split(new[] { '=' }, 2);
389
-
390
- if (variables.ContainsKey(value[0]))
391
- {
392
- this.Messaging.Write(ErrorMessages.DuplicateVariableDefinition(value[0], (1 == value.Length) ? String.Empty : value[1], variables[value[0]]));
393
- continue;
394
- }
395
-
396
- variables.Add(value[0], (1 == value.Length) ? String.Empty : value[1]);
179
+ this.Messaging.Write(ErrorMessages.FilePathRequired(commandlineSwitch));
180
+ }
181
+ else if (Directory.Exists(arg))
182
+ {
183
+ this.Messaging.Write(ErrorMessages.ExpectedFileGotDirectory(commandlineSwitch, arg));
184
+ }
185
+ else
186
+ {
187
+ path = this.VerifyPath(arg);
188
}
189
399
- return variables;
190
+ return path != null;
191
}
192
402
- private IEnumerable<BindPath> GatherBindPaths(IEnumerable<string> bindPaths)
193
+ /// <summary>
194
+ /// Get a set of files that possibly have a search pattern in the path (such as '*').
195
+ /// </summary>
196
+ /// <param name="searchPath">Search path to find files in.</param>
197
+ /// <param name="fileType">Type of file; typically "Source".</param>
198
+ /// <returns>An array of files matching the search path.</returns>
199
+ /// <remarks>
200
+ /// This method is written in this verbose way because it needs to support ".." in the path.
201
+ /// It needs the directory path isolated from the file name in order to use Directory.GetFiles
202
+ /// or DirectoryInfo.GetFiles. The only way to get this directory path is manually since
203
+ /// Path.GetDirectoryName does not support ".." in the path.
204
+ /// </remarks>
205
+ /// <exception cref="WixFileNotFoundException">Throws WixFileNotFoundException if no file matching the pattern can be found.</exception>
206
+ private string[] GetFiles(string searchPath, string fileType)
207
{
404
- var result = new List<BindPath>();
405
-
406
- foreach (var bindPath in bindPaths)
208
+ if (null == searchPath)
209
{
408
- var bp = ParseBindPath(bindPath);
210
+ throw new ArgumentNullException(nameof(searchPath));
211
+ }
212
410
- if (File.Exists(bp.Path))
213
+ // Convert alternate directory separators to the standard one.
214
+ var filePath = searchPath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
215
+ var lastSeparator = filePath.LastIndexOf(Path.DirectorySeparatorChar);
216
+ var files = new string[0];
217
+
218
+ try
219
+ {
220
+ if (0 > lastSeparator)
221
{
412
- this.Messaging.Write(ErrorMessages.ExpectedDirectoryGotFile("-bindpath", bp.Path));
222
+ files = Directory.GetFiles(".", filePath);
223
}
414
- else
224
+ else // found directory separator
225
{
416
- result.Add(bp);
226
+ files = Directory.GetFiles(filePath.Substring(0, lastSeparator + 1), filePath.Substring(lastSeparator + 1));
227
}
228
}
229
+ catch (DirectoryNotFoundException)
230
+ {
231
+ // Don't let this function throw the DirectoryNotFoundException. This exception
232
+ // occurs for non-existant directories and invalid characters in the searchPattern.
233
+ }
234
+ catch (ArgumentException)
235
+ {
236
+ // Don't let this function throw the ArgumentException. This exception
237
+ // occurs in certain situations such as when passing a malformed UNC path.
238
+ }
239
+ catch (IOException)
240
+ {
241
+ }
242
420
- return result;
243
+ if (0 == files.Length)
244
+ {
245
+ this.Messaging.Write(ErrorMessages.FileNotFound(null, searchPath, fileType));
246
+ }
247
+
248
+ return files;
249
}
250
423
- private bool TryParseCommandLineArgumentWithExtension(string arg, IParseCommandLine parse, IEnumerable<IExtensionCommandLine> extensions)
251
+ private string VerifyPath(string path)
252
{
425
- foreach (var extension in extensions)
253
+ string fullPath;
254
+
255
+ if (0 <= path.IndexOf('\"'))
256
{
427
- if (extension.TryParseArgument(parse, arg))
428
- {
429
- return true;
430
- }
257
+ this.Messaging.Write(ErrorMessages.PathCannotContainQuote(path));
258
+ return null;
259
}
260
433
- return false;
434
- }
261
+ try
262
+ {
263
+ fullPath = Path.GetFullPath(path);
264
+ }
265
+ catch (Exception e)
266
+ {
267
+ this.Messaging.Write(ErrorMessages.InvalidCommandLineFileName(path, e.Message));
268
+ return null;
269
+ }
270
436
- public static BindPath ParseBindPath(string bindPath)
437
- {
438
- var namedPath = bindPath.Split(BindPathSplit, 2);
439
- return (1 == namedPath.Length) ? new BindPath(namedPath[0]) : new BindPath(namedPath[0], namedPath[1]);
271
+ return fullPath;
272
}
273
}
274
}
src/WixToolset.Core/CommandLine/CompileCommand.cs
+17
-1
@@ -1,4 +1,4 @@
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.
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
{
@@ -12,6 +12,13 @@ namespace WixToolset.Core.CommandLine
12
13
internal class CompileCommand : ICommandLineCommand
14
{
15
+ public CompileCommand(IServiceProvider serviceProvider)
16
+ {
17
+ this.ServiceProvider = serviceProvider;
18
+ this.Messaging = serviceProvider.GetService<IMessaging>();
19
+ this.ExtensionManager = serviceProvider.GetService<IExtensionManager>();
20
+ }
21
+
22
public CompileCommand(IServiceProvider serviceProvider, IEnumerable<SourceFile> sources, IDictionary<string, string> preprocessorVariables, Platform platform)
23
{
24
this.ServiceProvider = serviceProvider;
@@ -36,6 +43,15 @@ namespace WixToolset.Core.CommandLine
43
44
public IEnumerable<string> IncludeSearchPaths { get; }
45
46
+ public bool ShowLogo => throw new NotImplementedException();
47
+
48
+ public bool StopParsing => throw new NotImplementedException();
49
+
50
+ public bool TryParseArgument(ICommandLineParser parseHelper, string argument)
51
+ {
52
+ throw new NotImplementedException();
53
+ }
54
+
55
public int Execute()
56
{
57
foreach (var sourceFile in this.SourceFiles)
src/WixToolset.Core/CommandLine/HelpCommand.cs
+9
-13
@@ -4,28 +4,24 @@ namespace WixToolset.Core.CommandLine
4
{
5
using System;
6
using WixToolset.Extensibility.Data;
7
+ using WixToolset.Extensibility.Services;
8
9
internal class HelpCommand : ICommandLineCommand
10
{
10
- public HelpCommand(Commands command)
11
- {
12
- this.Command = command;
13
- }
11
+ public bool ShowLogo => true;
12
15
- public Commands Command { get; }
13
+ public bool StopParsing => true;
14
15
public int Execute()
16
{
19
- if (this.Command == Commands.Unknown)
20
- {
21
- Console.WriteLine();
22
- }
23
- else
24
- {
25
- Console.WriteLine();
26
- }
17
+ Console.WriteLine("TODO: Show list of available commands");
18
19
return -1;
20
}
21
+
22
+ public bool TryParseArgument(ICommandLineParser parseHelper, string argument)
23
+ {
24
+ return true; // eat any arguments
25
+ }
26
}
27
}
src/WixToolset.Core/CommandLine/ParseCommandLine.cs
deleted
-263
@@ -1,263 +0,0 @@
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.Collections.Generic;
7
- using System.IO;
8
- using WixToolset.Data;
9
- using WixToolset.Extensibility.Services;
10
-
11
- internal class ParseCommandLine : IParseCommandLine
12
- {
13
- private const string ExpectedArgument = "expected argument";
14
-
15
- public string ErrorArgument { get; set; }
16
-
17
- private Queue<string> RemainingArguments { get; }
18
-
19
- private IMessaging Messaging { get; }
20
-
21
- public ParseCommandLine(IMessaging messaging, string[] arguments, string errorArgument)
22
- {
23
- this.Messaging = messaging;
24
- this.RemainingArguments = new Queue<string>(arguments);
25
- this.ErrorArgument = errorArgument;
26
- }
27
-
28
- public bool IsSwitch(string arg)
29
- {
30
- return !String.IsNullOrEmpty(arg) && ('/' == arg[0] || '-' == arg[0]);
31
- }
32
-
33
- public void GetArgumentAsFilePathOrError(string argument, string fileType, IList<string> paths)
34
- {
35
- foreach (var path in this.GetFiles(argument, fileType))
36
- {
37
- paths.Add(path);
38
- }
39
- }
40
-
41
- public string GetNextArgumentOrError(string commandLineSwitch)
42
- {
43
- if (this.TryGetNextNonSwitchArgumentOrError(out var argument))
44
- {
45
- return argument;
46
- }
47
-
48
- this.Messaging.Write(ErrorMessages.ExpectedArgument(commandLineSwitch));
49
- return null;
50
- }
51
-
52
- public bool GetNextArgumentOrError(string commandLineSwitch, IList<string> args)
53
- {
54
- if (this.TryGetNextNonSwitchArgumentOrError(out var arg))
55
- {
56
- args.Add(arg);
57
- return true;
58
- }
59
-
60
- this.Messaging.Write(ErrorMessages.ExpectedArgument(commandLineSwitch));
61
- return false;
62
- }
63
-
64
- public string GetNextArgumentAsDirectoryOrError(string commandLineSwitch)
65
- {
66
- if (this.TryGetNextNonSwitchArgumentOrError(out var arg) && this.TryGetDirectory(commandLineSwitch, this.Messaging, arg, out var directory))
67
- {
68
- return directory;
69
- }
70
-
71
- this.Messaging.Write(ErrorMessages.ExpectedArgument(commandLineSwitch));
72
- return null;
73
- }
74
-
75
- public bool GetNextArgumentAsDirectoryOrError(string commandLineSwitch, IList<string> directories)
76
- {
77
- if (this.TryGetNextNonSwitchArgumentOrError(out var arg) && this.TryGetDirectory(commandLineSwitch, this.Messaging, arg, out var directory))
78
- {
79
- directories.Add(directory);
80
- return true;
81
- }
82
-
83
- this.Messaging.Write(ErrorMessages.ExpectedArgument(commandLineSwitch));
84
- return false;
85
- }
86
-
87
- public string GetNextArgumentAsFilePathOrError(string commandLineSwitch)
88
- {
89
- if (this.TryGetNextNonSwitchArgumentOrError(out var arg) && this.TryGetFile(commandLineSwitch, arg, out var path))
90
- {
91
- return path;
92
- }
93
-
94
- this.Messaging.Write(ErrorMessages.ExpectedArgument(commandLineSwitch));
95
- return null;
96
- }
97
-
98
- public bool GetNextArgumentAsFilePathOrError(string commandLineSwitch, string fileType, IList<string> paths)
99
- {
100
- if (this.TryGetNextNonSwitchArgumentOrError(out var arg))
101
- {
102
- foreach (var path in this.GetFiles(arg, fileType))
103
- {
104
- paths.Add(path);
105
- }
106
-
107
- return true;
108
- }
109
-
110
- this.Messaging.Write(ErrorMessages.ExpectedArgument(commandLineSwitch));
111
- return false;
112
- }
113
-
114
- public bool TryGetNextSwitchOrArgument(out string arg)
115
- {
116
- return TryDequeue(this.RemainingArguments, out arg);
117
- }
118
-
119
- private bool TryGetNextNonSwitchArgumentOrError(out string arg)
120
- {
121
- var result = this.TryGetNextSwitchOrArgument(out arg);
122
-
123
- if (!result && !this.IsSwitch(arg))
124
- {
125
- this.ErrorArgument = arg ?? ParseCommandLine.ExpectedArgument;
126
- }
127
-
128
- return result;
129
- }
130
-
131
- private static bool IsValidArg(string arg)
132
- {
133
- return !(String.IsNullOrEmpty(arg) || '/' == arg[0] || '-' == arg[0]);
134
- }
135
-
136
- private static bool TryDequeue(Queue<string> q, out string arg)
137
- {
138
- if (q.Count > 0)
139
- {
140
- arg = q.Dequeue();
141
- return true;
142
- }
143
-
144
- arg = null;
145
- return false;
146
- }
147
-
148
- private bool TryGetDirectory(string commandlineSwitch, IMessaging messageHandler, string arg, out string directory)
149
- {
150
- directory = null;
151
-
152
- if (File.Exists(arg))
153
- {
154
- this.Messaging.Write(ErrorMessages.ExpectedDirectoryGotFile(commandlineSwitch, arg));
155
- return false;
156
- }
157
-
158
- directory = this.VerifyPath(arg);
159
- return directory != null;
160
- }
161
-
162
- private bool TryGetFile(string commandlineSwitch, string arg, out string path)
163
- {
164
- path = null;
165
-
166
- if (!IsValidArg(arg))
167
- {
168
- this.Messaging.Write(ErrorMessages.FilePathRequired(commandlineSwitch));
169
- }
170
- else if (Directory.Exists(arg))
171
- {
172
- this.Messaging.Write(ErrorMessages.ExpectedFileGotDirectory(commandlineSwitch, arg));
173
- }
174
- else
175
- {
176
- path = this.VerifyPath(arg);
177
- }
178
-
179
- return path != null;
180
- }
181
-
182
- /// <summary>
183
- /// Get a set of files that possibly have a search pattern in the path (such as '*').
184
- /// </summary>
185
- /// <param name="searchPath">Search path to find files in.</param>
186
- /// <param name="fileType">Type of file; typically "Source".</param>
187
- /// <returns>An array of files matching the search path.</returns>
188
- /// <remarks>
189
- /// This method is written in this verbose way because it needs to support ".." in the path.
190
- /// It needs the directory path isolated from the file name in order to use Directory.GetFiles
191
- /// or DirectoryInfo.GetFiles. The only way to get this directory path is manually since
192
- /// Path.GetDirectoryName does not support ".." in the path.
193
- /// </remarks>
194
- /// <exception cref="WixFileNotFoundException">Throws WixFileNotFoundException if no file matching the pattern can be found.</exception>
195
- private string[] GetFiles(string searchPath, string fileType)
196
- {
197
- if (null == searchPath)
198
- {
199
- throw new ArgumentNullException(nameof(searchPath));
200
- }
201
-
202
- // Convert alternate directory separators to the standard one.
203
- var filePath = searchPath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
204
- var lastSeparator = filePath.LastIndexOf(Path.DirectorySeparatorChar);
205
- var files = new string[0];
206
-
207
- try
208
- {
209
- if (0 > lastSeparator)
210
- {
211
- files = Directory.GetFiles(".", filePath);
212
- }
213
- else // found directory separator
214
- {
215
- files = Directory.GetFiles(filePath.Substring(0, lastSeparator + 1), filePath.Substring(lastSeparator + 1));
216
- }
217
- }
218
- catch (DirectoryNotFoundException)
219
- {
220
- // Don't let this function throw the DirectoryNotFoundException. This exception
221
- // occurs for non-existant directories and invalid characters in the searchPattern.
222
- }
223
- catch (ArgumentException)
224
- {
225
- // Don't let this function throw the ArgumentException. This exception
226
- // occurs in certain situations such as when passing a malformed UNC path.
227
- }
228
- catch (IOException)
229
- {
230
- }
231
-
232
- if (0 == files.Length)
233
- {
234
- this.Messaging.Write(ErrorMessages.FileNotFound(null, searchPath, fileType));
235
- }
236
-
237
- return files;
238
- }
239
-
240
- private string VerifyPath(string path)
241
- {
242
- string fullPath;
243
-
244
- if (0 <= path.IndexOf('\"'))
245
- {
246
- this.Messaging.Write(ErrorMessages.PathCannotContainQuote(path));
247
- return null;
248
- }
249
-
250
- try
251
- {
252
- fullPath = Path.GetFullPath(path);
253
- }
254
- catch (Exception e)
255
- {
256
- this.Messaging.Write(ErrorMessages.InvalidCommandLineFileName(path, e.Message));
257
- return null;
258
- }
259
-
260
- return fullPath;
261
- }
262
- }
263
-}
src/WixToolset.Core/CommandLine/VersionCommand.cs
+10
@@ -4,9 +4,14 @@ namespace WixToolset.Core.CommandLine
4
{
5
using System;
6
using WixToolset.Extensibility.Data;
7
+ using WixToolset.Extensibility.Services;
8
9
internal class VersionCommand : ICommandLineCommand
10
{
11
+ public bool ShowLogo => true;
12
+
13
+ public bool StopParsing => true;
14
+
15
public int Execute()
16
{
17
Console.WriteLine("wix version {0}", ThisAssembly.AssemblyInformationalVersion);
@@ -14,5 +19,10 @@ namespace WixToolset.Core.CommandLine
19
20
return 0;
21
}
22
+
23
+ public bool TryParseArgument(ICommandLineParser parseHelper, string argument)
24
+ {
25
+ return true; // eat any arguments
26
+ }
27
}
28
}
src/WixToolset.Core/WixToolsetServiceProvider.cs
+2
-2
@@ -1,4 +1,4 @@
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.
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
4
{
@@ -29,7 +29,7 @@ namespace WixToolset.Core
29
// Transients.
30
this.AddService<ICommandLineArguments>((provider, singletons) => new CommandLineArguments(provider));
31
this.AddService<ICommandLineContext>((provider, singletons) => new CommandLineContext(provider));
32
- this.AddService<ICommandLineParser>((provider, singletons) => new CommandLineParser(provider));
32
+ this.AddService<ICommandLine>((provider, singletons) => new CommandLine.CommandLine(provider));
33
this.AddService<IPreprocessContext>((provider, singletons) => new PreprocessContext(provider));
34
this.AddService<ICompileContext>((provider, singletons) => new CompileContext(provider));
35
this.AddService<ILibraryContext>((provider, singletons) => new LibraryContext(provider));
src/test/Example.Extension/ExamplePreprocessorExtensionAndCommandLine.cs
+10
-4
@@ -1,4 +1,4 @@
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.
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 Example.Extension
4
{
@@ -23,17 +23,23 @@ namespace Example.Extension
23
{
24
}
25
26
- public bool TryParseArgument(IParseCommandLine parseCommandLine, string arg)
26
+ public bool TryParseArgument(ICommandLineParser parser, string argument)
27
{
28
- if (parseCommandLine.IsSwitch(arg) && arg.Substring(1).Equals("example", StringComparison.OrdinalIgnoreCase))
28
+ if (parser.IsSwitch(argument) && argument.Substring(1).Equals("example", StringComparison.OrdinalIgnoreCase))
29
{
30
- this.exampleValueFromCommandLine = parseCommandLine.GetNextArgumentOrError(arg);
30
+ this.exampleValueFromCommandLine = parser.GetNextArgumentOrError(argument);
31
return true;
32
}
33
34
return false;
35
}
36
37
+ public bool TryParseCommand(ICommandLineParser parser, out ICommandLineCommand command)
38
+ {
39
+ command = null;
40
+ return false;
41
+ }
42
+
43
public void PostParse()
44
{
45
}