diff options
-rw-r--r-- | src/WixToolset.Core.TestPackage/WixRunner.cs | 2 | ||||
-rw-r--r-- | src/WixToolset.Core/CommandLine/BuildCommand.cs | 478 | ||||
-rw-r--r-- | src/WixToolset.Core/CommandLine/CommandLine.cs | 210 | ||||
-rw-r--r-- | src/WixToolset.Core/CommandLine/CommandLineArguments.cs | 4 | ||||
-rw-r--r-- | src/WixToolset.Core/CommandLine/CommandLineParser.cs | 514 | ||||
-rw-r--r-- | src/WixToolset.Core/CommandLine/CompileCommand.cs | 18 | ||||
-rw-r--r-- | src/WixToolset.Core/CommandLine/HelpCommand.cs | 22 | ||||
-rw-r--r-- | src/WixToolset.Core/CommandLine/ParseCommandLine.cs | 263 | ||||
-rw-r--r-- | src/WixToolset.Core/CommandLine/VersionCommand.cs | 10 | ||||
-rw-r--r-- | src/WixToolset.Core/WixToolsetServiceProvider.cs | 4 | ||||
-rw-r--r-- | src/test/Example.Extension/ExamplePreprocessorExtensionAndCommandLine.cs | 14 |
11 files changed, 824 insertions, 715 deletions
diff --git a/src/WixToolset.Core.TestPackage/WixRunner.cs b/src/WixToolset.Core.TestPackage/WixRunner.cs index d7487f6d..ab5045fa 100644 --- a/src/WixToolset.Core.TestPackage/WixRunner.cs +++ b/src/WixToolset.Core.TestPackage/WixRunner.cs | |||
@@ -35,7 +35,7 @@ namespace WixToolset.Core.TestPackage | |||
35 | var arguments = serviceProvider.GetService<ICommandLineArguments>(); | 35 | var arguments = serviceProvider.GetService<ICommandLineArguments>(); |
36 | arguments.Populate(args); | 36 | arguments.Populate(args); |
37 | 37 | ||
38 | var commandLine = serviceProvider.GetService<ICommandLineParser>(); | 38 | var commandLine = serviceProvider.GetService<ICommandLine>(); |
39 | commandLine.ExtensionManager = CreateExtensionManagerWithStandardBackends(serviceProvider, arguments.Extensions); | 39 | commandLine.ExtensionManager = CreateExtensionManagerWithStandardBackends(serviceProvider, arguments.Extensions); |
40 | commandLine.Arguments = arguments; | 40 | commandLine.Arguments = arguments; |
41 | var command = commandLine.ParseStandardCommandLine(); | 41 | var command = commandLine.ParseStandardCommandLine(); |
diff --git a/src/WixToolset.Core/CommandLine/BuildCommand.cs b/src/WixToolset.Core/CommandLine/BuildCommand.cs index 6052d979..87a3cd30 100644 --- a/src/WixToolset.Core/CommandLine/BuildCommand.cs +++ b/src/WixToolset.Core/CommandLine/BuildCommand.cs | |||
@@ -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 | 2 | ||
3 | namespace WixToolset.Core.CommandLine | 3 | namespace WixToolset.Core.CommandLine |
4 | { | 4 | { |
@@ -14,83 +14,84 @@ namespace WixToolset.Core.CommandLine | |||
14 | 14 | ||
15 | internal class BuildCommand : ICommandLineCommand | 15 | internal class BuildCommand : ICommandLineCommand |
16 | { | 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) | ||
18 | { | 20 | { |
19 | this.ServiceProvider = serviceProvider; | 21 | this.ServiceProvider = serviceProvider; |
20 | this.Messaging = serviceProvider.GetService<IMessaging>(); | 22 | this.Messaging = serviceProvider.GetService<IMessaging>(); |
21 | this.ExtensionManager = serviceProvider.GetService<IExtensionManager>(); | 23 | this.ExtensionManager = serviceProvider.GetService<IExtensionManager>(); |
22 | this.LocFiles = locFiles; | 24 | this.commandLine = new CommandLine(this.Messaging); |
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; | ||
40 | } | 25 | } |
41 | 26 | ||
42 | public IServiceProvider ServiceProvider { get; } | 27 | public bool ShowLogo => this.commandLine.ShowLogo; |
43 | 28 | ||
44 | public IMessaging Messaging { get; } | 29 | public bool StopParsing => this.commandLine.ShowHelp; |
45 | 30 | ||
46 | public IExtensionManager ExtensionManager { get; } | 31 | private IServiceProvider ServiceProvider { get; } |
47 | 32 | ||
48 | public IEnumerable<string> FilterCultures { get; } | 33 | private IMessaging Messaging { get; } |
49 | 34 | ||
50 | public IEnumerable<string> IncludeSearchPaths { get; } | 35 | private IExtensionManager ExtensionManager { get; } |
51 | 36 | ||
52 | public IEnumerable<string> LocFiles { get; } | 37 | private string IntermediateFolder { get; set; } |
53 | 38 | ||
54 | public IEnumerable<string> LibraryFiles { get; } | 39 | private OutputType OutputType { get; set; } |
55 | 40 | ||
56 | private IEnumerable<SourceFile> SourceFiles { get; } | 41 | private List<string> IncludeSearchPaths { get; set; } |
57 | 42 | ||
58 | private IDictionary<string, string> PreprocessorVariables { get; } | 43 | private Platform Platform { get; set; } |
59 | 44 | ||
60 | private string OutputPath { get; } | 45 | private string OutputFile { get; set; } |
61 | 46 | ||
62 | private OutputType OutputType { get; } | 47 | private string ContentsFile { get; set; } |
63 | 48 | ||
64 | private Platform Platform { get; } | 49 | private string OutputsFile { get; set; } |
65 | 50 | ||
66 | public string CabCachePath { get; } | 51 | private string BuiltOutputsFile { get; set; } |
67 | 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 | } | ||
69 | 60 | ||
70 | public IEnumerable<BindPath> BindPaths { get; } | 61 | this.IntermediateFolder = this.commandLine.CalculateIntermedateFolder(); |
71 | 62 | ||
72 | public string IntermediateFolder { get; } | 63 | this.OutputType = this.commandLine.CalculateOutputType(); |
73 | 64 | ||
74 | public string ContentsFile { get; } | 65 | this.IncludeSearchPaths = this.commandLine.IncludeSearchPaths; |
75 | 66 | ||
76 | public string OutputsFile { get; } | 67 | this.Platform = this.commandLine.Platform; |
77 | 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(); | ||
79 | 82 | ||
80 | public int Execute() | ||
81 | { | ||
82 | var creator = this.ServiceProvider.GetService<ITupleDefinitionCreator>(); | 83 | var creator = this.ServiceProvider.GetService<ITupleDefinitionCreator>(); |
83 | 84 | ||
84 | this.EvaluateSourceFiles(creator, out var codeFiles, out var wixipl); | 85 | this.EvaluateSourceFiles(sourceFiles, creator, out var codeFiles, out var wixipl); |
85 | 86 | ||
86 | if (this.Messaging.EncounteredError) | 87 | if (this.Messaging.EncounteredError) |
87 | { | 88 | { |
88 | return this.Messaging.LastErrorNumber; | 89 | return this.Messaging.LastErrorNumber; |
89 | } | 90 | } |
90 | 91 | ||
91 | var wixobjs = this.CompilePhase(codeFiles); | 92 | var wixobjs = this.CompilePhase(preprocessorVariables, codeFiles); |
92 | 93 | ||
93 | var wxls = this.LoadLocalizationFiles().ToList(); | 94 | var wxls = this.LoadLocalizationFiles(this.commandLine.LocalizationFilePaths, preprocessorVariables); |
94 | 95 | ||
95 | if (this.Messaging.EncounteredError) | 96 | if (this.Messaging.EncounteredError) |
96 | { | 97 | { |
@@ -99,29 +100,29 @@ namespace WixToolset.Core.CommandLine | |||
99 | 100 | ||
100 | if (this.OutputType == OutputType.Library) | 101 | if (this.OutputType == OutputType.Library) |
101 | { | 102 | { |
102 | var wixlib = this.LibraryPhase(wixobjs, wxls); | 103 | var wixlib = this.LibraryPhase(wixobjs, wxls, this.commandLine.BindFiles, this.commandLine.BindPaths); |
103 | 104 | ||
104 | if (!this.Messaging.EncounteredError) | 105 | if (!this.Messaging.EncounteredError) |
105 | { | 106 | { |
106 | wixlib.Save(this.OutputPath); | 107 | wixlib.Save(this.commandLine.OutputFile); |
107 | } | 108 | } |
108 | } | 109 | } |
109 | else | 110 | else |
110 | { | 111 | { |
111 | if (wixipl == null) | 112 | if (wixipl == null) |
112 | { | 113 | { |
113 | wixipl = this.LinkPhase(wixobjs, creator); | 114 | wixipl = this.LinkPhase(wixobjs, this.commandLine.LibraryFilePaths, creator); |
114 | } | 115 | } |
115 | 116 | ||
116 | if (!this.Messaging.EncounteredError) | 117 | if (!this.Messaging.EncounteredError) |
117 | { | 118 | { |
118 | if (this.OutputType == OutputType.IntermediatePostLink) | 119 | if (this.OutputType == OutputType.IntermediatePostLink) |
119 | { | 120 | { |
120 | wixipl.Save(this.OutputPath); | 121 | wixipl.Save(this.commandLine.OutputFile); |
121 | } | 122 | } |
122 | else | 123 | else |
123 | { | 124 | { |
124 | this.BindPhase(wixipl, wxls); | 125 | this.BindPhase(wixipl, wxls, filterCultures, this.commandLine.CabCachePath, this.commandLine.BindPaths); |
125 | } | 126 | } |
126 | } | 127 | } |
127 | } | 128 | } |
@@ -129,13 +130,18 @@ namespace WixToolset.Core.CommandLine | |||
129 | return this.Messaging.LastErrorNumber; | 130 | return this.Messaging.LastErrorNumber; |
130 | } | 131 | } |
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) | ||
133 | { | 139 | { |
134 | codeFiles = new List<SourceFile>(); | 140 | codeFiles = new List<SourceFile>(); |
135 | 141 | ||
136 | wixipl = null; | 142 | wixipl = null; |
137 | 143 | ||
138 | foreach (var sourceFile in this.SourceFiles) | 144 | foreach (var sourceFile in sourceFiles) |
139 | { | 145 | { |
140 | var extension = Path.GetExtension(sourceFile.SourcePath); | 146 | var extension = Path.GetExtension(sourceFile.SourcePath); |
141 | 147 | ||
@@ -167,13 +173,13 @@ namespace WixToolset.Core.CommandLine | |||
167 | } | 173 | } |
168 | } | 174 | } |
169 | 175 | ||
170 | private IEnumerable<Intermediate> CompilePhase(IEnumerable<SourceFile> sourceFiles) | 176 | private IEnumerable<Intermediate> CompilePhase(IDictionary<string, string> preprocessorVariables, IEnumerable<SourceFile> sourceFiles) |
171 | { | 177 | { |
172 | var intermediates = new List<Intermediate>(); | 178 | var intermediates = new List<Intermediate>(); |
173 | 179 | ||
174 | foreach (var sourceFile in sourceFiles) | 180 | foreach (var sourceFile in sourceFiles) |
175 | { | 181 | { |
176 | var document = this.Preprocess(sourceFile.SourcePath); | 182 | var document = this.Preprocess(preprocessorVariables, sourceFile.SourcePath); |
177 | 183 | ||
178 | if (this.Messaging.EncounteredError) | 184 | if (this.Messaging.EncounteredError) |
179 | { | 185 | { |
@@ -208,11 +214,11 @@ namespace WixToolset.Core.CommandLine | |||
208 | return intermediates; | 214 | return intermediates; |
209 | } | 215 | } |
210 | 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) |
212 | { | 218 | { |
213 | var context = this.ServiceProvider.GetService<ILibraryContext>(); | 219 | var context = this.ServiceProvider.GetService<ILibraryContext>(); |
214 | context.BindFiles = this.BindFiles; | 220 | context.BindFiles = bindFiles; |
215 | context.BindPaths = this.BindPaths; | 221 | context.BindPaths = bindPaths; |
216 | context.Extensions = this.ExtensionManager.Create<ILibrarianExtension>(); | 222 | context.Extensions = this.ExtensionManager.Create<ILibrarianExtension>(); |
217 | context.Localizations = localizations; | 223 | context.Localizations = localizations; |
218 | context.Intermediates = intermediates; | 224 | context.Intermediates = intermediates; |
@@ -230,10 +236,10 @@ namespace WixToolset.Core.CommandLine | |||
230 | 236 | ||
231 | return library; | 237 | return library; |
232 | } | 238 | } |
233 | 239 | ||
234 | private Intermediate LinkPhase(IEnumerable<Intermediate> intermediates, ITupleDefinitionCreator creator) | 240 | private Intermediate LinkPhase(IEnumerable<Intermediate> intermediates, IEnumerable<string> libraryFiles, ITupleDefinitionCreator creator) |
235 | { | 241 | { |
236 | var libraries = this.LoadLibraries(creator); | 242 | var libraries = this.LoadLibraries(libraryFiles, creator); |
237 | 243 | ||
238 | if (this.Messaging.EncounteredError) | 244 | if (this.Messaging.EncounteredError) |
239 | { | 245 | { |
@@ -251,7 +257,7 @@ namespace WixToolset.Core.CommandLine | |||
251 | return linker.Link(context); | 257 | return linker.Link(context); |
252 | } | 258 | } |
253 | 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) |
255 | { | 261 | { |
256 | var intermediateFolder = this.IntermediateFolder; | 262 | var intermediateFolder = this.IntermediateFolder; |
257 | if (String.IsNullOrEmpty(intermediateFolder)) | 263 | if (String.IsNullOrEmpty(intermediateFolder)) |
@@ -262,10 +268,10 @@ namespace WixToolset.Core.CommandLine | |||
262 | ResolveResult resolveResult; | 268 | ResolveResult resolveResult; |
263 | { | 269 | { |
264 | var context = this.ServiceProvider.GetService<IResolveContext>(); | 270 | var context = this.ServiceProvider.GetService<IResolveContext>(); |
265 | context.BindPaths = this.BindPaths; | 271 | context.BindPaths = bindPaths; |
266 | context.Extensions = this.ExtensionManager.Create<IResolverExtension>(); | 272 | context.Extensions = this.ExtensionManager.Create<IResolverExtension>(); |
267 | context.ExtensionData = this.ExtensionManager.Create<IExtensionData>(); | 273 | context.ExtensionData = this.ExtensionManager.Create<IExtensionData>(); |
268 | context.FilterCultures = this.FilterCultures; | 274 | context.FilterCultures = filterCultures; |
269 | context.IntermediateFolder = intermediateFolder; | 275 | context.IntermediateFolder = intermediateFolder; |
270 | context.IntermediateRepresentation = output; | 276 | context.IntermediateRepresentation = output; |
271 | context.Localizations = localizations; | 277 | context.Localizations = localizations; |
@@ -284,7 +290,7 @@ namespace WixToolset.Core.CommandLine | |||
284 | { | 290 | { |
285 | var context = this.ServiceProvider.GetService<IBindContext>(); | 291 | var context = this.ServiceProvider.GetService<IBindContext>(); |
286 | //context.CabbingThreadCount = this.CabbingThreadCount; | 292 | //context.CabbingThreadCount = this.CabbingThreadCount; |
287 | context.CabCachePath = this.CabCachePath; | 293 | context.CabCachePath = cabCachePath; |
288 | context.Codepage = resolveResult.Codepage; | 294 | context.Codepage = resolveResult.Codepage; |
289 | //context.DefaultCompressionLevel = this.DefaultCompressionLevel; | 295 | //context.DefaultCompressionLevel = this.DefaultCompressionLevel; |
290 | context.DelayedFields = resolveResult.DelayedFields; | 296 | context.DelayedFields = resolveResult.DelayedFields; |
@@ -293,8 +299,8 @@ namespace WixToolset.Core.CommandLine | |||
293 | context.Ices = Array.Empty<string>(); // TODO: set this correctly | 299 | context.Ices = Array.Empty<string>(); // TODO: set this correctly |
294 | context.IntermediateFolder = intermediateFolder; | 300 | context.IntermediateFolder = intermediateFolder; |
295 | context.IntermediateRepresentation = resolveResult.IntermediateRepresentation; | 301 | context.IntermediateRepresentation = resolveResult.IntermediateRepresentation; |
296 | context.OutputPath = this.OutputPath; | 302 | context.OutputPath = this.OutputFile; |
297 | context.OutputPdbPath = Path.ChangeExtension(this.OutputPath, ".wixpdb"); | 303 | context.OutputPdbPath = Path.ChangeExtension(this.OutputFile, ".wixpdb"); |
298 | context.SuppressIces = Array.Empty<string>(); // TODO: set this correctly | 304 | context.SuppressIces = Array.Empty<string>(); // TODO: set this correctly |
299 | context.SuppressValidation = true; // TODO: set this correctly | 305 | context.SuppressValidation = true; // TODO: set this correctly |
300 | 306 | ||
@@ -323,41 +329,39 @@ namespace WixToolset.Core.CommandLine | |||
323 | } | 329 | } |
324 | } | 330 | } |
325 | 331 | ||
326 | private IEnumerable<Intermediate> LoadLibraries(ITupleDefinitionCreator creator) | 332 | private IEnumerable<Intermediate> LoadLibraries(IEnumerable<string> libraryFiles, ITupleDefinitionCreator creator) |
327 | { | 333 | { |
328 | var libraries = new List<Intermediate>(); | 334 | var libraries = new List<Intermediate>(); |
329 | 335 | ||
330 | if (this.LibraryFiles != null) | 336 | foreach (var libraryFile in libraryFiles) |
331 | { | 337 | { |
332 | foreach (var libraryFile in this.LibraryFiles) | 338 | try |
333 | { | 339 | { |
334 | try | 340 | var library = Intermediate.Load(libraryFile, creator); |
335 | { | ||
336 | var library = Intermediate.Load(libraryFile, creator); | ||
337 | 341 | ||
338 | libraries.Add(library); | 342 | libraries.Add(library); |
339 | } | 343 | } |
340 | catch (WixCorruptFileException e) | 344 | catch (WixCorruptFileException e) |
341 | { | 345 | { |
342 | this.Messaging.Write(e.Error); | 346 | this.Messaging.Write(e.Error); |
343 | } | 347 | } |
344 | catch (WixUnexpectedFileFormatException e) | 348 | catch (WixUnexpectedFileFormatException e) |
345 | { | 349 | { |
346 | this.Messaging.Write(e.Error); | 350 | this.Messaging.Write(e.Error); |
347 | } | ||
348 | } | 351 | } |
349 | } | 352 | } |
350 | 353 | ||
351 | return libraries; | 354 | return libraries; |
352 | } | 355 | } |
353 | 356 | ||
354 | private IEnumerable<Localization> LoadLocalizationFiles() | 357 | private IEnumerable<Localization> LoadLocalizationFiles(IEnumerable<string> locFiles, IDictionary<string, string> preprocessorVariables) |
355 | { | 358 | { |
356 | var localizer = new Localizer(this.ServiceProvider); | 359 | var localizations = new List<Localization>(); |
360 | var localizer = this.ServiceProvider.GetService<ILocalizer>(); | ||
357 | 361 | ||
358 | foreach (var loc in this.LocFiles) | 362 | foreach (var loc in locFiles) |
359 | { | 363 | { |
360 | var document = this.Preprocess(loc); | 364 | var document = this.Preprocess(preprocessorVariables, loc); |
361 | 365 | ||
362 | if (this.Messaging.EncounteredError) | 366 | if (this.Messaging.EncounteredError) |
363 | { | 367 | { |
@@ -365,18 +369,20 @@ namespace WixToolset.Core.CommandLine | |||
365 | } | 369 | } |
366 | 370 | ||
367 | var localization = localizer.ParseLocalizationFile(document); | 371 | var localization = localizer.ParseLocalizationFile(document); |
368 | yield return localization; | 372 | localizations.Add(localization); |
369 | } | 373 | } |
374 | |||
375 | return localizations; | ||
370 | } | 376 | } |
371 | 377 | ||
372 | private XDocument Preprocess(string sourcePath) | 378 | private XDocument Preprocess(IDictionary<string, string> preprocessorVariables, string sourcePath) |
373 | { | 379 | { |
374 | var context = this.ServiceProvider.GetService<IPreprocessContext>(); | 380 | var context = this.ServiceProvider.GetService<IPreprocessContext>(); |
375 | context.Extensions = this.ExtensionManager.Create<IPreprocessorExtension>(); | 381 | context.Extensions = this.ExtensionManager.Create<IPreprocessorExtension>(); |
376 | context.Platform = this.Platform; | 382 | context.Platform = this.Platform; |
377 | context.IncludeSearchPaths = this.IncludeSearchPaths; | 383 | context.IncludeSearchPaths = this.IncludeSearchPaths; |
378 | context.SourcePath = sourcePath; | 384 | context.SourcePath = sourcePath; |
379 | context.Variables = this.PreprocessorVariables; | 385 | context.Variables = preprocessorVariables; |
380 | 386 | ||
381 | XDocument document = null; | 387 | XDocument document = null; |
382 | try | 388 | try |
@@ -391,5 +397,301 @@ namespace WixToolset.Core.CommandLine | |||
391 | 397 | ||
392 | return document; | 398 | return document; |
393 | } | 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 | } | ||
394 | } | 696 | } |
395 | } | 697 | } |
diff --git a/src/WixToolset.Core/CommandLine/CommandLine.cs b/src/WixToolset.Core/CommandLine/CommandLine.cs new file mode 100644 index 00000000..9aefc50a --- /dev/null +++ b/src/WixToolset.Core/CommandLine/CommandLine.cs | |||
@@ -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 | } | ||
diff --git a/src/WixToolset.Core/CommandLine/CommandLineArguments.cs b/src/WixToolset.Core/CommandLine/CommandLineArguments.cs index 2f8226df..5fa547b4 100644 --- a/src/WixToolset.Core/CommandLine/CommandLineArguments.cs +++ b/src/WixToolset.Core/CommandLine/CommandLineArguments.cs | |||
@@ -41,11 +41,11 @@ namespace WixToolset.Core.CommandLine | |||
41 | this.ProcessArgumentsAndParseExtensions(this.OriginalArguments); | 41 | this.ProcessArgumentsAndParseExtensions(this.OriginalArguments); |
42 | } | 42 | } |
43 | 43 | ||
44 | public IParseCommandLine Parse() | 44 | public ICommandLineParser Parse() |
45 | { | 45 | { |
46 | var messaging = (IMessaging)this.ServiceProvider.GetService(typeof(IMessaging)); | 46 | var messaging = (IMessaging)this.ServiceProvider.GetService(typeof(IMessaging)); |
47 | 47 | ||
48 | return new ParseCommandLine(messaging, this.Arguments, this.ErrorArgument); | 48 | return new CommandLineParser(messaging, this.Arguments, this.ErrorArgument); |
49 | } | 49 | } |
50 | 50 | ||
51 | private void FlattenArgumentsWithResponseFilesIntoOriginalArguments(string[] commandLineArguments) | 51 | private void FlattenArgumentsWithResponseFilesIntoOriginalArguments(string[] commandLineArguments) |
diff --git a/src/WixToolset.Core/CommandLine/CommandLineParser.cs b/src/WixToolset.Core/CommandLine/CommandLineParser.cs index d0484e45..11e5751d 100644 --- a/src/WixToolset.Core/CommandLine/CommandLineParser.cs +++ b/src/WixToolset.Core/CommandLine/CommandLineParser.cs | |||
@@ -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 | 2 | ||
3 | namespace WixToolset.Core.CommandLine | 3 | namespace WixToolset.Core.CommandLine |
4 | { | 4 | { |
@@ -6,437 +6,269 @@ namespace WixToolset.Core.CommandLine | |||
6 | using System.Collections.Generic; | 6 | using System.Collections.Generic; |
7 | using System.IO; | 7 | using System.IO; |
8 | using WixToolset.Data; | 8 | using WixToolset.Data; |
9 | using WixToolset.Extensibility; | ||
10 | using WixToolset.Extensibility.Data; | ||
11 | using WixToolset.Extensibility.Services; | 9 | using WixToolset.Extensibility.Services; |
12 | 10 | ||
13 | internal enum Commands | ||
14 | { | ||
15 | Unknown, | ||
16 | Build, | ||
17 | Preprocess, | ||
18 | Compile, | ||
19 | Link, | ||
20 | Bind, | ||
21 | } | ||
22 | |||
23 | internal class CommandLineParser : ICommandLineParser | 11 | internal class CommandLineParser : ICommandLineParser |
24 | { | 12 | { |
25 | private static readonly char[] BindPathSplit = { '=' }; | 13 | private const string ExpectedArgument = "expected argument"; |
26 | |||
27 | public CommandLineParser(IServiceProvider serviceProvider) | ||
28 | { | ||
29 | this.ServiceProvider = serviceProvider; | ||
30 | |||
31 | this.Messaging = this.ServiceProvider.GetService<IMessaging>(); | ||
32 | } | ||
33 | 14 | ||
34 | private IServiceProvider ServiceProvider { get; } | 15 | public string ErrorArgument { get; set; } |
35 | 16 | ||
36 | private IMessaging Messaging { get; set; } | 17 | private Queue<string> RemainingArguments { get; } |
37 | 18 | ||
38 | public IExtensionManager ExtensionManager { get; set; } | 19 | private IMessaging Messaging { get; } |
39 | 20 | ||
40 | public ICommandLineArguments Arguments { get; set; } | 21 | public CommandLineParser(IMessaging messaging, string[] arguments, string errorArgument) |
41 | 22 | { | |
42 | public static string ExpectedArgument { get; } = "expected argument"; | 23 | this.Messaging = messaging; |
43 | 24 | this.RemainingArguments = new Queue<string>(arguments); | |
44 | public string ActiveCommand { get; private set; } | 25 | this.ErrorArgument = errorArgument; |
26 | } | ||
45 | 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 | } | ||
47 | 32 | ||
48 | public ICommandLineCommand ParseStandardCommandLine() | 33 | public string GetArgumentAsFilePathOrError(string argument, string fileType) |
49 | { | 34 | { |
50 | var context = this.ServiceProvider.GetService<ICommandLineContext>(); | 35 | if (!File.Exists(argument)) |
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) => | ||
83 | { | 36 | { |
84 | if (parser.IsSwitch(arg)) | 37 | this.Messaging.Write(ErrorMessages.FileNotFound(null, argument, fileType)); |
85 | { | 38 | return null; |
86 | var parameter = arg.Substring(1); | 39 | } |
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 | }); | ||
193 | 40 | ||
194 | this.Messaging.ShowVerboseMessages = verbose; | 41 | return argument; |
42 | } | ||
195 | 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)) | ||
197 | { | 47 | { |
198 | return new VersionCommand(); | 48 | paths.Add(path); |
199 | } | 49 | } |
50 | } | ||
200 | 51 | ||
201 | if (showLogo) | 52 | public string GetNextArgumentOrError(string commandLineSwitch) |
53 | { | ||
54 | if (this.TryGetNextNonSwitchArgumentOrError(out var argument)) | ||
202 | { | 55 | { |
203 | AppCommon.DisplayToolHeader(); | 56 | return argument; |
204 | } | 57 | } |
205 | 58 | ||
206 | if (this.ShowHelp) | 59 | this.Messaging.Write(ErrorMessages.ExpectedArgument(commandLineSwitch)); |
207 | { | 60 | return null; |
208 | return new HelpCommand(command); | 61 | } |
209 | } | ||
210 | 62 | ||
211 | switch (command) | 63 | public bool GetNextArgumentOrError(string commandLineSwitch, IList<string> args) |
212 | { | 64 | { |
213 | case Commands.Build: | 65 | if (this.TryGetNextNonSwitchArgumentOrError(out var arg)) |
214 | { | 66 | { |
215 | var sourceFiles = GatherSourceFiles(files, outputFolder); | 67 | args.Add(arg); |
216 | var variables = this.GatherPreprocessorVariables(defines); | 68 | return true; |
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); | ||
222 | } | 69 | } |
223 | 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)) | ||
225 | { | 78 | { |
226 | var sourceFiles = GatherSourceFiles(files, outputFolder); | 79 | return directory; |
227 | var variables = this.GatherPreprocessorVariables(defines); | ||
228 | var platform = CalculatePlatform(platformType); | ||
229 | return new CompileCommand(this.ServiceProvider, sourceFiles, variables, platform); | ||
230 | } | ||
231 | } | 80 | } |
232 | 81 | ||
82 | this.Messaging.Write(ErrorMessages.ExpectedArgument(commandLineSwitch)); | ||
233 | return null; | 83 | return null; |
234 | } | 84 | } |
235 | 85 | ||
236 | private static IEnumerable<string> CalculateFilterCultures(List<string> cultures) | 86 | public bool GetNextArgumentAsDirectoryOrError(string commandLineSwitch, IList<string> directories) |
237 | { | 87 | { |
238 | var result = new List<string>(); | 88 | if (this.TryGetNextNonSwitchArgumentOrError(out var arg) && this.TryGetDirectory(commandLineSwitch, this.Messaging, arg, out var directory)) |
239 | |||
240 | if (cultures == null) | ||
241 | { | ||
242 | } | ||
243 | else if (cultures.Count == 1 && cultures[0].Equals("null", StringComparison.OrdinalIgnoreCase)) | ||
244 | { | 89 | { |
245 | // When null is used treat it as if cultures wasn't specified. This is | 90 | directories.Add(directory); |
246 | // needed for batching in the MSBuild task since MSBuild doesn't support | 91 | return true; |
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 | } | ||
258 | } | 92 | } |
259 | 93 | ||
260 | return result; | 94 | this.Messaging.Write(ErrorMessages.ExpectedArgument(commandLineSwitch)); |
95 | return false; | ||
261 | } | 96 | } |
262 | 97 | ||
263 | private static OutputType CalculateOutputType(string outputType, string outputFile) | 98 | public string GetNextArgumentAsFilePathOrError(string commandLineSwitch) |
264 | { | 99 | { |
265 | if (String.IsNullOrEmpty(outputType)) | 100 | if (this.TryGetNextNonSwitchArgumentOrError(out var arg) && this.TryGetFile(commandLineSwitch, arg, out var path)) |
266 | { | 101 | { |
267 | outputType = Path.GetExtension(outputFile); | 102 | return path; |
268 | } | 103 | } |
269 | 104 | ||
270 | switch (outputType.ToLowerInvariant()) | 105 | this.Messaging.Write(ErrorMessages.ExpectedArgument(commandLineSwitch)); |
271 | { | 106 | return null; |
272 | case "bundle": | 107 | } |
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; | ||
295 | 108 | ||
296 | case "transform": | 109 | public bool GetNextArgumentAsFilePathOrError(string commandLineSwitch, string fileType, IList<string> paths) |
297 | case ".mst": | 110 | { |
298 | return OutputType.Transform; | 111 | if (this.TryGetNextNonSwitchArgumentOrError(out var arg)) |
112 | { | ||
113 | foreach (var path in this.GetFiles(arg, fileType)) | ||
114 | { | ||
115 | paths.Add(path); | ||
116 | } | ||
299 | 117 | ||
300 | case "intermediatepostlink": | 118 | return true; |
301 | case ".wixipl": | ||
302 | return OutputType.IntermediatePostLink; | ||
303 | } | 119 | } |
304 | 120 | ||
305 | return OutputType.Unknown; | 121 | this.Messaging.Write(ErrorMessages.ExpectedArgument(commandLineSwitch)); |
122 | return false; | ||
306 | } | 123 | } |
307 | 124 | ||
308 | private static Platform CalculatePlatform(string platformType) | 125 | public bool TryGetNextSwitchOrArgument(out string arg) |
309 | { | 126 | { |
310 | return Enum.TryParse(platformType, true, out Platform platform) ? platform : Platform.X86; | 127 | return TryDequeue(this.RemainingArguments, out arg); |
311 | } | 128 | } |
312 | 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) |
314 | { | 131 | { |
315 | var extensions = this.ExtensionManager.Create<IExtensionCommandLine>(); | 132 | var result = this.TryGetNextSwitchOrArgument(out arg); |
316 | 133 | ||
317 | foreach (var extension in extensions) | 134 | if (!result && !this.IsSwitch(arg)) |
318 | { | 135 | { |
319 | extension.PreParse(context); | 136 | this.ErrorArgument = arg ?? CommandLineParser.ExpectedArgument; |
320 | } | 137 | } |
321 | 138 | ||
322 | var parser = context.Arguments.Parse(); | 139 | return result; |
323 | 140 | } | |
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 | } | ||
332 | 141 | ||
333 | if (parser.IsSwitch(arg)) | 142 | private static bool IsValidArg(string arg) |
334 | { | 143 | { |
335 | if (!parseArgument(this, parser, arg) && | 144 | return !(String.IsNullOrEmpty(arg) || '/' == arg[0] || '-' == arg[0]); |
336 | !this.TryParseCommandLineArgumentWithExtension(arg, parser, extensions)) | 145 | } |
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 | } | ||
358 | 146 | ||
359 | foreach (var extension in extensions) | 147 | private static bool TryDequeue(Queue<string> q, out string arg) |
148 | { | ||
149 | if (q.Count > 0) | ||
360 | { | 150 | { |
361 | extension.PostParse(); | 151 | arg = q.Dequeue(); |
152 | return true; | ||
362 | } | 153 | } |
363 | 154 | ||
364 | return this; | 155 | arg = null; |
156 | return false; | ||
365 | } | 157 | } |
366 | 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) |
368 | { | 160 | { |
369 | var files = new List<SourceFile>(); | 161 | directory = null; |
370 | 162 | ||
371 | foreach (var item in sourceFiles) | 163 | if (File.Exists(arg)) |
372 | { | 164 | { |
373 | var sourcePath = item; | 165 | this.Messaging.Write(ErrorMessages.ExpectedDirectoryGotFile(commandlineSwitch, arg)); |
374 | var outputPath = Path.Combine(intermediateDirectory, Path.GetFileNameWithoutExtension(sourcePath) + ".wir"); | 166 | return false; |
375 | |||
376 | files.Add(new SourceFile(sourcePath, outputPath)); | ||
377 | } | 167 | } |
378 | 168 | ||
379 | return files; | 169 | directory = this.VerifyPath(arg); |
170 | return directory != null; | ||
380 | } | 171 | } |
381 | 172 | ||
382 | private IDictionary<string, string> GatherPreprocessorVariables(IEnumerable<string> defineConstants) | 173 | private bool TryGetFile(string commandlineSwitch, string arg, out string path) |
383 | { | 174 | { |
384 | var variables = new Dictionary<string, string>(); | 175 | path = null; |
385 | 176 | ||
386 | foreach (var pair in defineConstants) | 177 | if (!IsValidArg(arg)) |
387 | { | 178 | { |
388 | var value = pair.Split(new[] { '=' }, 2); | 179 | this.Messaging.Write(ErrorMessages.FilePathRequired(commandlineSwitch)); |
389 | 180 | } | |
390 | if (variables.ContainsKey(value[0])) | 181 | else if (Directory.Exists(arg)) |
391 | { | 182 | { |
392 | this.Messaging.Write(ErrorMessages.DuplicateVariableDefinition(value[0], (1 == value.Length) ? String.Empty : value[1], variables[value[0]])); | 183 | this.Messaging.Write(ErrorMessages.ExpectedFileGotDirectory(commandlineSwitch, arg)); |
393 | continue; | 184 | } |
394 | } | 185 | else |
395 | 186 | { | |
396 | variables.Add(value[0], (1 == value.Length) ? String.Empty : value[1]); | 187 | path = this.VerifyPath(arg); |
397 | } | 188 | } |
398 | 189 | ||
399 | return variables; | 190 | return path != null; |
400 | } | 191 | } |
401 | 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) | ||
403 | { | 207 | { |
404 | var result = new List<BindPath>(); | 208 | if (null == searchPath) |
405 | |||
406 | foreach (var bindPath in bindPaths) | ||
407 | { | 209 | { |
408 | var bp = ParseBindPath(bindPath); | 210 | throw new ArgumentNullException(nameof(searchPath)); |
211 | } | ||
409 | 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) | ||
411 | { | 221 | { |
412 | this.Messaging.Write(ErrorMessages.ExpectedDirectoryGotFile("-bindpath", bp.Path)); | 222 | files = Directory.GetFiles(".", filePath); |
413 | } | 223 | } |
414 | else | 224 | else // found directory separator |
415 | { | 225 | { |
416 | result.Add(bp); | 226 | files = Directory.GetFiles(filePath.Substring(0, lastSeparator + 1), filePath.Substring(lastSeparator + 1)); |
417 | } | 227 | } |
418 | } | 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 | } | ||
419 | 242 | ||
420 | return result; | 243 | if (0 == files.Length) |
244 | { | ||
245 | this.Messaging.Write(ErrorMessages.FileNotFound(null, searchPath, fileType)); | ||
246 | } | ||
247 | |||
248 | return files; | ||
421 | } | 249 | } |
422 | 250 | ||
423 | private bool TryParseCommandLineArgumentWithExtension(string arg, IParseCommandLine parse, IEnumerable<IExtensionCommandLine> extensions) | 251 | private string VerifyPath(string path) |
424 | { | 252 | { |
425 | foreach (var extension in extensions) | 253 | string fullPath; |
254 | |||
255 | if (0 <= path.IndexOf('\"')) | ||
426 | { | 256 | { |
427 | if (extension.TryParseArgument(parse, arg)) | 257 | this.Messaging.Write(ErrorMessages.PathCannotContainQuote(path)); |
428 | { | 258 | return null; |
429 | return true; | ||
430 | } | ||
431 | } | 259 | } |
432 | 260 | ||
433 | return false; | 261 | try |
434 | } | 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 | } | ||
435 | 270 | ||
436 | public static BindPath ParseBindPath(string bindPath) | 271 | return fullPath; |
437 | { | ||
438 | var namedPath = bindPath.Split(BindPathSplit, 2); | ||
439 | return (1 == namedPath.Length) ? new BindPath(namedPath[0]) : new BindPath(namedPath[0], namedPath[1]); | ||
440 | } | 272 | } |
441 | } | 273 | } |
442 | } | 274 | } |
diff --git a/src/WixToolset.Core/CommandLine/CompileCommand.cs b/src/WixToolset.Core/CommandLine/CompileCommand.cs index 4007c263..69e35cab 100644 --- a/src/WixToolset.Core/CommandLine/CompileCommand.cs +++ b/src/WixToolset.Core/CommandLine/CompileCommand.cs | |||
@@ -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 | 2 | ||
3 | namespace WixToolset.Core.CommandLine | 3 | namespace WixToolset.Core.CommandLine |
4 | { | 4 | { |
@@ -12,6 +12,13 @@ namespace WixToolset.Core.CommandLine | |||
12 | 12 | ||
13 | internal class CompileCommand : ICommandLineCommand | 13 | internal class CompileCommand : ICommandLineCommand |
14 | { | 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 | |||
15 | public CompileCommand(IServiceProvider serviceProvider, IEnumerable<SourceFile> sources, IDictionary<string, string> preprocessorVariables, Platform platform) | 22 | public CompileCommand(IServiceProvider serviceProvider, IEnumerable<SourceFile> sources, IDictionary<string, string> preprocessorVariables, Platform platform) |
16 | { | 23 | { |
17 | this.ServiceProvider = serviceProvider; | 24 | this.ServiceProvider = serviceProvider; |
@@ -36,6 +43,15 @@ namespace WixToolset.Core.CommandLine | |||
36 | 43 | ||
37 | public IEnumerable<string> IncludeSearchPaths { get; } | 44 | public IEnumerable<string> IncludeSearchPaths { get; } |
38 | 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 | |||
39 | public int Execute() | 55 | public int Execute() |
40 | { | 56 | { |
41 | foreach (var sourceFile in this.SourceFiles) | 57 | foreach (var sourceFile in this.SourceFiles) |
diff --git a/src/WixToolset.Core/CommandLine/HelpCommand.cs b/src/WixToolset.Core/CommandLine/HelpCommand.cs index b1298e46..224b154c 100644 --- a/src/WixToolset.Core/CommandLine/HelpCommand.cs +++ b/src/WixToolset.Core/CommandLine/HelpCommand.cs | |||
@@ -4,28 +4,24 @@ namespace WixToolset.Core.CommandLine | |||
4 | { | 4 | { |
5 | using System; | 5 | using System; |
6 | using WixToolset.Extensibility.Data; | 6 | using WixToolset.Extensibility.Data; |
7 | using WixToolset.Extensibility.Services; | ||
7 | 8 | ||
8 | internal class HelpCommand : ICommandLineCommand | 9 | internal class HelpCommand : ICommandLineCommand |
9 | { | 10 | { |
10 | public HelpCommand(Commands command) | 11 | public bool ShowLogo => true; |
11 | { | ||
12 | this.Command = command; | ||
13 | } | ||
14 | 12 | ||
15 | public Commands Command { get; } | 13 | public bool StopParsing => true; |
16 | 14 | ||
17 | public int Execute() | 15 | public int Execute() |
18 | { | 16 | { |
19 | if (this.Command == Commands.Unknown) | 17 | Console.WriteLine("TODO: Show list of available commands"); |
20 | { | ||
21 | Console.WriteLine(); | ||
22 | } | ||
23 | else | ||
24 | { | ||
25 | Console.WriteLine(); | ||
26 | } | ||
27 | 18 | ||
28 | return -1; | 19 | return -1; |
29 | } | 20 | } |
21 | |||
22 | public bool TryParseArgument(ICommandLineParser parseHelper, string argument) | ||
23 | { | ||
24 | return true; // eat any arguments | ||
25 | } | ||
30 | } | 26 | } |
31 | } | 27 | } |
diff --git a/src/WixToolset.Core/CommandLine/ParseCommandLine.cs b/src/WixToolset.Core/CommandLine/ParseCommandLine.cs deleted file mode 100644 index 3cf6e032..00000000 --- a/src/WixToolset.Core/CommandLine/ParseCommandLine.cs +++ /dev/null | |||
@@ -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 | } | ||
diff --git a/src/WixToolset.Core/CommandLine/VersionCommand.cs b/src/WixToolset.Core/CommandLine/VersionCommand.cs index e67aafb8..1baee72d 100644 --- a/src/WixToolset.Core/CommandLine/VersionCommand.cs +++ b/src/WixToolset.Core/CommandLine/VersionCommand.cs | |||
@@ -4,9 +4,14 @@ namespace WixToolset.Core.CommandLine | |||
4 | { | 4 | { |
5 | using System; | 5 | using System; |
6 | using WixToolset.Extensibility.Data; | 6 | using WixToolset.Extensibility.Data; |
7 | using WixToolset.Extensibility.Services; | ||
7 | 8 | ||
8 | internal class VersionCommand : ICommandLineCommand | 9 | internal class VersionCommand : ICommandLineCommand |
9 | { | 10 | { |
11 | public bool ShowLogo => true; | ||
12 | |||
13 | public bool StopParsing => true; | ||
14 | |||
10 | public int Execute() | 15 | public int Execute() |
11 | { | 16 | { |
12 | Console.WriteLine("wix version {0}", ThisAssembly.AssemblyInformationalVersion); | 17 | Console.WriteLine("wix version {0}", ThisAssembly.AssemblyInformationalVersion); |
@@ -14,5 +19,10 @@ namespace WixToolset.Core.CommandLine | |||
14 | 19 | ||
15 | return 0; | 20 | return 0; |
16 | } | 21 | } |
22 | |||
23 | public bool TryParseArgument(ICommandLineParser parseHelper, string argument) | ||
24 | { | ||
25 | return true; // eat any arguments | ||
26 | } | ||
17 | } | 27 | } |
18 | } | 28 | } |
diff --git a/src/WixToolset.Core/WixToolsetServiceProvider.cs b/src/WixToolset.Core/WixToolsetServiceProvider.cs index 0337f771..7216ae2a 100644 --- a/src/WixToolset.Core/WixToolsetServiceProvider.cs +++ b/src/WixToolset.Core/WixToolsetServiceProvider.cs | |||
@@ -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 | 2 | ||
3 | namespace WixToolset.Core | 3 | namespace WixToolset.Core |
4 | { | 4 | { |
@@ -29,7 +29,7 @@ namespace WixToolset.Core | |||
29 | // Transients. | 29 | // Transients. |
30 | this.AddService<ICommandLineArguments>((provider, singletons) => new CommandLineArguments(provider)); | 30 | this.AddService<ICommandLineArguments>((provider, singletons) => new CommandLineArguments(provider)); |
31 | this.AddService<ICommandLineContext>((provider, singletons) => new CommandLineContext(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)); | 33 | this.AddService<IPreprocessContext>((provider, singletons) => new PreprocessContext(provider)); |
34 | this.AddService<ICompileContext>((provider, singletons) => new CompileContext(provider)); | 34 | this.AddService<ICompileContext>((provider, singletons) => new CompileContext(provider)); |
35 | this.AddService<ILibraryContext>((provider, singletons) => new LibraryContext(provider)); | 35 | this.AddService<ILibraryContext>((provider, singletons) => new LibraryContext(provider)); |
diff --git a/src/test/Example.Extension/ExamplePreprocessorExtensionAndCommandLine.cs b/src/test/Example.Extension/ExamplePreprocessorExtensionAndCommandLine.cs index 8fed7944..eddcf6e4 100644 --- a/src/test/Example.Extension/ExamplePreprocessorExtensionAndCommandLine.cs +++ b/src/test/Example.Extension/ExamplePreprocessorExtensionAndCommandLine.cs | |||
@@ -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 | 2 | ||
3 | namespace Example.Extension | 3 | namespace Example.Extension |
4 | { | 4 | { |
@@ -23,17 +23,23 @@ namespace Example.Extension | |||
23 | { | 23 | { |
24 | } | 24 | } |
25 | 25 | ||
26 | public bool TryParseArgument(IParseCommandLine parseCommandLine, string arg) | 26 | public bool TryParseArgument(ICommandLineParser parser, string argument) |
27 | { | 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 | { | 29 | { |
30 | this.exampleValueFromCommandLine = parseCommandLine.GetNextArgumentOrError(arg); | 30 | this.exampleValueFromCommandLine = parser.GetNextArgumentOrError(argument); |
31 | return true; | 31 | return true; |
32 | } | 32 | } |
33 | 33 | ||
34 | return false; | 34 | return false; |
35 | } | 35 | } |
36 | 36 | ||
37 | public bool TryParseCommand(ICommandLineParser parser, out ICommandLineCommand command) | ||
38 | { | ||
39 | command = null; | ||
40 | return false; | ||
41 | } | ||
42 | |||
37 | public void PostParse() | 43 | public void PostParse() |
38 | { | 44 | { |
39 | } | 45 | } |