Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add additional fallback probes #126

Merged
merged 3 commits into from
Nov 16, 2021
Merged

Add additional fallback probes #126

merged 3 commits into from
Nov 16, 2021

Conversation

baronfel
Copy link
Collaborator

This should fix #123 by

  • probing additional discovery mechanisms
  • failing fast on the init call and notifying the caller what they need to do in order to become compliant.

I thought about making this return a result, but this is kinda a catastrophic failure mode - if we can't find the dotnet binary then nothing else works, either.

Comment on lines +185 to +187
match exe with
| None -> failwith "No dotnet binary could be found via the DOTNET_HOST_PATH or DOTNET_ROOT environment variables, the PATH environment variable, or the default install locations"
| Some exe ->
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here's the new change to fail fast and informatively

Comment on lines +30 to +84
let private tryFindFromEnvVar () =
potentialDotnetHostEnvVars
|> List.tryPick (fun (envVar, transformer) ->
match Environment.GetEnvironmentVariable envVar |> existingEnvVarValue with
| Some varValue -> Some(transformer varValue |> FileInfo)
| None -> None)

let private PATHSeparator =
if isUnix then
':'
else
';'

let private tryFindFromPATH () =
System
.Environment
.GetEnvironmentVariable("PATH")
.Split(PATHSeparator, StringSplitOptions.RemoveEmptyEntries ||| StringSplitOptions.TrimEntries)
|> Array.tryPick (fun d ->
let fi = Path.Combine(d, dotnetBinaryName) |> FileInfo

if fi.Exists then
Some fi
else
None)


let private tryFindFromDefaultDirs () =
let windowsPath = $"C:\\Program Files\\dotnet\\{dotnetBinaryName}"
let macosPath = $"/usr/local/share/dotnet/{dotnetBinaryName}"
let linuxPath = $"/usr/share/dotnet/{dotnetBinaryName}"

let tryFindFile p =
let f = FileInfo p

if f.Exists then
Some f
else
None

if isWindows then
tryFindFile windowsPath
else if isMac then
tryFindFile macosPath
else if isLinux then
tryFindFile linuxPath
else
None

/// <summary>
/// provides the path to the `dotnet` binary running this library, respecting various dotnet <see href="https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-environment-variables#dotnet_root-dotnet_rootx86%5D">environment variables</see>
/// provides the path to the `dotnet` binary running this library, respecting various dotnet <see href="https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-environment-variables#dotnet_root-dotnet_rootx86%5D">environment variables</see>.
/// Also probes the PATH and checks the default installation locations
/// </summary>
let dotnetRoot =
potentialDotnetHostEnvVars
|> List.tryPick
(fun (envVar, transformer) ->
match Environment.GetEnvironmentVariable envVar |> existingEnvVarValue with
| Some varValue -> Some(transformer varValue |> FileInfo)
| None -> None)
|> Option.defaultWith
(fun _ ->
System
.Diagnostics
.Process
.GetCurrentProcess()
.MainModule
.FileName
|> FileInfo)
lazy (tryFindFromEnvVar () |> Option.orElseWith tryFindFromPATH |> Option.orElseWith tryFindFromDefaultDirs)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here's the meat of the logic changes. we no longer allow the process's main module.filename, as that's really only realistic for the dotnet muxer application. instead, we try the env var route, the path route, and finally the default directory route.

@Krzysztof-Cieslak
Copy link
Member

Failing test on OSX - maybe we should increase sleep calls that we have around (https://github.com/ionide/proj-info/blob/main/test/Ionide.ProjInfo.Tests/Tests.fs#L921 etc)? From the error message, it seems like it just didn't manage to finish all parsings in time

Copy link
Member

@Krzysztof-Cieslak Krzysztof-Cieslak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@baronfel baronfel merged commit 0c23bf9 into main Nov 16, 2021
@baronfel baronfel deleted the directory-probes branch November 16, 2021 19:31
@@ -167,7 +168,7 @@ module Init =

match System.Environment.GetEnvironmentVariable "DOTNET_HOST_PATH" with
| null
| "" -> Environment.SetEnvironmentVariable("DOTNET_HOST_PATH", Paths.dotnetRoot.FullName)
| "" -> Environment.SetEnvironmentVariable("DOTNET_HOST_PATH", dotnetExe.FullName)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, My problem in #123 was more that this environment variable was being set within the process to an incorrect value, and then never unset. This caused FSharp.Compiler.Service to do incorrect things.

So is the fix here that dotnetExe will now be a good value?

But even if it's a good value, I feel there should be an unset of this environment variable after sub-process launch, in the cases where it gets set. I had to add an explicit unset here for example: https://github.com/fsprojects/FSharp.Formatting/blob/main/src/fsdocs-tool/BuildCommand.fs#L851

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It turns out that we do need a value of DOTNET_HOST_PATH set, because the F# targets rely on it to set the fsc binary to invoke to fsc.dll instead of fsc.exe. This logic is here. Without this value set, our tests start failing left and right. I could probably try a version where we set these msbuild properties in this library explicitly, but I worry about the fragility of that, as these properties are subject to change at any time, while DOTNET_HOST_PATH is documented.

Another option would be for the init function to return an IDisposable that you could dispose to 'pop' the changes to the environment variables, but that's a bit of a foot-gun - if disposed then everything in this library becomes unusable until init is called again, so you'd want some sort of scoping on the client side.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Setting DOTNET_HOST_PATH is not a good move :)
3 participants