Fluent, strongly-typed C# DSL for describing a .NET "solution" and generating a compact .slnx file that MSBuild can parse to build all referenced projects. Provides two consumption modes:
- Wrapper project (XML) referencing a C# script (current / stable) — build with
dotnet build MySolution.slncs. - Pure single-file C# script (
.slncs) — build with the helper toolslncs-build(no manual wrapper needed).
The generator produces a .slnx file plus an optional aggregator MSBuild project (.slnx.proj) that references all discovered projects for convenient bulk operations.
- Quick Start
- DSL Overview
- Tooling
- Generated Artifacts
- How It Works
- Examples
- Incremental / Idempotent Behavior
- CLI & Tool Commands
- Local Development
- Testing
- Roadmap
- License
Suitable when you want to invoke dotnet build directly on the wrapper (no extra tooling).
MySolution.slncs (XML wrapper MSBuild project)
MySolution.slncs.cs (C# DSL script)
MySolution.slncs:
<Project Sdk="Slncs.Sdk">
<PropertyGroup>
<SlncsFile>MySolution.slncs.cs</SlncsFile>
<GeneratedSlnx>obj\MySolution.slnx</GeneratedSlnx>
</PropertyGroup>
</Project>MySolution.slncs.cs:
using Slncs;
Solution.Create()
.Folder("/Solution Items", f => f.Files("Directory.Build.props"))
.Project("src/ClassLibrary1/ClassLibrary1.csproj")
.Project("src/ConsoleApp1/ConsoleApp1.csproj")
.Write(OutputPath); // OutputPath provided by generator hostBuild it:
dotnet build MySolution.slncs -v:mNo wrapper XML; you keep only MySolution.slncs (C# code). The tool creates a transient wrapper under obj/.slncs-build.
MySolution.slncs (note: still C# code, just a different extension):
using Slncs;
Solution.Create()
.Project("src/App/App.csproj")
.Write(OutputPath);Build with the tool (after building or installing it):
# Run from repo (development):
dotnet run --project Slncs.Tool -- MySolution.slncs
# Or after packing & installing as a global/local tool:
slncs-build MySolution.slncsThe generated .slnx ends up at obj/MySolution.slnx.
The core entry point:
Solution.Create()
.Project("relative/path/to/Project.csproj")
.Folder("/Solution Items", f => f.Files("README.md", "Directory.Build.props"))
.Write(OutputPath);Fluent members:
Project(string path)— Add a project (relative recommended; duplicates coalesced).Folder(string name, Action<FolderBuilder>)— Add a logical folder (name normalized to include trailing '/').FolderBuilder.File(string path)/Files(params string[] paths)— Add file entries inside a folder.- Nested folders via
FolderBuilder.Folder(...). Write(string path)(internal use: you typically call.Write(OutputPath)inside the script). If the provided path does not end in.slnxit is appended.
Output XML structure (simplified):
<Solution>
<Folder Name="/Solution Items/">
<File Path="Directory.Build.props" />
</Folder>
<Project Path="src/ClassLibrary1/ClassLibrary1.csproj" />
<Project Path="src/ConsoleApp1/ConsoleApp1.csproj" />
</Solution>Sorting & de-duplication:
- Folders (SortKey prefix
0|) - Projects (
1|) - Files (
2|) - Within each group lexicographical ordering ensures deterministic output.
Generates a temporary wrapper project for a pure .slncs script, then calls dotnet build on it. The transient wrapper lives at obj/.slncs-build/<Name>.wrapper.slncs so that the repo's global.json pin (msbuild-sdks) applies.
Exit codes:
0success2invalid path or missing file- Other: underlying build failure
Imported via <Project Sdk="Slncs.Sdk">. It provides two internal tasks:
SlncsExec— runs the generator to produce.slnx+ optional aggregator.SlnxParse— reads.slnxand yields project paths for subsequentMSBuildinvocation.
Generated aggregator (if any): <YourSolution>.slnx.proj referencing all discovered projects via <ProjectReference/>.
| Artifact | Purpose |
|---|---|
obj/<Name>.slnx |
Canonical solution description used to enumerate projects. |
obj/<Name>.slnx.proj |
Aggregator no-targets project referencing all projects (IDE load, bulk build). |
obj/.slncs-build/<Name>.wrapper.slncs |
Transient wrapper created only in pure-script mode. |
- Your C# script (executed by Roslyn scripting) produces a
.slnxby callingSolution.Create()...Write(OutputPath). SlncsExectask captures that output, writes aggregator (optional) and returns control to MSBuild.SlnxParseenumerates<Project Path="..."/>elements for forwarding withMSBuildtasks (Restore;Build).
The .slnx format intentionally mirrors only essentials (projects, folders, loose files) — not a full .sln equivalent.
Minimal:
Solution.Create().Project("src/App/App.csproj").Write(OutputPath);With folders & multiple projects:
Solution.Create()
.Folder("/Solution Items", f => f.Files("README.md", "Directory.Build.props"))
.Project("src/Lib/Lib.csproj")
.Project("src/Tool/Tool.csproj")
.Write(OutputPath);- Duplicate calls to
Project("A.csproj")or identical file entries are coalesced. - Ordering is stable for deterministic diff-friendly output.
- Re-running the script regenerates
.slnx; aggregator is overwritten if contents change.
Build wrapper:
dotnet build MySolution.slncsBuild pure script:
slncs-build MySolution.slncsPack & install locally (development):
# From repo root
dotnet pack Slncs.Sdk -c Release
dotnet pack Slncs.Tool -c Release
# Optionally install tool (local feed or nupkg path)
dotnet tool install --global --add-source ./local-packages Slncs.ToolRun tests:
dotnet test -c DebugRecommended workflow:
# 1. Clone
git clone <repo>
cd Slncs
# 2. Build everything (including tool + generator copied into SDK tools folder)
dotnet build
# 3. Run the wrapper sample (template)
dotnet build samples/template/MyCsSln.slncs
# 4. Run the pure script sample (no wrapper)
# Using the tool project directly (dev scenario):
dotnet run --project Slncs.Tool -- samples/pure/MyCsSln.slncs
# Or if you installed the tool globally:
slncs-build samples/pure/MyCsSln.slncsTest projects:
Slncs.Tests— DSL & generator smoke tests.Slncs.Sdk.Tests— MSBuild task and runner tests.E2E.Tests— End-to-end wrapper and pure script build verifications.
All tests:
dotnet test- Optional caching / up-to-date checks for large solutions.
- Additional DSL helpers (globbing, conditional inclusion, grouping).
- Rich CLI (list projects, diff two
.slnxversions). - Editor integration (Roslyn analyzers / IntelliSense improvements).