Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions docs/Diagnostics.md
Original file line number Diff line number Diff line change
@@ -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.

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.

|----|----------|---------|
| `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.


`Microsoft.WebApplication.targets` ships with Visual Studio / the Visual Studio Build Tools, 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)` may not
exist and the build would otherwise fail with a cryptic `MSB4019`.

The `MissingWebApplicationsTargetPathAction` property controls what happens when that file cannot be found:

| Value | Behaviour |
|-------|-----------|
| `Ignore` | Do nothing. |
| `Skip` | Emit a high-importance informational message (`SYSWEB001`). |
| `Warn` | Emit a warning (`SYSWEB001`). |
| `Error` | Emit an error and fail the build (`SYSWEB001`). **Default.** |

> With `Ignore`, `Skip` or `Warn` the build continues **without** importing `Microsoft.WebApplication.targets`.
> For a real web project, later targets that depend on it may still fail. These options are intended for
> scenarios where the web targets are genuinely not needed.

### How to resolve a missing `Microsoft.WebApplication.targets`

- Install Visual Studio, or the Visual Studio Build Tools, with the **web development build tools** workload.
- Build from a **Visual Studio Developer command prompt** (which sets the correct environment).
- Set `VSToolsPath` (or `WebApplicationsTargetPath`) explicitly, e.g. computed via
[vswhere](https://github.com/microsoft/vswhere) in a CI pre-build step:

```xml
<!-- e.g. in Directory.Build.props, or pass via -p: on the command line -->
<PropertyGroup>
<VSToolsPath>C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Microsoft\VisualStudio\v17.0</VSToolsPath>
</PropertyGroup>
```

To opt out of the failure instead:

```xml
<PropertyGroup>
<MissingWebApplicationsTargetPathAction>Warn</MissingWebApplicationsTargetPathAction>
</PropertyGroup>
```
3 changes: 3 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ At evaluation time, MSBuild adds implicit imports at the top and bottom of the p

# 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.


## Binding Redirects
- [How to show Suggested Binding Redirects](Binding_Redirects/How-to-show-Suggested-Binding-Redirects.md)
- [Autogenerating Binding Redirects](Binding_Redirects/Autogenerating-Binding-Redirects.md)
Expand Down
24 changes: 23 additions & 1 deletion src/MSBuild.SDK.SystemWeb/Sdk/Sdk.targets
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">15.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<WebApplicationsTargetPath Condition=" '$(WebApplicationsTargetPath)' == '' ">$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets</WebApplicationsTargetPath>
<MissingWebApplicationsTargetPathAction Condition="'$(MissingWebApplicationsTargetPathAction)' == ''">Error</MissingWebApplicationsTargetPathAction>
</PropertyGroup>

<!-- Default item excludes -->
Expand All @@ -25,5 +26,26 @@
<!-- Inject BindingRedirects targets after Microsoft.NET.Sdk is injected -->
<Import Project="MSBuild.SDK.SystemWeb.BindingRedirects.targets" />

<Import Project="$(WebApplicationsTargetPath)" />
<Import Project="$(WebApplicationsTargetPath)" Condition="Exists('$(WebApplicationsTargetPath)')" />

<!-- When Microsoft.WebApplication.targets is missing, react per $(MissingWebApplicationsTargetPathAction)
instead of failing with a cryptic MSB4019. See docs/Diagnostics.md (SYSWEB000/SYSWEB001). -->
<Target Name="_SystemWebCheckWebApplicationsTargetPath"
BeforeTargets="BeforeBuild"
Condition="!Exists('$(WebApplicationsTargetPath)')">

<PropertyGroup>
<_SystemWebMissingWebTargetsText>SYSWEB001: 'Microsoft.WebApplication.targets' was not found at '$(WebApplicationsTargetPath)'. Install Visual Studio or the Visual Studio Build Tools with the "web development build tools" workload, build from a Visual Studio Developer command prompt, or set the 'VSToolsPath' (or 'WebApplicationsTargetPath') property to a valid Visual Studio tools path. Use the 'MissingWebApplicationsTargetPathAction' property (Ignore | Skip | Warn | Error) to control this behaviour.</_SystemWebMissingWebTargetsText>
</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.

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)"

<!-- 'Ignore' => no output -->
</Target>
</Project>