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
|
||||
run: dotnet publish Example
|
||||
- name: Run example
|
||||
run: ".\\Example\\bin\\Release\\net8.0\\win-x64\\Example.exe"
|
||||
run: ".\\Example\\bin\\Release\\net8.0\\Example.exe"
|
||||
|
||||
build:
|
||||
strategy:
|
||||
@@ -57,10 +57,14 @@ jobs:
|
||||
run: nix develop --command dotnet build --no-restore --configuration ${{matrix.config}}
|
||||
- name: Test
|
||||
run: nix develop --command dotnet test --no-build --verbosity normal --configuration ${{matrix.config}}
|
||||
- name: Publish example
|
||||
run: nix develop --command dotnet publish --no-build --verbosity normal --configuration ${{matrix.config}} Example
|
||||
- name: Publish example self-contained
|
||||
run: nix develop --command dotnet publish --self-contained --runtime linux-x64 --verbosity normal --configuration ${{matrix.config}} Example
|
||||
- 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:
|
||||
runs-on: ubuntu-latest
|
||||
|
@@ -3,7 +3,6 @@
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<SelfContained>true</SelfContained>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@@ -1,6 +1,8 @@
|
||||
namespace Example
|
||||
namespace Example
|
||||
|
||||
open System
|
||||
open System.IO
|
||||
open System.Reflection
|
||||
open WoofWare.DotnetRuntimeLocator
|
||||
|
||||
module Program =
|
||||
@@ -18,4 +20,16 @@ module Program =
|
||||
for f in info.Frameworks do
|
||||
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
|
||||
|
@@ -11,6 +11,11 @@ See [the example](Example/Program.fs).
|
||||
let info = DotnetEnvironmentInfo.Get ()
|
||||
// or, if you already know a path to the `dotnet` executable...
|
||||
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
|
||||
|
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")]
|
||||
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>
|
||||
/// This application advertises that it's fine with running under this roll-forward.
|
||||
/// </summary>
|
||||
|
@@ -45,6 +45,8 @@ WoofWare.DotnetRuntimeLocator.DotnetEnvironmentSdkInfo.Path [property]: string
|
||||
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentSdkInfo.set_Path [method]: string -> unit
|
||||
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentSdkInfo.set_Version [method]: string -> unit
|
||||
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.Disable [static field]: WoofWare.DotnetRuntimeLocator.RollForward = Disable
|
||||
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.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_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_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_Inequality [static method]: (WoofWare.DotnetRuntimeLocator.RuntimeOptions, WoofWare.DotnetRuntimeLocator.RuntimeOptions) -> bool
|
||||
WoofWare.DotnetRuntimeLocator.RuntimeOptions.RollForward [property]: WoofWare.DotnetRuntimeLocator.RollForward System.Nullable
|
||||
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_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_Tfm [method]: string -> unit
|
||||
WoofWare.DotnetRuntimeLocator.RuntimeOptions.Tfm [property]: string
|
@@ -8,6 +8,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="TestDotnetRuntime.fs" />
|
||||
<Compile Include="TestRuntimeConfigParse.fs"/>
|
||||
<Compile Include="TestSurface.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`` () =
|
||||
DocCoverage.assertFullyDocumented assembly
|
||||
|
||||
[<Test ; Explicit "Not yet published">]
|
||||
let ``Ensure version is monotonic`` () =
|
||||
[<Test>]
|
||||
// https://github.com/nunit/nunit3-vs-adapter/issues/876
|
||||
let ``EnsureVersionIsMonotonic`` () =
|
||||
MonotonicVersion.validate assembly "WoofWare.DotnetRuntimeLocator"
|
||||
|
@@ -23,6 +23,7 @@
|
||||
<Compile Include="DotnetEnvironmentFrameworkInfo.cs"/>
|
||||
<Compile Include="DotnetEnvironmentInfo.cs"/>
|
||||
<Compile Include="DotnetEnvironmentSdkInfo.cs"/>
|
||||
<Compile Include="DotnetRuntime.cs" />
|
||||
<Compile Include="InteropStructs.cs"/>
|
||||
<Compile Include="Boilerplate.cs"/>
|
||||
<Compile Include="RuntimeConfigOptions.cs"/>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "0.2",
|
||||
"version": "0.3",
|
||||
"publicReleaseRefSpec": [
|
||||
"^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;
|
||||
default = pkgs.buildDotnetModule {
|
||||
inherit pname version dotnet-sdk dotnet-runtime;
|
||||
name = "WoofWare.Myriad.Plugins";
|
||||
name = "WoofWare.DotnetRuntimeLocator";
|
||||
src = ./.;
|
||||
projectFile = "./WoofWare.DotnetRuntimeLocator/WoofWare.DotnetRuntimeLocator.csproj";
|
||||
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`
|
||||
doCheck = true;
|
||||
};
|
||||
|
Reference in New Issue
Block a user