A generic MSBuild SDK resolver that lets you develop and debug custom MSBuild SDKs locally, without packaging them as NuGet packages first.
MSBuild's <Project Sdk="My.Custom.Sdk"> syntax is resolved by a chain of SDK resolvers. The built-in resolvers only know how to find SDKs that are either:
- Installed alongside MSBuild/dotnet (the DefaultSdkResolver)
- Published as NuGet packages (the NuGet SDK resolver, with a version in
global.json)
During SDK development this creates a painful inner loop: edit SDK → pack NuGet → restore → build → test. Dayforce.LocalSdkResolver short-circuits this by resolving SDKs from local directories on disk.
Set the LOCAL_SDK_ROOTS environment variable to a semicolon-delimited list of directories that contain your SDK source folders:
SET LOCAL_SDK_ROOTS=C:\myrepo\sdk;D:\shared\sdks
When MSBuild encounters <Project Sdk="My.Custom.Sdk">, Dayforce.LocalSdkResolver checks each root for:
{root}\My.Custom.Sdk\Sdk\Sdk.props
The first match wins. If LOCAL_SDK_ROOTS is not set or no match is found, the resolver silently declines and the normal NuGet/default resolvers take over.
<root>/
My.Custom.Sdk/
Sdk/
Sdk.props ← required (MSBuild convention)
Sdk.targets ← optional but typical
Another.Sdk/
Sdk/
Sdk.props
Sdk.targets
| Resolver | Priority | Notes |
|---|---|---|
| NuGet SDK resolver | ~5000 | Checks global.json msbuild-sdks |
| Dayforce.LocalSdkResolver | 9000 | Checks LOCAL_SDK_ROOTS directories |
| DefaultSdkResolver | 10000 | Checks dotnet/VS SDK folders |
Lower priority number = runs first. Published NuGet SDKs take precedence over local ones, so setting a version in global.json will override the local copy.
The included deploy.ps1 script builds the resolver and copies it (with an XML manifest) to the SdkResolvers directories for both dotnet CLI and Visual Studio. It requires an elevated (Administrator) terminal:
.\deploy.ps1Then set the environment variable and build:
$env:LOCAL_SDK_ROOTS = "C:\myrepo\sdk"
dotnet build MySolution.sln.\deploy.ps1 -UninstallThe resolver logs at Low message importance. To see resolution messages, build with diagnostic verbosity:
dotnet build -v diag 2>&1 | Select-String "Dayforce.LocalSdkResolver"- SDK resolution happens before MSBuild properties are evaluated, so
-p:command-line properties are not available to the resolver. The environment variable is the only configuration mechanism. - The resolver only checks for
Sdk.propsexistence. It does not validate SDK contents or versions. - When
LOCAL_SDK_ROOTSis set, the resolver runs for every SDK reference (includingMicrosoft.NET.Sdk). It declines quickly for SDKs not found in the configured roots.
MIT