diff --git a/docs/release-notes/.VisualStudio/18.vNext.md b/docs/release-notes/.VisualStudio/18.vNext.md index ba03f663967..632fc3ab67e 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 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 a225e8861e8..5780968358c 100644 --- a/src/FSharp.Build/Microsoft.FSharp.Targets +++ b/src/FSharp.Build/Microsoft.FSharp.Targets @@ -54,7 +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 true + 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 @@ -66,8 +72,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/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/RegistryCompilerSelection.Tests.ps1 b/tests/fsharp/SDKTests/RegistryCompilerSelection.Tests.ps1 new file mode 100644 index 00000000000..6d8a6d14f7e --- /dev/null +++ b/tests/fsharp/SDKTests/RegistryCompilerSelection.Tests.ps1 @@ -0,0 +1,122 @@ +#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: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 } +} + +$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 { + $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=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)") +} +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/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 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 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..ddbcc9cd48d --- /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 + + + + + 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.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/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/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 @@ +