agentleFS
ericwood8ericwood8/skills/msbuild/SKILL.md

msbuild

Skill1 starsChanged 13 days agoMITView on GitHub

Use when editing a .csproj, .sln, .slnx or .targets file, diagnosing an MSBuild or .NET build error, or finding out why a file that should be embedded or copied is missing from the output. Trigger words - WMC9997, double hyphen in XML comment, satellite assembly, EmbeddedResource missing, RuntimeIdentifier, UseWindowsForms, slnx, -getItem, -v diag.

  • Deletes or force-pushes

1 other project uses a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

2 rules. About 0 are common, 0 are distinctive, and 2 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈0

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 2

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    After files are regenerated (a column renamed, a control swapped), a WinUI3 project can fail with C# errors pointing into obj\...\*.g.cs …

    line 85
  2. Very specific:

    Not re-run: stale obj after code generation, the output folder difference between solution and project builds, packing a tool.

    line 102

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

MSBuild / .csproj gotchas

XML comments can't contain -- — hits .csproj AND .xaml alike

Both project files and XAML files are parsed as strict XML, and standard XML forbids -- inside a comment (and a comment can't end in --> preceded by another -). Writing a comment the way you'd naturally write prose — using -- as an em-dash — reliably breaks the build:

error MSB4025: The project file could not be loaded. An XML comment cannot contain '--', and '-' cannot be the last character.
error WMC9997: An XML comment cannot contain '--', and '-' cannot be the last character.   (same rule, XAML compiler)

This is easy to reintroduce over and over across a session (it did, repeatedly) since it's a natural writing habit, not an obviously "wrong" thing to type. Use a real em dash (—), a colon, or a comma instead of -- in any <!-- ... --> comment, in both .csproj/.props/.targets files and .xaml files. // comments in .cs files are NOT affected (not XML) — only literal XML files.

EmbeddedResource silently vanishes for filenames containing a real ISO culture code

MSBuild's AssignCulture task splits each filename on dots looking for a segment that matches a known culture code (e.g. MyResource.fr.resx → French satellite resource). A filename like SP_Insert.tt.config has "tt" sitting in the middle — which is also the ISO 639-1 code for Tatar — so MSBuild silently reroutes that file into a tt\ satellite resource assembly instead of the main one. Assembly.GetManifestResourceNames() on the main assembly then just won't list it. No build warning or error is produced — the file compiles fine, it's just quietly not where you expect it at runtime.

Fix:

<EmbeddedResource Include="Templates\**\*.*" WithCulture="false" />

Diagnose this class of bug with a verbose build searching for the exact tell:

dotnet build -v diag 2>&1 | grep "Culture of"

which prints e.g. Culture of "tt" was assigned to file "..\Templates\SP_Insert.tt.config". — instant root cause once you know to look for it; otherwise it looks like the item was never included at all.

WinUI3 project properties

WindowsAppSDKSelfContained with SelfContained=false (singular RuntimeIdentifier) is in the winui3 skill, "Build and project".

Related trap: building the solution (dotnet build Foo.slnx) vs building the project directly (dotnet build Foo.App/Foo.App.csproj) can produce different output folder structures for the exact same project, e.g. bin\x64\Debug\net10.0-windows.../win-x64\ vs bin\Debug\net10.0-windows.../win-x64\, because the solution build implicitly resolves $(Platform) from the solution's own platform mappings while a bare project build may not, absent an explicit -p:Platform=x64. If a script or note says "look in bin\x64..." and the exe isn't there, check the sibling bin\Debug\... (no x64 segment) before assuming something is broken.

UseWindowsForms=true conflicts with a WinUI3 project's XAML

It fails the build with MC6000. The cause and the Win32 folder-picker alternative are in the winui3 skill, "Build and project".

dotnet new sln produces .slnx, not .sln, on newer SDKs

On .NET 10's SDK, dotnet new sln generates the new XML-based .slnx solution format by default, not the classic .sln. Build commands, CI scripts, and any tooling that hardcodes a .sln filename need to target the actual generated file (dotnet build MyProject.slnx), not assume .sln.

Quick diagnostic commands worth reaching for

  • Inspect resolved items without a full build, to check whether an Include= glob is matching what you think it is (item evaluation, before any target runs):
    dotnet build MyProject.csproj -getItem:EmbeddedResource
    dotnet build MyProject.csproj -getItem:None,Content
  • Trace exactly what happened to one specific file during a real build (which task touched it, what metadata it was assigned, whether it got filtered out) by grepping a diagnostic-verbosity log for that file's name:
    dotnet build -v diag 2>&1 | grep "SomeFile.ext"
    This is slow (diagnostic verbosity is chatty) but is the most reliable way to find where in the pipeline a file silently stopped being what you expected, rather than guessing from the .csproj alone.

A WinUI3 build full of impossible errors after a code-generation run: clear obj, restore, build twice

After files are regenerated (a column renamed, a control swapped), a WinUI3 project can fail with C# errors pointing into obj\...\*.g.cs (for example 'ItemDetailViewModel' does not contain a definition for 'ItemStatus' when the source no longer uses that name) plus XamlCompiler ... WMC9999: Object reference not set to an instance of an object and WMC1509 No LocalAssembly parameter. Those .g.cs files are stale output of the previous XAML; the real source is fine. Fix: rm -rf <project>/obj, dotnet restore <solution>, then dotnet build — and expect the first build after clearing obj to fail once with WMC0601 Error opening XAML file ... App.xaml before it has copied the file; the second build succeeds. A regeneration script should do this automatically on the first failure. (A running app locks the output instead and fails with MSB3021/MSB3027; close it first.)

Packing a command-line tool (dotnet tool)

<PackAsTool>true</PackAsTool>, <ToolCommandName>codegen</ToolCommandName>, PackageId, Version, and <None Include="..\README.md" Pack="true" PackagePath="\" /> for the readme and licence (plus PackageReadmeFile / PackageLicenseFile). dotnet pack -o <dir>, then dotnet tool install --tool-path <dir2> --add-source <dir> <id> to try it without touching the global tools. Two things bite:

  • A tool's files live under ...\.store\<id>\<version>\... and are replaced on every update: a program that keeps settings, templates or output next to its exe loses them. Detect the .store segment and use %APPDATA%\<Name> instead (with an environment variable to override).
  • An XML comment in the .csproj that mentions dotnet tool install --global fails the project load (-- in a comment, see the first section).

How this was verified

  • Written from build failures met while building CodeGenNew.
  • Re-checked with the .NET SDK 10.0.401, in a throwaway project: -- in a .csproj comment fails with MSB4025 (reproduced); a file SP_Insert.tt.config is given the culture tt and a tt satellite output folder appears, and WithCulture="false" removes both (reproduced, clean build before and after); dotnet new sln creates a .slnx.
  • Not re-run: stale obj after code generation, the output folder difference between solution and project builds, packing a tool. The WindowsAppSDKSelfContained and UseWindowsForms items are kept in the winui3 skill. The Windows App SDK version was not recorded.

What this does not prove

  • That a rule holds on an older SDK or another Windows App SDK version.
  • That the culture trap covers every two-letter segment: only tt was tested.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.