Såhär skapar du en binär modul för standardbibliotek
Jag fick nyligen en idé om modul som jag ville implementera som en binär modul. Jag har ännu inte skapat en med hjälp av PowerShell Standard Library så det kändes som en bra möjlighet. Jag använde guiden Skapa en plattformsoberoende binär modul för att skapa den här modulen utan några hinder. Vi ska gå samma process och jag ska lägga till lite extra kommentarer på vägen.
Anteckning
Den ursprungliga versionen av den här artikeln visades på bloggen skriven av @KevinMarquette. PowerShell-teamet tackar Kevin för att han har delat det här innehållet med oss. Kolla in hans blogg på PowerShellExplained.com.
Vad är PowerShell Standard-biblioteket?
Med PowerShell-standardbiblioteket kan vi skapa plattformsoberoende moduler som fungerar i både PowerShell och Windows PowerShell 5.1.
Planera vår modul
Planen för den här modulen är att skapa en src mapp för C#-koden och strukturera resten som jag skulle göra för en skriptmodul. Detta inkluderar att använda ett byggskript för att kompilera allt till en output mapp. Mappstrukturen ser ut så här:
MyModule
├───src
├───Output
│ └───MyModule
├───MyModule
│ ├───Data
│ ├───Private
│ └───Public
└───Tests
Komma igång
Jag använder normalt en Gips mall men min nuvarande mall har inte någon binär modul stöd ännu. Inte en stor sak. Jag skapar den här för hand den här gången.
Först måste jag skapa mappen och skapa git-lagringsplatsen. Jag använder $module som platshållare för modulnamnet. Detta bör göra det enklare för dig att återanvända dessa exempel om det behövs.
$module = 'MyModule'
New-Item -Path $module -Type Directory
Set-Location $module
git init
Skapa sedan mapparna på rotnivå.
New-Item -Path 'src' -Type Directory
New-Item -Path 'Output' -Type Directory
New-Item -Path 'Tests' -Type Directory
New-Item -Path $module -Type Directory
Konfiguration av binär modul
Den här artikeln fokuserar på den binära modulen så att det är där vi ska börja. Det här avsnittet hämtar exempel från guiden Skapa en binär modul för flera plattformar . Läs guiden om du behöver mer information eller har problem.
Det första vi vill göra är att kontrollera vilken version av dotnet Core SDK som vi har installerat. Jag använder 2.1.4, men du bör ha 2.0.0 eller senare innan du fortsätter.
PS> dotnet --version
2.1.4
Jag arbetar med src mappen för det här avsnittet.
Set-Location 'src'
Skapa ett nytt klassbibliotek med hjälp av kommandot dotnet.
dotnet new classlib --name $module
Detta skapade biblioteksprojektet i en undermapp, men jag vill inte ha den extra kapslingsnivån. Jag ska flytta filerna upp en nivå.
Move-Item -Path .\$module\* -Destination .\
Remove-Item $module -Recurse
Ange .NET Core SDK-versionen för projektet. Jag har 2.1 SDK så jag ska ange 2.1.0. Använd 2.0.0 om du använder 2.0 SDK.
dotnet new globaljson --sdk-version 2.1.0
Lägg till PowerShell Standard Library NuGet-paketet i projektet. Se till att du använder den senaste versionen som är tillgänglig för den kompatibilitetsnivå som du behöver. Jag skulle som standard använda den senaste versionen, men jag tror inte att den här modulen utnyttjar några funktioner som är nyare än PowerShell 3.0.
dotnet add package PowerShellStandard.Library --version 7.0.0-preview.1
Vi bör ha en src-mapp som ser ut så här:
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
Nu är vi redo att lägga till vår egen kod i projektet.
Skapa en binär cmdlet
Vi måste uppdatera så att den src\Class1.cs innehåller den här start-cmdleten:
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();
}
}
}
Byt namn på filen så att den matchar klassnamnet.
Rename-Item .\Class1.cs .\ResolveMyCmdletCommand.cs
Sedan kan vi skapa vår modul.
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
Vi kan anropa Import-Module den nya dll-filen för att läsa in vår nya 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
Om importen misslyckas i systemet kan du försöka uppdatera .NET till 4.7.1 eller senare. Guiden Skapa en plattformsoberoende binär modul innehåller mer information om .NET-stöd och kompatibilitet för äldre versioner av .NET.
Modulmanifest
Det är coolt att vi kan importera dll och ha en fungerande modul. Jag gillar att fortsätta med det och skapa ett modulmanifest. Vi behöver manifestet om vi vill publicera till PSGallery senare.
Från roten för vårt projekt kan vi köra det här kommandot för att skapa modulmanifestet som vi behöver.
$manifestSplat = @{
Path = ".\$module\$module.psd1"
Author = 'Kevin Marquette'
NestedModules = @('bin\MyModule.dll')
RootModule = "$module.psm1"
FunctionsToExport = @('Resolve-MyCmdlet')
}
New-ModuleManifest @manifestSplat
Jag ska också skapa en tom rotmodul för framtida PowerShell-funktioner.
Set-Content -Value '' -Path ".\$module\$module.psm1"
På så sätt kan jag blanda både normala PowerShell-funktioner och binära cmdletar i samma projekt.
Skapa hela modulen
Jag kompilerar allt tillsammans i en utdatamapp. Vi måste skapa ett byggskript för att göra det. Jag skulle normalt lägga till detta i ett Invoke-Build skript, men vi kan hålla det enkelt för det här exemplet. Lägg till detta i roten build.ps1 för projektet.
$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"
Dessa kommandon skapar vår DLL och placerar den i vår output\$module\bin mapp. Den kopierar sedan de andra modulfilerna på plats.
Output
└───MyModule
│ MyModule.psd1
│ MyModule.psm1
│
└───bin
MyModule.deps.json
MyModule.dll
MyModule.pdb
Nu kan vi importera modulen med psd1-filen.
Import-Module ".\Output\$module\$module.psd1"
Härifrån kan vi släppa .\Output\$module mappen i vår $env:PSModulePath katalog och den läser in kommandot automatiskt när vi behöver den.
Uppdatering: dotnet new PSModule
Jag har lärt mig att verktyget dotnet har en PSModule mall.
Alla steg som jag beskrev ovan är fortfarande giltiga, men den här mallen klipper ut många av dem. Det är fortfarande en ganska ny mall som fortfarande får lite polska placeras på den. Förvänta dig att det fortsätter att bli bättre härifrån.
Så här använder du installera och använda PSModule-mallen.
dotnet new -i Microsoft.PowerShell.Standard.Module.Template
dotnet new psmodule
dotnet build
Import-Module "bin\Debug\netstandard2.0\$module.dll"
Get-Module $module
Den här minimalt livskraftiga mallen tar hand om att lägga till .NET SDK, PowerShell Standard Library och skapar en exempelklass i projektet. Du kan skapa den och köra den direkt.
Viktig information
Innan vi avslutar den här artikeln, här är några andra detaljer värt att nämna.
Ta bort DLL:er
När en binär modul har lästs in kan du inte ta bort den. DLL-filen är låst tills du tar bort den. Detta kan vara irriterande när du utvecklar eftersom filen ofta är låst varje gång du gör en ändring och vill skapa den. Det enda tillförlitliga sättet att lösa detta är att stänga PowerShell-sessionen som läste in DLL:en.
Vs Code-åtgärd för att läsa in fönster igen
Jag utför det mesta av mitt PowerShell-utvecklingsarbete i VS Code. När jag arbetar med en binär modul (eller en modul med klasser) har jag fått för vana att läsa in VS Code varje gång jag skapar.
Ctrl+Skifta+P dyker upp i kommandofönstret och Reload Window är alltid överst i min lista.
Kapslade PowerShell-sessioner
Ett annat alternativ är att ha bra Pester-testtäckning. Sedan kan du justera build.ps1 skriptet för att starta en ny PowerShell-session, utföra versionen, köra testerna och stänga sessionen.
Uppdatera installerade moduler
Den här låsning kan vara irriterande när du försöker uppdatera din lokalt installerade modul. Om någon session har lästs in måste du leta upp den och stänga den. Det här är ett mindre problem när du installerar från ett PSGallery eftersom versionshantering av moduler placerar den nya i en annan mapp.
Du kan konfigurera ett lokalt PSGallery och publicera till det som en del av din version. Gör sedan din lokala installation från psGallery. Det här låter som mycket arbete, men det kan vara så enkelt som att starta en Docker-container. Jag täcker ett sätt att göra det i mitt inlägg på Använda en NuGet-server för en PSRepository.
Varför binära moduler?
Jag hoppade rakt in i hur man gör en binär modul och nämnde inte varför du vill göra en. I verkligheten skriver du C# och ger enkel åtkomst till PowerShell-cmdletar och funktioner. Det är den främsta anledningen till att jag inte har flyttat till binära moduler tidigare.
Men om du skapar en modul som inte är beroende av många andra PowerShell-kommandon kan prestandafördelarna vara betydande. Genom att släppa i C#, får du kasta omkostnaderna som lagts till av PowerShell. PowerShell har optimerats för administratören, inte datorn, och det lägger till lite omkostnader.
På jobbet har vi en kritisk process som utför mycket arbete med JSON och Hashtables. Vi optimerade PowerShell så mycket vi kunde, men den här processen kördes fortfarande i 12 minuter. Modulen innehöll redan en hel del PowerShell-format i C#-format. Detta gjorde konverteringen till en binär modul ren och enkel att göra. Vår konvertering minskade processen från över 12 minuter till under fyra minuter.
Hybridmoduler
Du kan blanda binära cmdletar med avancerade PowerShell-funktioner. Det är precis vad jag gör i den här guiden. Du kan ta allt du vet om skriptmoduler och allt gäller på samma sätt.
Den tomma psm1 filen som jag skapade idag finns där bara så att du kan släppa andra PowerShell-funktioner senare.
Nästan alla kompilerade cmdletar som vi skapade började som en PowerShell-funktion först. Alla våra binära moduler är verkligen hybridmoduler.
Skapa skript
Jag höll byggskriptet enkelt här. Jag använder vanligtvis ett stort Invoke-Build skript som en del av min CI/CD-pipeline. Det gör mer magi som att köra Pester-tester, köra PSScriptAnalyzer, hantera versionshantering och publicera till PSGallery. När jag började använda ett byggskript för mina moduler kunde jag hitta många saker att lägga till det.
Slutliga tankar
Binära moduler är enkla att skapa. Jag har inte berört C#-syntaxen för att skapa en cmdlet, men det finns gott om dokumentation om den i Windows PowerShell SDK. Det är definitivt något värt att experimentera med som en språngbräda till mer seriösA C#.