Initial commit

This commit is contained in:
Smaug123
2024-06-07 18:31:16 +01:00
parent 5bb3642433
commit b1d65b33d8
29 changed files with 1393 additions and 0 deletions

12
.config/dotnet-tools.json Normal file
View File

@@ -0,0 +1,12 @@
{
"version": 1,
"isRoot": true,
"tools": {
"fantomas": {
"version": "6.3.8",
"commands": [
"fantomas"
]
}
}
}

40
.editorconfig Normal file
View File

@@ -0,0 +1,40 @@
root = true
[*]
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 4
# ReSharper properties
resharper_xml_indent_size = 2
resharper_xml_max_line_length = 100
resharper_xml_tab_width = 2
[*.{csproj,fsproj,sqlproj,targets,props,ts,tsx,css,json}]
indent_style = space
indent_size = 2
[*.{fs,fsi}]
fsharp_bar_before_discriminated_union_declaration = true
fsharp_space_before_uppercase_invocation = true
fsharp_space_before_class_constructor = true
fsharp_space_before_member = true
fsharp_space_before_colon = true
fsharp_space_before_semicolon = true
fsharp_multiline_bracket_style = aligned
fsharp_newline_between_type_definition_and_members = true
fsharp_align_function_signature_to_indentation = true
fsharp_alternative_long_member_definitions = true
fsharp_multi_line_lambda_closing_newline = true
fsharp_experimental_keep_indent_in_branch = true
fsharp_max_value_binding_width = 80
fsharp_max_record_width = 0
max_line_length = 120
end_of_line = lf
[*.{appxmanifest,build,dtd,nuspec,xaml,xamlx,xoml,xsd}]
indent_style = space
indent_size = 2
tab_width = 2

1
.envrc Normal file
View File

@@ -0,0 +1 @@
use flake

1
.fantomasignore Normal file
View File

@@ -0,0 +1 @@
.direnv/

3
.github/CODEOWNERS vendored Normal file
View File

@@ -0,0 +1,3 @@
# See: https://help.github.com/articles/about-codeowners/
* @G-Research/rqf @G-Research/gr-oss

8
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,8 @@
# yaml-language-server: $schema=https://json.schemastore.org/dependabot-2.0.json
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"

217
.github/workflows/dotnet.yaml vendored Normal file
View File

@@ -0,0 +1,217 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/SchemaStore/schemastore/master/src/schemas/json/github-workflow.json
name: .NET
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
env:
DOTNET_NOLOGO: true
DOTNET_CLI_TELEMETRY_OPTOUT: true
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
NUGET_XMLDOC_MODE: ''
DOTNET_MULTILEVEL_LOOKUP: 0
jobs:
build-windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
- uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: Restore dependencies
run: dotnet restore
- name: Test
run: dotnet test
- name: Run example
run: ".\\Example\\bin\\Release\\net8.0\\win-x64\\Example.exe"
build:
strategy:
matrix:
config:
- Release
- Debug
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
- name: Install Nix
uses: cachix/install-nix-action@V27
with:
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
- name: Restore dependencies
run: nix develop --command dotnet restore
- name: Build
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}}
build-nix:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Nix
uses: cachix/install-nix-action@V27
with:
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
- name: Build
run: nix build
check-dotnet-format:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Nix
uses: cachix/install-nix-action@V27
with:
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
- name: Run Fantomas
run: nix run .#fantomas -- --check .
check-nix-format:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Nix
uses: cachix/install-nix-action@V27
with:
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
- name: Run Alejandra
run: nix develop --command alejandra --check .
linkcheck:
name: Check links
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Install Nix
uses: cachix/install-nix-action@V27
with:
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
- name: Run link checker
run: nix develop --command markdown-link-check README.md
flake-check:
name: Check flake
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Install Nix
uses: cachix/install-nix-action@V27
with:
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
- name: Flake check
run: nix flake check
nuget-pack:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
- name: Install Nix
uses: cachix/install-nix-action@V27
with:
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
- name: Restore dependencies
run: nix develop --command dotnet restore
- name: Build
run: nix develop --command dotnet build --no-restore --configuration Release
- name: Pack
run: nix develop --command dotnet pack --configuration Release
- name: Upload NuGet artifact
uses: actions/upload-artifact@v4
with:
name: nuget-package
path: WoofWare.DotnetRuntimeLocator/bin/Release/WoofWare.DotnetRuntimeLocator.*.nupkg
expected-pack:
needs: [nuget-pack]
runs-on: ubuntu-latest
steps:
- name: Download NuGet artifact
uses: actions/download-artifact@v4
with:
name: nuget-package
path: packed
- name: Check NuGet contents
# Verify that there is exactly one nupkg in the artifact that would be NuGet published
run: if [[ $(find packed -maxdepth 1 -name 'WoofWare.DotnetRuntimeLocator.*.nupkg' -printf c | wc -c) -ne "1" ]]; then exit 1; fi
github-release-dry-run:
needs: [nuget-pack]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Download NuGet artifact
uses: actions/download-artifact@v4
with:
name: nuget-package
- name: Tag and release
env:
DRY_RUN: 1
GITHUB_TOKEN: mock-token
run: sh .github/workflows/tag.sh
all-required-checks-complete:
needs: [check-dotnet-format, check-nix-format, build, build-nix, linkcheck, flake-check, nuget-pack, expected-pack, github-release-dry-run]
runs-on: ubuntu-latest
steps:
- run: echo "All required checks complete."
nuget-publish:
runs-on: ubuntu-latest
if: ${{ !github.event.repository.fork && github.ref == 'refs/heads/main' }}
needs: [all-required-checks-complete]
environment: main-deploy
steps:
- uses: actions/checkout@v4
- name: Install Nix
uses: cachix/install-nix-action@V27
with:
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
- name: Download NuGet artifact
uses: actions/download-artifact@v4
with:
name: nuget-package
path: packed
- name: Publish to NuGet
run: nix develop --command dotnet nuget push "packed/WoofWare.DotnetRuntimeLocator.*.nupkg" --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate
github-release:
runs-on: ubuntu-latest
if: ${{ !github.event.repository.fork && github.ref == 'refs/heads/main' }}
needs: [all-required-checks-complete]
environment: main-deploy
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Download NuGet artifact
uses: actions/download-artifact@v4
with:
name: nuget-package
- name: Tag and release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: sh .github/workflows/tag.sh

120
.github/workflows/tag.sh vendored Normal file
View File

@@ -0,0 +1,120 @@
#!/bin/bash
echo "Dry-run? $DRY_RUN!"
find . -maxdepth 1 -type f ! -name "$(printf "*\n*")" -name '*.nupkg' | while IFS= read -r file
do
tag=$(basename "$file" .nupkg)
git tag "$tag"
${DRY_RUN:+echo} git push origin "$tag"
done
export TAG
TAG=$(find . -maxdepth 1 -type f -name 'WoofWare.DotnetRuntimeLocator.*.nupkg' -exec sh -c 'basename "$1" .nupkg' shell {} \; | grep -v Attributes)
case "$TAG" in
*"
"*)
echo "Error: TAG contains a newline; multiple plugins found."
exit 1
;;
esac
# target_commitish empty indicates the repo default branch
curl_body='{"tag_name":"'"$TAG"'","target_commitish":"","name":"'"$TAG"'","draft":false,"prerelease":false,"generate_release_notes":false}'
echo "cURL body: $curl_body"
failed_output=$(cat <<'EOF'
{
"message": "Validation Failed",
"errors": [
{
"resource": "Release",
"code": "already_exists",
"field": "tag_name"
}
],
"documentation_url": "https://docs.github.com/rest/releases/releases#create-a-release"
}
EOF
)
success_output=$(cat <<'EOF'
{
"url": "https://api.github.com/repos/Smaug123/WoofWare.DotnetRuntimeLocator/releases/158152116",
"assets_url": "https://api.github.com/repos/Smaug123/WoofWare.DotnetRuntimeLocator/releases/158152116/assets",
"upload_url": "https://uploads.github.com/repos/Smaug123/WoofWare.DotnetRuntimeLocator/releases/158152116/assets{?name,label}",
"html_url": "https://github.com/Smaug123/WoofWare.DotnetRuntimeLocator/releases/tag/WoofWare.DotnetRuntimeLocator.2.1.30",
"id": 158152116,
"author": {
"login": "github-actions[bot]",
"id": 41898282,
"node_id": "MDM6Qm90NDE4OTgyODI=",
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/github-actions%5Bbot%5D",
"html_url": "https://github.com/apps/github-actions",
"followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
"following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
"gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
"starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
"organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
"repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
"events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
"received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
"type": "Bot",
"site_admin": false
},
"node_id": "RE_kwDOJfksgc4JbTW0",
"tag_name": "WoofWare.DotnetRuntimeLocator.2.1.30",
"target_commitish": "main",
"name": "WoofWare.DotnetRuntimeLocator.2.1.30",
"draft": false,
"prerelease": false,
"created_at": "2024-05-30T11:00:55Z",
"published_at": "2024-05-30T11:03:02Z",
"assets": [
],
"tarball_url": "https://api.github.com/repos/Smaug123/WoofWare.DotnetRuntimeLocator/tarball/WoofWare.DotnetRuntimeLocator.2.1.30",
"zipball_url": "https://api.github.com/repos/Smaug123/WoofWare.DotnetRuntimeLocator/zipball/WoofWare.DotnetRuntimeLocator.2.1.30",
"body": null
}
EOF
)
HANDLE_OUTPUT=''
handle_error() {
ERROR_OUTPUT="$1"
exit_message=$(echo "$ERROR_OUTPUT" | jq -r --exit-status 'if .errors | length == 1 then .errors[0].code else null end')
if [ "$exit_message" = "already_exists" ] ; then
HANDLE_OUTPUT="Did not create GitHub release because it already exists at this version."
else
echo "Unexpected error output from curl: $(cat curl_output.json)"
echo "JQ output: $(exit_message)"
exit 2
fi
}
run_tests() {
handle_error "$failed_output"
if [ "$HANDLE_OUTPUT" != "Did not create GitHub release because it already exists at this version." ]; then
echo "Bad output from handler: $HANDLE_OUTPUT"
exit 3
fi
HANDLE_OUTPUT=''
echo "Tests passed."
}
run_tests
if [ "$DRY_RUN" != 1 ] ; then
if curl --fail-with-body -L -X POST -H "Accept: application/vnd.github+json" -H "Authorization: Bearer $GITHUB_TOKEN" -H "X-GitHub-Api-Version: 2022-11-28" https://api.github.com/repos/Smaug123/WoofWare.DotnetRuntimeLocator/releases -d "$curl_body" > curl_output.json; then
echo "Curl succeeded."
else
handle_error "$(cat curl_output.json)"
echo "$HANDLE_OUTPUT"
fi
fi

12
.gitignore vendored Normal file
View File

@@ -0,0 +1,12 @@
bin/
obj/
/packages/
riderModule.iml
/_ReSharper.Caches/
.idea/
*.sln.DotSettings.user
.DS_Store
result
.analyzerpackages/
analysis.sarif
.direnv/

16
Directory.Build.props Normal file
View File

@@ -0,0 +1,16 @@
<Project>
<PropertyGroup>
<DebugType Condition=" '$(DebugType)' == '' ">embedded</DebugType>
<Deterministic>true</Deterministic>
<NetCoreTargetingPackRoot>[UNDEFINED]</NetCoreTargetingPackRoot>
<DisableImplicitLibraryPacksFolder>true</DisableImplicitLibraryPacksFolder>
<DisableImplicitNuGetFallbackFolder>true</DisableImplicitNuGetFallbackFolder>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<DebugType>embedded</DebugType>
<WarnOn>FS3388,FS3559</WarnOn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Nerdbank.GitVersioning" Version="3.6.133" PrivateAssets="all"/>
<SourceLinkGitHubHost Include="github.com" ContentUrl="https://raw.githubusercontent.com"/>
</ItemGroup>
</Project>

17
Example/Example.fsproj Normal file
View File

@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<SelfContained>true</SelfContained>
</PropertyGroup>
<ItemGroup>
<Compile Include="Program.fs"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\WoofWare.DotnetRuntimeLocator\WoofWare.DotnetRuntimeLocator.csproj" />
</ItemGroup>
</Project>

21
Example/Program.fs Normal file
View File

@@ -0,0 +1,21 @@
namespace Example
open System
open WoofWare.DotnetRuntimeLocator
module Program =
[<EntryPoint>]
let main argv =
let info = DotnetEnvironmentInfo.Get ()
Console.WriteLine info
Console.WriteLine ("SDKs:")
for sdk in info.Sdks do
Console.WriteLine $"SDK: %O{sdk}"
Console.WriteLine ("Frameworks:")
for f in info.Frameworks do
Console.WriteLine $"Framework: %O{f}"
0

14
README.md Normal file
View File

@@ -0,0 +1,14 @@
# WoofWare.DotnetRuntimeLocator
Helpers to locate the .NET runtime and SDKs programmatically.
(If you're parsing `dotnet --list-runtimes`, you're doing it wrong!)
## Usage
See [the example](Example/Program.fs).
```fsharp
let info = DotnetEnvironmentInfo.Get ()
// or, if you already know a path to the `dotnet` executable...
let info = DotnetEnvironmentInfo.Get "/path/to/dotnet"
```

View File

@@ -0,0 +1,34 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WoofWare.DotnetRuntimeLocator", "WoofWare.DotnetRuntimeLocator\WoofWare.DotnetRuntimeLocator.csproj", "{6AEC4B30-8AFE-4071-A5FB-DBA6EB2D8194}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Test", "WoofWare.DotnetRuntimeLocator\Test\Test.fsproj", "{7123924E-E6C5-4612-9E2E-2C4B8D14C7B2}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Example", "Example\Example.fsproj", "{C295BC67-F932-4225-9183-7173B26E1F9E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{6AEC4B30-8AFE-4071-A5FB-DBA6EB2D8194}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6AEC4B30-8AFE-4071-A5FB-DBA6EB2D8194}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6AEC4B30-8AFE-4071-A5FB-DBA6EB2D8194}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6AEC4B30-8AFE-4071-A5FB-DBA6EB2D8194}.Release|Any CPU.Build.0 = Release|Any CPU
{7123924E-E6C5-4612-9E2E-2C4B8D14C7B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7123924E-E6C5-4612-9E2E-2C4B8D14C7B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7123924E-E6C5-4612-9E2E-2C4B8D14C7B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7123924E-E6C5-4612-9E2E-2C4B8D14C7B2}.Release|Any CPU.Build.0 = Release|Any CPU
{C295BC67-F932-4225-9183-7173B26E1F9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C295BC67-F932-4225-9183-7173B26E1F9E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C295BC67-F932-4225-9183-7173B26E1F9E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C295BC67-F932-4225-9183-7173B26E1F9E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,28 @@
namespace System.Runtime.CompilerServices
{
internal class RequiredMemberAttribute : Attribute
{
}
internal class CompilerFeatureRequiredAttribute : Attribute
{
public CompilerFeatureRequiredAttribute(string name)
{
}
}
}
namespace System.Diagnostics.CodeAnalysis
{
[AttributeUsage(AttributeTargets.Constructor)]
internal sealed class SetsRequiredMembersAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
internal static class IsExternalInit
{
}
}

View File

@@ -0,0 +1,29 @@
using System.IO;
using System.Runtime.InteropServices;
namespace WoofWare.DotnetRuntimeLocator;
/// <summary>
/// Information about a single instance of the .NET runtime.
/// </summary>
/// <param name="Name">The name of this runtime, e.g. "Microsoft.NETCore.App"</param>
/// <param name="Path">
/// The path to this runtime, e.g. "/usr/bin/dotnet/shared/Microsoft.AspNetCore.App" (I'm guessing at
/// the prefix here, I use Nix so my paths are all different)
/// </param>
/// <param name="Version">The version of this runtime, e.g. "8.0.5"</param>
public record DotnetEnvironmentFrameworkInfo(string Name, string Path, string Version)
{
internal static DotnetEnvironmentFrameworkInfo FromNative(
InteropStructs.DotnetEnvironmentFrameworkInfoNative native)
{
if (native.size < 0 || native.size > int.MaxValue)
throw new InvalidDataException("size field did not fit in an int");
var size = (int)native.size;
if (size != Marshal.SizeOf(native))
throw new InvalidDataException($"size field {size} did not match expected size {Marshal.SizeOf(native)}");
return new DotnetEnvironmentFrameworkInfo(native.name, native.path, native.version);
}
}

View File

@@ -0,0 +1,231 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
namespace WoofWare.DotnetRuntimeLocator;
/// <summary>
/// Information known to `dotnet` about what frameworks and runtimes are available.
/// </summary>
/// <param name="HostFxrVersion">Version of the runtime, e.g. "8.0.5"</param>
/// <param name="HostFxrCommitHash">
/// A commit hash of the .NET runtime (as of this writing, this is probably a hash from
/// https://github.com/dotnet/runtime).
/// </param>
/// <param name="Sdks">Collection of .NET SDKs we were able to find.</param>
/// <param name="Frameworks">Collection of .NET runtimes we were able to find.</param>
public record DotnetEnvironmentInfo(
string HostFxrVersion,
string HostFxrCommitHash,
IReadOnlyList<DotnetEnvironmentSdkInfo> Sdks,
IReadOnlyList<DotnetEnvironmentFrameworkInfo> Frameworks)
{
private static readonly Lazy<FileInfo> HostFxr = new(() =>
{
// First, we might be self-contained: try and find it next to us.
var selfContainedAttempt = Directory.GetParent(Assembly.GetExecutingAssembly().Location);
if (selfContainedAttempt != null)
{
var attempt = selfContainedAttempt.EnumerateFiles("*hostfxr*").FirstOrDefault();
if (attempt != null) return attempt;
}
var runtimeDir = new DirectoryInfo(RuntimeEnvironment.GetRuntimeDirectory());
var parent1 = runtimeDir.Parent ??
throw new Exception("Unable to locate the host/fxr directory in the .NET runtime");
var parent2 = parent1.Parent ??
throw new Exception("Unable to locate the host/fxr directory in the .NET runtime");
var parent3 = parent2.Parent ??
throw new Exception("Unable to locate the host/fxr directory in the .NET runtime");
var fxrDir = new DirectoryInfo(Path.Combine(parent3.FullName, "host", "fxr"));
return fxrDir.EnumerateDirectories().First().EnumerateFiles("*hostfxr*").First();
});
private static FileInfo ResolveAllSymlinks(FileInfo f)
{
while (!ReferenceEquals(null, f.LinkTarget))
{
var parent = f.Directory ?? new DirectoryInfo("/");
f = new FileInfo(Path.Combine(parent.FullName, f.LinkTarget));
}
return f;
}
/// <summary>
/// Takes a DotnetEnvironmentInfoNative and a return location, which must fit a DotnetEnvironmentInfo.
/// Renders the DotnetEnvironmentInfoNative and stores it in the return location.
/// </summary>
private static void StoreResult(IntPtr envInfo, IntPtr retLoc)
{
var toRet = FromNativeConstructor.FromNative(
Marshal.PtrToStructure<InteropStructs.DotnetEnvironmentInfoNative>(envInfo));
var handle = GCHandle.FromIntPtr(retLoc);
handle.Target = toRet;
}
private static unsafe DotnetEnvironmentInfo CallDelegate(string? dotnetExePath, RuntimeDelegate f)
{
byte[]? dotnet = null;
if (dotnetExePath != null)
{
dotnet = Encoding.ASCII.GetBytes(dotnetExePath);
}
fixed (byte* dotnetPath = dotnet)
{
DotnetEnvironmentInfo? toRet = null;
var handle = GCHandle.Alloc(toRet);
try
{
var del = (StoreResultDelegate)StoreResult;
var callback = Marshal.GetFunctionPointerForDelegate(del);
var rc = f.Invoke((IntPtr)dotnetPath, IntPtr.Zero, callback, GCHandle.ToIntPtr(handle));
if (rc != 0) throw new Exception($"Could not obtain .NET environment information (exit code: {rc})");
if (ReferenceEquals(null, handle.Target))
throw new NullReferenceException(
"Unexpectedly failed to populate DotnetEnvironmentInfo, despite the native call succeeding.");
return (DotnetEnvironmentInfo)handle.Target;
}
finally
{
handle.Free();
}
}
}
/// <summary>
/// Get the environment information that is available to the specified `dotnet` executable.
/// </summary>
/// <param name="dotnetExe">A `dotnet` (or `dotnet.exe`) executable, e.g. one from /usr/bin/dotnet. Set this to null if you want us to just do our best.</param>
/// <returns>Information about the environment available to the given executable.</returns>
/// <exception cref="Exception">Throws on any failure; handles nothing gracefully.</exception>
public static DotnetEnvironmentInfo GetSpecific(FileInfo? dotnetExe)
{
var hostFxr = HostFxr.Value;
var lib = NativeLibrary.Load(hostFxr.FullName);
try
{
var ptr = NativeLibrary.GetExport(lib, "hostfxr_get_dotnet_environment_info");
if (ptr == IntPtr.Zero) throw new Exception("Unable to load function from native library");
var f = Marshal.GetDelegateForFunctionPointer<RuntimeDelegate>(ptr);
string? dotnetParent = null;
if (dotnetExe != null)
{
var dotnetNoSymlinks = ResolveAllSymlinks(dotnetExe);
var parent = dotnetNoSymlinks.Directory;
if (parent != null)
{
dotnetParent = parent.FullName;
}
}
return CallDelegate(dotnetParent, f);
}
finally
{
NativeLibrary.Free(lib);
}
}
private static FileInfo? FindDotnetAbove(DirectoryInfo path)
{
while (true)
{
var candidate = Path.Combine(path.FullName, "dotnet");
if (File.Exists(candidate)) return new FileInfo(candidate);
if (ReferenceEquals(path.Parent, null)) return null;
path = path.Parent;
}
}
/// <summary>
/// Get the environment information that is available to some arbitrary `dotnet` executable we were able to find.
/// </summary>
/// <returns>Information about the environment available to `dotnet`.</returns>
/// <exception cref="Exception">Throws on any failure; handles nothing gracefully.</exception>
public static DotnetEnvironmentInfo Get()
{
var dotnetExe = FindDotnetAbove(new DirectoryInfo(RuntimeEnvironment.GetRuntimeDirectory()));
if (ReferenceEquals(dotnetExe, null))
{
// This can happen! Maybe we're self-contained.
return GetSpecific(null);
}
return GetSpecific(dotnetExe);
}
/// <summary>
/// The signature of hostfxr_get_dotnet_environment_info.
/// Its implementation is
/// https://github.com/dotnet/runtime/blob/2dba5a3587de19160fb09129dcd3d7a4089b67b5/src/native/corehost/fxr/hostfxr.cpp#L357
/// Takes:
/// * The ASCII-encoded path to the directory which contains the `dotnet` executable
/// * A structure which is reserved for future use and which must currently be null
/// * A pointer to a callback which takes two arguments: a DotnetEnvironmentInfoNative
/// (https://github.com/dotnet/runtime/blob/2dba5a3587de19160fb09129dcd3d7a4089b67b5/src/native/corehost/hostfxr.h#L311)
/// and a context object you supplied.
/// This callback is represented by the type `StoreResultDelegate`.
/// * A pointer to the context object you want to consume in the callback.
/// Returns zero on success.
/// </summary>
internal delegate int RuntimeDelegate(IntPtr pathToDotnetExeDirectory, IntPtr mustBeNull, IntPtr outputCallback,
IntPtr outputArg);
/// <summary>
/// The callback which you pass to RuntimeDelegate.
/// Takes:
/// * a DotnetEnvironmentInfoNative
/// (https://github.com/dotnet/runtime/blob/2dba5a3587de19160fb09129dcd3d7a4089b67b5/src/native/corehost/hostfxr.h#L311)
/// * a context object, which is up to you to define and to pass into the RuntimeDelegate.
/// </summary>
internal delegate void StoreResultDelegate(IntPtr envInfo, IntPtr retLoc);
}
internal class FromNativeConstructor
{
internal static DotnetEnvironmentInfo FromNative(InteropStructs.DotnetEnvironmentInfoNative native)
{
if (native.size < 0 || native.size > int.MaxValue)
throw new InvalidDataException("size field did not fit in an int");
var size = (int)native.size;
if (native.framework_count < 0 || native.framework_count > int.MaxValue)
throw new InvalidDataException("framework_count field did not fit in an int");
var frameworkCount = (int)native.framework_count;
if (native.sdk_count < 0 || native.sdk_count > int.MaxValue)
throw new InvalidDataException("sdk_count field did not fit in an int");
var sdkCount = (int)native.sdk_count;
if (size != Marshal.SizeOf(native))
throw new InvalidDataException($"size field {size} did not match expected size {Marshal.SizeOf(native)}");
var frameworks = new List<DotnetEnvironmentFrameworkInfo>((int)native.framework_count);
for (var i = 0; i < frameworkCount; i++)
{
var frameworkInfo = new IntPtr(native.frameworks.ToInt64() +
i * Marshal.SizeOf<InteropStructs.DotnetEnvironmentFrameworkInfoNative>());
frameworks.Add(DotnetEnvironmentFrameworkInfo.FromNative(
Marshal.PtrToStructure<InteropStructs.DotnetEnvironmentFrameworkInfoNative>(frameworkInfo)));
}
var sdks = new List<DotnetEnvironmentSdkInfo>((int)native.sdk_count);
for (var i = 0; i < sdkCount; i++)
{
var sdkInfo = new IntPtr(native.sdks.ToInt64() +
i * Marshal.SizeOf<InteropStructs.DotnetEnvironmentSdkInfoNative>());
sdks.Add(DotnetEnvironmentSdkInfo.FromNative(
Marshal.PtrToStructure<InteropStructs.DotnetEnvironmentSdkInfoNative>(sdkInfo)));
}
return new DotnetEnvironmentInfo(native.hostfxr_version, native.hostfxr_commit_hash, sdks, frameworks);
}
}

View File

@@ -0,0 +1,27 @@
using System.IO;
using System.Runtime.InteropServices;
namespace WoofWare.DotnetRuntimeLocator;
/// <summary>
/// Information about a single instance of the .NET SDK.
/// </summary>
/// <param name="Path">
/// The path to this SDK, e.g. "/usr/bin/dotnet/sdk/8.0.300" (I'm guessing at the prefix there, I use
/// Nix so my paths are different)
/// </param>
/// <param name="Version">e.g. "8.0.300"</param>
public record DotnetEnvironmentSdkInfo(string Path, string Version)
{
internal static DotnetEnvironmentSdkInfo FromNative(InteropStructs.DotnetEnvironmentSdkInfoNative native)
{
if (native.size < 0 || native.size > int.MaxValue)
throw new InvalidDataException("size field did not fit in an int");
var size = (int)native.size;
if (size != Marshal.SizeOf(native))
throw new InvalidDataException($"size field {size} did not match expected size {Marshal.SizeOf(native)}");
return new DotnetEnvironmentSdkInfo(native.path, native.version);
}
}

View File

@@ -0,0 +1,46 @@
using System;
using System.Runtime.InteropServices;
namespace WoofWare.DotnetRuntimeLocator;
internal static class InteropStructs
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal struct DotnetEnvironmentSdkInfoNative
{
public nuint size;
public string version;
public string path;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal struct DotnetEnvironmentFrameworkInfoNative
{
public nuint size;
public string name;
public string version;
public string path;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal struct DotnetEnvironmentInfoNative
{
public nuint size;
public string hostfxr_version;
public string hostfxr_commit_hash;
public nuint sdk_count;
/// <summary>
/// Pointer to an array of DotnetEnvironmentSdkInfoNative, of length `sdk_count`
/// </summary>
public IntPtr sdks;
public nuint framework_count;
/// <summary>
/// Pointer to an array of DotnetEnvironmentFrameworkInfoNative, of length `framework_count`
/// </summary>
public IntPtr frameworks;
}
}

View File

@@ -0,0 +1,47 @@
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentFrameworkInfo inherit obj, implements WoofWare.DotnetRuntimeLocator.DotnetEnvironmentFrameworkInfo System.IEquatable
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentFrameworkInfo..ctor [constructor]: (string, string, string)
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentFrameworkInfo.<Clone>$ [method]: unit -> WoofWare.DotnetRuntimeLocator.DotnetEnvironmentFrameworkInfo
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentFrameworkInfo.Deconstruct [method]: (System.String&, System.String&, System.String&) -> unit
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentFrameworkInfo.get_Name [method]: unit -> string
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentFrameworkInfo.get_Path [method]: unit -> string
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentFrameworkInfo.get_Version [method]: unit -> string
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentFrameworkInfo.Name [property]: string
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentFrameworkInfo.op_Equality [static method]: (WoofWare.DotnetRuntimeLocator.DotnetEnvironmentFrameworkInfo, WoofWare.DotnetRuntimeLocator.DotnetEnvironmentFrameworkInfo) -> bool
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentFrameworkInfo.op_Inequality [static method]: (WoofWare.DotnetRuntimeLocator.DotnetEnvironmentFrameworkInfo, WoofWare.DotnetRuntimeLocator.DotnetEnvironmentFrameworkInfo) -> bool
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentFrameworkInfo.Path [property]: string
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentFrameworkInfo.set_Name [method]: string -> unit
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentFrameworkInfo.set_Path [method]: string -> unit
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentFrameworkInfo.set_Version [method]: string -> unit
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentFrameworkInfo.Version [property]: string
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentInfo inherit obj, implements WoofWare.DotnetRuntimeLocator.DotnetEnvironmentInfo System.IEquatable
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentInfo..ctor [constructor]: (string, string, WoofWare.DotnetRuntimeLocator.DotnetEnvironmentSdkInfo System.Collections.Generic.IReadOnlyList, WoofWare.DotnetRuntimeLocator.DotnetEnvironmentFrameworkInfo System.Collections.Generic.IReadOnlyList)
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentInfo.<Clone>$ [method]: unit -> WoofWare.DotnetRuntimeLocator.DotnetEnvironmentInfo
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentInfo.Deconstruct [method]: (System.String&, System.String&, System.Collections.Generic.IReadOnlyList, System.Collections.Generic.IReadOnlyList) -> unit
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentInfo.Frameworks [property]: WoofWare.DotnetRuntimeLocator.DotnetEnvironmentFrameworkInfo System.Collections.Generic.IReadOnlyList
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentInfo.Get [static method]: unit -> WoofWare.DotnetRuntimeLocator.DotnetEnvironmentInfo
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentInfo.get_Frameworks [method]: unit -> WoofWare.DotnetRuntimeLocator.DotnetEnvironmentFrameworkInfo System.Collections.Generic.IReadOnlyList
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentInfo.get_HostFxrCommitHash [method]: unit -> string
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentInfo.get_HostFxrVersion [method]: unit -> string
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentInfo.get_Sdks [method]: unit -> WoofWare.DotnetRuntimeLocator.DotnetEnvironmentSdkInfo System.Collections.Generic.IReadOnlyList
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentInfo.GetSpecific [static method]: System.IO.FileInfo -> WoofWare.DotnetRuntimeLocator.DotnetEnvironmentInfo
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentInfo.HostFxrCommitHash [property]: string
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentInfo.HostFxrVersion [property]: string
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentInfo.op_Equality [static method]: (WoofWare.DotnetRuntimeLocator.DotnetEnvironmentInfo, WoofWare.DotnetRuntimeLocator.DotnetEnvironmentInfo) -> bool
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentInfo.op_Inequality [static method]: (WoofWare.DotnetRuntimeLocator.DotnetEnvironmentInfo, WoofWare.DotnetRuntimeLocator.DotnetEnvironmentInfo) -> bool
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentInfo.Sdks [property]: WoofWare.DotnetRuntimeLocator.DotnetEnvironmentSdkInfo System.Collections.Generic.IReadOnlyList
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentInfo.set_Frameworks [method]: WoofWare.DotnetRuntimeLocator.DotnetEnvironmentFrameworkInfo System.Collections.Generic.IReadOnlyList -> unit
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentInfo.set_HostFxrCommitHash [method]: string -> unit
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentInfo.set_HostFxrVersion [method]: string -> unit
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentInfo.set_Sdks [method]: WoofWare.DotnetRuntimeLocator.DotnetEnvironmentSdkInfo System.Collections.Generic.IReadOnlyList -> unit
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentSdkInfo inherit obj, implements WoofWare.DotnetRuntimeLocator.DotnetEnvironmentSdkInfo System.IEquatable
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentSdkInfo..ctor [constructor]: (string, string)
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentSdkInfo.<Clone>$ [method]: unit -> WoofWare.DotnetRuntimeLocator.DotnetEnvironmentSdkInfo
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentSdkInfo.Deconstruct [method]: (System.String&, System.String&) -> unit
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentSdkInfo.get_Path [method]: unit -> string
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentSdkInfo.get_Version [method]: unit -> string
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentSdkInfo.op_Equality [static method]: (WoofWare.DotnetRuntimeLocator.DotnetEnvironmentSdkInfo, WoofWare.DotnetRuntimeLocator.DotnetEnvironmentSdkInfo) -> bool
WoofWare.DotnetRuntimeLocator.DotnetEnvironmentSdkInfo.op_Inequality [static method]: (WoofWare.DotnetRuntimeLocator.DotnetEnvironmentSdkInfo, WoofWare.DotnetRuntimeLocator.DotnetEnvironmentSdkInfo) -> bool
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

View File

@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<Compile Include="TestSurface.fs" />
<Compile Include="TestDotnetEnvironmentInfo.fs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="ApiSurface" Version="4.0.40" />
<PackageReference Include="FsUnit" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0"/>
<PackageReference Include="NUnit" Version="4.1.0"/>
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\WoofWare.DotnetRuntimeLocator.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,19 @@
namespace WoofWare.DotnetRuntimeLocator.Test
open System
open FsUnitTyped
open WoofWare.DotnetRuntimeLocator
open NUnit.Framework
[<TestFixture>]
module TestDotnetEnvironmentInfo =
[<Test>]
let ``Can locate the runtime`` () =
let runtimes = DotnetEnvironmentInfo.Get ()
// In the test setup, there should be an SDK!
runtimes.Sdks |> Seq.length |> shouldBeGreaterThan 0
runtimes.Frameworks |> Seq.length |> shouldBeGreaterThan 0
Console.WriteLine $"%O{runtimes}"

View File

@@ -0,0 +1,23 @@
namespace WoofWare.DotnetRuntimeLocator.Test
open NUnit.Framework
open ApiSurface
[<TestFixture>]
module TestSurface =
let assembly = typeof<WoofWare.DotnetRuntimeLocator.DotnetEnvironmentInfo>.Assembly
[<Test>]
let ``Ensure API surface has not been modified`` () = ApiSurface.assertIdentical assembly
[<Test ; Explicit>]
let ``Update API surface`` () =
ApiSurface.writeAssemblyBaseline assembly
[<Test>]
let ``Ensure public API is fully documented`` () =
DocCoverage.assertFullyDocumented assembly
[<Test ; Explicit "Not yet published">]
let ``Ensure version is monotonic`` () =
MonotonicVersion.validate assembly "WoofWare.DotnetRuntimeLocator"

View File

@@ -0,0 +1,40 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
<EnableDefaultItems>false</EnableDefaultItems>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<Authors>Patrick Stevens</Authors>
<Copyright>Copyright (c) Patrick Stevens 2024</Copyright>
<Description>Helpers to locate the .NET runtime and SDKs</Description>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/Smaug123/WoofWare.DotnetRuntimeLocator</RepositoryUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageIcon>logo.png</PackageIcon>
<PackageTags>runtime;locate;sdk;list-runtimes;list-sdks</PackageTags>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<Compile Include="DotnetEnvironmentFrameworkInfo.cs"/>
<Compile Include="DotnetEnvironmentInfo.cs"/>
<Compile Include="DotnetEnvironmentSdkInfo.cs"/>
<Compile Include="InteropStructs.cs"/>
<Compile Include="Boilerplate.cs"/>
<EmbeddedResource Include="SurfaceBaseline.txt"/>
<EmbeddedResource Include="version.json"/>
<None Include="..\README.md">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
<None Include="logo.png">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>
</Project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

View File

@@ -0,0 +1,10 @@
{
"version": "0.1",
"publicReleaseRefSpec": null,
"pathFilters": [
"^Test/",
":/WoofWare.DotnetRuntimeLocator",
":/Directory.Build.props",
":/README.md"
]
}

61
flake.lock generated Normal file
View File

@@ -0,0 +1,61 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1717646450,
"narHash": "sha256-KE+UmfSVk5PG8jdKdclPVcMrUB8yVZHbsjo7ZT1Bm3c=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "818dbe2f96df233d2041739d6079bb616d3e5597",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

66
flake.nix Normal file
View File

@@ -0,0 +1,66 @@
{
description = "Utilities to help you identify available .NET runtimes";
inputs = {
flake-utils.url = "github:numtide/flake-utils";
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
};
outputs = {
nixpkgs,
flake-utils,
...
}:
flake-utils.lib.eachDefaultSystem (system: let
pkgs = nixpkgs.legacyPackages.${system};
pname = "WoofWare.DotnetRuntimeLocator";
dotnet-sdk = pkgs.dotnet-sdk_8;
dotnet-runtime = pkgs.dotnetCorePackages.runtime_8_0;
version = "0.1";
dotnetTool = dllOverride: toolName: toolVersion: sha256:
pkgs.stdenvNoCC.mkDerivation rec {
name = toolName;
version = toolVersion;
nativeBuildInputs = [pkgs.makeWrapper];
src = pkgs.fetchNuGet {
pname = name;
version = version;
sha256 = sha256;
installPhase = ''mkdir -p $out/bin && cp -r tools/net6.0/any/* $out/bin'';
};
installPhase = let
dll =
if isNull dllOverride
then name
else dllOverride;
in ''
runHook preInstall
mkdir -p "$out/lib"
cp -r ./bin/* "$out/lib"
makeWrapper "${dotnet-runtime}/bin/dotnet" "$out/bin/${name}" --add-flags "$out/lib/${dll}.dll"
runHook postInstall
'';
};
in {
packages = {
fantomas = dotnetTool null "fantomas" (builtins.fromJSON (builtins.readFile ./.config/dotnet-tools.json)).tools.fantomas.version (builtins.head (builtins.filter (elem: elem.pname == "fantomas") ((import ./nix/deps.nix) {fetchNuGet = x: x;}))).sha256;
default = pkgs.buildDotnetModule {
inherit pname version dotnet-sdk dotnet-runtime;
name = "WoofWare.Myriad.Plugins";
src = ./.;
projectFile = "./WoofWare.DotnetRuntimeLocator/WoofWare.DotnetRuntimeLocator.csproj";
testProjectFile = "./WoofWare.DotnetRuntimeLocator/Test/Test.fsproj";
nugetDeps = ./nix/deps.nix; # `nix build .#default.passthru.fetch-deps && ./result` and put the result here
doCheck = true;
};
};
devShell = pkgs.mkShell {
buildInputs = [dotnet-sdk];
packages = [
pkgs.alejandra
pkgs.nodePackages.markdown-link-check
pkgs.shellcheck
];
};
});
}

224
nix/deps.nix Normal file
View File

@@ -0,0 +1,224 @@
# This file was automatically generated by passthru.fetch-deps.
# Please dont edit it manually, your changes might get overwritten!
{fetchNuGet}: [
(fetchNuGet {
pname = "ApiSurface";
version = "4.0.40";
sha256 = "1c9z0b6minlripwrjmv4yd5w8zj4lcpak4x41izh7ygx8kgmbvx0";
})
(fetchNuGet {
pname = "fantomas";
version = "6.3.8";
sha256 = "0qfgx08br57sigb8vmpkx9vzsf5bgl86ax7rv4q373ikx3kyrmkl";
})
(fetchNuGet {
pname = "FSharp.Core";
version = "8.0.300";
sha256 = "158xxr9hnhz2ibyzzp2d249angvxfc58ifflm4g3hz8qx9zxaq04";
})
(fetchNuGet {
pname = "FsUnit";
version = "6.0.0";
sha256 = "18q3p0z155znwj1l0qq3vq9nh9wl2i4mlfx4pmrnia4czr0xdkmb";
})
(fetchNuGet {
pname = "Microsoft.AspNetCore.App.Ref";
version = "6.0.31";
sha256 = "0hki4z9x60vzcg53s8cxnig4g1xnpqcj629r2cg5q1xw0sknfp5d";
})
(fetchNuGet {
pname = "Microsoft.AspNetCore.App.Runtime.linux-arm64";
version = "6.0.31";
sha256 = "0blf8hl2irl9r9x6f7cih87ps21rcs3b8r09z5wp7jcb5j1cv8fg";
})
(fetchNuGet {
pname = "Microsoft.AspNetCore.App.Runtime.linux-x64";
version = "6.0.31";
sha256 = "050dzfy49c4jwcm8dfrz2lqbbyhmgnq485zdhpcnc3w08z0ppbs6";
})
(fetchNuGet {
pname = "Microsoft.AspNetCore.App.Runtime.osx-arm64";
version = "6.0.31";
sha256 = "0w4sab66rjjyar9z139ls6rx29gvgj3rp3cbrsc8z00y9mw2sl22";
})
(fetchNuGet {
pname = "Microsoft.AspNetCore.App.Runtime.osx-x64";
version = "6.0.31";
sha256 = "13kww7x35926wik32z8cnvzhpqp3dwhazkzb569v87x8yww56n3k";
})
(fetchNuGet {
pname = "Microsoft.CodeCoverage";
version = "17.10.0";
sha256 = "0s0v7jmrq85n356xv7zixvwa4z94fszjcr5vll8x4im1a2lp00f9";
})
(fetchNuGet {
pname = "Microsoft.NET.Test.Sdk";
version = "17.10.0";
sha256 = "13g8fwl09li8fc71nk13dgkb7gahd4qhamyg2xby7am63nlchhdf";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Host.linux-arm64";
version = "6.0.31";
sha256 = "05s1c6bd4592xhy0y3w0cjckg11hb4pci729v59k3i3hl0hbad4s";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Host.linux-x64";
version = "6.0.31";
sha256 = "10s0p30qzfn9zibp1ldnqar87hqs47ni3rwqpvwx4jn3589cl9sn";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Host.osx-arm64";
version = "6.0.31";
sha256 = "0sah1gf2lccc93n3lmkgiahlz4jwr02cw20bvcwqyikpldy2awds";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Host.osx-x64";
version = "6.0.31";
sha256 = "0k16h1fwnvhw1gcx8ib01bidhrls5m56fiy6wldk3ajgs5dif8i6";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Ref";
version = "6.0.31";
sha256 = "19a4ainxj8jxij7ckglbmlnvrjxp72xfgx0r6lbglzh9dhsakwm7";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Runtime.linux-arm64";
version = "6.0.31";
sha256 = "1wmlwzy9bc1fs38r0vpn3ragp8pkimcq6sicj978yhk7brn52z1p";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Runtime.linux-x64";
version = "6.0.31";
sha256 = "0pw2n3j6vbmbghda1cvkhi3c39a49xk0a4w059mfya017adl6kac";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Runtime.osx-arm64";
version = "6.0.31";
sha256 = "115c220p0mbk30biaw0sfqprnaghks7lcvvz6n5rsg0kn4fvy7qs";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Runtime.osx-x64";
version = "6.0.31";
sha256 = "1cl561dgdk4mj48zw5xwg7a0cafkx8j2wjd4nlv0x3di300k75k5";
})
(fetchNuGet {
pname = "Microsoft.NETCore.Platforms";
version = "2.0.0";
sha256 = "1fk2fk2639i7nzy58m9dvpdnzql4vb8yl8vr19r2fp8lmj9w2jr0";
})
(fetchNuGet {
pname = "Microsoft.TestPlatform.ObjectModel";
version = "17.10.0";
sha256 = "07j69cw8r39533w4p39mnj00kahazz38760in3jfc45kmlcdb26x";
})
(fetchNuGet {
pname = "Microsoft.TestPlatform.TestHost";
version = "17.10.0";
sha256 = "1bl471s7fx9jycr0cc8rylwf34mrvlg9qn1an6l86nisavfcyb7v";
})
(fetchNuGet {
pname = "Nerdbank.GitVersioning";
version = "3.6.133";
sha256 = "1cdw8krvsnx0n34f7fm5hiiy7bs6h3asvncqcikc0g46l50w2j80";
})
(fetchNuGet {
pname = "Newtonsoft.Json";
version = "13.0.1";
sha256 = "0fijg0w6iwap8gvzyjnndds0q4b8anwxxvik7y8vgq97dram4srb";
})
(fetchNuGet {
pname = "Newtonsoft.Json";
version = "13.0.3";
sha256 = "0xrwysmrn4midrjal8g2hr1bbg38iyisl0svamb11arqws4w2bw7";
})
(fetchNuGet {
pname = "NuGet.Common";
version = "6.10.0";
sha256 = "0nizrnilmlcqbm945293h8q3wfqfchb4xi8g50x4kjn0rbpd1kbh";
})
(fetchNuGet {
pname = "NuGet.Configuration";
version = "6.10.0";
sha256 = "1aqaknaawnqx4mnvx9qw73wvj48jjzv0d78dzwl7m9zjlrl9myhz";
})
(fetchNuGet {
pname = "NuGet.Frameworks";
version = "6.10.0";
sha256 = "0hrd8y31zx9a0wps49czw0qgbrakb49zn3abfgylc9xrq990zkqk";
})
(fetchNuGet {
pname = "NuGet.Packaging";
version = "6.10.0";
sha256 = "18s53cvrf51lihmaqqdf48p2qi6ky1l48jv0hvbp76cxwdg7rba4";
})
(fetchNuGet {
pname = "NuGet.Protocol";
version = "6.10.0";
sha256 = "0hmv4q0ks9i34mfgpb13l01la9v3jjllfh1qd3aqv105xrqrdxac";
})
(fetchNuGet {
pname = "NuGet.Versioning";
version = "6.10.0";
sha256 = "1x19njx4x0sw9fz8y5fibi15xfsrw5avir0cx0599yd7p3ykik5g";
})
(fetchNuGet {
pname = "NUnit";
version = "4.1.0";
sha256 = "0fj6xwgqaxq3mrai86bklclfmjkzf038mrslwfqf4ignaz9f7g5j";
})
(fetchNuGet {
pname = "NUnit3TestAdapter";
version = "4.5.0";
sha256 = "1srx1629s0k1kmf02nmz251q07vj6pv58mdafcr5dr0bbn1fh78i";
})
(fetchNuGet {
pname = "System.Formats.Asn1";
version = "6.0.0";
sha256 = "1vvr7hs4qzjqb37r0w1mxq7xql2b17la63jwvmgv65s1hj00g8r9";
})
(fetchNuGet {
pname = "System.IO.Abstractions";
version = "4.2.13";
sha256 = "0s784iphsmj4vhkrzq9q3w39vsn76w44zclx3hsygsw458zbyh4y";
})
(fetchNuGet {
pname = "System.IO.FileSystem.AccessControl";
version = "4.5.0";
sha256 = "1gq4s8w7ds1sp8f9wqzf8nrzal40q5cd2w4pkf4fscrl2ih3hkkj";
})
(fetchNuGet {
pname = "System.Reflection.Metadata";
version = "1.6.0";
sha256 = "1wdbavrrkajy7qbdblpbpbalbdl48q3h34cchz24gvdgyrlf15r4";
})
(fetchNuGet {
pname = "System.Security.AccessControl";
version = "4.5.0";
sha256 = "1wvwanz33fzzbnd2jalar0p0z3x0ba53vzx1kazlskp7pwyhlnq0";
})
(fetchNuGet {
pname = "System.Security.Cryptography.Pkcs";
version = "6.0.4";
sha256 = "0hh5h38pnxmlrnvs72f2hzzpz4b2caiiv6xf8y7fzdg84r3imvfr";
})
(fetchNuGet {
pname = "System.Security.Cryptography.ProtectedData";
version = "4.4.0";
sha256 = "1q8ljvqhasyynp94a1d7jknk946m20lkwy2c3wa8zw2pc517fbj6";
})
(fetchNuGet {
pname = "System.Security.Principal.Windows";
version = "4.5.0";
sha256 = "0rmj89wsl5yzwh0kqjgx45vzf694v9p92r4x4q6yxldk1cv1hi86";
})
(fetchNuGet {
pname = "System.Text.Encodings.Web";
version = "7.0.0";
sha256 = "1151hbyrcf8kyg1jz8k9awpbic98lwz9x129rg7zk1wrs6vjlpxl";
})
(fetchNuGet {
pname = "System.Text.Json";
version = "7.0.3";
sha256 = "0zjrnc9lshagm6kdb9bdh45dmlnkpwcpyssa896sda93ngbmj8k9";
})
]