Skip to content

Replace MSBuildWorkspace hosting to remove the MSBuildLocator hazard in single-file native builds #50

Description

@woksin

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions