From 1dde7a65f7738cde6ab07740d97b42ac160bb1c1 Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Sat, 6 May 2023 13:00:12 +0100 Subject: [PATCH] Initial commit --- .editorconfig | 41 +++ .gitattributes | 5 + .github/CODEOWNERS | 3 + .github/dependabot.yml | 17 ++ .github/workflows/dotnet.yaml | 98 ++++++ .gitignore | 9 + Directory.Build.props | 14 + LICENSE | 21 ++ Myriad.sln | 28 ++ MyriadPlugin.Test/MyriadPlugin.Test.fsproj | 27 ++ MyriadPlugin.Test/TestSurface.fs | 20 ++ MyriadPlugin/MyriadPlugin.fsproj | 19 ++ MyriadPlugin/RemoveOptionsGenerator.fs | 146 +++++++++ MyriadPlugin/SurfaceBaseline.txt | 4 + MyriadPlugin/version.json | 7 + README.md | 31 ++ UsePlugin/Generated.fs | 20 ++ UsePlugin/Program.fs | 19 ++ UsePlugin/RecordFile.fs | 13 + UsePlugin/UsePlugin.fsproj | 29 ++ dotnet-tools.json | 12 + flake.lock | 60 ++++ flake.nix | 88 ++++++ global.json | 6 + hooks/pre-push | 6 + nix/deps.nix | 339 +++++++++++++++++++++ nix/fetchDeps.sh | 73 +++++ runmyriad.sh | 9 + 28 files changed, 1164 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .github/CODEOWNERS create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/dotnet.yaml create mode 100644 .gitignore create mode 100644 Directory.Build.props create mode 100644 LICENSE create mode 100644 Myriad.sln create mode 100644 MyriadPlugin.Test/MyriadPlugin.Test.fsproj create mode 100644 MyriadPlugin.Test/TestSurface.fs create mode 100644 MyriadPlugin/MyriadPlugin.fsproj create mode 100644 MyriadPlugin/RemoveOptionsGenerator.fs create mode 100644 MyriadPlugin/SurfaceBaseline.txt create mode 100644 MyriadPlugin/version.json create mode 100644 README.md create mode 100644 UsePlugin/Generated.fs create mode 100644 UsePlugin/Program.fs create mode 100644 UsePlugin/RecordFile.fs create mode 100644 UsePlugin/UsePlugin.fsproj create mode 100644 dotnet-tools.json create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 global.json create mode 100755 hooks/pre-push create mode 100644 nix/deps.nix create mode 100644 nix/fetchDeps.sh create mode 100755 runmyriad.sh diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..9ef5fed --- /dev/null +++ b/.editorconfig @@ -0,0 +1,41 @@ +root=true + +[*] +charset=utf-8 +end_of_line=crlf +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/.gitattributes b/.gitattributes new file mode 100644 index 0000000..a37b813 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +* eol=auto +*.sh text eol=lf +*.yaml text +*.nix text eol=lf +hooks/pre-push text eol=lf 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..72c2bfe --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,17 @@ +version: 2 +updates: + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + + - package-ecosystem: "nuget" + directory: "/ApiSurface" + schedule: + interval: "weekly" + ignore: + # Target the lowest version of FSharp.Core, for max compat + - dependency-name: "FSharp.Core" + # Target the lowest compatible version of System.Text.Json + - dependency-name: "System.Text.Json" diff --git a/.github/workflows/dotnet.yaml b/.github/workflows/dotnet.yaml new file mode 100644 index 0000000..5107855 --- /dev/null +++ b/.github/workflows/dotnet.yaml @@ -0,0 +1,98 @@ +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: + strategy: + matrix: + config: + - Release + - Debug + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 # so that NerdBank.GitVersioning has access to history + - name: Install Nix + uses: cachix/install-nix-action@v20 + 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@v3 + - name: Install Nix + uses: cachix/install-nix-action@v20 + 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@v3 + - name: Install Nix + uses: cachix/install-nix-action@v20 + 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@v3 + - name: Install Nix + uses: cachix/install-nix-action@v20 + 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@v20 + with: + extra_nix_config: | + access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} + - name: Run link checker + run: nix develop --command markdown-link-check README.md + + all-required-checks-complete: + needs: [check-dotnet-format, check-nix-format, build, build-nix, linkcheck] + runs-on: ubuntu-latest + steps: + - run: echo "All required checks complete." diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0d22c7d --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +bin/ +obj/ +/packages/ +riderModule.iml +/_ReSharper.Caches/ +.idea/ +*.sln.DotSettings.user +.DS_Store +result diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000..045d450 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,14 @@ + + + embedded + true + [UNDEFINED] + true + true + + + + + + + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f746d00 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Patrick Stevens + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Myriad.sln b/Myriad.sln new file mode 100644 index 0000000..33de901 --- /dev/null +++ b/Myriad.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "UsePlugin", "UsePlugin\UsePlugin.fsproj", "{0D174482-9CB2-448A-8BA8-846FAEC65579}" +EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "MyriadPlugin", "MyriadPlugin\MyriadPlugin.fsproj", "{DB86C53B-4090-4791-884B-024C5759855F}" +EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "MyriadPlugin.Test", "MyriadPlugin.Test\MyriadPlugin.Test.fsproj", "{13370CA7-2A80-4B4D-8DEB-F1AA77F206C4}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0D174482-9CB2-448A-8BA8-846FAEC65579}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0D174482-9CB2-448A-8BA8-846FAEC65579}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0D174482-9CB2-448A-8BA8-846FAEC65579}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0D174482-9CB2-448A-8BA8-846FAEC65579}.Release|Any CPU.Build.0 = Release|Any CPU + {DB86C53B-4090-4791-884B-024C5759855F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DB86C53B-4090-4791-884B-024C5759855F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DB86C53B-4090-4791-884B-024C5759855F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DB86C53B-4090-4791-884B-024C5759855F}.Release|Any CPU.Build.0 = Release|Any CPU + {13370CA7-2A80-4B4D-8DEB-F1AA77F206C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {13370CA7-2A80-4B4D-8DEB-F1AA77F206C4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {13370CA7-2A80-4B4D-8DEB-F1AA77F206C4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {13370CA7-2A80-4B4D-8DEB-F1AA77F206C4}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/MyriadPlugin.Test/MyriadPlugin.Test.fsproj b/MyriadPlugin.Test/MyriadPlugin.Test.fsproj new file mode 100644 index 0000000..e0ac57f --- /dev/null +++ b/MyriadPlugin.Test/MyriadPlugin.Test.fsproj @@ -0,0 +1,27 @@ + + + + net7.0 + + false + true + + + + + + + + + + + + + + + + + + + + diff --git a/MyriadPlugin.Test/TestSurface.fs b/MyriadPlugin.Test/TestSurface.fs new file mode 100644 index 0000000..ba65dcd --- /dev/null +++ b/MyriadPlugin.Test/TestSurface.fs @@ -0,0 +1,20 @@ +namespace MyriadPlugin.Test + +open NUnit.Framework +open MyriadPlugin +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 diff --git a/MyriadPlugin/MyriadPlugin.fsproj b/MyriadPlugin/MyriadPlugin.fsproj new file mode 100644 index 0000000..bd2fe1b --- /dev/null +++ b/MyriadPlugin/MyriadPlugin.fsproj @@ -0,0 +1,19 @@ + + + + net7.0 + true + + + + + + + + + + + + + + diff --git a/MyriadPlugin/RemoveOptionsGenerator.fs b/MyriadPlugin/RemoveOptionsGenerator.fs new file mode 100644 index 0000000..371735d --- /dev/null +++ b/MyriadPlugin/RemoveOptionsGenerator.fs @@ -0,0 +1,146 @@ +namespace MyriadPlugin + +open System +open FSharp.Compiler.Syntax +open FSharp.Compiler.Xml +open Myriad.Core + +/// Attribute indicating a record type to which the "Remove Options" Myriad +/// generator should apply during build. +type RemoveOptionsAttribute () = + inherit Attribute () + +module internal Create = + open FSharp.Compiler.Text.Range + open Myriad.Core.Ast + + let isOptionIdent (ident : SynLongIdent) : bool = + match ident.LongIdent with + | [ i ] when String.Equals (i.idText, "option", StringComparison.OrdinalIgnoreCase) -> true + // TODO: consider Microsoft.FSharp.Option or whatever it is + | _ -> false + + let (|OptionIdent|_|) (ident : SynLongIdent) = + if isOptionIdent ident then Some () else None + + let private removeOption (s : SynField) : SynField = + let (SynField.SynField (synAttributeLists, + isStatic, + identOption, + fieldType, + isMutable, + preXmlDoc, + synAccessOption, + range)) = + s + + let newType = + match fieldType with + | SynType.App (SynType.LongIdent OptionIdent, _, [ innerType ], _, _, _, _) -> innerType + | _ -> fieldType + + SynField.SynField ( + synAttributeLists, + isStatic, + identOption, + newType, + isMutable, + preXmlDoc, + synAccessOption, + range + ) + + let createCreate (xmlDoc : PreXmlDoc option) (fields : SynField list) = + let fields : SynField list = fields |> List.map removeOption + let name = Ident.Create "Short" + + let typeDecl : SynTypeDefn = + match xmlDoc with + | None -> SynTypeDefn.CreateRecord (name, fields) + | Some xmlDoc -> SynTypeDefn.CreateRecord (name, fields, xmldoc = xmlDoc) + + SynModuleDecl.Types ([ typeDecl ], range0) + + let createRecordModule (namespaceId : LongIdent) (typeDefn : SynTypeDefn) = + let (SynTypeDefn (synComponentInfo, synTypeDefnRepr, _members, _implicitCtor, _, _)) = + typeDefn + + let (SynComponentInfo (_attributes, _typeParams, _constraints, recordId, doc, _preferPostfix, _access, _)) = + synComponentInfo + + match synTypeDefnRepr with + | SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Record (_accessibility, recordFields, _recordRange), _) -> + + let create = createCreate (Some doc) recordFields + + let decls = [ yield create ] + + let compilationRepresentation : SynAttribute = + { + TypeName = SynLongIdent.CreateString "CompilationRepresentation" + ArgExpr = + SynExpr.CreateLongIdent ( + false, + SynLongIdent.Create [ "CompilationRepresentationFlags" ; "ModuleSuffix" ], + None + ) + |> SynExpr.CreateParen + Target = None + AppliesToGetterAndSetter = false + Range = range0 + } + + let attributes = + [ + SynAttributeList.Create (SynAttribute.RequireQualifiedAccess ()) + SynAttributeList.Create compilationRepresentation + ] + + let xmlDoc = + recordId + |> Seq.map (fun i -> i.idText) + |> String.concat "." + |> sprintf " Module containing an option-truncated version of the %s type" + |> PreXmlDoc.Create + + let info = + SynComponentInfo.Create (recordId, attributes = attributes, xmldoc = xmlDoc) + + let mdl = SynModuleDecl.CreateNestedModule (info, decls) + + SynModuleOrNamespace.CreateNamespace (namespaceId, decls = [ mdl ]) + | _ -> failwithf "Not a record type" + +/// Myriad generator that stamps out a record with option types stripped +/// from the fields at the top level. +[] +type RemoveOptionsGenerator () = + + interface IMyriadGenerator with + member _.ValidInputExtensions = [ ".fs" ] + + member _.Generate (context : GeneratorContext) = + let ast, _ = + Ast.fromFilename context.InputFilename |> Async.RunSynchronously |> Array.head + + let records = Ast.extractRecords ast + + let namespaceAndRecords = + records + |> List.choose (fun (ns, types) -> + match types |> List.filter Ast.hasAttribute with + | [] -> None + | types -> Some (ns, types) + ) + + let modules = + namespaceAndRecords + |> List.collect (fun (ns, records) -> + records + |> List.map (fun record -> + let recordModule = Create.createRecordModule ns record + recordModule + ) + ) + + Output.Ast modules diff --git a/MyriadPlugin/SurfaceBaseline.txt b/MyriadPlugin/SurfaceBaseline.txt new file mode 100644 index 0000000..71d54ed --- /dev/null +++ b/MyriadPlugin/SurfaceBaseline.txt @@ -0,0 +1,4 @@ +MyriadPlugin.RemoveOptionsAttribute inherit System.Attribute +MyriadPlugin.RemoveOptionsAttribute..ctor [constructor]: unit +MyriadPlugin.RemoveOptionsGenerator inherit obj, implements Myriad.Core.IMyriadGenerator +MyriadPlugin.RemoveOptionsGenerator..ctor [constructor]: unit \ No newline at end of file diff --git a/MyriadPlugin/version.json b/MyriadPlugin/version.json new file mode 100644 index 0000000..7b32cd8 --- /dev/null +++ b/MyriadPlugin/version.json @@ -0,0 +1,7 @@ +{ + "version": "0.1", + "publicReleaseRefSpec": [ + "^refs/heads/main$" + ], + "pathFilters": null +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..8dccb93 --- /dev/null +++ b/README.md @@ -0,0 +1,31 @@ +# fsharp-arguments + +Some helpers in [Myriad](https://github.com/MoiraeSoftware/myriad/) which might be useful for someone writing an argument parser. + +## `RemoveOptions` + +Takes a record like this: + +```fsharp +type Foo = + { + A : int option + B : string + C : float list + } +``` + +and stamps out a record like this: + +```fsharp +[] +module Foo = + type Short = + { + A : int + B : string + C : float list + } +``` + +(This is a proof of concept. It would be better to somehow disambiguate the module name.) diff --git a/UsePlugin/Generated.fs b/UsePlugin/Generated.fs new file mode 100644 index 0000000..1c8c3e2 --- /dev/null +++ b/UsePlugin/Generated.fs @@ -0,0 +1,20 @@ +//------------------------------------------------------------------------------ +// This code was generated by myriad. +// Changes to this file will be lost when the code is regenerated. +//------------------------------------------------------------------------------ +namespace UsePlugin + +/// Module containing an option-truncated version of the RecordType type +[] +[] +module RecordType = + /// My whatnot + type Short = + { + /// A thing! + A : int + /// Another thing! + B : string + /// Yet another thing! + C : float list + } diff --git a/UsePlugin/Program.fs b/UsePlugin/Program.fs new file mode 100644 index 0000000..91e606f --- /dev/null +++ b/UsePlugin/Program.fs @@ -0,0 +1,19 @@ +namespace UsePlugin + +module Program = + let f : RecordType = + { + A = Some 3 + B = "hello" + C = [ 0.3 ] + } + + let g : RecordType.Short = + { + A = 3 + B = "hello" + C = [ 0.3 ] + } + + [] + let main _ = 0 diff --git a/UsePlugin/RecordFile.fs b/UsePlugin/RecordFile.fs new file mode 100644 index 0000000..b261175 --- /dev/null +++ b/UsePlugin/RecordFile.fs @@ -0,0 +1,13 @@ +namespace UsePlugin + +/// My whatnot +[] +type RecordType = + { + /// A thing! + A : int option + /// Another thing! + B : string + /// Yet another thing! + C : float list + } diff --git a/UsePlugin/UsePlugin.fsproj b/UsePlugin/UsePlugin.fsproj new file mode 100644 index 0000000..251bcf7 --- /dev/null +++ b/UsePlugin/UsePlugin.fsproj @@ -0,0 +1,29 @@ + + + + Exe + net7.0 + false + + + + + + + + + RecordFile.fs + + + + runmyriad.sh + + + + + + + + + + diff --git a/dotnet-tools.json b/dotnet-tools.json new file mode 100644 index 0000000..2d138b7 --- /dev/null +++ b/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "fantomas": { + "version": "6.0.1", + "commands": [ + "fantomas" + ] + } + } +} diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..bd6cd92 --- /dev/null +++ b/flake.lock @@ -0,0 +1,60 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1681202837, + "narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "cfacdce06f30d2b68473a46042957675eebb3401", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1683353485, + "narHash": "sha256-Skp5El3egmoXPiINWjnoW0ktVfB7PR/xc4F4bhD+BJY=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "caf436a52b25164b71e0d48b671127ac2e2a5b75", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "ref": "nixpkgs-unstable", + "type": "indirect" + } + }, + "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..6167181 --- /dev/null +++ b/flake.nix @@ -0,0 +1,88 @@ +{ + description = "Myriad plugins to help with argument parsing"; + + inputs = { + flake-utils.url = "github:numtide/flake-utils"; + nixpkgs.url = "nixpkgs/nixpkgs-unstable"; + }; + + outputs = { + self, + nixpkgs, + flake-utils, + ... + }: + flake-utils.lib.eachDefaultSystem (system: let + pkgs = nixpkgs.legacyPackages.${system}; + pname = "dotnet-cipher-suite"; + dotnet-sdk = pkgs.dotnet-sdk_7; + dotnet-runtime = pkgs.dotnetCorePackages.runtime_7_0; + version = "0.1"; + dotnetTool = 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 = '' + runHook preInstall + mkdir -p "$out/lib" + cp -r ./bin/* "$out/lib" + makeWrapper "${dotnet-runtime}/bin/dotnet" "$out/bin/${name}" --add-flags "$out/lib/${name}.dll" + runHook postInstall + ''; + }; + in { + packages = { + fantomas = dotnetTool "fantomas" "6.0.1" "sha256-TNAkurZ0NYI2Tkr99ms9MdAMLLKCQzemx5zHo/hDOTo="; + fetchDeps = let + flags = []; + runtimeIds = ["win-x64"] ++ map (system: pkgs.dotnetCorePackages.systemToDotnetRid system) dotnet-sdk.meta.platforms; + in + pkgs.writeShellScriptBin "fetch-${pname}-deps" (builtins.readFile (pkgs.substituteAll { + src = ./nix/fetchDeps.sh; + pname = pname; + binPath = pkgs.lib.makeBinPath [pkgs.coreutils dotnet-sdk (pkgs.nuget-to-nix.override {inherit dotnet-sdk;})]; + projectFiles = toString ["./MyriadPlugin/MyriadPlugin.fsproj" "./UsePlugin/UsePlugin.fsproj"]; + testProjectFiles = ["./MyriadPlugin.Test/MyriadPlugin.Test.fsproj"]; + rids = pkgs.lib.concatStringsSep "\" \"" runtimeIds; + packages = dotnet-sdk.packages; + storeSrc = pkgs.srcOnly { + src = ./.; + pname = pname; + version = version; + }; + })); + default = pkgs.buildDotnetModule { + pname = pname; + name = "argument-helpers"; + version = version; + src = ./.; + projectFile = "./MyriadPlugin/MyriadPlugin.fsproj"; + nugetDeps = ./nix/deps.nix; + doCheck = true; + dotnet-sdk = dotnet-sdk; + dotnet-runtime = dotnet-runtime; + }; + }; + devShell = pkgs.mkShell { + buildInputs = with pkgs; [ + (with dotnetCorePackages; + combinePackages [ + dotnet-sdk_7 + dotnetPackages.Nuget + ]) + ]; + packages = [ + pkgs.alejandra + pkgs.nodePackages.markdown-link-check + pkgs.shellcheck + ]; + }; + }); +} diff --git a/global.json b/global.json new file mode 100644 index 0000000..a022b38 --- /dev/null +++ b/global.json @@ -0,0 +1,6 @@ +{ + "sdk": { + "version": "7.0.200", + "rollForward": "latestFeature" + } +} diff --git a/hooks/pre-push b/hooks/pre-push new file mode 100755 index 0000000..1229ad8 --- /dev/null +++ b/hooks/pre-push @@ -0,0 +1,6 @@ +#!/bin/sh + +if ! dotnet tool run fantomas --check . ; then + echo "Formatting incomplete. Consider running 'dotnet tool run fantomas .'" + exit 1 +fi diff --git a/nix/deps.nix b/nix/deps.nix new file mode 100644 index 0000000..a869ec0 --- /dev/null +++ b/nix/deps.nix @@ -0,0 +1,339 @@ +# This file was automatically generated by passthru.fetch-deps. +# Please don't edit it manually, your changes might get overwritten! +{fetchNuGet}: [ + (fetchNuGet { + pname = "ApiSurface"; + version = "4.0.8"; + sha256 = "0xf3kp9lzi1bgm3c1h4lclvf1nvbn3cy5zfmys3i58h9c71yfsak"; + }) + (fetchNuGet { + pname = "coverlet.collector"; + version = "3.2.0"; + sha256 = "1qxpv8v10p5wn162lzdm193gdl6c5f81zadj8h889dprlnj3g8yr"; + }) + (fetchNuGet { + pname = "Fantomas.Core"; + version = "5.0.6"; + sha256 = "10550v04qk4diiv3hfqrxxgqmd9b4awb3vr4ja2wf353ccs5wcla"; + }) + (fetchNuGet { + pname = "Fantomas.FCS"; + version = "5.0.6"; + sha256 = "167q4ivfclhcqqbkfllilicx101jri10w3hydk27bbkxdg87vrxc"; + }) + (fetchNuGet { + pname = "FSharp.Core"; + version = "6.0.6"; + sha256 = "1mb1rwzs48c124pqxymnjgv6g3r6zb8n1v953hflcf20nq6yxi77"; + }) + (fetchNuGet { + pname = "FSharp.Core"; + version = "7.0.200"; + sha256 = "1ji816r8idwjmxk8bzyq1z32ybz7xdg3nb0a7pnvqr8vys11bkgb"; + }) + (fetchNuGet { + pname = "Microsoft.AspNetCore.App.Ref"; + version = "7.0.5"; + sha256 = "09amylhcl0fgrn08zan5xcsa4wjw5prdnlgypbvsz4z930lm4zf4"; + }) + (fetchNuGet { + pname = "Microsoft.AspNetCore.App.Runtime.linux-arm64"; + version = "7.0.5"; + sha256 = "1f7j3fxfdbin5zh39knsr1icpbdf5zkyjdxds9m8brraw9gj5mlw"; + }) + (fetchNuGet { + pname = "Microsoft.AspNetCore.App.Runtime.linux-x64"; + version = "7.0.5"; + sha256 = "01irhwqq80ifrqf87897jlh8v0mr5yls000gryv4v8cagsq648s0"; + }) + (fetchNuGet { + pname = "Microsoft.AspNetCore.App.Runtime.osx-arm64"; + version = "7.0.5"; + sha256 = "152dlxn5bqvf0nyhmxbcmaqj95bmm4vhvm4y23ajfwwgh373n00a"; + }) + (fetchNuGet { + pname = "Microsoft.AspNetCore.App.Runtime.osx-x64"; + version = "7.0.5"; + sha256 = "1nwlyz0sgykx801fg1lj7la2b3vbgyvk51132v0gnz48m8b62n3w"; + }) + (fetchNuGet { + pname = "Microsoft.AspNetCore.App.Runtime.win-x64"; + version = "7.0.5"; + sha256 = "0nxyv0bz9c46pmwvvbmpb6c7id8l9ka9lpymi0ljwln01xwhi8fx"; + }) + (fetchNuGet { + pname = "Microsoft.Build.Tasks.Git"; + version = "1.1.1"; + sha256 = "1bb5p4zlnfn88skkvymxfsn0jybqncl4356hwnic9jxdq2d4fz1w"; + }) + (fetchNuGet { + pname = "Microsoft.CodeCoverage"; + version = "17.5.0"; + sha256 = "0briw00gb5bz9k9kx00p6ghq47w501db7gb6ig5zzmz9hb8lw4a4"; + }) + (fetchNuGet { + pname = "Microsoft.NET.Test.Sdk"; + version = "17.5.0"; + sha256 = "00gz2i8kx4mlq1ywj3imvf7wc6qzh0bsnynhw06z0mgyha1a21jy"; + }) + (fetchNuGet { + pname = "Microsoft.NETCore.App.Host.linux-arm64"; + version = "7.0.5"; + sha256 = "1fq6bjpsmqdgv5z4ncxnxrfn10aw90n2zh8sqw0whhv2kjsq7v8l"; + }) + (fetchNuGet { + pname = "Microsoft.NETCore.App.Host.linux-x64"; + version = "7.0.5"; + sha256 = "12p3zq5n8pmpscrgz944rkrjb12q702if8510xyf2b4na85r85qh"; + }) + (fetchNuGet { + pname = "Microsoft.NETCore.App.Host.osx-arm64"; + version = "7.0.5"; + sha256 = "0b87x1r9103fwg3bg6y42hgv4dk40kgysnvksv3wssd9m40v3kqf"; + }) + (fetchNuGet { + pname = "Microsoft.NETCore.App.Host.osx-x64"; + version = "7.0.5"; + sha256 = "1lnv3z082ijmyzwa3in98wz7jchaxld2gbc3dk2k804pavaamr8r"; + }) + (fetchNuGet { + pname = "Microsoft.NETCore.App.Host.win-x64"; + version = "7.0.5"; + sha256 = "08raqcy32yni373c6kdmxvyndxlwrhnxadfjp4fn7rfqyrgqkifn"; + }) + (fetchNuGet { + pname = "Microsoft.NETCore.App.Ref"; + version = "7.0.5"; + sha256 = "1sam55nhsa0q6npcx2qa2q2rfqss3lk27djyhp4q7yazsnlihq1d"; + }) + (fetchNuGet { + pname = "Microsoft.NETCore.App.Runtime.linux-arm64"; + version = "7.0.5"; + sha256 = "08ak2khqcn9dqinb59c5nlpa7imdhi5j7l4g9p2xm62jm6816qlp"; + }) + (fetchNuGet { + pname = "Microsoft.NETCore.App.Runtime.linux-x64"; + version = "7.0.5"; + sha256 = "1cl6g85yaigyzixdqnxqpclf46x32f3ndjl08x9lpypwsv62cd9z"; + }) + (fetchNuGet { + pname = "Microsoft.NETCore.App.Runtime.osx-arm64"; + version = "7.0.5"; + sha256 = "1qw07w5qll6y8rdids8bv3717hmhcv69vs7xbgpddh7ag0xxihr7"; + }) + (fetchNuGet { + pname = "Microsoft.NETCore.App.Runtime.osx-x64"; + version = "7.0.5"; + sha256 = "1rmim6wrkh9vd0klmlwm5yr6xszrhv2qmw4sh12453khxdsi0xpl"; + }) + (fetchNuGet { + pname = "Microsoft.NETCore.App.Runtime.win-x64"; + version = "7.0.5"; + sha256 = "1wd6i75alsj2hv8aich5gjc6979s4shmrdmfraqj2qr51k3jdf0r"; + }) + (fetchNuGet { + pname = "Microsoft.NETCore.Platforms"; + version = "1.1.0"; + sha256 = "08vh1r12g6ykjygq5d3vq09zylgb84l63k49jc4v8faw9g93iqqm"; + }) + (fetchNuGet { + pname = "Microsoft.NETCore.Platforms"; + version = "1.1.1"; + sha256 = "164wycgng4mi9zqi2pnsf1pq6gccbqvw6ib916mqizgjmd8f44pj"; + }) + (fetchNuGet { + pname = "Microsoft.NETCore.Platforms"; + version = "2.0.0"; + sha256 = "1fk2fk2639i7nzy58m9dvpdnzql4vb8yl8vr19r2fp8lmj9w2jr0"; + }) + (fetchNuGet { + pname = "Microsoft.NETCore.Targets"; + version = "1.1.3"; + sha256 = "05smkcyxir59rgrmp7d6327vvrlacdgldfxhmyr1azclvga1zfsq"; + }) + (fetchNuGet { + pname = "Microsoft.SourceLink.Common"; + version = "1.1.1"; + sha256 = "0xkdqs7az2cprar7jzjlgjpd64l6f8ixcmwmpkdm03fyb4s5m0bg"; + }) + (fetchNuGet { + pname = "Microsoft.SourceLink.GitHub"; + version = "1.1.1"; + sha256 = "099y35f2npvva3jk1zp8hn0vb9pwm2l0ivjasdly6y2idv53s5yy"; + }) + (fetchNuGet { + pname = "Microsoft.TestPlatform.ObjectModel"; + version = "17.5.0"; + sha256 = "0qkjyf3ky6xpjg5is2sdsawm99ka7fzgid2bvpglwmmawqgm8gls"; + }) + (fetchNuGet { + pname = "Microsoft.TestPlatform.TestHost"; + version = "17.5.0"; + sha256 = "17g0k3r5n8grba8kg4nghjyhnq9w8v0w6c2nkyyygvfh8k8x9wh3"; + }) + (fetchNuGet { + pname = "Myriad.Core"; + version = "0.8.2"; + sha256 = "11cx1dfhf4lf5abg63l6xlvd8p8s6a5yh9fkdv3ryfp9l63sq6n5"; + }) + (fetchNuGet { + pname = "Myriad.Sdk"; + version = "0.8.2"; + sha256 = "0xzsy22nn205fcrm2qfk1rhy31fjap5i1gfyv7hjcjvkzhblcfvj"; + }) + (fetchNuGet { + pname = "Nerdbank.GitVersioning"; + version = "3.6.128"; + sha256 = "1ip5qlhssfhx7q6gjnx7syvwc9m1bf4ikd17z5cbn9l257465hrj"; + }) + (fetchNuGet { + pname = "NETStandard.Library"; + version = "2.0.0"; + sha256 = "1bc4ba8ahgk15m8k4nd7x406nhi0kwqzbgjk2dmw52ss553xz7iy"; + }) + (fetchNuGet { + pname = "Newtonsoft.Json"; + version = "13.0.1"; + sha256 = "0fijg0w6iwap8gvzyjnndds0q4b8anwxxvik7y8vgq97dram4srb"; + }) + (fetchNuGet { + pname = "NuGet.Common"; + version = "6.5.0"; + sha256 = "1k24azm8hvsm74fmy67i1a3jkzpnsa9hp6dzbam8cdz8ayy6zqc8"; + }) + (fetchNuGet { + pname = "NuGet.Configuration"; + version = "6.5.0"; + sha256 = "1yakqg4x5j69p5zi5wz77ybgyrblrnszj3z1ddsi1ahkxpffx2w4"; + }) + (fetchNuGet { + pname = "NuGet.Frameworks"; + version = "5.11.0"; + sha256 = "0wv26gq39hfqw9md32amr5771s73f5zn1z9vs4y77cgynxr73s4z"; + }) + (fetchNuGet { + pname = "NuGet.Frameworks"; + version = "6.5.0"; + sha256 = "0s37d1p4md0k6d4cy6sq36f2dgkd9qfbzapxhkvi8awwh0vrynhj"; + }) + (fetchNuGet { + pname = "NuGet.Packaging"; + version = "6.5.0"; + sha256 = "0rkczrmw7rss91nam10rf771r31k3fwc271nvh0wn35axv3ibssl"; + }) + (fetchNuGet { + pname = "NuGet.Protocol"; + version = "6.5.0"; + sha256 = "16df7p835aqach4qhnp9dwa335hgfjmmj520fy0h8in1zrnlfazh"; + }) + (fetchNuGet { + pname = "NuGet.Versioning"; + version = "6.5.0"; + sha256 = "095al1ys6379gl52b6fvcn7pplc8gvbphsjz28124kcbx1a5g6vs"; + }) + (fetchNuGet { + pname = "NUnit"; + version = "3.13.3"; + sha256 = "0wdzfkygqnr73s6lpxg5b1pwaqz9f414fxpvpdmf72bvh4jaqzv6"; + }) + (fetchNuGet { + pname = "NUnit.Analyzers"; + version = "3.6.1"; + sha256 = "16dw5375k2wyhiw9x387y7pjgq6zms30y036qb8z7idx4lxw9yi9"; + }) + (fetchNuGet { + pname = "NUnit3TestAdapter"; + version = "4.4.2"; + sha256 = "1n2jlc16vjdd81cb1by4qbp75sq73zsjz5w3zc61ssmbdci1q2ri"; + }) + (fetchNuGet { + pname = "runtime.any.System.Runtime"; + version = "4.3.0"; + sha256 = "1cqh1sv3h5j7ixyb7axxbdkqx6cxy00p4np4j91kpm492rf4s25b"; + }) + (fetchNuGet { + pname = "runtime.native.System"; + version = "4.3.0"; + sha256 = "15hgf6zaq9b8br2wi1i3x0zvmk410nlmsmva9p0bbg73v6hml5k4"; + }) + (fetchNuGet { + pname = "runtime.unix.System.Private.Uri"; + version = "4.3.0"; + sha256 = "1jx02q6kiwlvfksq1q9qr17fj78y5v6mwsszav4qcz9z25d5g6vk"; + }) + (fetchNuGet { + pname = "System.Formats.Asn1"; + version = "5.0.0"; + sha256 = "1axc8z0839yvqi2cb63l73l6d9j6wd20lsbdymwddz9hvrsgfwpn"; + }) + (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.Memory"; + version = "4.5.4"; + sha256 = "14gbbs22mcxwggn0fcfs1b062521azb9fbb7c113x0mq6dzq9h6y"; + }) + (fetchNuGet { + pname = "System.Private.Uri"; + version = "4.3.0"; + sha256 = "04r1lkdnsznin0fj4ya1zikxiqr0h6r6a1ww2dsm60gqhdrf0mvx"; + }) + (fetchNuGet { + pname = "System.Reflection.Metadata"; + version = "1.6.0"; + sha256 = "1wdbavrrkajy7qbdblpbpbalbdl48q3h34cchz24gvdgyrlf15r4"; + }) + (fetchNuGet { + pname = "System.Runtime"; + version = "4.3.1"; + sha256 = "03ch4d2acf6q037a4njxpll2kkx3dwzlg07yxr4z5m6j1kqgmm27"; + }) + (fetchNuGet { + pname = "System.Runtime.CompilerServices.Unsafe"; + version = "6.0.0"; + sha256 = "0qm741kh4rh57wky16sq4m0v05fxmkjjr87krycf5vp9f0zbahbc"; + }) + (fetchNuGet { + pname = "System.Security.AccessControl"; + version = "4.5.0"; + sha256 = "1wvwanz33fzzbnd2jalar0p0z3x0ba53vzx1kazlskp7pwyhlnq0"; + }) + (fetchNuGet { + pname = "System.Security.Cryptography.Cng"; + version = "5.0.0"; + sha256 = "06hkx2za8jifpslkh491dfwzm5dxrsyxzj5lsc0achb6yzg4zqlw"; + }) + (fetchNuGet { + pname = "System.Security.Cryptography.Pkcs"; + version = "5.0.0"; + sha256 = "0hb2mndac3xrw3786bsjxjfh19bwnr991qib54k6wsqjhjyyvbwj"; + }) + (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 = "6.0.0"; + sha256 = "06n9ql3fmhpjl32g3492sj181zjml5dlcc5l76xq2h38c4f87sai"; + }) + (fetchNuGet { + pname = "System.Text.Json"; + version = "6.0.0"; + sha256 = "1si2my1g0q0qv1hiqnji4xh9wd05qavxnzj9dwgs23iqvgjky0gl"; + }) +] diff --git a/nix/fetchDeps.sh b/nix/fetchDeps.sh new file mode 100644 index 0000000..e15b822 --- /dev/null +++ b/nix/fetchDeps.sh @@ -0,0 +1,73 @@ +#!/bin/bash + +# This file was adapted from +# https://github.com/NixOS/nixpkgs/blob/b981d811453ab84fb3ea593a9b33b960f1ab9147/pkgs/build-support/dotnet/build-dotnet-module/default.nix#L173 +set -euo pipefail +export PATH="@binPath@" +for arg in "$@"; do + case "$arg" in + --keep-sources|-k) + keepSources=1 + shift + ;; + --help|-h) + echo "usage: $0 [--keep-sources] [--help] " + echo " The path to write the lockfile to. A temporary file is used if this is not set" + echo " --keep-sources Don't remove temporary directories upon exit, useful for debugging" + echo " --help Show this help message" + exit + ;; + esac +done +tmp=$(mktemp -td "@pname@-tmp-XXXXXX") +export tmp +HOME=$tmp/home +exitTrap() { + test -n "${ranTrap-}" && return + ranTrap=1 + if test -n "${keepSources-}"; then + echo -e "Path to the source: $tmp/src\nPath to the fake home: $tmp/home" + else + rm -rf "$tmp" + fi + # Since mktemp is used this will be empty if the script didnt succesfully complete + if ! test -s "$depsFile"; then + rm -rf "$depsFile" + fi +} +trap exitTrap EXIT INT TERM +dotnetRestore() { + local -r project="${1-}" + local -r rid="$2" + dotnet restore "${project-}" \ + -p:ContinuousIntegrationBuild=true \ + -p:Deterministic=true \ + --packages "$tmp/nuget_pkgs" \ + --runtime "$rid" \ + --no-cache \ + --force +} +declare -a projectFiles=( @projectFiles@ ) +declare -a testProjectFiles=( @testProjectFiles@ ) +export DOTNET_NOLOGO=1 +export DOTNET_CLI_TELEMETRY_OPTOUT=1 +depsFile=$(realpath "${1:-$(mktemp -t "@pname@-deps-XXXXXX.nix")}") +mkdir -p "$tmp/nuget_pkgs" +storeSrc="@storeSrc@" +src="$tmp/src" +cp -rT "$storeSrc" "$src" +chmod -R +w "$src" +cd "$src" +echo "Restoring project..." +rids=("@rids@") +for rid in "${rids[@]}"; do + (( ${#projectFiles[@]} == 0 )) && dotnetRestore "" "$rid" + for project in "${projectFiles[@]-}" "${testProjectFiles[@]-}"; do + dotnetRestore "$project" "$rid" + done +done +echo "Successfully restored project" +echo "Writing lockfile..." +echo -e "# This file was automatically generated by passthru.fetch-deps.\n# Please don't edit it manually, your changes might get overwritten!\n" > "$depsFile" +nuget-to-nix "$tmp/nuget_pkgs" "@packages@" >> "$depsFile" +echo "Successfully wrote lockfile to $depsFile" diff --git a/runmyriad.sh b/runmyriad.sh new file mode 100755 index 0000000..ca68dac --- /dev/null +++ b/runmyriad.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +dotnet \ + "/Users/patrick/.nuget/packages/myriad.sdk/0.8.2/build/../tools/net6.0/any/Myriad.dll" \ + --inputfile "/Users/patrick/Documents/GitHub/MyriadPlugin/UsePlugin/RecordFile.fs" \ + --outputfile "/Users/patrick/Documents/GitHub/MyriadPlugin/UsePlugin/Generated.fs" \ + --configfile "/Users/patrick/Documents/GitHub/MyriadPlugin/UsePlugin/myriad.toml" \ + --contextfile "/Users/patrick/Documents/GitHub/MyriadPlugin/UsePlugin/obj/myriad.context.toml" + --plugin "/Users/patrick/Documents/GitHub/MyriadPlugin/bin/Debug/net7.0/MyriadPlugin.dll"