Enable trim/AOT analyzers for Microsoft.Build and clean up annotations - #14064
Conversation
There was a problem hiding this comment.
Pull request overview
This PR enables IL trim/AOT analysis for Microsoft.Build (via IsAotCompatible on net8.0+) and resolves the resulting IL warnings primarily by propagating [RequiresUnreferencedCode] through reflection-heavy/extensibility call chains, adding targeted suppressions at message-pump/delegate boundaries, and tightening DynamicallyAccessedMembers annotations.
Changes:
- Enable trim/AOT analyzers for
src/Build/Microsoft.Build.csproj(net8.0+ only) and eliminate IL warnings acrossnet10.0+net472. - Propagate
[RequiresUnreferencedCode]across task loading, SDK resolution, logging, project cache, evaluation/graph, build-request engine, and BuildCheck acquisition. - Apply a small set of AOT-friendly refactors (notably
ParallelWorkSet, task-parameter array creation, and logger registration) to avoid analyzer-triggering patterns.
Reviewed changes
Copilot reviewed 64 out of 64 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Build/Microsoft.Build.csproj | Enables IsAotCompatible for net8.0+ to run trim/AOT analyzers. |
| src/Tasks/XamlTaskFactory/XamlTaskFactory.cs | Adds DynamicallyAccessedMembers annotation to TaskType. |
| src/Tasks/RoslynCodeTaskFactory/RoslynCodeTaskFactory.cs | Isolates trim-unsafe reflection behind RUC helpers; adjusts suppressions. |
| src/Tasks/CodeTaskFactory.cs | Annotates TaskType with DynamicallyAccessedMembers; updates stub branch. |
| src/Shared/TypeLoader.cs | Marks runtime assembly/type loading paths as RUC. |
| src/Shared/TaskParameter.cs | AOT-friendly array creation for primitive arrays during translation. |
| src/Shared/TaskLoader.cs | Adds suppression on type-filter delegate used during reflective discovery. |
| src/Shared/TaskEngineAssemblyResolver.cs | Adds suppression on assembly-resolve handlers. |
| src/Framework/ReflectableTaskPropertyInfo.cs | Reworks property lookup to avoid trim-unfriendly APIs; narrows DAM. |
| src/Framework/Loader/LoadedType.cs | Expands DAM requirements to include public parameterless ctor. |
| src/Framework/ITaskFactory.cs | Adds DAM annotation to ITaskFactory.TaskType. |
| src/Build/Utilities/NuGetFrameworkWrapper.cs | Marks runtime load/reflection over NuGet.Frameworks as RUC. |
| src/Build/ObjectModelRemoting/DefinitionObjectsLinks/ProjectLink.cs | Marks remote evaluation/build helper APIs as RUC. |
| src/Build/Logging/LoggerDescription.cs | Marks reflective logger creation as RUC; adds delegate suppressions. |
| src/Build/Instance/TaskRegistry.cs | Marks reflective task factory loading/parameter reflection as RUC; adds suppressions for unrecognized reflection patterns. |
| src/Build/Instance/TaskFactories/AssemblyTaskFactory.cs | Marks assembly task factory type-loading paths as RUC; annotates TaskType. |
| src/Build/Instance/ProjectInstance.cs | Marks project instance constructors/build/evaluation-heavy entrypoints as RUC. |
| src/Build/Graph/ProjectGraph.cs | Marks project graph construction/evaluation entrypoints as RUC. |
| src/Build/Graph/ParallelWorkSet.cs | Replaces Lazy<TResult> with custom lazy work item to avoid IL2091. |
| src/Build/Graph/GraphBuildSubmission.cs | Marks cache-plugin initialization paths as RUC. |
| src/Build/Evaluation/IntrinsicFunctions.cs | Suppresses trim warning around Lazy factory calling RUC code. |
| src/Build/Evaluation/Expander.cs | Adds DAM annotations and suppressions around property-function reflection paths. |
| src/Build/Evaluation/Evaluator.cs | Marks evaluation/import expansion paths as RUC. |
| src/Build/Definition/ProjectCollection.cs | Adds RUC propagation and boundary suppressions around logger registration and project loading. |
| src/Build/Definition/Project.cs | Marks public project construction/evaluation/build entrypoints as RUC. |
| src/Build/Construction/Solution/SolutionProjectGenerator.cs | Marks solution evaluation/generation paths as RUC. |
| src/Build/BuildCheck/Infrastructure/NullBuildCheckManager.cs | Marks custom BuildCheck acquisition as RUC. |
| src/Build/BuildCheck/Infrastructure/IBuildCheckManager.cs | Adds RUC to custom BuildCheck acquisition contract. |
| src/Build/BuildCheck/Infrastructure/BuildCheckManagerProvider.cs | Marks custom check materialization/acquisition paths as RUC; adds suppression boundary. |
| src/Build/BuildCheck/Infrastructure/BuildCheckBuildEventHandler.cs | Adds suppression boundary on build-event handler invoking acquisition. |
| src/Build/BuildCheck/Acquisition/IBuildCheckAcquisitionModule.cs | Marks acquisition interface as RUC. |
| src/Build/BuildCheck/Acquisition/BuildCheckAcquisitionModule.cs | Marks acquisition implementation as RUC. |
| src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs | Adds RUC propagation and AOT-friendly array creation for task parameters. |
| src/Build/BackEnd/Shared/BuildRequestConfiguration.cs | Marks project load/eval into configuration as RUC. |
| src/Build/BackEnd/Node/OutOfProcNode.cs | Adds message-pump suppression boundary; propagates RUC into request handling. |
| src/Build/BackEnd/Node/InProcNode.cs | Adds message-pump suppression boundary; propagates RUC into request handling. |
| src/Build/BackEnd/Components/SdkResolution/SdkResolverService.cs | Marks SDK resolution (runtime resolver load/reflection) as RUC. |
| src/Build/BackEnd/Components/SdkResolution/SdkResolverLoader.cs | Marks SDK resolver discovery/loading/reflection as RUC. |
| src/Build/BackEnd/Components/SdkResolution/OutOfProcNodeSdkResolverService.cs | Propagates RUC to out-of-proc resolver service. |
| src/Build/BackEnd/Components/SdkResolution/MainNodeSdkResolverService.cs | Adds suppression boundary in packet handler; propagates RUC to ResolveSdk. |
| src/Build/BackEnd/Components/SdkResolution/ISdkResolverService.cs | Adds RUC to SDK resolver service contract. |
| src/Build/BackEnd/Components/SdkResolution/HostedSdkResolverServiceBase.cs | Adds RUC to hosted resolver service base contract. |
| src/Build/BackEnd/Components/SdkResolution/CachingSdkResolverService.cs | Propagates RUC to caching wrapper. |
| src/Build/BackEnd/Components/SdkResolution/CachingSdkResolverLoader.cs | Propagates RUC to caching loader overrides. |
| src/Build/BackEnd/Components/RequestBuilder/TaskHost.cs | Adds suppression boundary for public task-callback contract; propagates RUC into internal build entrypoints. |
| src/Build/BackEnd/Components/RequestBuilder/TaskBuilder.cs | Propagates RUC to task execution pipeline methods. |
| src/Build/BackEnd/Components/RequestBuilder/TargetEntry.cs | Propagates RUC to target execution pipeline methods. |
| src/Build/BackEnd/Components/RequestBuilder/TargetBuilder.cs | Propagates RUC to target build/callback methods. |
| src/Build/BackEnd/Components/RequestBuilder/RequestBuilder.cs | Propagates RUC through request builder thread/pump paths. |
| src/Build/BackEnd/Components/RequestBuilder/ITaskBuilder.cs | Adds RUC to task-builder contract. |
| src/Build/BackEnd/Components/RequestBuilder/ITargetBuilderCallback.cs | Adds RUC to target-builder callback contract. |
| src/Build/BackEnd/Components/RequestBuilder/ITargetBuilder.cs | Adds RUC to target-builder contract. |
| src/Build/BackEnd/Components/RequestBuilder/IRequestBuilderCallback.cs | Adds RUC to request-builder callback contract. |
| src/Build/BackEnd/Components/RequestBuilder/IRequestBuilder.cs | Adds RUC to request-builder contract. |
| src/Build/BackEnd/Components/RequestBuilder/IntrinsicTasks/MSBuild.cs | Adds suppression boundary for intrinsic-task reflective execution wall. |
| src/Build/BackEnd/Components/RequestBuilder/IntrinsicTasks/IntrinsicTaskFactory.cs | Adds DAM annotations to intrinsic task factory TaskType. |
| src/Build/BackEnd/Components/ProjectCache/ProjectCacheService.cs | Marks plugin loading/reflection/materialization as RUC; adds DAM for ctor activation. |
| src/Build/BackEnd/Components/Logging/LoggingService.cs | Removes reflection for in-box forwarding logger; factors core registration helper; adds RUC to reflective entrypoints. |
| src/Build/BackEnd/Components/Logging/ILoggingService.cs | Adds RUC to distributed logger registration / node logger init contracts. |
| src/Build/BackEnd/Components/Logging/BuildErrorTelemetryTracker.cs | Replaces enum-values reflection with constant-length array sizing. |
| src/Build/BackEnd/Components/BuildRequestEngine/IBuildRequestEngine.cs | Adds RUC to engine contract entrypoints. |
| src/Build/BackEnd/Components/BuildRequestEngine/BuildRequestEngine.cs | Propagates RUC into engine implementation; adds suppression boundaries for event handlers. |
| src/Build/BackEnd/BuildManager/BuildSubmission.cs | Propagates RUC through submission execution entrypoints. |
| src/Build/BackEnd/BuildManager/BuildManager.cs | Propagates RUC through build lifecycle and packet processing entrypoints; adds message-pump suppression boundary. |
There was a problem hiding this comment.
Design Before Implementation — ISSUE
SEVERITY: MAJOR
FILE: src/Build/BackEnd/Components/Logging/BuildErrorTelemetryTracker.cs
LINES: 39 (the _errorCounts initializer)
SCENARIO: A developer adds a new ErrorCategory value after Other — e.g., ErrorCategory.Security at ordinal 16. (int)ErrorCategory.Other + 1 remains 16 (array indices 0–15). TrackError calls Interlocked.Increment(ref _errorCounts[(int)ErrorCategory.Security]) which dereferences _errorCounts[16] → IndexOutOfRangeException at runtime. There is nothing in the code that enforces the "Other is always last" invariant, so this is a silent time bomb for the next contributor who extends the enum.
FINDING: The replacement of Enum.GetValues(typeof(ErrorCategory)).Length with (int)ErrorCategory.Other + 1 avoids the reflection/trimming warning but introduces a fragile positional assumption that is not machine-checkable. The original code was self-maintaining; the new code is not.
RECOMMENDATION: Use the trim-safe generic overload Enum.GetValues<ErrorCategory>().Length (available since .NET 5, annotated only with [RequiresDynamicCode], not [RequiresUnreferencedCode], so it does not trigger IL2026). Add a #if NET guard for the .NET Framework build path (Enum.GetValues(typeof(ErrorCategory)).Length). Alternatively, add a sentinel Count member at the end of the enum and use new int[(int)ErrorCategory.Count] — that is the classic pattern to make array sizing both AOT-safe and self-maintaining.
All other evaluated dimensions are clean:
WorkItemclass (ParallelWorkSet.cs): The explicit comment correctly explains whyLazy<T>is unsuitable (its[DynamicallyAccessedMembers]annotation onTpropagates toParallelWorkSet<TKey, TResult>and produces IL2091). The documented single-writer / happens-before model makes the lock-freeValuegetter sound. LGTM.RegisterDistributedLoggerCoreextraction (LoggingService.cs): Both call sites hold_lockObjectbefore enteringCore, so all shared mutable state is properly guarded. The extraction cleanly separates the reflection-free fast path (RegisterLoggerwith a pre-instantiatedCentralForwardingLogger) from the reflection path. LGTM.ReflectableTaskPropertyInfo.csmanual loop: The change fromGetProperty(..., BindingFlags.IgnoreCase)(which requiredNonPublicPropertiesin the DAM annotation) to an explicit loop overGetProperties(PublicProperties)withOrdinalIgnoreCaseis the correct trim-safe replacement. Using LINQFirstOrDefaultwould be semantically equivalent but would allocate an enumerator; the manual loop is preferred on a property-lookup hot path. LGTM.Array.CreateInstanceFromArrayType(TaskParameter.cs,TaskExecutionHost.cs): Switching the switch arms to concrete array types (typeof(char[])etc.) and usingArray.CreateInstanceFromArrayTypeis exactly the right API for AOT-safe typed-array construction, with a correct#if NETfallback for .NET Framework. LGTM.
Warning
Firewall blocked 2 domains
The following domains were blocked by the firewall during workflow execution:
dnceng.pkgs.visualstudio.compkgs.dev.azure.com
To allow these domains, add them to the
network.allowedlist in your workflow frontmatter:
network:
allowed:
- defaults
- "dnceng.pkgs.visualstudio.com"
- "pkgs.dev.azure.com"See Network Configuration for more information.
Generated by Expert Code Review (on open) for issue #14064 · 5.7K AIC · ⌖ 12.4 AIC · ⊞ 30.1K ambient context
c720aed to
360ef93
Compare
…14079) ## Summary Splits the **functional / method-body changes** out of the broader trim-enablement work (#14064) so they can be reviewed independently of the annotation churn. This PR contains **no** `[RequiresUnreferencedCode]`/`[DynamicallyAccessedMembers]` propagation cascade and does **not** flip on the trim/AOT analyzers — those remain in #14064. ### Property-function evaluation (made trim-compatible by construction) - `Function<T>._receiverType` (and the `Function` ctor `receiverType` parameter) are annotated with `[DynamicallyAccessedMembers(All)]` as a single chokepoint, with proof-based suppressions on the `InvokeMember`/`GetMethods`/`GetConstructor` sites. - The curated receiver-type allowlist (`AvailableStaticMethods`) is preserved with `[DynamicDependency]`, constrained to the members property functions actually use (public ctors/methods/properties/fields; non-public methods for `IntrinsicFunctions`). - The runtime assembly-probing path (`MSBUILDENABLEALLPROPERTYFUNCTIONS=1`) is gated behind the `EnableAllPropertyFunctions` trimmer feature switch so the trimmer removes it; the env var stays honored at run time. net472 polyfills added for `FeatureSwitchDefinition`/`DynamicDependency`. ### Functional changes making reflection AOT/trim-friendly (no behavior change) - **TaskParameter / TaskExecutionHost**: construct typed arrays via `Array.CreateInstanceFromArrayType` instead of `Array.CreateInstance(elementType)`. - **ParallelWorkSet**: a custom `WorkItem` replaces `Lazy<TResult>` (avoids the DAM requirement `Lazy<T>` places on `TResult`). - **BuildErrorTelemetryTracker**: size the count array from a `Count` sentinel instead of `Enum.GetValues(...)`. - **LoggingService**: instantiate the built-in `CentralForwardingLogger` directly instead of via reflection. - **ReflectableTaskPropertyInfo**: resolve the property via `GetProperties()` enumeration (also avoids `AmbiguousMatchException` on shadowed properties). ## Validation - `Microsoft.Build` compiles for **net10.0** (`-p:IsTrimmable=true`) and **net472**. - Targeted unit tests pass: `ParallelWorkSet`, `TaskExecutionHost_Tests`, `LoggingService_Tests`, `TaskParameter`.
360ef93 to
db37c8a
Compare
b138d25 to
b0dce1c
Compare
Makes the MSBuild evaluation object model trim- and Native-AOT-capable so an AOT-compiled host (the dotnet CLI) can evaluate and build in-process, and fail observably where reflective loading is required so the host can fall back to a JIT MSBuild. Code: trim/AOT annotations, feature switches, and closed-world host-registration APIs (SdkResolver.Register, Task.RegisterTask, TaskItem task-parameter-type registration); new diagnostics MSB4282/MSB4283 for unsupported reflective paths. Validation: a Native AOT harness that publishes and runs an AOT image, evaluating and building the classlib (library) and console (executable) templates. Documentation: design/strategy notes, living suppression and annotation trackers, per-area deep dives, and the host-registration API specs.
Add ReadAllBytes/GetCreationTimeUtc/GetLastWriteTimeUtc to the read-only File property-function allowlist; the -mt tests for those methods now rely on the allowlist instead of the escape hatch. Route every EnableAllPropertyFunctions hatch test through the AppContext feature switch for deterministic, order-independent behavior, and keep a single env-var test that reflectively clears the switch (AppContext has no public unset state) to vet that MSBUILDENABLEALLPROPERTYFUNCTIONS still flows. Null-guard the BuildProject finally so a rejected overlapping build cannot NRE.
…ontext value When the .NET SDK hosts MSBuild in a trimmed / Native AOT process the BCL 'where am I' APIs point at the muxer / install root, not the versioned SDK directory that contains MSBuild. Following dotnet/sdk#55110, read the SDK-published 'Microsoft.DotNet.Sdk.Root' AppContext value (new DotNetSdkPaths helper) as an early step in BuildEnvironmentHelper resolution, before falling back to process/assembly discovery.
The rebase onto upstream/main integrated the typed TaskItem<T> / ITaskItem<T> task-parameter feature into Microsoft.Build, which this branch compiles with the trim/AOT analyzers enabled. Fix the merge-resolved using block (drop a duplicate System.Diagnostics and an unused System.Globalization) and guard CreateTaskItemOfT's MakeGenericType + expression Compile() behind RuntimeFeature.IsDynamicCodeSupported so it fails observably under trimming / Native AOT (clearing IL3050). Tracked as follow-up item 7.
b0dce1c to
89e0239
Compare
Windows Core failure — investigationTL;DR: The What failed
Why it's unrelated to this PR
Why it's flaky
RecommendationRe-run the Windows Core leg — no code change is warranted for this PR. If the Coordinator suite keeps flaking it may warrant an additional quarantine entry, but that's separate from this change. |
|
@ViktorHofer I've updated for merge conflicts and the AppContext SDK root that the SDK pushes. |
|
How should we maintain that list of BuildInTasks? I assume we don't want to register ALL msbuild inbox tasks? https://github.com/dotnet/msbuild/blob/main/src/Tasks/Microsoft.Common.tasks |
|
/review |
Updated [Microsoft.Build.Utilities.Core](https://github.com/dotnet/msbuild) from 18.9.6 to 18.10.1. <details> <summary>Release notes</summary> _Sourced from [Microsoft.Build.Utilities.Core's releases](https://github.com/dotnet/msbuild/releases)._ ## 18.10.1 ## What's Changed * [vs16.11] Update dependencies from dotnet/arcade by @dotnet-maestro[bot] in dotnet/msbuild#13103 * [vs17.12] Update dependencies from dotnet/arcade by @dotnet-maestro[bot] in dotnet/msbuild#13796 * [vs17.8] Update dependencies from dotnet/arcade by @dotnet-maestro[bot] in dotnet/msbuild#13902 * [vs17.11] Update dependencies from dotnet/arcade by @dotnet-maestro[bot] in dotnet/msbuild#13903 * [vs17.12] Update dependencies from dotnet/arcade by @dotnet-maestro[bot] in dotnet/msbuild#13909 * [vs17.12] Update dependencies from dotnet/arcade by @dotnet-maestro[bot] in dotnet/msbuild#13986 * Add vs18.9 to merge-flow config; retire vs18.3 by @JanProvaznik in dotnet/msbuild#14214 * Bump labeler-cache-retention to use issue-labeler v2.1.0 by @jeffhandley in dotnet/msbuild#14171 * Bump main to 18.10.0 after vs18.9 snap by @JanProvaznik in dotnet/msbuild#14216 * Improve release skill: Phase 2 DARC rules, VMR backflow, deterministic baseline by @JanProvaznik in dotnet/msbuild#14220 * Determinize release: hardcode OptProf baseline + Phase 3.2 baseline resolver by @JanProvaznik in dotnet/msbuild#14222 * Serialize BuildRequestConfiguration.RequestedTargets to fix solution metaproject MSB4057 in parallel builds by @ViktorHofer in dotnet/msbuild#14223 * [main] Update dependencies from nuget/nuget.client by @dotnet-maestro[bot] in dotnet/msbuild#14203 * Core support for AbsolutePath/FileInfo/DirectoryInfo and ITaskItem<T> as task parameters by @baronfel in dotnet/msbuild#13971 * [main] Update dependencies from dotnet/roslyn by @dotnet-maestro[bot] in dotnet/msbuild#14206 * Fix existence cache kind poisoning by @AlesProkop in dotnet/msbuild#14249 * [main] Source code updates from dotnet/dotnet by @dotnet-maestro[bot] in dotnet/msbuild#14226 * Don't disable the MSBuild server for /mt builds when node reuse is off by @AR-May in dotnet/msbuild#14248 * Enhance expert reviewer guidelines with additional checks. by @AR-May in dotnet/msbuild#14255 * [main] Source code updates from dotnet/dotnet by @dotnet-maestro[bot] in dotnet/msbuild#14253 * [main] Update dependencies from dotnet/roslyn by @dotnet-maestro[bot] in dotnet/msbuild#14268 * [main] Update dependencies from nuget/nuget.client by @dotnet-maestro[bot] in dotnet/msbuild#14267 * Bump github/gh-aw-actions/setup from 0.81.6 to 0.82.2 by @dependabot[bot] in dotnet/msbuild#14266 * Avoid boxing the struct enumerator in PropertyDictionary<T>.GetEnumerator() by @nareshjo in dotnet/msbuild#14272 * Refresh copy marker when implementation output changes by @AlesProkop in dotnet/msbuild#14231 * Send task-host build process environment as delta by @OvesN in dotnet/msbuild#14126 * Add regression coverage for metadata newline preservation by @VolPlita in dotnet/msbuild#14261 * Fix EmbedInBinlog items with relative paths from child projects by @huulinhnguyen-dev in dotnet/msbuild#13990 * Stop requiring VersionPrefix updates in servicing - insert prerelease versions to VS by @ViktorHofer in dotnet/msbuild#14277 * Fix WriteLinesToFile rewriting unchanged file when custom encoding is used by @huulinhnguyen-dev in dotnet/msbuild#14146 * Enable trim/AOT analyzers for Microsoft.Build and clean up annotations by @JeremyKuhne in dotnet/msbuild#14064 * [automated] Merge branch 'vs18.9' => 'main' by @github-actions[bot] in dotnet/msbuild#14291 * Fix MicroBuild plugin feed URL to use allowed pkgs.dev.azure.com format by @AlesProkop in dotnet/msbuild#14295 * Pass ExcludeRestorePackageImports during restore to avoid redundant evaluations by @ViktorHofer with @Copilot in dotnet/msbuild#14274 * [vs18.7] Update dependencies from dotnet/arcade by @dotnet-maestro[bot] in dotnet/msbuild#13988 * Adopt Clever Test Selection (CTS) as parallel, non-blocking PR pipeline by @jankratochvilcz in dotnet/msbuild#14212 * Harden exceptions when connecting to server by @JanProvaznik in dotnet/msbuild#14292 * Update MicrosoftBuildVersion in analyzer template by @github-actions[bot] in dotnet/msbuild#13886 * Fix MSBuild Server client dropping build result under WaitAny race (#14172) by @JanProvaznik in dotnet/msbuild#14251 * Partially revert #13660: remove NuGet RestoreTask transient TaskHost workaround by @JanProvaznik in dotnet/msbuild#14297 * Disable daily AI credits guardrail for Expert Code Review workflow by @JanProvaznik with @Copilot in dotnet/msbuild#14314 * Localized file check-in by OneLocBuild Task: Build definition ID 9434: Build ID 14614733 by @dotnet-bot in dotnet/msbuild#14246 * Add opt-in partial (stop-after-pass) project evaluation by @ViktorHofer in dotnet/msbuild#14290 * Use partial evaluation for -getProperty/-getItem without a target by @ViktorHofer in dotnet/msbuild#14296 * [main] Source code updates from dotnet/dotnet by @dotnet-maestro[bot] in dotnet/msbuild#14324 * [main] Update dependencies from dotnet/roslyn by @dotnet-maestro[bot] in dotnet/msbuild#14333 * [main] Update dependencies from nuget/nuget.client by @dotnet-maestro[bot] in dotnet/msbuild#14330 * Bump github/gh-aw-actions/setup from 0.82.2 to 0.82.8 by @dependabot[bot] in dotnet/msbuild#14328 * Restrict partial evaluation to ProjectInstance by @ViktorHofer in dotnet/msbuild#14340 ... (truncated) Commits viewable in [compare view](dotnet/msbuild@v18.9.6...v18.10.1). </details> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Summary
Makes the Microsoft.Build evaluation object model trim- and Native-AOT-capable so an AOT-compiled host (the
dotnetCLI) can evaluate and build projects in-process, and fail observably wherever a path genuinely requires run-time reflection (loading a task, SDK resolver, logger, or build check discovered by name) so the host can fall back to a JIT MSBuild - never a silent no-op and never a crash deep in the engine.The work is evaluation-first: evaluation is kept fully trim/AOT-clean today, and execution becomes possible through closed-world host registration (registered SDK resolvers and task classes) while open-world reflective paths fail observably. The build is clean on both
net10.0andnet472(0 IL warnings, 0 warnings, 0 errors).Change breakdown
About half the diff is documentation; validation and engine code are each roughly a quarter. The engine change is intentionally small and surgical - overwhelmingly trim/AOT annotations and feature-switch gating rather than behavior changes to the JIT path.
.xlf/.resxfor 4 new diagnostics).gitignore)The actual engine code change is ~2,000 lines across 69 files; the remaining ~7,000 lines are documentation, the validation harness/tests, and auto-generated localization.
New public API surface
Three reflection-free, closed-world registration entry points let a host hand MSBuild the types it needs up front so the trimmer preserves them:
Microsoft.Build.Framework.SdkResolver.Register(SdkResolver)- contribute a pre-constructed SDK resolver instead of MSBuild discovering andAssembly.LoadFrom-ing one by reflection.Microsoft.Build.Utilities.Task.RegisterTask<T>()(plus aRegisterTask(string, Func<ITask>)overload) - register a task class so the engine constructs, binds, and runs it with reflective task execution disabled.Microsoft.Build.Utilities.TaskItem.RegisterTaskParameterValueType<T>()andRegisterTaskParameterItemType<T>()- resolve<UsingTask>/<ParameterGroup>parameter types without a by-nameType.GetType.Four new diagnostics report unsupported reflective paths under trimming/AOT:
API-review callout: besides the additive registration APIs above, there is a deliberate public trim-metadata change - the public
ITaskFactory/ITaskFactory2/ITaskFactory3InitializeandCreateTaskmembers now carry[RequiresUnreferencedCode], andITaskFactory.TaskTypecarries[DynamicallyAccessedMembers(PublicProperties)]. There is no managed signature change, but a host reaching task creation through the interface now sees an honestIL2026, and a third-partyITaskFactorythat enables trim analysis getsIL2046until it adds the matching attribute.AOT validation
A new Native AOT validation harness (
src/aot-validation/) publishes a fully AOT-compiled image and runs it end-to-end. It validates that AOT works for both evaluating and building the basic library (dotnet new classlib) and executable (dotnet new console) project templates:DotnetNew_Classlib_EvaluatesAsLibraryProject,DotnetNew_Console_EvaluatesAsExecutableProject: evaluate the stock templates through the object model under AOT.DotnetNew_Classlib_BuildUnderAot_RunsRegisteredTasksThenFailsObservably,DotnetNew_Console_BuildUnderAot_RunsRegisteredTasksThenFailsObservably: build the templates in-process under AOT, confirming registered/intrinsic tasks run and that an unsupported reflective task fails observably (MSB4283) rather than crashing.Additional harness coverage exercised against the published AOT image: object-model evaluation, property-function reachability, and each of the three registration APIs (SDK resolver, task class, task-parameter type).
Design criterion: fail observably, never silently
A trimmed/AOT path that cannot run surfaces a reported error (or a host-readable property) so the host can branch and fall back; it never silently drops a project's expressed intent and never crashes in the engine.
[UnconditionalSuppressMessage]is used only for provable false positives; accurate warnings are gated behind feature switches (with observable failure) or carry honest[RequiresUnreferencedCode]to a public boundary.Design notes, the strategy catalog, and living suppression/annotation trackers are in
documentation/aot/; the host-registration API proposals are indocumentation/specs/.