forked from microsoft/msbuild-extractor-sample
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCommandLineOptions.cs
More file actions
326 lines (283 loc) · 15.4 KB
/
Copy pathCommandLineOptions.cs
File metadata and controls
326 lines (283 loc) · 15.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
using System.CommandLine;
namespace MSBuild.CompileCommands.Extractor
{
public class CommandLineOptions
{
public string[] Projects { get; set; } = [];
public string[] Solutions { get; set; } = [];
public string? Project => Projects.Length == 1 ? Projects[0] : null;
public string? Solution => Solutions.Length == 1 ? Solutions[0] : null;
public string Configuration { get; set; } = "Debug";
public string Platform { get; set; } = "x64";
public string? VsPath { get; set; }
public string? VcTargetsPath { get; set; }
public string? ClPath { get; set; }
public string? SolutionDir { get; set; }
public string? VcToolsInstallDir { get; set; }
public string? MsBuildPath { get; set; }
public string? Output { get; set; }
public bool EnableLogger { get; set; }
public bool UseDevEnv { get; set; }
public bool AllConfigurations { get; set; }
public bool Merge { get; set; }
public bool Strict { get; set; }
public bool Validate { get; set; }
public string Format { get; set; } = "standard";
public bool Deduplicate { get; set; }
public string PreferConfiguration { get; set; } = "Debug";
public string PreferPlatform { get; set; } = "x64";
public bool ListInstances { get; set; }
public string? VsInstance { get; set; }
public bool EmitCCppProperties { get; set; }
public bool EmitDefaults { get; set; }
public bool MergeDefaults { get; set; }
public Dictionary<string, string> MsBuildProperties { get; set; } = new(StringComparer.OrdinalIgnoreCase);
public Dictionary<string, string> MsBuildEnv { get; set; } = new(StringComparer.OrdinalIgnoreCase);
public string MsBuildLauncher { get; set; } = "auto";
public string IncludePathOrder { get; set; } = "auto";
public static CommandLineOptions Parse(string[] args)
{
CommandLineOptions? result = null;
var projectOption = new Option<string[]>("--project", "-p")
{
Description = "Path to .vcxproj file (can be specified multiple times)",
AllowMultipleArgumentsPerToken = true
};
var solutionOption = new Option<string[]>("--solution", "-s")
{
Description = "Path to .sln or .slnx file (can be specified multiple times)",
AllowMultipleArgumentsPerToken = true
};
var configOption = new Option<string>("--configuration", "-c")
{
Description = "Build configuration",
DefaultValueFactory = _ => "Debug"
};
var platformOption = new Option<string>("--platform", "-a")
{
Description = "Build platform",
DefaultValueFactory = _ => "x64"
};
var vsPathOption = new Option<string?>("--vs-path")
{
Description = "Path to Visual Studio installation"
};
var vcTargetsPathOption = new Option<string?>("--vc-targets-path")
{
Description = "Path to VC targets (e.g. ...\\MSBuild\\Microsoft\\VC\\v180)"
};
var clPathOption = new Option<string?>("--cl-path")
{
Description = "Path to cl.exe"
};
var solutionDirOption = new Option<string?>("--solution-dir")
{
Description = "Value for the SolutionDir MSBuild property (auto-derived when using --solution)"
};
var vcToolsInstallDirOption = new Option<string?>("--vc-tools-install-dir")
{
Description = "Value for the VCToolsInstallDir MSBuild property"
};
var outputOption = new Option<string?>("--output", "-o")
{
Description = "Output path for compile_commands.json"
};
var loggerOption = new Option<bool>("--logger")
{
Description = "Enable MSBuild console logger output",
DefaultValueFactory = _ => false
};
var useDevEnvOption = new Option<bool>("--use-dev-env")
{
Description = "Read environment variables from Developer Command Prompt (VCToolsInstallDir, VCTargetsPath, etc.)",
DefaultValueFactory = _ => false
};
var msbuildPathOption = new Option<string?>("--msbuild-path")
{
Description = "Path to msbuild.exe (enables out-of-process mode for custom MSBuild/toolchain locations)"
};
var allConfigsOption = new Option<bool>("--all-configurations")
{
Description = "Extract for all configuration/platform combinations found in the project or solution",
DefaultValueFactory = _ => false
};
var mergeOption = new Option<bool>("--merge")
{
Description = "Merge all configurations into a single output file (use with --all-configurations)",
DefaultValueFactory = _ => false
};
var strictOption = new Option<bool>("--strict")
{
Description = "Treat configuration/platform validation warnings as errors",
DefaultValueFactory = _ => false
};
var validateOption = new Option<bool>("--validate")
{
Description = "After extraction, verify each compile command by running cl.exe /c",
DefaultValueFactory = _ => false
};
var formatOption = new Option<string>("--format", "-f")
{
Description = "Output format: 'standard' (compile_commands.json) or 'rich' (hierarchical compile_database.json)",
DefaultValueFactory = _ => "standard"
};
formatOption.AcceptOnlyFromAmong("standard", "rich");
var deduplicateOption = new Option<bool>("--deduplicate")
{
Description = "Merge duplicate entries for the same file into one best-compromise entry for IntelliSense",
DefaultValueFactory = _ => false
};
var preferConfigOption = new Option<string>("--prefer-configuration")
{
Description = "Preferred configuration for conflict resolution during deduplication (default: Debug)",
DefaultValueFactory = _ => "Debug"
};
var preferPlatformOption = new Option<string>("--prefer-platform")
{
Description = "Preferred platform for conflict resolution during deduplication (default: x64)",
DefaultValueFactory = _ => "x64"
};
var listInstancesOption = new Option<bool>("--list-instances", "--list-vs")
{
Description = "List all Visual Studio installations and exit"
};
var vsInstanceOption = new Option<string?>("--vs-instance")
{
Description = "Select VS installation by instance ID (use --list-instances to see available)"
};
var cCppPropertiesOption = new Option<bool>("--c-cpp-properties")
{
Description = "Emit a .vscode/c_cpp_properties.json pointing to the generated compile_commands.json",
DefaultValueFactory = _ => false
};
var emitDefaultsOption = new Option<bool>("--emit-defaults")
{
Description = "Include the project-wide default compile entry in the output (synthetic __project_defaults.cpp with the baseline switches for the project)",
DefaultValueFactory = _ => false
};
var mergeDefaultsOption = new Option<bool>("--merge-defaults")
{
Description = "Merge project-wide default switches (defines, language standard, warning level, etc.) into each per-file entry when they are not already present",
DefaultValueFactory = _ => false
};
var msbuildPropertyOption = new Option<string[]>("--msbuild-property")
{
Description = "Pass an MSBuild global property as KEY=VALUE (repeatable). Overrides built-in defaults (e.g. BuildProjectReferences=true).",
AllowMultipleArgumentsPerToken = false
};
var msbuildEnvOption = new Option<string[]>("--msbuild-env")
{
Description = "Set an environment variable for the MSBuild process as KEY=VALUE (repeatable). Overrides built-in defaults.",
AllowMultipleArgumentsPerToken = false
};
var msbuildLauncherOption = new Option<string>("--msbuild-launcher")
{
Description = "How to launch MSBuild: auto (sniff extension, default), cmd (force cmd.exe /c wrapper), direct (run executable directly), dotnet (force dotnet exec)",
DefaultValueFactory = _ => "auto"
};
msbuildLauncherOption.AcceptOnlyFromAmong("auto", "cmd", "direct", "dotnet");
var includePathOrderOption = new Option<string>("--include-path-order")
{
Description = "Where to place include paths from MSBuild's IncludePath/ExternalIncludePath properties: auto (per-path heuristic, default), prepend (before /I), append (after /I, matches cl.exe INCLUDE-env semantics)",
DefaultValueFactory = _ => "auto"
};
includePathOrderOption.AcceptOnlyFromAmong("auto", "prepend", "append");
var rootCommand = new RootCommand("Extract compile_commands.json from Visual C++ MSBuild projects");
rootCommand.Options.Add(projectOption);
rootCommand.Options.Add(solutionOption);
rootCommand.Options.Add(configOption);
rootCommand.Options.Add(platformOption);
rootCommand.Options.Add(vsPathOption);
rootCommand.Options.Add(vcTargetsPathOption);
rootCommand.Options.Add(clPathOption);
rootCommand.Options.Add(solutionDirOption);
rootCommand.Options.Add(vcToolsInstallDirOption);
rootCommand.Options.Add(msbuildPathOption);
rootCommand.Options.Add(outputOption);
rootCommand.Options.Add(loggerOption);
rootCommand.Options.Add(useDevEnvOption);
rootCommand.Options.Add(allConfigsOption);
rootCommand.Options.Add(mergeOption);
rootCommand.Options.Add(strictOption);
rootCommand.Options.Add(validateOption);
rootCommand.Options.Add(formatOption);
rootCommand.Options.Add(deduplicateOption);
rootCommand.Options.Add(preferConfigOption);
rootCommand.Options.Add(preferPlatformOption);
rootCommand.Options.Add(listInstancesOption);
rootCommand.Options.Add(vsInstanceOption);
rootCommand.Options.Add(cCppPropertiesOption);
rootCommand.Options.Add(emitDefaultsOption);
rootCommand.Options.Add(mergeDefaultsOption);
rootCommand.Options.Add(msbuildPropertyOption);
rootCommand.Options.Add(msbuildEnvOption);
rootCommand.Options.Add(msbuildLauncherOption);
rootCommand.Options.Add(includePathOrderOption);
rootCommand.Validators.Add(commandResult =>
{
var listInstances = commandResult.GetValue(listInstancesOption);
if (listInstances)
return;
var projects = commandResult.GetValue(projectOption) ?? [];
var solutions = commandResult.GetValue(solutionOption) ?? [];
if (projects.Length == 0 && solutions.Length == 0)
commandResult.AddError("At least one --project or --solution must be specified.");
if (commandResult.GetValue(cCppPropertiesOption) && commandResult.GetValue(formatOption) == "rich")
commandResult.AddError("--c-cpp-properties cannot be used with --format rich (rich format does not produce compile_commands.json).");
});
rootCommand.SetAction(parseResult =>
{
result = new CommandLineOptions
{
Projects = parseResult.GetValue(projectOption) ?? [],
Solutions = parseResult.GetValue(solutionOption) ?? [],
Configuration = parseResult.GetValue(configOption)!,
Platform = parseResult.GetValue(platformOption)!,
VsPath = parseResult.GetValue(vsPathOption),
VcTargetsPath = parseResult.GetValue(vcTargetsPathOption),
ClPath = parseResult.GetValue(clPathOption),
SolutionDir = parseResult.GetValue(solutionDirOption),
VcToolsInstallDir = parseResult.GetValue(vcToolsInstallDirOption),
MsBuildPath = parseResult.GetValue(msbuildPathOption),
Output = parseResult.GetValue(outputOption),
EnableLogger = parseResult.GetValue(loggerOption),
UseDevEnv = parseResult.GetValue(useDevEnvOption),
AllConfigurations = parseResult.GetValue(allConfigsOption),
Merge = parseResult.GetValue(mergeOption),
Strict = parseResult.GetValue(strictOption),
Validate = parseResult.GetValue(validateOption),
Format = parseResult.GetValue(formatOption)!,
Deduplicate = parseResult.GetValue(deduplicateOption),
PreferConfiguration = parseResult.GetValue(preferConfigOption)!,
PreferPlatform = parseResult.GetValue(preferPlatformOption)!,
ListInstances = parseResult.GetValue(listInstancesOption),
VsInstance = parseResult.GetValue(vsInstanceOption),
EmitCCppProperties = parseResult.GetValue(cCppPropertiesOption),
EmitDefaults = parseResult.GetValue(emitDefaultsOption),
MergeDefaults = parseResult.GetValue(mergeDefaultsOption),
MsBuildProperties = ParseKeyValuePairs(parseResult.GetValue(msbuildPropertyOption), "--msbuild-property"),
MsBuildEnv = ParseKeyValuePairs(parseResult.GetValue(msbuildEnvOption), "--msbuild-env"),
MsBuildLauncher = parseResult.GetValue(msbuildLauncherOption)!,
IncludePathOrder = parseResult.GetValue(includePathOrderOption)!
};
});
var exitCode = rootCommand.Parse(args).Invoke();
if (result == null)
Environment.Exit(exitCode);
return result;
}
private static Dictionary<string, string> ParseKeyValuePairs(string[]? values, string flagName)
{
var dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
if (values == null) return dict;
foreach (var entry in values)
{
var separatorIndex = entry.IndexOf('=');
if (separatorIndex <= 0)
throw new ArgumentException($"{flagName} value '{entry}' must be in KEY=VALUE form.");
dict[entry.Substring(0, separatorIndex)] = entry.Substring(separatorIndex + 1);
}
return dict;
}
}
}