Myriad.Plugins 0.8.3

dotnet add package Myriad.Plugins --version 0.8.3                
NuGet\Install-Package Myriad.Plugins -Version 0.8.3                
此命令意在用于 Visual Studio 的包管理器控制台内,因为它使用了 NuGet 模块的 Install-Package 版本。
<PackageReference Include="Myriad.Plugins" Version="0.8.3" />                
对于支持 PackageReference 的项目,将此 XML 节点复制到项目文件中以便引用此包。
paket add Myriad.Plugins --version 0.8.3                
#r "nuget: Myriad.Plugins, 0.8.3"                
#r 指令可用于 F# Interactive 和 Polyglot Notebooks。将其复制到交互式工具或脚本的源代码中以便引用包。
// Install Myriad.Plugins as a Cake Addin
#addin nuget:?package=Myriad.Plugins&version=0.8.3

// Install Myriad.Plugins as a Cake Tool
#tool nuget:?package=Myriad.Plugins&version=0.8.3                

Myriad

Myriad 是一个代码生成器。它接受任意文件,并提供了不同的机制来允许产生 F# 代码,无论是 F# 源文件还是简单的文本文件。

可以从 MSBuild 扩展或 Myriad 的 CLI 工具使用 Myriad。

Myriad 的理念在于尽可能简化 F# 中进行元编程的能力。在这里,我所说的元编程是指在 F# 中使用原生类型(如区分联合和记录)来生成惯用 F# 代码,这是 F# 类型提供程序等所无法做到的,因为它们只输出基本的 .NET 类。

Myriad 是我在与 F# 的类型提供器和类似引用和 AST 简化等功能一起工作时发展出的想法的演变。Myriad 致力于通过 Myriad 插件轻松扩展编译器。Myriad 提供了一种编译器扩展方法,该方法不修改或调整类型提供器,也不需要等待很长时间的其他 F# 语言改进。您编写一个对 AST 输入片段进行操作的 Myriad 插件,插件提供最终形式的 AST 输出,这是一段编译到您项目中的源代码。这可以让编译器优化生成的输出,并允许工具有效地运行。

如果您想帮助并提供代码,那就太好了,请检查问题并做出拉的请求。

如果您喜欢这个存储库并且希望表达您的感激之情等,那么我确实有 Ko-fi

ko-fi

通过 MSBuild 使用

要通过 MSBuild 支持使用 Myriad,您需要添加 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>

这是配置 Myriad 以使得名为 Generated.fs 的文件能够在构建中使用 Library.fs 作为 Myriad 的输入。

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

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

Myriad 通过使用插件来生成代码工作。Myriad 中包含了一个名为 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 的插件,则不需要提供输入。Myriad 致力于成为一个库,而不是一个完整的框架,该框架将您绑定到用于输入和生成代码的机制。传递给 "fields" 属性的参数指定了用于配置部分的 myriad.toml 文件中的插件。在此实例中,使用了 fields,而 myriad.toml 文件如下

[fields]
namespace = "TestFields"

这指定了插件使用的命名空间,在这个例子中是 "TestFields"。

在这个例子中,field 插件将在预构建时间生成以下代码并将代码编译到您的程序集

//------------------------------------------------------------------------------
//        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 }

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

每个字段的 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 插件

Myriad 还可以为记录和单一情况的辨别联合生成 lenses。Lens 是类型中一个属性的 ,包含一个 getter 和一个 setter。给定对象 Lens 允许您获取属性的值或更新它,创建一个新的对象。Lens 优势在于可以组合它们以读取或更新对象的嵌套字段。

要为您的类型创建 lenses,首先用 Generator.Lenses 属性注解您想生成 lenses 的类型。

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

Myriad 将生成以下代码

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 })

通常,镜头被定义为围绕一对获取器和设置器的一个单例联合。Myriad还具备调用此类DU构造函数的能力。

要实现这一点,请在您的类型上使用Lens属性,并指定DU构造函数的名称:[<Generator.Lenses("Lens")>],Myriad会生成以下代码

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>

插件

Myriad的插件是通过在项目中包含nuget包来提供的。nuget基础设施提供了必要的MSBuild属性和目标,以便插件能够被Myriad自动使用。按照对字段插件的来源,可以作为编写插件的参考,直到创建更多有关插件的详细信息。

插件的命名

如果您编写了一个插件,则非正式的命名约定是使用:{{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>

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

在您的测试fsproj中,您将为允许插件在本地使用而不是必须消耗nuget包添加以下内容


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

调试

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

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

这些可以通过分别使用<MyriadSdkVerboseOutput>true</MyriadSdkVerboseOutput><MyriadSdkWaitForDebugger>true</MyriadSdkWaitForDebugger>属性从MSBuild触发。

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. 更新CHANGELOG.md,并添加新的条目(例如:## [0.X.X])并提交
  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.0 已计算。 netcoreapp3.1 已计算。
.NET Standard netstandard2.1 兼容。
MonoAndroid monoandroid 已计算。
MonoMac monomac 已计算。
MonoTouch monotouch 已计算。
Tizen tizen60 已计算。
Xamarin.iOS xamarinios 已计算。
Xamarin.Mac xamarinmac 已计算。
1. Xamarin.TVOS xamarintvos 已计算。 
2. Xamarin.WatchOS xamarinwatchos 已计算。 
兼容的目标框架
包含的目标框架(在包中)
了解更多关于 目标框架.NET Standard 的信息。

NuGet 包

此包未被任何 NuGet 包使用。

GitHub 仓库

此包未被任何流行的 GitHub 仓库使用。

版本 下载 最后更新
0.8.3 1,659 9/8/2023
0.8.2 1,122 2/10/2023
0.8.1 493 5/13/2022
0.8.0 454 5/4/2022
0.7.4 743 10/29/2021
0.7.3 317 10/28/2021
0.7.3-alpha 217 10/28/2021
0.7.2 344 10/28/2021
0.7.1 306 10/26/2021
0.7.0 308 10/21/2021
0.6.4 334 10/11/2021
0.6.3 318 9/22/2021
0.6.2 350 9/17/2021
0.6.1 310 9/17/2021
0.6.0 330 9/9/2021
0.5.4 347 9/2/2021
0.5.3 910 5/21/2021
0.5.1 616 4/15/2021
0.5.0 868 12/22/2020
0.4.1 709 9/23/2020
0.4.0 650 7/21/2020
0.2.8 486 6/5/2020
0.2.7 724 5/18/2020
0.2.6 461 5/13/2020