cratis screenplay generate loads projects through MSBuildWorkspace, which requires MSBuildLocator.RegisterDefaults() to run before any MSBuild type is touched. That is workable for the SDK-hosted CLI, but it is a hazard for the single-file native builds distributed via Homebrew: MSBuildLocator resolves and loads MSBuild assemblies from an installed .NET SDK at runtime, and the assembly-load behaviour a single-file binary gets is not the behaviour the SDK-hosted one gets.
The current code is written defensively around this — every member that touches an MSBuild type carries [MethodImpl(MethodImplOptions.NoInlining)], because the JIT would otherwise resolve those types while RegisterMSBuild was still running. That workaround is a good signal of how sharp the edge is.
Suggested direction
Drop MSBuildWorkspace and shell out to the user's own SDK instead:
dotnet msbuild -getItem:Compile,ReferencePathWithRefAssemblies
after a build, then construct the CSharpCompilation directly from the returned items.
This has three advantages beyond removing the hazard:
- It matches the CLI's existing posture of driving an external tool found on
PATH (the way cratis run uses docker), rather than hosting a toolchain in-process.
- It removes
Microsoft.Build.Locator, Microsoft.CodeAnalysis.Workspaces.MSBuild and the Microsoft.Build.Framework ExcludeAssets="runtime" dance from the CLI's dependency graph.
- The item list comes from a real build, so build-generated compile items are present by construction.
Relationship to #49
#49 reported that build-generated compile items (strongly-typed resx designer classes) are missing from the design-time build, and floated this refactor as one possible fix. That specific defect has since been fixed a different way — by injecting a targets file through CustomAfterMicrosoftCommonTargets so PrepareResources runs before CoreCompile, which makes MSBuild generate the sources and add them to @(Compile) itself, even for a project that has never been built.
So this issue is not about that defect, which is resolved. It is about the hosting model, which is worth deciding on its own merits rather than as a side effect of a bug fix.
Scope note
This is a behavioural change to how projects are loaded, so it needs re-verification against a real application end to end: generate, then compile the result with the official Screenplay compiler, and confirm the document is unchanged and still deterministic.
cratis screenplay generateloads projects throughMSBuildWorkspace, which requiresMSBuildLocator.RegisterDefaults()to run before any MSBuild type is touched. That is workable for the SDK-hosted CLI, but it is a hazard for the single-file native builds distributed via Homebrew:MSBuildLocatorresolves and loads MSBuild assemblies from an installed .NET SDK at runtime, and the assembly-load behaviour a single-file binary gets is not the behaviour the SDK-hosted one gets.The current code is written defensively around this — every member that touches an MSBuild type carries
[MethodImpl(MethodImplOptions.NoInlining)], because the JIT would otherwise resolve those types whileRegisterMSBuildwas still running. That workaround is a good signal of how sharp the edge is.Suggested direction
Drop
MSBuildWorkspaceand shell out to the user's own SDK instead:after a build, then construct the
CSharpCompilationdirectly from the returned items.This has three advantages beyond removing the hazard:
PATH(the waycratis runusesdocker), rather than hosting a toolchain in-process.Microsoft.Build.Locator,Microsoft.CodeAnalysis.Workspaces.MSBuildand theMicrosoft.Build.FrameworkExcludeAssets="runtime"dance from the CLI's dependency graph.Relationship to #49
#49 reported that build-generated compile items (strongly-typed resx designer classes) are missing from the design-time build, and floated this refactor as one possible fix. That specific defect has since been fixed a different way — by injecting a targets file through
CustomAfterMicrosoftCommonTargetssoPrepareResourcesruns beforeCoreCompile, which makes MSBuild generate the sources and add them to@(Compile)itself, even for a project that has never been built.So this issue is not about that defect, which is resolved. It is about the hosting model, which is worth deciding on its own merits rather than as a side effect of a bug fix.
Scope note
This is a behavioural change to how projects are loaded, so it needs re-verification against a real application end to end: generate, then compile the result with the official Screenplay compiler, and confirm the document is unchanged and still deterministic.