Escolhendo o pacote NuGet do PowerShell certo para seu projeto .NET

Juntamente com os pwsh pacotes executáveis publicados com cada versão do PowerShell, a equipe do PowerShell também mantém vários pacotes disponíveis no NuGet. Esses pacotes permitem direcionar o PowerShell como uma plataforma de API no .NET.

Como um aplicativo .NET que fornece APIs e espera carregar bibliotecas .NET implementando seus próprios (módulos binários), é essencial que o PowerShell esteja disponível na forma de um pacote NuGet.

Atualmente, há vários pacotes NuGet que fornecem alguma representação da área de superfície da API do PowerShell. Nem sempre ficou claro qual pacote usar com um determinado projeto. Este artigo lança alguma luz sobre alguns cenários comuns para projetos .NET direcionados ao PowerShell e como escolher o pacote NuGet certo para direcionar para seu projeto .NET orientado ao PowerShell.

Hospedagem vs referência

Alguns projetos .NET procuram escrever código a ser carregado em um tempo de execução pré-existente do PowerShell (como pwsh, powershell.exe, o Console Integrado do PowerShell ou o ISE), enquanto outros desejam executar o PowerShell em seus próprios aplicativos.

  • A referência é para quando um projeto, geralmente um módulo, se destina a ser carregado no PowerShell. Ele deve ser compilado em relação às APIs que o PowerShell fornece para interagir com ele, mas a implementação do PowerShell é fornecida pelo processo do PowerShell carregando-o. Para referência, um projeto pode usar assemblies de referência ou os assemblies de tempo de execução reais como um destino de compilação, mas deve garantir que ele não publique nenhum deles com sua compilação.
  • Hospedagem é quando um projeto precisa de sua própria implementação do PowerShell, geralmente porque é um aplicativo autônomo que precisa executar o PowerShell. Neste caso, não podem ser utilizados conjuntos de referência puros. Em vez disso, uma implementação concreta do PowerShell deve ser dependida. Como uma implementação concreta do PowerShell deve ser usada, uma versão específica do PowerShell deve ser escolhida para hospedagem; um único aplicativo host não pode ter versões do PowerShell de vários destinos.

Publicando projetos que visam o PowerShell como referência

Nota

Usamos o termo publicar neste artigo para nos referir à execução dotnet publish, que coloca uma biblioteca .NET em um diretório com todas as suas dependências, pronta para implantação em um tempo de execução específico.

Para evitar dependências de projeto de publicação que estão sendo usadas apenas como destinos de referência de compilação, é recomendável definir o atributo PrivateAssets :

<PackageReference Include="PowerShellStandard.Library" Version="5.1.0.0" PrivateAssets="all" />

Se você esquecer de fazer isso e usar um assembly de referência como destino, poderá ver problemas relacionados ao uso da implementação padrão do assembly de referência em vez da implementação real. Isso pode assumir a forma de um NullReferenceException, já que os assemblies de referência geralmente simulam a API de implementação simplesmente retornando null.

Principais tipos de projetos .NET direcionados ao PowerShell

Embora qualquer biblioteca ou aplicativo .NET possa incorporar o PowerShell, há alguns cenários comuns que usam APIs do PowerShell:

  • Implementando um módulo binário do PowerShell

    Os módulos binários do PowerShell são bibliotecas .NET carregadas pelo PowerShell que devem implementar APIs do PowerShell, como os tipos PSCmdlet ou CmdletProvider , para expor cmdlets ou provedores, respectivamente. Como eles são carregados, os módulos procuram compilar com base em referências ao PowerShell sem publicá-lo em sua compilação. Também é comum que os módulos desejem oferecer suporte a várias versões e plataformas do PowerShell, idealmente com um mínimo de sobrecarga de espaço em disco, complexidade ou implementação repetida. Consulte about_Modules para obter mais informações sobre módulos.

  • Implementando um host do PowerShell

    Um Host PowerShell fornece uma camada de interação para o tempo de execução do PowerShell. É uma forma específica de hospedagem, onde um PSHost é implementado como uma nova interface de usuário para o PowerShell. Por exemplo, o PowerShell ConsoleHost fornece uma interface de usuário de terminal para executáveis do PowerShell, enquanto o Host de Serviços do Editor do PowerShell e o Host ISE fornecem uma interface de usuário parcialmente gráfica integrada ao editor em torno do PowerShell. Embora seja possível carregar um host em um processo existente do PowerShell, é muito mais comum que uma implementação de host atue como uma implementação autônoma do PowerShell que redistribui o mecanismo do PowerShell.

  • Chamando para o PowerShell a partir de outro aplicativo .NET

    Como em qualquer aplicativo, o PowerShell pode ser chamado como um subprocesso para executar cargas de trabalho. No entanto, como um aplicativo .NET, também é possível invocar o PowerShell em processo para recuperar objetos .NET completos para uso no aplicativo de chamada. Esta é uma forma mais geral de hospedagem, onde o aplicativo mantém sua própria implementação do PowerShell para uso interno. Exemplos disso podem ser um serviço ou daemon executando o PowerShell para gerenciar o estado da máquina ou um aplicativo Web que executa o PowerShell mediante solicitação para fazer algo como gerenciar implantações na nuvem.

  • Módulos PowerShell de teste de unidade do .NET

    Embora os módulos e outras bibliotecas projetados para expor a funcionalidade ao PowerShell devam ser testados principalmente a partir do PowerShell (recomendamos o Pester), às vezes é necessário testar APIs de unidade escritas para um módulo do PowerShell do .NET. Essa situação envolve o código do módulo tentando direcionar várias versões do PowerShell, enquanto o teste deve executá-lo em implementações específicas e concretas.

Visão geral dos pacotes NuGet do PowerShell

Neste artigo, abordaremos os seguintes pacotes NuGet que expõem APIs do PowerShell:

  • PowerShellStandard.Library, um assembly de referência que permite criar um único assembly que pode ser carregado por vários tempos de execução do PowerShell.
  • Microsoft.PowerShell.SDK, a maneira de direcionar e rehospedar todo o SDK do PowerShell
  • O pacote System.Management.Automation , o tempo de execução principal do PowerShell e a implementação do mecanismo, que pode ser útil em implementações hospedadas mínimas e para cenários de direcionamento específicos da versão.
  • Os assemblies de referência do Windows PowerShell, a maneira de direcionar e rehospedar efetivamente o Windows PowerShell (PowerShell versões 5.1 e inferiores).

Nota

O pacote NuGet do PowerShell não é um pacote de biblioteca .NET, mas fornece a implementação da ferramenta global dotnet do PowerShell. Isso não deve ser usado por nenhum projeto, uma vez que fornece apenas um executável.

PowerShellStandard.Library

A biblioteca padrão do PowerShell é um assembly de referência que captura a interseção das APIs das versões 7, 6 e 5.1 do PowerShell. Isso fornece uma superfície de API verificada em tempo de compilação para compilar o código .NET, permitindo que projetos .NET tenham como destino as versões 7, 6 e 5.1 do PowerShell sem correr o risco de chamar uma API que não estará lá.

O PowerShell Standard destina-se a escrever módulos do PowerShell ou outro código destinado apenas a ser executado depois de carregá-lo em um processo do PowerShell. Por ser um assembly de referência, o PowerShell Standard não contém nenhuma implementação em si, portanto, não fornece funcionalidade para aplicativos autônomos.

Usando o PowerShell Standard com diferentes tempos de execução do .NET

O PowerShell Standard tem como alvo o tempo de execução de destino do .NET Standard 2.0 , que é um tempo de execução de fachada projetado para fornecer uma área de superfície comum compartilhada pelo .NET Framework e pelo .NET Core. Isso permite direcionar um único tempo de execução para produzir um único assembly que funcionará com várias versões do PowerShell, mas tem as seguintes consequências:

  • O PowerShell carregando o módulo ou biblioteca deve estar executando um mínimo de .NET 4.6.1; .NET 4.6 e .NET 4.5.2 não suportam o .NET Standard. Observe que uma versão mais recente do Windows PowerShell não significa uma versão mais recente do .NET Framework; O Windows PowerShell 5.1 pode ser executado no .NET 4.5.2.
  • Para trabalhar com um PowerShell executando o .NET Framework 4.7.1 ou inferior, a implementação do .NET 4.6.1 NETStandard.Library é necessária para fornecer o netstandard.dll e outros assemblies shim em versões mais antigas do .NET Framework.

O PowerShell 6+ fornece seus próprios assemblies shim para encaminhamento de tipos do .NET Framework 4.6.1 (e superior) para o .NET Core. Isso significa que, desde que um módulo use apenas APIs que existem no .NET Core, o PowerShell 6+ pode carregá-lo e executá-lo quando tiver sido criado para o .NET Framework 4.6.1 (o destino de net461 tempo de execução).

Isso significa que os módulos binários que usam o PowerShell Standard para direcionar várias versões do PowerShell com uma única DLL publicada têm duas opções:

  1. Publicação de um assembly criado para o tempo de execução de net461 destino. Esses alicerces envolvem:

    • Publicando o projeto para o net461 tempo de execução
    • Também compilando em tempo netstandard2.0 de execução (sem usar sua saída de compilação) para garantir que todas as APIs usadas também estejam presentes no .NET Core.
  2. Publicação de uma compilação de assembly para o tempo de execução de netstandard2.0 destino. Para tal, é necessário:

    • Publicando o projeto para o netstandard2.0 tempo de execução
    • Pegar as net461 dependências do NETStandard.Library e copiá-las para o local de publicação do assembly do projeto para que o assembly seja encaminhado corrigido no .NET Framework.

Para criar módulos ou bibliotecas do PowerShell destinados a versões mais antigas do .NET Framework, pode ser preferível direcionar vários tempos de execução do .NET. Isso publicará um assembly para cada tempo de execução de destino, e o assembly correto precisará ser carregado no tempo de carregamento do módulo (por exemplo, com um pequeno psm1 como módulo raiz).

Testando projetos do PowerShell Standard no .NET

Quando se trata de testar seu módulo em executores de teste .NET como xUnit, lembre-se de que as verificações em tempo de compilação só podem ir tão longe. Você deve testar seu módulo em relação às plataformas PowerShell relevantes.

Para testar APIs criadas em relação ao PowerShell Standard no .NET, você deve adicionar Microsoft.Powershell.SDK como uma dependência de teste com o .NET Core (com a versão definida para corresponder à versão desejada do PowerShell) e os assemblies de referência apropriados do Windows PowerShell com o .NET Framework.

Para obter mais informações sobre o PowerShell Standard e usá-lo para escrever um módulo binário que funciona em várias versões do PowerShell, consulte esta postagem de blog. Consulte também o repositório padrão do PowerShell no GitHub.

Microsoft.PowerShell.SDK

Microsoft.PowerShell.SDK é um metapacote que reúne todos os componentes do SDK do PowerShell em um único pacote NuGet. Um aplicativo .NET autônomo pode usar Microsoft.PowerShell.SDK para executar a funcionalidade arbitrária do PowerShell sem depender de quaisquer instalações ou bibliotecas externas do PowerShell.

Nota

O SDK do PowerShell refere-se apenas a todos os pacotes de componentes que compõem o PowerShell e que podem ser usados para desenvolvimento .NET com o PowerShell.

Uma determinada Microsoft.Powershell.SDK versão contém a implementação concreta da mesma versão do aplicativo PowerShell, a versão 7.0 contém a implementação do PowerShell 7.0 e executar comandos ou scripts com ele se comportará em grande parte como executá-los no PowerShell 7.0.

Executar comandos do PowerShell a partir do SDK é principalmente, mas não totalmente, o mesmo que executá-los a partir do pwsh. Por exemplo, Start-Job atualmente depende do pwsh executável estar disponível e, portanto, não funcionará com Microsoft.Powershell.SDK por padrão.

A segmentação Microsoft.Powershell.SDK a partir de um aplicativo .NET permite que você se integre a todos os assemblies de implementação do PowerShell, como System.Management.Automation, Microsoft.PowerShell.Managemente outros assemblies de módulo.

A publicação de um direcionamento Microsoft.Powershell.SDK de aplicativo incluirá todos esses assemblies e todas as dependências exigidas pelo PowerShell. Ele também incluirá outros ativos que o PowerShell exigiu em sua compilação, como os manifestos de módulo para Microsoft.PowerShell.* módulos e o ref diretório exigido pelo Add-Type.

Dada a completude do Microsoft.Powershell.SDK, é mais adequado para:

  • Implementação de hosts PowerShell.
  • xTeste de unidade de bibliotecas destinadas a assemblies de referência do PowerShell.
  • Invocando o PowerShell em processo a partir de um aplicativo .NET.

Microsoft.PowerShell.SDK também pode ser usado como um destino de referência quando um projeto .NET se destina a ser usado como um módulo ou carregado pelo PowerShell, mas depende de APIs presentes apenas em uma versão específica do PowerShell. Observe que um assembly publicado em uma versão específica do Microsoft.PowerShell.SDK só será seguro para carregar e usar nessa versão do PowerShell. Para direcionar várias versões do PowerShell com APIs específicas, são necessárias várias compilações, cada uma direcionada à sua própria versão do Microsoft.Powershell.SDK.

Nota

O SDK do PowerShell só está disponível para as versões 6 e superiores do PowerShell. Para fornecer funcionalidade equivalente com o Windows PowerShell, use os assemblies de referência do Windows PowerShell descritos abaixo.

System.Management.Automação

O System.Management.Automation pacote é o coração do SDK do PowerShell. Ele existe no NuGet, principalmente, como um trunfo para Microsoft.Powershell.SDK puxar para dentro. No entanto, ele também pode ser usado diretamente como um pacote para cenários de hospedagem menores e módulos de direcionamento de versão.

Especificamente, o System.Management.Automation pacote pode ser um provedor preferível de funcionalidade do PowerShell quando:

  • Você está procurando usar apenas a System.Management.Automation.Language funcionalidade de linguagem do PowerShell (no namespace), como o analisador do PowerShell, AST e APIs de visitante AST (por exemplo, para análise estática do PowerShell).
  • Você só deseja executar comandos específicos do Microsoft.PowerShell.Core módulo e pode executá-los em um estado de sessão criado com o método de fábrica CreateDefault2 .

Além disso, System.Management.Automation é um assembly de referência útil quando:

  • Você deseja direcionar APIs que estão presentes apenas em uma versão específica do PowerShell
  • Você não dependerá de tipos que ocorrem fora do System.Management.Automation assembly (por exemplo, tipos exportados por cmdlets em Microsoft.PowerShell.* módulos).

Assemblies de referência do Windows PowerShell

Para as versões 5.1 e anteriores do PowerShell (Windows PowerShell), não há SDK para fornecer uma implementação do PowerShell, uma vez que a implementação do Windows PowerShell faz parte do Windows.

Em vez disso, os assemblies de referência do Windows PowerShell fornecem destinos de referência e uma maneira de rehospedar o Windows PowerShell, agindo da mesma forma que o SDK do PowerShell faz para as versões 6 e superiores.

Em vez de serem diferenciados por versão, os assemblies de referência do Windows PowerShell têm um pacote diferente para cada versão do Windows PowerShell:

Informações sobre como usar os assemblies de referência do Windows PowerShell podem ser encontradas no SDK do Windows PowerShell.

Exemplos do mundo real usando esses pacotes NuGet

Diferentes projetos de ferramentas do PowerShell visam diferentes pacotes NuGet do PowerShell, dependendo de suas necessidades. Aqui estão listados alguns exemplos notáveis.

PSReadLine

PSReadLine, o módulo do PowerShell que fornece grande parte da rica experiência de console do PowerShell, tem como alvo o PowerShell Standard como uma dependência, em vez de uma versão específica do PowerShell, e tem como alvo o tempo de execução do net461 .NET em seu csproj.

O PowerShell 6+ fornece seus próprios assemblies shim que permitem que uma DLL direcionada net461 ao tempo de execução "apenas funcione" quando carregada (redirecionando a vinculação ao .NET Framework mscorlib.dll para o assembly .NET Core relevante).

Isso simplifica significativamente o layout e a entrega do módulo PSReadLine, já que o PowerShell Standard garante que as únicas APIs usadas estarão presentes no PowerShell 5.1 e no PowerShell 6+, ao mesmo tempo em que permite que o módulo seja fornecido com apenas um único assembly.

O destino do .NET 4.6.1 significa que o Windows PowerShell em execução no .NET 4.5.2 e no .NET 4.6 não é suportado.

Serviços de Editor do PowerShell

Os Serviços de Editor do PowerShell (PSES) são o back-end da extensão do PowerShell para o Visual Studio Code e são, na verdade, uma forma de módulo do PowerShell que é carregada por um executável do PowerShell e, em seguida, assume esse processo para rehospedar o PowerShell dentro de si mesmo, ao mesmo tempo em que fornece recursos do Protocolo de Serviço de Linguagem e do Adaptador de Depuração.

O PSES fornece metas de implementação concretas para netcoreapp2.1 o PowerShell 6+ de destino (já que o netcoreapp3.1 tempo de execução do PowerShell 7 é compatível com versões anteriores) e net461 para o Windows PowerShell 5.1, mas contém a maior parte de sua lógica em um segundo assembly destinado netstandard2.0 ao PowerShell Standard e ao PowerShell Standard. Isso permite que ele obtenha dependências necessárias para plataformas .NET Core e .NET Framework, enquanto ainda simplifica a maior parte da base de código por trás de uma abstração uniforme.

Como ele é criado com base no PowerShell Standard, o PSES requer uma implementação de tempo de execução do PowerShell para ser testado corretamente. Para fazer isso, os testes xUnit do PSES são puxados para dentro Microsoft.PowerShell.SDK e Microsoft.PowerShell.5.ReferenceAssemblies para fornecer uma implementação do PowerShell no ambiente de teste.

Assim como o PSReadLine, o PSES não pode oferecer suporte ao .NET 4.6 e inferior, mas ele executa uma verificação no tempo de execução antes de chamar qualquer uma das APIs que podem causar uma falha nos tempos de execução inferiores do .NET Framework.

PSScriptAnalyzer

O PSScriptAnalyzer, o linter do PowerShell, deve direcionar elementos sintáticos introduzidos apenas em determinadas versões do PowerShell. Como o reconhecimento desses elementos sintáticos é realizado implementando um AstVisitor2, não é possível usar o PowerShellStandard e também implementar métodos de visitante AST para sintaxes mais recentes do PowerShell.

Em vez disso, o PSScriptAnalyzer direciona cada versão do PowerShell como uma configuração de compilação e produz uma DLL separada para cada uma delas. Isso aumenta o tamanho e a complexidade da compilação, mas permite:

  • Segmentação de API específica da versão
  • Funcionalidade específica da versão a ser implementada essencialmente sem custo de tempo de execução
  • Suporte total para Windows PowerShell até o .NET Framework 4.5.2

Resumo

Neste artigo, listamos e discutimos os pacotes NuGet disponíveis para destino ao implementar um projeto .NET que usa o PowerShell e os motivos que você pode ter para usar um em detrimento do outro.

Se você pulou para o resumo, algumas recomendações gerais são:

  • Os módulos do PowerShell devem ser compilados em relação ao PowerShell Standard se exigirem apenas APIs comuns a diferentes versões do PowerShell.
  • Os hosts e aplicativos do PowerShell que precisam executar o PowerShell internamente devem ter como destino o SDK do PowerShell para PowerShell 6+ ou os assemblies de referência relevantes do Windows PowerShell para Windows PowerShell.
  • Os módulos do PowerShell que precisam de APIs específicas de versão devem direcionar os assemblies de referência do SDK do PowerShell ou do Windows PowerShell para as versões necessárias do PowerShell, usando-os como assemblies de referência (ou seja, não publicando as dependências do PowerShell).