From b1d65b33d8f12852a726d6720717ac6efbffddeb Mon Sep 17 00:00:00 2001 From: Smaug123 <3138005+Smaug123@users.noreply.github.com> Date: Fri, 7 Jun 2024 18:31:16 +0100 Subject: [PATCH] Initial commit --- .config/dotnet-tools.json | 12 + .editorconfig | 40 +++ .envrc | 1 + .fantomasignore | 1 + .github/CODEOWNERS | 3 + .github/dependabot.yml | 8 + .github/workflows/dotnet.yaml | 217 ++++++++++++++++ .github/workflows/tag.sh | 120 +++++++++ .gitignore | 12 + Directory.Build.props | 16 ++ Example/Example.fsproj | 17 ++ Example/Program.fs | 21 ++ README.md | 14 ++ WoofWare.DotnetRuntimeLocator.sln | 34 +++ WoofWare.DotnetRuntimeLocator/Boilerplate.cs | 28 +++ .../DotnetEnvironmentFrameworkInfo.cs | 29 +++ .../DotnetEnvironmentInfo.cs | 231 ++++++++++++++++++ .../DotnetEnvironmentSdkInfo.cs | 27 ++ .../InteropStructs.cs | 46 ++++ .../SurfaceBaseline.txt | 47 ++++ .../Test/Test.fsproj | 26 ++ .../Test/TestDotnetEnvironmentInfo.fs | 19 ++ .../Test/TestSurface.fs | 23 ++ .../WoofWare.DotnetRuntimeLocator.csproj | 40 +++ WoofWare.DotnetRuntimeLocator/logo.png | Bin 0 -> 75554 bytes WoofWare.DotnetRuntimeLocator/version.json | 10 + flake.lock | 61 +++++ flake.nix | 66 +++++ nix/deps.nix | 224 +++++++++++++++++ 29 files changed, 1393 insertions(+) create mode 100644 .config/dotnet-tools.json create mode 100644 .editorconfig create mode 100644 .envrc create mode 100644 .fantomasignore create mode 100644 .github/CODEOWNERS create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/dotnet.yaml create mode 100644 .github/workflows/tag.sh create mode 100644 .gitignore create mode 100644 Directory.Build.props create mode 100644 Example/Example.fsproj create mode 100644 Example/Program.fs create mode 100644 README.md create mode 100644 WoofWare.DotnetRuntimeLocator.sln create mode 100644 WoofWare.DotnetRuntimeLocator/Boilerplate.cs create mode 100644 WoofWare.DotnetRuntimeLocator/DotnetEnvironmentFrameworkInfo.cs create mode 100644 WoofWare.DotnetRuntimeLocator/DotnetEnvironmentInfo.cs create mode 100644 WoofWare.DotnetRuntimeLocator/DotnetEnvironmentSdkInfo.cs create mode 100644 WoofWare.DotnetRuntimeLocator/InteropStructs.cs create mode 100644 WoofWare.DotnetRuntimeLocator/SurfaceBaseline.txt create mode 100644 WoofWare.DotnetRuntimeLocator/Test/Test.fsproj create mode 100644 WoofWare.DotnetRuntimeLocator/Test/TestDotnetEnvironmentInfo.fs create mode 100644 WoofWare.DotnetRuntimeLocator/Test/TestSurface.fs create mode 100644 WoofWare.DotnetRuntimeLocator/WoofWare.DotnetRuntimeLocator.csproj create mode 100644 WoofWare.DotnetRuntimeLocator/logo.png create mode 100644 WoofWare.DotnetRuntimeLocator/version.json create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 nix/deps.nix diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json new file mode 100644 index 0000000..c1e4bf5 --- /dev/null +++ b/.config/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "fantomas": { + "version": "6.3.8", + "commands": [ + "fantomas" + ] + } + } +} diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..e744fe1 --- /dev/null +++ b/.editorconfig @@ -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 diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.fantomasignore b/.fantomasignore new file mode 100644 index 0000000..9b42106 --- /dev/null +++ b/.fantomasignore @@ -0,0 +1 @@ +.direnv/ diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..203a3b5 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,3 @@ +# See: https://help.github.com/articles/about-codeowners/ + +* @G-Research/rqf @G-Research/gr-oss diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..1d54482 --- /dev/null +++ b/.github/dependabot.yml @@ -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" diff --git a/.github/workflows/dotnet.yaml b/.github/workflows/dotnet.yaml new file mode 100644 index 0000000..6abf99e --- /dev/null +++ b/.github/workflows/dotnet.yaml @@ -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 diff --git a/.github/workflows/tag.sh b/.github/workflows/tag.sh new file mode 100644 index 0000000..e2795f1 --- /dev/null +++ b/.github/workflows/tag.sh @@ -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 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..38fb0e1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +bin/ +obj/ +/packages/ +riderModule.iml +/_ReSharper.Caches/ +.idea/ +*.sln.DotSettings.user +.DS_Store +result +.analyzerpackages/ +analysis.sarif +.direnv/ diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000..cbf2f3e --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,16 @@ + + + embedded + true + [UNDEFINED] + true + true + true + embedded + FS3388,FS3559 + + + + + + diff --git a/Example/Example.fsproj b/Example/Example.fsproj new file mode 100644 index 0000000..18f0d71 --- /dev/null +++ b/Example/Example.fsproj @@ -0,0 +1,17 @@ + + + + Exe + net8.0 + true + + + + + + + + + + + diff --git a/Example/Program.fs b/Example/Program.fs new file mode 100644 index 0000000..26250a3 --- /dev/null +++ b/Example/Program.fs @@ -0,0 +1,21 @@ +namespace Example + +open System +open WoofWare.DotnetRuntimeLocator + +module Program = + [] + 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 diff --git a/README.md b/README.md new file mode 100644 index 0000000..debcfa2 --- /dev/null +++ b/README.md @@ -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" +``` diff --git a/WoofWare.DotnetRuntimeLocator.sln b/WoofWare.DotnetRuntimeLocator.sln new file mode 100644 index 0000000..b293925 --- /dev/null +++ b/WoofWare.DotnetRuntimeLocator.sln @@ -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 diff --git a/WoofWare.DotnetRuntimeLocator/Boilerplate.cs b/WoofWare.DotnetRuntimeLocator/Boilerplate.cs new file mode 100644 index 0000000..698fadd --- /dev/null +++ b/WoofWare.DotnetRuntimeLocator/Boilerplate.cs @@ -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 + { + } +} diff --git a/WoofWare.DotnetRuntimeLocator/DotnetEnvironmentFrameworkInfo.cs b/WoofWare.DotnetRuntimeLocator/DotnetEnvironmentFrameworkInfo.cs new file mode 100644 index 0000000..5909afa --- /dev/null +++ b/WoofWare.DotnetRuntimeLocator/DotnetEnvironmentFrameworkInfo.cs @@ -0,0 +1,29 @@ +using System.IO; +using System.Runtime.InteropServices; + +namespace WoofWare.DotnetRuntimeLocator; + +/// +/// Information about a single instance of the .NET runtime. +/// +/// The name of this runtime, e.g. "Microsoft.NETCore.App" +/// +/// 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) +/// +/// The version of this runtime, e.g. "8.0.5" +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); + } +} diff --git a/WoofWare.DotnetRuntimeLocator/DotnetEnvironmentInfo.cs b/WoofWare.DotnetRuntimeLocator/DotnetEnvironmentInfo.cs new file mode 100644 index 0000000..d917d1f --- /dev/null +++ b/WoofWare.DotnetRuntimeLocator/DotnetEnvironmentInfo.cs @@ -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; + +/// +/// Information known to `dotnet` about what frameworks and runtimes are available. +/// +/// Version of the runtime, e.g. "8.0.5" +/// +/// A commit hash of the .NET runtime (as of this writing, this is probably a hash from +/// https://github.com/dotnet/runtime). +/// +/// Collection of .NET SDKs we were able to find. +/// Collection of .NET runtimes we were able to find. +public record DotnetEnvironmentInfo( + string HostFxrVersion, + string HostFxrCommitHash, + IReadOnlyList Sdks, + IReadOnlyList Frameworks) +{ + private static readonly Lazy 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; + } + + /// + /// Takes a DotnetEnvironmentInfoNative and a return location, which must fit a DotnetEnvironmentInfo. + /// Renders the DotnetEnvironmentInfoNative and stores it in the return location. + /// + private static void StoreResult(IntPtr envInfo, IntPtr retLoc) + { + var toRet = FromNativeConstructor.FromNative( + Marshal.PtrToStructure(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(); + } + } + } + + /// + /// Get the environment information that is available to the specified `dotnet` executable. + /// + /// 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. + /// Information about the environment available to the given executable. + /// Throws on any failure; handles nothing gracefully. + 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(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; + } + } + + /// + /// Get the environment information that is available to some arbitrary `dotnet` executable we were able to find. + /// + /// Information about the environment available to `dotnet`. + /// Throws on any failure; handles nothing gracefully. + 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); + } + + /// + /// 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. + /// + internal delegate int RuntimeDelegate(IntPtr pathToDotnetExeDirectory, IntPtr mustBeNull, IntPtr outputCallback, + IntPtr outputArg); + + /// + /// 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. + /// + 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((int)native.framework_count); + for (var i = 0; i < frameworkCount; i++) + { + var frameworkInfo = new IntPtr(native.frameworks.ToInt64() + + i * Marshal.SizeOf()); + frameworks.Add(DotnetEnvironmentFrameworkInfo.FromNative( + Marshal.PtrToStructure(frameworkInfo))); + } + + var sdks = new List((int)native.sdk_count); + for (var i = 0; i < sdkCount; i++) + { + var sdkInfo = new IntPtr(native.sdks.ToInt64() + + i * Marshal.SizeOf()); + sdks.Add(DotnetEnvironmentSdkInfo.FromNative( + Marshal.PtrToStructure(sdkInfo))); + } + + return new DotnetEnvironmentInfo(native.hostfxr_version, native.hostfxr_commit_hash, sdks, frameworks); + } +} diff --git a/WoofWare.DotnetRuntimeLocator/DotnetEnvironmentSdkInfo.cs b/WoofWare.DotnetRuntimeLocator/DotnetEnvironmentSdkInfo.cs new file mode 100644 index 0000000..b76c42b --- /dev/null +++ b/WoofWare.DotnetRuntimeLocator/DotnetEnvironmentSdkInfo.cs @@ -0,0 +1,27 @@ +using System.IO; +using System.Runtime.InteropServices; + +namespace WoofWare.DotnetRuntimeLocator; + +/// +/// Information about a single instance of the .NET SDK. +/// +/// +/// 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) +/// +/// e.g. "8.0.300" +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); + } +} diff --git a/WoofWare.DotnetRuntimeLocator/InteropStructs.cs b/WoofWare.DotnetRuntimeLocator/InteropStructs.cs new file mode 100644 index 0000000..c3a3f85 --- /dev/null +++ b/WoofWare.DotnetRuntimeLocator/InteropStructs.cs @@ -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; + + /// + /// Pointer to an array of DotnetEnvironmentSdkInfoNative, of length `sdk_count` + /// + public IntPtr sdks; + + public nuint framework_count; + + /// + /// Pointer to an array of DotnetEnvironmentFrameworkInfoNative, of length `framework_count` + /// + public IntPtr frameworks; + } +} diff --git a/WoofWare.DotnetRuntimeLocator/SurfaceBaseline.txt b/WoofWare.DotnetRuntimeLocator/SurfaceBaseline.txt new file mode 100644 index 0000000..8c34e00 --- /dev/null +++ b/WoofWare.DotnetRuntimeLocator/SurfaceBaseline.txt @@ -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.$ [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.$ [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.$ [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 \ No newline at end of file diff --git a/WoofWare.DotnetRuntimeLocator/Test/Test.fsproj b/WoofWare.DotnetRuntimeLocator/Test/Test.fsproj new file mode 100644 index 0000000..cea00be --- /dev/null +++ b/WoofWare.DotnetRuntimeLocator/Test/Test.fsproj @@ -0,0 +1,26 @@ + + + + net8.0 + false + true + + + + + + + + + + + + + + + + + + + + diff --git a/WoofWare.DotnetRuntimeLocator/Test/TestDotnetEnvironmentInfo.fs b/WoofWare.DotnetRuntimeLocator/Test/TestDotnetEnvironmentInfo.fs new file mode 100644 index 0000000..e892c06 --- /dev/null +++ b/WoofWare.DotnetRuntimeLocator/Test/TestDotnetEnvironmentInfo.fs @@ -0,0 +1,19 @@ +namespace WoofWare.DotnetRuntimeLocator.Test + +open System +open FsUnitTyped +open WoofWare.DotnetRuntimeLocator +open NUnit.Framework + +[] +module TestDotnetEnvironmentInfo = + + [] + 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}" diff --git a/WoofWare.DotnetRuntimeLocator/Test/TestSurface.fs b/WoofWare.DotnetRuntimeLocator/Test/TestSurface.fs new file mode 100644 index 0000000..cafcd6c --- /dev/null +++ b/WoofWare.DotnetRuntimeLocator/Test/TestSurface.fs @@ -0,0 +1,23 @@ +namespace WoofWare.DotnetRuntimeLocator.Test + +open NUnit.Framework +open ApiSurface + +[] +module TestSurface = + let assembly = typeof.Assembly + + [] + let ``Ensure API surface has not been modified`` () = ApiSurface.assertIdentical assembly + + [] + let ``Update API surface`` () = + ApiSurface.writeAssemblyBaseline assembly + + [] + let ``Ensure public API is fully documented`` () = + DocCoverage.assertFullyDocumented assembly + + [] + let ``Ensure version is monotonic`` () = + MonotonicVersion.validate assembly "WoofWare.DotnetRuntimeLocator" diff --git a/WoofWare.DotnetRuntimeLocator/WoofWare.DotnetRuntimeLocator.csproj b/WoofWare.DotnetRuntimeLocator/WoofWare.DotnetRuntimeLocator.csproj new file mode 100644 index 0000000..bb04fe1 --- /dev/null +++ b/WoofWare.DotnetRuntimeLocator/WoofWare.DotnetRuntimeLocator.csproj @@ -0,0 +1,40 @@ + + + + net6.0 + enable + latest + false + true + true + Patrick Stevens + Copyright (c) Patrick Stevens 2024 + Helpers to locate the .NET runtime and SDKs + git + https://github.com/Smaug123/WoofWare.DotnetRuntimeLocator + MIT + README.md + logo.png + runtime;locate;sdk;list-runtimes;list-sdks + true + + + + + + + + + + + + True + \ + + + True + \ + + + + diff --git a/WoofWare.DotnetRuntimeLocator/logo.png b/WoofWare.DotnetRuntimeLocator/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..89f4d2f331dfdfd13573f062ea1fd9451607f66d GIT binary patch literal 75554 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 z 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 TsT87D3_w;&NwVsT@vr{^OvO@? literal 0 HcmV?d00001 diff --git a/WoofWare.DotnetRuntimeLocator/version.json b/WoofWare.DotnetRuntimeLocator/version.json new file mode 100644 index 0000000..bb9aa91 --- /dev/null +++ b/WoofWare.DotnetRuntimeLocator/version.json @@ -0,0 +1,10 @@ +{ + "version": "0.1", + "publicReleaseRefSpec": null, + "pathFilters": [ + "^Test/", + ":/WoofWare.DotnetRuntimeLocator", + ":/Directory.Build.props", + ":/README.md" + ] +} \ No newline at end of file diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..f15c20e --- /dev/null +++ b/flake.lock @@ -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 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..ef41b67 --- /dev/null +++ b/flake.nix @@ -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 + ]; + }; + }); +} diff --git a/nix/deps.nix b/nix/deps.nix new file mode 100644 index 0000000..d9caa56 --- /dev/null +++ b/nix/deps.nix @@ -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"; + }) +]