A comprehensive, robust MSBuild-based SDK for .NET projects (.NET 5.0+, .NET Standard 2.0/2.1) with optimizations for .NET 10.0. Features intelligent project structure detection with hierarchical solution discovery, path-based namespace generation from directory structure, and comprehensive error handling that prevents common MSBuild failures. Includes advanced package management with multi-target support, automatic metadata integration from markdown files, source linking, and package validation. Streamlines cross-platform development with automatic project type detection, smart cross-project referencing, InternalsVisibleTo configuration for tests, and GitHub workflow integration. Designed for deeply nested project structures with graceful fallbacks and robust validation throughout.
$ dotnet add package ktsu.Sdk.AppA comprehensive, robust MSBuild-based SDK for .NET projects that standardizes configuration, metadata management, and package workflows. Features intelligent project structure detection, hierarchical solution discovery, and path-based namespace generation. Supports multiple .NET versions (.NET 5.0+, .NET Standard 2.0/2.1) with optimizations for .NET 9.0+.
Add the SDK to your global.json (recommended):
{
"sdk": {
"version": "10.0.0",
"rollForward": "latestMinor"
},
"msbuild-sdks": {
"ktsu.Sdk": "2.2.0",
"ktsu.Sdk.ConsoleApp": "2.2.0",
"ktsu.Sdk.App": "2.2.0"
}
}
Or reference directly in your project file:
<Project Sdk="Microsoft.NET.Sdk">
<Sdk Name="ktsu.Sdk" Version="2.2.0" />
<PropertyGroup>
<!-- Your project-specific properties -->
</PropertyGroup>
</Project>
For a library project:
<Project Sdk="Microsoft.NET.Sdk">
<Sdk Name="ktsu.Sdk" />
</Project>
For a console application:
<Project Sdk="Microsoft.NET.Sdk">
<Sdk Name="ktsu.Sdk" />
<Sdk Name="ktsu.Sdk.ConsoleApp" />
</Project>
For a GUI application:
<Project Sdk="Microsoft.NET.Sdk">
<Sdk Name="ktsu.Sdk" />
<Sdk Name="ktsu.Sdk.App" />
</Project>
This repository contains three SDK packages:
The base SDK that all projects should reference. Provides:
Extension SDK for console applications. Adds:
OutputType=Exe configurationExtension SDK for GUI applications (ImGui, WinForms, WPF, etc.). Adds:
OutputType=WinExe on Windows (no console window)OutputType=Exe on other platformsDirectory.Packages.props file at your solution root:<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
</Project>
Metadata Files: Create these optional markdown files at your solution root (they will be automatically included in NuGet packages):
AUTHORS.md - Used for namespace generation and package authorsVERSION.md - Version number (can be managed by build scripts)DESCRIPTION.md - Package description (checked in project directory first, then solution directory for multi-package support)CHANGELOG.md - Release notesLICENSE.md - License informationCOPYRIGHT.md - Copyright noticeTAGS.md - NuGet package tags (checked in project directory first, then solution directory for multi-package support)README.md - Package documentation (checked in project directory first, then solution directory for multi-package support)AUTHORS.url - URL to author/organizationPROJECT.url - URL to project repositoryicon.png: Optional package icon at solution root
The SDK provides sensible defaults, but you can override any property:
<Project Sdk="Microsoft.NET.Sdk">
<Sdk Name="ktsu.Sdk" />
<PropertyGroup>
<!-- Override target frameworks -->
<TargetFrameworks>net10.0;net9.0</TargetFrameworks>
<!-- Override namespace -->
<RootNamespace>MyCompany.MyProject</RootNamespace>
<!-- Disable nullable if needed -->
<Nullable>disable</Nullable>
<!-- Allow warnings in test projects -->
<TreatWarningsAsErrors Condition="$(IsTestProject) == 'true'">false</TreatWarningsAsErrors>
</PropertyGroup>
</Project>
The SDK automatically detects different project types in your solution:
Each project type receives appropriate default settings, references, and output configurations (console apps vs. GUI apps).
The SDK creates intelligent namespaces based on your project's directory structure:
Examples:
MySolution/src/Core/Utils/MyProject.csproj
→ ProjectNamespace: src.Core.Utils.MyProject
MySolution/libs/MyLib/MyLib.csproj
→ ProjectNamespace: libs.MyLib (already ends with project name)
MySolution/MyApp/MyApp.csproj
→ ProjectNamespace: MyApp (directory equals project name)
Final Namespace Pattern:
{AuthorsNamespace}.{ProjectNamespace} where AuthorsNamespace comes from AUTHORS.md
The SDK automatically searches for solution files up the directory hierarchy:
MyProject/ ← Level 3: Check here
├── MyProject.sln ← Found! Use this directory
└── apps/ ← Level 2: Check here
└── frontend/ ← Level 1: Check here
└── src/ ← Level 0: Start here (project directory)
└── MyApp.csproj
This enables the SDK to work with any nested project structure without configuration.
The SDK automatically includes the ktsu.Sdk.Analyzers package (with version synchronization) that enforces proper project configuration with helpful diagnostics and code fixers:
KTSU0001 (Error): Projects must include required standard packages
KTSU0002 (Error): Projects must expose internals to test projects
[assembly: InternalsVisibleTo(...)] attributeKTSU0003 (Error): Use Ensure.NotNull over ArgumentNullException.ThrowIfNull
KTSU0004 (Error): Use Ensure.NotNull instead of manual null checks
if (x == null) throw new ArgumentNullException(...)if (x is null) throw new ArgumentNullException(...)x ?? throw new ArgumentNullException(...)Polyfill Configuration: For non-test projects, the SDK automatically enables:
PolyEnsure=true - Enables ensure/guard clause polyfillsPolyNullability=true - Enables nullability-related polyfillsPolyArgumentExceptions=true - Enables argument exception polyfillsPolyStringInterpolation=true - Enables string interpolation polyfillsThese analyzers ensure consistent project structure while giving you explicit control over dependencies.
The SDK makes these properties available for conditional logic in your project files:
Project Type Detection:
IsPrimaryProject - True if this is the main library projectIsCliProject - True if this is a console applicationIsAppProject - True if this is a GUI applicationIsTestProject - True if this is a test projectProject Type Existence:
PrimaryProjectExists - True if primary project was foundCliProjectExists - True if CLI project was foundAppProjectExists - True if app project was foundTestProjectExists - True if test project was foundProject Paths:
SolutionDir - Path to solution directorySolutionPath - Full path to .sln fileSolutionName - Solution name without extensionPrimaryProjectPath - Path to primary projectTestProjectPath - Path to test projectNamespace Properties:
AuthorsNamespace - Namespace prefix from AUTHORS.mdProjectNamespace - Namespace from directory pathRootNamespace - Final combined namespaceTestProjectNamespace - Namespace for test projectPackage Properties:
IsPackable - True for library projectsIsPublishable - True for executable projectsIsExecutable - True if OutputType is Exe or WinExeIsLibrary - True if OutputType is Library and not a test projectUse these in your project files:
<PropertyGroup>
<!-- Example: Only pack if not a prerelease -->
<IsPackable Condition="$(IsPrerelease) == 'true'">false</IsPackable>
<!-- Example: Different settings for test projects -->
<SomeProperty Condition="$(IsTestProject) == 'true'">TestValue</SomeProperty>
</PropertyGroup>
The SDK includes comprehensive error handling to prevent common MSBuild failures:
Library projects are automatically configured for NuGet packaging with:
Projects are configured with multiple runtime identifiers:
win-x64, win-x86, win-arm64osx-x64, osx-arm64linux-x64, linux-arm64The SDK enforces (via analyzers) that projects include these NuGet packages:
The SDK enforces strict code quality standards by default:
latest - Use latest C# language featuresenable - Nullable reference types enabledtrue - All warnings treated as errorsenable - Implicit global usings enabledlatest-all - All latest analyzer rules enabledtrue - .NET code analyzers enabledtrue - Code style rules enforced during buildThe following warnings are suppressed globally:
Additional suppressions for test projects:
true - Invariant culture for better performanceen-USProblem: NuGet restore fails with "ManagePackageVersionsCentrally is not enabled"
Solution: Ensure Directory.Packages.props exists at your solution root with ManagePackageVersionsCentrally enabled.
Problem: Generated namespace doesn't match expectations
Solution:
AUTHORS.md exists and contains valid content{FirstPartOfAuthors}.{PathToProject}.{ProjectName}<RootNamespace> in your project fileProblem: Build fails due to warnings being treated as errors
Solution: Either fix the warnings or selectively disable warnings:
<PropertyGroup>
<NoWarn>$(NoWarn);CA1234;IDE5678</NoWarn>
</PropertyGroup>
Problem: Project builds for too many frameworks
Solution: Override TargetFrameworks for specific projects:
<PropertyGroup>
<!-- Single target for applications -->
<TargetFramework>net10.0</TargetFramework>
<TargetFrameworks></TargetFrameworks>
</PropertyGroup>
Problem: SDK reports it cannot find a solution file
Solution: The SDK searches up to 5 directory levels. Ensure your project is within 5 levels of your .sln file, or manually set <SolutionDir> in your project.
See the LICENSE.md file for license information.
Contributions are welcome! Please feel free to submit a Pull Request.