Skip to content

Commit 1695c5e

Browse files
authored
F# Scripts: Fix resolving the dotnet host path when an SDK directory is specified (#18960)
1 parent 2890cd6 commit 1695c5e

File tree

17 files changed

+214
-114
lines changed

17 files changed

+214
-114
lines changed
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
### Fixed
2+
3+
* Scripts: Fix resolving the dotnet host path when an SDK directory is specified. ([PR #18960](https://github.com/dotnet/fsharp/pull/18960))
24
* Fix excessive StackGuard thread jumping ([PR #18971](https://github.com/dotnet/fsharp/pull/18971))
35

46
### Added
57

68
### Changed
79

8-
### Breaking Changes
10+
### Breaking Changes

src/Compiler/DependencyManager/DependencyProvider.fs

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace FSharp.Compiler.DependencyManager
44

55
open System
6+
open System.Collections.Generic
67
open System.IO
78
open System.Reflection
89
open System.Runtime.InteropServices
@@ -152,14 +153,27 @@ type ReflectionDependencyManagerProvider
152153
resolveDepsExWithScriptInfoAndTimeout: MethodInfo option,
153154
clearResultCache: MethodInfo option,
154155
outputDir: string option,
155-
useResultsCache: bool
156+
useResultsCache: bool,
157+
sdkDirOverride: string option
156158
) =
157159

158160
let instance =
159-
if not (isNull (theType.GetConstructor([| typeof<string option>; typeof<bool> |]))) then
160-
Activator.CreateInstance(theType, [| outputDir :> objnull; useResultsCache :> objnull |])
161-
else
162-
Activator.CreateInstance(theType, [| outputDir :> objnull |])
161+
let arguments: (Type * objnull) array =
162+
[|
163+
typeof<string option>, outputDir
164+
typeof<bool>, useResultsCache
165+
typeof<IDictionary<string, obj>>, dict [ "sdkDirOverride", sdkDirOverride :> obj ]
166+
|]
167+
168+
let n = arguments.Length
169+
170+
// Searches for the most suitable constructor,
171+
// starting from the latest version (with more parameters) and falling back to earlier ones.
172+
seq { for i in n - 1 .. -1 .. 0 -> arguments[0..i] }
173+
|> Seq.map Array.unzip
174+
|> Seq.tryFind (fun (types, _) -> isNotNull (theType.GetConstructor(types)))
175+
|> Option.map (fun (_, values) -> Activator.CreateInstance(theType, values))
176+
|> Option.toObj
163177

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

174-
static member InstanceMaker(theType: Type, outputDir: string option, useResultsCache: bool) =
188+
static member InstanceMaker(theType: Type, outputDir: string option, useResultsCache: bool, sdkDirOverride: string option) =
175189
match
176190
getAttributeNamed theType dependencyManagerAttributeName,
177191
getInstanceProperty<string> theType namePropertyName,
@@ -246,7 +260,8 @@ type ReflectionDependencyManagerProvider
246260
resolveDepsExWithScriptInfoAndTimeout,
247261
clearResultsCacheMethod,
248262
outputDir,
249-
useResultsCache
263+
useResultsCache,
264+
sdkDirOverride
250265
)
251266
:> IDependencyManagerProvider)
252267

@@ -315,7 +330,8 @@ type ReflectionDependencyManagerProvider
315330
resolveDepsExWithScriptInfoAndTimeout,
316331
clearResultsCacheMethod,
317332
outputDir,
318-
useResultsCache
333+
useResultsCache,
334+
sdkDirOverride
319335
)
320336
:> IDependencyManagerProvider)
321337

@@ -510,7 +526,12 @@ type DependencyProvider
510526
let mutable registeredDependencyManagers: Map<string, IDependencyManagerProvider> option =
511527
None
512528

513-
let RegisteredDependencyManagers (compilerTools: seq<string>) (outputDir: string option) (reportError: ResolvingErrorReport) =
529+
let RegisteredDependencyManagers
530+
(compilerTools: seq<string>)
531+
(outputDir: string option)
532+
(sdkDirOverride: string option)
533+
(reportError: ResolvingErrorReport)
534+
=
514535
match registeredDependencyManagers with
515536
| Some managers -> managers
516537
| None ->
@@ -520,7 +541,8 @@ type DependencyProvider
520541
let loadedProviders =
521542
enumerateDependencyManagerAssemblies compilerTools reportError
522543
|> Seq.collect (fun a -> a.GetTypes())
523-
|> Seq.choose (fun t -> ReflectionDependencyManagerProvider.InstanceMaker(t, outputDir, useResultsCache))
544+
|> Seq.choose (fun t ->
545+
ReflectionDependencyManagerProvider.InstanceMaker(t, outputDir, useResultsCache, sdkDirOverride))
524546
|> Seq.map (fun maker -> maker ())
525547

526548
defaultProviders
@@ -547,32 +569,37 @@ type DependencyProvider
547569
new() = new DependencyProvider(None, None, true)
548570

549571
/// Returns a formatted help messages for registered dependencymanagers for the host to present
550-
member _.GetRegisteredDependencyManagerHelpText(compilerTools, outputDir: string | null, errorReport) =
572+
member _.GetRegisteredDependencyManagerHelpText(compilerTools, outputDir: string | null, sdkDirOverride: string option, errorReport) =
551573
[|
552574
let managers =
553-
RegisteredDependencyManagers compilerTools (Option.ofString outputDir) errorReport
575+
RegisteredDependencyManagers compilerTools (Option.ofString outputDir) sdkDirOverride errorReport
554576

555577
for kvp in managers do
556578
let dm = kvp.Value
557579
yield! dm.HelpMessages
558580
|]
559581

560582
/// Clear the DependencyManager results caches
561-
member _.ClearResultsCache(compilerTools, outputDir: string | null, errorReport) =
583+
member _.ClearResultsCache(compilerTools, outputDir: string | null, sdkDirOverride: string option, errorReport) =
562584
let managers =
563-
RegisteredDependencyManagers compilerTools (Option.ofString outputDir) errorReport
585+
RegisteredDependencyManagers compilerTools (Option.ofString outputDir) sdkDirOverride errorReport
564586

565587
for kvp in managers do
566588
kvp.Value.ClearResultsCache()
567589

568590
/// Returns a formatted error message for the host to present
569591
member _.CreatePackageManagerUnknownError
570-
(compilerTools: seq<string>, outputDir: string, packageManagerKey: string, reportError: ResolvingErrorReport)
571-
=
592+
(
593+
compilerTools: seq<string>,
594+
outputDir: string,
595+
sdkDirOverride: string option,
596+
packageManagerKey: string,
597+
reportError: ResolvingErrorReport
598+
) =
572599
let registeredKeys =
573600
String.Join(
574601
", ",
575-
RegisteredDependencyManagers compilerTools (Option.ofString outputDir) reportError
602+
RegisteredDependencyManagers compilerTools (Option.ofString outputDir) sdkDirOverride reportError
576603
|> Seq.map (fun kv -> kv.Value.Key)
577604
)
578605

@@ -581,17 +608,17 @@ type DependencyProvider
581608

582609
/// Fetch a dependencymanager that supports a specific key
583610
member this.TryFindDependencyManagerInPath
584-
(compilerTools: seq<string>, outputDir: string, reportError: ResolvingErrorReport, path: string)
611+
(compilerTools: seq<string>, outputDir: string, sdkDirOverride: string option, reportError: ResolvingErrorReport, path: string)
585612
: string | null * IDependencyManagerProvider | null =
586613
try
587614
if path.Contains ":" && not (Path.IsPathRooted path) then
588615
let managers =
589-
RegisteredDependencyManagers compilerTools (Option.ofString outputDir) reportError
616+
RegisteredDependencyManagers compilerTools (Option.ofString outputDir) sdkDirOverride reportError
590617

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

596623
reportError.Invoke(ErrorReportType.Error, err, msg)
597624
null, null
@@ -607,10 +634,10 @@ type DependencyProvider
607634

608635
/// Fetch a dependencymanager that supports a specific key
609636
member _.TryFindDependencyManagerByKey
610-
(compilerTools: seq<string>, outputDir: string, reportError: ResolvingErrorReport, key: string)
637+
(compilerTools: seq<string>, outputDir: string, sdkDirOverride: string option, reportError: ResolvingErrorReport, key: string)
611638
: IDependencyManagerProvider | null =
612639
try
613-
RegisteredDependencyManagers compilerTools (Option.ofString outputDir) reportError
640+
RegisteredDependencyManagers compilerTools (Option.ofString outputDir) sdkDirOverride reportError
614641
|> Map.tryFind key
615642
|> Option.toObj
616643

src/Compiler/DependencyManager/DependencyProvider.fsi

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,13 +105,16 @@ type DependencyProvider =
105105
DependencyProvider
106106

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

110111
/// Clear the DependencyManager results caches
111-
member ClearResultsCache: string seq * string MaybeNull * ResolvingErrorReport -> unit
112+
member ClearResultsCache:
113+
string seq * string MaybeNull * sdkDirOverride: string option * ResolvingErrorReport -> unit
112114

113115
/// Returns a formatted error message for the host to present
114-
member CreatePackageManagerUnknownError: string seq * string * string * ResolvingErrorReport -> int * string
116+
member CreatePackageManagerUnknownError:
117+
string seq * string * sdkDirOverride: string option * string * ResolvingErrorReport -> int * string
115118

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

130133
/// Fetch a dependencymanager that supports a specific key
131134
member TryFindDependencyManagerByKey:
132-
compilerTools: string seq * outputDir: string * reportError: ResolvingErrorReport * key: string ->
135+
compilerTools: string seq *
136+
outputDir: string *
137+
sdkDirOverride: string option *
138+
reportError: ResolvingErrorReport *
139+
key: string ->
133140
IDependencyManagerProvider | null
134141

135142
/// TryFindDependencyManagerInPath - given a #r "key:sometext" go and find a DependencyManager that satisfies the key
136143
member TryFindDependencyManagerInPath:
137-
compilerTools: string seq * outputDir: string * reportError: ResolvingErrorReport * path: string ->
144+
compilerTools: string seq *
145+
outputDir: string *
146+
sdkDirOverride: string option *
147+
reportError: ResolvingErrorReport *
148+
path: string ->
138149
string | null * IDependencyManagerProvider | null

src/Compiler/Driver/CompilerConfig.fs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1084,7 +1084,13 @@ type TcConfigBuilder =
10841084
| ErrorReportType.Error -> errorR (Error(error, m)))
10851085

10861086
let dm =
1087-
dependencyProvider.TryFindDependencyManagerInPath(tcConfigB.compilerToolPaths, output, reportError, path)
1087+
dependencyProvider.TryFindDependencyManagerInPath(
1088+
tcConfigB.compilerToolPaths,
1089+
output,
1090+
tcConfigB.sdkDirOverride,
1091+
reportError,
1092+
path
1093+
)
10881094

10891095
match dm with
10901096
// #r "Assembly"

src/Compiler/Driver/FxResolver.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ type internal FxResolver
112112
desiredDotNetSdkVersionForDirectoryCache.GetOrAdd(
113113
projectDir,
114114
(fun _ ->
115-
match getDotnetHostPath () with
115+
match getDotnetHostPath None with
116116
| Some dotnetHostPath ->
117117
try
118118
let workingDir =

src/Compiler/Driver/ScriptClosure.fs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,7 @@ module ScriptPreprocessClosure =
347347
dependencyProvider.TryFindDependencyManagerByKey(
348348
tcConfig.compilerToolPaths,
349349
outputDir,
350+
tcConfig.sdkDirOverride,
350351
reportError m,
351352
packageManagerKey
352353
)
@@ -357,6 +358,7 @@ module ScriptPreprocessClosure =
357358
dependencyProvider.CreatePackageManagerUnknownError(
358359
tcConfig.compilerToolPaths,
359360
outputDir,
361+
tcConfig.sdkDirOverride,
360362
packageManagerKey,
361363
reportError m
362364
)

src/Compiler/Facilities/CompilerLocation.fs

Lines changed: 52 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -301,45 +301,60 @@ module internal FSharpEnvironment =
301301
// Can't find it --- give up
302302
None
303303

304-
let getDotnetHostPath () =
305-
// How to find dotnet.exe --- woe is me; probing rules make me sad.
306-
// Algorithm:
307-
// 1. Look for DOTNET_HOST_PATH environment variable
308-
// this is the main user programmable override .. provided by user to find a specific dotnet.exe
309-
// 2. Probe for are we part of an .NetSDK install
310-
// In an sdk install we are always installed in: sdk\3.0.100-rc2-014234\FSharp
311-
// dotnet or dotnet.exe will be found in the directory that contains the sdk directory
312-
// 3. We are loaded in-process to some other application ... Eg. try .net
313-
// See if the host is dotnet.exe ... from net5.0 on this is fairly unlikely
314-
// 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
315-
// Use the path to search for dotnet.exe
316-
let probePathForDotnetHost () =
317-
let paths =
318-
let p = Environment.GetEnvironmentVariable("PATH")
319-
320-
match p with
321-
| null -> [||]
322-
| p -> p.Split(Path.PathSeparator)
323-
324-
paths |> Array.tryFind (fun f -> fileExists (Path.Combine(f, dotnet)))
304+
let getDotnetHostPath sdkDirOverride =
305+
let dotnetHostPathOverride =
306+
sdkDirOverride
307+
|> Option.bind (fun sdkDirOverride ->
308+
let dotnetHostPath =
309+
Path.GetFullPath(Path.Combine(sdkDirOverride, "..", "..", dotnet))
310+
311+
if fileExists dotnetHostPath then
312+
Some dotnetHostPath
313+
else
314+
None)
315+
316+
match dotnetHostPathOverride with
317+
| Some _ -> dotnetHostPathOverride
318+
| None ->
325319

326-
match (Environment.GetEnvironmentVariable("DOTNET_HOST_PATH")) with
327-
// Value set externally
328-
| NonEmptyString value when fileExists value -> Some value
329-
| _ ->
330-
// Probe for netsdk install, dotnet. and dotnet.exe is a constant offset from the location of System.Int32
331-
let candidate =
332-
let assemblyLocation =
333-
Path.GetDirectoryName(typeof<Int32>.GetTypeInfo().Assembly.Location)
320+
// How to find dotnet.exe --- woe is me; probing rules make me sad.
321+
// Algorithm:
322+
// 1. Look for DOTNET_HOST_PATH environment variable
323+
// this is the main user programmable override .. provided by user to find a specific dotnet.exe
324+
// 2. Probe for are we part of an .NetSDK install
325+
// In an sdk install we are always installed in: sdk\3.0.100-rc2-014234\FSharp
326+
// dotnet or dotnet.exe will be found in the directory that contains the sdk directory
327+
// 3. We are loaded in-process to some other application ... Eg. try .net
328+
// See if the host is dotnet.exe ... from net5.0 on this is fairly unlikely
329+
// 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
330+
// Use the path to search for dotnet.exe
331+
let probePathForDotnetHost () =
332+
let paths =
333+
let p = Environment.GetEnvironmentVariable("PATH")
334+
335+
match p with
336+
| null -> [||]
337+
| p -> p.Split(Path.PathSeparator)
338+
339+
paths |> Array.tryFind (fun f -> fileExists (Path.Combine(f, dotnet)))
340+
341+
match (Environment.GetEnvironmentVariable("DOTNET_HOST_PATH")) with
342+
// Value set externally
343+
| NonEmptyString value when fileExists value -> Some value
344+
| _ ->
345+
// Probe for netsdk install, dotnet. and dotnet.exe is a constant offset from the location of System.Int32
346+
let candidate =
347+
let assemblyLocation =
348+
Path.GetDirectoryName(typeof<Int32>.GetTypeInfo().Assembly.Location)
334349

335-
Path.GetFullPath(Path.Combine(!!assemblyLocation, "..", "..", "..", dotnet))
350+
Path.GetFullPath(Path.Combine(!!assemblyLocation, "..", "..", "..", dotnet))
336351

337-
if fileExists candidate then
338-
Some candidate
339-
else
340-
match probePathForDotnetHost () with
341-
| Some f -> Some(Path.Combine(f, dotnet))
342-
| None -> getDotnetGlobalHostPath ()
352+
if fileExists candidate then
353+
Some candidate
354+
else
355+
match probePathForDotnetHost () with
356+
| Some f -> Some(Path.Combine(f, dotnet))
357+
| None -> getDotnetGlobalHostPath ()
343358

344359
let getDotnetHostDirectories () =
345360
let isDotnetMultilevelLookup =
@@ -348,7 +363,7 @@ module internal FSharpEnvironment =
348363
<> 0
349364

350365
[|
351-
match getDotnetHostPath (), getDotnetGlobalHostPath () with
366+
match getDotnetHostPath None, getDotnetGlobalHostPath () with
352367
| Some hostPath, Some globalHostPath ->
353368
yield !!Path.GetDirectoryName(hostPath)
354369

src/Compiler/Facilities/CompilerLocation.fsi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ module internal FSharpEnvironment =
5555

5656
val dotnet: string
5757

58-
val getDotnetHostPath: unit -> string option
58+
val getDotnetHostPath: sdkDirOverride: string option -> string option
5959

6060
val getDotnetHostDirectories: unit -> string[]
6161

0 commit comments

Comments
 (0)