Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/release-notes/.VisualStudio/18.vNext.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<inheritdoc/>` 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

Expand Down
14 changes: 11 additions & 3 deletions src/FSharp.Build/Microsoft.FSharp.Targets
Original file line number Diff line number Diff line change
Expand Up @@ -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.
<FSharpPreferNetFrameworkTools> boolean: true or false === default value true
<FSharpPreferNetFrameworkTools> 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.
<FSharpPreferAnyCpuTools> boolean: true or false === default value true
Expand All @@ -66,8 +72,10 @@ this file.
On Windows Arm64 default to Arm64 build, otherwise default to AnyCpu.
-->

<PropertyGroup Condition="'$(FSharp_Shim_Present)' == 'true'">
<FSharpPreferNetFrameworkTools Condition="'$(FSharpPreferNetFrameworkTools)' == ''">true</FSharpPreferNetFrameworkTools>
<PropertyGroup Condition="'$(FSharp_Shim_Present)' == 'true' and '$(FSharpPreferNetFrameworkTools)' == ''">
<_FSharpUseNetSdkCompilerVsOption>$([MSBuild]::GetRegistryValueFromView('HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\FSharp', 'UseNetSdkCompiler', null, RegistryView.Default))</_FSharpUseNetSdkCompilerVsOption>
<FSharpPreferNetFrameworkTools Condition="'$(_FSharpUseNetSdkCompilerVsOption)' == '0' or '$(NetCoreRoot)' == '' or '$(NETCoreSdkVersion)' == ''">true</FSharpPreferNetFrameworkTools>
<FSharpPreferNetFrameworkTools Condition="'$(FSharpPreferNetFrameworkTools)' == ''">false</FSharpPreferNetFrameworkTools>
Comment thread
T-Gro marked this conversation as resolved.
</PropertyGroup>

<PropertyGroup Condition="'$(FSharp_Shim_Present)' == 'true' and '$(FSharpPreferNetFrameworkTools)' == 'true'">
Expand Down
2 changes: 2 additions & 0 deletions tests/fsharp/SDKTests/AllSdkTargetsTests.proj
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

<ItemGroup>
<TestsToRun Include="tests\*.proj" />
<TestsToRun Include="tests\WhichFSharpCompiler - Missing SDK Paths.proj" AdditionalProperties="MissingSdkPath=Root" />
<TestsToRun Include="tests\WhichFSharpCompiler - Missing SDK Paths.proj" AdditionalProperties="MissingSdkPath=Version" />
</ItemGroup>

<Target Name="Build">
Expand Down
122 changes: 122 additions & 0 deletions tests/fsharp/SDKTests/RegistryCompilerSelection.Tests.ps1
Original file line number Diff line number Diff line change
@@ -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"
@"
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Language>F#</Language>
<Configuration>Debug</Configuration>
<FSharpCompilerPath></FSharpCompilerPath>
</PropertyGroup>
<Import Project="$shim" />
<Import Project="$targets" />
</Project>
"@ | 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
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
<PropertyGroup>
<ExpectedFSharpShimPresent>true</ExpectedFSharpShimPresent>
<ExpectedFSharpCompilerPath>/Common7/IDE/CommonExtensions/Microsoft/FSharp/Tools/</ExpectedFSharpCompilerPath>
<ExpectedFscToolExe>fscAnyCpu.exe</ExpectedFscToolExe>
<ExpectedFscToolPath>_VsInstallRoot_/Common7/IDE/CommonExtensions/Microsoft/FSharp/Tools/</ExpectedFscToolPath>
<ExpectedDotnetFscCompilerPath></ExpectedDotnetFscCompilerPath>
<ExpectedFscToolExe>dotnet.exe</ExpectedFscToolExe>
<ExpectedFscToolPath>/_NetCoreRoot_/</ExpectedFscToolPath>
<ExpectedDotnetFscCompilerPath>/FSharp/fsc.dll</ExpectedDotnetFscCompilerPath>
<ExpectedCompile>One;Two;Three;Four;Five;Six;Seven;Eight;Nine;Ten;Eleven;Twelve;Thirteen;Fourteen</ExpectedCompile>
</PropertyGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
<PropertyGroup>
<ExpectedFSharpShimPresent>true</ExpectedFSharpShimPresent>
<ExpectedFSharpCompilerPath>/Common7/IDE/CommonExtensions/Microsoft/FSharp/Tools/</ExpectedFSharpCompilerPath>
<ExpectedFscToolExe>fscAnyCpu.exe</ExpectedFscToolExe>
<ExpectedFscToolPath>_VsInstallRoot_/Common7/IDE/CommonExtensions/Microsoft/FSharp/Tools/</ExpectedFscToolPath>
<ExpectedDotnetFscCompilerPath></ExpectedDotnetFscCompilerPath>
<ExpectedFscToolExe>dotnet.exe</ExpectedFscToolExe>
<ExpectedFscToolPath>/_NetCoreRoot_/</ExpectedFscToolPath>
<ExpectedDotnetFscCompilerPath>/FSharp/fsc.dll</ExpectedDotnetFscCompilerPath>
</PropertyGroup>

<Import Project="ToolsTest.props" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project ToolsVersion="4.0" DefaultTargets="Test" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

<PropertyGroup>
<FSharpCompilerPath></FSharpCompilerPath>
<ExpectedFSharpShimPresent>true</ExpectedFSharpShimPresent>
<ExpectedFSharpCompilerPath>/Common7/IDE/CommonExtensions/Microsoft/FSharp/Tools/</ExpectedFSharpCompilerPath>
<ExpectedFscToolExe>fscAnyCpu.exe</ExpectedFscToolExe>
<ExpectedFscToolPath>_VsInstallRoot_/Common7/IDE/CommonExtensions/Microsoft/FSharp/Tools/</ExpectedFscToolPath>
<ExpectedDotnetFscCompilerPath></ExpectedDotnetFscCompilerPath>
</PropertyGroup>

<Import Project="ToolsTest.props" />

<PropertyGroup>
<NetCoreRoot Condition="'$(MissingSdkPath)' != 'Version'"></NetCoreRoot>
<NETCoreSdkVersion Condition="'$(MissingSdkPath)' != 'Root'"></NETCoreSdkVersion>
<FSharpPreferAnyCpuTools>true</FSharpPreferAnyCpuTools>
</PropertyGroup>

<Import Project="ToolsTest.targets" />

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ type internal FSharpSettingsFactory [<Composition.ImportingConstructor>] (settin

[<Guid(FSharpConstants.packageGuidString)>]
[<ProvideOptionPage(typeof<FSharp.Interactive.FsiPropertyPage>, "F# Tools", "F# Interactive", 6000s, 6001s, true)>] // true = supports automation
[<ProvideOptionPage(typeof<FSharp.Interactive.FSharpCompilerPropertyPage>, "F# Tools", "Compiler", 6000s, 6015s, true)>]

[<ProvideKeyBindingTable("{dee22b65-9761-4a26-8fb2-759b971d6dfc}", 6001s)>] // <-- resource ID for localised name

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,9 @@
<data name="6003" xml:space="preserve">
<value>Visual F# Files (*.fs,*.fsi,*.fsx,*.fsscript);*.fs,*.fsi,*.fsx,*.fsscript</value>
</data>
<data name="6015" xml:space="preserve">
<value>Compiler</value>
</data>
<data name="7000" xml:space="preserve">
<value>Visual F#</value>
</data>
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading