Skip to content

Use MSBuild's command-line parser instead of hand-maintained switch allowlists #56123

Description

@JanProvaznik

Summary

The SDK decides which command-line tokens belong to MSBuild and which belong to the process a verb launches. It does this in four different places, with three different policies, none of which consult MSBuild's own parser. Every time MSBuild adds a switch, the SDK silently misroutes it until someone notices and patches an allowlist.

MSBuild already exposes the parsing routine needed to stop this. dotnet/msbuild#12836 extracted the command-line parsing out of XMake into a reusable CommandLineParser, and src/MSBuild/AssemblyInfo.cs carries [assembly: InternalsVisibleTo("dotnet", ...)] specifically so the SDK can call it:

Microsoft.Build.CommandLine.Experimental.CommandLineParser
    .Parse(IEnumerable<string> args) -> CommandLineSwitchesAccessor

CommandLineSwitchesAccessor exposes typed properties for every switch — MultiThreaded, NodeReuse, MaxCpuCount, GraphBuild, IsolateProjects, Restore, BinaryLogger, TerminalLogger, and so on.

Current state

Location Policy
LoggerUtility.SeparateLoggerArguments Hardcoded allowlist: bl, noConsoleLogger, tl, ll, tlp, clp. Used by build/publish/pack/clean/restore, run, and test under the Microsoft.Testing.Platform runner.
Test/VSTest/TestCommand.cs Forwards all unmatched tokens to MSBuild.
dotnet-watch CommandLineOptions.GetCommandArguments Modeled forwarding options only, plus a hand-extracted -bl (s_binaryLogOptionNames).
MSBuildArgs.AnalyzeMSBuildArguments Re-parses tokens against a small hand-built set of Option objects.

Consequences

Bugs in this class are found one switch at a time:

Each fix is a separate patch to a separate allowlist. Switches such as -nr/-nodeReuse, -graph, and -isolate are still misrouted today by the verbs that route unmatched tokens away from MSBuild, and any switch MSBuild adds in the future starts out broken.

Proposal

Replace the hand-maintained allowlists with a single classification helper backed by CommandLineParser, and have all four call sites use it. The routing policy for each verb — which recognized MSBuild switches a verb should intercept rather than forward to the launched process — stays an SDK decision, but "is this an MSBuild switch at all" stops being one.

Open questions

Two properties of the current API make it unusable as a drop-in, and would need to be addressed on the MSBuild side first:

  1. Parse throws on unrecognized switches. It ends in CommandLineSwitches.ThrowErrors(), and unknown tokens raise UnknownSwitchError. The SDK's use case is the opposite: a token MSBuild does not recognize is a valid argument for the test application or the launched app. This needs a non-throwing mode that reports unrecognized tokens rather than failing.

  2. Parse reads response files. It calls GatherAllSwitches, which walks parent directories looking for Directory.Build.rsp and MSBuild.rsp. For classifying tokens that the SDK has already received, that is unwanted I/O, and it would make argument routing depend on response file contents. A parse-only overload would be needed.

Raising as a design discussion rather than a concrete work item, since the shape depends on what MSBuild is willing to expose.

/cc @baronfel

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    untriagedRequest triage from a team member

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions