Skip to content

Commit 1c9317b

Browse files
authored
add in support for more dotnet environment variables (#117)
1 parent 823fe86 commit 1c9317b

File tree

8 files changed

+165
-78
lines changed

8 files changed

+165
-78
lines changed

.config/dotnet-tools.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"isRoot": true,
44
"tools": {
55
"paket": {
6-
"version": "6.0.0-rc006",
6+
"version": "6.2.1",
77
"commands": [
88
"paket"
99
]

.paket/Paket.Restore.targets

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -236,17 +236,16 @@
236236
<PackageName>$([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[0])</PackageName>
237237
<PackageVersion>$([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[1])</PackageVersion>
238238
<AllPrivateAssets>$([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[4])</AllPrivateAssets>
239-
<CopyLocal Condition="'%(PaketReferencesFileLinesInfo.Splits)' == '6'">$([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[5])</CopyLocal>
240-
<OmitContent Condition="'%(PaketReferencesFileLinesInfo.Splits)' == '7'">$([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[6])</OmitContent>
241-
<ImportTargets Condition="'%(PaketReferencesFileLinesInfo.Splits)' == '8'">$([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[7])</ImportTargets>
239+
<CopyLocal Condition="%(PaketReferencesFileLinesInfo.Splits) &gt;= 6">$([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[5])</CopyLocal>
240+
<OmitContent Condition="%(PaketReferencesFileLinesInfo.Splits) &gt;= 7">$([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[6])</OmitContent>
241+
<ImportTargets Condition="%(PaketReferencesFileLinesInfo.Splits) &gt;= 8">$([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[7])</ImportTargets>
242242
</PaketReferencesFileLinesInfo>
243243
<PackageReference Include="%(PaketReferencesFileLinesInfo.PackageName)">
244244
<Version>%(PaketReferencesFileLinesInfo.PackageVersion)</Version>
245245
<PrivateAssets Condition=" ('%(PaketReferencesFileLinesInfo.AllPrivateAssets)' == 'true') Or ('$(PackAsTool)' == 'true') ">All</PrivateAssets>
246-
<ExcludeAssets Condition=" '%(PaketReferencesFileLinesInfo.Splits)' == '6' And %(PaketReferencesFileLinesInfo.CopyLocal) == 'false'">runtime</ExcludeAssets>
247-
<ExcludeAssets Condition=" '%(PaketReferencesFileLinesInfo.Splits)' != '6' And %(PaketReferencesFileLinesInfo.AllPrivateAssets) == 'exclude'">runtime</ExcludeAssets>
248-
<ExcludeAssets Condition=" '%(PaketReferencesFileLinesInfo.Splits)' != '6' And %(PaketReferencesFileLinesInfo.OmitContent) == 'true'">$(ExcludeAssets);contentFiles</ExcludeAssets>
249-
<ExcludeAssets Condition=" '%(PaketReferencesFileLinesInfo.Splits)' != '6' And %(PaketReferencesFileLinesInfo.ImportTargets) == 'false'">$(ExcludeAssets);build;buildMultitargeting;buildTransitive</ExcludeAssets>
246+
<ExcludeAssets Condition=" %(PaketReferencesFileLinesInfo.CopyLocal) == 'false' or %(PaketReferencesFileLinesInfo.AllPrivateAssets) == 'exclude'">runtime</ExcludeAssets>
247+
<ExcludeAssets Condition=" %(PaketReferencesFileLinesInfo.OmitContent) == 'true'">$(ExcludeAssets);contentFiles</ExcludeAssets>
248+
<ExcludeAssets Condition=" %(PaketReferencesFileLinesInfo.ImportTargets) == 'false'">$(ExcludeAssets);build;buildMultitargeting;buildTransitive</ExcludeAssets>
250249
<Publish Condition=" '$(PackAsTool)' == 'true' ">true</Publish>
251250
<AllowExplicitVersion>true</AllowExplicitVersion>
252251
</PackageReference>
@@ -366,9 +365,9 @@
366365
PackageLicenseFile="$(PackageLicenseFile)"
367366
PackageLicenseExpression="$(PackageLicenseExpression)"
368367
PackageLicenseExpressionVersion="$(PackageLicenseExpressionVersion)"
369-
PackageReadmeFile="$(PackageReadmeFile)"
370-
NoDefaultExcludes="$(NoDefaultExcludes) "/>
371-
368+
Readme="$(PackageReadmeFile)"
369+
NoDefaultExcludes="$(NoDefaultExcludes)"/>
370+
372371
<PackTask Condition="$(UseMSBuild16_0_Pack)"
373372
PackItem="$(PackProjectInputFile)"
374373
PackageFiles="@(_PackageFiles)"

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
# Changelog
8+
9+
## [0.54.1] - 2021-10-16
10+
11+
### Fixed
12+
13+
- Added more environment variable lookups for the `dotnet` binary, so modes like running under `dotnet test` should work more consistently.
14+
715
## [0.54.0] - 2021-08-08
816
### Added
917

src/Ionide.ProjInfo.Tool/Program.fs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@ type LoaderFunc = ToolsPath * list<string * string> -> IWorkspaceLoader
2424

2525
let parseProject (loaderFunc: LoaderFunc) (path: string) =
2626
let cwd = System.IO.Path.GetDirectoryName path |> System.IO.DirectoryInfo
27-
let toolsPath = Ionide.ProjInfo.Init.init cwd
27+
let toolsPath = Ionide.ProjInfo.Init.init cwd None
2828
let loader = loaderFunc (toolsPath, [])
2929
loader.LoadProjects([ path ], [], BinaryLogGeneration.Within cwd)
3030

3131
let parseSolution (loaderFunc: LoaderFunc) (path: string) =
3232
let cwd = System.IO.Path.GetDirectoryName path |> System.IO.DirectoryInfo
33-
let toolsPath = Ionide.ProjInfo.Init.init cwd
33+
let toolsPath = Ionide.ProjInfo.Init.init cwd None
3434
let loader = loaderFunc (toolsPath, [])
3535
loader.LoadSln(path, [], BinaryLogGeneration.Within cwd)
3636

src/Ionide.ProjInfo/Library.fs

Lines changed: 94 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -17,46 +17,95 @@ module SdkDiscovery =
1717
let internal msbuildForSdk (sdkPath: DirectoryInfo) =
1818
Path.Combine(sdkPath.FullName, "MSBuild.dll")
1919

20-
let private versionedPaths (root: string) =
21-
System.IO.Directory.EnumerateDirectories root
22-
|> Seq.choose
23-
(fun dir ->
24-
let dirName = Path.GetFileName dir
20+
type DotnetRuntimeInfo =
21+
{ RuntimeName: string
22+
Version: SemanticVersioning.Version
23+
Path: DirectoryInfo }
24+
25+
let private execDotnet (cwd: DirectoryInfo) (binaryFullPath: FileInfo) args =
26+
let info = ProcessStartInfo()
27+
info.WorkingDirectory <- cwd.FullName
28+
info.FileName <- binaryFullPath.FullName
2529

26-
match SemanticVersioning.Version.TryParse dirName with
27-
| true, v -> Some v
28-
| false, _ -> None)
29-
|> Seq.sortDescending
30-
|> Seq.map (fun v -> v, Path.Combine(root, string v) |> DirectoryInfo)
30+
for arg in args do
31+
info.ArgumentList.Add arg
3132

32-
/// Given the DOTNET_ROOT, that is the directory where the `dotnet` binary is present and the sdk/runtimes/etc are,
33-
/// enumerates the available SDKs in descending version order
34-
let sdks (dotnetDirectory: DirectoryInfo) =
35-
let sdksPath = Path.Combine(dotnetDirectory.FullName, "sdk")
36-
versionedPaths sdksPath
33+
info.RedirectStandardOutput <- true
34+
let p = System.Diagnostics.Process.Start(info)
35+
p.WaitForExit()
36+
37+
seq {
38+
while not p.StandardOutput.EndOfStream do
39+
yield p.StandardOutput.ReadLine()
40+
}
41+
42+
let private (|SemVer|_|) version =
43+
match SemanticVersioning.Version.TryParse version with
44+
| true, v -> Some v
45+
| false, _ -> None
46+
47+
let private (|SdkOutputDirectory|) (path: string) =
48+
path.TrimStart('[').TrimEnd(']') |> DirectoryInfo
49+
50+
let private (|RuntimeParts|_|) (line: string) =
51+
match line.IndexOf ' ' with
52+
| -1 -> None
53+
| n ->
54+
let runtimeName, rest = line.[0..n - 1], line.[n + 1..]
55+
56+
match rest.IndexOf ' ' with
57+
| -1 -> None
58+
| n -> Some(runtimeName, rest.[0..n - 1], rest.[n + 1..])
59+
60+
let private (|SdkParts|_|) (line: string) =
61+
match line.IndexOf ' ' with
62+
| -1 -> None
63+
| n -> Some(line.[0..n - 1], line.[n + 1..])
3764

3865
/// Given the DOTNET_ROOT, that is the directory where the `dotnet` binary is present and the sdk/runtimes/etc are,
3966
/// enumerates the available runtimes in descending version order
40-
let runtimes (dotnetDirectory: DirectoryInfo) =
41-
let netcoreAppPath = Path.Combine(dotnetDirectory.FullName, "shared", "Microsoft.NETCore.App")
42-
versionedPaths netcoreAppPath
67+
let runtimes (dotnetBinaryPath: FileInfo) : DotnetRuntimeInfo [] =
68+
execDotnet dotnetBinaryPath.Directory dotnetBinaryPath [ "--list-runtimes" ]
69+
|> Seq.choose
70+
(fun line ->
71+
match line with
72+
| RuntimeParts (runtimeName, SemVer version, SdkOutputDirectory path) ->
73+
Some
74+
{ RuntimeName = runtimeName
75+
Version = version
76+
Path = Path.Combine(path.FullName, string version) |> DirectoryInfo }
77+
| line -> None)
78+
|> Seq.toArray
79+
80+
type DotnetSdkInfo =
81+
{ Version: SemanticVersioning.Version
82+
Path: DirectoryInfo }
83+
84+
/// Given the DOTNET_sROOT, that is the directory where the `dotnet` binary is present and the sdk/runtimes/etc are,
85+
/// enumerates the available SDKs in descending version order
86+
let sdks (dotnetBinaryPath: FileInfo) : DotnetSdkInfo [] =
87+
execDotnet dotnetBinaryPath.Directory dotnetBinaryPath [ "--list-sdks" ]
88+
|> Seq.choose
89+
(fun line ->
90+
match line with
91+
| SdkParts (SemVer sdkVersion, SdkOutputDirectory path) ->
92+
Some
93+
{ Version = sdkVersion
94+
Path = Path.Combine(path.FullName, string sdkVersion) |> DirectoryInfo }
95+
| line -> None)
96+
|> Seq.toArray
4397

4498
/// performs a `dotnet --version` command at the given directory to get the version of the
4599
/// SDK active at that location.
46-
let versionAt (cwd: DirectoryInfo) =
47-
let exe = Paths.dotnetRoot
48-
let info = ProcessStartInfo()
49-
info.WorkingDirectory <- cwd.FullName
50-
info.FileName <- exe
51-
info.ArgumentList.Add("--version")
52-
info.RedirectStandardOutput <- true
53-
let p = System.Diagnostics.Process.Start(info)
54-
p.WaitForExit()
55-
let stdout = p.StandardOutput.ReadToEnd()
100+
let versionAt (cwd: DirectoryInfo) (dotnetBinaryPath: FileInfo) =
101+
execDotnet cwd dotnetBinaryPath [ "--version" ]
102+
|> Seq.head
103+
|> function
104+
| version ->
105+
match SemanticVersioning.Version.TryParse version with
106+
| true, v -> Ok v
107+
| false, _ -> Error(dotnetBinaryPath, [ "--version" ], cwd, version)
56108

57-
match SemanticVersioning.Version.TryParse stdout with
58-
| true, v -> Ok v
59-
| false, _ -> Error(exe, info.ArgumentList, cwd, stdout)
60109

61110
[<RequireQualifiedAccess>]
62111
module Init =
@@ -113,7 +162,7 @@ module Init =
113162

114163
match System.Environment.GetEnvironmentVariable "DOTNET_HOST_PATH" with
115164
| null
116-
| "" -> Environment.SetEnvironmentVariable("DOTNET_HOST_PATH", Paths.dotnetRoot)
165+
| "" -> Environment.SetEnvironmentVariable("DOTNET_HOST_PATH", Paths.dotnetRoot.FullName)
117166
| alreadySet -> ()
118167

119168
if resolveHandler <> null then
@@ -124,17 +173,20 @@ module Init =
124173

125174
/// Initialize the MsBuild integration. Returns path to MsBuild tool that was detected by Locator. Needs to be called before doing anything else.
126175
/// Call it again when the working directory changes.
127-
let init (workingDirectory: DirectoryInfo) =
128-
match SdkDiscovery.versionAt workingDirectory with
176+
let init (workingDirectory: DirectoryInfo) (dotnetExe: FileInfo option) =
177+
let exe = dotnetExe |> Option.defaultWith (fun _ -> Paths.dotnetRoot)
178+
179+
match SdkDiscovery.versionAt workingDirectory exe with
129180
| Ok dotnetSdkVersionAtPath ->
130-
let sdkVersion, sdkPath =
131-
SdkDiscovery.sdks (Path.GetDirectoryName Paths.dotnetRoot |> DirectoryInfo)
132-
|> Seq.skipWhile (fun (v, path) -> v > dotnetSdkVersionAtPath)
133-
|> Seq.head
134-
135-
let msbuild = SdkDiscovery.msbuildForSdk sdkPath
136-
setupForSdkVersion sdkPath
137-
ToolsPath msbuild
181+
let sdks = SdkDiscovery.sdks exe
182+
let sdkInfo: SdkDiscovery.DotnetSdkInfo option = sdks |> Array.skipWhile (fun { Version = v } -> v < dotnetSdkVersionAtPath) |> Array.tryHead
183+
184+
match sdkInfo with
185+
| Some sdkInfo ->
186+
let msbuild = SdkDiscovery.msbuildForSdk sdkInfo.Path
187+
setupForSdkVersion sdkInfo.Path
188+
ToolsPath msbuild
189+
| None -> failwithf $"Unable to get sdk versions at least from the string '{dotnetSdkVersionAtPath}'. This found sdks were {sdks |> Array.toList}"
138190
| 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}"
139191

140192
[<RequireQualifiedAccess>]

src/Ionide.ProjInfo/Utils.fs

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,48 @@
11
namespace Ionide.ProjInfo
22

3+
open System.Runtime.InteropServices
4+
open System.IO
5+
open System
6+
37
module Paths =
4-
/// provides the path to the `dotnet` binary running this library, duplicated from
5-
/// https://github.com/dotnet/sdk/blob/b91b88aec2684e3d2988df8d838d3aa3c6240a35/src/Cli/Microsoft.DotNet.Cli.Utils/Muxer.cs#L39
6-
let dotnetRoot =
7-
match System.Environment.GetEnvironmentVariable "DOTNET_HOST_PATH" with
8+
let private isUnix = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)
9+
10+
let private dotnetBinaryName =
11+
if isUnix then
12+
"dotnet"
13+
else
14+
"dotnet.exe"
15+
16+
let private potentialDotnetHostEnvVars =
17+
[ "DOTNET_HOST_PATH", id // is a full path to dotnet binary
18+
"DOTNET_ROOT", (fun s -> Path.Combine(s, dotnetBinaryName)) // needs dotnet binary appended
19+
"DOTNET_ROOT(x86)", (fun s -> Path.Combine(s, dotnetBinaryName)) ] // needs dotnet binary appended
20+
21+
let private existingEnvVarValue envVarValue =
22+
match envVarValue with
823
| null
9-
| "" ->
10-
System
11-
.Diagnostics
12-
.Process
13-
.GetCurrentProcess()
14-
.MainModule
15-
.FileName
16-
| alreadySet -> alreadySet
24+
| "" -> None
25+
| other -> Some other
26+
27+
/// <summary>
28+
/// 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>
29+
/// </summary>
30+
let dotnetRoot =
31+
potentialDotnetHostEnvVars
32+
|> List.tryPick
33+
(fun (envVar, transformer) ->
34+
match Environment.GetEnvironmentVariable envVar |> existingEnvVarValue with
35+
| Some varValue -> Some(transformer varValue |> FileInfo)
36+
| None -> None)
37+
|> Option.defaultWith
38+
(fun _ ->
39+
System
40+
.Diagnostics
41+
.Process
42+
.GetCurrentProcess()
43+
.MainModule
44+
.FileName
45+
|> FileInfo)
1746

1847
let sdksPath (dotnetRoot: string) =
1948
System.IO.Path.Combine(dotnetRoot, "Sdks")

test/Ionide.ProjInfo.Tests/Program.fs

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,7 @@ open Expecto.Logging
1111

1212
[<EntryPoint>]
1313
let main argv =
14-
let baseDir = System.Environment.GetEnvironmentVariable "DOTNET_ROOT"
15-
// need to set this because these tests aren't run directly via the `dotnet` binary
16-
let dotnetExe =
17-
if Environment.isMacOS || Environment.isUnix then
18-
"dotnet"
19-
else
20-
"dotnet.exe"
21-
22-
Environment.SetEnvironmentVariable("DOTNET_HOST_PATH", IO.Path.Combine(baseDir, dotnetExe))
23-
let toolsPath = Init.init (IO.DirectoryInfo Environment.CurrentDirectory)
14+
let toolsPath = Init.init (IO.DirectoryInfo Environment.CurrentDirectory) None
2415

2516
Tests.runTestsWithArgs
2617
{ defaultConfig with

test/Ionide.ProjInfo.Tests/Tests.fs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1034,4 +1034,12 @@ let tests toolsPath =
10341034
debugTets toolsPath "WorkspaceLoaderViaProjectGraph" WorkspaceLoaderViaProjectGraph.Create
10351035
//Binlog test
10361036
testSample2WithBinLog toolsPath "WorkspaceLoader" WorkspaceLoader.Create
1037-
testSample2WithBinLog toolsPath "WorkspaceLoaderViaProjectGraph" WorkspaceLoaderViaProjectGraph.Create ]
1037+
testSample2WithBinLog toolsPath "WorkspaceLoaderViaProjectGraph" WorkspaceLoaderViaProjectGraph.Create
1038+
test "can get runtimes" {
1039+
let runtimes = SdkDiscovery.runtimes Paths.dotnetRoot
1040+
Expect.isNonEmpty runtimes "should have found at least the currently-executing runtime"
1041+
}
1042+
test "can get sdks" {
1043+
let sdks = SdkDiscovery.sdks Paths.dotnetRoot
1044+
Expect.isNonEmpty sdks "should have found at least the currently-executing sdk"
1045+
} ]

0 commit comments

Comments
 (0)