"Myriad" 0.8.3
dotnet tool install --global Myriad --version 0.8.3
dotnet new tool-manifest # if you are setting up this repo dotnet tool install --local Myriad --version 0.8.3
#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。
通过 MSBuild 使用
要使用 Myriad 的 MSBuild 支持,您需要添加 Myriad.Core
和 Myriad.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 是一个类型的一个属性的 getter 和 setter 的 pair。给定对象 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
如何构建和测试
- 请确保您已安装.Net Core SDK - 在
global.json
中检查所需版本。 - 运行
dotnet tool restore
- 运行
dotnet build -c Release -t:Build
如何发布新版本
- 通过添加新的条目(
## [0.X.X]
)并提交它来更新CHANGELOG.md。 - 创建版本标签(
git tag v0.X.X
) - 在
Directory.Build.props
中更新VersionPrefix
以匹配上面的标签。 - 运行
dotnet build -t:Pack
以创建nuget包并在本地测试/检查它。 - 推送标签到仓库(
git push origin v0.X.X
)- 这将启动CI过程,创建GitHub发布并将在其中放置生成的NuGet包 - 上传生成的包到NuGet.org
另请参阅
外部插件
以下是已构建的外部插件列表:
产品 | 版本 兼容和额外的计算目标框架版本。 |
---|---|
.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 兼容。 |
此包没有依赖项。
版本 | 下载 | 最后更新 |
---|---|---|
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 |