A 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>- Hierarchical Solution Discovery: Automatically finds solution files up to 5 directory levels above the project
- Path-Based Namespace Generation: Creates namespaces from directory structure between solution and project
- Smart Project Detection: Automatically detects primary, console, GUI, and test project types
- Nested Project Support: Works seamlessly with deeply nested project structures
- Safe Array Operations: Prevents index-out-of-bounds errors in MSBuild expressions
- Null-Safe String Operations: Comprehensive null/empty checks for all string manipulations
- Graceful Fallbacks: Provides sensible defaults when metadata files or properties are missing
- Comprehensive Validation: Built-in validation for all file operations and property access
- Multi-Target Support: .NET 10.0, 9.0, 8.0, 7.0, 6.0, 5.0, .NET Standard 2.0/2.1 (default: net10.0)
- MSBuildSdk Packaging: Properly configured for MSBuild SDK project packaging
- Automatic Metadata Integration: Seamlessly includes markdown files in package metadata
- Package Validation: Built-in API compatibility and package validation
- Source Link Integration: Automatic GitHub and Azure Repos source linking for debugging
- Central Package Management: Requires and works with Directory.Packages.props
- Analyzer-Enforced Requirements: Roslyn analyzers (KTSU0001/KTSU0002) ensure proper package dependencies and internals visibility with helpful diagnostics and code fixers
- Internals Visibility: Code fixer to easily add InternalsVisibleTo attributes for test projects
- GitHub Integration: Built-in support for GitHub workflows and CI/CD
- Cross-Platform Support: Compatible with Windows, macOS, and Linux
- Documentation Generation: Automated XML documentation file generation
- Strict Code Quality: Nullable enabled, warnings as errors, latest analyzer rules
This repository contains three SDK packages:
The base SDK that all projects should reference. Provides:
- Solution and project discovery
- Namespace generation
- Metadata file integration
- Multi-target framework support
- Automatic project references
- Package configuration
- Code quality defaults
Extension SDK for console applications. Adds:
OutputType=Execonfiguration- Single target framework (net10.0)
- Cross-platform console optimizations
Extension SDK for GUI applications (ImGui, WinForms, WPF, etc.). Adds:
OutputType=WinExeon Windows (no console window)OutputType=Exeon other platforms- Single target framework (net10.0)
- Platform-specific runtime configurations
- Central Package Management: Create a
Directory.Packages.propsfile 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 descriptionCHANGELOG.md- Release notesLICENSE.md- License informationCOPYRIGHT.md- Copyright noticeTAGS.md- NuGet package tagsREADME.md- Package documentationAUTHORS.url- URL to author/organizationPROJECT.url- URL to project repository
-
icon.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:
- Primary Project: The main project of your solution (YourSolution, YourSolution.Core)
- Console Projects: Command-line interface projects (YourSolution.CLI, YourSolution.Cli, YourSolutionCli, YourSolutionCLI, YourSolution.ConsoleApp, YourSolution.Console)
- GUI App Projects: Application projects (YourSolution.App, YourSolutionApp, YourSolution.WinApp, YourSolutionWinApp, YourSolution.ImGuiApp, YourSolutionImGuiApp)
- Test Projects: Test projects (YourSolution.Test, YourSolution.Tests, YourSolutionTest, YourSolutionTests)
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
- Enforces SourceLink packages (GitHub, Azure Repos)
- Enforces Polyfill package for non-test projects
- Enforces compatibility packages (System.Memory, System.Threading.Tasks.Extensions) based on target framework
- Diagnostic message includes package name and version number
KTSU0002 (Error): Projects must expose internals to test projects
- Code fixer automatically adds
[assembly: InternalsVisibleTo(...)]attribute - Use Ctrl+. (Quick Actions) to apply the fix
Polyfill Configuration: For non-test projects, the SDK automatically enables:
PolyEnsure=true- Enables ensure/guard clause polyfillsPolyNullability=true- Enables nullability-related polyfills
These 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 project
Project 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 found
Project Paths:
SolutionDir- Path to solution directorySolutionPath- Full path to .sln fileSolutionName- Solution name without extensionPrimaryProjectPath- Path to primary projectTestProjectPath- Path to test project
Namespace Properties:
AuthorsNamespace- Namespace prefix from AUTHORS.mdProjectNamespace- Namespace from directory pathRootNamespace- Final combined namespaceTestProjectNamespace- Namespace for test project
Package 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 project
Use 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:
- Safe Array Access: Prevents "index out of bounds" errors when accessing file lists or string arrays
- Null Property Checks: All string operations include null/empty validation
- File Existence Validation: All file operations verify existence before processing
- Graceful Degradation: Missing metadata files don't cause build failures
Library projects are automatically configured for NuGet packaging with:
- Automatic Metadata Population: Uses markdown files for package description, changelog, etc.
- Source Link Integration: Enables source code debugging for published packages
- Package Validation: Built-in API compatibility and package structure validation
- Multi-Framework Support: Targets multiple .NET versions simultaneously
Projects are configured with multiple runtime identifiers:
- Windows:
win-x64,win-x86,win-arm64 - macOS:
osx-x64,osx-arm64 - Linux:
linux-x64,linux-arm64
- Automatic InternalsVisibleTo: Test projects automatically access internal members
- Test Project Detection: Identifies and configures test projects with appropriate settings
- Relaxed Warnings: Test projects suppress documentation and code style warnings
The SDK enforces (via analyzers) that projects include these NuGet packages:
- Microsoft.SourceLink.GitHub - GitHub source linking for debugging
- Microsoft.SourceLink.AzureRepos.Git - Azure Repos source linking
- Polyfill - Modern language feature support for older frameworks, with automatic configuration for PolyEnsure and PolyNullability source generators
- System.Memory - For .NET Standard and .NET Framework
- System.Threading.Tasks.Extensions - For netstandard2.0, netcoreapp2.0, and .NET Framework
- ktsu.Sdk.Analyzers - Automatically included with version synchronization to enforce SDK requirements
The SDK enforces strict code quality standards by default:
- LangVersion:
latest- Use latest C# language features - Nullable:
enable- Nullable reference types enabled - TreatWarningsAsErrors:
true- All warnings treated as errors - ImplicitUsings:
enable- Implicit global usings enabled
- AnalysisLevel:
latest-all- All latest analyzer rules enabled - EnableNETAnalyzers:
true- .NET code analyzers enabled - EnforceCodeStyleInBuild:
true- Code style rules enforced during build
The following warnings are suppressed globally:
- CA1724: Type names should not match namespaces
- CA1034: Nested types should not be visible
- CA1000: Do not declare static members on generic types
- CA2260: Implement ISerializable correctly
- CA1515: Override methods should call base methods
Additional suppressions for test projects:
- CS1591: Missing XML comment
- CA2225: Operator overloads have named alternates
- IDE0022: Use expression body for methods
- IDE0058: Expression value is never used
- CA1305: Specify IFormatProvider
- CA5394: Do not use insecure randomness
- CA1707: Identifiers should not contain underscores
- InvariantGlobalization:
true- Invariant culture for better performance - NeutralLanguage:
en-US
Problem: 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:
- Check that
AUTHORS.mdexists and contains valid content - The namespace format is:
{FirstPartOfAuthors}.{PathToProject}.{ProjectName} - You can always override with
<RootNamespace>in your project file
Problem: 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.
- .NET SDK 5.0 or later (optimized for .NET SDK 10.0)
- Central Package Management (Directory.Packages.props)
See the LICENSE.md file for license information.
Contributions are welcome! Please feel free to submit a Pull Request.