Skip to content

feat: make missing Microsoft.WebApplication.targets configurable#110

Open
ehasis wants to merge 1 commit into
CZEMacLeod:mainfrom
ehasis:feat/missing-webapptargets-action
Open

feat: make missing Microsoft.WebApplication.targets configurable#110
ehasis wants to merge 1 commit into
CZEMacLeod:mainfrom
ehasis:feat/missing-webapptargets-action

Conversation

@ehasis

@ehasis ehasis commented Jun 24, 2026

Copy link
Copy Markdown

Make missing Microsoft.WebApplication.targets configurable (MissingWebApplicationsTargetPathAction)

Closes #106. Split out from #103 (item 2).

Problem

Microsoft.WebApplication.targets ships with Visual Studio, not with the .NET SDK. When building with the dotnet CLI (Core MSBuild), $(MSBuildExtensionsPath32) points at the .NET SDK rather than a Visual Studio install, so the default $(WebApplicationsTargetPath) doesn't exist and the build fails with a cryptic, low‑level error:

error MSB4019: The imported project "...\dotnet\sdk\x.y.z\Microsoft\VisualStudio\v18.0\WebApplications\Microsoft.WebApplication.targets" was not found.

Change

Implements the approach you proposed in #106.

  • The Microsoft.WebApplication.targets import is now conditional on Exists('$(WebApplicationsTargetPath)').

  • New property MissingWebApplicationsTargetPathAction (default Error) controls what happens when the file can't be found:

    Value Behaviour
    Ignore do nothing
    Skip high‑importance informational message (SYSWEB001)
    Warn warning (SYSWEB001)
    Error error + fail the build (SYSWEB001) — default
  • The message (SYSWEB001) is actionable: install the VS / Build Tools web workload, build from a Developer command prompt, or set VSToolsPath/WebApplicationsTargetPath (e.g. via VSWhere in CI).

  • An invalid property value fails fast with SYSWEB000.

  • Comparison is case‑insensitive (error == Error).

With Ignore/Skip/Warn the build continues without importing the web targets; for a real web project later targets may still fail. These options are the explicit opt‑out documented for that choice.

This composes cleanly with the optional auto‑location work (the other split‑out item): that would set WebApplicationsTargetPath before this check, so when it finds a valid path Exists(...) is true and no action fires.

Diagnostics / message IDs

Per your request, this starts a registry of message IDs. New docs/Diagnostics.md lists them and is linked from the docs index:

ID Severity Meaning
SYSWEB000 Error invalid MissingWebApplicationsTargetPathAction value
SYSWEB001 Error / Warning / message Microsoft.WebApplication.targets could not be resolved

Files

  • src/MSBuild.SDK.SystemWeb/Sdk/Sdk.targets — property default, conditional import, dispatch target.
  • docs/Diagnostics.md — message‑ID registry + property reference (new).
  • docs/README.md — link to the diagnostics page.

Testing

Built a minimal net48 project with the dotnet CLI against a locally‑packed SDK, on Windows with VS 2026 (v18) + .NET SDK 10:

Scenario Result
default / Error error SYSWEB001
Warn ⚠️ warning SYSWEB001, build continues
Skip ℹ️ SYSWEB001 message, build continues
Ignore no output, build continues
invalid value error SYSWEB000
path exists (VS / -p:VSToolsPath=…) ✅ target doesn't fire; import works (no regression)

Verified with both the dotnet CLI and the full Visual Studio MSBuild.exe.

Open questions

  • Message‑ID prefix / numbering — I used SYSWEB starting at 000/001; happy to change to whatever scheme you prefer before merge.
  • Numbering existing messages — this PR establishes the registry and numbers the two new messages. Numbering the SDK's other existing messages is a broader sweep — I'd suggest a separate issue/PR for that rather than expanding this one. Let me know if you'd rather fold it in.

Building with the `dotnet` CLI (Core MSBuild) resolves $(WebApplicationsTargetPath) to a path under the .NET SDK where Microsoft.WebApplication.targets does not exist, failing with a cryptic MSB4019.

Make the web-targets import conditional on Exists(...) and add a $(MissingWebApplicationsTargetPathAction) property (default Error) that controls the reaction when it is missing:

  Ignore | Skip (info message) | Warn | Error

Emit message SYSWEB001 (severity per the property) with actionable guidance, and SYSWEB000 for an invalid property value. Add docs/Diagnostics.md as the registry of message IDs and link it from the docs index.

Fix: CZEMacLeod#106
Comment thread docs/Diagnostics.md
| `SYSWEB000` | Error | Invalid value for the `MissingWebApplicationsTargetPathAction` property (expected `Ignore`, `Skip`, `Warn` or `Error`). |
| `SYSWEB001` | Error / Warning / message | `Microsoft.WebApplication.targets` could not be resolved at `$(WebApplicationsTargetPath)`. Configurable via the `MissingWebApplicationsTargetPathAction` property — see below. |

## `MissingWebApplicationsTargetPathAction`

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 2 properties - MissingWebApplicationsTargetPathAction and WebApplicationsTargetPath - should probably be documented in doc\SDK.md.

There are sections for Common Properties (used by the main SDK and RazorLibrary) and the specific ones for the main SDK.

I'd like to keep the Diagnostics.md file 'clean' and only have a table of the codes.

Comment thread docs/Diagnostics.md
Messages emitted by `MSBuild.SDK.SystemWeb` use the `SYSWEB` prefix followed by a number.
This page is the central registry of those messages.

| ID | Severity | Meaning |

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we have a 4th column for 'more info' with a deep dive link for some errors - e.g. 000 and 001 could point to a generic WebApplicationsTarget.md file which has all the information about how it is normally derived (VSToolsPath) and how to set it externally. Perhaps the example powershell script / github action for vswhere can go there too.

Comment thread docs/Diagnostics.md
@@ -0,0 +1,51 @@
# Diagnostics (message IDs)

Messages emitted by `MSBuild.SDK.SystemWeb` use the `SYSWEB` prefix followed by a number.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm coming round to SYSWEB as the prefix - in another issue I had suggested SWSDK for SystemWebSDK.

Comment thread docs/README.md
# Useful Information

## Diagnostics
- [Message IDs and the `MissingWebApplicationsTargetPathAction` property](Diagnostics.md)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should just reference the Message IDs here.
We could consider breaking the common and specific properties out into separate files and link them here, but I think that that might be a future consideration.

</PropertyGroup>

<!-- Reject unknown values up front. -->
<Error Code="SYSWEB000"

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Error and Warn actions should have the HelpLink property pointing to https://github.com/CZEMacLeod/MSBuild.SDK.SystemWeb/blob/main/docs/Diagnostics.md#SYSWEB000 etc.

Hopefully we can put anchors into the markup.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either that or we have a file per error (or group of errors) and directly link to that.

<!-- Reject unknown values up front. -->
<Error Code="SYSWEB000"
Condition="'$(MissingWebApplicationsTargetPathAction)' != 'Ignore' and '$(MissingWebApplicationsTargetPathAction)' != 'Skip' and '$(MissingWebApplicationsTargetPathAction)' != 'Warn' and '$(MissingWebApplicationsTargetPathAction)' != 'Error'"
Text="SYSWEB000: Unknown MissingWebApplicationsTargetPathAction '$(MissingWebApplicationsTargetPathAction)'. Valid values are: Ignore, Skip, Warn, Error." />

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if the default is to have the error code at the start of the text - I thought it would be implicit from the Code property...


<Error Code="SYSWEB001" Text="$(_SystemWebMissingWebTargetsText)" Condition="'$(MissingWebApplicationsTargetPathAction)' == 'Error'" />
<Warning Code="SYSWEB001" Text="$(_SystemWebMissingWebTargetsText)" Condition="'$(MissingWebApplicationsTargetPathAction)' == 'Warn'" />
<Message Importance="high" Text="$(_SystemWebMissingWebTargetsText)" Condition="'$(MissingWebApplicationsTargetPathAction)' == 'Skip'" />

@CZEMacLeod CZEMacLeod Jun 24, 2026

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As the message can't have a helplink or code (yet?), here we could include them directly into the

 Text="SYSWEB001: $(_SystemWebMissingWebTargetsText) - $(_SystemWebMissingWebTargetsLink)"

@CZEMacLeod

Copy link
Copy Markdown
Owner

Regarding your open questions:

Naming - I think I put it in the inline comments - but I think SYSWEB is reasonable. I am still unsure if SWSDK is better to make it clear this is an SDK error, not an issue with System.Web or systemweb-adapters which definately uses the SYSWEB prefix for some analysers and other errors - see systemweb-adapters SYSWEB search.
If there isn't a limit (and I don't think there is) perhaps a compromise of SYSWEBSDK might be best.

Issue #109 covers updating the other errors and warnings to have codes and links. Keeping this PR to the scope of #106 is good. However, we need to setup the framework for how the other errors will be dealt with.
Maybe a global property in the root SDK.props file for _SystemWebSDKHelpLinkBase pointing to the Diagnostics.md file would make it easy to do

HelpLink="$(_SystemWebSDKHelpLinkBase)#SYSWEBnnn"

where required.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Emit an actionable error when Microsoft.WebApplication.targets / WebApplicationsTargetPath cannot be resolved

2 participants