Files
vs-code-dotnet-solution-lau…/README.md
T
maxandClaude Fable 5.1 3f67eef9aa Initial version of .NET Solution Launcher
Startup project and solution configuration (Debug|x64, Test|x64, ...) in
the status bar, with build and debug tasks that pass both configuration
and platform. Made for DotRush, whose build never passes -p:Platform and
whose status item shows the configuration but not the project.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0169iPWwKHZoBTNN9qwXiwqk
2026-09-08 13:10:55 +02:00

142 lines
7.2 KiB
Markdown

# .NET Solution Launcher
Startup project and solution configuration in the status bar, with build and debug that
actually use them. Built for [DotRush](https://github.com/JaneySprings/DotRush), next to
the `.NET Hot Reload` extension in this repo.
```
$(project) MyGame.Editor $(settings-gear) Test | x64 $(debug-alt)
```
- The **project item** shows which `.csproj` is the startup project, and picks another one
from the executables in the solution (Runtime is a library, so it is not offered).
- The **configuration item** shows the *solution* configuration — `Debug | x64`,
`Test | x64`, `Release | Any CPU` — exactly as `MyGame.sln` lists them.
- The **debug button** builds with that configuration and launches the startup project.
## Why, when DotRush already has a status bar item
DotRush shows `Debug | net10.0`. Two things are missing from that:
1. **The project name.** It is in the tooltip only. With three executables in the
solution (Builder, Editor, and whatever comes next) that is the thing you need to see.
2. **The platform.** DotRush's build task runs `dotnet build <project> -p:Configuration=X`
with no `-p:Platform`. MSBuild then defaults to `AnyCPU`, and every property group
conditioned on `'$(Configuration)|$(Platform)' == 'Debug|x64'` is skipped. In this
solution that is not cosmetic:
| `MyGame.Runtime`, Configuration=Debug | `DefineConstants` |
| --- | --- |
| without `-p:Platform` (what DotRush runs) | `TRACE;DEBUG` |
| with `-p:Platform=x64` | `TRACE;LOG_INFO;PROFILING;DEBUG` |
So logging and profiling silently vanish, `Optimize` is never set for Test/Release,
and `MyGame.Builder` loses `AllowUnsafeBlocks` and fails to compile.
DotRush also cannot be *told* a configuration from outside — its selection lives in its
own workspace state — so this extension owns the build rather than wrapping
`dotrush: Build`. The startup *project* is synced with DotRush in both directions
(`dotnetSolution.syncDotRush`), so its test explorer, `launchSettings.json` lookup and
`${command:dotrush.activeProjectPath}` all agree with what the status bar says.
## What a build does
`dotnetSolution.buildScope` decides (default `solution`):
| Scope | Command | Notes |
| --- | --- | --- |
| `solution` | `dotnet build MyGame.sln -c Test -p:Platform=x64` | Like Visual Studio's *Build Solution*. MSBuild maps every project through the `.sln`: MyGame.* build as `Test\|x64`, MoonWorks as `Debug\|Any CPU`, and the Editor → Builder `ProjectDependencies` entry is honoured, so the Builder exists before the Editor's post-build step needs it. |
| `project` | `dotnet build MyGame.Editor.csproj -c Test -p:Platform=x64` | Only the startup project and its `ProjectReference`s. Faster, but solution-only dependencies are ignored. |
Rebuild adds `--no-incremental`; Clean runs `dotnet clean`. All three are tasks of type
`dotnet-solution`, so they show up under *Run Task* and can be used as `preLaunchTask`.
## What a launch does
`program` comes from MSBuild, not from guessing: the extension evaluates the startup
project with the mapped configuration and platform (`dotnet msbuild -getProperty:TargetPath …`),
which is the only way to get it right here — `OutDir` comes from `Directory.Build.props`
and `AppendTargetFrameworkToOutputPath` is off. The result is cached until a project or
solution file changes.
The apphost `.exe` is launched when the project produces one, since vsdbg wants an
executable; otherwise it runs `dotnet <TargetPath>`. Working directory is `TargetDir`
unless `dotnetSolution.launch.cwd` says otherwise. DotRush's debug configuration provider
still runs after this one and fills in `justMyCode`, symbol options and the console.
## Usage
| Action | How |
| --- | --- |
| Pick the startup project | Click the project name, or right-click a `.csproj`*Set as Startup Project (.NET Solution)* |
| Pick the configuration | Click `Debug \| x64`. Entries the solution does not build the project under are marked with `$(warning)` |
| Build / Rebuild / Clean | *.NET Solution: Build* etc., or the `dotnet-solution` tasks |
| Debug | The `$(debug-alt)` button, *.NET Solution: Debug Startup Project*, or F5 with the launch.json entry below |
| Run without debugging | *.NET Solution: Run Startup Project (without debugging)* |
| Several `.sln` files | *.NET Solution: Select Solution*, or `dotnetSolution.solution` |
### F5
The first time it sees a solution the extension offers to write a `launch.json` entry
(*.NET Solution: Create launch.json and tasks.json entries* does it later):
```jsonc
{
"name": ".NET Solution: Debug startup project",
"type": "coreclr",
"request": "launch",
"program": "${command:dotnetSolution.activeProgram}",
"cwd": "${command:dotnetSolution.activeTargetDir}",
"preLaunchTask": "dotnet-solution: Build"
}
```
The same entry is offered dynamically in the *Run and Debug* dropdown even without a
`launch.json`. It replaces the DotRush template's `${command:dotrush.activeTargetPath}` +
`preLaunchTask: "dotrush: Build"` pair, which is the one that builds without a platform.
### Variables for your own launch.json / tasks.json
| `${command:…}` | Value |
| --- | --- |
| `dotnetSolution.activeProgram` | apphost `.exe`, or the `.dll` when there is none |
| `dotnetSolution.activeTargetPath` | the built assembly |
| `dotnetSolution.activeTargetDir` | its directory |
| `dotnetSolution.activeProjectPath`, `activeProjectName` | the startup project |
| `dotnetSolution.activeSolutionPath` | the solution |
| `dotnetSolution.activeConfiguration`, `activePlatform` | solution configuration, e.g. `Test`, `Any CPU` |
| `dotnetSolution.activeProjectConfiguration`, `activeProjectPlatform` | what the project gets, e.g. `Test`, `AnyCPU` |
## Settings
| Setting | Default | |
| --- | --- | --- |
| `dotnetSolution.solution` | `""` | Solution to use; empty picks the shallowest, then the largest |
| `dotnetSolution.buildScope` | `solution` | See above |
| `dotnetSolution.targetFramework` | `""` | For `TargetFrameworks` projects; empty takes the first |
| `dotnetSolution.launch.args` / `.env` / `.cwd` / `.console` | | Passed to the launched application |
| `dotnetSolution.debugType` | `coreclr` | |
| `dotnetSolution.syncDotRush` | `true` | Push the startup project to DotRush and follow its changes |
| `dotnetSolution.syncDotRushWorkspaceProperties` | `false` | Write `Configuration=…;Platform=…` into `dotrush.roslyn.workspaceProperties` so IntelliSense sees the same `DefineConstants`. Off because DotRush reloads its workspace on every change |
| `dotnetSolution.additionalBuildArguments` | `[]` | Appended to every build |
`dotrush.roslyn.dotnetSdkDirectory` and `dotrush.msbuild.additionalEnvironment` are
honoured, so both extensions use the same SDK.
## Tips
- DotRush's own `Debug | net10.0` item can be hidden: right-click the status bar and
untick *DotRush*. Nothing else depends on it once this extension drives the build.
- The status bar goes to a spinner while a build runs; the build output is in the
terminal and the *Problems* panel (`$msCompile`).
- The *.NET Solution* output channel logs every `dotnet` invocation.
## Development
```
npm install
npm run compile
npm run test:unit # parser tests, against D:\Projects\MyGame when present
npm test # launches VS Code on that folder (SOLUTION_TEST_FOLDER overrides)
```