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
4 changes: 3 additions & 1 deletion docs/release-notes/.FSharp.Compiler.Service/11.0.0.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
### Fixed

* Scripts: Fix resolving the dotnet host path when an SDK directory is specified. ([PR #18960](https://github.com/dotnet/fsharp/pull/18960))
* Fix excessive StackGuard thread jumping ([PR #18971](https://github.com/dotnet/fsharp/pull/18971))

### Added

### Changed

### Breaking Changes
### Breaking Changes
71 changes: 49 additions & 22 deletions src/Compiler/DependencyManager/DependencyProvider.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace FSharp.Compiler.DependencyManager

open System
open System.Collections.Generic
open System.IO
open System.Reflection
open System.Runtime.InteropServices
Expand Down Expand Up @@ -152,14 +153,27 @@ type ReflectionDependencyManagerProvider
resolveDepsExWithScriptInfoAndTimeout: MethodInfo option,
clearResultCache: MethodInfo option,
outputDir: string option,
useResultsCache: bool
useResultsCache: bool,
sdkDirOverride: string option
) =

let instance =
if not (isNull (theType.GetConstructor([| typeof<string option>; typeof<bool> |]))) then
Activator.CreateInstance(theType, [| outputDir :> objnull; useResultsCache :> objnull |])
else
Activator.CreateInstance(theType, [| outputDir :> objnull |])
let arguments: (Type * objnull) array =
[|
typeof<string option>, outputDir
typeof<bool>, useResultsCache
typeof<IDictionary<string, obj>>, dict [ "sdkDirOverride", sdkDirOverride :> obj ]
|]

let n = arguments.Length

// Searches for the most suitable constructor,
// starting from the latest version (with more parameters) and falling back to earlier ones.
seq { for i in n - 1 .. -1 .. 0 -> arguments[0..i] }
|> Seq.map Array.unzip
|> Seq.tryFind (fun (types, _) -> isNotNull (theType.GetConstructor(types)))
|> Option.map (fun (_, values) -> Activator.CreateInstance(theType, values))
|> Option.toObj

let nameProperty (x: objnull) = x |> nameProperty.GetValue |> string
let keyProperty (x: objnull) = x |> keyProperty.GetValue |> string
Expand All @@ -171,7 +185,7 @@ type ReflectionDependencyManagerProvider
| Some helpMessagesProperty -> x |> helpMessagesProperty.GetValue |> toStringArray
| None -> [||]

static member InstanceMaker(theType: Type, outputDir: string option, useResultsCache: bool) =
static member InstanceMaker(theType: Type, outputDir: string option, useResultsCache: bool, sdkDirOverride: string option) =
match
getAttributeNamed theType dependencyManagerAttributeName,
getInstanceProperty<string> theType namePropertyName,
Expand Down Expand Up @@ -246,7 +260,8 @@ type ReflectionDependencyManagerProvider
resolveDepsExWithScriptInfoAndTimeout,
clearResultsCacheMethod,
outputDir,
useResultsCache
useResultsCache,
sdkDirOverride
)
:> IDependencyManagerProvider)

Expand Down Expand Up @@ -315,7 +330,8 @@ type ReflectionDependencyManagerProvider
resolveDepsExWithScriptInfoAndTimeout,
clearResultsCacheMethod,
outputDir,
useResultsCache
useResultsCache,
sdkDirOverride
)
:> IDependencyManagerProvider)

Expand Down Expand Up @@ -510,7 +526,12 @@ type DependencyProvider
let mutable registeredDependencyManagers: Map<string, IDependencyManagerProvider> option =
None

let RegisteredDependencyManagers (compilerTools: seq<string>) (outputDir: string option) (reportError: ResolvingErrorReport) =
let RegisteredDependencyManagers
(compilerTools: seq<string>)
(outputDir: string option)
(sdkDirOverride: string option)
(reportError: ResolvingErrorReport)
=
match registeredDependencyManagers with
| Some managers -> managers
| None ->
Expand All @@ -520,7 +541,8 @@ type DependencyProvider
let loadedProviders =
enumerateDependencyManagerAssemblies compilerTools reportError
|> Seq.collect (fun a -> a.GetTypes())
|> Seq.choose (fun t -> ReflectionDependencyManagerProvider.InstanceMaker(t, outputDir, useResultsCache))
|> Seq.choose (fun t ->
ReflectionDependencyManagerProvider.InstanceMaker(t, outputDir, useResultsCache, sdkDirOverride))
|> Seq.map (fun maker -> maker ())

defaultProviders
Expand All @@ -547,32 +569,37 @@ type DependencyProvider
new() = new DependencyProvider(None, None, true)

/// Returns a formatted help messages for registered dependencymanagers for the host to present
member _.GetRegisteredDependencyManagerHelpText(compilerTools, outputDir: string | null, errorReport) =
member _.GetRegisteredDependencyManagerHelpText(compilerTools, outputDir: string | null, sdkDirOverride: string option, errorReport) =
[|
let managers =
RegisteredDependencyManagers compilerTools (Option.ofString outputDir) errorReport
RegisteredDependencyManagers compilerTools (Option.ofString outputDir) sdkDirOverride errorReport

for kvp in managers do
let dm = kvp.Value
yield! dm.HelpMessages
|]

/// Clear the DependencyManager results caches
member _.ClearResultsCache(compilerTools, outputDir: string | null, errorReport) =
member _.ClearResultsCache(compilerTools, outputDir: string | null, sdkDirOverride: string option, errorReport) =
let managers =
RegisteredDependencyManagers compilerTools (Option.ofString outputDir) errorReport
RegisteredDependencyManagers compilerTools (Option.ofString outputDir) sdkDirOverride errorReport

for kvp in managers do
kvp.Value.ClearResultsCache()

/// Returns a formatted error message for the host to present
member _.CreatePackageManagerUnknownError
(compilerTools: seq<string>, outputDir: string, packageManagerKey: string, reportError: ResolvingErrorReport)
=
(
compilerTools: seq<string>,
outputDir: string,
sdkDirOverride: string option,
packageManagerKey: string,
reportError: ResolvingErrorReport
) =
let registeredKeys =
String.Join(
", ",
RegisteredDependencyManagers compilerTools (Option.ofString outputDir) reportError
RegisteredDependencyManagers compilerTools (Option.ofString outputDir) sdkDirOverride reportError
|> Seq.map (fun kv -> kv.Value.Key)
)

Expand All @@ -581,17 +608,17 @@ type DependencyProvider

/// Fetch a dependencymanager that supports a specific key
member this.TryFindDependencyManagerInPath
(compilerTools: seq<string>, outputDir: string, reportError: ResolvingErrorReport, path: string)
(compilerTools: seq<string>, outputDir: string, sdkDirOverride: string option, reportError: ResolvingErrorReport, path: string)
: string | null * IDependencyManagerProvider | null =
try
if path.Contains ":" && not (Path.IsPathRooted path) then
let managers =
RegisteredDependencyManagers compilerTools (Option.ofString outputDir) reportError
RegisteredDependencyManagers compilerTools (Option.ofString outputDir) sdkDirOverride reportError

match managers |> Seq.tryFind (fun kv -> path.StartsWithOrdinal(kv.Value.Key + ":")) with
| None ->
let err, msg =
this.CreatePackageManagerUnknownError(compilerTools, outputDir, path.Split(':').[0], reportError)
this.CreatePackageManagerUnknownError(compilerTools, outputDir, sdkDirOverride, path.Split(':').[0], reportError)

reportError.Invoke(ErrorReportType.Error, err, msg)
null, null
Expand All @@ -607,10 +634,10 @@ type DependencyProvider

/// Fetch a dependencymanager that supports a specific key
member _.TryFindDependencyManagerByKey
(compilerTools: seq<string>, outputDir: string, reportError: ResolvingErrorReport, key: string)
(compilerTools: seq<string>, outputDir: string, sdkDirOverride: string option, reportError: ResolvingErrorReport, key: string)
: IDependencyManagerProvider | null =
try
RegisteredDependencyManagers compilerTools (Option.ofString outputDir) reportError
RegisteredDependencyManagers compilerTools (Option.ofString outputDir) sdkDirOverride reportError
|> Map.tryFind key
|> Option.toObj

Expand Down
21 changes: 16 additions & 5 deletions src/Compiler/DependencyManager/DependencyProvider.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,16 @@ type DependencyProvider =
DependencyProvider

/// Returns a formatted help messages for registered dependencymanagers for the host to present
member GetRegisteredDependencyManagerHelpText: string seq * string MaybeNull * ResolvingErrorReport -> string[]
member GetRegisteredDependencyManagerHelpText:
string seq * string MaybeNull * sdkDirOverride: string option * ResolvingErrorReport -> string[]

/// Clear the DependencyManager results caches
member ClearResultsCache: string seq * string MaybeNull * ResolvingErrorReport -> unit
member ClearResultsCache:
string seq * string MaybeNull * sdkDirOverride: string option * ResolvingErrorReport -> unit

/// Returns a formatted error message for the host to present
member CreatePackageManagerUnknownError: string seq * string * string * ResolvingErrorReport -> int * string
member CreatePackageManagerUnknownError:
string seq * string * sdkDirOverride: string option * string * ResolvingErrorReport -> int * string

/// Resolve reference for a list of package manager lines
member Resolve:
Expand All @@ -129,10 +132,18 @@ type DependencyProvider =

/// Fetch a dependencymanager that supports a specific key
member TryFindDependencyManagerByKey:
compilerTools: string seq * outputDir: string * reportError: ResolvingErrorReport * key: string ->
compilerTools: string seq *
outputDir: string *
sdkDirOverride: string option *
reportError: ResolvingErrorReport *
key: string ->
IDependencyManagerProvider | null

/// TryFindDependencyManagerInPath - given a #r "key:sometext" go and find a DependencyManager that satisfies the key
member TryFindDependencyManagerInPath:
compilerTools: string seq * outputDir: string * reportError: ResolvingErrorReport * path: string ->
compilerTools: string seq *
outputDir: string *
sdkDirOverride: string option *
reportError: ResolvingErrorReport *
path: string ->
string | null * IDependencyManagerProvider | null
8 changes: 7 additions & 1 deletion src/Compiler/Driver/CompilerConfig.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1084,7 +1084,13 @@ type TcConfigBuilder =
| ErrorReportType.Error -> errorR (Error(error, m)))

let dm =
dependencyProvider.TryFindDependencyManagerInPath(tcConfigB.compilerToolPaths, output, reportError, path)
dependencyProvider.TryFindDependencyManagerInPath(
tcConfigB.compilerToolPaths,
output,
tcConfigB.sdkDirOverride,
reportError,
path
)

match dm with
// #r "Assembly"
Expand Down
2 changes: 1 addition & 1 deletion src/Compiler/Driver/FxResolver.fs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ type internal FxResolver
desiredDotNetSdkVersionForDirectoryCache.GetOrAdd(
projectDir,
(fun _ ->
match getDotnetHostPath () with
match getDotnetHostPath None with
| Some dotnetHostPath ->
try
let workingDir =
Expand Down
2 changes: 2 additions & 0 deletions src/Compiler/Driver/ScriptClosure.fs
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ module ScriptPreprocessClosure =
dependencyProvider.TryFindDependencyManagerByKey(
tcConfig.compilerToolPaths,
outputDir,
tcConfig.sdkDirOverride,
reportError m,
packageManagerKey
)
Expand All @@ -357,6 +358,7 @@ module ScriptPreprocessClosure =
dependencyProvider.CreatePackageManagerUnknownError(
tcConfig.compilerToolPaths,
outputDir,
tcConfig.sdkDirOverride,
packageManagerKey,
reportError m
)
Expand Down
89 changes: 52 additions & 37 deletions src/Compiler/Facilities/CompilerLocation.fs
Original file line number Diff line number Diff line change
Expand Up @@ -301,45 +301,60 @@ module internal FSharpEnvironment =
// Can't find it --- give up
None

let getDotnetHostPath () =
// How to find dotnet.exe --- woe is me; probing rules make me sad.
// Algorithm:
// 1. Look for DOTNET_HOST_PATH environment variable
// this is the main user programmable override .. provided by user to find a specific dotnet.exe
// 2. Probe for are we part of an .NetSDK install
// In an sdk install we are always installed in: sdk\3.0.100-rc2-014234\FSharp
// dotnet or dotnet.exe will be found in the directory that contains the sdk directory
// 3. We are loaded in-process to some other application ... Eg. try .net
// See if the host is dotnet.exe ... from net5.0 on this is fairly unlikely
// 4. If it's none of the above we are going to have to rely on the path containing the way to find dotnet.exe
// Use the path to search for dotnet.exe
let probePathForDotnetHost () =
let paths =
let p = Environment.GetEnvironmentVariable("PATH")

match p with
| null -> [||]
| p -> p.Split(Path.PathSeparator)

paths |> Array.tryFind (fun f -> fileExists (Path.Combine(f, dotnet)))
let getDotnetHostPath sdkDirOverride =
let dotnetHostPathOverride =
sdkDirOverride
|> Option.bind (fun sdkDirOverride ->
let dotnetHostPath =
Path.GetFullPath(Path.Combine(sdkDirOverride, "..", "..", dotnet))

if fileExists dotnetHostPath then
Some dotnetHostPath
else
None)

match dotnetHostPathOverride with
| Some _ -> dotnetHostPathOverride
| None ->

match (Environment.GetEnvironmentVariable("DOTNET_HOST_PATH")) with
// Value set externally
| NonEmptyString value when fileExists value -> Some value
| _ ->
// Probe for netsdk install, dotnet. and dotnet.exe is a constant offset from the location of System.Int32
let candidate =
let assemblyLocation =
Path.GetDirectoryName(typeof<Int32>.GetTypeInfo().Assembly.Location)
// How to find dotnet.exe --- woe is me; probing rules make me sad.
// Algorithm:
// 1. Look for DOTNET_HOST_PATH environment variable
// this is the main user programmable override .. provided by user to find a specific dotnet.exe
// 2. Probe for are we part of an .NetSDK install
// In an sdk install we are always installed in: sdk\3.0.100-rc2-014234\FSharp
// dotnet or dotnet.exe will be found in the directory that contains the sdk directory
// 3. We are loaded in-process to some other application ... Eg. try .net
// See if the host is dotnet.exe ... from net5.0 on this is fairly unlikely
// 4. If it's none of the above we are going to have to rely on the path containing the way to find dotnet.exe
// Use the path to search for dotnet.exe
let probePathForDotnetHost () =
let paths =
let p = Environment.GetEnvironmentVariable("PATH")

match p with
| null -> [||]
| p -> p.Split(Path.PathSeparator)

paths |> Array.tryFind (fun f -> fileExists (Path.Combine(f, dotnet)))

match (Environment.GetEnvironmentVariable("DOTNET_HOST_PATH")) with
// Value set externally
| NonEmptyString value when fileExists value -> Some value
| _ ->
// Probe for netsdk install, dotnet. and dotnet.exe is a constant offset from the location of System.Int32
let candidate =
let assemblyLocation =
Path.GetDirectoryName(typeof<Int32>.GetTypeInfo().Assembly.Location)

Path.GetFullPath(Path.Combine(!!assemblyLocation, "..", "..", "..", dotnet))
Path.GetFullPath(Path.Combine(!!assemblyLocation, "..", "..", "..", dotnet))

if fileExists candidate then
Some candidate
else
match probePathForDotnetHost () with
| Some f -> Some(Path.Combine(f, dotnet))
| None -> getDotnetGlobalHostPath ()
if fileExists candidate then
Some candidate
else
match probePathForDotnetHost () with
| Some f -> Some(Path.Combine(f, dotnet))
| None -> getDotnetGlobalHostPath ()

let getDotnetHostDirectories () =
let isDotnetMultilevelLookup =
Expand All @@ -348,7 +363,7 @@ module internal FSharpEnvironment =
<> 0

[|
match getDotnetHostPath (), getDotnetGlobalHostPath () with
match getDotnetHostPath None, getDotnetGlobalHostPath () with
| Some hostPath, Some globalHostPath ->
yield !!Path.GetDirectoryName(hostPath)

Expand Down
2 changes: 1 addition & 1 deletion src/Compiler/Facilities/CompilerLocation.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ module internal FSharpEnvironment =

val dotnet: string

val getDotnetHostPath: unit -> string option
val getDotnetHostPath: sdkDirOverride: string option -> string option

val getDotnetHostDirectories: unit -> string[]

Expand Down
Loading
Loading