Skip to content

start-vs-VisualFSharpSln.ps1: build against a locally built hive Roslyn from its own packages - #20528

Open
xperiandri wants to merge 3 commits into
dotnet:mainfrom
xperiandri:start-vs-local-roslyn-packages
Open

xperiandri wants to merge 3 commits into
dotnet:mainfrom
xperiandri:start-vs-local-roslyn-packages

Conversation

@xperiandri

Copy link
Copy Markdown
Contributor

Description

Depends on #20467, which is the base of this branch. Only the last commit, "Build against a locally built hive Roslyn from its own packages", belongs to this pull request; please review it after #20467 merges.

#20467 teaches the script to read the Roslyn deployed in the target hive. When that Roslyn is a local build (a -dev version), it either matched the minor the repo flows and wrote no override, or stopped and asked for -RoslynVersion. Neither builds against the Roslyn F5 actually runs.

A Roslyn is built locally and deployed into a hive because its API surface differs from the flowed package: it is where an ExternalAccess contract lives before it flows. So a matching minor says nothing about whether the flowed packages will do, and asking for a published version gives up on the one that differs.

With this change:

  • A -dev version always overrides the flowed packages.
  • The override adds that Roslyn build's package folders to RestoreAdditionalProjectSources, because no feed carries its version. This has to go through the props import: Microsoft.FSharp.NetSdk.targets declares the property TreatAsLocalProperty and appends to it, so a command-line value would not survive.
  • -RoslynRepo names the Roslyn repository, defaulting to a roslyn folder next to this one. The script checks that it holds the package for that version and says so, instead of restore failing halfway with NU1101.

Shipped Roslyn versions behave as in #20467.

Checked with -DryRun:

Hive Roslyn found Result
RoslynDev 5.12.5-dev, deployed there override with the Roslyn repo's Shipping and NonShipping package folders
Foo none, so the installed 5.11.0-1.26424.5 override without extra sources
RoslynDev with -RoslynRepo C:\no-such-roslyn 5.12.5-dev stops with "no repository at C:\no-such-roslyn; pass -RoslynRepo"

Tooling only; no compiler or IDE code path changes, so no release notes entry.

Checklist

  • Test cases added
  • Performance benchmarks added in case of performance changes
  • Release notes entry updated

🤖 Generated with Claude Code

xperiandri and others added 3 commits September 11, 2026 17:33
F5 loads the extension into a hive (RootSuffix), and that hive's own
deployed Roslyn wins over the installed VS's when one is present: a
locally built Roslyn deployed there stamps itself 42.42.42.42 and
redirects every reference to itself via a hive-level binding
redirect. The script detected the installed VS's Roslyn version and
overrode the repo's packages to match it unconditionally, which
built against the wrong version whenever the target hive carried its
own Roslyn - RoslynDev in particular, the hive this repo's own DEVGUIDE
points contributors at.

It now reads devenv.isolation.ini to find the hive's own Extensions
folder first, and only falls back to the installed VS's Roslyn when
the hive has none of its own. A new -RootSuffix parameter names the
hive (default RoslynDev, matching the VisualFSharpDebug launch
profile). When the detected version's minor matches what the repo's
Version.Details.props already flows, no override is written at all -
the common case for a hive built from this same source - and a
locally built hive Roslyn with no package version at all now fails
fast asking for one instead of silently building against packages
that do not match what will actually load.

The override file also moves to its own path
(RoslynOverride.start-vs.props) so a build-vs-VisualFSharpSln.ps1 run
in the same session cannot silently overwrite it, since a long-lived
VS process restores against whatever CustomAfterMicrosoftCommonProps
last pointed at.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
-RootSuffix only chose which hive to read the Roslyn version from:
the VSIX projects hard-coded VSRootSuffix to RoslynDev, and that is
both the hive F5 deploys into and the /rootsuffix launchSettings
passes, so -RootSuffix Foo built against Foo's Roslyn and then ran in
RoslynDev.

VSRootSuffix now defaults to RoslynDev only when nothing set it, and
the script hands its suffix to the Visual Studio it launches, whose
builds read it from the environment.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A Roslyn built locally and deployed into the hive is there because
its API surface differs from the flowed package: it is where an
ExternalAccess contract lives before it flows. Matching the minor the
repo flows therefore says nothing about whether the flowed packages
will do, and refusing the dev version left no way to build against
the Roslyn F5 actually runs.

A dev version now always overrides, and the override adds the package
folders of that Roslyn build to RestoreAdditionalProjectSources, since
no feed carries its version. -RoslynRepo names the repository, next to
this one by default, and the script checks that it actually holds the
package before restore fails halfway with NU1101.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

✅ No release notes required

@github-actions github-actions Bot added ⚠️ Affects-Build-Infra Tooling check: PR touches build infrastructure ⚠️ Affects-Restore Tooling check: PR touches NuGet packages or feeds labels Sep 11, 2026
@github-actions

This comment has been minimized.

@github-actions

Copy link
Copy Markdown
Contributor

🔍 Tooling Safety Check — Affects-Build-Infra, Affects-Restore
Affects-Build-Infra: Changes Visual Studio launch and build script behavior.
Affects-Restore: Changes package source and Roslyn package resolution.

Generated by PR Tooling Safety Check · gpt56 3.1M ·

@T-Gro T-Gro left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 🕵️ AI review — verify independently.

# RestoreAdditionalProjectSources has to arrive through the props import rather than on the command
# line: Microsoft.FSharp.NetSdk.targets declares it TreatAsLocalProperty and appends to it.
$sources =
if ($feeds) { "<RestoreAdditionalProjectSources>`$(RestoreAdditionalProjectSources);$($feeds -join ';')</RestoreAdditionalProjectSources>" }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 🕵️ [P2] Repacked Roslyn packages ignored when the -dev version is unchanged: restore succeeds with the previous contents from NuGet's global-packages cache, despite the updated local feed.

.\start-vs-VisualFSharpSln.ps1 -RoslynVersion 5.10.0-dev -RoslynRepo 'C:\src\roslyn'
# Change Roslyn APIs, rebuild and repack as the same 5.10.0-dev version.
.\start-vs-VisualFSharpSln.ps1 -RoslynVersion 5.10.0-dev -RoslynRepo 'C:\src\roslyn'
# Still consumes the first build's packages until their cache entries are removed.

throw "$RootSuffix runs a locally built Roslyn $version, which only its own packages provide, but there is no repository at $repo; pass -RoslynRepo."
}

$feeds = 'Shipping', 'NonShipping' | ForEach-Object { Join-Path $repo "artifacts\packages\Release\$_" }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 🕵️ [P2] Valid relative repository passes validation but nested projects fail restore with NU1301: NuGet resolves the emitted relative sources against each project directory, not the launcher's working directory.

# From the F# checkout, with packed Roslyn packages in the sibling checkout:
.\start-vs-VisualFSharpSln.ps1 -RoslynVersion 5.12.0-dev -RoslynRepo '..\roslyn'

# RestoreAdditionalProjectSources has to arrive through the props import rather than on the command
# line: Microsoft.FSharp.NetSdk.targets declares it TreatAsLocalProperty and appends to it.
$sources =
if ($feeds) { "<RestoreAdditionalProjectSources>`$(RestoreAdditionalProjectSources);$($feeds -join ';')</RestoreAdditionalProjectSources>" }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 🕵️ [P2] & in an existing repository path makes the generated override invalid XML; restore stops with MSB4024 while parsing the unescaped feed path.

.\start-vs-VisualFSharpSln.ps1 -RoslynVersion 5.12.0-dev -RoslynRepo 'C:\src\roslyn&tree'

# less about that than a message here does.
function Get-RoslynDevFeeds([string]$repo, [string]$version) {
if (-not $repo) { $repo = Join-Path (Split-Path $PSScriptRoot) 'roslyn' }
if (-not (Test-Path $repo)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 🕵️ [P2] Existing roslyn[1] repository rejected as nonexistent: both new Test-Path checks interpret brackets as wildcards instead of literal path characters.

.\start-vs-VisualFSharpSln.ps1 -RoslynVersion 5.12.0-dev -RoslynRepo 'C:\src\roslyn[1]'

@T-Gro T-Gro added the AI-reviewed PR reviewed by AI review council label Sep 14, 2026
@T-Gro
T-Gro self-requested a review September 14, 2026 14:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⚠️ Affects-Build-Infra Tooling check: PR touches build infrastructure ⚠️ Affects-Restore Tooling check: PR touches NuGet packages or feeds AI-reviewed PR reviewed by AI review council

Projects

Status: New

Development

Successfully merging this pull request may close these issues.

2 participants