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 durantedotnet 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.
Publicar um aplicativo de arquivo único
Publique um aplicativo de arquivo único usando o comando dotnet publish.
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>
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:
Para acessar arquivos ao lado do executável, use AppContext.BaseDirectory.
Para localizar o nome do arquivo do executável, use o primeiro elemento de Environment.GetCommandLineArgs(), ou começando com o .NET 6, use o nome do arquivo de ProcessPath.
Para evitar totalmente o envio de arquivos soltos, considere o uso de recursos inseridos.
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 deGenerateSingleFileBundle
- 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.