From 6b6fa06c35c4d1d3011d2f7b927c798319cd38a0 Mon Sep 17 00:00:00 2001 From: Copilot Date: Tue, 8 Sep 2026 09:41:34 +0200 Subject: [PATCH 1/6] Add VS Tools>Options>F# Tools>Compiler 'Use the .NET SDK F# compiler for builds' option Adds an HKCU-backed (Software\Microsoft\VisualStudio\FSharp\UseNetSdkCompiler) DialogPage 'FSharpCompilerPropertyPage', on by default (absent/1 = ON, 0 = OFF), registered via ProvideOptionPage. Makes writeHKCU create-or-open the subkey so a fresh VS hive works. Adds resources, page-name id 6015, release note, and persistence unit tests. Implements issue #20484 (VS UI slice). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/release-notes/.VisualStudio/18.vNext.md | 1 + .../LanguageService/LanguageService.fs | 1 + .../src/FSharp.VS.FSI/Properties.resx | 9 +++ .../src/FSharp.VS.FSI/VSPackage.resx | 3 + vsintegration/src/FSharp.VS.FSI/fsiBasis.fs | 5 +- .../src/FSharp.VS.FSI/fsiLanguageService.fs | 22 ++++++ .../src/FSharp.VS.FSI/xlf/Properties.cs.xlf | 15 ++++ .../src/FSharp.VS.FSI/xlf/Properties.de.xlf | 15 ++++ .../src/FSharp.VS.FSI/xlf/Properties.es.xlf | 15 ++++ .../src/FSharp.VS.FSI/xlf/Properties.fr.xlf | 15 ++++ .../src/FSharp.VS.FSI/xlf/Properties.it.xlf | 15 ++++ .../src/FSharp.VS.FSI/xlf/Properties.ja.xlf | 15 ++++ .../src/FSharp.VS.FSI/xlf/Properties.ko.xlf | 15 ++++ .../src/FSharp.VS.FSI/xlf/Properties.pl.xlf | 15 ++++ .../FSharp.VS.FSI/xlf/Properties.pt-BR.xlf | 15 ++++ .../src/FSharp.VS.FSI/xlf/Properties.ru.xlf | 15 ++++ .../src/FSharp.VS.FSI/xlf/Properties.tr.xlf | 15 ++++ .../FSharp.VS.FSI/xlf/Properties.zh-Hans.xlf | 15 ++++ .../FSharp.VS.FSI/xlf/Properties.zh-Hant.xlf | 15 ++++ .../Tests.FSharpCompilerOptionPage.fs | 72 +++++++++++++++++++ .../UnitTests/VisualFSharp.UnitTests.fsproj | 1 + 21 files changed, 308 insertions(+), 1 deletion(-) create mode 100644 vsintegration/tests/UnitTests/Tests.FSharpCompilerOptionPage.fs diff --git a/docs/release-notes/.VisualStudio/18.vNext.md b/docs/release-notes/.VisualStudio/18.vNext.md index ba03f663967..bf7f405521b 100644 --- a/docs/release-notes/.VisualStudio/18.vNext.md +++ b/docs/release-notes/.VisualStudio/18.vNext.md @@ -2,6 +2,7 @@ * Code-fixes for FS3888 (compiler-semantic attribute on the `.fs` but not the `.fsi`): copy the attribute into the `.fsi`, or remove it from the `.fs`. ([Issue #19560](https://github.com/dotnet/fsharp/issues/19560), [PR #19880](https://github.com/dotnet/fsharp/pull/19880)) * Expand `` in IDE tooltips, completion, and signature help, inheriting XML documentation from base classes, interfaces, overridden members, and constructors. ([Issue #19175](https://github.com/dotnet/fsharp/issues/19175), [PR #19188](https://github.com/dotnet/fsharp/pull/19188)) +* Added a **Tools > Options > F# Tools > Compiler** option, **"Use the .NET SDK F# compiler for builds"**, on by default. Visual Studio now builds F# projects with the F# compiler from the installed .NET SDK (matching `dotnet build`); turn the option off to build with the .NET Framework compiler bundled with Visual Studio. Command-line builds are unaffected. ([Issue #20484](https://github.com/dotnet/fsharp/issues/20484)) ### Fixed diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index 427baf0c6ab..ac8ceffd4f9 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -249,6 +249,7 @@ type internal FSharpSettingsFactory [] (settin [] [, "F# Tools", "F# Interactive", 6000s, 6001s, true)>] // true = supports automation +[, "F# Tools", "Compiler", 6000s, 6015s, true)>] [] // <-- resource ID for localised name diff --git a/vsintegration/src/FSharp.VS.FSI/Properties.resx b/vsintegration/src/FSharp.VS.FSI/Properties.resx index 6a0e1964b77..3d1d7960446 100644 --- a/vsintegration/src/FSharp.VS.FSI/Properties.resx +++ b/vsintegration/src/FSharp.VS.FSI/Properties.resx @@ -162,4 +162,13 @@ Enable .NET Core script editing and execution for all F# scripts and the F# Interactive window + + Compiler + + + Use the .NET SDK F# compiler for builds + + + When enabled (the default), Visual Studio builds F# projects with the compiler from the installed .NET SDK, matching command-line 'dotnet build'. Turn it off to build with the .NET Framework F# compiler bundled with Visual Studio - for example when a project uses a type provider that ships only a .NET Framework build. Command-line builds are unaffected. Changing this takes effect on the next (re)build. + \ No newline at end of file diff --git a/vsintegration/src/FSharp.VS.FSI/VSPackage.resx b/vsintegration/src/FSharp.VS.FSI/VSPackage.resx index 0785825ba61..0b955a487a5 100644 --- a/vsintegration/src/FSharp.VS.FSI/VSPackage.resx +++ b/vsintegration/src/FSharp.VS.FSI/VSPackage.resx @@ -122,4 +122,7 @@ J1H0RTAZRQRKDEJ2I3A2PCIAPQQRQIDZI3CTKAIAQPJIE3EPHAMRRKAJCHI1PJM2CPDIRZKMMJHKPHJ9E1HDZHZDC1DEHHR0QIARA8RKA3PICCHAA3QKA1QMJZMKKZKZ + + Compiler + diff --git a/vsintegration/src/FSharp.VS.FSI/fsiBasis.fs b/vsintegration/src/FSharp.VS.FSI/fsiBasis.fs index 752570fa79b..9a634dec45d 100644 --- a/vsintegration/src/FSharp.VS.FSI/fsiBasis.fs +++ b/vsintegration/src/FSharp.VS.FSI/fsiBasis.fs @@ -99,9 +99,12 @@ module internal Util = /// write value to HKCU subkey, uses default .NET/registry type conversions let writeHKCU subkey valueName value = - use key = Registry.CurrentUser.OpenSubKey(subkey, true) + use key = Registry.CurrentUser.CreateSubKey(subkey) key.SetValue(valueName, value) + let fsharpCompilerRegSubKey = @"Software\Microsoft\VisualStudio\FSharp" + let useNetSdkCompilerRegValue = "UseNetSdkCompiler" // DWORD; absent/1 = SDK (ON), 0 = .NET Framework + module ArgParsing = let private lastIndexOfPattern s patt = let patt' = sprintf @"(\s|^)%s(\s|$)" patt diff --git a/vsintegration/src/FSharp.VS.FSI/fsiLanguageService.fs b/vsintegration/src/FSharp.VS.FSI/fsiLanguageService.fs index 6df01e0d2a0..ae1c8175e27 100644 --- a/vsintegration/src/FSharp.VS.FSI/fsiLanguageService.fs +++ b/vsintegration/src/FSharp.VS.FSI/fsiLanguageService.fs @@ -61,6 +61,28 @@ type FsiPropertyPage() = [] member this.FsiPreview with get() = SessionsProperties.fsiPreview and set (x:bool) = SessionsProperties.fsiPreview <- x +// FSharpCompilerPropertyPage - Tools > Options > F# Tools > Compiler +[] +[] +[] +[] +type FSharpCompilerPropertyPage() = + inherit DialogPage() + + // Registry (HKCU) is the single source of truth shared with the MSBuild build process, + // so disable the base DialogPage storage to avoid spurious writes on VS start. + override _.LoadSettingsFromStorage() = () + override _.SaveSettingsToStorage() = () + + [] + [] + [] + member _.UseNetSdkCompiler + with get () = + RegistryHelpers.tryReadHKCU fsharpCompilerRegSubKey useNetSdkCompilerRegValue <> Some 0 + and set v = + RegistryHelpers.writeHKCU fsharpCompilerRegSubKey useNetSdkCompilerRegValue (if v then 1 else 0) + // CompletionSet type internal FsiCompletionSet(imageList,source:Source) = inherit CompletionSet(imageList, source) diff --git a/vsintegration/src/FSharp.VS.FSI/xlf/Properties.cs.xlf b/vsintegration/src/FSharp.VS.FSI/xlf/Properties.cs.xlf index 41bcdf4ead2..e97b60c2e13 100644 --- a/vsintegration/src/FSharp.VS.FSI/xlf/Properties.cs.xlf +++ b/vsintegration/src/FSharp.VS.FSI/xlf/Properties.cs.xlf @@ -2,6 +2,21 @@ + + Compiler + Compiler + + + + Use the .NET SDK F# compiler for builds + Use the .NET SDK F# compiler for builds + + + + When enabled (the default), Visual Studio builds F# projects with the compiler from the installed .NET SDK, matching command-line 'dotnet build'. Turn it off to build with the .NET Framework F# compiler bundled with Visual Studio - for example when a project uses a type provider that ships only a .NET Framework build. Command-line builds are unaffected. Changing this takes effect on the next (re)build. + When enabled (the default), Visual Studio builds F# projects with the compiler from the installed .NET SDK, matching command-line 'dotnet build'. Turn it off to build with the .NET Framework F# compiler bundled with Visual Studio - for example when a project uses a type provider that ships only a .NET Framework build. Command-line builds are unaffected. Changing this takes effect on the next (re)build. + + Use fsiAnyCpu.exe Použít fsiAnyCpu.exe diff --git a/vsintegration/src/FSharp.VS.FSI/xlf/Properties.de.xlf b/vsintegration/src/FSharp.VS.FSI/xlf/Properties.de.xlf index d4d63a38c81..bda0ae1842e 100644 --- a/vsintegration/src/FSharp.VS.FSI/xlf/Properties.de.xlf +++ b/vsintegration/src/FSharp.VS.FSI/xlf/Properties.de.xlf @@ -2,6 +2,21 @@ + + Compiler + Compiler + + + + Use the .NET SDK F# compiler for builds + Use the .NET SDK F# compiler for builds + + + + When enabled (the default), Visual Studio builds F# projects with the compiler from the installed .NET SDK, matching command-line 'dotnet build'. Turn it off to build with the .NET Framework F# compiler bundled with Visual Studio - for example when a project uses a type provider that ships only a .NET Framework build. Command-line builds are unaffected. Changing this takes effect on the next (re)build. + When enabled (the default), Visual Studio builds F# projects with the compiler from the installed .NET SDK, matching command-line 'dotnet build'. Turn it off to build with the .NET Framework F# compiler bundled with Visual Studio - for example when a project uses a type provider that ships only a .NET Framework build. Command-line builds are unaffected. Changing this takes effect on the next (re)build. + + Use fsiAnyCpu.exe „fsiAnyCpu.exe“ verwenden diff --git a/vsintegration/src/FSharp.VS.FSI/xlf/Properties.es.xlf b/vsintegration/src/FSharp.VS.FSI/xlf/Properties.es.xlf index 24dd9544453..3da5557f060 100644 --- a/vsintegration/src/FSharp.VS.FSI/xlf/Properties.es.xlf +++ b/vsintegration/src/FSharp.VS.FSI/xlf/Properties.es.xlf @@ -2,6 +2,21 @@ + + Compiler + Compiler + + + + Use the .NET SDK F# compiler for builds + Use the .NET SDK F# compiler for builds + + + + When enabled (the default), Visual Studio builds F# projects with the compiler from the installed .NET SDK, matching command-line 'dotnet build'. Turn it off to build with the .NET Framework F# compiler bundled with Visual Studio - for example when a project uses a type provider that ships only a .NET Framework build. Command-line builds are unaffected. Changing this takes effect on the next (re)build. + When enabled (the default), Visual Studio builds F# projects with the compiler from the installed .NET SDK, matching command-line 'dotnet build'. Turn it off to build with the .NET Framework F# compiler bundled with Visual Studio - for example when a project uses a type provider that ships only a .NET Framework build. Command-line builds are unaffected. Changing this takes effect on the next (re)build. + + Use fsiAnyCpu.exe Use fsiAnyCpu.exe diff --git a/vsintegration/src/FSharp.VS.FSI/xlf/Properties.fr.xlf b/vsintegration/src/FSharp.VS.FSI/xlf/Properties.fr.xlf index b75b10492c8..b174b13e492 100644 --- a/vsintegration/src/FSharp.VS.FSI/xlf/Properties.fr.xlf +++ b/vsintegration/src/FSharp.VS.FSI/xlf/Properties.fr.xlf @@ -2,6 +2,21 @@ + + Compiler + Compiler + + + + Use the .NET SDK F# compiler for builds + Use the .NET SDK F# compiler for builds + + + + When enabled (the default), Visual Studio builds F# projects with the compiler from the installed .NET SDK, matching command-line 'dotnet build'. Turn it off to build with the .NET Framework F# compiler bundled with Visual Studio - for example when a project uses a type provider that ships only a .NET Framework build. Command-line builds are unaffected. Changing this takes effect on the next (re)build. + When enabled (the default), Visual Studio builds F# projects with the compiler from the installed .NET SDK, matching command-line 'dotnet build'. Turn it off to build with the .NET Framework F# compiler bundled with Visual Studio - for example when a project uses a type provider that ships only a .NET Framework build. Command-line builds are unaffected. Changing this takes effect on the next (re)build. + + Use fsiAnyCpu.exe Use fsiAnyCpu.exe diff --git a/vsintegration/src/FSharp.VS.FSI/xlf/Properties.it.xlf b/vsintegration/src/FSharp.VS.FSI/xlf/Properties.it.xlf index 2106a068776..c673a78b989 100644 --- a/vsintegration/src/FSharp.VS.FSI/xlf/Properties.it.xlf +++ b/vsintegration/src/FSharp.VS.FSI/xlf/Properties.it.xlf @@ -2,6 +2,21 @@ + + Compiler + Compiler + + + + Use the .NET SDK F# compiler for builds + Use the .NET SDK F# compiler for builds + + + + When enabled (the default), Visual Studio builds F# projects with the compiler from the installed .NET SDK, matching command-line 'dotnet build'. Turn it off to build with the .NET Framework F# compiler bundled with Visual Studio - for example when a project uses a type provider that ships only a .NET Framework build. Command-line builds are unaffected. Changing this takes effect on the next (re)build. + When enabled (the default), Visual Studio builds F# projects with the compiler from the installed .NET SDK, matching command-line 'dotnet build'. Turn it off to build with the .NET Framework F# compiler bundled with Visual Studio - for example when a project uses a type provider that ships only a .NET Framework build. Command-line builds are unaffected. Changing this takes effect on the next (re)build. + + Use fsiAnyCpu.exe Usare fsiAnyCpu.exe diff --git a/vsintegration/src/FSharp.VS.FSI/xlf/Properties.ja.xlf b/vsintegration/src/FSharp.VS.FSI/xlf/Properties.ja.xlf index aa7c8682d02..b320ee7a67c 100644 --- a/vsintegration/src/FSharp.VS.FSI/xlf/Properties.ja.xlf +++ b/vsintegration/src/FSharp.VS.FSI/xlf/Properties.ja.xlf @@ -2,6 +2,21 @@ + + Compiler + Compiler + + + + Use the .NET SDK F# compiler for builds + Use the .NET SDK F# compiler for builds + + + + When enabled (the default), Visual Studio builds F# projects with the compiler from the installed .NET SDK, matching command-line 'dotnet build'. Turn it off to build with the .NET Framework F# compiler bundled with Visual Studio - for example when a project uses a type provider that ships only a .NET Framework build. Command-line builds are unaffected. Changing this takes effect on the next (re)build. + When enabled (the default), Visual Studio builds F# projects with the compiler from the installed .NET SDK, matching command-line 'dotnet build'. Turn it off to build with the .NET Framework F# compiler bundled with Visual Studio - for example when a project uses a type provider that ships only a .NET Framework build. Command-line builds are unaffected. Changing this takes effect on the next (re)build. + + Use fsiAnyCpu.exe fsiAnyCpu.exe を使用する diff --git a/vsintegration/src/FSharp.VS.FSI/xlf/Properties.ko.xlf b/vsintegration/src/FSharp.VS.FSI/xlf/Properties.ko.xlf index d7a6fd30b44..ef7d076db13 100644 --- a/vsintegration/src/FSharp.VS.FSI/xlf/Properties.ko.xlf +++ b/vsintegration/src/FSharp.VS.FSI/xlf/Properties.ko.xlf @@ -2,6 +2,21 @@ + + Compiler + Compiler + + + + Use the .NET SDK F# compiler for builds + Use the .NET SDK F# compiler for builds + + + + When enabled (the default), Visual Studio builds F# projects with the compiler from the installed .NET SDK, matching command-line 'dotnet build'. Turn it off to build with the .NET Framework F# compiler bundled with Visual Studio - for example when a project uses a type provider that ships only a .NET Framework build. Command-line builds are unaffected. Changing this takes effect on the next (re)build. + When enabled (the default), Visual Studio builds F# projects with the compiler from the installed .NET SDK, matching command-line 'dotnet build'. Turn it off to build with the .NET Framework F# compiler bundled with Visual Studio - for example when a project uses a type provider that ships only a .NET Framework build. Command-line builds are unaffected. Changing this takes effect on the next (re)build. + + Use fsiAnyCpu.exe fsiAnyCpu.exe 사용 diff --git a/vsintegration/src/FSharp.VS.FSI/xlf/Properties.pl.xlf b/vsintegration/src/FSharp.VS.FSI/xlf/Properties.pl.xlf index f5843a5554d..cf9d4f03794 100644 --- a/vsintegration/src/FSharp.VS.FSI/xlf/Properties.pl.xlf +++ b/vsintegration/src/FSharp.VS.FSI/xlf/Properties.pl.xlf @@ -2,6 +2,21 @@ + + Compiler + Compiler + + + + Use the .NET SDK F# compiler for builds + Use the .NET SDK F# compiler for builds + + + + When enabled (the default), Visual Studio builds F# projects with the compiler from the installed .NET SDK, matching command-line 'dotnet build'. Turn it off to build with the .NET Framework F# compiler bundled with Visual Studio - for example when a project uses a type provider that ships only a .NET Framework build. Command-line builds are unaffected. Changing this takes effect on the next (re)build. + When enabled (the default), Visual Studio builds F# projects with the compiler from the installed .NET SDK, matching command-line 'dotnet build'. Turn it off to build with the .NET Framework F# compiler bundled with Visual Studio - for example when a project uses a type provider that ships only a .NET Framework build. Command-line builds are unaffected. Changing this takes effect on the next (re)build. + + Use fsiAnyCpu.exe Użyj fsiAnyCpu.exe diff --git a/vsintegration/src/FSharp.VS.FSI/xlf/Properties.pt-BR.xlf b/vsintegration/src/FSharp.VS.FSI/xlf/Properties.pt-BR.xlf index 1f0be866e4b..8d15744b542 100644 --- a/vsintegration/src/FSharp.VS.FSI/xlf/Properties.pt-BR.xlf +++ b/vsintegration/src/FSharp.VS.FSI/xlf/Properties.pt-BR.xlf @@ -2,6 +2,21 @@ + + Compiler + Compiler + + + + Use the .NET SDK F# compiler for builds + Use the .NET SDK F# compiler for builds + + + + When enabled (the default), Visual Studio builds F# projects with the compiler from the installed .NET SDK, matching command-line 'dotnet build'. Turn it off to build with the .NET Framework F# compiler bundled with Visual Studio - for example when a project uses a type provider that ships only a .NET Framework build. Command-line builds are unaffected. Changing this takes effect on the next (re)build. + When enabled (the default), Visual Studio builds F# projects with the compiler from the installed .NET SDK, matching command-line 'dotnet build'. Turn it off to build with the .NET Framework F# compiler bundled with Visual Studio - for example when a project uses a type provider that ships only a .NET Framework build. Command-line builds are unaffected. Changing this takes effect on the next (re)build. + + Use fsiAnyCpu.exe Usar fsiAnyCpu.exe diff --git a/vsintegration/src/FSharp.VS.FSI/xlf/Properties.ru.xlf b/vsintegration/src/FSharp.VS.FSI/xlf/Properties.ru.xlf index 555b2827511..9e6d791f25c 100644 --- a/vsintegration/src/FSharp.VS.FSI/xlf/Properties.ru.xlf +++ b/vsintegration/src/FSharp.VS.FSI/xlf/Properties.ru.xlf @@ -2,6 +2,21 @@ + + Compiler + Compiler + + + + Use the .NET SDK F# compiler for builds + Use the .NET SDK F# compiler for builds + + + + When enabled (the default), Visual Studio builds F# projects with the compiler from the installed .NET SDK, matching command-line 'dotnet build'. Turn it off to build with the .NET Framework F# compiler bundled with Visual Studio - for example when a project uses a type provider that ships only a .NET Framework build. Command-line builds are unaffected. Changing this takes effect on the next (re)build. + When enabled (the default), Visual Studio builds F# projects with the compiler from the installed .NET SDK, matching command-line 'dotnet build'. Turn it off to build with the .NET Framework F# compiler bundled with Visual Studio - for example when a project uses a type provider that ships only a .NET Framework build. Command-line builds are unaffected. Changing this takes effect on the next (re)build. + + Use fsiAnyCpu.exe Использовать fsiAnyCpu.exe diff --git a/vsintegration/src/FSharp.VS.FSI/xlf/Properties.tr.xlf b/vsintegration/src/FSharp.VS.FSI/xlf/Properties.tr.xlf index 8ea48ad6885..7539f80b31e 100644 --- a/vsintegration/src/FSharp.VS.FSI/xlf/Properties.tr.xlf +++ b/vsintegration/src/FSharp.VS.FSI/xlf/Properties.tr.xlf @@ -2,6 +2,21 @@ + + Compiler + Compiler + + + + Use the .NET SDK F# compiler for builds + Use the .NET SDK F# compiler for builds + + + + When enabled (the default), Visual Studio builds F# projects with the compiler from the installed .NET SDK, matching command-line 'dotnet build'. Turn it off to build with the .NET Framework F# compiler bundled with Visual Studio - for example when a project uses a type provider that ships only a .NET Framework build. Command-line builds are unaffected. Changing this takes effect on the next (re)build. + When enabled (the default), Visual Studio builds F# projects with the compiler from the installed .NET SDK, matching command-line 'dotnet build'. Turn it off to build with the .NET Framework F# compiler bundled with Visual Studio - for example when a project uses a type provider that ships only a .NET Framework build. Command-line builds are unaffected. Changing this takes effect on the next (re)build. + + Use fsiAnyCpu.exe fsiAnyCpu.exe dosyasını kullanın diff --git a/vsintegration/src/FSharp.VS.FSI/xlf/Properties.zh-Hans.xlf b/vsintegration/src/FSharp.VS.FSI/xlf/Properties.zh-Hans.xlf index 236c117d8a0..bc83b525574 100644 --- a/vsintegration/src/FSharp.VS.FSI/xlf/Properties.zh-Hans.xlf +++ b/vsintegration/src/FSharp.VS.FSI/xlf/Properties.zh-Hans.xlf @@ -2,6 +2,21 @@ + + Compiler + Compiler + + + + Use the .NET SDK F# compiler for builds + Use the .NET SDK F# compiler for builds + + + + When enabled (the default), Visual Studio builds F# projects with the compiler from the installed .NET SDK, matching command-line 'dotnet build'. Turn it off to build with the .NET Framework F# compiler bundled with Visual Studio - for example when a project uses a type provider that ships only a .NET Framework build. Command-line builds are unaffected. Changing this takes effect on the next (re)build. + When enabled (the default), Visual Studio builds F# projects with the compiler from the installed .NET SDK, matching command-line 'dotnet build'. Turn it off to build with the .NET Framework F# compiler bundled with Visual Studio - for example when a project uses a type provider that ships only a .NET Framework build. Command-line builds are unaffected. Changing this takes effect on the next (re)build. + + Use fsiAnyCpu.exe Use fsiAnyCpu.exe diff --git a/vsintegration/src/FSharp.VS.FSI/xlf/Properties.zh-Hant.xlf b/vsintegration/src/FSharp.VS.FSI/xlf/Properties.zh-Hant.xlf index 4c505ccb010..d27171c6597 100644 --- a/vsintegration/src/FSharp.VS.FSI/xlf/Properties.zh-Hant.xlf +++ b/vsintegration/src/FSharp.VS.FSI/xlf/Properties.zh-Hant.xlf @@ -2,6 +2,21 @@ + + Compiler + Compiler + + + + Use the .NET SDK F# compiler for builds + Use the .NET SDK F# compiler for builds + + + + When enabled (the default), Visual Studio builds F# projects with the compiler from the installed .NET SDK, matching command-line 'dotnet build'. Turn it off to build with the .NET Framework F# compiler bundled with Visual Studio - for example when a project uses a type provider that ships only a .NET Framework build. Command-line builds are unaffected. Changing this takes effect on the next (re)build. + When enabled (the default), Visual Studio builds F# projects with the compiler from the installed .NET SDK, matching command-line 'dotnet build'. Turn it off to build with the .NET Framework F# compiler bundled with Visual Studio - for example when a project uses a type provider that ships only a .NET Framework build. Command-line builds are unaffected. Changing this takes effect on the next (re)build. + + Use fsiAnyCpu.exe 使用 fsiAnyCpu.exe diff --git a/vsintegration/tests/UnitTests/Tests.FSharpCompilerOptionPage.fs b/vsintegration/tests/UnitTests/Tests.FSharpCompilerOptionPage.fs new file mode 100644 index 00000000000..c951f2b9576 --- /dev/null +++ b/vsintegration/tests/UnitTests/Tests.FSharpCompilerOptionPage.fs @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace Tests + +open System +open System.Runtime.Serialization +open Xunit +open Microsoft.Win32 +open Microsoft.VisualStudio.FSharp.Interactive + +module FSharpCompilerOptionPageTests = + + let private subKey = @"Software\Microsoft\VisualStudio\FSharp" + let private valueName = "UseNetSdkCompiler" + + // The base DialogPage constructor needs live VS services (JoinableTaskContext), which are + // unavailable in a headless unit-test host. The UseNetSdkCompiler get/set only touch HKCU and + // never read base/DialogPage state, so bypass the constructor to exercise the real property. + let private newPage () = + FormatterServices.GetUninitializedObject(typeof) :?> FSharpCompilerPropertyPage + + // Save/restore the real HKCU value so the test never corrupts a developer's setting. + let private withCleanRegistry (body: unit -> unit) = + let original = + use k = Registry.CurrentUser.OpenSubKey(subKey) + if isNull k then None else Option.ofObj (k.GetValue(valueName)) + // start from "absent" + (use k = Registry.CurrentUser.OpenSubKey(subKey, true) + if not (isNull k) then k.DeleteValue(valueName, false)) + try + body () + finally + use k = Registry.CurrentUser.CreateSubKey(subKey) + match original with + | Some v -> k.SetValue(valueName, v) + | None -> k.DeleteValue(valueName, false) + + let private readRaw () = + use k = Registry.CurrentUser.OpenSubKey(subKey) + if isNull k then None else Option.ofObj (k.GetValue(valueName)) + + [] + let ``Default is ON when no registry value is present`` () = + withCleanRegistry (fun () -> + let page = newPage () + Assert.True(page.UseNetSdkCompiler) + // merely constructing + getting must not write anything + Assert.Equal(None, readRaw () |> Option.map (fun o -> unbox o))) + + [] + let ``Setting false writes DWORD 0 and get returns false`` () = + withCleanRegistry (fun () -> + let page = newPage () + page.UseNetSdkCompiler <- false + Assert.Equal(Some 0, readRaw () |> Option.map (fun o -> unbox o)) + Assert.False(page.UseNetSdkCompiler)) + + [] + let ``Setting true writes DWORD 1 and get returns true`` () = + withCleanRegistry (fun () -> + let page = newPage () + page.UseNetSdkCompiler <- true + Assert.Equal(Some 1, readRaw () |> Option.map (fun o -> unbox o)) + Assert.True(page.UseNetSdkCompiler)) + + [] + let ``A get-only cycle does not write to the registry`` () = + withCleanRegistry (fun () -> + let page = newPage () + page.UseNetSdkCompiler |> ignore + page.UseNetSdkCompiler |> ignore + Assert.True(readRaw () |> Option.isNone)) diff --git a/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj b/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj index 7e5640241fd..ef2a0bb4872 100644 --- a/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj +++ b/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj @@ -36,6 +36,7 @@ + From e3288203ad5f377d01c66a57010ec3460f912295 Mon Sep 17 00:00:00 2001 From: Copilot Date: Tue, 8 Sep 2026 10:06:12 +0200 Subject: [PATCH 2/6] Flip VS build default to .NET SDK F# compiler (registry-driven) Microsoft.FSharp.Targets no longer hard-defaults FSharpPreferNetFrameworkTools to true for VS builds. It now reads HKCU\Software\Microsoft\VisualStudio\FSharp\UseNetSdkCompiler and defaults to false (SDK compiler) when unset; explicit project values still win, registry value 0 forces the .NET Framework compiler. CLI (dotnet build) builds are unaffected (shim absent). Updates the stale comment block, the 'WhichFSharpCompiler - Default Settings.proj' baseline to the SDK selection, and adds RegistryCompilerSelection.Tests.ps1 (5-case matrix). RED-first confirmed: Cases 1 and 3 fail against the un-patched targets; all 5 pass after the change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/FSharp.Build/Microsoft.FSharp.Targets | 12 +- .../RegistryCompilerSelection.Tests.ps1 | 112 ++++++++++++++++++ ...hichFSharpCompiler - Default Settings.proj | 6 +- 3 files changed, 124 insertions(+), 6 deletions(-) create mode 100644 tests/fsharp/SDKTests/RegistryCompilerSelection.Tests.ps1 diff --git a/src/FSharp.Build/Microsoft.FSharp.Targets b/src/FSharp.Build/Microsoft.FSharp.Targets index a225e8861e8..d3522b290ec 100644 --- a/src/FSharp.Build/Microsoft.FSharp.Targets +++ b/src/FSharp.Build/Microsoft.FSharp.Targets @@ -54,7 +54,11 @@ this file. Project file properties that control compiler selection: ======================================================== Suggest that the compiler used be the desktop framework version. On computers without Visual Studio these properties is ignored. - boolean: true or false === default value true + boolean: true or false === default value false (use the .NET SDK F# compiler) + When unset, Visual Studio builds default to the .NET SDK compiler; the .NET Framework compiler is used + only if this property is set to true in the project, or if the Tools > Options > F# Tools > Compiler + option "Use the .NET SDK F# compiler for builds" is turned off (HKCU\Software\Microsoft\VisualStudio\FSharp, + DWORD UseNetSdkCompiler = 0). Command-line (dotnet build) builds are unaffected. Suggest that the compiler used be the 64 Bit compiler. On computers without Visual Studio this property is ignored. boolean: true or false === default value true @@ -66,8 +70,10 @@ this file. On Windows Arm64 default to Arm64 build, otherwise default to AnyCpu. --> - - true + + <_FSharpUseNetSdkCompilerVsOption>$([MSBuild]::GetRegistryValueFromView('HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\FSharp', 'UseNetSdkCompiler', null, RegistryView.Default)) + true + false diff --git a/tests/fsharp/SDKTests/RegistryCompilerSelection.Tests.ps1 b/tests/fsharp/SDKTests/RegistryCompilerSelection.Tests.ps1 new file mode 100644 index 00000000000..d1a493113ba --- /dev/null +++ b/tests/fsharp/SDKTests/RegistryCompilerSelection.Tests.ps1 @@ -0,0 +1,112 @@ +#Requires -Version 5 +<# + Verifies src/FSharp.Build/Microsoft.FSharp.Targets selects the F# compiler correctly based on the + HKCU\Software\Microsoft\VisualStudio\FSharp\UseNetSdkCompiler value and the FSharp_Shim_Present gate. + Windows-only (registry + VS shim). Evaluates the SOURCE targets via `dotnet msbuild -getProperty` + from a temp directory OUTSIDE the repo so the repo's global.json (which pins an SDK that may not be + installed) does not apply. +#> +[CmdletBinding()] +param( + [string] $RepoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..\..")).Path, + [string] $DotNet = "dotnet" +) + +$ErrorActionPreference = "Stop" + +$shim = Join-Path $RepoRoot "vsintegration\shims\Microsoft.FSharp.ShimHelpers.props" +$targets = Join-Path $RepoRoot "src\FSharp.Build\Microsoft.FSharp.Targets" +if (-not (Test-Path $shim)) { throw "Shim not found: $shim" } +if (-not (Test-Path $targets)) { throw "Targets not found: $targets" } + +$work = Join-Path ([System.IO.Path]::GetTempPath()) ("fsc_regsel_" + [guid]::NewGuid().ToString("N")) +New-Item -ItemType Directory -Force -Path $work | Out-Null +$probe = Join-Path $work "probe.proj" +@" + + + F# + Debug + + + + + +"@ | Set-Content -Path $probe -Encoding UTF8 + +$regKey = "HKCU\Software\Microsoft\VisualStudio\FSharp" +$regVal = "UseNetSdkCompiler" + +# Save & later restore any pre-existing value so we never corrupt a developer's setting. +$saved = (reg query $regKey /v $regVal 2>$null | Select-String $regVal) + +function Clear-Reg { reg delete $regKey /v $regVal /f 2>$null | Out-Null } +function Set-Reg([int]$v) { reg add $regKey /v $regVal /t REG_DWORD /d $v /f | Out-Null } + +function Get-Props([string[]]$extra) { + Push-Location $work + try { + $args = @("msbuild", "probe.proj", + "-getProperty:FSharp_Shim_Present", + "-getProperty:FSharpPreferNetFrameworkTools", + "-getProperty:FscToolExe", + "-getProperty:DotnetFscCompilerPath") + $extra + $json = & $DotNet @args 2>&1 | Out-String + return ($json | ConvertFrom-Json).Properties + } finally { Pop-Location } +} + +$failures = New-Object System.Collections.Generic.List[string] +function Check($name, $cond, $detail) { + if ($cond) { Write-Host "PASS: $name" -ForegroundColor Green } + else { Write-Host "FAIL: $name -- $detail" -ForegroundColor Red; $failures.Add($name) } +} + +try { + # Case 1: no registry value -> SDK selected (default flipped) + Clear-Reg + $p = Get-Props @() + Check "Case1 no-registry -> SDK" ` + ($p.FSharpPreferNetFrameworkTools -eq 'false' -and $p.FscToolExe -eq 'dotnet.exe' -and $p.DotnetFscCompilerPath -match 'fsc\.dll') ` + ("got: $($p | ConvertTo-Json -Compress)") + + # Case 2: registry 0 -> .NET Framework selected + Set-Reg 0 + $p = Get-Props @() + Check "Case2 registry=0 -> .NET Framework" ` + ($p.FSharpPreferNetFrameworkTools -eq 'true' -and $p.FscToolExe -match '^fsc.*\.exe$' -and $p.DotnetFscCompilerPath -eq '') ` + ("got: $($p | ConvertTo-Json -Compress)") + + # Case 3: registry 1 -> SDK selected + Set-Reg 1 + $p = Get-Props @() + Check "Case3 registry=1 -> SDK" ` + ($p.FSharpPreferNetFrameworkTools -eq 'false' -and $p.FscToolExe -eq 'dotnet.exe' -and $p.DotnetFscCompilerPath -match 'fsc\.dll') ` + ("got: $($p | ConvertTo-Json -Compress)") + + # Case 4: explicit project value wins over registry 1 + Set-Reg 1 + $p = Get-Props @("-p:FSharpPreferNetFrameworkTools=true") + Check "Case4 explicit=true beats registry=1 -> .NET Framework" ` + ($p.FSharpPreferNetFrameworkTools -eq 'true' -and $p.FscToolExe -match '^fsc.*\.exe$' -and $p.DotnetFscCompilerPath -eq '') ` + ("got: $($p | ConvertTo-Json -Compress)") + + # Case 5 (NEGATIVE): CLI build (shim absent) -> selection blocks skipped, unaffected + Set-Reg 0 + $p = Get-Props @("-p:FSharpCompilerPath=/Explicit/") + Check "Case5 CLI (shim absent) -> unaffected (no fsc selection)" ` + ($p.FSharp_Shim_Present -eq '' -and $p.FscToolExe -eq '' -and $p.DotnetFscCompilerPath -eq '') ` + ("got: $($p | ConvertTo-Json -Compress)") +} +finally { + # restore registry + if ($saved) { + $v = ($saved -split '\s+')[-1] + if ($v -match '^0x') { $v = [Convert]::ToInt32($v,16) } + Set-Reg ([int]$v) + } else { Clear-Reg } + Remove-Item -Recurse -Force $work -ErrorAction SilentlyContinue +} + +if ($failures.Count -gt 0) { Write-Error "Registry compiler-selection matrix FAILED: $($failures -join ', ')"; exit 1 } +Write-Host "All registry compiler-selection cases passed." -ForegroundColor Green diff --git a/tests/fsharp/SDKTests/tests/WhichFSharpCompiler - Default Settings.proj b/tests/fsharp/SDKTests/tests/WhichFSharpCompiler - Default Settings.proj index de865ff9f57..c8daf91d062 100644 --- a/tests/fsharp/SDKTests/tests/WhichFSharpCompiler - Default Settings.proj +++ b/tests/fsharp/SDKTests/tests/WhichFSharpCompiler - Default Settings.proj @@ -9,9 +9,9 @@ true /Common7/IDE/CommonExtensions/Microsoft/FSharp/Tools/ - fscAnyCpu.exe - _VsInstallRoot_/Common7/IDE/CommonExtensions/Microsoft/FSharp/Tools/ - + dotnet.exe + /_NetCoreRoot_/ + /FSharp/fsc.dll From 04b46abec54ed46d6076710ddb62aed0c9439d93 Mon Sep 17 00:00:00 2001 From: Copilot Date: Tue, 8 Sep 2026 11:02:30 +0200 Subject: [PATCH 3/6] Fix Compiler option-page node title resource to resolve from the package satellite The Tools>Options>F# Tools>Compiler option page is registered via [] on FSharpPackage (FSharp.Editor). VS resolves those node-title resource IDs from that package's UI satellite, which is FSharp.ProjectSystem.FSharp/VSPackage.resx - the same place the sibling "F# Interactive" page's 6000/6001 live. The new 6015="Compiler" string had been added to FSharp.VS.FSI/VSPackage.resx (the FSI assembly's own satellite), where VS would never find it, leaving the Compiler node title blank/unlocalized. Adding it to the FSI resx without matching xlf also left that resx/xlf pair out of sync. Move 6015="Compiler" to FSharp.ProjectSystem.FSharp/VSPackage.resx (next to 6000/6001) and add the corresponding trans-units to all 13 VSPackage.*.xlf files; drop the stray entry from FSharp.VS.FSI/VSPackage.resx, restoring that pair's xlf sync. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- vsintegration/src/FSharp.ProjectSystem.FSharp/VSPackage.resx | 3 +++ .../src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.cs.xlf | 5 +++++ .../src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.de.xlf | 5 +++++ .../src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.es.xlf | 5 +++++ .../src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.fr.xlf | 5 +++++ .../src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.it.xlf | 5 +++++ .../src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.ja.xlf | 5 +++++ .../src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.ko.xlf | 5 +++++ .../src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.pl.xlf | 5 +++++ .../src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.pt-BR.xlf | 5 +++++ .../src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.ru.xlf | 5 +++++ .../src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.tr.xlf | 5 +++++ .../FSharp.ProjectSystem.FSharp/xlf/VSPackage.zh-Hans.xlf | 5 +++++ .../FSharp.ProjectSystem.FSharp/xlf/VSPackage.zh-Hant.xlf | 5 +++++ vsintegration/src/FSharp.VS.FSI/VSPackage.resx | 3 --- 15 files changed, 68 insertions(+), 3 deletions(-) diff --git a/vsintegration/src/FSharp.ProjectSystem.FSharp/VSPackage.resx b/vsintegration/src/FSharp.ProjectSystem.FSharp/VSPackage.resx index 8cc0c1c625e..0896f870300 100644 --- a/vsintegration/src/FSharp.ProjectSystem.FSharp/VSPackage.resx +++ b/vsintegration/src/FSharp.ProjectSystem.FSharp/VSPackage.resx @@ -457,6 +457,9 @@ Visual F# Files (*.fs,*.fsi,*.fsx,*.fsscript);*.fs,*.fsi,*.fsx,*.fsscript + + Compiler + Visual F# diff --git a/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.cs.xlf b/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.cs.xlf index a6804b56c87..f0df18c1080 100644 --- a/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.cs.xlf +++ b/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.cs.xlf @@ -422,6 +422,11 @@ Soubory Visual F# (*.fs, *.fsi, *.fsx, *.fsscript); *.fs, *.fsi, *.fsx,*.fsscript + + Compiler + Compiler + + Visual F# Visual F# diff --git a/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.de.xlf b/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.de.xlf index d1773499751..72c4f8ae619 100644 --- a/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.de.xlf +++ b/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.de.xlf @@ -422,6 +422,11 @@ Visual F#-Dateien (*.fs,*.fsi,*.fsx,*.fsscript);*.fs,*.fsi,*.fsx,*.fsscript + + Compiler + Compiler + + Visual F# Visual F# diff --git a/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.es.xlf b/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.es.xlf index 22c129e7c91..08cb1bb812b 100644 --- a/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.es.xlf +++ b/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.es.xlf @@ -422,6 +422,11 @@ Archivos de Visual F# (*.fs,*.fsi,*.fsx,*.fsscript);*.fs,*.fsi,*.fsx,*.fsscript + + Compiler + Compiler + + Visual F# Visual F# diff --git a/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.fr.xlf b/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.fr.xlf index 13f26a3372c..1dd067f35bf 100644 --- a/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.fr.xlf +++ b/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.fr.xlf @@ -422,6 +422,11 @@ Fichiers Visual F# (*.fs,*.fsi,*.fsx,*.fsscript);*.fs,*.fsi,*.fsx,*.fsscript + + Compiler + Compiler + + Visual F# Visual F# diff --git a/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.it.xlf b/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.it.xlf index 8ab09f312bd..a7db57a6dc3 100644 --- a/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.it.xlf +++ b/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.it.xlf @@ -422,6 +422,11 @@ File Visual F# (*.fs,*.fsi,*.fsx,*.fsscript);*.fs,*.fsi,*.fsx,*.fsscript + + Compiler + Compiler + + Visual F# Visual F# diff --git a/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.ja.xlf b/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.ja.xlf index 28da7334851..6fe3598ee58 100644 --- a/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.ja.xlf +++ b/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.ja.xlf @@ -422,6 +422,11 @@ Visual F# ファイル (*.fs,*.fsi,*.fsx,*.fsscript);*.fs,*.fsi,*.fsx,*.fsscript + + Compiler + Compiler + + Visual F# Visual F# diff --git a/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.ko.xlf b/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.ko.xlf index fb1457315e4..f125d1c3ba4 100644 --- a/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.ko.xlf +++ b/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.ko.xlf @@ -422,6 +422,11 @@ Visual F# 파일 (*.fs,*.fsi,*.fsx,*.fsscript);*.fs,*.fsi,*.fsx,*.fsscript + + Compiler + Compiler + + Visual F# Visual F# diff --git a/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.pl.xlf b/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.pl.xlf index 031acb41295..441e09f6de7 100644 --- a/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.pl.xlf +++ b/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.pl.xlf @@ -422,6 +422,11 @@ Pliki języka Visual F# (*.fs,*.fsi,*.fsx,*.fsscript);*.fs,*.fsi,*.fsx,*.fsscript + + Compiler + Compiler + + Visual F# Visual F# diff --git a/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.pt-BR.xlf b/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.pt-BR.xlf index 74dfbed93a7..dd9702aa861 100644 --- a/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.pt-BR.xlf +++ b/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.pt-BR.xlf @@ -422,6 +422,11 @@ Arquivos Visual F# (*.fs,*.fsi,*.fsx,*.fsscript);*.fs,*.fsi,*.fsx,*.fsscript + + Compiler + Compiler + + Visual F# Visual F# diff --git a/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.ru.xlf b/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.ru.xlf index 001d9fc5109..4874c0492d3 100644 --- a/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.ru.xlf +++ b/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.ru.xlf @@ -422,6 +422,11 @@ Файлы Visual F# (*.fs,*.fsi,*.fsx,*.fsscript);*.fs,*.fsi,*.fsx,*.fsscript + + Compiler + Compiler + + Visual F# Visual F# diff --git a/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.tr.xlf b/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.tr.xlf index d7ad7e7efde..7daafd2418b 100644 --- a/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.tr.xlf +++ b/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.tr.xlf @@ -422,6 +422,11 @@ Visual F# Dosyaları (*.fs,*.fsi,*.fsx,*.fsscript);*.fs,*.fsi,*.fsx,*.fsscript + + Compiler + Compiler + + Visual F# Visual F# diff --git a/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.zh-Hans.xlf b/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.zh-Hans.xlf index 8e5c98bfcb9..932fd7dcdd3 100644 --- a/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.zh-Hans.xlf +++ b/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.zh-Hans.xlf @@ -422,6 +422,11 @@ Visual F# 文件(*.fs,*.fsi,*.fsx,*.fsscript);*.fs,*.fsi,*.fsx,*.fsscript + + Compiler + Compiler + + Visual F# Visual F# diff --git a/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.zh-Hant.xlf b/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.zh-Hant.xlf index fb348e872fe..a6a335b4c98 100644 --- a/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.zh-Hant.xlf +++ b/vsintegration/src/FSharp.ProjectSystem.FSharp/xlf/VSPackage.zh-Hant.xlf @@ -422,6 +422,11 @@ Visual F# 檔案 (*.fs,*.fsi,*.fsx,*.fsscript);*.fs,*.fsi,*.fsx,*.fsscript + + Compiler + Compiler + + Visual F# Visual F# diff --git a/vsintegration/src/FSharp.VS.FSI/VSPackage.resx b/vsintegration/src/FSharp.VS.FSI/VSPackage.resx index 0b955a487a5..0785825ba61 100644 --- a/vsintegration/src/FSharp.VS.FSI/VSPackage.resx +++ b/vsintegration/src/FSharp.VS.FSI/VSPackage.resx @@ -122,7 +122,4 @@ J1H0RTAZRQRKDEJ2I3A2PCIAPQQRQIDZI3CTKAIAQPJIE3EPHAMRRKAJCHI1PJM2CPDIRZKMMJHKPHJ9E1HDZHZDC1DEHHR0QIARA8RKA3PICCHAA3QKA1QMJZMKKZKZ - - Compiler - From 4b22dbb788516dbe769c466f716eb0fab3b34686 Mon Sep 17 00:00:00 2001 From: Copilot Date: Tue, 8 Sep 2026 13:33:41 +0200 Subject: [PATCH 4/6] Update CompileOrder SDKTest baseline for the flipped SDK-compiler default Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../SDKTests/tests/CompileOrder - BeforeAndAfter.proj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/fsharp/SDKTests/tests/CompileOrder - BeforeAndAfter.proj b/tests/fsharp/SDKTests/tests/CompileOrder - BeforeAndAfter.proj index 55134523861..16a19fa0343 100644 --- a/tests/fsharp/SDKTests/tests/CompileOrder - BeforeAndAfter.proj +++ b/tests/fsharp/SDKTests/tests/CompileOrder - BeforeAndAfter.proj @@ -9,9 +9,9 @@ true /Common7/IDE/CommonExtensions/Microsoft/FSharp/Tools/ - fscAnyCpu.exe - _VsInstallRoot_/Common7/IDE/CommonExtensions/Microsoft/FSharp/Tools/ - + dotnet.exe + /_NetCoreRoot_/ + /FSharp/fsc.dll One;Two;Three;Four;Five;Six;Seven;Eight;Nine;Ten;Eleven;Twelve;Thirteen;Fourteen From e57a159d43a4689d5c12abe9ca4cae78ea32e7b6 Mon Sep 17 00:00:00 2001 From: Copilot Date: Wed, 9 Sep 2026 13:58:07 +0200 Subject: [PATCH 5/6] Address review feedback Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/release-notes/.VisualStudio/18.vNext.md | 2 +- src/FSharp.Build/Microsoft.FSharp.Targets | 14 ++-- .../RegistryCompilerSelection.Tests.ps1 | 66 +++++++++++-------- ...ichFSharpCompiler - Missing SDK Paths.proj | 22 +++++++ 4 files changed, 69 insertions(+), 35 deletions(-) create mode 100644 tests/fsharp/SDKTests/tests/WhichFSharpCompiler - Missing SDK Paths.proj diff --git a/docs/release-notes/.VisualStudio/18.vNext.md b/docs/release-notes/.VisualStudio/18.vNext.md index bf7f405521b..632fc3ab67e 100644 --- a/docs/release-notes/.VisualStudio/18.vNext.md +++ b/docs/release-notes/.VisualStudio/18.vNext.md @@ -2,7 +2,7 @@ * Code-fixes for FS3888 (compiler-semantic attribute on the `.fs` but not the `.fsi`): copy the attribute into the `.fsi`, or remove it from the `.fs`. ([Issue #19560](https://github.com/dotnet/fsharp/issues/19560), [PR #19880](https://github.com/dotnet/fsharp/pull/19880)) * Expand `` in IDE tooltips, completion, and signature help, inheriting XML documentation from base classes, interfaces, overridden members, and constructors. ([Issue #19175](https://github.com/dotnet/fsharp/issues/19175), [PR #19188](https://github.com/dotnet/fsharp/pull/19188)) -* Added a **Tools > Options > F# Tools > Compiler** option, **"Use the .NET SDK F# compiler for builds"**, on by default. Visual Studio now builds F# projects with the F# compiler from the installed .NET SDK (matching `dotnet build`); turn the option off to build with the .NET Framework compiler bundled with Visual Studio. Command-line builds are unaffected. ([Issue #20484](https://github.com/dotnet/fsharp/issues/20484)) +* Added a **Tools > Options > F# Tools > Compiler** option, **"Use the .NET SDK F# compiler for builds"**, on by default. Visual Studio uses the .NET SDK F# compiler when the project supplies SDK paths, matching `dotnet build`. Otherwise, it uses the bundled .NET Framework compiler. Turn the option off to use the bundled compiler for all projects. Command-line builds are unaffected. ([Issue #20484](https://github.com/dotnet/fsharp/issues/20484), [PR #20485](https://github.com/dotnet/fsharp/pull/20485)) ### Fixed diff --git a/src/FSharp.Build/Microsoft.FSharp.Targets b/src/FSharp.Build/Microsoft.FSharp.Targets index d3522b290ec..5780968358c 100644 --- a/src/FSharp.Build/Microsoft.FSharp.Targets +++ b/src/FSharp.Build/Microsoft.FSharp.Targets @@ -54,11 +54,13 @@ this file. Project file properties that control compiler selection: ======================================================== Suggest that the compiler used be the desktop framework version. On computers without Visual Studio these properties is ignored. - boolean: true or false === default value false (use the .NET SDK F# compiler) - When unset, Visual Studio builds default to the .NET SDK compiler; the .NET Framework compiler is used - only if this property is set to true in the project, or if the Tools > Options > F# Tools > Compiler - option "Use the .NET SDK F# compiler for builds" is turned off (HKCU\Software\Microsoft\VisualStudio\FSharp, - DWORD UseNetSdkCompiler = 0). Command-line (dotnet build) builds are unaffected. + boolean: true or false === default value false when SDK paths are available + When unset, Visual Studio builds use the .NET SDK compiler if NetCoreRoot and NETCoreSdkVersion are available. + Otherwise, they use the bundled .NET Framework compiler. + Set this property to true to use the .NET Framework compiler, or turn off + Tools > Options > F# Tools > Compiler > "Use the .NET SDK F# compiler for builds" + (HKCU\Software\Microsoft\VisualStudio\FSharp, DWORD UseNetSdkCompiler = 0). + Command-line (dotnet build) builds are unaffected. Suggest that the compiler used be the 64 Bit compiler. On computers without Visual Studio this property is ignored. boolean: true or false === default value true @@ -72,7 +74,7 @@ this file. <_FSharpUseNetSdkCompilerVsOption>$([MSBuild]::GetRegistryValueFromView('HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\FSharp', 'UseNetSdkCompiler', null, RegistryView.Default)) - true + true false diff --git a/tests/fsharp/SDKTests/RegistryCompilerSelection.Tests.ps1 b/tests/fsharp/SDKTests/RegistryCompilerSelection.Tests.ps1 index d1a493113ba..6d8a6d14f7e 100644 --- a/tests/fsharp/SDKTests/RegistryCompilerSelection.Tests.ps1 +++ b/tests/fsharp/SDKTests/RegistryCompilerSelection.Tests.ps1 @@ -47,11 +47,14 @@ function Get-Props([string[]]$extra) { Push-Location $work try { $args = @("msbuild", "probe.proj", + "-getProperty:MSBuildToolsPath", "-getProperty:FSharp_Shim_Present", "-getProperty:FSharpPreferNetFrameworkTools", + "-getProperty:FscToolPath", "-getProperty:FscToolExe", "-getProperty:DotnetFscCompilerPath") + $extra $json = & $DotNet @args 2>&1 | Out-String + if ($LASTEXITCODE -ne 0) { throw "MSBuild probe failed: $json" } return ($json | ConvertFrom-Json).Properties } finally { Pop-Location } } @@ -63,37 +66,44 @@ function Check($name, $cond, $detail) { } try { - # Case 1: no registry value -> SDK selected (default flipped) - Clear-Reg - $p = Get-Props @() - Check "Case1 no-registry -> SDK" ` - ($p.FSharpPreferNetFrameworkTools -eq 'false' -and $p.FscToolExe -eq 'dotnet.exe' -and $p.DotnetFscCompilerPath -match 'fsc\.dll') ` - ("got: $($p | ConvertTo-Json -Compress)") - - # Case 2: registry 0 -> .NET Framework selected - Set-Reg 0 - $p = Get-Props @() - Check "Case2 registry=0 -> .NET Framework" ` - ($p.FSharpPreferNetFrameworkTools -eq 'true' -and $p.FscToolExe -match '^fsc.*\.exe$' -and $p.DotnetFscCompilerPath -eq '') ` - ("got: $($p | ConvertTo-Json -Compress)") - - # Case 3: registry 1 -> SDK selected - Set-Reg 1 - $p = Get-Props @() - Check "Case3 registry=1 -> SDK" ` - ($p.FSharpPreferNetFrameworkTools -eq 'false' -and $p.FscToolExe -eq 'dotnet.exe' -and $p.DotnetFscCompilerPath -match 'fsc\.dll') ` - ("got: $($p | ConvertTo-Json -Compress)") - - # Case 4: explicit project value wins over registry 1 - Set-Reg 1 - $p = Get-Props @("-p:FSharpPreferNetFrameworkTools=true") - Check "Case4 explicit=true beats registry=1 -> .NET Framework" ` - ($p.FSharpPreferNetFrameworkTools -eq 'true' -and $p.FscToolExe -match '^fsc.*\.exe$' -and $p.DotnetFscCompilerPath -eq '') ` - ("got: $($p | ConvertTo-Json -Compress)") + $sdk = Get-Props @() + $sdkRoot = (Split-Path (Split-Path $sdk.MSBuildToolsPath -Parent) -Parent) + '\' + $sdkVersion = Split-Path $sdk.MSBuildToolsPath -Leaf + $sdkCases = @( + @{ Name = 'resolved SDK'; Root = $sdkRoot; Version = $sdkVersion }, + @{ Name = 'missing root'; Root = ''; Version = $sdkVersion }, + @{ Name = 'missing version'; Root = $sdkRoot; Version = '' }, + @{ Name = 'non-SDK project'; Root = ''; Version = '' } + ) + foreach ($registry in @($null, 0, 1)) { + if ($null -eq $registry) { Clear-Reg } else { Set-Reg $registry } + foreach ($sdkCase in $sdkCases) { + foreach ($explicit in @('', 'true', 'false')) { + $properties = @("-p:NetCoreRoot=$($sdkCase.Root)", "-p:NETCoreSdkVersion=$($sdkCase.Version)") + if ($explicit -ne '') { $properties += "-p:FSharpPreferNetFrameworkTools=$explicit" } + $p = Get-Props $properties + $desktop = $explicit -eq 'true' -or + ($explicit -eq '' -and ($registry -eq 0 -or $sdkCase.Root -eq '' -or $sdkCase.Version -eq '')) + if ($desktop) { + $correct = $p.FSharpPreferNetFrameworkTools -eq 'true' -and + $p.FscToolExe -match '^fsc.*\.exe$' -and + $p.FscToolPath -like '*/Common7/IDE/CommonExtensions/Microsoft/FSharp/Tools/' -and + $p.DotnetFscCompilerPath -eq '' + } else { + $correct = $p.FSharpPreferNetFrameworkTools -eq 'false' -and + $p.FscToolExe -eq 'dotnet.exe' -and $p.FscToolPath -eq $sdkCase.Root -and + $p.DotnetFscCompilerPath -eq "`"$($sdkCase.Root)sdk/$($sdkCase.Version)/FSharp/fsc.dll`"" + } + Check "$($sdkCase.Name), registry='$registry', explicit='$explicit'" ` + ($p.FSharp_Shim_Present -eq 'true' -and $correct) ` + ("got: $($p | ConvertTo-Json -Compress)") + } + } + } # Case 5 (NEGATIVE): CLI build (shim absent) -> selection blocks skipped, unaffected Set-Reg 0 - $p = Get-Props @("-p:FSharpCompilerPath=/Explicit/") + $p = Get-Props @("-p:FSharpCompilerPath=C:\Explicit\") Check "Case5 CLI (shim absent) -> unaffected (no fsc selection)" ` ($p.FSharp_Shim_Present -eq '' -and $p.FscToolExe -eq '' -and $p.DotnetFscCompilerPath -eq '') ` ("got: $($p | ConvertTo-Json -Compress)") diff --git a/tests/fsharp/SDKTests/tests/WhichFSharpCompiler - Missing SDK Paths.proj b/tests/fsharp/SDKTests/tests/WhichFSharpCompiler - Missing SDK Paths.proj new file mode 100644 index 00000000000..1f128c37c68 --- /dev/null +++ b/tests/fsharp/SDKTests/tests/WhichFSharpCompiler - Missing SDK Paths.proj @@ -0,0 +1,22 @@ + + + + + true + /Common7/IDE/CommonExtensions/Microsoft/FSharp/Tools/ + fscAnyCpu.exe + _VsInstallRoot_/Common7/IDE/CommonExtensions/Microsoft/FSharp/Tools/ + + + + + + + + + true + + + + + From c9564eff026a6b451ff645317cdaec9ad7e27d3c Mon Sep 17 00:00:00 2001 From: Copilot Date: Wed, 9 Sep 2026 15:55:11 +0200 Subject: [PATCH 6/6] Address review feedback Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tests/fsharp/SDKTests/AllSdkTargetsTests.proj | 2 ++ .../tests/WhichFSharpCompiler - Missing SDK Paths.proj | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/fsharp/SDKTests/AllSdkTargetsTests.proj b/tests/fsharp/SDKTests/AllSdkTargetsTests.proj index 9da3a21fea1..8713c4ed36f 100644 --- a/tests/fsharp/SDKTests/AllSdkTargetsTests.proj +++ b/tests/fsharp/SDKTests/AllSdkTargetsTests.proj @@ -6,6 +6,8 @@ + + diff --git a/tests/fsharp/SDKTests/tests/WhichFSharpCompiler - Missing SDK Paths.proj b/tests/fsharp/SDKTests/tests/WhichFSharpCompiler - Missing SDK Paths.proj index 1f128c37c68..ddbcc9cd48d 100644 --- a/tests/fsharp/SDKTests/tests/WhichFSharpCompiler - Missing SDK Paths.proj +++ b/tests/fsharp/SDKTests/tests/WhichFSharpCompiler - Missing SDK Paths.proj @@ -12,8 +12,8 @@ - - + + true