Agent skill

eval-performance

Guide for diagnosing and improving MSBuild project evaluation performance. Only activate in MSBuild/.NET build context. USE FOR: builds slow before any compilation starts, high evaluation time in binlog analysis, expensive glob patterns walking large directories (node_modules, .git, bin/obj), deep import chains (>20 levels), preprocessed output >10K lines indicating heavy evaluation, property functions with file I/O ($([System.IO.File]::ReadAllText(...))), multiple evaluations per project. Covers the 5 MSBuild evaluation phases, glob optimization via DefaultItemExcludes, import chain analysis with /pp preprocessing. DO NOT USE FOR: compilation-time slowness (use build-perf-diagnostics), incremental build issues (use incremental-build), non-MSBuild build systems. INVOKES: dotnet msbuild -pp:full.xml for preprocessing, /clp:PerformanceSummary.

Stars 302
Forks 22

Install this agent skill to your Project

npx add-skill https://github.com/managedcode/dotnet-skills/tree/main/catalog/Tools/Official-DotNet-MSBuild/skills/eval-performance

SKILL.md

MSBuild Evaluation Phases

For a comprehensive overview of MSBuild's evaluation and execution model, see Build process overview.

  1. Initial properties: environment variables, global properties, reserved properties
  2. Imports and property evaluation: process <Import>, evaluate <PropertyGroup> top-to-bottom
  3. Item definition evaluation: <ItemDefinitionGroup> metadata defaults
  4. Item evaluation: <ItemGroup> with Include, Remove, Update, glob expansion
  5. UsingTask evaluation: register custom tasks

Key insight: evaluation happens BEFORE any targets run. Slow evaluation = slow build start even when nothing needs compiling.

Diagnosing Evaluation Performance

Using binlog

  1. Replay the binlog: dotnet msbuild build.binlog -noconlog -fl -flp:v=diag;logfile=full.log
  2. Search for evaluation events: grep -i 'Evaluation started\|Evaluation finished' full.log
  3. Multiple evaluations for the same project = overbuilding
  4. Look for "Project evaluation started/finished" messages and their timestamps

Using /pp (preprocess)

  • dotnet msbuild -pp:full.xml MyProject.csproj
  • Shows the fully expanded project with ALL imports inlined
  • Use to understand: what's imported, import depth, total content volume
  • Large preprocessed output (>10K lines) = heavy evaluation

Using /clp:PerformanceSummary

  • Add to build command for timing breakdown
  • Shows evaluation time separately from target/task execution

Expensive Glob Patterns

  • Globs like **/*.cs walk the entire directory tree
  • Default SDK globs are optimized, but custom globs may not be
  • Problem: globbing over node_modules/, .git/, bin/, obj/ — millions of files
  • Fix: use <DefaultItemExcludes> to exclude large directories
  • Fix: be specific with glob paths: src/**/*.cs instead of **/*.cs
  • Fix: use <EnableDefaultItems>false</EnableDefaultItems> only as last resort (lose SDK defaults)
  • Check: grep for Compile items in the diagnostic log → if Compile items include unexpected files, globs are too broad

Import Chain Analysis

  • Deep import chains (>20 levels) slow evaluation
  • Each import: file I/O + parse + evaluate
  • Common causes: NuGet packages adding .props/.targets, framework SDK imports, Directory.Build chains
  • Diagnosis: /pp output → search for <!-- Importing comments to see import tree
  • Fix: reduce transitive package imports where possible, consolidate imports

Multiple Evaluations

  • A project evaluated multiple times = wasted work
  • Common causes: referenced from multiple other projects with different global properties
  • Each unique set of global properties = separate evaluation
  • Diagnosis: grep 'Evaluation started.*ProjectName' full.log → if count > 1, check for differing global properties
  • Fix: normalize global properties, use graph build (/graph)

TreatAsLocalProperty

  • Prevents property values from flowing to child projects via MSBuild task
  • Overuse: declaring many TreatAsLocalProperty entries adds evaluation overhead
  • Correct use: only when you genuinely need to override an inherited property

Property Function Cost

  • Property functions execute during evaluation
  • Most are cheap (string operations)
  • Expensive: $([System.IO.File]::ReadAllText(...)) during evaluation — reads file on every evaluation
  • Expensive: network calls, heavy computation
  • Rule: property functions should be fast and side-effect-free

Optimization Checklist

  • Check preprocessed output size: dotnet msbuild -pp:full.xml
  • Verify evaluation count: should be 1 per project per TFM
  • Exclude large directories from globs
  • Avoid file I/O in property functions during evaluation
  • Minimize import depth
  • Use graph build to reduce redundant evaluations
  • Check for unnecessary UsingTask declarations

Expand your agent's capabilities with these related and highly-rated skills.

managedcode/dotnet-skills

dotnet-project-setup

Create or reorganize .NET solutions with clean project boundaries, repeatable SDK settings, and a maintainable baseline for libraries, apps, tests, CI, and local development.

302 22
Explore
managedcode/dotnet-skills

csharp-scripts

Run single-file C# programs as scripts (file-based apps) for quick experimentation, prototyping, and concept testing. Use when the user wants to write and execute a small C# program without creating a full project.

302 22
Explore
managedcode/dotnet-skills

dotnet-pinvoke

Correctly call native (C/C++) libraries from .NET using P/Invoke and LibraryImport. Covers function signatures, string marshalling, memory lifetime, SafeHandle, and cross-platform patterns. USE FOR: writing new P/Invoke or LibraryImport declarations, reviewing or debugging existing native interop code, wrapping a C or C++ library for use in .NET, diagnosing crashes, memory leaks, or corruption at the managed/native boundary. DO NOT USE FOR: COM interop, C++/CLI mixed-mode assemblies, or pure managed code with no native dependencies.

302 22
Explore
managedcode/dotnet-skills

nuget-trusted-publishing

Set up NuGet trusted publishing (OIDC) on a GitHub Actions repo — replaces long-lived API keys with short-lived tokens. USE FOR: trusted publishing, NuGet OIDC, keyless NuGet publish, migrate from NuGet API key, NuGet/login, secure NuGet publishing. DO NOT USE FOR: publishing to private feeds or Azure Artifacts (OIDC is nuget.org only). INVOKES: shell (powershell or bash), edit, create, ask_user for guided repo setup.

302 22
Explore
managedcode/dotnet-skills

dotnet-legacy-aspnet

Maintain classic ASP.NET applications on .NET Framework, including Web Forms, older MVC, and legacy hosting patterns, while planning realistic modernization boundaries.

302 22
Explore
managedcode/dotnet-skills

dotnet-code-review

Review .NET changes for bugs, regressions, architectural drift, missing tests, incorrect async or disposal behavior, and platform-specific pitfalls before you approve or merge them.

302 22
Explore

Didn't find tool you were looking for?

Be as detailed as possible for better results