A compiler turns one language into another. Building a real program needs far more — resolving dependencies, ordering projects, generating config, producing something runnable. The SDK is the whole assembly line; the compiler is one station on it.
Order 3 drew the line: the runtime runs apps, the SDK builds them, and the SDK contains a runtime. On the .NET download page this shows up as two separate choices.
This is why a development box never installs the runtime separately. Two commands make the split visible:
dotnet --list-sdks # what can build
dotnet --list-runtimes # what can run
A machine can have several of each, at different versions, side by side.
The SDK is not one program. It is a coordinated bundle of tools, each responsible for one part of turning source into a running application:
dotnet SDK
|
+-- Runtime also lets the SDK run apps
| +-- CLR · JIT · GC · BCL
|
+-- Roslyn C# compiler (source -> IL)
+-- MSBuild build orchestrator (reads .csproj)
+-- dotnet CLI the command that gets typed
+-- NuGet client fetches package dependencies
+-- Templates dotnet new scaffolding
+-- Test host dotnet test integration
Every later topic in this series is really a close-up of one of these boxes.
Roslyn's job is narrow and precise: take the .cs files, produce IL and metadata for one assembly. That is genuinely all a compiler does — and it is not enough to produce a program anyone can run.
Building a real project also requires:
deps.json and runtimeconfig.json,bin.MSBuild coordinates all of that and calls Roslyn as a single step inside it. The SDK ships both tools plus the rules that wire them together. That gap — between "compile one assembly" and "produce a runnable project" — is exactly why the SDK is more than a compiler.
Every one of those tools is reached through a single command: dotnet.
dotnet new scaffold a project from a template
dotnet restore fetch dependencies
dotnet build compile to IL, produce output
dotnet run build, then start the app
dotnet publish produce a deployable output
dotnet test build and run tests
dotnet add package edit the .csproj dependency list
The CLI itself is a thin driver. It reads the command, then hands the real work to MSBuild, NuGet, Roslyn, or the runtime as appropriate. One tool to memorise; many engines behind it.
An SDK carries its own version — 8.0.400, for instance. That single SDK can build projects targeting several different runtime versions, so "which .NET is installed" always has two answers: which SDKs can build, and which runtimes can run. The two lists from earlier exist precisely because these are different questions.
For repeatable builds across a team, a global.json file can pin which SDK version a repository uses, so every machine builds with the same toolchain rather than whatever happens to be newest locally.
The most-typed command is dotnet build, and it is a relay:
dotnet build
-> CLI dispatches to MSBuild
-> MSBuild reads the .csproj, restores, resolves references
-> MSBuild calls Roslyn (source -> IL)
-> output written to bin: MyApi.dll + config files
dotnet run does all of that and then starts the app through the runtime. That relay is the map for the next stretch of the series: order 5 walks the whole build end to end, order 6 zooms into MSBuild, and order 7 into Roslyn.
So the natural next question is the one behind the most common command of all — step by step, what actually happens when .cs files become a DLL? That is order 5.
The dotnet executable wears two hats, and which one it wears depends on the first argument. dotnet build / dotnet run — it acts as the CLI driver, dispatching to SDK tools. dotnet MyApi.dll — it acts as the app host, starting the runtime to execute an assembly. Same binary, two completely different jobs, chosen by whether the first argument is a known SDK verb or a path to a DLL. Noticing this collapses a lot of "wait, is dotnet the build tool or the runtime?" confusion — it is both, by design.