mirror of
https://github.com/Smaug123/WoofWare.DotnetRuntimeLocator
synced 2025-10-04 23:08:42 +00:00
Add runtime lookup (#94)
This commit is contained in:
12
.github/workflows/dotnet.yaml
vendored
12
.github/workflows/dotnet.yaml
vendored
@@ -31,7 +31,7 @@ jobs:
|
|||||||
- name: Publish
|
- name: Publish
|
||||||
run: dotnet publish Example
|
run: dotnet publish Example
|
||||||
- name: Run example
|
- name: Run example
|
||||||
run: ".\\Example\\bin\\Release\\net8.0\\win-x64\\Example.exe"
|
run: ".\\Example\\bin\\Release\\net8.0\\Example.exe"
|
||||||
|
|
||||||
build:
|
build:
|
||||||
strategy:
|
strategy:
|
||||||
@@ -57,10 +57,14 @@ jobs:
|
|||||||
run: nix develop --command dotnet build --no-restore --configuration ${{matrix.config}}
|
run: nix develop --command dotnet build --no-restore --configuration ${{matrix.config}}
|
||||||
- name: Test
|
- name: Test
|
||||||
run: nix develop --command dotnet test --no-build --verbosity normal --configuration ${{matrix.config}}
|
run: nix develop --command dotnet test --no-build --verbosity normal --configuration ${{matrix.config}}
|
||||||
- name: Publish example
|
- name: Publish example self-contained
|
||||||
run: nix develop --command dotnet publish --no-build --verbosity normal --configuration ${{matrix.config}} Example
|
run: nix develop --command dotnet publish --self-contained --runtime linux-x64 --verbosity normal --configuration ${{matrix.config}} Example
|
||||||
- name: Run example self-contained
|
- name: Run example self-contained
|
||||||
run: "./Example/bin/${{matrix.config}}/*/*/Example"
|
run: "./Example/bin/${{matrix.config}}/net*/*-*/Example"
|
||||||
|
- name: Publish example non-self-contained
|
||||||
|
run: nix develop --command dotnet publish --verbosity normal --configuration ${{matrix.config}} Example
|
||||||
|
- name: Run example non-self-contained
|
||||||
|
run: "./Example/bin/${{matrix.config}}/net*/Example"
|
||||||
|
|
||||||
build-nix:
|
build-nix:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
@@ -3,7 +3,6 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<SelfContained>true</SelfContained>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
namespace Example
|
namespace Example
|
||||||
|
|
||||||
open System
|
open System
|
||||||
|
open System.IO
|
||||||
|
open System.Reflection
|
||||||
open WoofWare.DotnetRuntimeLocator
|
open WoofWare.DotnetRuntimeLocator
|
||||||
|
|
||||||
module Program =
|
module Program =
|
||||||
@@ -18,4 +20,16 @@ module Program =
|
|||||||
for f in info.Frameworks do
|
for f in info.Frameworks do
|
||||||
Console.WriteLine $"Framework: %O{f}"
|
Console.WriteLine $"Framework: %O{f}"
|
||||||
|
|
||||||
|
// Identify the runtime which would execute this DLL
|
||||||
|
let self = Assembly.GetExecutingAssembly().Location
|
||||||
|
let runtimeSearchDirs = DotnetRuntime.SelectForDll self
|
||||||
|
// For example, the System.Text.Json.dll which this DLL would load:
|
||||||
|
runtimeSearchDirs
|
||||||
|
|> Seq.tryPick (fun dir ->
|
||||||
|
let attempt = Path.Combine (dir, "System.Text.Json.dll")
|
||||||
|
if File.Exists attempt then Some attempt else None
|
||||||
|
)
|
||||||
|
|> Option.get
|
||||||
|
|> fun s -> Console.WriteLine $"System.Text.Json location: %s{s}"
|
||||||
|
|
||||||
0
|
0
|
||||||
|
@@ -11,6 +11,11 @@ See [the example](Example/Program.fs).
|
|||||||
let info = DotnetEnvironmentInfo.Get ()
|
let info = DotnetEnvironmentInfo.Get ()
|
||||||
// or, if you already know a path to the `dotnet` executable...
|
// or, if you already know a path to the `dotnet` executable...
|
||||||
let info = DotnetEnvironmentInfo.GetSpecific "/path/to/dotnet"
|
let info = DotnetEnvironmentInfo.GetSpecific "/path/to/dotnet"
|
||||||
|
|
||||||
|
// identify the directories containing the framework which would execute a given DLL
|
||||||
|
let dirsToSearch : string seq = DotnetRuntime.SelectForDll "/path/to/dll.dll"
|
||||||
|
// or, if you would be running with a specific `/path/to/dotnet exec /path/to/dll.dll`:
|
||||||
|
let dirsToSearch : string seq = DotnetRuntime.SelectForDll ("/path/to/dll.dll", "/path/to/dotnet")
|
||||||
```
|
```
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
307
WoofWare.DotnetRuntimeLocator/DotnetRuntime.cs
Normal file
307
WoofWare.DotnetRuntimeLocator/DotnetRuntime.cs
Normal file
@@ -0,0 +1,307 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace WoofWare.DotnetRuntimeLocator;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The result of a call to `DotnetRuntime.Select`.
|
||||||
|
/// This is `type DotnetRuntimeSelection = | Framework of DotnetEnvironmentFrameworkInfo | Sdk of
|
||||||
|
/// DotnetEnvironmentSdkInfo | Absent`.
|
||||||
|
/// </summary>
|
||||||
|
internal class DotnetRuntimeSelection
|
||||||
|
{
|
||||||
|
private readonly int _discriminator;
|
||||||
|
private readonly DotnetEnvironmentFrameworkInfo? _framework;
|
||||||
|
private readonly DotnetEnvironmentSdkInfo? _sdk;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The constructor which means "We found the right runtime, and it's from this framework".
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="framework">For example, </param>
|
||||||
|
public DotnetRuntimeSelection(DotnetEnvironmentFrameworkInfo framework)
|
||||||
|
{
|
||||||
|
_discriminator = 1;
|
||||||
|
_framework = framework;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The constructor which means "We found the right runtime, and it's from this SDK".
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sdk">For example, </param>
|
||||||
|
public DotnetRuntimeSelection(DotnetEnvironmentSdkInfo sdk)
|
||||||
|
{
|
||||||
|
_discriminator = 2;
|
||||||
|
_sdk = sdk;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The constructor which means "We were unable to find an appropriate runtime".
|
||||||
|
/// </summary>
|
||||||
|
public DotnetRuntimeSelection()
|
||||||
|
{
|
||||||
|
_discriminator = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Exhaustive match on this discriminated union.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="withFramework">If `this` is a `Framework`, call this continuation with its value.</param>
|
||||||
|
/// <param name="withSdk">If `this` is a `Sdk`, call this continuation with its value.</param>
|
||||||
|
/// <param name="withNone">If `this` represents the absence of a result, call this continuation.</param>
|
||||||
|
/// <returns>The result of the continuation which was called.</returns>
|
||||||
|
public TRet Visit<TRet>(Func<DotnetEnvironmentFrameworkInfo, TRet> withFramework,
|
||||||
|
Func<DotnetEnvironmentSdkInfo, TRet> withSdk,
|
||||||
|
Func<TRet> withNone)
|
||||||
|
{
|
||||||
|
return _discriminator switch
|
||||||
|
{
|
||||||
|
1 => withFramework.Invoke(_framework!),
|
||||||
|
2 => withSdk.Invoke(_sdk!),
|
||||||
|
3 => withNone.Invoke(),
|
||||||
|
_ => throw new InvalidOperationException($"unrecognised union discriminator %i{_discriminator}")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Module to hold methods for automatically identifying a .NET runtime.
|
||||||
|
/// </summary>
|
||||||
|
public static class DotnetRuntime
|
||||||
|
{
|
||||||
|
/// <returns>For each requested runtime in the RuntimeOptions, the resolved place in which to find that runtime.</returns>
|
||||||
|
private static IReadOnlyDictionary<string, DotnetRuntimeSelection> SelectRuntime(RuntimeOptions options,
|
||||||
|
DotnetEnvironmentInfo env)
|
||||||
|
{
|
||||||
|
var rollForwardEnvVar = Environment.GetEnvironmentVariable("DOTNET_ROLL_FORWARD");
|
||||||
|
RollForward rollForward;
|
||||||
|
if (rollForwardEnvVar == null)
|
||||||
|
{
|
||||||
|
rollForward = options.RollForward ?? RollForward.Minor;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!Enum.TryParse(rollForwardEnvVar, out rollForward))
|
||||||
|
throw new ArgumentException(
|
||||||
|
$"Unable to parse the value of environment variable DOTNET_ROLL_FORWARD, which was: {rollForwardEnvVar}");
|
||||||
|
}
|
||||||
|
|
||||||
|
IReadOnlyDictionary<string, Version> desiredVersions;
|
||||||
|
if (options.IncludedFrameworks == null)
|
||||||
|
{
|
||||||
|
if (options.Framework == null)
|
||||||
|
{
|
||||||
|
if (options.Frameworks == null)
|
||||||
|
throw new InvalidDataException(
|
||||||
|
"Expected runtimeconfig.json file to have either a framework or frameworks entry, but it had neither");
|
||||||
|
|
||||||
|
desiredVersions = options.Frameworks.Select(x => (x.Name, new Version(x.Version))).GroupBy(x => x.Name)
|
||||||
|
.Select(data =>
|
||||||
|
{
|
||||||
|
var versions = (IReadOnlyList<Version>)data.Select(datum => datum.Item2).ToList();
|
||||||
|
if (versions.Count != 1)
|
||||||
|
{
|
||||||
|
var description = string.Join(", ", versions.Select(x => x.ToString()));
|
||||||
|
throw new InvalidDataException(
|
||||||
|
$"Unexpectedly had not-exactly-one version desired for framework {data.Key}: {description}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (data.Key, versions[0]);
|
||||||
|
})
|
||||||
|
.ToDictionary();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var result = new Dictionary<string, Version>
|
||||||
|
{ { options.Framework.Name, new Version(options.Framework.Version) } };
|
||||||
|
desiredVersions = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
desiredVersions = options.IncludedFrameworks.Select(x => (x.Name, new Version(x.Version)))
|
||||||
|
.GroupBy(x => x.Name)
|
||||||
|
.Select(data =>
|
||||||
|
{
|
||||||
|
var versions = (IReadOnlyList<Version>)data.Select(datum => datum.Item2).ToList();
|
||||||
|
if (versions.Count != 1)
|
||||||
|
{
|
||||||
|
var description = string.Join(", ", versions.Select(x => x.ToString()));
|
||||||
|
throw new InvalidDataException(
|
||||||
|
$"Unexpectedly had not-exactly-one version desired for framework {data.Key}: {description}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (data.Key, versions[0]);
|
||||||
|
})
|
||||||
|
.ToDictionary();
|
||||||
|
}
|
||||||
|
|
||||||
|
IReadOnlyDictionary<string, IReadOnlyList<RuntimeOnDisk>> availableRuntimes = env
|
||||||
|
.Frameworks.SelectMany(availableFramework =>
|
||||||
|
{
|
||||||
|
var availableVersion = new Version(availableFramework.Version);
|
||||||
|
if (!desiredVersions.TryGetValue(availableFramework.Name, out var desiredVersion))
|
||||||
|
{
|
||||||
|
// we don't desire this framework at any version; skip it
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (availableVersion < desiredVersion)
|
||||||
|
{
|
||||||
|
// It's never desired to roll *backward*.
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return new List<(string, DotnetEnvironmentFrameworkInfo)>
|
||||||
|
{ (availableFramework.Name, availableFramework) };
|
||||||
|
}).GroupBy(x => x.Item1)
|
||||||
|
.Select(group =>
|
||||||
|
{
|
||||||
|
var grouping = group.Select(x => new RuntimeOnDisk(x.Item2, new Version(x.Item2.Version))).ToList();
|
||||||
|
return (group.Key, (IReadOnlyList<RuntimeOnDisk>)grouping);
|
||||||
|
})
|
||||||
|
.ToDictionary();
|
||||||
|
|
||||||
|
switch (rollForward)
|
||||||
|
{
|
||||||
|
case RollForward.Minor:
|
||||||
|
{
|
||||||
|
return desiredVersions.Select(desired =>
|
||||||
|
{
|
||||||
|
if (!availableRuntimes.TryGetValue(desired.Key, out var available))
|
||||||
|
{
|
||||||
|
return (desired.Key, new DotnetRuntimeSelection());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ReferenceEquals(available, null))
|
||||||
|
{
|
||||||
|
throw new NullReferenceException("logic error: contents of non-nullable dict can't be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there's a correct major and minor version, take the latest patch.
|
||||||
|
var correctMajorAndMinorVersion =
|
||||||
|
available.Where(data =>
|
||||||
|
data.InstalledVersion.Major == desired.Value.Major &&
|
||||||
|
data.InstalledVersion.Minor == desired.Value.Minor).ToList();
|
||||||
|
if (correctMajorAndMinorVersion.Count > 0)
|
||||||
|
{
|
||||||
|
return (desired.Key, new DotnetRuntimeSelection(correctMajorAndMinorVersion.MaxBy(v => v.InstalledVersion)!.Installed));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise roll forward to lowest higher minor version
|
||||||
|
var candidate = available.Where(data => data.InstalledVersion.Major == desired.Value.Major)
|
||||||
|
.MinBy(v => (v.InstalledVersion.Minor, -v.InstalledVersion.Build));
|
||||||
|
|
||||||
|
return (desired.Key, candidate == null ? new DotnetRuntimeSelection() : new DotnetRuntimeSelection(candidate.Installed));
|
||||||
|
}).ToDictionary();
|
||||||
|
}
|
||||||
|
case RollForward.Major:
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
case RollForward.LatestPatch:
|
||||||
|
{
|
||||||
|
return desiredVersions.Select(desired =>
|
||||||
|
{
|
||||||
|
var matches = availableRuntimes[desired.Key]
|
||||||
|
.Where(data =>
|
||||||
|
data.InstalledVersion.Minor == desired.Value.Minor &&
|
||||||
|
data.InstalledVersion.Major == desired.Value.Major).MaxBy(data => data.InstalledVersion);
|
||||||
|
return matches == null
|
||||||
|
? (desired.Key, new DotnetRuntimeSelection())
|
||||||
|
: (desired.Key, new DotnetRuntimeSelection(matches.Installed));
|
||||||
|
}).ToDictionary();
|
||||||
|
}
|
||||||
|
case RollForward.LatestMinor:
|
||||||
|
{
|
||||||
|
return desiredVersions.Select(desired =>
|
||||||
|
{
|
||||||
|
var matches = availableRuntimes[desired.Key]
|
||||||
|
.Where(data =>
|
||||||
|
data.InstalledVersion.Major == desired.Value.Major).MaxBy(data => data.InstalledVersion);
|
||||||
|
return matches == null
|
||||||
|
? (desired.Key, new DotnetRuntimeSelection())
|
||||||
|
: (desired.Key, new DotnetRuntimeSelection(matches.Installed));
|
||||||
|
}).ToDictionary();
|
||||||
|
}
|
||||||
|
case RollForward.LatestMajor:
|
||||||
|
{
|
||||||
|
return desiredVersions.Select(desired =>
|
||||||
|
{
|
||||||
|
var match = availableRuntimes[desired.Key].MaxBy(data => data.InstalledVersion);
|
||||||
|
return match == null ? (desired.Key, new DotnetRuntimeSelection()) : (desired.Key, new DotnetRuntimeSelection(match.Installed));
|
||||||
|
}).ToDictionary();
|
||||||
|
}
|
||||||
|
case RollForward.Disable:
|
||||||
|
{
|
||||||
|
return desiredVersions.Select(desired =>
|
||||||
|
{
|
||||||
|
var exactMatch = availableRuntimes[desired.Key]
|
||||||
|
.FirstOrDefault(available => available.InstalledVersion == desired.Value);
|
||||||
|
if (exactMatch != null)
|
||||||
|
{
|
||||||
|
return (desired.Key, new DotnetRuntimeSelection(exactMatch.Installed));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return (desired.Key, new DotnetRuntimeSelection());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
).ToDictionary();
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Given a .NET executable DLL, identify the most appropriate .NET runtime to run it.
|
||||||
|
/// This is pretty half-baked at the moment; test this yourself to make sure it does what you want it to!
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dllPath">Path to an OutputType=Exe .dll file.</param>
|
||||||
|
/// <param name="dotnet">
|
||||||
|
/// Path to the `dotnet` binary which you would use e.g. in `dotnet exec` to run the DLL specified by
|
||||||
|
/// `dllPath`.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// An ordered collection of folder paths. When resolving any particular DLL during the execution of the input
|
||||||
|
/// DLL, search these folders; if a DLL name appears in multiple of these folders, the earliest is correct for that
|
||||||
|
/// DLL.
|
||||||
|
/// </returns>
|
||||||
|
public static IReadOnlyList<string> SelectForDll(string dllPath, string? dotnet = null)
|
||||||
|
{
|
||||||
|
if (!dllPath.EndsWith(".dll", StringComparison.Ordinal))
|
||||||
|
throw new ArgumentException(
|
||||||
|
$"SelectForDll requires the input DLL to have the extension '.dll'; provided: {dllPath}");
|
||||||
|
|
||||||
|
var dll = new FileInfo(dllPath);
|
||||||
|
var dllParentDir = dll.Directory ?? throw new ArgumentException($"dll path {dllPath} had no parent");
|
||||||
|
var name = dll.Name.Substring(0, dll.Name.Length - ".dll".Length);
|
||||||
|
|
||||||
|
var configFilePath = Path.Combine(dllParentDir.FullName, $"{name}.runtimeconfig.json");
|
||||||
|
|
||||||
|
// It appears to be undocumented why this returns a nullable, and the Rider decompiler doesn't suggest there are
|
||||||
|
// any code paths where it can return null?
|
||||||
|
var runtimeConfig =
|
||||||
|
JsonSerializer.Deserialize<RuntimeConfig>(File.ReadAllText(configFilePath)) ??
|
||||||
|
throw new NullReferenceException($"Failed to parse contents of file {configFilePath} as a runtime config");
|
||||||
|
|
||||||
|
var availableRuntimes = dotnet == null
|
||||||
|
? DotnetEnvironmentInfo.Get()
|
||||||
|
: DotnetEnvironmentInfo.GetSpecific(new FileInfo(dotnet));
|
||||||
|
|
||||||
|
var runtimes = SelectRuntime(runtimeConfig.RuntimeOptions, availableRuntimes);
|
||||||
|
|
||||||
|
return runtimes.SelectMany(runtime => runtime.Value.Visit(framework => new[] { $"{framework.Path}/{framework.Version}" },
|
||||||
|
sdk => [sdk.Path],
|
||||||
|
() => []
|
||||||
|
)).Prepend(dllParentDir.FullName).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private record RuntimeOnDisk(
|
||||||
|
DotnetEnvironmentFrameworkInfo Installed,
|
||||||
|
Version InstalledVersion);
|
||||||
|
}
|
@@ -86,6 +86,12 @@ public record RuntimeOptions
|
|||||||
[JsonPropertyName("frameworks")]
|
[JsonPropertyName("frameworks")]
|
||||||
public IReadOnlyList<RuntimeConfigFramework>? Frameworks { get; init; }
|
public IReadOnlyList<RuntimeConfigFramework>? Frameworks { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This is a self-contained executable which has these framework entirely contained next to it.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("includedFrameworks")]
|
||||||
|
public IReadOnlyList<RuntimeConfigFramework>? IncludedFrameworks { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This application advertises that it's fine with running under this roll-forward.
|
/// This application advertises that it's fine with running under this roll-forward.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -45,6 +45,8 @@ WoofWare.DotnetRuntimeLocator.DotnetEnvironmentSdkInfo.Path [property]: string
|
|||||||
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentSdkInfo.set_Path [method]: string -> unit
|
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentSdkInfo.set_Path [method]: string -> unit
|
||||||
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentSdkInfo.set_Version [method]: string -> unit
|
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentSdkInfo.set_Version [method]: string -> unit
|
||||||
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentSdkInfo.Version [property]: string
|
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentSdkInfo.Version [property]: string
|
||||||
|
WoofWare.DotnetRuntimeLocator.DotnetRuntime inherit obj
|
||||||
|
WoofWare.DotnetRuntimeLocator.DotnetRuntime.SelectForDll [static method]: (string, string) -> string System.Collections.Generic.IReadOnlyList
|
||||||
WoofWare.DotnetRuntimeLocator.RollForward inherit System.Enum
|
WoofWare.DotnetRuntimeLocator.RollForward inherit System.Enum
|
||||||
WoofWare.DotnetRuntimeLocator.RollForward.Disable [static field]: WoofWare.DotnetRuntimeLocator.RollForward = Disable
|
WoofWare.DotnetRuntimeLocator.RollForward.Disable [static field]: WoofWare.DotnetRuntimeLocator.RollForward = Disable
|
||||||
WoofWare.DotnetRuntimeLocator.RollForward.LatestMajor [static field]: WoofWare.DotnetRuntimeLocator.RollForward = LatestMajor
|
WoofWare.DotnetRuntimeLocator.RollForward.LatestMajor [static field]: WoofWare.DotnetRuntimeLocator.RollForward = LatestMajor
|
||||||
@@ -79,13 +81,16 @@ WoofWare.DotnetRuntimeLocator.RuntimeOptions.Framework [property]: WoofWare.Dotn
|
|||||||
WoofWare.DotnetRuntimeLocator.RuntimeOptions.Frameworks [property]: WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework System.Collections.Generic.IReadOnlyList
|
WoofWare.DotnetRuntimeLocator.RuntimeOptions.Frameworks [property]: WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework System.Collections.Generic.IReadOnlyList
|
||||||
WoofWare.DotnetRuntimeLocator.RuntimeOptions.get_Framework [method]: unit -> WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework
|
WoofWare.DotnetRuntimeLocator.RuntimeOptions.get_Framework [method]: unit -> WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework
|
||||||
WoofWare.DotnetRuntimeLocator.RuntimeOptions.get_Frameworks [method]: unit -> WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework System.Collections.Generic.IReadOnlyList
|
WoofWare.DotnetRuntimeLocator.RuntimeOptions.get_Frameworks [method]: unit -> WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework System.Collections.Generic.IReadOnlyList
|
||||||
|
WoofWare.DotnetRuntimeLocator.RuntimeOptions.get_IncludedFrameworks [method]: unit -> WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework System.Collections.Generic.IReadOnlyList
|
||||||
WoofWare.DotnetRuntimeLocator.RuntimeOptions.get_RollForward [method]: unit -> WoofWare.DotnetRuntimeLocator.RollForward System.Nullable
|
WoofWare.DotnetRuntimeLocator.RuntimeOptions.get_RollForward [method]: unit -> WoofWare.DotnetRuntimeLocator.RollForward System.Nullable
|
||||||
WoofWare.DotnetRuntimeLocator.RuntimeOptions.get_Tfm [method]: unit -> string
|
WoofWare.DotnetRuntimeLocator.RuntimeOptions.get_Tfm [method]: unit -> string
|
||||||
|
WoofWare.DotnetRuntimeLocator.RuntimeOptions.IncludedFrameworks [property]: WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework System.Collections.Generic.IReadOnlyList
|
||||||
WoofWare.DotnetRuntimeLocator.RuntimeOptions.op_Equality [static method]: (WoofWare.DotnetRuntimeLocator.RuntimeOptions, WoofWare.DotnetRuntimeLocator.RuntimeOptions) -> bool
|
WoofWare.DotnetRuntimeLocator.RuntimeOptions.op_Equality [static method]: (WoofWare.DotnetRuntimeLocator.RuntimeOptions, WoofWare.DotnetRuntimeLocator.RuntimeOptions) -> bool
|
||||||
WoofWare.DotnetRuntimeLocator.RuntimeOptions.op_Inequality [static method]: (WoofWare.DotnetRuntimeLocator.RuntimeOptions, WoofWare.DotnetRuntimeLocator.RuntimeOptions) -> bool
|
WoofWare.DotnetRuntimeLocator.RuntimeOptions.op_Inequality [static method]: (WoofWare.DotnetRuntimeLocator.RuntimeOptions, WoofWare.DotnetRuntimeLocator.RuntimeOptions) -> bool
|
||||||
WoofWare.DotnetRuntimeLocator.RuntimeOptions.RollForward [property]: WoofWare.DotnetRuntimeLocator.RollForward System.Nullable
|
WoofWare.DotnetRuntimeLocator.RuntimeOptions.RollForward [property]: WoofWare.DotnetRuntimeLocator.RollForward System.Nullable
|
||||||
WoofWare.DotnetRuntimeLocator.RuntimeOptions.set_Framework [method]: WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework -> unit
|
WoofWare.DotnetRuntimeLocator.RuntimeOptions.set_Framework [method]: WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework -> unit
|
||||||
WoofWare.DotnetRuntimeLocator.RuntimeOptions.set_Frameworks [method]: WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework System.Collections.Generic.IReadOnlyList -> unit
|
WoofWare.DotnetRuntimeLocator.RuntimeOptions.set_Frameworks [method]: WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework System.Collections.Generic.IReadOnlyList -> unit
|
||||||
|
WoofWare.DotnetRuntimeLocator.RuntimeOptions.set_IncludedFrameworks [method]: WoofWare.DotnetRuntimeLocator.RuntimeConfigFramework System.Collections.Generic.IReadOnlyList -> unit
|
||||||
WoofWare.DotnetRuntimeLocator.RuntimeOptions.set_RollForward [method]: WoofWare.DotnetRuntimeLocator.RollForward System.Nullable -> unit
|
WoofWare.DotnetRuntimeLocator.RuntimeOptions.set_RollForward [method]: WoofWare.DotnetRuntimeLocator.RollForward System.Nullable -> unit
|
||||||
WoofWare.DotnetRuntimeLocator.RuntimeOptions.set_Tfm [method]: string -> unit
|
WoofWare.DotnetRuntimeLocator.RuntimeOptions.set_Tfm [method]: string -> unit
|
||||||
WoofWare.DotnetRuntimeLocator.RuntimeOptions.Tfm [property]: string
|
WoofWare.DotnetRuntimeLocator.RuntimeOptions.Tfm [property]: string
|
@@ -8,6 +8,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="TestDotnetRuntime.fs" />
|
||||||
<Compile Include="TestRuntimeConfigParse.fs"/>
|
<Compile Include="TestRuntimeConfigParse.fs"/>
|
||||||
<Compile Include="TestSurface.fs"/>
|
<Compile Include="TestSurface.fs"/>
|
||||||
<Compile Include="TestDotnetEnvironmentInfo.fs"/>
|
<Compile Include="TestDotnetEnvironmentInfo.fs"/>
|
||||||
|
36
WoofWare.DotnetRuntimeLocator/Test/TestDotnetRuntime.fs
Normal file
36
WoofWare.DotnetRuntimeLocator/Test/TestDotnetRuntime.fs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
namespace WoofWare.DotnetRuntimeLocator.Test
|
||||||
|
|
||||||
|
open System.IO
|
||||||
|
open System.Reflection
|
||||||
|
open NUnit.Framework
|
||||||
|
open WoofWare.DotnetRuntimeLocator
|
||||||
|
|
||||||
|
[<TestFixture>]
|
||||||
|
module TestDotnetRuntime =
|
||||||
|
|
||||||
|
let inline shouldBeSome (x : 'a option) : unit =
|
||||||
|
match x with
|
||||||
|
| None -> failwith "option was None"
|
||||||
|
| Some _ -> ()
|
||||||
|
|
||||||
|
let inline shouldBeNone (x : 'a option) : unit =
|
||||||
|
match x with
|
||||||
|
| Some x -> failwith $"expectd None, but option was Some %O{x}"
|
||||||
|
| None -> ()
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Test DotnetRuntime`` () =
|
||||||
|
let assy = Assembly.GetExecutingAssembly ()
|
||||||
|
let selectedRuntime = DotnetRuntime.SelectForDll assy.Location
|
||||||
|
|
||||||
|
let existsDll (name : string) =
|
||||||
|
selectedRuntime
|
||||||
|
|> Seq.tryPick (fun dir ->
|
||||||
|
let attempt = Path.Combine (dir, name)
|
||||||
|
if File.Exists attempt then Some attempt else None
|
||||||
|
)
|
||||||
|
|
||||||
|
existsDll "System.Private.CoreLib.dll" |> shouldBeSome
|
||||||
|
existsDll "System.Text.Json.dll" |> shouldBeSome
|
||||||
|
existsDll "Test.dll" |> shouldBeSome
|
||||||
|
existsDll "blah-de-blah.dll" |> shouldBeNone
|
@@ -18,6 +18,7 @@ module TestSurface =
|
|||||||
let ``Ensure public API is fully documented`` () =
|
let ``Ensure public API is fully documented`` () =
|
||||||
DocCoverage.assertFullyDocumented assembly
|
DocCoverage.assertFullyDocumented assembly
|
||||||
|
|
||||||
[<Test ; Explicit "Not yet published">]
|
[<Test>]
|
||||||
let ``Ensure version is monotonic`` () =
|
// https://github.com/nunit/nunit3-vs-adapter/issues/876
|
||||||
|
let ``EnsureVersionIsMonotonic`` () =
|
||||||
MonotonicVersion.validate assembly "WoofWare.DotnetRuntimeLocator"
|
MonotonicVersion.validate assembly "WoofWare.DotnetRuntimeLocator"
|
||||||
|
@@ -23,6 +23,7 @@
|
|||||||
<Compile Include="DotnetEnvironmentFrameworkInfo.cs"/>
|
<Compile Include="DotnetEnvironmentFrameworkInfo.cs"/>
|
||||||
<Compile Include="DotnetEnvironmentInfo.cs"/>
|
<Compile Include="DotnetEnvironmentInfo.cs"/>
|
||||||
<Compile Include="DotnetEnvironmentSdkInfo.cs"/>
|
<Compile Include="DotnetEnvironmentSdkInfo.cs"/>
|
||||||
|
<Compile Include="DotnetRuntime.cs" />
|
||||||
<Compile Include="InteropStructs.cs"/>
|
<Compile Include="InteropStructs.cs"/>
|
||||||
<Compile Include="Boilerplate.cs"/>
|
<Compile Include="Boilerplate.cs"/>
|
||||||
<Compile Include="RuntimeConfigOptions.cs"/>
|
<Compile Include="RuntimeConfigOptions.cs"/>
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"version": "0.2",
|
"version": "0.3",
|
||||||
"publicReleaseRefSpec": [
|
"publicReleaseRefSpec": [
|
||||||
"^refs/heads/main$"
|
"^refs/heads/main$"
|
||||||
],
|
],
|
||||||
|
@@ -48,10 +48,11 @@
|
|||||||
fantomas = dotnetTool null "fantomas" (builtins.fromJSON (builtins.readFile ./.config/dotnet-tools.json)).tools.fantomas.version (builtins.head (builtins.filter (elem: elem.pname == "fantomas") deps)).hash;
|
fantomas = dotnetTool null "fantomas" (builtins.fromJSON (builtins.readFile ./.config/dotnet-tools.json)).tools.fantomas.version (builtins.head (builtins.filter (elem: elem.pname == "fantomas") deps)).hash;
|
||||||
default = pkgs.buildDotnetModule {
|
default = pkgs.buildDotnetModule {
|
||||||
inherit pname version dotnet-sdk dotnet-runtime;
|
inherit pname version dotnet-sdk dotnet-runtime;
|
||||||
name = "WoofWare.Myriad.Plugins";
|
name = "WoofWare.DotnetRuntimeLocator";
|
||||||
src = ./.;
|
src = ./.;
|
||||||
projectFile = "./WoofWare.DotnetRuntimeLocator/WoofWare.DotnetRuntimeLocator.csproj";
|
projectFile = "./WoofWare.DotnetRuntimeLocator/WoofWare.DotnetRuntimeLocator.csproj";
|
||||||
testProjectFile = "./WoofWare.DotnetRuntimeLocator/Test/Test.fsproj";
|
testProjectFile = "./WoofWare.DotnetRuntimeLocator/Test/Test.fsproj";
|
||||||
|
disabledTests = ["WoofWare.DotnetRuntimeLocator.Test.TestSurface.EnsureVersionIsMonotonic"];
|
||||||
nugetDeps = ./nix/deps.json; # `nix build .#default.fetch-deps && ./result nix/deps.json`
|
nugetDeps = ./nix/deps.json; # `nix build .#default.fetch-deps && ./result nix/deps.json`
|
||||||
doCheck = true;
|
doCheck = true;
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user