Skip to content

Commit

Permalink
Merge pull request #2665 from mclark1129/bugfix/2641-resolve-local-sdk
Browse files Browse the repository at this point in the history
SdkAssemblyResolver: Use dotnet --version to resolve the correct SDK
  • Loading branch information
yazeedobaid authored Apr 25, 2022
2 parents 04235f0 + d472613 commit 562dfc6
Show file tree
Hide file tree
Showing 9 changed files with 1,908 additions and 136 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"sdk" : {
"version": "6.0.100"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#r "paket:
storage: none
source https://api.nuget.org/v3/index.json
source ../../../release/dotnetcore
nuget Fake.Runtime prerelease
nuget FSharp.Core prerelease"

open Fake.Runtime

printfn "Starting Build."
Trace.traceFAKE "Some Info from FAKE"
printfn "Ending Build."
18 changes: 9 additions & 9 deletions src/app/Fake.DotNet.Cli/DotNet.fs
Original file line number Diff line number Diff line change
Expand Up @@ -661,10 +661,8 @@ module DotNet =
// make sure to write global.json if we did not read the version from it
// We need to do this as the SDK will use this file to select the actual version
// See https://github.com/fsharp/FAKE/pull/1963 and related discussions
if File.Exists globalJsonPath then
let readVersion = getSDKVersionFromGlobalJsonDir workDir
if readVersion <> version then failwithf "Existing global.json with a different version found!"
false
if File.Exists globalJsonPath
then false
else
let template = sprintf """{ "sdk": { "version": "%s" } }""" version
File.WriteAllText(globalJsonPath, template)
Expand Down Expand Up @@ -1838,14 +1836,16 @@ module DotNet =
let args = Args.toWindowsCommandLine(buildTemplateUninstallArgs param)
let result = exec (fun _ -> param.Common) "new" args

// we will check if the uninstall command has returned error and message is template is not found.
// if that is the case, then we will just redirect output as success and change process result to
// exit code of zero.
// If the process returns error (exit code != 0) then check to see if a message is
// that the template was not found. If this message exists, assume the process
// completed with success
let templateIsNotFoundToUninstall =
result.Results
|> List.exists(fun (result:ConsoleMessage) -> result.Message.Contains $"The template package '{templateName}' is not found.")

match templateIsNotFoundToUninstall with
| true -> ignore ""
let success = (result.ExitCode = 0) || templateIsNotFoundToUninstall
match success with
| true -> ()
| false -> failwithf $"dotnet new --uninstall failed with code %i{result.ExitCode}"

__.MarkSuccess()
210 changes: 106 additions & 104 deletions src/app/Fake.Runtime/SdkAssemblyResolver.fs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ type SdkAssemblyResolver(logLevel:Trace.VerboseLevel) =
let CustomDotNetHostPath = Environment.environVarOrDefault "FAKE_SDK_RESOLVER_CUSTOM_DOTNET_PATH" ""
let RuntimeResolverResolveMethod = Environment.environVarOrDefault "FAKE_SDK_RESOLVER_RUNTIME_VERSION_RESOLVE_METHOD" ""


member this.LogLevel = logLevel

member this.SdkVersionRaw = "6.0"
Expand All @@ -50,97 +49,26 @@ type SdkAssemblyResolver(logLevel:Trace.VerboseLevel) =
| Some version -> ReleaseVersion(version).Major.Equals(this.SdkVersion.Major)
| None -> false

member this.TryResolveSdkRuntimeVersionFromNetwork() =
if this.LogLevel.PrintVerbose then
Trace.tracefn "Trying to resolve runtime version from network.."
try
let sdkVersionReleases =
ProductCollection.GetAsync()
|> Async.AwaitTask
|> Async.RunSynchronously
|> List.ofSeq
|> List.find (fun product -> product.ProductVersion.Equals(this.SdkVersionRaw))

sdkVersionReleases.GetReleasesAsync()
|> Async.AwaitTask
|> Async.RunSynchronously
|> List.ofSeq
|> Some
with ex ->
Trace.traceError $"Could not get SDK runtime version from network due to: {ex.Message}"
None

member this.TryResolveSdkRuntimeVersionFromCache() =
if this.LogLevel.PrintVerbose then
Trace.tracefn "Trying to resolve runtime version from cache.."
try
System.Reflection.Assembly.GetExecutingAssembly().Location
|> Path.GetDirectoryName
</> "cachedDotnetSdkReleases.json"
|> Product.GetReleasesAsync
|> Async.AwaitTask
|> Async.RunSynchronously
|> List.ofSeq
|> Some
with ex ->
Trace.traceError $"Could not get SDK runtime version from cache due to: {ex.Message}"
None

member this.ResolveSdkRuntimeVersion() =

let resolvedSdkVersion =
this.SdkVersionFromGlobalJson
|> Option.get
|> ReleaseVersion

let resolutionMethod =
match not(String.isNullOrEmpty RuntimeResolverResolveMethod) with
| true ->
// for testing only!
match RuntimeResolverResolveMethod = "cache" with
| true -> this.TryResolveSdkRuntimeVersionFromCache()
| false -> this.TryResolveSdkRuntimeVersionFromNetwork()
| false ->
// this is the default case, we will try the network, if we could not, then we will reach for cached file.
this.TryResolveSdkRuntimeVersionFromNetwork()
|> Option.orElseWith(fun _ ->
this.TryResolveSdkRuntimeVersionFromCache()
)


let resolved =
resolutionMethod
|> Option.orElseWith(fun _ ->
failwithf $"Could not find a suitable runtime version matching SDK version: {resolvedSdkVersion.ToString()}"
)
|> Option.get
|> List.tryFind
(fun release ->
release.Sdks
|> List.ofSeq
|> List.exists (fun sdk -> sdk.Version.Equals(resolvedSdkVersion)))
|> Option.get

if this.LogLevel.PrintVerbose then
Trace.tracefn $"resolved runtime version: {resolved.Runtime.Version.ToString()}"
resolved.Runtime.Version.ToString()

member this.SdkReferenceAssemblies() =
member this.DotNetBinaryName =
if Environment.isUnix then
"dotnet"
else
"dotnet.exe"

/// <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>.
/// Also probes the PATH and checks the default installation locations
/// </summary>
member this.ResolveDotNetRoot() =
let isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
let isMac = RuntimeInformation.IsOSPlatform(OSPlatform.OSX)
let isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux)
let isUnix = isLinux || isMac

let dotnetBinaryName =
if Environment.isUnix then
"dotnet"
else
"dotnet.exe"


let 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
"DOTNET_ROOT", (fun s -> Path.Combine(s, this.DotNetBinaryName)) // needs dotnet binary appended
"DOTNET_ROOT(x86)", (fun s -> Path.Combine(s, this.DotNetBinaryName)) ] // needs dotnet binary appended

let existingEnvVarValue envVarValue =
match envVarValue with
Expand Down Expand Up @@ -175,17 +103,17 @@ type SdkAssemblyResolver(logLevel:Trace.VerboseLevel) =
.GetEnvironmentVariable("PATH")
.Split(PATHSeparator, StringSplitOptions.RemoveEmptyEntries ||| StringSplitOptions.TrimEntries)
|> Array.tryPick (fun d ->
let fi = Path.Combine(d, dotnetBinaryName) |> FileInfo
let fi = Path.Combine(d, this.DotNetBinaryName) |> FileInfo

if fi.Exists then
Some fi
else
None)

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

let tryFindFile p =
let f = FileInfo p
Expand All @@ -204,22 +132,96 @@ type SdkAssemblyResolver(logLevel:Trace.VerboseLevel) =
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>.
/// Also probes the PATH and checks the default installation locations
/// </summary>
let dotnetRoot =
if not(String.isNullOrEmpty CustomDotNetHostPath) then
Some CustomDotNetHostPath
else
tryFindFromEnvVar ()
|> Option.orElseWith tryFindFromPATH
|> Option.orElseWith tryFindFromDefaultDirs
|> Option.bind resolveFile
|> Option.map (fun dotnetRoot -> dotnetRoot.Directory.FullName)
if not(String.isNullOrEmpty CustomDotNetHostPath) then
Some CustomDotNetHostPath
else
tryFindFromEnvVar ()
|> Option.orElseWith tryFindFromPATH
|> Option.orElseWith tryFindFromDefaultDirs
|> Option.bind resolveFile
|> Option.map (fun dotnetRoot -> dotnetRoot.Directory.FullName)

member this.TryResolveSdkRuntimeVersionFromNetwork() =
if this.LogLevel.PrintVerbose then
Trace.tracefn "Trying to resolve runtime version from network.."
try
let sdkVersionReleases =
ProductCollection.GetAsync()
|> Async.AwaitTask
|> Async.RunSynchronously
|> List.ofSeq
|> List.find (fun product -> product.ProductVersion.Equals(this.SdkVersionRaw))

sdkVersionReleases.GetReleasesAsync()
|> Async.AwaitTask
|> Async.RunSynchronously
|> List.ofSeq
|> Some
with ex ->
Trace.traceError $"Could not get SDK runtime version from network due to: {ex.Message}"
None

member this.TryResolveSdkRuntimeVersionFromCache() =
if this.LogLevel.PrintVerbose then
Trace.tracefn "Trying to resolve runtime version from cache.."
try
System.Reflection.Assembly.GetExecutingAssembly().Location
|> Path.GetDirectoryName
</> "cachedDotnetSdkReleases.json"
|> Product.GetReleasesAsync
|> Async.AwaitTask
|> Async.RunSynchronously
|> List.ofSeq
|> Some
with ex ->
Trace.traceError $"Could not get SDK runtime version from cache due to: {ex.Message}"
None

member this.GetProductReleaseForSdk (version: ReleaseVersion) =
let net60releases =
if RuntimeResolverResolveMethod = "cache" then
// for testing only!
this.TryResolveSdkRuntimeVersionFromCache ()
else
// this is the default case, we will try the network, if we could not, then we will reach for cached file.
this.TryResolveSdkRuntimeVersionFromNetwork ()
|> Option.orElseWith(this.TryResolveSdkRuntimeVersionFromCache)

let sdkRelease (release: ProductRelease) =
release.Sdks
|> List.ofSeq
|> List.exists (fun sdk -> sdk.Version.Equals(version))

net60releases |> Option.bind (List.tryFind sdkRelease)

member this.ResolveSdkRuntimeVersion() =
let versionOptions (options: DotNet.VersionOptions) =
// If a custom CLI path is provided, configure the version command
// to use that path. This really only accomodates a test scenarios
// in which FAKE_SDK_RESOLVER_CUSTOM_DOTNET_PATH is set.
match this.ResolveDotNetRoot() with
| Some root ->
options.WithCommon(fun common ->
{ common with DotNetCliPath = root </> this.DotNetBinaryName } )
| None -> options

let sdkVersion = DotNet.getVersion versionOptions |> ReleaseVersion

match this.GetProductReleaseForSdk sdkVersion with
| Some release ->
let version = release.Runtime.Version.ToString()
if this.LogLevel.PrintVerbose then
Trace.trace $"Resolved runtime version: {version}"

version

| None ->
failwithf $"Could not find a suitable .NET 6 runtime version matching SDK version: {sdkVersion.ToString()}"

member this.SdkReferenceAssemblies() =

let referenceAssembliesPath =
dotnetRoot
this.ResolveDotNetRoot()
|> Option.map (fun dotnetRoot ->
dotnetRoot
</> "packs"
Expand Down
Loading

0 comments on commit 562dfc6

Please sign in to comment.