Don't log TaskAssemblyLocationMismatch for task-host-routed tasks - #14550
Conversation
The TaskAssemblyLocationMismatch diagnostic reads TaskInstance.GetType().Assembly.Location to detect when Assembly.LoadFrom() resolved a task assembly to a different location than the one we probed. That read is only meaningful when TaskInstance *is* the task. For a task routed to a TaskHost, TaskInstance is a TaskHostTask proxy declared in Microsoft.Build.dll, and the real task assembly is loaded in the task host process - so the read can never say anything about where the task came from. IsTaskAssemblyMatchFactoryType() was added to suppress the message in that case, but it compared AssemblyLoadInfo.AssemblyLocation with LoadedType.Path. AssemblyLoadInfo.AssemblyLocation returns the assembly *name* for tasks registered by AssemblyName (nearly everything in Microsoft.Common.tasks), and for tasks registered by AssemblyFile it returns the registration-time path, which is not always byte-identical to the normalized Assembly.Location. Either way the comparison fails and the message is logged. The guard was also tautological in every real code path: AssemblyTaskFactory hands the same LoadedType instance to both TaskFactoryWrapper.TaskFactoryLoadedType and TaskHostTask, so it could only ever produce false positives. Because virtually nothing runs in a TaskHost in a normal build, this was never exercised. Multi-threaded mode (-mt) routes every task without [MSBuildMultiThreadableTask] to a sidecar TaskHost, which made it the common path: a self-build of dotnet/msbuild emitted ~1200 extra normal-importance messages, drowning out real assembly-identity problems and inflating binlogs. Skip the diagnostic for TaskHostTask instead, before the wasted Assembly.Location read. The in-proc case the diagnostic was written for is unchanged. This also covers the second TaskHost route (CreateTaskHostTaskForOutOfProcFactory, used for inline tasks under -mt), which a fix comparing AssemblyLoadInfo objects would have missed. No ChangeWave: this only removes bogus messages, no build behavior changes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2493860f-1d05-4dee-9d50-04eb95d1e624
There was a problem hiding this comment.
Pull request overview
This PR reduces binlog and console noise by preventing TaskAssemblyLocationMismatch from being logged for TaskHost-routed tasks, where the in-proc TaskHostTask is only a proxy and its Assembly.Location is inherently unrelated to the actual task assembly (loaded in the task host process).
Changes:
- Skip the
Assembly.Locationmismatch diagnostic whenTaskInstanceis aTaskHostTask(both NET and non-NET TFMs). - Remove the now-dead
IsTaskAssemblyMatchFactoryTypelocal function and the unusedTaskHostTask.LoadedTaskAssemblyInfoproperty. - Add an integration regression test ensuring TaskHost-routed tasks do not emit
TaskAssemblyLocationMismatch(covers both-mtrouting and explicitTaskFactory="TaskHostFactory"routing).
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/Build/Instance/TaskFactories/TaskHostTask.cs | Removes unused internal LoadedTaskAssemblyInfo accessor. |
| src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs | Suppresses mismatch diagnostic for TaskHostTask proxies; deletes dead/incorrect guard logic. |
| src/Build.UnitTests/BackEnd/TaskRouter_IntegrationTests.cs | Adds regression coverage for the “no mismatch message for TaskHost-routed tasks” behavior. |
Comments suppressed due to low confidence (1)
src/Build.UnitTests/BackEnd/TaskRouter_IntegrationTests.cs:427
- Prefer collection expressions over
new[] { ... }for target-typed string arrays in tests.
new[] { "TestTarget" },
There was a problem hiding this comment.
Thanks for the fix — I reviewed this across the MSBuild dimensions and I don’t see any blocking issues.
The TaskInstance is not TaskHostTask guard is the right fix: for task-host-routed tasks the in-proc proxy type always comes from Microsoft.Build.dll, so the previous assembly-location comparison was inherently meaningless and produced spurious TaskAssemblyLocationMismatch messages.
A few specific checks:
- Back-compat / ChangeWave: this removes incorrect
MessageImportance.Normaloutput only; it does not change execution semantics, warnings, or errors. I don’t think this needs a ChangeWave. - API surface:
TaskHostTaskis internal, and removingLoadedTaskAssemblyInfois not a public API break. - Test coverage: the new regression test covers both TaskHost routing paths (
MultiThreaded=trueand explicitTaskHostFactory) and uses a locale-safe assertion for the message text. - Cross-platform: the
Path.Combine(..., ".", ...)non-normalized path case is valid on Unix as well, so the test looks portable. - .NET Framework branch: the new
#elseguard is consistent with the bug fix there too; previously the suppression helper was ineffective forTaskHostTask, so skipping the diagnostic entirely for proxies is the correct behavior.
I also checked the removed member in TaskHostTask; it was internal-only and I found no remaining references.
Overall: no findings from my review.
Warning
Firewall blocked 1 domain
The following domain was blocked by the firewall during workflow execution:
awmgmcpg
To allow these domains, add them to the
network.allowedlist in your workflow frontmatter:
network:
allowed:
- defaults
- "awmgmcpg"See Network Configuration for more information.
Generated by Expert Code Review (on open) for #14550 · sonnet46 · 71 AIC · ⌖ 5.11 AIC · ⊞ 4.9K
The findings document carried a full write-up of the spurious TaskAssemblyLocationMismatch messages - root cause, the offending code, a suggested fix, a distribution table. #14550 already fixes it, so none of that belongs in the repo. Replaced with a short statement of what the difference is, that it is log noise, and a link to that PR. Same for the reason fields on the two knownMtOnly rules, which restated the analysis at length. Also links #13603, which asked for this validation leg, and lists the pool runs behind the numbers, including the failed ones and what caused them. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: f9d28016-afd7-451c-a9f2-2450478f93a6
#14550 stops MSBuild logging TaskAssemblyLocationMismatch for task-host-routed tasks, which was the only log difference -mt produced. Both allowances go: the one for the messages themselves and the one for the target headers they caused targets to acquire at normal verbosity. The log comparison is now strict in both directions - any line on the -mt side that the baseline does not have, and that the control run does not also produce, fails the pipeline. The list should stay empty; an entry in it is an unfixed bug, not a normalization. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: f9d28016-afd7-451c-a9f2-2450478f93a6
… main Removing them after #14550 merged and rerunning on the pool (14817555) failed with the same 1364 messages and 12 collateral target headers. They are logged by C:\Program Files\Microsoft Visual Studio\18\Enterprise\MSBuild\Current\Bin\ Microsoft.Build.dll - the installed VS MSBuild that *drives* the build. This pipeline applies -mt to the driver, not to the MSBuild the build produces, so a fix in this repo changes nothing here until it ships in the VS on the agents. Restored both entries with that explanation, and wrote the general rule down in both the harness README and the findings document, because it is not obvious and the next person to see a fixed bug still listed here will want to know why. The run was still useful as evidence: 0 artifact differences and the three builds structurally identical on the post-merge tree (384 targets / 18 993 executions, 9 306 (project, target) pairs, 122 tasks / 16 084, 53 projects / 1 842). Replaying that run's own binlogs with the entries restored passes, so no further pool time was spent re-confirming it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: f9d28016-afd7-451c-a9f2-2450478f93a6
Read out of the run's own binlog: VSEng-MicroBuildVSStable drives the build with MSBuildVersion 18.8.2, while #14550 is in main, which is 18.11. That is the gap the entries are waiting on, so it is worth stating rather than leaving 'once it reaches the agents'. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: f9d28016-afd7-451c-a9f2-2450478f93a6
Context
-mtbuilds of OrchardCore emit ~1200 spuriousTaskAssemblyLocationMismatchmessages at normal importance that a baseline build does not:They are pure noise — no build output changes — but they drown out real assembly-identity problems, inflate binlogs, and give 11 otherwise-silent targets a target header at
-v:n.Root cause
The diagnostic reads
TaskInstance.GetType().Assembly.Locationto detect whenAssembly.LoadFrom()resolved a task assembly to a location other than the one we probed. That read is only meaningful whenTaskInstanceis the task. For a task routed to a TaskHost,TaskInstanceis aTaskHostTaskproxy declared inMicrosoft.Build.dll, and the real assembly is loaded in the task host process — so the read can never say anything about where the task came from.IsTaskAssemblyMatchFactoryType()(added in #12509) was meant to suppress the message in that case, but it comparedAssemblyLoadInfo.AssemblyLocationwithLoadedType.Path:AssemblyLoadInfo.AssemblyLocationreturns the assembly name for tasks registered byAssemblyName— nearly everything inMicrosoft.Common.tasks.AssemblyFileit returns the registration-time path, which is not always byte-identical to the normalizedAssembly.Location.Either way the comparison fails and the message is logged.
The guard was also tautological in every real code path:
AssemblyTaskFactoryhands the sameLoadedTypeinstance to bothTaskFactoryWrapper.TaskFactoryLoadedTypeandTaskHostTask, so it could only ever produce false positives — it could not detect a genuine mismatch.Because virtually nothing runs in a TaskHost in a normal build, this was never exercised.
-mtroutes every task without[MSBuildMultiThreadableTask]to a sidecar TaskHost, making it the common path.Changes
Skip the diagnostic when
TaskInstance is TaskHostTask, before the (now wasted)Assembly.Locationread. The in-procAssembly.LoadFromidentity-redirect case the diagnostic was written for is unchanged.This also covers the second TaskHost route —
CreateTaskHostTaskForOutOfProcFactory, used for inline tasks under-mt— which a fix that merely comparedAssemblyLoadInfoobjects would have missed.Removed the now-dead
IsTaskAssemblyMatchFactoryTypelocal function andTaskHostTask.LoadedTaskAssemblyInfo(internal type, no API surface change).No ChangeWave: this only removes bogus normal-importance messages; no build behavior changes.
Testing
New
TaskHostRoutedTask_DoesNotLogAssemblyLocationMismatchtheory inTaskRouter_IntegrationTests, covering both TaskHost routes:MultiThreaded = truewith the default factory (sidecar TaskHost — the reported scenario)TaskFactory="TaskHostFactory"without-mt(proves the bug isn't-mt-specific)The registered
AssemblyFiledeliberately contains a redundant.segment so it is not byte-identical toAssembly.Location— the same normalization difference real builds hit. Verified the test fails onmainwith exactly the reported message and passes with the fix.Also ran
TaskRouter_IntegrationTests,TaskHostFactory_TestsandTaskExecutionHost_Tests(165/165 onnet11.0andnet472) plus the fullMicrosoft.Build.Engine.UnitTestssuite; remaining failures are pre-existing/environmental and unrelated.Notes
Reviewers may want to consider a follow-up that makes the diagnostic meaningful for out-of-proc tasks by reporting it from the task host process, where the assembly is actually loaded.