Implantação de arquivo único

O agrupamento de todos os arquivos dependentes do aplicativo em um binário único fornece a um desenvolvedor de aplicativos a opção atraente de implantar e distribuir o aplicativo como um arquivo único. A implantação de arquivo único está disponível tanto para o modelo de implantação dependente da estrutura quanto para aplicativos autossuficientes.

O tamanho do arquivo único em um aplicativo autossuficiente é grande, pois ele inclui o runtime e as bibliotecas de estrutura. No .NET 6, você pode publicar com corte para reduzir o tamanho total de aplicativos compatíveis com corte. A opção de implantação de arquivo único pode ser combinada com as opções de publicação ReadyToRun e Trim.

Importante

Para executar um único aplicativo de arquivos no Windows 7, você deve usar o Runtime do .NET 6.0.3 ou posterior.

Arquivo de projeto de exemplo

Aqui está um arquivo de projeto de exemplo que especifica a publicação de arquivo único:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <PublishSingleFile>true</PublishSingleFile>
    <SelfContained>true</SelfContained>
    <RuntimeIdentifier>win-x64</RuntimeIdentifier>
  </PropertyGroup>

</Project>

Essas propriedades têm as seguintes funções:

  • PublishSingleFile. Habilita a publicação de arquivo único. Também habilita avisos de arquivo único durante dotnet build.
  • SelfContained. Determina se o aplicativo é autossuficiente ou dependente de uma estrutura.
  • RuntimeIdentifier. Especifica o sistema operacional e o tipo de CPU de destino. Também define <SelfContained>true</SelfContained> por padrão.

Os aplicativos de arquivo único são sempre específicos do sistema operacional e da arquitetura. Você precisa fazer uma publicação para cada configuração, como Linux x64, Linux Arm64, Windows x64 e assim por diante.

Os arquivos de configuração de runtime, como *.runtimeconfig.json e *.deps.json, são incluídos no arquivo único. Se um arquivo de configuração extra for necessário, você poderá colocá-lo ao lado do arquivo único.

Publicar um aplicativo de arquivo único

Publique um aplicativo de arquivo único usando o comando dotnet publish.

  1. Adicione <PublishSingleFile>true</PublishSingleFile> ao arquivo de projeto.

    Essa alteração produz um aplicativo de arquivo único na publicação autossuficiente. Ela também mostra avisos de compatibilidade de arquivo único durante o build.

    <PropertyGroup>
        <PublishSingleFile>true</PublishSingleFile>
    </PropertyGroup>
    
  2. Publicar o aplicativo para um identificador de runtime específico usando dotnet publish -r <RID>

    O exemplo a seguir publica o aplicativo para Windows como um aplicativo de arquivo único autossuficiente.

    dotnet publish -r win-x64

    O exemplo a seguir publica o aplicativo para Linux como um aplicativo de arquivo único autossuficiente.

    dotnet publish -r linux-x64 --self-contained false

<PublishSingleFile> deve ser definido no arquivo de projeto para habilitar a análise de arquivo durante o build, mas também é possível passar essas opções como argumentos de dotnet publish:

dotnet publish -r linux-x64 -p:PublishSingleFile=true --self-contained false

Para obter mais informações, confira Publicar aplicativos .NET Core com a CLI do .NET.

Excluir arquivos da inserção

Determinados arquivos podem ser excluídos explicitamente da inserção no arquivo único por meio da definição dos seguintes metadados:

<ExcludeFromSingleFile>true</ExcludeFromSingleFile>

Por exemplo, para colocar alguns arquivos no diretório de publicação, mas não agrupá-los no arquivo:

<ItemGroup>
  <Content Update="Plugin.dll">
    <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
    <ExcludeFromSingleFile>true</ExcludeFromSingleFile>
  </Content>
</ItemGroup>

Incluir arquivos PDB dentro do pacote

O arquivo PDB de um assembly pode ser inserido no próprio assembly (o .dll) usando a configuração abaixo. Como os símbolos fazem parte do assembly, eles também fazem parte do aplicativo:

<DebugType>embedded</DebugType>

Por exemplo, adicione a seguinte propriedade ao arquivo de projeto de um assembly para inserir o arquivo PDB nesse assembly:

<PropertyGroup>
  <DebugType>embedded</DebugType>
</PropertyGroup>

Outras considerações

Aplicativos de arquivo único têm todos os arquivos PDB relacionados ao lado do aplicativo, não agrupados por padrão. Se você quiser incluir PDBs dentro do assembly dos projetos criados, defina DebugType como embedded. Confira Incluir arquivos PDB dentro do pacote.

Os componentes C++ gerenciados não são adequados para implantação de arquivo único. Recomendamos que você escreva aplicativos em C# ou em outra linguagem C++ não gerenciada para que eles sejam compatíveis com um arquivo único.

Bibliotecas nativas

Somente DLLs gerenciadas são agrupadas com o aplicativo em um único executável. Quando o aplicativo é iniciado, as DLLs gerenciadas são extraídas e carregadas na memória, evitando a extração para uma pasta. Com essa abordagem, os binários gerenciados são inseridos no pacote de arquivo único, mas os binários nativos do próprio runtime principal são arquivos separados.

Para inserir esses arquivos para extração e obter um arquivo de saída, defina a propriedade IncludeNativeLibrariesForSelfExtract como true.

A especificação de IncludeAllContentForSelfExtract extrai todos os arquivos, incluindo os assemblies gerenciados, antes de executar o executável. Isso pode ser útil para problemas raros de compatibilidade de aplicativo.

Importante

Se a extração for usada, os arquivos serão extraídos para o disco antes do início do aplicativo:

  • Se a variável de ambiente DOTNET_BUNDLE_EXTRACT_BASE_DIR for definida como um caminho, os arquivos serão extraídos para um diretório nesse caminho.
  • Caso contrário, ao realizar a execução no Linux ou no macOS, os arquivos serão extraídos para um diretório em $HOME/.net.
  • Em execuções no Windows, os arquivos serão extraídos para um diretório em %TEMP%/.net.

Para evitar adulteração, esses diretórios não devem ser graváveis por usuários ou serviços com privilégios diferentes. Não use /tmp nem /var/tmp na maioria dos sistemas Linux e macOS.

Observação

Em alguns ambientes do Linux, como em systemd, a extração padrão não funciona porque $HOME não está definido. Nesses casos, é recomendado definir $DOTNET_BUNDLE_EXTRACT_BASE_DIR explicitamente.

Para systemd, uma boa alternativa é definir DOTNET_BUNDLE_EXTRACT_BASE_DIR no arquivo de unidade do serviço como %h/.net, que systemd expande corretamente para $HOME/.net na conta que executa o serviço.

[Service]
Environment="DOTNET_BUNDLE_EXTRACT_BASE_DIR=%h/.net"

Incompatibilidade de API

Algumas APIs não são compatíveis com a implantação de arquivo único. Os aplicativos poderão exigir modificação se usarem essas APIs. Se você usar uma estrutura ou um pacote de terceiros, talvez eles usem uma dessas APIs e precisem de modificação. A causa mais comum de problemas é a dependência de caminhos de arquivo para arquivos ou DLLs enviados com o aplicativo.

A tabela a seguir tem os detalhes relevantes da API de biblioteca de runtime para uso de arquivo único.

API Observação
Assembly.CodeBase Gera PlatformNotSupportedException.
Assembly.EscapedCodeBase Gera PlatformNotSupportedException.
Assembly.GetFile Gera IOException.
Assembly.GetFiles Gera IOException.
Assembly.Location Retorna uma cadeia de caracteres vazia.
AssemblyName.CodeBase Retorna null.
AssemblyName.EscapedCodeBase Retorna null.
Module.FullyQualifiedName Retorna uma cadeia de caracteres com o valor <Unknown> ou gera uma exceção.
Marshal.GetHINSTANCE Retorna -1.
Module.Name Retorna uma cadeia de caracteres com o valor de <Unknown>.

Temos algumas recomendações para corrigir cenários comuns:

Binários de pós-processamento antes do agrupamento

Alguns fluxos de trabalho exigem o pós-processamento de binários antes do agrupamento. Um exemplo comum é a assinatura. O SDK do dotnet fornece pontos de extensão do MSBuild para permitir o processamento de binários antes do agrupamento de arquivo único. As APIs disponíveis são as seguintes:

  • Um destino PrepareForBundle que será chamado antes de GenerateSingleFileBundle
  • Um <ItemGroup><FilesToBundle /></ItemGroup> que contém todos os arquivos que serão agrupados
  • Uma propriedade AppHostFile que especificará o modelo do AppHost. O pós-processamento pode tentar excluir o AppHost do processamento.

Essa conexão envolve a criação de um destino que será executado entre PrepareForBundle e GenerateSingleFileBundle.

Considere o seguinte exemplo de nó Target do projeto do .NET:

<Target Name="MySignedBundledFile" BeforeTargets="GenerateSingleFileBundle" DependsOnTargets="PrepareForBundle">

É possível que ferramentas precisem copiar arquivos no processo de assinatura. Isso pode acontecer quando o arquivo original é um item compartilhado que não pertence à compilação, por exemplo, o arquivo é proveniente de um cache do NuGet. Nesse caso, espera-se que a ferramenta modifique o caminho do item FilesToBundle correspondente para apontar para a cópia modificada.

Compactar assemblies em aplicativos de arquivo único

Aplicativos de arquivo único podem ser criados com compactação habilitada nos assemblies inseridos. Defina a propriedade EnableCompressionInSingleFile como true. O arquivo único produzido terá todos os assemblies inseridos compactados, o que pode reduzir significativamente o tamanho do executável.

A compactação vem com um custo de desempenho. No início do aplicativo, os assemblies precisam ser descompactados na memória, o que demora um pouco. É recomendável medir a alteração de tamanho e o custo de inicialização da habilitação da compactação antes de usá-la. O impacto pode variar significativamente entre diferentes aplicativos.

Inspecionar um aplicativo de arquivo único

Os aplicativos de arquivo único podem ser inspecionados usando a ferramenta ILSpy. A ferramenta pode mostrar todos os arquivos agrupados no aplicativo e pode inspecionar o conteúdo dos assemblies gerenciados.

Confira também