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.100",
"rollForward": "latestFeature"
},
"msbuild-sdks": {
"MSTest.Sdk": "4.3.2",
"ktsu.Sdk": "2.26.1",
"ktsu.Sdk.ConsoleApp": "2.26.1",
"ktsu.Sdk.App": "2.26.1",
"ktsu.Sdk.Tool": "2.26.1",
"ktsu.Sdk.Windows": "2.26.1",
"ktsu.Sdk.Linux": "2.26.1",
"ktsu.Sdk.macOS": "2.26.1",
"ktsu.Sdk.iOS": "2.26.1",
"ktsu.Sdk.Android": "2.26.1",
"ktsu.Sdk.Unity": "2.26.1",
"ktsu.Sdk.Godot": "2.26.1"
},
"test": {
"runner": "Microsoft.Testing.Platform"
}
}And reference in your project file:
<Project>
<Sdk Name="Microsoft.NET.Sdk" />
<Sdk Name="ktsu.Sdk" />
<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>For a .NET tool (distributed via dotnet tool install):
<Project Sdk="Microsoft.NET.Sdk">
<Sdk Name="ktsu.Sdk" />
<Sdk Name="ktsu.Sdk.Tool" />
</Project>For a platform-specific application (e.g. Linux), reference the matching SDK:
<Project Sdk="Microsoft.NET.Sdk">
<Sdk Name="ktsu.Sdk" />
<Sdk Name="ktsu.Sdk.Linux" />
</Project>The same pattern applies to ktsu.Sdk.Windows, ktsu.Sdk.macOS,
ktsu.Sdk.iOS, and ktsu.Sdk.Android. Mobile targets require the relevant .NET
workload (dotnet workload install android ios maui), and iOS additionally
requires a macOS host with Xcode.
For a Unity managed plug-in:
<Project Sdk="Microsoft.NET.Sdk">
<Sdk Name="ktsu.Sdk" />
<Sdk Name="ktsu.Sdk.Unity" />
</Project>For a Godot game assembly, Godot.NET.Sdk is the outer SDK:
<Project Sdk="Godot.NET.Sdk/4.7.2">
<Sdk Name="ktsu.Sdk" />
<Sdk Name="ktsu.Sdk.Godot" />
</Project>Order matters in both cases: ktsu.Sdk first, then the extension SDK, which is
what lets the extension override the core defaults.
- 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, .NET Standard 2.0/2.1 (default: net10.0). Frameworks follow the .NET support lifecycle: one enters the list when it ships and leaves when it goes out of support. .NET Standard 2.0/2.1 stay as the fallback, so a consumer on an older framework resolves the netstandard2.1 asset rather than being stranded.
- 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
Upgrading: Moving to a ktsu.Sdk version that dropped a target framework from this list can fail
your next dotnet pack if your project carries a CompatibilitySuppressions.xml. See
Package Validation Fails After a Framework Is Dropped
below.
- 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 the following 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
Extension SDK for applications distributed as .NET tools. Adds:
PackAsTool=true, producing aDotnetToolpackage instead of an executable- Single target framework (net10.0), since a tool package cannot multi-target
ToolCommandNamederived from the lowercased solution name — a solution namedKtsuBuildinstalls asktsubuild. SetToolCommandNameexplicitly to override, which short or generic solution names should do.- No runtime identifiers: a tool ships as one RID-agnostic, framework-dependent
package, so consumers need the .NET 10 runtime. For a standalone binary, add a
separate
ktsu.Sdk.ConsoleAppproject.
A project named {Solution}.Tool or {Solution}Tool also sets IsToolProject.
{Solution}.CLI deliberately does not — CLI projects stay console apps unless they
reference this SDK.
Note that packing any project requires the metadata files the SDK declares as package
metadata (LICENSE.md, README.md, icon.png) to exist in the solution directory.
These extension SDKs target a single platform. They fall into two groups:
Desktop (RID-based, no extra prerequisites): build self-contained apps on
the base net10.0 runtime with the runtime identifiers narrowed to one OS.
- ktsu.Sdk.Windows —
net10.0,OutputType=WinExe, RIDswin-x64;win-x86;win-arm64 - ktsu.Sdk.Linux —
net10.0,OutputType=Exe, RIDslinux-x64;linux-arm64;linux-musl-x64;linux-musl-arm64 - ktsu.Sdk.macOS —
net10.0,OutputType=Exe, RIDsosx-x64;osx-arm64
Mobile (TFM + workload): use platform-specific target frameworks and require the corresponding .NET workload.
- ktsu.Sdk.iOS —
net10.0-ios,SupportedOSPlatformVersion=15.0. Requires theiosworkload and a macOS host with Xcode to build/run consumers. - ktsu.Sdk.Android —
net10.0-android,SupportedOSPlatformVersion=21.0. Requires theandroidworkload; builds on Linux, macOS, or Windows.
Prerequisites for mobile targets: install the workloads once with
dotnet workload install android ios maui. The SDK packages themselves carry no workload dependency — only consuming app projects do.
Unity and Godot both host their own runtime and produce their own executable, so a C# project for either is a library the engine loads, not an app the .NET SDK publishes. Each of these extension SDKs pins the shape that engine can actually load and puts back the core SDK defaults that would otherwise get in the way. Neither needs the engine installed to build.
- ktsu.Sdk.Unity —
netstandard2.1,OutputType=Library, no runtime identifiers. Builds a managed plug-in to drop into a Unity project'sAssets/Plugins(or to publish for NuGetForUnity). - ktsu.Sdk.Godot —
net10.0,OutputType=Library,EnableDynamicLoading=true, no runtime identifiers. Composes withGodot.NET.Sdk, which supplies the GodotSharp bindings, the source generators and the engine's output layout.
Unity: why netstandard2.1. Unity's scripting runtime is Mono or IL2CPP, not
.NET Core. Both API Compatibility Levels Unity offers (".NET Standard 2.1", the
default, and ".NET Framework") implement netstandard2.1, so it is the one pin that
loads in every supported configuration; a netX.0 assembly fails to import
outright. Override to netstandard2.0 for editors older than Unity 2021.2, or to a
net4x framework for a project fixed on the .NET Framework profile — KTSU1003
fails the build for anything else, rather than letting the failure surface later as
an import error in the Unity console. The assembly is compiled by your own .NET
SDK, so it may use language features newer than Unity's in-editor compiler accepts;
features that need a newer runtime (ref fields, static abstract interface members)
will still fail on Unity's.
Godot: what the SDK puts back. Godot.NET.Sdk deliberately sets no
TargetFramework — GodotSharp declares the minimum and the project chooses — so
ktsu.Sdk.Godot pins the same net10.0 as the rest of this SDK family. It also
restores two values the core SDK overwrites, and only when Godot.NET.Sdk is the
outer SDK:
AppendTargetFrameworkToOutputPath=false, because Godot loads the assembly from.godot/mono/temp/bin/$(Configuration)/with no framework folder. The core SDK sets it back totrue, which moves the output one directory deeper and leaves the editor reporting a missing assembly.AssemblyName=$(MSBuildProjectName), becauseproject.godotrecords the assembly to load indotnet/project/assembly_nameand Godot writes the project name there. The core SDK setsAssemblyNameto the fully-qualified namespace (e.g.ktsu.MyGame.Godot), which is no longer the file Godot looks for.RootNamespace,PackageIdandTitlekeep the ktsu-namespaced value — only the assembly file name is pinned — and a project whoseproject.godotsays otherwise can still setAssemblyNameitself.
A project named {Solution}.Unity/{Solution}Unity or
{Solution}.Godot/{Solution}Godot also sets IsUnityProject / IsGodotProject.
Runnable demos for both, including the engine-side halves and the deployment step, are under
examples/demos/Unity and
examples/demos/Godot.
The .gitignore the SDK syncs into consuming repositories gains Godot's .godot/ cache, and
negates two of its own generic rules for Unity: **/[Pp]ackages/* (a NuGet restore folder, but
Unity's Packages/ is project source) and *.meta (the Visual Studio C++ build artifact, but
Unity generates one .meta per asset carrying the GUID scenes and prefabs reference). Both
negations are scoped, so a Unity project keeps its source while the artifacts they were written
for stay ignored everywhere else. Unity's generated caches — Library/, Temp/, Logs/ — are
deliberately not added: those names are only caches beside an Assets/ folder, so they belong
in the Unity project's own .gitignore, as the demo shows.
- 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 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 repository (PROJECT_URL.urlis also accepted)
-
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>By default, ktsu.Sdk syncs these files from the SDK package into the solution root during build:
.editorconfig.gitattributes.gitignore.runsettings
The sync updates files that already exist in the solution root.
When COPYRIGHT.md exists in the consumer solution root, .editorconfig is still synced but its
file_header_template line is rewritten from the full contents of COPYRIGHT.md.
To opt out:
<PropertyGroup>
<KtsuSyncStyleConfigFiles>false</KtsuSyncStyleConfigFiles>
</PropertyGroup>By default, a project carries every runtime identifier its packages ship, and its output
holds one copy of native assets per runtime. Set KtsuHostRuntimeOnly to build for the
host's runtime only instead:
<PropertyGroup>
<KtsuHostRuntimeOnly>true</KtsuHostRuntimeOnly>
</PropertyGroup>or on the command line:
dotnet test -p:KtsuHostRuntimeOnly=trueThis exists so a whole workspace can be tested with one dotnet test invocation, which
cannot take a RuntimeIdentifier global property directly (NETSDK1134), without every
test project copying native assets for every runtime its packages ship.
The flag is off by default and applies only when the project has not already resolved a
runtime identifier before ktsu.Sdk.props is imported (a command-line value or one set in
Directory.Build.props). A project that sets RuntimeIdentifier in its own project body
is not protected by that check: the flag's property group still runs, and the project body
simply overwrites the runtime identifier afterward because it evaluates later. Such a
project also keeps SelfContained=false, which the flag imposes before the project body
runs. So the project ends up with its own runtime identifier, but not because the flag
deferred to it.
The platform SDKs (ktsu.Sdk.Linux, ktsu.Sdk.macOS, ktsu.Sdk.Windows) set their
runtime identifier behind that same == '' check, which the flag has already satisfied by
the time those SDKs run. Under the flag, a ktsu.Sdk.Linux project built on a Windows host
resolves RuntimeIdentifier to win-x64 while RuntimeIdentifiers still lists the Linux
set. Treat the flag as incompatible with the platform SDKs until this is fixed.
Never combine this flag with packing. ktsu.Sdk.Tool clears RuntimeIdentifiers
(plural) so a tool ships as one runtime-agnostic package, but it does not clear
RuntimeIdentifier (singular), and the base SDK that sets RuntimeIdentifier under the
flag is imported first. Packing a PackAsTool project under the flag moves the tool
payload from tools/net10.0/any/ to a runtime-specific folder such as
tools/net10.0/win-x64/, producing a package that will not install on any other platform.
Four repositories use ktsu.Sdk.Tool, including KtsuBuild itself, the tool every ktsu
repository installs. This flag is for building and testing, never for packing or
releasing.
Deliberate consequence: ktsu.Sdk.App sets OutputType to WinExe when the runtime
identifier starts with win, and the base SDK that sets RuntimeIdentifier under this
flag is imported before it, so app projects build as WinExe during a run with this flag
set. That is accepted. It is harmless when the purpose of the run is to execute tests, and
it is the reason a build with this flag is not interchangeable with a release build.
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)
- Platform App Projects: Per-OS app projects (YourSolution.Windows, YourSolution.Linux, YourSolution.macOS, YourSolution.iOS, YourSolution.Android, and their suffix-free and abbreviated forms)
- Game Engine Projects: Engine assemblies (YourSolution.Unity, YourSolutionUnity, YourSolution.Godot, YourSolutionGodot)
- Tool Projects: .NET tool projects (YourSolution.Tool, YourSolutionTool)
- 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 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
KTSU0003 (Error): Use Ensure.NotNull over ArgumentNullException.ThrowIfNull
- ArgumentNullException.ThrowIfNull was introduced in .NET 6
- Ensure.NotNull from the Polyfill package maintains compatibility with older frameworks
- Code fixer automatically replaces the invocation
KTSU0004 (Error): Use Ensure.NotNull instead of manual null checks
- Detects patterns like
if (x == null) throw new ArgumentNullException(...) - Detects patterns like
if (x is null) throw new ArgumentNullException(...) - Detects patterns like
x ?? throw new ArgumentNullException(...) - Code fixer automatically replaces with Ensure.NotNull
KTSU0005 (Error): Orphaned PackageVersion entry in Directory.Packages.props
- Flags a centrally-managed version that no project in the solution references
- Code fixer removes the entry
- Disable with
<KtsuEnableOrphanedPackageVersionAnalysis>false</KtsuEnableOrphanedPackageVersionAnalysis>
KTSU0006 (Error): Transitive package used directly
- Flags use of a type or member that comes from a transitive dependency with no direct
PackageReference - Code fixer adds the
PackageReference, and a matchingPackageVersionunder Central Package Management - Disable with
<KtsuEnableTransitivePackageAnalysis>false</KtsuEnableTransitivePackageAnalysis>
KTSU0007 (Error): Build-time package reference is not private
- Requires
PrivateAssets="all"on the Polyfill reference in non-test projects - Polyfill is a source-embedding, build-time-only package. NuGet only omits a dependency from the
produced package when every asset kind is private, so a partial
PrivateAssetsvalue still leaks Polyfill to every downstream consumer - Code fixer sets the attribute on the
PackageReference - The other standard packages are deliberately not covered:
System.MemoryandSystem.Threading.Tasks.Extensionsare genuine runtime dependencies that must flow transitively
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 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 applicationIsWindowsProject,IsLinuxProject,IsMacProject,IsIosProject,IsAndroidProject- True for the matching per-OS app projectIsUnityProject- True if this is a Unity managed plug-in projectIsGodotProject- True if this is a Godot game assembly projectIsToolProject- True if this is a .NET tool projectIsTestProject- 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 foundToolProjectExists- True if tool 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 projects and projects withPackAsToolIsPublishable- 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:
- 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:
10.0-all- All .NET 10 analyzer rules enabled. Pinned rather thanlatest-allso a .NET SDK update cannot turn newly-added rules into build errors (TreatWarningsAsErrorsis on). Override with<AnalysisLevel>latest-all</AnalysisLevel>to track the newest rules. - 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: dotnet pack fails with EnablePackageValidation=true after you take a version of
ktsu.Sdk that drops a target framework. The errors are CP0001, CP0002, CP0008, CP0014, CP0015, or
CP0016 diagnostics naming the removed framework (for example net5.0, net6.0, or net7.0), even
though PackageValidationBaselineVersion isn't set anywhere in your project.
Solution: This isn't baseline validation, and setting PackageValidationBaselineVersion won't
fix it. The real cause is a committed CompatibilitySuppressions.xml file that still records
comparisons against the framework you dropped. The package validation tool reprocesses those
entries on every pack, and once a recorded comparison names a framework that no longer exists, the
suppression can never match a live comparison again. The default
ApiCompatPermitUnnecessarySuppressions=false then treats that stale, unmatched suppression as an
error instead of discarding it. Regenerate the file once, against the new SDK, and commit the
result:
dotnet pack -p:ApiCompatGenerateSuppressionFile=trueThis replaces the stale entries with ones for your current target frameworks. If your project has
no CompatibilitySuppressions.xml, this problem doesn't apply to you, and a package validation
failure has a different cause.
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.