diff --git a/CommandLineOptions.cs b/CommandLineOptions.cs
index de87e2e..e012e62 100644
--- a/CommandLineOptions.cs
+++ b/CommandLineOptions.cs
@@ -38,128 +38,147 @@ public class CommandLineOptions
public string MsBuildLauncher { get; set; } = "auto";
public string IncludePathOrder { get; set; } = "auto";
+ /// Path to the JSON config file that supplied defaults, or null if none was used.
+ public string? ConfigPath { get; set; }
+
public static CommandLineOptions Parse(string[] args)
{
CommandLineOptions? result = null;
+ // Load an optional config file; its values become the defaults for the options built
+ // below, so anything passed on the command line still wins. config stays null when no
+ // config file is present.
+ Config? config = new Config();
+ if (!config.Load(args))
+ config = null;
+
var projectOption = new Option("--project", "-p")
{
Description = "Path to .vcxproj file (can be specified multiple times)",
- AllowMultipleArgumentsPerToken = true
+ AllowMultipleArgumentsPerToken = true,
+ DefaultValueFactory = _ => config?.Projects ?? []
};
var solutionOption = new Option("--solution", "-s")
{
Description = "Path to .sln or .slnx file (can be specified multiple times)",
- AllowMultipleArgumentsPerToken = true
+ AllowMultipleArgumentsPerToken = true,
+ DefaultValueFactory = _ => config?.Solutions ?? []
};
var configOption = new Option("--configuration", "-c")
{
Description = "Build configuration",
- DefaultValueFactory = _ => "Debug"
+ DefaultValueFactory = _ => config?.Configuration ?? "Debug"
};
var platformOption = new Option("--platform", "-a")
{
Description = "Build platform",
- DefaultValueFactory = _ => "x64"
+ DefaultValueFactory = _ => config?.Platform ?? "x64"
};
var vsPathOption = new Option("--vs-path")
{
- Description = "Path to Visual Studio installation"
+ Description = "Path to Visual Studio installation",
+ DefaultValueFactory = _ => config?.VsPath
};
var vcTargetsPathOption = new Option("--vc-targets-path")
{
- Description = "Path to VC targets (e.g. ...\\MSBuild\\Microsoft\\VC\\v180)"
+ Description = "Path to VC targets (e.g. ...\\MSBuild\\Microsoft\\VC\\v180)",
+ DefaultValueFactory = _ => config?.VcTargetsPath
};
var clPathOption = new Option("--cl-path")
{
- Description = "Path to cl.exe"
+ Description = "Path to cl.exe",
+ DefaultValueFactory = _ => config?.ClPath
};
var solutionDirOption = new Option("--solution-dir")
{
- Description = "Value for the SolutionDir MSBuild property (auto-derived when using --solution)"
+ Description = "Value for the SolutionDir MSBuild property (auto-derived when using --solution)",
+ DefaultValueFactory = _ => config?.SolutionDir
};
var vcToolsInstallDirOption = new Option("--vc-tools-install-dir")
{
- Description = "Value for the VCToolsInstallDir MSBuild property"
+ Description = "Value for the VCToolsInstallDir MSBuild property",
+ DefaultValueFactory = _ => config?.VcToolsInstallDir
};
var outputOption = new Option("--output", "-o")
{
- Description = "Output path for compile_commands.json"
+ Description = "Output path for compile_commands.json",
+ DefaultValueFactory = _ => config?.Output
};
var loggerOption = new Option("--logger")
{
Description = "Enable MSBuild console logger output",
- DefaultValueFactory = _ => false
+ DefaultValueFactory = _ => config?.Logger ?? false
};
var useDevEnvOption = new Option("--use-dev-env")
{
Description = "Read environment variables from Developer Command Prompt (VCToolsInstallDir, VCTargetsPath, etc.)",
- DefaultValueFactory = _ => false
+ DefaultValueFactory = _ => config?.UseDevEnv ?? false
};
var msbuildPathOption = new Option("--msbuild-path")
{
- Description = "Path to msbuild.exe (enables out-of-process mode for custom MSBuild/toolchain locations)"
+ Description = "Path to msbuild.exe (enables out-of-process mode for custom MSBuild/toolchain locations)",
+ DefaultValueFactory = _ => config?.MsBuildPath
};
var allConfigsOption = new Option("--all-configurations")
{
Description = "Extract for all configuration/platform combinations found in the project or solution",
- DefaultValueFactory = _ => false
+ DefaultValueFactory = _ => config?.AllConfigurations ?? false
};
var mergeOption = new Option("--merge")
{
Description = "Merge all configurations into a single output file (use with --all-configurations)",
- DefaultValueFactory = _ => false
+ DefaultValueFactory = _ => config?.Merge ?? false
};
var strictOption = new Option("--strict")
{
Description = "Treat configuration/platform validation warnings as errors",
- DefaultValueFactory = _ => false
+ DefaultValueFactory = _ => config?.Strict ?? false
};
var validateOption = new Option("--validate")
{
Description = "After extraction, verify each compile command by running cl.exe /c",
- DefaultValueFactory = _ => false
+ DefaultValueFactory = _ => config?.Validate ?? false
};
var formatOption = new Option("--format", "-f")
{
Description = "Output format: 'standard' (compile_commands.json) or 'rich' (hierarchical compile_database.json)",
- DefaultValueFactory = _ => "standard"
+ DefaultValueFactory = _ => config?.Format ?? "standard"
};
formatOption.AcceptOnlyFromAmong("standard", "rich");
var deduplicateOption = new Option("--deduplicate")
{
Description = "Merge duplicate entries for the same file into one best-compromise entry for IntelliSense",
- DefaultValueFactory = _ => false
+ DefaultValueFactory = _ => config?.Deduplicate ?? false
};
var preferConfigOption = new Option("--prefer-configuration")
{
Description = "Preferred configuration for conflict resolution during deduplication (default: Debug)",
- DefaultValueFactory = _ => "Debug"
+ DefaultValueFactory = _ => config?.PreferConfiguration ?? "Debug"
};
var preferPlatformOption = new Option("--prefer-platform")
{
Description = "Preferred platform for conflict resolution during deduplication (default: x64)",
- DefaultValueFactory = _ => "x64"
+ DefaultValueFactory = _ => config?.PreferPlatform ?? "x64"
};
var listInstancesOption = new Option("--list-instances", "--list-vs")
@@ -169,25 +188,26 @@ public static CommandLineOptions Parse(string[] args)
var vsInstanceOption = new Option("--vs-instance")
{
- Description = "Select VS installation by instance ID (use --list-instances to see available)"
+ Description = "Select VS installation by instance ID (use --list-instances to see available)",
+ DefaultValueFactory = _ => config?.VsInstance
};
var cCppPropertiesOption = new Option("--c-cpp-properties")
{
Description = "Emit a .vscode/c_cpp_properties.json pointing to the generated compile_commands.json",
- DefaultValueFactory = _ => false
+ DefaultValueFactory = _ => config?.EmitCCppProperties ?? false
};
var emitDefaultsOption = new Option("--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
+ DefaultValueFactory = _ => config?.EmitDefaults ?? false
};
var mergeDefaultsOption = new Option("--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
+ DefaultValueFactory = _ => config?.MergeDefaults ?? false
};
var msbuildPropertyOption = new Option("--msbuild-property")
@@ -205,17 +225,22 @@ public static CommandLineOptions Parse(string[] args)
var msbuildLauncherOption = new Option("--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"
+ DefaultValueFactory = _ => config?.MsBuildLauncher ?? "auto"
};
msbuildLauncherOption.AcceptOnlyFromAmong("auto", "cmd", "direct", "dotnet");
var includePathOrderOption = new Option("--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"
+ DefaultValueFactory = _ => config?.IncludePathOrder ?? "auto"
};
includePathOrderOption.AcceptOnlyFromAmong("auto", "prepend", "append");
+ var configFileOption = new Option("--config")
+ {
+ Description = "Path to a JSON config file supplying option defaults. When omitted, a 'msbuild-extractor.json' in the current directory is used automatically if present. Command-line options override values from the file."
+ };
+
var rootCommand = new RootCommand("Extract compile_commands.json from Visual C++ MSBuild projects");
rootCommand.Options.Add(projectOption);
rootCommand.Options.Add(solutionOption);
@@ -247,6 +272,7 @@ public static CommandLineOptions Parse(string[] args)
rootCommand.Options.Add(msbuildEnvOption);
rootCommand.Options.Add(msbuildLauncherOption);
rootCommand.Options.Add(includePathOrderOption);
+ rootCommand.Options.Add(configFileOption);
rootCommand.Validators.Add(commandResult =>
{
@@ -257,8 +283,12 @@ public static CommandLineOptions Parse(string[] args)
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.");
+ // Inputs may also come from the config file. A validator cannot see values produced
+ // by an option's DefaultValueFactory, so read them from the config directly.
+ var hasConfigInputs = (config?.Projects?.Length ?? 0) > 0 || (config?.Solutions?.Length ?? 0) > 0;
+
+ if (projects.Length == 0 && solutions.Length == 0 && !hasConfigInputs)
+ commandResult.AddError("At least one --project or --solution must be specified (via the command line or a config file).");
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).");
@@ -294,11 +324,23 @@ public static CommandLineOptions Parse(string[] args)
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"),
+ MsBuildProperties = MergeKeyValues(config?.MsBuildProperties,
+ ParseKeyValuePairs(parseResult.GetValue(msbuildPropertyOption), "--msbuild-property")),
+ MsBuildEnv = MergeKeyValues(config?.MsBuildEnv,
+ ParseKeyValuePairs(parseResult.GetValue(msbuildEnvOption), "--msbuild-env")),
MsBuildLauncher = parseResult.GetValue(msbuildLauncherOption)!,
- IncludePathOrder = parseResult.GetValue(includePathOrderOption)!
+ IncludePathOrder = parseResult.GetValue(includePathOrderOption)!,
+ ConfigPath = config?.SourcePath
};
+
+ // Re-check option combinations against the effective (merged) values. As with the
+ // input check above, a validator cannot see config-supplied defaults, so a
+ // config-only "rich + c-cpp-properties" clash would otherwise slip through.
+ if (result.EmitCCppProperties && result.Format == "rich")
+ {
+ Console.Error.WriteLine("Error: --c-cpp-properties / \"emitCCppProperties\" cannot be used with the 'rich' format (rich format does not produce compile_commands.json).");
+ Environment.Exit(1);
+ }
});
var exitCode = rootCommand.Parse(args).Invoke();
@@ -322,5 +364,17 @@ private static Dictionary ParseKeyValuePairs(string[]? values, s
}
return dict;
}
+
+ private static Dictionary MergeKeyValues(
+ Dictionary? fromConfig, Dictionary fromCommandLine)
+ {
+ var merged = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ if (fromConfig != null)
+ foreach (var kvp in fromConfig)
+ merged[kvp.Key] = kvp.Value;
+ foreach (var kvp in fromCommandLine)
+ merged[kvp.Key] = kvp.Value;
+ return merged;
+ }
}
}
diff --git a/Config.cs b/Config.cs
new file mode 100644
index 0000000..5274e28
--- /dev/null
+++ b/Config.cs
@@ -0,0 +1,192 @@
+using System.Text.Json;
+
+namespace MSBuild.CompileCommands.Extractor
+{
+ ///
+ /// Options loaded from an optional JSON config file. Every member is nullable so an absent key
+ /// leaves the matching command-line default untouched. Keys bind case-insensitively, and the
+ /// loader tolerates // comments and trailing commas.
+ ///
+ public sealed class Config
+ {
+ /// File name looked up in the current directory when --config is not passed.
+ public const string DefaultFileName = "msbuild-extractor.json";
+
+ public string[]? Projects { get; set; }
+ public string[]? Solutions { get; set; }
+ public string? Configuration { get; set; }
+ public string? Platform { get; set; }
+ public string? Output { get; set; }
+ public string? Format { get; set; }
+
+ public bool? AllConfigurations { get; set; }
+ public bool? Merge { get; set; }
+ public bool? Deduplicate { get; set; }
+ public string? PreferConfiguration { get; set; }
+ public string? PreferPlatform { get; set; }
+
+ public bool? Strict { get; set; }
+ public bool? Validate { get; set; }
+ public bool? Logger { get; set; }
+ public bool? EmitCCppProperties { get; set; }
+ public bool? EmitDefaults { get; set; }
+ public bool? MergeDefaults { get; set; }
+
+ public bool? UseDevEnv { get; set; }
+ public string? VsInstance { get; set; }
+ public string? VsPath { get; set; }
+ public string? MsBuildPath { get; set; }
+ public string? VcTargetsPath { get; set; }
+ public string? ClPath { get; set; }
+ public string? VcToolsInstallDir { get; set; }
+ public string? SolutionDir { get; set; }
+
+ public string? MsBuildLauncher { get; set; }
+ public string? IncludePathOrder { get; set; }
+
+ public Dictionary? MsBuildProperties { get; set; }
+ public Dictionary? MsBuildEnv { get; set; }
+
+ /// The file this config was loaded from, or null when no config was applied.
+ public string? SourcePath { get; private set; }
+
+ private static readonly JsonSerializerOptions SerializerOptions = new()
+ {
+ PropertyNameCaseInsensitive = true,
+ ReadCommentHandling = JsonCommentHandling.Skip,
+ AllowTrailingCommas = true
+ };
+
+ ///
+ /// Finds a config file (an explicit --config <path>, otherwise a msbuild-extractor.json in
+ /// the current directory) and loads it into this instance. Returns true when one was applied.
+ /// Exits with an error on a missing explicit path, malformed JSON, or an invalid value.
+ ///
+ public bool Load(string[] args)
+ {
+ var path = FindPath(args);
+ if (path == null)
+ return false;
+
+ Config data;
+ try
+ {
+ data = JsonSerializer.Deserialize(File.ReadAllText(path), SerializerOptions) ?? new Config();
+ }
+ catch (JsonException ex)
+ {
+ Console.Error.WriteLine($"Error: invalid config file '{path}': {ex.Message}");
+ Environment.Exit(1);
+ return false;
+ }
+
+ Projects = data.Projects;
+ Solutions = data.Solutions;
+ Configuration = data.Configuration;
+ Platform = data.Platform;
+ Output = data.Output;
+ Format = data.Format;
+ AllConfigurations = data.AllConfigurations;
+ Merge = data.Merge;
+ Deduplicate = data.Deduplicate;
+ PreferConfiguration = data.PreferConfiguration;
+ PreferPlatform = data.PreferPlatform;
+ Strict = data.Strict;
+ Validate = data.Validate;
+ Logger = data.Logger;
+ EmitCCppProperties = data.EmitCCppProperties;
+ EmitDefaults = data.EmitDefaults;
+ MergeDefaults = data.MergeDefaults;
+ UseDevEnv = data.UseDevEnv;
+ VsInstance = data.VsInstance;
+ VsPath = data.VsPath;
+ MsBuildPath = data.MsBuildPath;
+ VcTargetsPath = data.VcTargetsPath;
+ ClPath = data.ClPath;
+ VcToolsInstallDir = data.VcToolsInstallDir;
+ SolutionDir = data.SolutionDir;
+ MsBuildLauncher = data.MsBuildLauncher;
+ IncludePathOrder = data.IncludePathOrder;
+ MsBuildProperties = data.MsBuildProperties;
+ MsBuildEnv = data.MsBuildEnv;
+
+ SourcePath = path;
+ ValidateValues(path);
+ ResolvePaths(Path.GetDirectoryName(Path.GetFullPath(path)) ?? ".");
+ return true;
+ }
+
+ private string? FindPath(string[] args)
+ {
+ var explicitPath = GetConfigArgument(args);
+ if (explicitPath != null)
+ {
+ if (!File.Exists(explicitPath))
+ {
+ Console.Error.WriteLine($"Error: config file not found: {explicitPath}");
+ Environment.Exit(1);
+ }
+ return Path.GetFullPath(explicitPath);
+ }
+
+ var auto = Path.Combine(Directory.GetCurrentDirectory(), DefaultFileName);
+ return File.Exists(auto) ? auto : null;
+ }
+
+ private string? GetConfigArgument(string[] args)
+ {
+ for (int i = 0; i < args.Length; i++)
+ {
+ var arg = args[i];
+ if (arg == "--config")
+ return i + 1 < args.Length ? args[i + 1] : null;
+ if (arg.StartsWith("--config=", StringComparison.Ordinal))
+ return arg.Substring("--config=".Length);
+ }
+ return null;
+ }
+
+ private void ValidateValues(string path)
+ {
+ void Check(string? value, string field, params string[] allowed)
+ {
+ if (value != null && !allowed.Contains(value, StringComparer.OrdinalIgnoreCase))
+ {
+ Console.Error.WriteLine(
+ $"Error: config file '{path}': '{field}' must be one of [{string.Join(", ", allowed)}] but was '{value}'.");
+ Environment.Exit(1);
+ }
+ }
+
+ Check(Format, "format", "standard", "rich");
+ Check(MsBuildLauncher, "msBuildLauncher", "auto", "cmd", "direct", "dotnet");
+ Check(IncludePathOrder, "includePathOrder", "auto", "prepend", "append");
+ }
+
+ private void ResolvePaths(string baseDir)
+ {
+ Projects = ResolveEach(Projects, baseDir, exe: false);
+ Solutions = ResolveEach(Solutions, baseDir, exe: false);
+ Output = ResolveOne(Output, baseDir, exe: false);
+ VsPath = ResolveOne(VsPath, baseDir, exe: false);
+ VcTargetsPath = ResolveOne(VcTargetsPath, baseDir, exe: false);
+ VcToolsInstallDir = ResolveOne(VcToolsInstallDir, baseDir, exe: false);
+ SolutionDir = ResolveOne(SolutionDir, baseDir, exe: false);
+ MsBuildPath = ResolveOne(MsBuildPath, baseDir, exe: true);
+ ClPath = ResolveOne(ClPath, baseDir, exe: true);
+ }
+
+ private string[]? ResolveEach(string[]? values, string baseDir, bool exe)
+ => values?.Select(v => ResolveOne(v, baseDir, exe)!).ToArray();
+
+ private string? ResolveOne(string? value, string baseDir, bool exe)
+ {
+ if (string.IsNullOrEmpty(value) || Path.IsPathRooted(value))
+ return value;
+ // Bare executable names (no directory separator) are left alone so PATH lookup still works.
+ if (exe && !value.Contains('/') && !value.Contains('\\'))
+ return value;
+ return Path.GetFullPath(Path.Combine(baseDir, value));
+ }
+ }
+}
diff --git a/Program.cs b/Program.cs
index 5f9567d..dd4c306 100644
--- a/Program.cs
+++ b/Program.cs
@@ -9,6 +9,9 @@ static void Main(string[] args)
{
var options = CommandLineOptions.Parse(args);
+ if (options.ConfigPath != null && !options.ListInstances)
+ Console.WriteLine($"Using config file: {options.ConfigPath}");
+
// Handle --list-instances: print all VS installations and exit
if (options.ListInstances)
{
diff --git a/README.md b/README.md
index 817e238..30052a7 100644
--- a/README.md
+++ b/README.md
@@ -148,9 +148,66 @@ Options:
--use-dev-env Read Developer Command Prompt environment variables
--c-cpp-properties Emit .vscode/c_cpp_properties.json alongside the output
--logger Enable MSBuild console logger output
+ --config JSON config file supplying option defaults (see below)
```
-At least one `--project` or `--solution` must be specified. Both can be repeated and combined.
+At least one `--project` or `--solution` must be specified (on the command line **or** in a config file). Both can be repeated and combined.
+
+## Configuration file
+
+Options can be supplied from a JSON config file so the tool can run with no arguments:
+
+```bash
+# Uses ./msbuild-extractor.json automatically if it exists
+msbuild-extractor-sample
+
+# Or point at a config file explicitly
+msbuild-extractor-sample --config path/to/config.json
+```
+
+- **Auto-detection:** when `--config` is not passed, a `msbuild-extractor.json` in the current directory is loaded automatically if present.
+- **Precedence:** command-line options override the config file, which overrides the built-in defaults.
+- **Inputs required:** at least one `projects` or `solutions` entry must come from the config file or the command line.
+- **Relative paths** in the config file are resolved from the config file's own directory.
+- The loader is lenient: keys match case-insensitively, and `//` comments and trailing commas are allowed.
+
+See [`msbuild-extractor.example.json`](msbuild-extractor.example.json) for a ready-to-copy example that lists every supported key set to its default. Copy it to `msbuild-extractor.json` to get started.
+
+### Config keys
+
+At least one `projects` or `solutions` entry is required. All other keys are optional; an omitted key keeps its default.
+
+| Key | Type | Default | Description |
+| --- | --- | --- | --- |
+| `projects` | string[] | `[]` | Paths to `.vcxproj` files (equivalent to repeated `--project`). |
+| `solutions` | string[] | `[]` | Paths to `.sln` / `.slnx` files (equivalent to repeated `--solution`). |
+| `configuration` | string | `"Debug"` | Build configuration. |
+| `platform` | string | `"x64"` | Build platform. |
+| `allConfigurations` | bool | `false` | Extract every configuration/platform combination. |
+| `merge` | bool | `false` | With `allConfigurations`, merge into a single output file. |
+| `output` | string | `null` | Output path (defaults to `compile_commands.json`). |
+| `format` | string | `"standard"` | Output format: `"standard"` or `"rich"`. |
+| `emitCCppProperties` | bool | `false` | Also emit `.vscode/c_cpp_properties.json` (not allowed with `"rich"`). |
+| `deduplicate` | bool | `false` | Merge duplicate entries per file for IntelliSense. |
+| `preferConfiguration` | string | `"Debug"` | Preferred configuration for dedup conflicts. |
+| `preferPlatform` | string | `"x64"` | Preferred platform for dedup conflicts. |
+| `emitDefaults` | bool | `false` | Include the synthetic `__project_defaults.cpp` entry. |
+| `mergeDefaults` | bool | `false` | Merge project-wide default switches into each per-file entry. |
+| `validate` | bool | `false` | Verify each entry by running `cl.exe /c`. |
+| `strict` | bool | `false` | Treat configuration/platform warnings as errors. |
+| `logger` | bool | `false` | Enable MSBuild console logger output. |
+| `useDevEnv` | bool | `false` | Read environment from a Developer Command Prompt. |
+| `vsInstance` | string | `null` | Select the VS installation by instance ID. |
+| `vsPath` | string | `null` | Path to a Visual Studio installation (auto-detected). |
+| `msBuildPath` | string | `null` | Path to `msbuild.exe` (enables out-of-process mode). |
+| `vcTargetsPath` | string | `null` | VC targets directory (auto-detected). |
+| `clPath` | string | `null` | Path to `cl.exe` (auto-resolved). |
+| `vcToolsInstallDir` | string | `null` | `VCToolsInstallDir` MSBuild property (auto-detected). |
+| `solutionDir` | string | `null` | `SolutionDir` MSBuild property (auto-derived with `solutions`). |
+| `msBuildLauncher` | string | `"auto"` | How to launch MSBuild: `"auto"`, `"cmd"`, `"direct"`, or `"dotnet"`. |
+| `includePathOrder` | string | `"auto"` | Include-path placement: `"auto"`, `"prepend"`, or `"append"`. |
+| `msBuildProperties` | object | `{}` | MSBuild global properties as `KEY: VALUE` (merged with `--msbuild-property`). |
+| `msBuildEnv` | object | `{}` | Environment variables for the MSBuild process as `KEY: VALUE` (merged with `--msbuild-env`). |
The output includes a sentinel entry (`.msbuild-extractor-sample`) as the first element so consumers can identify the generator. Standard C++ LSP tools silently skip it since the file does not exist on disk.
diff --git a/msbuild-extractor.example.json b/msbuild-extractor.example.json
new file mode 100644
index 0000000..f5c764a
--- /dev/null
+++ b/msbuild-extractor.example.json
@@ -0,0 +1,42 @@
+{
+ "projects": [
+ "src/app/app.vcxproj",
+ "src/lib/lib.vcxproj"
+ ],
+ "solutions": [
+ "MySolution.slnx"
+ ],
+ "configuration": "Debug",
+ "platform": "x64",
+ "allConfigurations": false,
+ "merge": false,
+ "output": null,
+ "format": "standard",
+ "emitCCppProperties": false,
+ "deduplicate": false,
+ "preferConfiguration": "Debug",
+ "preferPlatform": "x64",
+ "emitDefaults": false,
+ "mergeDefaults": false,
+ "validate": false,
+ "strict": false,
+ "logger": false,
+ "useDevEnv": false,
+ "vsInstance": null,
+ "vsPath": null,
+ "msBuildPath": null,
+ "vcTargetsPath": null,
+ "clPath": null,
+ "vcToolsInstallDir": null,
+ "solutionDir": null,
+ "msBuildLauncher": "auto",
+ "includePathOrder": "auto",
+ "msBuildProperties": {
+ "BuildProjectReferences": "true",
+ "UseMultiToolTask": "false"
+ },
+ "msBuildEnv": {
+ "CL": "/DDEBUG",
+ "TMP": "C:\\Temp"
+ }
+}