Stride Build System (SDK)

June 18, 2026 · View on GitHub

The Stride build system is implemented as a set of MSBuild SDK packages under sources/sdk/. All projects import the SDK files directly from source using $(StrideRoot)-relative paths (see How Projects Import the SDK).

SDK Packages

PackagePurpose
Stride.Build.SdkBase SDK for all Stride projects. Platform detection, target frameworks, graphics API multi-targeting, assembly processor, native dependencies, shader support.
Stride.Build.Sdk.EditorComposes Stride.Build.Sdk. Adds StrideEditorTargetFramework and StrideXplatEditorTargetFramework.
Stride.Build.Sdk.TestsComposes Stride.Build.Sdk.Editor. Adds xunit packages, test infrastructure, launcher code, and asset compilation support.

Hierarchy

Stride.Build.Sdk (base: platform, graphics, assembly processor, shaders)
  +-- Stride.Build.Sdk.Editor (adds editor framework properties)
        +-- Stride.Build.Sdk.Tests (adds xunit, test infrastructure, asset compilation)

Each SDK internally imports Microsoft.NET.Sdk (internal chaining pattern, same approach as Microsoft.NET.Sdk.Web). Users only reference a single SDK.

Version Management

SDK versions are pinned in global.json:

{
  "msbuild-sdks": {
    "Stride.Build.Sdk": "4.3.0-dev",
    "Stride.Build.Sdk.Editor": "4.3.0-dev",
    "Stride.Build.Sdk.Tests": "4.3.0-dev"
  }
}

Only one version of each SDK can be active during a build.


How Projects Import the SDK

All Stride projects import the SDK files directly from source:

<Project>
  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), 'Directory.Build.props'))/sdk/Stride.Build.Sdk/Sdk/Sdk.props" />

  <PropertyGroup>
    <TargetFrameworks>$(StrideRuntimeTargetFrameworks)</TargetFrameworks>
  </PropertyGroup>

  <Import Project="$(StrideRoot)sources/sdk/Stride.Build.Sdk/Sdk/Sdk.targets" />
</Project>

The props import uses GetDirectoryNameOfFileAbove — a static MSBuild function that locates the nearest Directory.Build.props without relying on any pre-set property (see How $(StrideRoot) is set for why this is necessary).

The targets import uses $(StrideRoot) because Directory.Build.props has already been evaluated by that point.

This replaces the earlier <Project Sdk="Stride.Build.Sdk"> style which required the SDK packages to be pre-built and cached in ~/.nuget/packages/ before any project could load.

Why direct imports?

The Sdk="..." attribute triggers MSBuild SDK resolution before any target runs, so a missing package prevents the solution from opening in Visual Studio. With direct imports, .props and .targets files are loaded from their source location — edits take effect immediately with no rebuild or cache clear.

How $(StrideRoot) is set

sources/Directory.Build.props defines:

<StrideRoot>$(MSBuildThisFileDirectory)../</StrideRoot>

$(MSBuildThisFileDirectory) evaluates to the directory of Directory.Build.props itself — i.e., sources/. So $(StrideRoot) becomes the repo root.

Bootstrap constraint: Directory.Build.props is auto-discovered during Microsoft.Common.props evaluation, which happens inside Microsoft.NET.Sdk/Sdk.props — which is itself imported inside Stride.Build.Sdk/Sdk/Sdk.props. This means $(StrideRoot) is not yet set when MSBuild evaluates the opening props <Import> in a project file, because loading Sdk.props is precisely what triggers Directory.Build.props discovery.

The props import therefore uses a static MSBuild function that needs no pre-set property:

<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), 'Directory.Build.props'))/sdk/Stride.Build.Sdk/Sdk/Sdk.props" />

By the time the project file reaches the closing targets <Import>, Directory.Build.props has been evaluated and $(StrideRoot) is available:

<Import Project="$(StrideRoot)sources/sdk/Stride.Build.Sdk/Sdk/Sdk.targets" />

Reverting to full-SDK style

To go back to <Project Sdk="Stride.Build.Sdk">:

Step 1 — Restore SDK internal cross-references (4 files):

sources/sdk/Stride.Build.Sdk.Editor/Sdk/Sdk.props — replace:

<Import Project="$(MSBuildThisFileDirectory)..\..\Stride.Build.Sdk/Sdk/Sdk.props" />

with:

<Import Project="Sdk.props" Sdk="Stride.Build.Sdk" />

sources/sdk/Stride.Build.Sdk.Editor/Sdk/Sdk.targets — replace:

<Import Project="$(MSBuildThisFileDirectory)..\..\Stride.Build.Sdk/Sdk/Sdk.targets" />

with:

<Import Project="Sdk.targets" Sdk="Stride.Build.Sdk" />

sources/sdk/Stride.Build.Sdk.Tests/Sdk/Sdk.props — replace:

<Import Project="$(MSBuildThisFileDirectory)..\..\Stride.Build.Sdk.Editor/Sdk/Sdk.props" />

with:

<Import Project="Sdk.props" Sdk="Stride.Build.Sdk.Editor" />

sources/sdk/Stride.Build.Sdk.Tests/Sdk/Sdk.targets — replace:

<Import Project="$(MSBuildThisFileDirectory)..\..\Stride.Build.Sdk.Editor/Sdk/Sdk.targets" />

with:

<Import Project="Sdk.targets" Sdk="Stride.Build.Sdk.Editor" />

Step 2 — Restore project files:

Use git to revert the project file changes (all 125 .csproj files). The direct-import form was introduced in one commit, so a targeted revert or checkout is the most reliable approach.

Step 3 — Uncomment global.json msbuild-sdks entries.

Step 4 — Uncomment nuget.config packageSourceMapping entry and re-add the stride-sdks source.

Step 5 — Re-add BuildSdk target to build/Stride.build:

Restore the target and add DependsOnTargets="BuildSdk" (or BuildSdk; prefix where multiple dependencies exist) to: Build, BuildRuntime, BuildWindows, BuildWindowsDirect3D11, BuildWindowsDirect3D12, BuildAndroid, BuildiOS, BuildUWP, BuildWindowsVulkan, BuildLinux, BuildmacOS, BuildLauncher, RunTestsWindows, RunTestsMobile.

<!--
Build Stride MSBuild SDK packages (Stride.Build.Sdk, Stride.Build.Sdk.Editor, Stride.Build.Sdk.Tests)
into the local NuGet cache. Required before any project using Sdk="Stride.Build.Sdk" can build.
-->
<Target Name="BuildSdk">
  <PropertyGroup>
    <StrideSdkSolution>$(StrideRoot)sources\sdk\Stride.Build.Sdk.slnx</StrideSdkSolution>
  </PropertyGroup>
  <MSBuild Targets="Restore" Projects="$(StrideSdkSolution)" />
  <MSBuild Targets="Build" Projects="$(StrideSdkSolution)" />
</Target>

Step 6 — Build the SDK packages:

dotnet build sources/sdk/Stride.Build.Sdk.slnx

Project Examples

Runtime library

<Project>
  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), 'Directory.Build.props'))/sdk/Stride.Build.Sdk/Sdk/Sdk.props" />
  <PropertyGroup>
    <TargetFrameworks>$(StrideRuntimeTargetFrameworks)</TargetFrameworks>
    <StrideAssemblyProcessor>true</StrideAssemblyProcessor>
  </PropertyGroup>
  <ItemGroup>
    <ProjectReference Include="..\Stride.Core\Stride.Core.csproj" />
  </ItemGroup>
  <Import Project="$(StrideRoot)sources/sdk/Stride.Build.Sdk/Sdk/Sdk.targets" />
</Project>

Editor / tool project

<Project>
  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), 'Directory.Build.props'))/sdk/Stride.Build.Sdk.Editor/Sdk/Sdk.props" />
  <PropertyGroup>
    <TargetFramework>$(StrideEditorTargetFramework)</TargetFramework>
  </PropertyGroup>
  <Import Project="$(StrideRoot)sources/sdk/Stride.Build.Sdk.Editor/Sdk/Sdk.targets" />
</Project>

Test project

<Project>
  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), 'Directory.Build.props'))/sdk/Stride.Build.Sdk.Tests/Sdk/Sdk.props" />
  <PropertyGroup>
    <TargetFramework>$(StrideXplatEditorTargetFramework)</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <ProjectReference Include="..\Stride.Core\Stride.Core.csproj" />
  </ItemGroup>
  <Import Project="$(StrideRoot)sources/sdk/Stride.Build.Sdk.Tests/Sdk/Sdk.targets" />
</Project>

A test that must run on multiple platforms imports Stride.Build.Sdk.MultiPlatform.Tests instead and declares <TargetFrameworks>$(StrideTestTargetFrameworks)</TargetFrameworks> (the …MacOS variant to also build net10.0-macos). That set is computed from $(StrideTestPlatforms) — see Property Evaluation Order.


SDK File Structure

sources/sdk/
+-- Stride.Build.Sdk/
|   +-- Stride.Build.Sdk.csproj
|   +-- Sdk/
|       +-- Sdk.props                          # Entry point (before project file)
|       +-- Sdk.targets                        # Entry point (after project file)
|       +-- Stride.Frameworks.props            # Framework constants (net10.0, net10.0-android, ...)
|       +-- Stride.Frameworks.targets          # Per-TFM StrideTargetFramework/StridePlatform re-derivation
|       +-- Stride.Platform.props              # Platform detection, output paths, runtime TargetFrameworks sets
|       +-- Stride.Platform.targets            # Platform-specific compiler defines
|       +-- Stride.Graphics.props              # Default graphics APIs per platform
|       +-- Stride.Graphics.targets            # Graphics API defines and UI framework
|       +-- Stride.GraphicsApi.InnerBuild.targets  # Multi-API inner build dispatch
|       +-- Stride.AssemblyProcessor.targets   # IL post-processing
|       +-- Stride.Dependencies.targets        # .ssdeps native dependency system
|       +-- Stride.CodeAnalysis.targets        # Code analysis rules
|       +-- Stride.PackageInfo.targets         # NuGet metadata, versioning
|       +-- Stride.NativeBuildMode.props       # Clang/MSVC selection
|       +-- Stride.DisableBuild.targets        # Empty targets for build skip
|       +-- Stride.ruleset                     # Code analysis ruleset
+-- Stride.Build.Sdk.Editor/
|   +-- Stride.Build.Sdk.Editor.csproj
|   +-- Sdk/
|       +-- Sdk.props                          # Imports Stride.Build.Sdk + editor frameworks
|       +-- Sdk.targets                        # Passthrough to Stride.Build.Sdk
|       +-- Stride.Editor.Frameworks.props     # Editor framework definitions
+-- Stride.Build.Sdk.Tests/
|   +-- Stride.Build.Sdk.Tests.csproj
|   +-- Sdk/
|       +-- Sdk.props                          # Test defaults, output paths
|       +-- Sdk.targets                        # xunit packages, shader support, launchers
|       +-- LauncherGame.Desktop.cs            # Test launcher for graphics tests
|       +-- LauncherSimple.Desktop.cs          # Test launcher for simple tests
+-- Stride.Build.Sdk.slnx                           # Solution for building SDK packages
+-- Directory.Build.props                      # Shared SDK project config

Important: SDK packages must ONLY use the Sdk/ folder. Never add a build/ folder — NuGet auto-imports build/PackageId.props and build/PackageId.targets even for SDK packages, causing double-import when combined with Sdk="PackageName" on the <Project> element. This was the root cause of a critical bug where Configuration became empty during restore with 2+ ProjectReferences.


Property Evaluation Order

This is the most important concept for understanding and modifying the SDK.

When MSBuild processes <Project Sdk="Stride.Build.Sdk">, it evaluates files in this strict order:

Phase 1: Stride.Build.Sdk/Sdk/Sdk.props       <-- BEFORE project file
                |
Phase 2: YourProject.csproj              <-- User properties
                |
Phase 3: Stride.Build.Sdk/Sdk/Sdk.targets     <-- AFTER project file

What this means

LocationCan see .csproj properties?Use for
Sdk.propsNoDefault values, framework constants
.csprojYes (own + Sdk.props)User configuration
Sdk.targetsYes (all)Conditional logic, derived properties, build targets

Correct patterns

<!-- Sdk.props: Set defaults (user hasn't defined anything yet) -->
<StrideCodeAnalysis Condition="'$(StrideCodeAnalysis)' == ''">false</StrideCodeAnalysis>

<!-- .csproj: Override defaults -->
<StrideCodeAnalysis>true</StrideCodeAnalysis>

<!-- Sdk.targets: Act on final value (user's value is visible) -->
<PropertyGroup Condition="'$(StrideCodeAnalysis)' == 'true'">
  <EnableNETAnalyzers>true</EnableNETAnalyzers>
</PropertyGroup>

Framework selection is the exception that proves the rule: because consuming a user flag in .targets is awkward, runtime/test projects instead declare <TargetFrameworks> explicitly from a set the SDK precomputes at props time (StrideRuntimeTargetFrameworks / StrideTestTargetFrameworks), so there is no user flag to consume late.

Rules of thumb

  • Properties that set defaults -> Sdk.props
  • Properties that check user values or compute derived values -> Sdk.targets
  • Build targets and tasks -> Sdk.targets

Historical note

The old build system used <Import Project="..\..\targets\Stride.Core.props" /> placed after setting properties in the .csproj. This allowed properties to be visible during the import, but required users to carefully order their property definitions before the import — a fragile pattern. The SDK approach standardizes the evaluation order, eliminating this class of bugs.

The old system had a critical bug where a user flag (the former StrideRuntime) was checked in the .props phase before the user's .csproj defined it, causing multi-targeting to silently fail unless the property was set before the import or passed on the command line. The SDK avoids this entirely: the runtime/test framework sets are precomputed in .props from $(StridePlatforms), and each project declares <TargetFrameworks>$(StrideRuntimeTargetFrameworks)</TargetFrameworks> directly — so there is no late-consumed user flag at all.

Full import order

Stride.Build.Sdk/Sdk/Sdk.props (top)
  +-- Stride.Frameworks.props       (framework constants)
  +-- Stride.Platform.props         (platform detection, output paths)
  +-- Stride.Graphics.props         (default graphics APIs)
  +-- Stride.NativeBuildMode.props   (Clang/MSVC)
  +-- Microsoft.NET.Sdk/Sdk.props   (base .NET SDK)
  +-- Sdk.props (bottom)             (AllowUnsafeBlocks, etc.)
      |
YourProject.csproj
      |
Stride.Build.Sdk/Sdk/Sdk.targets (top)
  +-- Microsoft.NET.Sdk/Sdk.targets (base .NET SDK)
  +-- Stride.Platform.targets       (platform defines, mobile properties)
  +-- Stride.Frameworks.targets     (per-TFM StrideTargetFramework/StridePlatform)
  +-- Stride.Graphics.targets       (API defines, UI framework)
  +-- Stride.GraphicsApi.InnerBuild.targets (multi-API dispatch)
  +-- Stride.Dependencies.targets   (native .ssdeps system)
  +-- Stride.AssemblyProcessor.targets
  +-- Stride.CodeAnalysis.targets
  +-- Stride.PackageInfo.targets
  +-- Sdk.targets (bottom)           (shader codegen, auto-pack, etc.)

Property Reference

Platform

PropertyPurposeSet by
StridePlatformCurrent platform (Windows, Linux, macOS, Android, iOS)Auto-detected in Stride.Platform.props
StridePlatformOriginalOriginal platform value before TFM-based overrideStride.Platform.props
StridePlatformFullNamePlatform name + optional StrideBuildDirExtension suffixStride.Platform.props
StridePlatformsSemicolon-separated list of target platformsAuto-detected per OS
StridePlatformDepsPlatform identifier for native deps (dotnet, Android, iOS)Stride.Platform.props

Platform defines (added to DefineConstants):

PlatformDefines
Windows/Linux/macOSSTRIDE_PLATFORM_DESKTOP
AndroidSTRIDE_PLATFORM_MONO_MOBILE;STRIDE_PLATFORM_ANDROID
iOSSTRIDE_PLATFORM_MONO_MOBILE;STRIDE_PLATFORM_IOS
All .NETSTRIDE_RUNTIME_CORECLR

Graphics API

PropertyPurposeSet by
StrideGraphicsApiCurrent API (Direct3D11, Direct3D12, Vulkan)Stride.Graphics.props (platform default)
StrideGraphicsApisSemicolon-separated list of target APIsStride.Graphics.props
StrideDefaultGraphicsApiDefault/fallback API for the platformStride.Graphics.props
StrideGraphicsApiDependentEnable multi-API inner buildsProject (.csproj)
StrideGraphicsApiDependentBuildAllForce building all APIs (CI mode)Command line / build script

Default graphics APIs per platform:

PlatformDefaultAvailable
WindowsDirect3D11Direct3D11, Direct3D12,Vulkan
LinuxVulkanVulkan
macOSVulkanVulkan
AndroidVUlkanVulkan
iOSVulkanVulkan

Graphics API defines (added to DefineConstants):

APIDefines
Direct3D11STRIDE_GRAPHICS_API_DIRECT3D;STRIDE_GRAPHICS_API_DIRECT3D11
Direct3D12STRIDE_GRAPHICS_API_DIRECT3D;STRIDE_GRAPHICS_API_DIRECT3D12
VulkanSTRIDE_GRAPHICS_API_VULKAN

Build Control

PropertyPurposeSet by
StrideRuntimeTargetFrameworksSDK-computed runtime TFM set; a runtime project sets <TargetFrameworks> to this (or the …Windows/…MacOS variant)SDK (Stride.Platform.props)
StrideAssemblyProcessorEnable IL post-processing (serialization, module init)Project (.csproj)
StrideAssemblyProcessorOptionsProcessor flags (e.g., --serialization --auto-module-initializer)Project (.csproj)
StrideCodeAnalysisEnable code analysis rulesProject (.csproj)
StrideCompileAssetsEnable asset compilationProject (.csproj)
StrideScriptProject is a script assembly (auto-enables StrideAssemblyProcessor)Project (.csproj)
StridePublicApiGenerate .usrdoc documentation filesProject (.csproj)
StridePackageBuildBuilding for NuGet releaseBuild script
StrideSkipUnitTestsSkip test projects (faster builds)Command line
StrideLocalizedProject has localization satellite assembliesProject (.csproj)

Frameworks

PropertyValuePurpose
StrideFrameworknet10.0Base target framework
StrideFrameworkWindowsnet10.0-windowsWindows-specific TFM
StrideFrameworkAndroidnet10.0-androidAndroid TFM
StrideFrameworkiOSnet10.0-iosiOS TFM
StrideEditorTargetFrameworknet10.0-windowsEditor TFM (WPF)
StrideXplatEditorTargetFrameworknet10.0Cross-platform editor TFM

UI Framework

PropertyPurpose
StrideUISemicolon-separated UI frameworks: SDL, WINFORMS, WPF
StrideUIListItem group generated from $(StrideUI)

SDL is included for all non-UWP platforms. WINFORMS and WPF are added on Windows when using Direct3D11, Direct3D12, or Vulkan.

Defines: STRIDE_UI_SDL, STRIDE_UI_WINFORMS, STRIDE_UI_WPF.


Graphics API Multi-Targeting

Projects with StrideGraphicsApiDependent=true build separate binaries per API:

bin/Release/net10.0/
    Direct3D11/Stride.Graphics.dll
    Direct3D12/Stride.Graphics.dll
    Vulkan/Stride.Graphics.dll

This is implemented via a custom inner build system (Stride.GraphicsApi.InnerBuild.targets) that:

  1. Dispatches separate MSBuild inner builds per API, each with StrideGraphicsApi set
  2. Adjusts output paths to include the API name
  3. Propagates StrideGraphicsApiDependent through ProjectReference chains
  4. Creates the correct NuGet package layout with API-specific subdirectories

Note: This is non-standard MSBuild. IDEs may default IntelliSense to the first API.


Assembly Processor

When StrideAssemblyProcessor=true, the SDK runs IL post-processing after compilation:

  • Serialization code generation — generates binary serializers for [DataContract] types
  • Parameter key generation — for shader parameter keys
  • Auto module initializer — registers assemblies at startup

The processor is copied to a temp directory (keyed by hash) to avoid file locking during parallel builds.

Common option combinations:

Project typeOptions
Engine library--parameter-key --auto-module-initializer --serialization
Core library--auto-module-initializer --serialization

Native Build Mode (Clang / MSVC)

Native C++ projects (.vcxproj under sources/native/) are built using one of two toolchains, selected by Stride.NativeBuildMode.props:

ModeCompilerLinkerOutput
MSVCclangMSVC link.exeWindows PE
ClangclangLLVM LLDPE / ELF / Mach-O (cross-platform)

Auto-selection rules (in order):

  1. Explicit override via -p:StrideNativeBuildMode=Clang|MSVC, env var, or property → wins.
  2. Cross-compile (StridePlatform set to anything but Windows) → Clang (MSVC linker can't produce ELF/Mach-O).
  3. VCINSTALLDIR set (VS Developer Command Prompt) AND MSBuildRuntimeType == Full (running under MSBuild.exe, not dotnet build) → MSVC.
  4. Everything else → Clang.

The MSBuildRuntimeType == Full requirement is load-bearing: the .NET Core MSBuild engine used by dotnet build and dotnet msbuild cannot build .vcxproj end-to-end (it loads the Cpp targets but fails at task execution because Microsoft.Build.CPPTasks.Common depends on the Framework-only Microsoft.Build.Utilities.Core). Without this gate, dotnet build from a Developer Command Prompt would pick MSVC and fail with MSB4278 / TypeLoadException. Refs: dotnet/msbuild#6482, dotnet/sdk#10239.

Practical effect:

ShellToolMode
Regular shell on Windowsdotnet buildClang
VS Developer Command PromptMSBuild.exeMSVC
VS Developer Command Promptdotnet build / dotnet msbuildClang
VS IDE(msbuild.exe)MSVC
Linux/macOSdotnet buildClang

Native Dependencies (.ssdeps)

The .ssdeps system (Stride.Dependencies.targets) handles native library distribution:

  • .ssdeps files sit alongside referenced DLLs, listing native libraries (.dll/.so/.dylib) and content files
  • At build time, native libs are resolved and copied to the output directory
  • During NuGet packaging, native libs are placed in the correct runtimes/ layout
  • Platform-specific handling for desktop, Android, and iOS

Development Workflow

Building the SDK

The SDK packages don't need to be built for day-to-day development — projects now import SDK files directly from source. Rebuild the SDK packages only when preparing a NuGet release or when testing the full-SDK (Sdk="Stride.Build.Sdk") mode.

If you do need to build the packages (e.g. for a NuGet release), rebuild and clear the NuGet cache:

# 1. Kill any running MSBuild/dotnet processes
taskkill /F /IM dotnet.exe 2>nul

# 2. Clean NuGet cache
rmdir /s /q "%USERPROFILE%\.nuget\packages\stride.build.sdk" 2>nul
rmdir /s /q "%USERPROFILE%\.nuget\packages\stride.build.sdk.editor" 2>nul
rmdir /s /q "%USERPROFILE%\.nuget\packages\stride.build.sdk.tests" 2>nul

# 3. Build the SDK
dotnet build sources\sdk\Stride.Build.Sdk.slnx

# 4. Verify packages
dir build\packages\*.nupkg

NuGet Package Flow

sources/sdk/              (SDK source code)
    | dotnet build
build/packages/            (Local .nupkg files)
    | dotnet restore (on consuming project)
%USERPROFILE%\.nuget\packages\  (NuGet global cache)
    | Build uses cached SDK

Common issue: Old SDK version cached. Always clear cache after SDK changes.

Testing Changes

# Test a single project
dotnet build sources\core\Stride.Core\Stride.Core.csproj

# Test with restore (catches restore-phase issues)
dotnet msbuild -restore -t:Build sources\core\Stride.Core\Stride.Core.csproj

Debugging MSBuild Evaluation

Preprocess a project to see the fully expanded MSBuild XML:

dotnet msbuild -preprocess:output.xml sources\core\Stride.Core\Stride.Core.csproj
dotnet msbuild -property:TargetFramework=net10.0 -preprocess:output.xml sources\core\Stride.Core\Stride.Core.csproj

Verbose build output:

dotnet build -v:detailed sources\core\Stride.Core\Stride.Core.csproj

Design Decisions

SDK composition: internal chaining

Stride.Build.Sdk internally imports Microsoft.NET.Sdk. Users only reference <Project Sdk="Stride.Build.Sdk">. This follows the pattern used by Microsoft.NET.Sdk.Web and gives the SDK full control over import order.

The alternative (additive SDKs where users write <Project Sdk="Microsoft.NET.Sdk"><Sdk Name="Stride.Build.Sdk" />) was rejected: more verbose, potential ordering issues, and requires users to manage two SDK references.

Three SDK packages instead of one

Separating Stride.Build.Sdk.Editor prevents engine runtime projects from accidentally depending on editor frameworks (WPF). Separating Stride.Build.Sdk.Tests keeps xunit dependencies out of production code. The hierarchy ensures each project type gets exactly the right defaults.

No Stride.Build.Sdk.Runtime package

Initially considered, but unnecessary. Runtime projects use Stride.Build.Sdk directly and declare <TargetFrameworks>$(StrideRuntimeTargetFrameworks)</TargetFrameworks> — a set the SDK precomputes in Stride.Platform.props from the active platforms.

Evaluation timing: defaults in props, logic in targets

All user-configurable flags (StrideAssemblyProcessor, StrideCodeAnalysis, etc.) get default values in Sdk.props and are checked in Sdk.targets. This is the standard MSBuild SDK pattern and avoids the evaluation-order bugs present in the old system. (Framework selection sidesteps it differently — see the note under Correct patterns.)

No build/ convention files

NuGet's build/ convention auto-imports .props and .targets files even for SDK packages, causing double-import. The SDK exclusively uses the Sdk/ folder for MSBuild SDK resolution.


Features Intentionally Not Ported

FeatureReason
Xamarin-specific workarounds.NET for Android/iOS doesn't need them
SolutionName defaultNot needed in SDK-style builds
StridePackageStride path resolutionPackage paths are SDK-relative
DependencyDir, BuildDir, SourceDirPackage structure replaces relative paths
Empty default targets (Build, Clean)Microsoft.NET.Sdk provides these
ErrorReport=prompt, FileAlignment=512.NET defaults are sufficient
ExecutableExtension.NET SDK handles this
C++ output path for vcxprojC++ projects don't use Stride.Build.Sdk
UWP-specific propertiesUWP is being phased out

Troubleshooting

Build fails after SDK changes

SDK files are imported directly from source — changes take effect on the next build with no cache clear needed. If you are testing the NuGet package mode (Sdk="Stride.Build.Sdk"), then after SDK changes you still need to kill dotnet processes, clear the cache, and rebuild the packages. See "Building the SDK" above.

Configuration is empty (bin\net10.0\ instead of bin\Debug\net10.0\)

This was caused by build/ convention files in the SDK package. They have been removed. If it recurs, check that no build/ folder exists in the SDK packages.

Properties from .csproj not visible

The property is likely being read in Sdk.props (too early). Move the logic to Sdk.targets.

Multi-targeting not working

Ensure the .csproj declares <TargetFrameworks>$(StrideRuntimeTargetFrameworks)</TargetFrameworks> (or the …Windows/…MacOS variant; tests use $(StrideTestTargetFrameworks)). The set is computed in Stride.Platform.props from $(StridePlatforms), so pass -p:StridePlatforms=Windows;Android (etc.) to target other platforms.

Assembly processor not running

Check that StrideAssemblyProcessor=true is set. Verify the processor binaries exist. Clear the NuGet cache and rebuild the SDK.


References