"Myriad" 0.8.3

dotnet tool install --global Myriad --version 0.8.3                
此软件包包含一个可以从外壳/命令行调用的 .NET 工具
dotnet new tool-manifest # if you are setting up this repo
dotnet tool install --local Myriad --version 0.8.3                
此软件包包含一个可以从外壳/命令行调用的 .NET 工具
#tool dotnet:?package=Myriad&version=0.8.3                
nuke :add-package Myriad --version 0.8.3                

Myriad

Myriad 是一个代码生成器。它接受一个任意文件,并提供不同的机制,以便在文件的响应中生成 F# 代码,无论该文件是 F# 源文件还是简单的文本文件。

Myriad 可以通过 MSBuild 扩展或其 CLI 工具使用。

Myriad 的理念是将 F# 的元编程尽可能简化。在这里,元编程指的是使用 F# 原生类型,如命名区别联合和记录生成惯用的 F# 代码,这是 F# 类型提供程序等无法做到的,它们仅输出基本的 .NET 类。

Myriad 是我开发 F# 类型提供程序和其他元编程功能(如引语和 AST 操作)时的想法的进化。Myriad 力求通过 Myriad 插件轻松扩展编译器。Myriad 提供了一种扩展编译器的途径,它不需要修改或调整类型提供程序,也不需要等待其他 F# 语言改进的长时间。您编写一个 Myriad 插件,它处理 AST 输入的一个片段,该插件提供 AST 输出,最终形式为构建到您的项目中的源代码。这使得编译器可以优化生成输出,同时允许工具有效地运行。

如果您想帮助并贡献代码,那太好了,请查看问题列表并提交一个 PR。

如果您喜欢这个仓库并想表达您的感激之情等,我确实有 Ko-fi。

ko-fi

通过 MSBuild 使用

要使用 Myriad 的 MSBuild 支持,您需要添加 Myriad.CoreMyriad.Sdk 包引用

    <ItemGroup>
      <PackageReference Include="Myriad.Core" Version="0.5.0" />
      <PackageReference Include="Myriad.Sdk" Version="0.5.0" />
    </ItemGroup>

使用常规 Compile 元素指定输入文件

<Compile Include="Library.fs"/>
<Compile Include="Generated.fs">
    <MyriadFile>Library.fs</MyriadFile>
</Compile>

本节将配置 Nubian,以便一个名为 Generated.fs 的文件会在使用 Library.fs 作为 Nubian 输入时被包含到构建中。

还可以将生成的内容附加到输入文件中。

<Compile Include="Library.fs">
    <MyriadInlineGeneration>true</MyriadInlineGeneration>
</Compile>

Nubian 通过插件生成代码。Nubian 内置了一个名为 fields 的插件,该插件的名字灵感来自 OCaml 的同名插件 ppx_fields_conv

本例中的输入文件 Library.fs 看起来是这样的

namespace Example
open Myriad.Plugins

[<Generator.Fields "fields">]
type Test1 = { one: int; two: string; three: float; four: float32 }
type Test2 = { one: Test1; two: string }

使用属性来告知代码生成器哪些输入 AST 的部分需要被插件处理。如果您有几个记录,而只想让 fields 插件处理 Test1,那么就可以像示例中使用属性一样将对 Test1 应用 Generator.Fields。请注意,如果您想要一个只需要整个输入 AST 的插件,则不需要提供输入。Nubian 旨在成为一个库而不是一个完整的框架,而是一个捆绑输入和生成代码机制的框架。传递给属性 "fields" 的参数指定了在 myriad.toml 文件中用作插件的配置部分。在这个例子中使用了 fields,且 myriad.toml 文件如下

[fields]
namespace = "TestFields"

这指定了插件所使用的命名空间,在这种情况下是 "TestFields"。

在这个例子中,fields 插件将在构建前生成以下代码并将其编译到您的汇编中

//------------------------------------------------------------------------------
//        This code was generated by myriad.
//        Changes to this file will be lost when the code is regenerated.
//------------------------------------------------------------------------------
namespace rec TestFields

module Test1 =
    open Example

    let one (x : Test1) = x.one
    let two (x : Test1) = x.two
    let three (x : Test1) = x.three
    let four (x : Test1) = x.four

    let create (one : int) (two : string) (three : float) (four : float32) : Test1 =
        { one = one
          two = two
          three = three
          four = four }

    let map (mapone : int -> int) (maptwo : string -> string) (mapthree : float -> float) (mapfour : float32 -> float32) (record': Test1) =
      { record' with
          one = mapone record'.one
          two = maptwo record'.two
          three = mapthree record'.three
          four = mapfour record'.four }

fields 插件为每个输入记录的字段生成一个 map,一个接受每个字段的 create 函数,以及一个接受输入记录中每个字段的函数的 map 函数。

每个字段的对应用户在只需要在 lambda 函数中使用记录的单一字段的情况下是有用的,例如记录的列表

let records = [{one = "a"; two = "aa"; three = 42.0; four = 172.0f}
               {one = "b"; two = "bb"; three = 42.0; four = 172.0f}]
 records |> List.sortBy Test1.one

Lens 插件

Nubian 还可以生成记录和单案例判别联合的 lenses。Lens 是一个类型的一个属性的 gettersetterpair。给定对象 Lens 允许您获取属性值或更新它,创建一个新的对象。Lens 的优点是能够将它们组合起来读取或更新对象的嵌套字段。

首先,使用 Generator.Lenses 属性注释您想要生成的 lenses 的类型,以创建类型 lenses

[<Generator.Lenses("lens")>]
type Record =
    { one: int
      two: string }

Nubian 将生成以下代码

module RecordLenses =
    let one = (fun (x: Test1) -> x.one), (fun (x: Test1) (value: int) -> { x with one = value })
    let two = (fun (x: Test1) -> x.two), (fun (x: Test1) (value: string) -> { x with two = value })

通常将 lenses 定义为一个围绕一对 getter 和 setter 的单案例联合。Nubian 也能够添加此类 DU 构造函数的调用。

为此,使用 Lens 属性装饰您的类型,指定 DU 构造函数的名称:[<Generator.Lenses("Lens")>],Nubian 将生成以下代码

module RecordLenses =
    let one = Lens((fun (x: Test1) -> x.one), (fun (x: Test1) (value: int) -> { x with one = value }))
    let two = Lens((fun (x: Test1) -> x.two), (fun (x: Test1) (value: string) -> { x with two = value }))

您可以用几种方法提供 DU 构造函数的名称

  • 作为字符串:[<Generator.Lenses("lens", "Lens")>]
  • 或作为类型:[<Generator.Lenses("lens", typedefof<Lens<_, _>>)>][<Generator.Lenses(typeof<Lens<_, _>>)>]

如果 Lens 类型与使用属性装饰的类型在不同的命名空间/模块中,提供 Lens 构造函数的完整名称:[<Generator.Lenses("Namespace.And.Module.Of.Lens")>]


完整的 fsproj 的细节如下

<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <TargetFramework>net6.0</TargetFramework>
    </PropertyGroup>
    <ItemGroup>
        <Compile Include="Library.fs" />
        <Compile Include="Generated.fs">
            <MyriadFile>Library.fs</MyriadFile>
        </Compile>
    </ItemGroup>
    <ItemGroup>
      <PackageReference Include="Myriad.Core" Version="0.5.0" />
      <PackageReference Include="Myriad.Sdk" Version="0.5.0" />
    </ItemGroup>
</Project>

插件

Nubian 的插件通过在项目中包括 nuget 包提供。nuget 基础设施提供必要的 MSBuild 属性和目标,以便插件自动由 Nubian 使用。fields 插件的源代码可以作为编写插件的参考,直到创建更多的插件编写细节。

插件的命名

如果您创建了一个插件,非正式的命名约定是使用:{{OwnerNamespace}}.Myriad.Plugin

使用外部插件

要使用不在Myriad.Plugins包中的外部插件,您必须将它们注册到Myriad中。如果您正在使用CLI工具,则可以通过传递--plugin <dll路径>命令行参数来完成此操作。如果您使用MSBuild,则可以在项目文件中添加到MyriadSdkGenerator属性。

<ItemGroup>
    <MyriadSdkGenerator Include="<path to plugin dll>" />
</ItemGroup>

例如,如果您有一个像这样的项目布局

\src
-\GeneratorLib
 - Generator.fs
 - Generator.fsproj
-\GeneratorTests
 - Tests.fs
 - GeneratorTests.fsproj

您会在Generator.fsproj中添加以下内容:

  <ItemGroup>
    <Content Include="build\Generator.props">
      <Pack>true</Pack>
      <PackagePath>%(Identity)</PackagePath>
      <Visible>true</Visible>
    </Content>
  </ItemGroup>

然后创建一个名为build的新文件夹,并在其中添加Generator.props文件。

<Project>
    <ItemGroup>
        <MyriadSdkGenerator Include="$(MSBuildThisFileDirectory)/../lib/netstandard2.1/Generator.dll" />
    </ItemGroup>
</Project>

通常还会使用一个额外的props文件(在这个示例中文件是Generator.InTest.props),这使得测试更容易。测试.fsproj的匹配元素可能如下所示:

<Project>
    <ItemGroup>
        <MyriadSdkGenerator Include="$(MSBuildThisFileDirectory)/../bin/$(Configuration)/netstandard2.1/Generator.dll" />
    </ItemGroup>
</Project>

请注意,Include路径是指向本地路径而非包内的nuget文件夹结构。

在您的测试fsproj文件中,您会添加以下内容,以允许局部使用插件而不是必须消耗nuget包:


<Import Project="<Path to Generator plugin location>\build\Myriad.Plugins.InTest.props" />

调试

要调试Myriad,您可以使用以下两个命令行选项:

  • --verbose — 将诊断日志写入标准输出
  • --wait-for-debugger —导致Myriad等待调试器附加到Myriad进程

这些选项可以通过MSBuild的<MyriadSdkVerboseOutput>true</MyriadSdkVerboseOutput><MyriadSdkWaitForDebugger>true</MyriadSdkWaitForDebugger>属性来触发。

Nuget

Myriad的Nuget包可以在以下位置找到:Nuget包

Dotnet模板

Myriad插件/生成器的dotnet模板可以在以下位置找到:

#install dotnet template
dotnet new -i Myriad.Templates

#create myriad generator from the template
dotnet new myriadgenerator -n myMyriadPlugin

如何构建和测试

  1. 请确保您已安装.Net Core SDK - 在global.json中检查所需版本。
  2. 运行dotnet tool restore
  3. 运行dotnet build -c Release -t:Build

如何发布新版本

  1. 通过添加新的条目(## [0.X.X])并提交它来更新CHANGELOG.md
  2. 创建版本标签(git tag v0.X.X
  3. Directory.Build.props中更新VersionPrefix以匹配上面的标签。
  4. 运行dotnet build -t:Pack以创建nuget包并在本地测试/检查它。
  5. 推送标签到仓库(git push origin v0.X.X)- 这将启动CI过程,创建GitHub发布并将在其中放置生成的NuGet包
  6. 上传生成的包到NuGet.org

另请参阅

外部插件

以下是已构建的外部插件列表:

SqlHyra JsonWrapper TypeSafeInternals

产品 兼容和额外的计算目标框架版本。
.NET net5.0 已计算。 net5.0-windows 已计算。 net6.0 兼容。 net6.0-android 已计算。 net6.0-ios 已计算。 net6.0-maccatalyst 已计算。 net6.0-macos 已计算。 net6.0-tvos 已计算。 net6.0-windows 已计算。 net7.0 已计算。 net7.0-android 已计算。 net7.0-ios 已计算。 net7.0-maccatalyst 已计算。 net7.0-macos 已计算。 net7.0-tvos 已计算。 net7.0-windows 已计算。 net8.0 已计算。 net8.0-android 已计算。 net8.0-browser 已计算。 net8.0-ios 已计算。 net8.0-maccatalyst 已计算。 net8.0-macos 已计算。 net8.0-tvos 已计算。 net8.0-windows 已计算。
.NET Core netcoreapp3.1 兼容。
兼容的目标框架
包含的目标框架(在包中)
了解更多关于 目标框架.NET Standard 的信息。

此包没有依赖项。

版本 下载 最后更新
0.8.3 1,019 9/8/2023
0.8.2 1,189 2/10/2023
0.8.1 2,577 5/13/2022
0.8.0 1,540 5/4/2022
0.7.4 1,395 10/29/2021
0.7.3 1,315 10/28/2021
0.7.3-alpha 1,172 10/28/2021
0.7.2 1,438 10/28/2021
0.7.1 684 10/26/2021
0.7.0 1,232 10/21/2021
0.6.4 1,197 10/11/2021
0.6.3 1,088 9/22/2021
0.6.2 1,197 9/17/2021
0.6.1 1,261 9/17/2021
0.6.0 1,167 9/9/2021
0.5.4 1,068 9/2/2021
0.5.3 1,397 5/21/2021
0.5.1 1,085 4/15/2021
0.5.0 54,914 12/22/2020
0.4.1 1,255 9/23/2020
0.4.0 1,253 7/21/2020
0.2.8 1,396 6/5/2020
0.2.7 1,434 5/18/2020
0.2.6 1,289 5/13/2020
0.2.4 1,407 11/3/2019
0.2.3 1,261 11/3/2019
0.2.2 1,142 11/2/2019
0.2.1 1,303 11/2/2019
0.2.0 1,315 10/23/2019
0.1.0 2,419 4/19/2019
0.0.5 2,374 4/18/2019
0.0.4 1,048 4/17/2019
0.0.3 1,014 4/17/2019
0.0.2 1,060 4/17/2019
0.0.1 1,007 4/16/2019
0.0.1-master-0000006 933 4/16/2019