diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
index 225d4ad8..070fa394 100644
--- a/.config/dotnet-tools.json
+++ b/.config/dotnet-tools.json
@@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"paket": {
- "version": "6.0.0-rc006",
+ "version": "6.2.1",
"commands": [
"paket"
]
diff --git a/.paket/Paket.Restore.targets b/.paket/Paket.Restore.targets
index 03928aab..e230bb21 100644
--- a/.paket/Paket.Restore.targets
+++ b/.paket/Paket.Restore.targets
@@ -236,17 +236,16 @@
$([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[0])
$([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[1])
$([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[4])
- $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[5])
- $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[6])
- $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[7])
+ $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[5])
+ $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[6])
+ $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[7])
%(PaketReferencesFileLinesInfo.PackageVersion)
All
- runtime
- runtime
- $(ExcludeAssets);contentFiles
- $(ExcludeAssets);build;buildMultitargeting;buildTransitive
+ runtime
+ $(ExcludeAssets);contentFiles
+ $(ExcludeAssets);build;buildMultitargeting;buildTransitive
true
true
@@ -366,9 +365,9 @@
PackageLicenseFile="$(PackageLicenseFile)"
PackageLicenseExpression="$(PackageLicenseExpression)"
PackageLicenseExpressionVersion="$(PackageLicenseExpressionVersion)"
- PackageReadmeFile="$(PackageReadmeFile)"
- NoDefaultExcludes="$(NoDefaultExcludes) "/>
-
+ Readme="$(PackageReadmeFile)"
+ NoDefaultExcludes="$(NoDefaultExcludes)"/>
+
-> IWorkspaceLoader
let parseProject (loaderFunc: LoaderFunc) (path: string) =
let cwd = System.IO.Path.GetDirectoryName path |> System.IO.DirectoryInfo
- let toolsPath = Ionide.ProjInfo.Init.init cwd
+ let toolsPath = Ionide.ProjInfo.Init.init cwd None
let loader = loaderFunc (toolsPath, [])
loader.LoadProjects([ path ], [], BinaryLogGeneration.Within cwd)
let parseSolution (loaderFunc: LoaderFunc) (path: string) =
let cwd = System.IO.Path.GetDirectoryName path |> System.IO.DirectoryInfo
- let toolsPath = Ionide.ProjInfo.Init.init cwd
+ let toolsPath = Ionide.ProjInfo.Init.init cwd None
let loader = loaderFunc (toolsPath, [])
loader.LoadSln(path, [], BinaryLogGeneration.Within cwd)
diff --git a/src/Ionide.ProjInfo/Library.fs b/src/Ionide.ProjInfo/Library.fs
index 7f269f5c..0f3047ea 100644
--- a/src/Ionide.ProjInfo/Library.fs
+++ b/src/Ionide.ProjInfo/Library.fs
@@ -17,46 +17,95 @@ module SdkDiscovery =
let internal msbuildForSdk (sdkPath: DirectoryInfo) =
Path.Combine(sdkPath.FullName, "MSBuild.dll")
- let private versionedPaths (root: string) =
- System.IO.Directory.EnumerateDirectories root
- |> Seq.choose
- (fun dir ->
- let dirName = Path.GetFileName dir
+ type DotnetRuntimeInfo =
+ { RuntimeName: string
+ Version: SemanticVersioning.Version
+ Path: DirectoryInfo }
+
+ let private execDotnet (cwd: DirectoryInfo) (binaryFullPath: FileInfo) args =
+ let info = ProcessStartInfo()
+ info.WorkingDirectory <- cwd.FullName
+ info.FileName <- binaryFullPath.FullName
- match SemanticVersioning.Version.TryParse dirName with
- | true, v -> Some v
- | false, _ -> None)
- |> Seq.sortDescending
- |> Seq.map (fun v -> v, Path.Combine(root, string v) |> DirectoryInfo)
+ for arg in args do
+ info.ArgumentList.Add arg
- /// Given the DOTNET_ROOT, that is the directory where the `dotnet` binary is present and the sdk/runtimes/etc are,
- /// enumerates the available SDKs in descending version order
- let sdks (dotnetDirectory: DirectoryInfo) =
- let sdksPath = Path.Combine(dotnetDirectory.FullName, "sdk")
- versionedPaths sdksPath
+ info.RedirectStandardOutput <- true
+ let p = System.Diagnostics.Process.Start(info)
+ p.WaitForExit()
+
+ seq {
+ while not p.StandardOutput.EndOfStream do
+ yield p.StandardOutput.ReadLine()
+ }
+
+ let private (|SemVer|_|) version =
+ match SemanticVersioning.Version.TryParse version with
+ | true, v -> Some v
+ | false, _ -> None
+
+ let private (|SdkOutputDirectory|) (path: string) =
+ path.TrimStart('[').TrimEnd(']') |> DirectoryInfo
+
+ let private (|RuntimeParts|_|) (line: string) =
+ match line.IndexOf ' ' with
+ | -1 -> None
+ | n ->
+ let runtimeName, rest = line.[0..n - 1], line.[n + 1..]
+
+ match rest.IndexOf ' ' with
+ | -1 -> None
+ | n -> Some(runtimeName, rest.[0..n - 1], rest.[n + 1..])
+
+ let private (|SdkParts|_|) (line: string) =
+ match line.IndexOf ' ' with
+ | -1 -> None
+ | n -> Some(line.[0..n - 1], line.[n + 1..])
/// Given the DOTNET_ROOT, that is the directory where the `dotnet` binary is present and the sdk/runtimes/etc are,
/// enumerates the available runtimes in descending version order
- let runtimes (dotnetDirectory: DirectoryInfo) =
- let netcoreAppPath = Path.Combine(dotnetDirectory.FullName, "shared", "Microsoft.NETCore.App")
- versionedPaths netcoreAppPath
+ let runtimes (dotnetBinaryPath: FileInfo) : DotnetRuntimeInfo [] =
+ execDotnet dotnetBinaryPath.Directory dotnetBinaryPath [ "--list-runtimes" ]
+ |> Seq.choose
+ (fun line ->
+ match line with
+ | RuntimeParts (runtimeName, SemVer version, SdkOutputDirectory path) ->
+ Some
+ { RuntimeName = runtimeName
+ Version = version
+ Path = Path.Combine(path.FullName, string version) |> DirectoryInfo }
+ | line -> None)
+ |> Seq.toArray
+
+ type DotnetSdkInfo =
+ { Version: SemanticVersioning.Version
+ Path: DirectoryInfo }
+
+ /// Given the DOTNET_sROOT, that is the directory where the `dotnet` binary is present and the sdk/runtimes/etc are,
+ /// enumerates the available SDKs in descending version order
+ let sdks (dotnetBinaryPath: FileInfo) : DotnetSdkInfo [] =
+ execDotnet dotnetBinaryPath.Directory dotnetBinaryPath [ "--list-sdks" ]
+ |> Seq.choose
+ (fun line ->
+ match line with
+ | SdkParts (SemVer sdkVersion, SdkOutputDirectory path) ->
+ Some
+ { Version = sdkVersion
+ Path = Path.Combine(path.FullName, string sdkVersion) |> DirectoryInfo }
+ | line -> None)
+ |> Seq.toArray
/// performs a `dotnet --version` command at the given directory to get the version of the
/// SDK active at that location.
- let versionAt (cwd: DirectoryInfo) =
- let exe = Paths.dotnetRoot
- let info = ProcessStartInfo()
- info.WorkingDirectory <- cwd.FullName
- info.FileName <- exe
- info.ArgumentList.Add("--version")
- info.RedirectStandardOutput <- true
- let p = System.Diagnostics.Process.Start(info)
- p.WaitForExit()
- let stdout = p.StandardOutput.ReadToEnd()
+ let versionAt (cwd: DirectoryInfo) (dotnetBinaryPath: FileInfo) =
+ execDotnet cwd dotnetBinaryPath [ "--version" ]
+ |> Seq.head
+ |> function
+ | version ->
+ match SemanticVersioning.Version.TryParse version with
+ | true, v -> Ok v
+ | false, _ -> Error(dotnetBinaryPath, [ "--version" ], cwd, version)
- match SemanticVersioning.Version.TryParse stdout with
- | true, v -> Ok v
- | false, _ -> Error(exe, info.ArgumentList, cwd, stdout)
[]
module Init =
@@ -113,7 +162,7 @@ module Init =
match System.Environment.GetEnvironmentVariable "DOTNET_HOST_PATH" with
| null
- | "" -> Environment.SetEnvironmentVariable("DOTNET_HOST_PATH", Paths.dotnetRoot)
+ | "" -> Environment.SetEnvironmentVariable("DOTNET_HOST_PATH", Paths.dotnetRoot.FullName)
| alreadySet -> ()
if resolveHandler <> null then
@@ -124,17 +173,20 @@ module Init =
/// Initialize the MsBuild integration. Returns path to MsBuild tool that was detected by Locator. Needs to be called before doing anything else.
/// Call it again when the working directory changes.
- let init (workingDirectory: DirectoryInfo) =
- match SdkDiscovery.versionAt workingDirectory with
+ let init (workingDirectory: DirectoryInfo) (dotnetExe: FileInfo option) =
+ let exe = dotnetExe |> Option.defaultWith (fun _ -> Paths.dotnetRoot)
+
+ match SdkDiscovery.versionAt workingDirectory exe with
| Ok dotnetSdkVersionAtPath ->
- let sdkVersion, sdkPath =
- SdkDiscovery.sdks (Path.GetDirectoryName Paths.dotnetRoot |> DirectoryInfo)
- |> Seq.skipWhile (fun (v, path) -> v > dotnetSdkVersionAtPath)
- |> Seq.head
-
- let msbuild = SdkDiscovery.msbuildForSdk sdkPath
- setupForSdkVersion sdkPath
- ToolsPath msbuild
+ let sdks = SdkDiscovery.sdks exe
+ let sdkInfo: SdkDiscovery.DotnetSdkInfo option = sdks |> Array.skipWhile (fun { Version = v } -> v < dotnetSdkVersionAtPath) |> Array.tryHead
+
+ match sdkInfo with
+ | Some sdkInfo ->
+ let msbuild = SdkDiscovery.msbuildForSdk sdkInfo.Path
+ setupForSdkVersion sdkInfo.Path
+ ToolsPath msbuild
+ | None -> failwithf $"Unable to get sdk versions at least from the string '{dotnetSdkVersionAtPath}'. This found sdks were {sdks |> Array.toList}"
| Error (dotnetExe, args, cwd, erroringVersionString) -> failwithf $"Unable to parse sdk version from the string '{erroringVersionString}'. This value came from running `{dotnetExe} {args}` at path {cwd}"
[]
diff --git a/src/Ionide.ProjInfo/Utils.fs b/src/Ionide.ProjInfo/Utils.fs
index 510f2a42..0823593f 100644
--- a/src/Ionide.ProjInfo/Utils.fs
+++ b/src/Ionide.ProjInfo/Utils.fs
@@ -1,19 +1,48 @@
namespace Ionide.ProjInfo
+open System.Runtime.InteropServices
+open System.IO
+open System
+
module Paths =
- /// provides the path to the `dotnet` binary running this library, duplicated from
- /// https://github.com/dotnet/sdk/blob/b91b88aec2684e3d2988df8d838d3aa3c6240a35/src/Cli/Microsoft.DotNet.Cli.Utils/Muxer.cs#L39
- let dotnetRoot =
- match System.Environment.GetEnvironmentVariable "DOTNET_HOST_PATH" with
+ let private isUnix = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)
+
+ let private dotnetBinaryName =
+ if isUnix then
+ "dotnet"
+ else
+ "dotnet.exe"
+
+ let private potentialDotnetHostEnvVars =
+ [ "DOTNET_HOST_PATH", id // is a full path to dotnet binary
+ "DOTNET_ROOT", (fun s -> Path.Combine(s, dotnetBinaryName)) // needs dotnet binary appended
+ "DOTNET_ROOT(x86)", (fun s -> Path.Combine(s, dotnetBinaryName)) ] // needs dotnet binary appended
+
+ let private existingEnvVarValue envVarValue =
+ match envVarValue with
| null
- | "" ->
- System
- .Diagnostics
- .Process
- .GetCurrentProcess()
- .MainModule
- .FileName
- | alreadySet -> alreadySet
+ | "" -> None
+ | other -> Some other
+
+ ///
+ /// provides the path to the `dotnet` binary running this library, respecting various dotnet environment variables
+ ///
+ 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)
let sdksPath (dotnetRoot: string) =
System.IO.Path.Combine(dotnetRoot, "Sdks")
diff --git a/test/Ionide.ProjInfo.Tests/Program.fs b/test/Ionide.ProjInfo.Tests/Program.fs
index 795be393..2f63b4c2 100644
--- a/test/Ionide.ProjInfo.Tests/Program.fs
+++ b/test/Ionide.ProjInfo.Tests/Program.fs
@@ -11,16 +11,7 @@ open Expecto.Logging
[]
let main argv =
- let baseDir = System.Environment.GetEnvironmentVariable "DOTNET_ROOT"
- // need to set this because these tests aren't run directly via the `dotnet` binary
- let dotnetExe =
- if Environment.isMacOS || Environment.isUnix then
- "dotnet"
- else
- "dotnet.exe"
-
- Environment.SetEnvironmentVariable("DOTNET_HOST_PATH", IO.Path.Combine(baseDir, dotnetExe))
- let toolsPath = Init.init (IO.DirectoryInfo Environment.CurrentDirectory)
+ let toolsPath = Init.init (IO.DirectoryInfo Environment.CurrentDirectory) None
Tests.runTestsWithArgs
{ defaultConfig with
diff --git a/test/Ionide.ProjInfo.Tests/Tests.fs b/test/Ionide.ProjInfo.Tests/Tests.fs
index 386e6551..2afc579a 100644
--- a/test/Ionide.ProjInfo.Tests/Tests.fs
+++ b/test/Ionide.ProjInfo.Tests/Tests.fs
@@ -1034,4 +1034,12 @@ let tests toolsPath =
debugTets toolsPath "WorkspaceLoaderViaProjectGraph" WorkspaceLoaderViaProjectGraph.Create
//Binlog test
testSample2WithBinLog toolsPath "WorkspaceLoader" WorkspaceLoader.Create
- testSample2WithBinLog toolsPath "WorkspaceLoaderViaProjectGraph" WorkspaceLoaderViaProjectGraph.Create ]
+ testSample2WithBinLog toolsPath "WorkspaceLoaderViaProjectGraph" WorkspaceLoaderViaProjectGraph.Create
+ test "can get runtimes" {
+ let runtimes = SdkDiscovery.runtimes Paths.dotnetRoot
+ Expect.isNonEmpty runtimes "should have found at least the currently-executing runtime"
+ }
+ test "can get sdks" {
+ let sdks = SdkDiscovery.sdks Paths.dotnetRoot
+ Expect.isNonEmpty sdks "should have found at least the currently-executing sdk"
+ } ]