当前位置:网站首页 > 更多 > 玩电脑 > 正文

[玩转系统] 如何创建标准库二进制模块

作者:精品下载站 日期:2024-12-14 02:56:53 浏览:13 分类:玩电脑

如何创建标准库二进制模块


我最近有一个模块的想法,我想将其实现为二进制模块。我还没有使用 PowerShell 标准库创建一个,所以这感觉是一个很好的机会。我使用创建跨平台二进制模块指南来创建此模块,没有任何障碍。我们将遵循相同的过程,并且我会在此过程中添加一些额外的评论。

笔记

本文的原始版本出现在@KevinMarquette 撰写的博客上。 PowerShell 团队感谢 Kevin 与我们分享这些内容。请查看他的博客:PowerShellExplained.com。

什么是 PowerShell 标准库?

PowerShell 标准库允许我们创建可在 PowerShell 和 Windows PowerShell 5.1 中工作的跨平台模块。

为什么是二进制模块?

当您用 C# 编写模块时,您就放弃了对 PowerShell cmdlet 和函数的轻松访问。但如果您创建的模块不依赖于许多其他 PowerShell 命令,则性能优势可能会非常显着。 PowerShell 是针对管理员而不是计算机进行优化的。通过切换到 C#,您可以摆脱 PowerShell 增加的开销。

例如,我们有一个关键流程,它使用 JSON 和哈希表进行大量工作。我们尽可能地优化了 PowerShell,但该过程仍需要 12 分钟才能完成。该模块已经包含了很多 C# 风格的 PowerShell。这使得到二进制模块的转换变得干净而简单。通过转换为二进制模块,我们将处理时间从 12 分钟以上减少到 4 分钟以下。

混合模块

您可以将二进制 cmdlet 与 PowerShell 高级功能混合使用。您所了解的有关脚本模块的一切都以相同的方式应用。其中包含空的 psm1 文件,以便您稍后可以添加其他 PowerShell 函数。

我创建的几乎所有已编译的 cmdlet 首先都是作为 PowerShell 函数开始的。我们所有的二进制模块都是真正的混合模块。

构建脚本

我在这里保持构建脚本简单。我通常使用大型 Invoke-Build 脚本作为 CI/CD 管道的一部分。它还有更多魔力,比如运行 Pester 测试、运行 PSScriptAnalyzer、管理版本控制以及发布到 PSGallery。一旦我开始为我的模块使用构建脚本,我就能够找到很多可以添加到其中的东西。

规划模块

该模块的计划是为 C# 代码创建一个 src 文件夹,并像构建脚本模块一样构造其余部分。这包括使用构建脚本将所有内容编译到 Output 文件夹中。文件夹结构如下所示:

MyModule
├───src
├───Output
│   └───MyModule
├───MyModule
│   ├───Data
│   ├───Private
│   └───Public
└───Tests

入门

首先,我需要创建文件夹并创建 git 存储库。我使用 $module 作为模块名称的占位符。这将使您在需要时更容易重用这些示例。

$module = 'MyModule'
New-Item -Path $module -Type Directory
Set-Location $module
git init

然后创建根级文件夹。

New-Item -Path 'src' -Type Directory
New-Item -Path 'Output' -Type Directory
New-Item -Path 'Tests' -Type Directory
New-Item -Path $module -Type Directory

二进制模块设置

本文重点讨论二进制模块,因此我们将从这里开始。本节从创建跨平台二进制模块指南中获取示例。如果您需要更多详细信息或有任何问题,请查看该指南。

我们要做的第一件事是检查已安装的 dotnet core SDK 的版本。我使用的是 2.1.4,但在继续之前您应该拥有 2.0.0 或更高版本。

PS> dotnet --version
2.1.4

我正在使用 src 文件夹来处理本节。

Set-Location 'src'

使用 dotnet 命令创建一个新的类库。

dotnet new classlib --name $module

这在子文件夹中创建了库项目,但我不想要额外的嵌套级别。我将把这些文件提升一个级别。

Move-Item -Path .$module\* -Destination .\
Remove-Item $module -Recurse

设置项目的.NET core SDK版本。我有 2.1 SDK,因此我将指定 2.1.0。如果您使用的是 2.0 SDK,请使用 2.0.0

dotnet new globaljson --sdk-version 2.1.0

PowerShell 标准库 NuGet 包添加到项目中。确保您使用的最新版本可满足您所需的兼容性级别。我会默认使用最新版本,但我认为该模块没有利用任何比 PowerShell 3.0 更新的功能。

dotnet add package PowerShellStandard.Library --version 7.0.0-preview.1

我们应该有一个如下所示的 src 文件夹:

PS> Get-ChildItem
    Directory: \MyModule\src

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----        7/14/2018   9:51 PM                obj
-a----        7/14/2018   9:51 PM             86 Class1.cs
-a----        7/14/2018  10:03 PM            259 MyModule.csproj
-a----        7/14/2018  10:05 PM             45 global.json

现在我们准备将自己的代码添加到项目中。

构建二进制 cmdlet

我们需要更新 src\Class1.cs 以包含此启动 cmdlet:

using System;
using System.Management.Automation;

namespace MyModule
{
    [Cmdlet( VerbsDiagnostic.Resolve , "MyCmdlet")]
    public class ResolveMyCmdletCommand : PSCmdlet
    {
        [Parameter(Position=0)]
        public Object InputObject { get; set; }

        protected override void EndProcessing()
        {
            this.WriteObject(this.InputObject);
            base.EndProcessing();
        }
    }
}

重命名该文件以匹配类名。

Rename-Item .\Class1.cs .\ResolveMyCmdletCommand.cs

然后我们就可以构建我们的模块了。

PS> dotnet build

Microsoft (R) Build Engine version 15.5.180.51428 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.

Restore completed in 18.19 ms for C:\workspace\MyModule\src\MyModule.csproj.
MyModule -> C:\workspace\MyModule\src\bin\Debug\netstandard2.0\MyModule.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:02.19

我们可以在新的 dll 上调用 Import-Module 来加载新的 cmdlet。

PS> Import-Module .\bin\Debug\netstandard2.0$module.dll
PS> Get-Command -Module $module

CommandType Name                    Version Source
----------- ----                    ------- ------
Cmdlet      Resolve-MyCmdlet        1.0.0.0 MyModule

如果导入在您的系统上失败,请尝试将 .NET 更新到 4.7.1 或更高版本。创建跨平台二进制模块指南详细介绍了 .NET 支持以及旧版本 .NET 的兼容性。

模块清单

我们可以导入 dll 并拥有一个工作模块,这真是太酷了。我喜欢继续使用它并创建一个模块清单。如果我们想稍后发布到 PSGallery,我们需要清单。

从项目的根目录,我们可以运行此命令来创建我们需要的模块清单。

$manifestSplat = @{
    Path              = ".$module$module.psd1"
    Author            = 'Kevin Marquette'
    NestedModules     = @('bin\MyModule.dll')
    RootModule        = "$module.psm1"
    FunctionsToExport = @('Resolve-MyCmdlet')
}
New-ModuleManifest @manifestSplat

我还将为未来的 PowerShell 功能创建一个空的根模块。

Set-Content -Value '' -Path ".$module$module.psm1"

这允许我在同一个项目中混合普通的 PowerShell 函数和二进制 cmdlet。

构建完整的模块

我将所有内容一起编译到输出文件夹中。我们需要创建一个构建脚本来做到这一点。我通常会将其添加到 Invoke-Build 脚本中,但在此示例中我们可以保持简单。将其添加到项目根目录下的 build.ps1 中。

$module = 'MyModule'
Push-Location $PSScriptRoot

dotnet build $PSScriptRoot\src -o $PSScriptRoot\output$module\bin
Copy-Item "$PSScriptRoot$module\*" "$PSScriptRoot\output$module" -Recurse -Force

Import-Module "$PSScriptRoot\Output$module$module.psd1"
Invoke-Pester "$PSScriptRoot\Tests"

这些命令构建我们的 DLL 并将其放入我们的 output$module\bin 文件夹中。然后它将其他模块文件复制到位。

Output
└───MyModule
    ├───MyModule.psd1
    ├───MyModule.psm1
    └───bin
        ├───MyModule.deps.json
        ├───MyModule.dll
        └───MyModule.pdb

此时,我们可以使用 psd1 文件导入我们的模块。

Import-Module ".\Output$module$module.psd1"

从这里,我们可以将 .\Output$module 文件夹放入 $env:PSModulePath 目录中,只要我们需要它,它就会自动加载我们的命令。

更新:dotnet 新 PSModule

我了解到 dotnet 工具有一个 PSModule 模板。

我上面概述的所有步骤仍然有效,但此模板删除了其中的许多步骤。它仍然是一个相当新的模板,仍在进行一些改进。期待它从这里开始变得更好。

这是安装和使用 PSModule 模板的方式。

dotnet new -i Microsoft.PowerShell.Standard.Module.Template
dotnet new psmodule
dotnet build
Import-Module "bin\Debug\netstandard2.0$module.dll"
Get-Module $module

这个最低限度可行的模板负责添加.NET SDK、PowerShell 标准库,并在项目中创建一个示例类。您可以立即构建并运行它。

重要细节

在结束本文之前,还有一些其他值得一提的细节。

卸载 DLL

一旦加载了二进制模块,您就无法真正卸载它。 DLL 文件将被锁定,直到您卸载它。这在开发时可能会很烦人,因为每次您进行更改并想要构建它时,文件通常都会被锁定。解决此问题的唯一可靠方法是关闭加载 DLL 的 PowerShell 会话。

VS Code 重新加载窗口操作

我的大部分 PowerShell 开发工作都是在 VS Code 中完成的。当我处理二进制模块(或带有类的模块)时,我养成了每次构建时都重新加载 VS Code 的习惯。 Ctrl+Shift+P 弹出命令窗口,并且重新加载窗口始终位于我的列表顶部。

嵌套 PowerShell 会话

另一种选择是拥有良好的 Pester 测试覆盖率。然后,您可以调整 build.ps1 脚本来启动新的 PowerShell 会话、执行构建、运行测试并关闭会话。

更新已安装的模块

当尝试更新本地安装的模块时,这种锁定可能会很烦人。如果任何会话加载了它,您必须找到它并关闭它。从 PSGallery 安装时,这不是一个问题,因为模块版本控制将新版本放置在不同的文件夹中。

您可以设置本地 PSGallery 并作为构建的一部分发布到该 PSGallery。然后从 PSGallery 进行本地安装。这听起来工作量很大,但这就像启动一个 docker 容器一样简单。我在《为 PSRepository 使用 NuGet 服务器》一文中介绍了一种实现此目的的方法。

最后的想法

我没有涉及用于创建 cmdlet 的 C# 语法,但 Windows PowerShell SDK 中有大量相关文档。作为进入更严肃的 C# 的垫脚石,这绝对值得尝试。

您需要 登录账户 后才能发表评论

取消回复欢迎 发表评论:

关灯