From df73a5d14f1f632bc46529bb5682f113a9f7918c Mon Sep 17 00:00:00 2001
From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com>
Date: Sun, 5 Apr 2026 00:23:16 +0100
Subject: [PATCH 1/5] perf: optimize solution build performance
- Enable ProduceReferenceAssembly to skip recompilation of dependents
when only internals change
- Enable RestoreUseStaticGraphEvaluation for faster NuGet restore
- Enable hardlinks for CopyToOutputDirectory to avoid file copies
- Move GitVersion.MsBuild from GlobalPackageReference (all 69 projects)
to explicit PackageReference on the 4 projects that use it
- Separate restore from build in CI workflow and enable graph build
scheduling (-graphBuild:True) for better parallelism
- Update workflow docs with fast inner-loop build commands
---
.claude/docs/workflows.md | 11 +++++++++++
.github/workflows/dotnet.yml | 6 +++++-
Directory.Build.props | 9 +++++++++
Directory.Packages.props | 5 +----
TUnit.Assertions/TUnit.Assertions.csproj | 6 ++++++
TUnit.Core/TUnit.Core.csproj | 6 ++++++
TUnit.Engine/TUnit.Engine.csproj | 4 ++++
TUnit.Mocks/TUnit.Mocks.csproj | 6 ++++++
8 files changed, 48 insertions(+), 5 deletions(-)
diff --git a/.claude/docs/workflows.md b/.claude/docs/workflows.md
index 54d08d393c..773d67aad6 100644
--- a/.claude/docs/workflows.md
+++ b/.claude/docs/workflows.md
@@ -5,6 +5,17 @@
## Common Commands
```bash
+# Build (fast inner loop — use dev solution, separate restore, graph scheduling)
+dotnet restore TUnit.Dev.slnx
+dotnet build TUnit.Dev.slnx --no-restore -graphBuild:True
+
+# Build with centralized output layout
+dotnet build TUnit.Dev.slnx --no-restore -graphBuild:True --artifacts-path artifacts
+
+# Build full solution (CI or pre-commit)
+dotnet restore TUnit.slnx
+dotnet build TUnit.slnx --no-restore -graphBuild:True
+
# Run all tests (excludes TUnit.TestProject)
dotnet test
diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml
index 8da6269d71..ce594ffec1 100644
--- a/.github/workflows/dotnet.yml
+++ b/.github/workflows/dotnet.yml
@@ -125,10 +125,14 @@ jobs:
id: gitversion
uses: gittools/actions/gitversion/execute@v3
+ - name: Restore
+ shell: bash
+ run: dotnet restore TUnit.CI.slnx
+
- name: Build
shell: bash
run: >-
- dotnet build TUnit.CI.slnx -c Release
+ dotnet build TUnit.CI.slnx -c Release --no-restore -graphBuild:True
"-p:Version=${{ steps.gitversion.outputs.semVer }}"
"-p:AssemblyVersion=${{ steps.gitversion.outputs.assemblySemVer }}"
"-p:FileVersion=${{ steps.gitversion.outputs.assemblySemFileVer }}"
diff --git a/Directory.Build.props b/Directory.Build.props
index 53ee2f1295..e376ada6aa 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -31,6 +31,15 @@
enable
enable
preview
+
+
+ true
+
+ true
+
+ true
+ true
+
false
NU1507;NU1903;CS9107
buildTransitive/$(TargetFramework)/
diff --git a/Directory.Packages.props b/Directory.Packages.props
index adfc7a52b4..f747768636 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -4,10 +4,7 @@
-
- all
- runtime; build; native; contentfiles; analyzers
-
+
diff --git a/TUnit.Assertions/TUnit.Assertions.csproj b/TUnit.Assertions/TUnit.Assertions.csproj
index 1809c6042f..c91e9f676b 100644
--- a/TUnit.Assertions/TUnit.Assertions.csproj
+++ b/TUnit.Assertions/TUnit.Assertions.csproj
@@ -48,6 +48,12 @@
+
+
+ all
+ runtime; build; native; contentfiles; analyzers
+
+
diff --git a/TUnit.Core/TUnit.Core.csproj b/TUnit.Core/TUnit.Core.csproj
index c0c1082638..14d727fe60 100644
--- a/TUnit.Core/TUnit.Core.csproj
+++ b/TUnit.Core/TUnit.Core.csproj
@@ -60,6 +60,12 @@
Include="$(MSBuildProjectDirectory)\..\TUnit.Core.SourceGenerator.Roslyn414\bin\$(Configuration)\netstandard2.0\TUnit.Core.SourceGenerator.dll"
Pack="true" PackagePath="analyzers/dotnet/roslyn4.14/cs" Visible="false" />
+
+
+ all
+ runtime; build; native; contentfiles; analyzers
+
+
diff --git a/TUnit.Engine/TUnit.Engine.csproj b/TUnit.Engine/TUnit.Engine.csproj
index 33b43df182..a4773857a8 100644
--- a/TUnit.Engine/TUnit.Engine.csproj
+++ b/TUnit.Engine/TUnit.Engine.csproj
@@ -14,6 +14,10 @@
+
+ all
+ runtime; build; native; contentfiles; analyzers
+
diff --git a/TUnit.Mocks/TUnit.Mocks.csproj b/TUnit.Mocks/TUnit.Mocks.csproj
index 106df21a1c..af36644996 100644
--- a/TUnit.Mocks/TUnit.Mocks.csproj
+++ b/TUnit.Mocks/TUnit.Mocks.csproj
@@ -51,6 +51,12 @@
+
+
+ all
+ runtime; build; native; contentfiles; analyzers
+
+
From f860da1173c6d4568de133cc38799ba7749fa996 Mon Sep 17 00:00:00 2001
From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com>
Date: Sun, 5 Apr 2026 00:30:27 +0100
Subject: [PATCH 2/5] refactor: centralize GitVersion PackageReference and
clean up comments
- Move GitVersion.MsBuild PackageReference from 4 individual .csproj
files into a single conditional block in Directory.Build.props
- Move GitVersion.MsBuild PackageVersion to the alphabetized main
ItemGroup in Directory.Packages.props
- Remove redundant WHAT comments on build performance properties
---
Directory.Build.props | 15 ++++++++++++---
Directory.Packages.props | 2 +-
TUnit.Assertions/TUnit.Assertions.csproj | 6 ------
TUnit.Core/TUnit.Core.csproj | 6 ------
TUnit.Engine/TUnit.Engine.csproj | 4 ----
TUnit.Mocks/TUnit.Mocks.csproj | 6 ------
6 files changed, 13 insertions(+), 26 deletions(-)
diff --git a/Directory.Build.props b/Directory.Build.props
index e376ada6aa..555c835416 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -32,11 +32,8 @@
enable
preview
-
true
-
true
-
true
true
@@ -105,4 +102,16 @@
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers
+
+
+
\ No newline at end of file
diff --git a/Directory.Packages.props b/Directory.Packages.props
index f747768636..e2f8fc3aa0 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -4,7 +4,6 @@
-
@@ -19,6 +18,7 @@
+
diff --git a/TUnit.Assertions/TUnit.Assertions.csproj b/TUnit.Assertions/TUnit.Assertions.csproj
index c91e9f676b..1809c6042f 100644
--- a/TUnit.Assertions/TUnit.Assertions.csproj
+++ b/TUnit.Assertions/TUnit.Assertions.csproj
@@ -48,12 +48,6 @@
-
-
- all
- runtime; build; native; contentfiles; analyzers
-
-
diff --git a/TUnit.Core/TUnit.Core.csproj b/TUnit.Core/TUnit.Core.csproj
index 14d727fe60..c0c1082638 100644
--- a/TUnit.Core/TUnit.Core.csproj
+++ b/TUnit.Core/TUnit.Core.csproj
@@ -60,12 +60,6 @@
Include="$(MSBuildProjectDirectory)\..\TUnit.Core.SourceGenerator.Roslyn414\bin\$(Configuration)\netstandard2.0\TUnit.Core.SourceGenerator.dll"
Pack="true" PackagePath="analyzers/dotnet/roslyn4.14/cs" Visible="false" />
-
-
- all
- runtime; build; native; contentfiles; analyzers
-
-
diff --git a/TUnit.Engine/TUnit.Engine.csproj b/TUnit.Engine/TUnit.Engine.csproj
index a4773857a8..33b43df182 100644
--- a/TUnit.Engine/TUnit.Engine.csproj
+++ b/TUnit.Engine/TUnit.Engine.csproj
@@ -14,10 +14,6 @@
-
- all
- runtime; build; native; contentfiles; analyzers
-
diff --git a/TUnit.Mocks/TUnit.Mocks.csproj b/TUnit.Mocks/TUnit.Mocks.csproj
index af36644996..106df21a1c 100644
--- a/TUnit.Mocks/TUnit.Mocks.csproj
+++ b/TUnit.Mocks/TUnit.Mocks.csproj
@@ -51,12 +51,6 @@
-
-
- all
- runtime; build; native; contentfiles; analyzers
-
-
From 0f1bc8f4429a699d3d7a56e3fb52fbc572fa7fc4 Mon Sep 17 00:00:00 2001
From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com>
Date: Sun, 5 Apr 2026 00:31:35 +0100
Subject: [PATCH 3/5] chore: add TUnit.Dev.slnx for fast inner-loop builds
Trimmed solution (38 projects) excluding templates, CloudShop examples,
Roslyn variants, benchmarks, and pipeline.
---
TUnit.Dev.slnx | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 66 insertions(+)
create mode 100644 TUnit.Dev.slnx
diff --git a/TUnit.Dev.slnx b/TUnit.Dev.slnx
new file mode 100644
index 0000000000..00629adc7f
--- /dev/null
+++ b/TUnit.Dev.slnx
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From 3bef9adce397ba8d06c9e8017b5289acddb2680e Mon Sep 17 00:00:00 2001
From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com>
Date: Sun, 5 Apr 2026 01:21:28 +0100
Subject: [PATCH 4/5] fix: remove hard link publish setting causing AOT file
lock conflicts
Parallel Native AOT publish processes all point to the same inode when
hard links are enabled, causing IOException when ILCompiler tries to
access TUnit.Assertions.dll concurrently.
---
Directory.Build.props | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Directory.Build.props b/Directory.Build.props
index 555c835416..c53b236870 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -34,8 +34,8 @@
true
true
+
true
- true
false
NU1507;NU1903;CS9107
From 06d5a3191a8f87fdfebca26de161eb905677c64f Mon Sep 17 00:00:00 2001
From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com>
Date: Sun, 5 Apr 2026 01:22:10 +0100
Subject: [PATCH 5/5] fix: remove copy hard link setting that also risks AOT
file lock conflicts
ILCompiler reads from build output directories during AOT publish.
With hard links, all test projects point to the same inode for shared
assemblies (e.g. TUnit.Assertions.dll), causing the same lock contention
as the publish hard links removed in the previous commit.
---
Directory.Build.props | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Directory.Build.props b/Directory.Build.props
index c53b236870..7064dddf7f 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -34,8 +34,8 @@
true
true
-
- true
+
false
NU1507;NU1903;CS9107