Runtime: Suporta distribuição de arquivo único

Criado em 5 out. 2018  ·  225Comentários  ·  Fonte: dotnet/runtime

Esse problema acompanha o progresso no recurso de distribuição de arquivo único do .NET Core 3.0.
Aqui está o documento de design e o plano de preparação para o recurso.

area-Single-File

Comentários muito úteis

Espera-se que o primeiro início seja muito mais lento - ele extrai o aplicativo para o disco - muito IO.

Isso nunca deveria ter acontecido, você torna a experiência do usuário horrível BY DESIGN, escolha horrível, é assim que você faz os usuários odiarem a tecnologia que o desenvolvedor está usando para eles

Conforme mencionado por @Safirion , estamos trabalhando na próxima melhoria para arquivo único, que deve executar a maior parte do código gerenciado diretamente do .exe (sem extração para o disco). Não posso prometer um trem de lançamento ainda.

Por que lançar isso oficialmente agora se vai mudar em breve? deve ser marcado como visualização/experimental

na minha opinião isso é perda de tempo e recursos, concentre-se na compilação AOT e no tree-shaking, coloque todos os seus recursos lá, pare com hacks

Todos 225 comentários

Por curiosidade, como essa iniciativa se compara ao CoreRT? Eles parecem esforços semelhantes ?

Está relacionado a ' possivelmente código de usuário nativo ', ou seja, isso ainda permitirá que o código seja compilado por JIT, não apenas AOT?

Além disso, suponho que os componentes de tempo de execução (' Código nativo (tempo de execução, host, partes nativas do framework.. ') serão os do repositório CoreCLR?

Você está fazendo ótimas perguntas, mas como isso ainda está no início do design, ainda não tenho ótimas respostas.

Por curiosidade, como essa iniciativa se compara ao CoreRT? Eles parecem esforços semelhantes?

Provavelmente haveria resultados semelhantes (um único arquivo), mas o design pode ter características de desempenho diferentes ou recursos que funcionam/não funcionam. Por exemplo, um design possível pode ser essencialmente concatenar todos os arquivos em um aplicativo independente do .NET Core em um único arquivo. São 10s de MB e podem começar mais lentamente, mas, por outro lado, permitiriam todos os recursos do CoreCLR, incluindo carregamento de plug-ins, emissão de reflexão e diagnósticos avançados. CoreRT pode ser considerado o outro extremo do espectro - é MB de um dígito e tem um tempo de inicialização muito rápido, mas por não ter um JIT, não pode carregar plugins ou usar emissão de reflexão e o tempo de compilação é mais lento do que a maioria dos arquivos . NET os desenvolvedores estão acostumados. Atualmente, ele tem algumas outras limitações que podem melhorar com o tempo, mas podem não ser melhores com o .NET Core 3.0 (possivelmente exigindo anotações para reflexão, faltando alguns cenários de interoperabilidade, diagnósticos limitados no Linux). Há também idéias em algum lugar entre os dois. Se as pessoas têm compensações que gostariam de fazer/evitar, ficaríamos curiosos em saber sobre elas.

Está relacionado ao 'código de usuário possivelmente nativo', ou seja, isso ainda permitirá que o código seja compilado por JIT, não apenas AOT?

Por "código de usuário nativo", eu quis dizer que seu aplicativo pode ter algum código nativo C++ (escrito por você ou por um componente de terceiros). Pode haver limites sobre o que podemos fazer com esse código -- se ele estiver compilado em um .dll, a única maneira de executá-lo é fora do disco; se for um .lib, pode ser possível vinculá-lo, mas isso traz outras complicações.

Além disso, presumo que os componentes de tempo de execução ('Código nativo (tempo de execução, host, partes nativas do framework..') serão os do repositório CoreCLR?

Com base em tudo acima, descobriremos quais repositórios estão envolvidos. "Partes nativas da estrutura" incluiriam arquivos nativos CoreFX como ClrCompression e Unix PAL.

Uma distribuição de arquivo único dessa maneira, mesmo que tenha um tempo de inicialização um pouco mais lento, pode ser inestimável para facilitar a implantação. Eu preferiria ter a capacidade de ter todo o poder do que ser forçado a desistir de um pouco disso.

Alguns cenários que nos interessam. Como isso funcionaria em termos de plataforma cruzada?
Suponho que teremos um "arquivo" separado por plataforma?

Com relação ao código nativo, como eu poderia escolher diferentes componentes nativos com base na plataforma?

Alguns cenários que nos interessam. Como isso funcionaria em termos de plataforma cruzada?
Suponho que teremos um "arquivo" separado por plataforma?
Com relação ao código nativo, como eu poderia escolher diferentes componentes nativos com base na plataforma?

@ayende , estou citando o comentário do @morganbr :

um projeto possível poderia ser essencialmente concatenar todos os arquivos em um aplicativo independente do .NET Core em um único arquivo.

A história atual de plataforma cruzada para aplicativos independentes é criar um pacote de implantação por plataforma que você gostaria de direcionar, porque você envia o aplicativo com o tempo de execução, que é específico da plataforma.

@morganbr Agradeço por dedicar um tempo para fornecer uma resposta tão detalhada

Estarei interessado em ver para onde vai o design, esta é uma iniciativa muito interessante

Eu tenho algumas perguntas para pessoas que gostariam de usar arquivo único. Suas respostas nos ajudarão a restringir nossas opções:

  1. Com que tipo de aplicativo você provavelmente usaria? (por exemplo, WPF no Windows? ASP.NET em um contêiner Linux Docker? Outra coisa?)
  2. Seu aplicativo inclui código C++/nativo (não-.NET)?
  3. Seu aplicativo carregaria plug-ins ou outras dlls externas que você não incluiu originalmente na compilação do aplicativo?
  4. Você está disposto a reconstruir e redistribuir seu aplicativo para incorporar correções de segurança?
  5. Você o usaria se seu aplicativo iniciasse 200-500 ms mais lentamente? E 5 segundos?
  6. Qual é o maior tamanho que você considera aceitável para seu aplicativo? 5MB? 10? 20? 50? 75? 100?
  7. Você aceitaria um tempo de compilação de lançamento mais longo para otimizar o tamanho e/ou o tempo de inicialização? Qual é o maior tempo que você aceitaria? 15 segundos? 30 segundos? 1 minuto? 5 minutos?
  8. Você estaria disposto a fazer um trabalho extra se isso reduzisse o tamanho do seu aplicativo pela metade?
  1. Aplicativo de console/IU em todas as plataformas.
  2. Talvez como um componente de terceiros.
  3. Possivelmente sim.
  4. Sim, especialmente se houver um sistema simples do tipo ClickOnce.
  5. Alguma desaceleração inicial pode ser tolerada. O ponto 3 pode ajudar com isso?
  6. Depende dos ativos. Olá mundo deve ter tamanho na ordem de MB.
  7. Não importa se é apenas produção.
  8. Como coisas de reflexão de lista de permissões? sim.

@morganbr , você acha que essas perguntas são melhores para um público mais amplo; ou seja, mais amplo que as pessoas que conhecem esse problema do GitHub?

Por exemplo, um design possível pode ser essencialmente concatenar todos os arquivos em um aplicativo independente do .NET Core em um único arquivo.

Olhando para comprimi-lo; ou usando um sistema de arquivos compactado no arquivo?

@tpetrina , obrigado! O ponto 3 cobre alguns ângulos de design:

  1. A agitação da árvore não combina bem com o carregamento de plug-ins que o agitador de árvore não viu, pois poderia eliminar o código do qual o plug-in depende.
  2. CoreRT atualmente não tem uma maneira de carregar plugins
    O ponto 5 é mais sobre se otimizaríamos o tamanho ou o tempo de inicialização (e quanto)
    Ponto 8, sim, eu estava pensando principalmente em coisas de reflexão

@TheBlueSky , também contatamos outras pessoas, mas ajuda a obter informações das pessoas apaixonadas da comunidade do GitHub.

@benaadams , a compactação está na mesa, mas atualmente estou pensando nela como ortogonal ao design geral. A experimentação leve sugere que a compactação pode obter cerca de 50% de redução de tamanho ao custo de vários segundos de tempo de inicialização (e tempo de construção). Para mim, essa é uma troca radical o suficiente para que, se fizermos isso, seja opcional.

@morganbr vários segundos de tempo de inicialização ao usar a compactação? Acho isso difícil de acreditar ao considerar que o UPX reivindica velocidades de descompressão de

~10 MB/seg em um antigo Pentium 133, ~200 MB/seg em um Athlon XP 2000+.

@morganbr , para mim as respostas são:

1) Serviço (aplicativo de console executando o Kestrel, basicamente). Executando como Windows Service / Linux Daemon ou no docker.
2) Sim
3) Sim, normalmente assemblies gerenciados usando AssemblyContext.LoadFrom . Estes são fornecidos pelo usuário final.
4) Sim, isso é esperado. Na verdade, já empacotamos toda a estrutura de qualquer maneira, portanto, nenhuma mudança nessa perspectiva.
5) Como serviço, não nos importamos muito com o tempo de inicialização. 5 segundos seria razoável.
6) 75MB é provavelmente o limite. Depende muito do tamanho real compactado, pois todos os pacotes são entregues compactados.
7) Para compilações de lançamento, tempos de compilação mais longos (até muito mais longos) são aceitáveis.
8) Sim, absolutamente. O tamanho não importa muito, mas menor é melhor.

Algo que não vi mencionado e é muito importante é a depuração disso.
Espero que isso não destrua os rastreamentos de pilha, e gostaríamos de poder incluir arquivos pdb ou algum tipo de símbolo de depuração.

Sobre a compactação, leve em consideração o fato de que, em quase todos os casos, o mecanismo de entrega real já está compactado.
Por exemplo, pacotes nuget.
Os usuários também são muito versados ​​em descompactar coisas, então isso não é um grande problema.
Eu acho que você pode fazer compressão na lateral.

Obrigado, @ayende! Você está certo que eu deveria ter chamado a depuração. Eu acho que existem apenas algumas pequenas maneiras pelas quais a depuração pode ser afetada:

  1. Pode não ser possível usar Editar e Continuar em um único arquivo (devido à necessidade de uma maneira de reconstruir e recarregar o assembly original)
  2. A compilação de arquivo único pode produzir um PDB ou alguns outros arquivos necessários para depuração além daqueles que acompanham seus assemblies.
  3. Se o CoreRT for usado, ele poderá ter alguns recursos de depuração que serão preenchidos com o tempo (especialmente no Linux/Mac).

Quando você diz "incluir arquivos pdb", você quer esses _dentro_ do arquivo único ou apenas a capacidade de gerá-los e pendurá-los no caso de precisar depurar a compilação de arquivo único?

1) Não é um problema para nós. E&C não é relevante aqui, pois provavelmente será usado apenas para implantação real, não no dia a dia.
2) Idealmente, temos um único arquivo para tudo, incluindo os PDBs, não um arquivo e um conjunto de pdbs ao lado. Já existe a opção de PDB embutido, se isso funcionasse, seria ótimo.
3) Ao falar sobre depuração, estou falando mais sobre o tempo de produção ao invés de anexar um depurador ao vivo. Mais especificamente, informações de rastreamento de pilha, incluindo números de arquivo e linha, sendo capaz de resolver símbolos ao ler o dump, etc.

  1. Principalmente serviços, mas alguma interface do usuário
  2. Alguns fazem, mas isso não seria urgente
  3. sim
  4. sim
  5. Alguns segundos está ok
  6. Não importa para nós. A soma do tamanho da dll está bem
  7. Idealmente não
  8. O tamanho não é de importância primordial para nós

Outra questão para nós é se você seria capaz de fazer isso para componentes individuais também (talvez até encenado)? Por exemplo, temos dlls de bibliotecas que usam muitas dependências. Se pudéssemos empacotá-los, isso economizaria muita dor de gerenciamento de versões, etc. Se estes, por sua vez, pudessem ser empacotados em um exe, seria ainda melhor?

  1. Serviços e alguma interface do usuário.
  2. Não no momento.
  3. sim. Idealmente plugins que podem ser carregados de uma pasta e recarregados em tempo de execução.
  4. sim
  5. Não é um problema, desde que não estejamos empurrando 10-15+.
  6. Soma do tamanho da DLL ou similar.
  7. sim. Para um tempo de compilação de produção não é realmente um problema, desde que compilações de depuração/teste sejam construídas razoavelmente rápido.
  8. Depende, mas a opção seria útil.
  1. Serviço e interface do usuário.
  2. Às vezes.
  3. Sim, normalmente.
  4. sim.
  5. É melhor ser inferior a 5 segundos.
  6. A interface do usuário é inferior a 5 segundos, o serviço não importa.
  7. O tempo de construção não é importante, e o efeito de otimização é o mais importante.
  8. sim.

@tpetrina @ayende @bencyoung @Kosyne @expcat você respondeu sim à pergunta 3 ("Seu aplicativo carregaria plugins ou outras dlls externas que você não incluiu originalmente na compilação do aplicativo?") - você pode nos contar mais sobre seu uso caso?

O principal ponto de venda de uma distribuição de arquivo único é que há apenas um arquivo para distribuir. Se o seu aplicativo tiver plug-ins em arquivos separados, que valor você obteria de uma única distribuição de arquivos que possui vários arquivos? Por que "app.exe+plugin1.dll+plugin2.dll" é melhor que "app.exe+coreclr.dll+clrjit.dll+...+plugin1.dll+plugin2.dll"?

app.exe + 300+ dlls - que é o estado atual hoje é realmente estranho.
app.exe + 1-5 dlls que geralmente são definidas pelo próprio usuário é muito mais fácil.

Nosso cenário é que permitimos determinadas extensões pelo usuário, portanto, normalmente implantamos apenas um único exe e o usuário pode adicionar funcionalidades adicionais conforme necessário.

Não é tanto que _planejamos_ fazer isso, mas queremos _ser capazes_ de fazer isso se surgir a necessidade.

@ayende De acordo , o mesmo conosco.

Além disso, se pudéssemos fazer isso no nível da dll, poderíamos empacotar dependências dentro de nossos assemblies para que não entrassem em conflito com os assemblies do cliente. Ou seja, ao escolher uma versão do NewtonSoft.Json você está atualmente definindo-o para todos os programas, plugins e assemblies de terceiros na mesma pasta, mas se você puder incorporá-lo, os terceiros terão flexibilidade e aumentarão a compatibilidade de versão

Concordo com @ayende .

Obrigado a todos por suas respostas! Com base no número de pessoas que usarão código nativo ou precisam carregar plugins, achamos que a abordagem mais compatível que podemos gerenciar é o lugar certo para começar. Para fazer isso, usaremos uma abordagem de "empacotar e extrair".

Este será um conjunto de ferramentas que essencialmente incorpora todos os arquivos do aplicativo e do .NET como recursos em um executável de extração. Quando o executável for executado, ele extrairá todos esses arquivos em um diretório temporário e, em seguida, será executado como se o aplicativo fosse publicado como um aplicativo de arquivo não único. Ele não começará com compactação, mas podemos adicioná-lo no futuro, se necessário.

O detalhe mais complicado deste plano é para onde extrair os arquivos. Precisamos levar em conta vários cenários:

  • Primeiro lançamento - o aplicativo só precisa extrair para algum lugar no disco
  • Lançamentos subsequentes -- para evitar pagar o custo de extração (provavelmente vários segundos) em cada lançamento, seria preferível que o local de extração fosse determinístico e permitisse que o segundo lançamento usasse arquivos extraídos pelo primeiro lançamento.
  • Upgrade -- Se uma nova versão do aplicativo for lançada, ele não deve usar os arquivos extraídos por uma versão antiga. (O inverso também é verdadeiro; as pessoas podem querer executar várias versões lado a lado). Isso sugere que o caminho determinístico deve ser baseado no conteúdo do aplicativo.
  • Desinstalar -- Os usuários devem ser capazes de encontrar os diretórios extraídos para excluí-los, se desejarem.
  • Tolerância a falhas -- Se uma primeira inicialização falhar após extrair parcialmente seu conteúdo, uma segunda inicialização deverá refazer a extração
  • Executando elevado -- Os processos executados como administrador devem ser executados apenas em locais graváveis ​​pelo administrador para evitar que processos de baixa integridade os adulterem.
  • Executando não elevado -- Os processos executados sem privilégios de administrador devem ser executados em locais graváveis ​​pelo usuário

Acho que podemos explicar tudo isso construindo um caminho que incorpore:

  1. Um diretório base bem conhecido (por exemplo, %LOCALAPPDATA%\dotnetApps no Windows e locais de perfil de usuário em outros sistemas operacionais)
  2. Um subdiretório separado para
  3. Identidade do aplicativo (talvez apenas o nome do exe)
  4. Um identificador de versão. A versão numérica provavelmente é útil, mas insuficiente, pois também precisa incorporar versões de dependência exatas. Um guid ou hash por compilação pode ser apropriado.

Juntos, isso pode ser algo como c:\users\username\AppData\Local\dotnetApps\elevated\MyCoolApp\1.0.0.0_abc123\MyCoolApp.dll
(Onde o aplicativo é chamado MyCoolApp, seu número de versão é 1.0.0.0 e seu hash/guid é abc123 e foi iniciado elevado).

Também haverá trabalho necessário para incorporar arquivos no extrator. No Windows, podemos simplesmente usar recursos nativos, mas Linux e Mac podem precisar de um trabalho personalizado.

Por fim, isso também pode precisar de ajustes no host (para localizar arquivos extraídos) e diagnósticos (para localizar o DAC ou outros arquivos).

CC @swaroop- sridhar @jeffschwMSFT @vitek-karas

Eu sinto que essa cura é pior que a doença. Se tivermos que lidar com diretórios externos (diferentes entre os sistemas operacionais), atualização, desinstalação e afins, isso vai contra a minha razão para desejar esse recurso em primeiro lugar (manter tudo simples, portátil, independente e limpo).

Se absolutamente tiver que ser assim, para o meu projeto, eu preferiria que um único executável principal e os arquivos descompactados residissem em um diretório ao lado desse executável, ou possivelmente a capacidade de decidir para onde esse diretório vai.

Isso sou apenas eu, porém, estou curioso para ouvir os outros também.

Eu tenho que concordar aqui, usar um diretório diferente pode ter muitos problemas interessantes - por exemplo, você coloca um arquivo de configuração ao lado do exe e este exe não é selecionado porque o diretório "real" está em outro lugar.
O espaço em disco pode ser um problema, também bloqueios de arquivos aleatórios devido a políticas de acesso, etc. pp.
Eu gostaria de usar esse recurso, mas não se ele adicionar uma série de modos de falha que são impossíveis de detectar antes.

Acordado com @Kosyne - A solução inicial proposta parece simplesmente automatizar um tipo de "instalador". Se esse fosse o limite do problema que estamos tentando resolver com um único executivo, acho que todos nós teríamos simplesmente executado essa automação.

O objetivo principal da proposta de um único executivo deve ser poder executar um executável em um sistema não gerenciado. Quem sabe se ele ainda tem acesso de gravação a qualquer diretório de "instalação" de destino escolhido? Certamente não deve deixar artefatos de si mesmo após o lançamento (não por padrão).

Como uma pequena modificação à proposta existente para satisfazer o acima: não poderíamos descompactar na memória e executar a partir daí?

Concordo com o resto dos comentários. Descompactar para outro local é algo que _já_ está disponível.
Podemos ter um zip de extração automática que executará os valores extraídos com bastante facilidade. Isso não responde a muitas das preocupações que se destina a responder e é apenas outro nome para instalação.

A localização do arquivo é importante. Por exemplo, no nosso caso, isso significaria:

  • Encontrar o arquivo de configuração (que geramos na hora, se não estiver lá e deixar o usuário personalizar)
  • Encontrar/criar arquivos de dados, que geralmente são relativos ao exe de origem.
  • O PID/nome do processo deve corresponder, para garantir o acompanhamento/suporte adequado.

Um de nossos usuários precisa executar nosso software a partir de um DVD, como isso funciona, em um sistema que pode não _ter_ um HD para rodar.

Concordo que seria melhor fazer tudo na memória. E a preocupação com o tempo de inicialização não é tão grande, eu ficaria bem pagando isso para cada reinicialização ou fazendo manualmente uma etapa para aliviar isso, se necessário.

Outro problema aqui é o tamanho real. Se este é apenas (efetivamente) um instalador, isso significa que estamos falando de tamanhos de arquivo para um aplicativo razoável na casa dos 100 MB, não?

Parece que a construção da solução proposta não requer (muitas, se houver) mudanças no CLR. Os usuários já podem construir uma solução como essa. Não faz sentido adicionar isso ao CoreCLR. Especialmente, uma vez que o caso de uso para isso é bastante restrito e específico.

@GSPP Isso parece basicamente algo que posso fazer hoje com 7z-Extra , concordo que, se for esse o caso, seria melhor _não_ tê-lo.

Desculpe, estou atrasado para esta festa, cheguei aqui depois de seguir um link postado em um bilhete duplicado que estava rastreando.
Depois de ler os comentários mais recentes aqui, lamento ver que você está pensando em empacotar e extrair. Isso parece um exagero, por que não começar com a capacidade de implantar SFAs para aplicativos básicos de console? Parece-me que deve ser possível criar um aplicativo de console rudimentar com alguma rede, IO e algumas dependências externas (nuget, etc) que ficam em um único arquivo.
Acho que o que estou tentando dizer é que, em vez de reunir os requisitos de todos, não reúna os requisitos de ninguém e, em vez disso, comece pequeno com uma primeira iteração que faça sentido para todos e produza resultados rapidamente.

Isso parece basicamente algo que eu posso fazer hoje com 7z-Extra

Você está certo que o número de soluções agnósticas do ambiente de programação para resolver esse problema existe hoje. Outro exemplo de muitos: https://www.boxedapp.com/exe_bundle.html

O valor agregado aqui seria a integração com as ferramentas dotnet para que mesmo usuários não especialistas possam fazê-lo facilmente. Usuários experientes podem fazer isso hoje unindo as ferramentas existentes como você apontou.

Pessoalmente, concordo com você que não está claro que estamos fazendo a escolha certa aqui. Tivemos muita discussão sobre isso dentro da equipe principal.

Colocar um estagiário nele e lançar uma ferramenta global (que é recomendada, mas não suportada) faria o mesmo, e pode ser bastante fácil de instalar também.

Efetivamente, estamos falando de dotnet publish-single-file e isso faria tudo o que é necessário nos bastidores.

Não vejo nada que seja realmente exigido pela estrutura ou pelo tempo de execução para dar suporte a esse cenário e, tornando isso algo explicitamente fora da estrutura, você permitirá aos usuários muito mais liberdade sobre como modificar isso.
Não há "necessidade" de obter um PR (com toda a cerimônia associada, compactação reversa, segurança etc.) que você obteria se desejasse fazer uma alteração na estrutura.
Você apenas bifurca um projeto secundário comum e usa isso.

Observe que, por mais que eu gostasse desse recurso, preferiria não ter algo (o que significa que _sempre_ estará) que possa ser feito do lado de fora.

Quero fazer uma pergunta de nível superior: Qual é a principal motivação para os clientes que desejam a distribuição de um único arquivo?
É principalmente:

  1. Embalagem? Em caso afirmativo, independentemente de a solução ser caixa de entrada ou uma ferramenta de terceiros, quais características são mais importantes?
    a) Tempo de inicialização (além da primeira execução)
    b) Capacidade de execução em ambientes não graváveis
    c) Não deixar arquivos para trás após a execução
    d) Não ter que executar um instalador
    2) Desempenho?
    a) Velocidade (vinculação estática de código nativo, evitando várias cargas de biblioteca, otimizações entre módulos, etc.)
    b) Tamanho do código (precisa apenas de um certificado, trepidação de árvores, etc)
    3) Algum outro?

Estou um pouco preocupado que esse recurso esteja sendo lido por alguns como não tão importante, ou pelo menos pode não ser um bom objetivo para incluir no conjunto de recursos principal aqui. Gostaria apenas de reiterar que acho que seria imensamente poderoso para qualquer aplicativo que atue como um serviço ou submódulo de um aplicativo maior. Um que você, o autor, pode nem ser o desenvolvedor. Não quero ter que passar requisitos de dependência que só podem ser resolvidos com instalações (automáticas ou não) ou artefatos pós-execução em disco que podem precisar de elevação de segurança adicional pelo usuário, etc.
No momento, o .NET não é uma boa escolha para esse problema de nicho. Mas poderia ser (e deveria ser IMO).

O executável compilado deve:

  • Empacote todas as dependências relevantes
  • Não tem artefatos de execução (sem gravações de disco)

Re @swaroop-sridhar Eu ouso dizer que cada aplicativo terá sua própria ordem exclusiva de necessidades de desempenho e, portanto, imagino que a melhor abordagem depois de abordar a solução principal é escolher os frutos mais fáceis e partir daí.

@swaroop-sridhar Para mim, trata-se de embalagem e facilidade de uso para o usuário final.
Não há necessidade de lidar com a instalação de todo o sistema, basta clicar e executar.

Isso é importante porque permitimos que nosso software seja incorporado e um complemento de arquivo único é muito mais fácil de gerenciar.

@strich O ponto sobre a incorporação é bom. Normalmente, somos usados ​​como um componente em uma arquitetura de microsserviços, e reduzir a sobrecarga de implantação tornará isso mais fácil.

O problema não é o que quer que seja ou não esta é uma característica importante. A questão é qualquer que seja a solução proposta (essencialmente compactar as coisas, neste momento) que deve estar _no núcleo_.
As coisas que estão no núcleo têm um padrão muito mais alto para mudanças. Ter isso como uma ferramenta externa seria melhor, porque é mais fácil de modificar e estender.

Para ser honesto, eu preferiria ver uma opção melhor completamente. Por exemplo, é possível carregar dlls da memória, em vez de arquivos (requer algum trabalho, mas é possível).
Se isso acontecer, você pode executar a coisa _inteira_ puramente da memória, com a descompactação sendo feita na memória pura e sem hits de disco.

Isso é algo que deve ir no núcleo, porque muito provavelmente exigirá modificações no tempo de execução para permitir isso.
E isso seria valioso em si e fora dele. E não algo que podemos fazer atualmente.

Então, um bom exemplo é olhar para a experiência de ferramentas Go como o Consul da Hashicorp. Um único arquivo exe que você pode soltar em qualquer máquina em execução. Sem instaladores, sem copiar pastas, sem caçar arquivos de configuração em listas de centenas de arquivos, apenas uma experiência de usuário final muito boa.

Para nós, não tenho certeza se a abordagem na memória funcionaria, pois também gostaríamos que isso funcionasse para dlls de plug-in (portanto, soltar um plug-in também seria um único arquivo em vez de todas as suas dependências), mas qualquer progresso seria seja bom. Analisamos o Fody.Costura e isso funciona bem para algumas coisas, mas tivemos problemas com o .NET Core para isso.

Então, um bom exemplo é olhar para a experiência de ferramentas Go como o Consul da Hashicorp

Eh, para ferramentas como o cônsul a solução ideal seria a corert, não essa improvisação de auto-extração.

@mikedn Por que isso? Não vejo o que a compilação AOT ou JIT tem a ver com o método de implantação?

Quero apoiar as palavras de @strich , as implantações de arquivo único seriam uma lufada de ar fresco para implantações de arquitetura de microsserviço, bem como para qualquer aplicativo de console que seja - ou pelo menos comece sua vida como - uma pequena ferramenta com linha de comando comuta.

Por que isso? Não vejo o que a compilação AOT ou JIT tem a ver com o método de implantação?

Porque lhe dá exatamente o que você quer - um único arquivo exe que você pode soltar em qualquer máquina (bem, qualquer máquina com um sistema operacional adequado, afinal é um arquivo nativo) e executar. Também tende a usar menos recursos, o que para agentes como o cônsul é uma coisa boa. É praticamente o equivalente ao que o Go oferece, mais do que uma solução de extração automática.

@mikedn eu acho, mas

1) Ele ainda não existe em forma de produção (até onde eu sei)!
2) Usamos muitos recursos dinâmicos (geração IL, reflexão arbitrária)
3) Ainda queremos poder adicionar plugins (novamente compactados de forma ideal)

Veja como essa edição era sobre perguntar às pessoas o que elas querem, estamos apenas dando nossa opinião! Nós realmente não queremos ter que mudar para um modelo de tempo de execução diferente apenas para obter esse benefício. Para mim são preocupações ortogonais

Não vejo o que a compilação AOT ou JIT tem a ver com o método de implantação?

Sem um JIT, é mais fácil fazer com que coisas como a história de depuração sejam boas o suficiente. A parte JIT torna o problema mais difícil e é por isso que você não o encontrará em Go. Isso é engenharia, então você joga mais engenheiros no problema mais difícil e convive com a nova complexidade, ou reduz o escopo para outro lugar. O auto-extrator trata-se de reduzir o escopo porque o número de engenheiros com as habilidades necessárias é limitado.

Pessoas com projetos que são mais parecidos com projetos Go (sem requisitos JIT) podem ficar muito felizes com o CoreRT, se estiverem bem com o rótulo "experimental" nele. É muito fácil tentar nos dias de hoje. Ele usa o mesmo coletor de lixo, gerador de código, CoreFX e a maior parte do CoreLib que o CoreCLR completo e produz executáveis ​​pequenos (megabytes de um dígito) que são autocontidos.

Ele ainda não existe em uma forma de produção (até onde eu sei)!

Sim, meu comentário foi direcionado principalmente para pessoas com MS : sorriso :. Eles têm todos esses projetos/idéias paralelos, relacionados, em andamento (corret, illlinker) e agora eles adicionam mais um, essa coisa de auto-extração que, como muitos já apontaram, é um pouco "meh, podemos fazer isso nós mesmos" tipo de coisa. E também vem com desvantagens, como extrair arquivos para um diretório "oculto".

Usamos muitos recursos dinâmicos (geração de IL, reflexão arbitrária)

Isso é algo que a comunidade como um todo pode querer pensar duas vezes. Claro que é útil poder fazer isso, mas meio que entra em conflito com outros desejos, como implantação de arquivo único. Você ainda pode obter a implantação de um único arquivo, mas isso tende a ter um custo - um arquivo bastante grande. Se você está bem com isso, então está perfeitamente bem. Mas, na minha experiência, quanto maior o arquivo, menos útil se torna o aspecto de "arquivo único".

@MichalStrehovsky claro, existem opções diferentes. No entanto, para nós, não podemos usar o experimental (convencer que era hora de migrar para o .NET Core já era difícil o suficiente) e não acho que extrair para uma pasta temporária também funcione no nosso caso. No entanto, o pior caso é continuarmos como estamos e não usarmos esse recurso.

É algo que gostaríamos que se fosse do jeito que gostaríamos :)

@mikedn concordo. Várias soluções paralelas são ainda mais confusas. Acho que nossa solução ideal seria algum tipo de abordagem super ILLinker/weaver, mas estou feliz em deixar isso acontecer e ver onde acabamos.

Estou realmente muito empolgado com o pouso dessa funcionalidade, mas tbm estou igualmente desanimado com a proposta inicial que você postou @morganbr. Minhas respostas para a lista de perguntas que você fez são semelhantes ao que outros postaram (então acho que há um conjunto comum de recursos desejados), mas IMHO a solução proposta de 'descompactar em disco' não é o que eu espero ver implementado e como outros disseram seria quase pior do que a 'doença'. @jkotas

Concordo com @strich e @ayende
O executável compilado deve:
Empacote todas as dependências relevantes
Não tem artefatos de execução (sem gravações de disco)

Carregar .dlls da memória em vez do disco pode não ser fácil, mas esse é o tipo de recursos que o IMO valeria a profunda experiência dos desenvolvedores de baixo nível da MSFT (e, em seguida, aproveitando o CoreClr) versus a proposta acima, que poderia ser implementada como uma ferramenta externa (e já tem, veja https://github.com/dgiagio/warp). Se isso fosse alcançado, eu me pergunto quanta diferença de tempo haveria entre a primeira e as execuções subsequentes? Para inspiração/exemplo, acho que o Linux 3.17+ tem memfd_create que pode ser usado pelo dlopen.

Em outro tópico, gostaria de saber se o requisito de suporte a plugins está superindexando a proposta de design? Valeria a pena tornar essa funcionalidade opt-in, para que apenas as pessoas que precisam dessa capacidade incorram nas possíveis penalidades (falta de tremor de árvore etc.) e todos os outros (maioria significativa?)

Recuando @swaroop- sridhar @MichalStrehovsky , posso ver dois amplos casos de uso que podem ter objetivos/desejos diferentes o suficiente para dificultar a acomodação de todos com uma solução:

  • O conjunto de ferramentas CLI idealmente deseja uma execução rápida sempre, pequena distribuição; talvez melhor ajuste para CoreRT?
  • o microsserviço provavelmente pode tolerar uma primeira execução mais longa (idealmente <1s), distribuível maior, em troca de funcionalidades mais ricas, como JIT e recursos de código dinâmico.

Espero que este braindump faça algum sentido, não estou tentando ser um idiota, mas apenas fornecer feedback porque estou muito interessado neste tópico. Obrigado por todo o seu trabalho árduo e solicitando a opinião da comunidade! :)

Sobre plug-ins.
Basicamente, a única coisa que eu gostaria é _não_ ser bloqueado em chamadas Assembly.LoadFrom ou LoadLibrary .
Não preciso de mais nada e posso fazer o resto sozinha.

@ayende Você pode explicar com um pouco mais de detalhes o que você quer dizer com "ser bloqueado no LoadFrom e tal"?

Por exemplo, algumas das sugestões para isso incluíam o CoreRT, o que significava que (provavelmente) não poderíamos simplesmente carregar uma dll gerenciada.
Mas contanto que eu possa fornecer um caminho para uma dll gerenciada e obter um assembly ou chamar LoadLibrary em uma dll nativa, estou bem com esse mecanismo de plug-in.

Estou dizendo isso para deixar claro que os cenários de plugins não são algo que deve ser _considerado_, mas sim algo que não deve ser _bloqueado_.

Passei algum tempo investigando o código e, à primeira vista, parece que deveria ser _possível_ (falando sobre o Windows apenas para simplificar as coisas):

  • Empacote todo o CoreCLR em um exec (digamos que seja como recurso incorporado ou algo parecido).
  • Enumere as dlls nativas, chame algo como MemoryModule (https://github.com/fancycode/MemoryModule) para carregá-las na memória
  • Invoque o tempo de execução e carregue um assembly da memória.

Eu vou assumir que isso _não_ é tão simples assim. Por exemplo, ICLRRuntimeHost2::ExecuteAssembly não fornece nenhuma maneira de fornecer um buffer, apenas um arquivo em disco.
Isso o torna o primeiro (do que tenho certeza que serão muitos) a parar de realmente fazê-lo funcionar.

Tenho certeza de que há muito código que pode se referir a coisas relacionadas como arquivos que podem falhar, mas esse é o tipo de coisa que quero dizer quando digo que quero um único arquivo exec e por que esse tipo de solução precisa ser feita no CoreCLR e não externamente (como no exemplo zip).

chame algo como MemoryModule

Os binários carregados usando MemoryModule não são diagnosticáveis ​​(por exemplo, nenhum dos depuradores, criadores de perfil etc. regulares funcionará neles), e MemoryModule ignora todas as medidas de segurança integradas do sistema operacional (por exemplo, antivírus, etc.). Não é algo que poderíamos enviar como uma solução suportada.

Eu vou assumir que isso não é tão simples assim.

Certo, seriam necessárias novas APIs (gerenciadas e não gerenciadas) para dar suporte à execução de um pacote de arquivo único não expandido. Os programas e bibliotecas existentes não "simplesmente funcionariam".

Seria razoável pedir que a interface de varredura antimalware fosse
implementado por engenheiros do Windows no MemoryModule como foi recentemente para
todos os assemblies .net incluindo aqueles carregados de mem?

https://twitter.com/WDSecurity/status/1047380732031762432?s=19

EDIT: Hmm MemoryModule parecia algo embutido no sistema operacional para carregar módulos nativos, mas aparentemente não é, provavelmente é motivo suficiente para desconsiderar minha sugestão acima

Em qua, 5 de dezembro de 2018, 01:46 Jan Kotas [email protected] escreveu:

chame algo como MemoryModule

Os binários carregados usando MemoryModule não são diagnosticáveis ​​(por exemplo, nenhum dos
os depuradores regulares, criadores de perfil, etc.) funcionarão neles e o MemoryModule
ignora todas as medidas de segurança do sistema operacional (por exemplo, antivírus, etc.). Não é
algo que poderíamos enviar como solução suportada.

Eu vou assumir que isso não é tão simples assim.

Certo, seriam necessárias novas APIs (gerenciadas e não gerenciadas) para
suportam a execução de um pacote de arquivo único não expandido. O existente
programas e bibliotecas não "simplesmente funcionariam".


Você está recebendo isso porque está inscrito neste tópico.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/dotnet/coreclr/issues/20287#issuecomment-444314969 ,
ou silenciar o thread
https://github.com/notifications/unsubscribe-auth/AEBfudvVHxpbeKa-L_vTQjbnNZZsn6Meks5u1xdlgaJpZM4XK-X_
.

Obrigado por todos os comentários.
Em breve, publicaremos um plano atualizado para oferecer suporte à distribuição de arquivo único.

@NinoFloris veja dotnet/coreclr#21370

Eu percebo que estou atrasado para esta discussão, então minhas desculpas se isso não ajudar.

Para reiterar o que @mikedn estava dizendo, pode ser confuso ter todas essas soluções paralelas e levanta a questão de onde o foco do MS estará no futuro. O fato de o trabalho brilhante feito no CoreRT ainda ser rotulado de experimental sem um roteiro oficial e com funcionalidade semelhante sendo implementada no CoreCLR (CPAOT, agora publicação de arquivo único) dificulta a tomada de decisões de negócios com base na tecnologia. Eu não gostaria de escolher o cavalo errado novamente (Silverlight...).

Por que não ficar com o tempo de execução brilhante do CoreRT e focar o trabalho na incorporação de um JIT (e expandir o trabalho do intérprete até que esteja pronto) no CoreRT para aqueles que desejam recursos de estrutura completos enquanto ainda têm código nativo construído para eles? Não seria possível, no caso de querer habilidades JIT, manter os metadados e JIT dentro de um executável compilado nativamente do CoreRT?
Não seria este o melhor dos mundos? Por exemplo. você teria executáveis ​​menores, tempo de inicialização mais rápido, as limitações do código AOT no CoreRT poderiam ser expandidas com um JIT em vigor e mantendo os metadados e o código IL para aqueles que desejam manter a funcionalidade completa da estrutura.
IMO, o foco deve estar na 'melhor solução possível', em vez de na funcionalidade de curto prazo, que pode desviar a atenção dos objetivos de longo prazo.

Talvez eu esteja enganado, mas acho que publicar um único arquivo como parte do CoreCLR distrairia ainda mais os clientes do CoreRT, que em muitos casos seria uma solução potencialmente melhor para eles. Por exemplo. se isso for marcado como "agora você pode publicar seu código .NET como um único arquivo", mas está criando arquivos enormes, sofrerá no desempenho de inicialização, etc., então posso ver as pessoas começarem a reclamar sobre esse "tempo de execução .NET inchado" novamente . Embora o .NET já tenha uma solução brilhante exatamente para isso no CoreRT, falta o suporte oficial / compromisso oficial do produto para ir até o fim.

Talvez minha preocupação seja principalmente que eu gostaria de ver menos produtos paralelos (novamente citando @mikedn) e mais ênfase em se comprometer profundamente, estender e apoiar aqueles que estão aqui (incluindo o compartilhamento de roteiros).

produtos menos paralelos

Temos apenas um produto: .NET Core. CoreRT não é um produto separado e nunca será. Trata-se de adicionar opções a este único produto. Aqui estão alguns exemplos de opções que temos hoje: GC de estação de trabalho e GC de servidor; publicação de aplicativo independente versus publicação de aplicativo dependente de estrutura.

@jkotas Eu entendo e concordo completamente que adicionar opções geralmente é ótimo. Se você substituir meu uso da palavra "produtos" por "opções/soluções", embora eu ainda mantenha minha preocupação de não saber totalmente se o compromisso de nosso produto com o CoreRT como uma opção/solução dentro do produto geral .NET Core é algo que pode apostar com segurança para ser apoiado nos próximos anos. Imagine como seria se as APIs fizessem alterações imprevisíveis ao longo do tempo e fossem subitamente obsoletas sem que você soubesse se pode ou não confiar em uma API para permanecer lá. Para uma entidade não-MS, as ferramentas/opções que fazem parte do produto têm a mesma sensação. Somos dependentes de vários recursos do CoreRT que não poderíamos fazer (pelo menos atualmente) com o CoreCLR (um deles a capacidade de proteger nossos executáveis ​​com PACE e remover muitos e muitos metadados, IL, etc., ligação estática fácil, compreensão mais fácil da plataforma subjacente, perspectiva de construir ofuscação diretamente no compilador, etc.). Basicamente, eu sinto que o seu trabalho e o de Michal e outros no CoreRT deveriam ser priorizados, pois o IMO é uma das melhores coisas que aconteceram no ecossistema .NET desde que foi projetado. Sempre que se fala de uma nova ferramenta/opção que pode ser vista internamente como concorrente do CoreRT, em vez da opção CoreRT a ser concluída e estendida para mim, parece a alocação errada de recursos. Desculpe, não quis prolongar a discussão, só queria ter certeza de que você entendeu o quanto o trabalho do CoreRT é apreciado e que alguns de nós aqui acreditam que ele deve ser o futuro da plataforma.
Vou me abster de poluir ainda mais esta discussão. Desejando tudo de melhor para as equipes e ansioso para o que quer que você venha com, tenho certeza que será ótimo.

@christianscheuer Seu feedback sobre este problema e sobre outros problemas relacionados ao CoreRT foi muito valioso. Você não está poluindo essa discussão.

fala-se de uma nova ferramenta/opção que pode ser encarada internamente como concorrente do CoreRT

Entre a equipe principal, conversamos sobre várias opções diferentes, ambiciosas ou mais loucas. Não considero o "unzip to disk" um concorrente significativo com o CoreRT full AOT. Cada um tem como alvo um segmento diferente de usuários/aplicativos .NET.

Nossa equipe de gerenciamento de produtos que compila dados de muitos canais diferentes (incluindo problemas no github ou conversas cara a cara com clientes) sugeriu que o maior retorno do investimento é uma solução como a proposta aqui. Ainda estamos validando que temos todos os dados sobre esse direito e que terá o resultado desejado.

Não posso prometer quando a tecnologia CoreRT será enviada como parte do produto .NET Core com suporte neste momento. Eu sei que ele tem pontos fortes únicos e tenho certeza de que vamos lançar uma solução suportada como essa para o segmento de usuários .NET importantes que eventualmente se beneficiarão dela.

A distribuição de arquivo único faz sentido se:

  • Não afeta o tempo de inicialização (caso contrário, apenas o menor dos aplicativos poderá usá-lo)
  • Atua como um VFS (Virtual File System) Permite acessar montagens e recursos como se fossem do disco, da pasta do aplicativo).
  • Não afeta a "patchability" do aplicativo. IE: A Microsoft deve ser capaz de corrigir assemblies críticos, mesmo que estejam incorporados, fornecendo substituições em outro lugar, no nível do sistema.

Assemblies como recursos incorporados, extraídos e copiados para uma pasta temporária não são uma solução necessária da Microsoft. Ele pode ser facilmente implementado por qualquer cliente que precise.

Uma solução de arquivo único "verdadeira" é necessária e forneceria muito valor.

@jkotas seus comentários soam verdadeiros para mim. Eu teria dificuldade em convencer os desenvolvedores .NET da minha empresa (LOB/backoffice/integrations) a mudar para CoreRT porque seria um salto muito grande para eles (especialmente devido ao rótulo Experimental), mas eles podem ver o valor em um solução relativamente simples (de usar) para o Core, como este tópico está discutindo. Obrigado

@popcatalin81 Na verdade, discordo fortemente de seus pontos aqui.

O tempo de inicialização não é significativo para muitos aplicativos. Terei prazer em trocar um aumento de 50% a 100% no tempo de inicialização de qualquer serviço de longa execução com o objetivo de reduzir a complexidade operacional de sua implantação.

Mesmo se adicionarmos 30 segundos ao tempo de inicialização de um serviço, isso não é realmente significativo a longo prazo para os serviços.
Para aplicativos voltados para o usuário, isso provavelmente será um matador, mas até o lançamento do 3.0, praticamente todos os aplicativos CoreCLR são console ou serviços.

Não que o CoreCLR já tenha um modo no qual você possa agrupar a estrutura com seu aplicativo (implantação independente).
No nosso caso (RavenDB), nós _dependemos_ disso para vários propósitos. Primeiro, a capacidade de escolher nossa própria estrutura, independentemente do que nossos usuários estejam usando. Este é um _grande_ benefício para nós, pois significa que não precisamos usar o framework globalmente instalado e, portanto, não estamos vinculados a qualquer coisa que o administrador decidiu que seria o framework (costumávamos levar em conta que estaríamos rodando em .NET 4.0 às vezes, mesmo anos após o lançamento do 4.5, por exemplo, e isso foi uma dor).
Outro aspecto importante é que podemos rodar com _nosso_ fork do framework. Isso é muito útil se nos deparamos com coisas que devemos mudar.

Como parte disso, nos apropriamos dos ciclos de patch e não queremos que mais ninguém mexa com isso.

Eu não _mind_ VFS, mas eu não acho que isso seria necessário. E estou perfeitamente bem em ter que passar por API dedicada / pular alguns aros para chegar aos assemblies/dlls/recursos nativos que são agrupados dessa maneira.

Fui apontado para esse problema por @briacht por dois dias, então estou MUITO atrasado na discussão.
Só quero adicionar meus 2 centavos!

Primeiro minhas respostas para as perguntas do @morganbr :

  1. Aplicativo do Windows, misturando WPF e WinForms.
  2. Atualmente não
  3. Sim, usando Assembly.Load em diferentes variações
  4. sim
  5. Depende se isso é uma penalidade que está em todas as startups. Sim, se for alguns milissegundos, mas 5 segundos: não. Se for uma única vez, 5 segundos não é problema.
  6. 50 MB já é muito grande comparado a <2 MB... mas se isso significar que o tempo de execução do dotnet está incluído... OK. Talvez eu possa fornecer um download com e sem.
  7. O tempo de liberação não importa diretamente, mas não deve incomodar o provedor de CI usando muita CPU por um longo período de tempo.
  8. SIM!

O aplicativo sobre o qual falo Greenshot, a versão atual lançada ainda é voltada para o .NET Framework 2.0 (sim, na verdade), mas funciona sem problemas na versão mais recente (boa compatibilidade com versões anteriores)! Zhe download, incluindo o instalador mas sem o .NET Framework, é de cerca de 1,7 MB. O diretório a partir do qual isso é iniciado contém 1 .exe e 4 .dlls, também existem 11 complementos em um subdiretório.

Eu estava lendo os comentários anteriores, e muitos deles parecem que você quer algo com funcionalidade para a qual eu usaria um instalador. Isso faz algum sentido, pois atualmente não há solução independente de plataforma.

Eu estava discutindo o tópico atual com algumas pessoas da Microsoft em agosto, meu foco principal era falar sobre vincular tudo para reduzir o tamanho e a complexidade do aplicativo para implantação e também reduzir o carregamento do assembly que leva MUITO tempo durante a inicialização da minha aplicação.

Com a próxima versão do Greenshot, que tem como alvo .NET Framework 4.7.1 e dotnet core 3.0 lado a lado, o download resultante para dotnet core 3.0 atualmente tem cerca de 103 dlls (!!!), um exe e 15 add-ons . O tamanho total é de cerca de 37 MB e o maior arquivo é MahApps.Metro.IconPacks.dll (12 MB!!) que contém a maioria dos ícones que usamos, o que provavelmente representa cerca de 4% do arquivo. O mesmo com muitas das outras dlls, eu diria que, em geral, 50% do uso de código é muito!

Se o usuário não quiser instalar o aplicativo, basta baixar um .zip e encontrar o executável entre cerca de 118 arquivos... não é uma experiência muito legal! O tamanho é cerca de 35 MB (17x) maior, então onde está o benefício para o usuário?

Nos meses anteriores ao dotnet core 3.0, eu estava usando o Fody.Costura para fazer praticamente o que foi descrito aqui. Empacotando durante a compilação e descompactando na inicialização! Ele até fez um único executável sem extrair os arquivos possível hackeando um but. Mas também causou muitos problemas, então esta não é a minha solução preferida.

Estou esperando uma solução mais ou menos padrão da qual posso dizer: simplesmente funciona ™
Talvez faça sentido definir uma estrutura de pastas padrão, para que possamos colocar o executável, readme, arquivos de licença etc na raiz e ter diferentes diretórios "conhecidos" para diferentes arquivos (dotnet core, dependências etc). Isso pode facilitar o trabalho das ferramentas e oferecer funcionalidades em que todos podem decidir o que querem usar e o que não. Às vezes, até mesmo executar o zip no diretório resolve alguns problemas.

Ter uma solução semelhante ao Fody.Costury é definitivamente em algum momento que eu poderia usar imagens para simplificar a implantação do Greenshot, mas isso não reduz diretamente o tamanho e o tempo de carregamento. Então eu espero que haja algum tempo gasto aqui também!

@ayende Por favor, permita-me discordar de você também :)

Embora sejam verdadeiros serviços, aplicativos da Web e, em geral, aplicativos de longa duração baseados em servidor não são afetados por um custo de inicialização único, ao mesmo tempo, esses aplicativos não são os que mais obtêm os benefícios do modelo de distribuição de arquivo único. ( Raven é uma exceção, não a norma ;) )

Na minha opinião, a distribuição de arquivo único é voltada principalmente para aplicativos e utilitários voltados para usuários. Usuários que baixarão, copiarão, executarão tais aplicativos. E em um futuro próximo, quando o.Net Core 3.0 oferecer suporte para bibliotecas de interface do usuário, esse modelo de distribuição se tornará bastante comum. No entanto, devo enfatizar, este modelo não será adequado para nenhum aplicativo. Os principais aplicativos ainda usarão um instalador.

Agora a questão é, este modelo de assemblies incorporados copiados para pastas temporárias, funcionará bem se se tornar altamente popular?

Na minha opinião, estes são alguns problemas potenciais para o modelo atual:

  • Dezenas de aplicativos criam milhares de arquivos temporários com assemblies duplicados. Há potencial para criar uma bagunça aqui. (Despercebido pelos usuários, mas ainda uma bagunça)
  • Sem compressão, sem linker. Isso pode criar uma desvantagem para pequenos aplicativos utilitários em comparação com FDD ou compactação de eventos.
  • Na verdade, pode dificultar as implantações de servidor em vez de torná-las mais fáceis, o motivo é: configuração pós-implantação com substituições de token. (Onde você vai para encontrar a pasta temporária para substituir os arquivos de configuração? Você dá ao serviço acesso de gravação à sua própria pasta para criar o arquivo de configuração lá? Isso é um não-não de segurança)

Não estou aqui para dizer que este modelo não é ideal para todos os tipos de aplicativos. Eu o implementei com sucesso e o usei no passado para o Full.Net Framework, e é uma boa opção para determinados aplicativos.

Eu só acho que a implementação ideal é a versão em que um vinculador mescla todos os assemblies em um único arquivo.

@popcatalin81 Um mundo com um único ponto de vista é ruim. E não sou arrogante o suficiente para pensar que meu modelo de implantação é o único disponível.

O caso que tenho em mente é o cenário de download e clique duplo.
No momento, temos que descompactar e clicar em um script de shell para executar, porque entrar em um diretório com centenas de arquivos e encontrar o correto é uma tarefa árdua.

Sobre a configuração em arquivos temporários. Eu seria muito contra isso. Arquivos de configuração são outras coisas visíveis ao usuário _devem_ residir ao lado do arquivo real que está sendo usado, não oculto em algum lugar.
Já vimos o quão ruim isso era com arquivos IsolatedStorage quando os usuários precisavam deles.

Nas versões anteriores do RavenDB, mesclamos muitas coisas para ter um layout de diretório mais simples e reduzir o número de arquivos visíveis, e isso teve um sério impacto na usabilidade do nosso software.

Esse recurso usará o ILMerge? Se não, por que não? Eu gostaria de me educar se houver uma armadilha.

Não gosto da abordagem proposta de "empacotar e extrair", especialmente parte em que extrairia arquivos no disco. É simples, mas tem muitos problemas. Tudo deve funcionar "na memória" sem extrair para o disco.

Eu acho que deveria ser algo como compilador nativo corert ou Costura.Fody .

Opcionalmente, ele também deve tentar reduzir o tamanho do binário (remover referências não utilizadas, IL morto).

Nos meses anteriores ao dotnet core 3.0, eu estava usando o Fody.Costura para fazer praticamente o que foi descrito aqui. Empacotando durante a compilação e descompactando na inicialização!

observe que a costura não (por padrão) "descompacta em disco". ele extrai (na memória) os assemblies incorporados dos recursos e os carrega por meio de bytes

Obrigado pelo contexto @SimonCropp , estamos explorando as duas opções de descompactar em disco e carregar de um fluxo. Cobrir o carregamento de um fluxo para IL puro pode ser possível em nossa primeira iteração.

@jeffschwMSFT btw o conjunto de recursos de assemblies perfeitamente mesclados (e agitação de árvore opcional) é algo que eu adoraria ver como um cidadão de primeira classe. costura é um dos meus projetos mais populares, mas também um que leva um tempo significativo para apoiar. então, se/quando o MS sair com uma alternativa (mesmo como beta), me avise e eu passarei algum tempo revisando e adicionarei um pouco de "existe uma alternativa do MS" ao readme costurado e à descrição do nuget

embora improvável, se você precisar que eu libere algum código costurado sob uma licença diff, me avise.

Obrigado @SimonCropp à medida que começamos a integrar o mono/illinker na cadeia de ferramentas do .NET Core, aceitaremos sua oferta de revisão.

observe que a costura não (por padrão) "descompacta em disco". ele extrai (na memória) os assemblies incorporados dos recursos e os carrega por meio de bytes

Melhor ainda, acredito que ele carrega sob demanda usando o evento AppDomain.AssemblyResolve , então a inicialização não deve ser muito impactada, pois só carregará assemblies (na memória) sempre que necessário.

Tarde para a conversa aqui. Desculpe se isso já foi abordado.

Podemos assumir que o artefato aqui será determinístico? Portanto, a mesma compilação sempre produzirá o mesmo binário? Byte por byte. Ser capaz de distribuir um binário com uma soma de verificação canônica e acompanhante também seria útil.

@morganbr Há muita leitura necessária aqui. Seria possível registrar as decisões tomadas até agora?

@edblackburn estamos montando um design atualizado com base no feedback. @swaroop-sridhar está conduzindo o design agora.

Esse recurso usará o ILMerge? Se não, por que não? Eu gostaria de me educar se houver uma armadilha.

@simplejackcoder ILMerge combina o IL de muitos assemblies em um, mas no processo, perde a identidade original dos assemblies mesclados. Portanto, coisas como usar reflexão com base em nomes de assembly originais falharão.

O objetivo desse esforço não é mesclar assemblies, mas empacotá-los enquanto mantém a identidade do assembly. Além disso, precisamos empacotar não apenas os assemblies, mas também os binários nativos, os arquivos de configuração, possivelmente o tempo de execução e quaisquer outras dependências exigidas pelo aplicativo.

Mesclar IL é um recurso útil para aplicativos que podem se adaptar a ele - é apenas um recurso diferente deste problema.

Uma revisão de design é postada em https://github.com/dotnet/designs/pull/52
Vamos continuar a discussão sobre o PR daqui em diante.

Eu tenho algumas perguntas para pessoas que gostariam de usar arquivo único. Suas respostas nos ajudarão a restringir nossas opções:

  1. Com que tipo de aplicativo você provavelmente usaria? (por exemplo, WPF no Windows? ASP.NET em um contêiner Linux Docker? Outra coisa?)
  2. Seu aplicativo inclui código C++/nativo (não-.NET)?
  3. Seu aplicativo carregaria plug-ins ou outras dlls externas que você não incluiu originalmente na compilação do aplicativo?
  4. Você está disposto a reconstruir e redistribuir seu aplicativo para incorporar correções de segurança?
  5. Você o usaria se seu aplicativo iniciasse 200-500 ms mais lentamente? E 5 segundos?
  6. Qual é o maior tamanho que você considera aceitável para seu aplicativo? 5MB? 10? 20? 50? 75? 100?
  7. Você aceitaria um tempo de compilação de lançamento mais longo para otimizar o tamanho e/ou o tempo de inicialização? Qual é o maior tempo que você aceitaria? 15 segundos? 30 segundos? 1 minuto? 5 minutos?
  8. Você estaria disposto a fazer um trabalho extra se isso reduzisse o tamanho do seu aplicativo pela metade?
  1. Nosso caso de uso dotnet/coreclr#1, de longe, são aplicativos de console que são utilitários. Gostaríamos de fazer uma publicação para Windows ou Linux e especificar --single-file para obter um executável. Para o Windows, esperamos que isso termine em .exe , para o linux, seria um binário executável.
  2. Normalmente não, mas pode ser por meio de um pacote NuGet sem o nosso conhecimento
  3. Não inicialmente, mas esse seria um ótimo recurso futuro
  4. sim
  5. sim
  6. O que quer que funcione
  7. Sim, se otimizou o tamanho resultante do executável. Idealmente, pode ser um sinalizador para definir o nível de otimização usando --optimizing-level=5
  8. Sim, até certo ponto

Obrigado @BlitzkriegSoftware.
Acho que o design proposto aqui cobre seu caso de uso. Por favor, dê uma olhada no design - agradecemos seu feedback.

Eu tenho algumas perguntas para pessoas que gostariam de usar arquivo único. Suas respostas nos ajudarão a restringir nossas opções:

  1. Com que tipo de aplicativo você provavelmente usaria? (por exemplo, WPF no Windows? ASP.NET em um contêiner Linux Docker? Outra coisa?)
  2. Seu aplicativo inclui código C++/nativo (não-.NET)?
  3. Seu aplicativo carregaria plug-ins ou outras dlls externas que você não incluiu originalmente na compilação do aplicativo?
  4. Você está disposto a reconstruir e redistribuir seu aplicativo para incorporar correções de segurança?
  5. Você o usaria se seu aplicativo iniciasse 200-500 ms mais lentamente? E 5 segundos?
  6. Qual é o maior tamanho que você considera aceitável para seu aplicativo? 5MB? 10? 20? 50? 75? 100?
  7. Você aceitaria um tempo de compilação de lançamento mais longo para otimizar o tamanho e/ou o tempo de inicialização? Qual é o maior tempo que você aceitaria? 15 segundos? 30 segundos? 1 minuto? 5 minutos?
  8. Você estaria disposto a fazer um trabalho extra se isso reduzisse o tamanho do seu aplicativo pela metade?
  1. Aplicativo WPF .Core 3.0
  2. Talvez em um pacote nuget
  3. Não
  4. sim
  5. Sim para 200-500ms máximo
  6. Não é importante desde que não seja 2 ou 3 vezes maior que o tamanho da pasta de publicação original
  7. É para produção, então não é realmente importante
  8. Sim, mas tem a possibilidade de não fazer isso pode ser útil se o trabalho for uma biblioteca de terceiros

O feedback que você recebeu foi principalmente para desenvolvimento antes de 2019, que geralmente são serviços da web, aplicativos de console e outros serviços lançados em segundo plano. Mas agora com o .net core 3.0, precisamos de soluções para aplicativos WPF e UI. O .Net Framework 4.8 não suportará o .Net Standard 2.1 e precisamos de uma solução para substituir o ILMerge que usamos até agora para aplicativos WPF, pois precisamos atualizar nossos aplicativos do .net framework (dead framework) para o .net core.

Um pacote de extração automática claramente não é uma boa solução, conforme explicado em outros comentários anteriores para esse tipo de programa.

  1. Principalmente aplicativos CLI. Pequenos serviços do Windows e o aplicativo WPF ocasional também são interessantes. Para coisas da web, a distribuição de um único arquivo parece basicamente irrelevante. Por esse motivo, o restante é principalmente orientado ao Windows; nosso interesse no Linux .NET Core é em torno das coisas da web.
  2. Às vezes. Quando isso acontece, é quase sempre para bibliotecas de compactação. Se as coisas nativas não fossem empacotadas, não seria o fim do mundo. Uma boa solução para coisas gerenciadas puras que exigiam a implantação de uma dependência nativa como uma dll separada ainda teria valor.
  3. Não, não nos casos em que nos preocupamos com arquivo único.
  4. sim.
  5. 200ms é aproximadamente o limite antes que as coisas fiquem irritantes para uma CLI iniciar. > 1 segundo e quase certamente ficaríamos com .NET Framework (ILMerge + ngen) ou olharíamos para outras linguagens que compilam para um binário nativo com qualquer tempo de execução necessário vinculado estaticamente. Em muitos casos, estamos falando de coisas que foi implementado em C# porque um aplicativo C# integrado e ngen-ed na verdade inicia razoavelmente rápido. Algumas dessas coisas são, em termos de complexidade, razoáveis ​​para implementar em uma linguagem de script, mas tendem a ter custos de inicialização significativos, especialmente se você precisar inserir vários módulos.
  6. Depende. Mais de 10 MB para hello world seria uma venda difícil. Enormes também executáveis ​​tendem a ter custos de inicialização não triviais. Mesmo que o sistema operacional seja teoricamente capaz de iniciar a execução sem ler tudo, pelo menos no Windows, quase sempre há algo que deseja obter um hash para algum propósito de segurança primeiro. Falando nisso, essa coisa de descompactar um monte de binários para temp vai enlouquecer o AV (Defender absolutamente incluído), em termos de utilização de recursos, verificando freneticamente, se nada mais.
  7. Certo. Eu ficaria feliz em adicionar um minuto ou mais a uma compilação de lançamento se isso significasse obter algo de tamanho razoável e iniciar a execução rapidamente.
  8. Definitivamente.

Relendo a proposta, ela não aborda nenhum cenário que temos e que não usaríamos. Pode haver alguém para quem ele resolva um problema, mas não somos nós.

Parece que há muitas coisas relevantes para cenários como o nosso que estão sendo trabalhados que simplesmente não se juntaram em uma cadeia de ferramentas que é realmente utilizável/provável de continuar funcionando. Talvez isso seja apenas ignorância da minha parte, mas tento prestar atenção e não está claro para mim. Entre ILLink, Microsoft.Packaging.Tools.Trimming, CoreRT, .NET Native e o que quer que aconteça neste problema, parece que deve haver alguma maneira de obter uma única árvore de inicialização rápida (possivelmente AoT?), binário de tamanho razoável fora do .NET Core (e não para UWP).

  1. A distribuição de arquivo único seria super útil para aplicativos https://github.com/AvaloniaUI/Avalonia (Windows, Linux, MacOS) e WPF (Windows). Ter um único exe facilita as atualizações automáticas, por exemplo, e em geral é mais conveniente ter um único arquivo em comparação com uma centena de dlls.
  2. Sim (no sentido de que faz referência a bibliotecas nativas pré-construídas, como sqlite e skia)
  3. Não
  4. sim
  5. 200ms provavelmente é bom. Ter 5s de tempo de inicialização para um aplicativo GUI não é bom.
  6. Realmente não importa
  7. Realmente não importa. Poderíamos construir um único arquivo apenas para cenários de produção.
  8. sim

Por favor, não vá com a abordagem de pacote e extração. Já poderíamos conseguir isso usando ferramentas de arquivamento.

Por favor, não vá com a abordagem de pacote e extração. Já poderíamos conseguir isso usando ferramentas de arquivamento.

Eu não posso concordar mais. Já existem várias ferramentas disponíveis que fazem isso.

Obrigado pelo feedback @Safirion , @mattpwhite , @x2bool , @thegreatco.
A partir deles, vejo uma variedade de cenários de uso (aplicativos de console, WPF, aplicativos GUI etc.), mas os requisitos gerais parecem semelhantes:

  • Concentre-se no tempo de execução/inicialização em vez de no tempo de construção.
  • Capacidade de reconstruir aplicativos para lançamentos/patches posteriores
  • Capacidade de executar diretamente do pacote configurável (especialmente para componentes de gerenciamento puro).

Acredito que esses são os requisitos que tentamos abordar no documento de design .

Por favor, não vá com a abordagem de pacote e extração

Sim, conforme explicado neste documento de preparação , o objetivo é minimizar progressivamente a dependência da extração de arquivos.

Relendo a proposta, ela não aborda nenhum cenário que temos e que não usaríamos.

@mattpwhite , queria esclarecer se você acha que essa solução não funciona para você com base em:

  • A abordagem baseada em auto-extração proposta anteriormente neste item de trabalho, ou
  • O design observado neste documento
    Acredito que o documento posterior tenta abordar o cenário que você detalhou. Obrigado.

Parece que há muitas coisas relevantes para cenários como o nosso que estão sendo trabalhados que simplesmente não se juntaram em uma cadeia de ferramentas que é realmente utilizável/provável de continuar funcionando.

@mattpwhite , há um trabalho em andamento para integrar ILLink (tree-shaking), crossgen (gerando código nativo pronto para execução) e aspectos de agrupamento de arquivo único na CLI msbuild/dotnet de maneira simplificada para .net core 3. Uma vez isso é feito, o uso dessas ferramentas pode ser feito facilmente (ex: definir certas propriedades do msbuild).

Todas essas ferramentas vêm com compensações. Por exemplo, crossgen gera código nativo antes do tempo, ajudando na inicialização, mas tende a aumentar o tamanho dos binários. Usando a opção de arquivo único, os aplicativos podem ser publicados em um único arquivo, mas isso pode retardar a inicialização, porque os binários nativos são derramados no disco etc.

@swaroop-sridhar, obrigado por responder.

queria esclarecer se você acha que esta solução não funciona para você com base em

Parecia que a extração ainda está em jogo para qualquer coisa independente, e o status do crossgen (ou qualquer outra coisa para reduzir no tempo do JIT no lançamento) não estava claro para mim. Ler o documento de preparação que o design atual vincula a ele parecia que as coisas que tornariam isso mais interessante para nós estavam mais longe. Dado isso, meu pensamento era que continuaríamos presos usando o .NET Framework para esses nichos por algum tempo. Eu posso ter entendido errado.

Todas essas ferramentas vêm com compensações. Por exemplo, crossgen gera código nativo antes do tempo, ajudando na inicialização, mas tende a aumentar o tamanho dos binários.

Sim, entendido que alguns destes são facas de dois gumes. Com a maneira como o JIT tradicionalmente funciona, o ngen sempre foi uma vitória para os aplicativos CLI/GUI. É possível que alguma combinação de compilação em camadas e o fato de que carregar o tempo de execução em si não seja necessariamente amortizado por muitos outros processos do .NET Framework em execução na mesma caixa torne isso menos claro no Core.

@swaroop-sridhar a grande preocupação que tenho com a proposta atualmente aceita é esta etapa

Os 216 arquivos restantes serão extraídos para o disco na inicialização.

Além do fato de que "Hello World" parece exigir 216 arquivos para ser executado, extraí-los para o disco em algum local dificulta a remoção de um aplicativo de linha de comando simples. Quando as pessoas excluem uma ferramenta que não veio por meio de um instalador, elas não esperam precisar caçar outros arquivos para removê-la completamente.

IMHO, devemos ter como alvo uma experiência de publicação semelhante à do golang. Embora exija que crossgen esteja no lugar para corresponder totalmente à experiência golang (e pode-se argumentar que uma partida 1:1 não é uma coisa _boa_, pois perdemos o JIT e a compilação em camadas), um boa parte pode ser alcançada sem ele. Usei muitas ferramentas ao longo dos anos para obter uma experiência semelhante, e a mais eficaz foi recursos incorporados com um gancho no carregador de montagem (o ILMerge era melindroso e o Costura.Fody ainda não existia). O que está descrito na proposta nem sequer replica totalmente essa funcionalidade. O .NET Core tem um futuro muito forte, mas seu uso para o desenvolvimento de ferramentas de linha de comando é limitado, desde que precisemos carregar cerca de 100 dependências ou cuspi-las em algum lugar do disco com algum código bootstrap ondulado.

Meu caso de uso dotnet/coreclr#1 é utilitários de linha de comando, um arquivo, nenhuma extração seria minha preferência. Mas eu destacaria que dar uma escolha como parte do comando muda para auto-extração all-in-one vs. all-in-one. No caso de extração automática, deve incluir um undo que limpa os arquivos descompactados. No primeiro caso, remover o utilitário significa deletar um arquivo que o torna adequado para residir em uma pasta de utilitários gerais, enquanto que no 2º caso, a instalação deve ter sua própria pasta para facilitar a limpeza e evitar cenários em que a desinstalação remove arquivos compartilhados. Apenas alguns pensamentos. Como eu disse, o utilitário de um arquivo (sem necessidade de instalação) é meu principal caso de uso, pois faço utilitários para projetos (como ferreiro) com frequência. Mas posso ver um caso de uso para ambos os cenários. Mas eles são diferentes .

Eu tenho algumas perguntas para pessoas que gostariam de usar arquivo único. Suas respostas nos ajudarão a restringir nossas opções:

  1. Com que tipo de aplicativo você provavelmente usaria? (por exemplo, WPF no Windows? ASP.NET em um contêiner Linux Docker? Outra coisa?)
  2. Seu aplicativo inclui código C++/nativo (não-.NET)?
  3. Seu aplicativo carregaria plug-ins ou outras dlls externas que você não incluiu originalmente na compilação do aplicativo?
  4. Você está disposto a reconstruir e redistribuir seu aplicativo para incorporar correções de segurança?
  5. Você o usaria se seu aplicativo iniciasse 200-500 ms mais lentamente? E 5 segundos?
  6. Qual é o maior tamanho que você considera aceitável para seu aplicativo? 5MB? 10? 20? 50? 75? 100?
  7. Você aceitaria um tempo de compilação de lançamento mais longo para otimizar o tamanho e/ou o tempo de inicialização? Qual é o maior tempo que você aceitaria? 15 segundos? 30 segundos? 1 minuto? 5 minutos?
  8. Você estaria disposto a fazer um trabalho extra se isso reduzisse o tamanho do seu aplicativo pela metade?
  1. CLI (Windows, Linux, MacOS), WPF e WinForms.
  2. Às vezes.
  3. Sim, muitas vezes e acho que o cenário em que um plugin pode reutilizar uma biblioteca compartilhada do aplicativo principal deve ser abordado!
  4. sim.
  5. 200-500ms estão ok!
  6. Para um aplicativo CLI simples sem muitas dependências, 10 MB são suficientes (compactados)!
  7. Sim definitivamente! O tempo de compilação (especialmente para compilações de lançamento!) não importa! O tamanho da compactação e/ou mudança para publicação de um único arquivo deve ser opcional!
  8. Sim, por exemplo, CoreRT!

IMHO, devemos ter como alvo uma experiência de publicação semelhante à do golang.

@thegreatco : Sim, a compilação completa para código nativo é uma boa opção para criar aplicativos de arquivo único. Linguagens como GO são construídas com compilação antecipada (AOT) como seu modelo principal - que vem com certas limitações nos recursos de linguagem dinâmica obtidos por meio da compilação JIT.

CoreRT é a solução .Net Core para aplicativos compilados AOT. No entanto, até que o CoreRT esteja disponível, estamos tentando obter aplicativos de arquivo único por outros meios nesta edição.

Além do fato de que "Hello World" parece exigir 216 arquivos para ser executado,

Existem algumas opções disponíveis para reduzir o número de arquivos necessários para o HelloWorld"

  • Crie um aplicativo dependente de estrutura, que precisa apenas de 3 a 4 arquivos
  • Se precisarmos construir aplicativos independentes, use o ILLinker para reduzir o número de dependências de arquivos

extraí-los para o disco em algum local dificulta a remoção de um aplicativo de linha de comando simples.

  • À medida que o desenvolvimento do recurso de arquivo único avança para os estágios posteriores, o número de arquivos derramados no disco diminuirá e, idealmente, será zero para a maioria dos aplicativos. Mas nos estágios iniciais, vários arquivos (aqueles que contêm código nativo) terão que ser derramados.
  • A localização dos arquivos derramados no disco é sempre determinística e configurável. Assim, os arquivos derramados podem ser limpos com um script simples. Mas concordo que isso não é o ideal.

Obrigado pelo esclarecimento @mattpwhite.

Sim, até os estágios posteriores do desenvolvimento do recurso, os aplicativos independentes verão os arquivos extraídos para o disco. Você pode usar algumas das técnicas que escrevi no comentário acima para melhorar a situação de seus aplicativos.

Obrigado pela sua resposta @BlitzkriegSoftware.

Mas gostaria de salientar que dar uma escolha como parte do comando muda para all-in-one vs. all-in-one-auto-extraível

Se um aplicativo publicado como um único arquivo requer extração depende do conteúdo do aplicativo (por exemplo: se ele contém arquivos nativos). O design do bundler fornece algumas configurações para decidir se todos os arquivos devem ser extraídos. Podemos adicionar uma propriedade para dizer que o aplicativo não deve usar extração de arquivos em tempo de execução.

<propertygroup>
    <SingleFileExtract> Never | Always | AsNeeded </SingleFileExtract>
</propertygroup>

O entendimento aqui é: ao compilar SingleFileExtract=Never, se o aplicativo contiver arquivos que só podem ser manipulados por meio de extração (binários nativos), a publicação em um único arquivo falhará.

No caso de extração automática, deve incluir um desfazer que limpa os arquivos descompactados.

Parece razoável. A localização exata da extração (se houver) é determinística e configurável. Assim, podemos ter um script para remover um aplicativo e todas as suas dependências extraídas.

Obrigado pela sua resposta @DoCode. Seu cenário é interessante porque usa plugins.
Se os próprios plug-ins tiverem algumas dependências (que não são compartilhadas com o aplicativo principal), você precisa/prefere publicar o plug-in também como um único arquivo (com dependências incorporadas)? Obrigado.

Sim @swaroop-sridhar, esse é o objetivo. Achamos que neste caso o plugin deve ter suas dependências incorporadas. Apenas as dependências compartilhadas deveriam ter ficado fora do aplicativo principal de arquivo único.

Hoje, @madskristensen compartilhou um bom esclarecimento com o Visual Studio e o popular pacote Newtonsoft.Json NuGet.

Nós as mesmas situações em uma situação semelhante às vezes!

Obrigado @DoCode. Planejamos o endereço do recurso de plug-ins de arquivo único, uma vez que os aplicativos de arquivo único estejam implementados e estáveis. O documento de design fala sobre plugins aqui .

Obrigado pelo envolvimento contínuo neste recurso @swaroop-sridhar, é muito apreciado. Ter um comando de desfazer será ótimo para os estágios originais, especialmente.

  1. Com que tipo de aplicativo você provavelmente usaria? (por exemplo, WPF no Windows? ASP.NET em um contêiner Linux Docker? Outra coisa?)
    -- Utilitários de linha de comando (aplicativos de console)
  2. Seu aplicativo inclui código C++/nativo (não-.NET)?
    -- Não
  3. Seu aplicativo carregaria plug-ins ou outras dlls externas que você não incluiu originalmente na compilação do aplicativo?
    -- Normalmente não
  4. Você está disposto a reconstruir e redistribuir seu aplicativo para incorporar correções de segurança?
    -- Sim
  5. Você o usaria se seu aplicativo iniciasse 200-500 ms mais lentamente? E 5 segundos?
    -- < 1 segundo seria minha preferência
  6. Qual é o maior tamanho que você considera aceitável para seu aplicativo? 5MB? 10? 20? 50? 75? 100?
    -- O tamanho não é importante
  7. Você aceitaria um tempo de compilação de lançamento mais longo para otimizar o tamanho e/ou o tempo de inicialização? Qual é o maior tempo que você aceitaria? 15 segundos? 30 segundos? 1 minuto? 5 minutos?
    -- 30 segundos
  8. Você estaria disposto a fazer um trabalho extra se isso reduzisse o tamanho do seu aplicativo pela metade?
    -- Claro se pode ser roteirizado ou configurado

1 - Com que tipo de aplicativo você provavelmente usaria? (por exemplo, WPF no Windows? ASP.NET em um contêiner Linux Docker? Outra coisa?)

  • Jogos
  • Ferramentas de linha de comando

2 - Seu aplicativo inclui código C++/nativo (não-.NET)?

sim

3 - Seu aplicativo carregaria plugins ou outras dlls externas que você não incluiu originalmente na compilação do seu aplicativo?

Sim, mas com símbolos já conhecidos, não confio na reflexão, pois é lenta

4 - Você está disposto a reconstruir e redistribuir seu aplicativo para incorporar correções de segurança?

sim

5 - Você usaria se seu aplicativo iniciasse 200-500 ms mais devagar? E 5 segundos?

Não, já está um pouco lento

6 - Qual é o maior tamanho que você considera aceitável para seu aplicativo? 5MB? 10? 20? 50? 75? 100?

Depende do projeto, mas para uma ferramenta de linha de comando simples, não espero que seja maior que mbs de um dígito

7 - Você aceitaria um tempo de compilação de lançamento mais longo para otimizar o tamanho e/ou o tempo de inicialização? Qual é o maior tempo que você aceitaria? 15 segundos? 30 segundos? 1 minuto? 5 minutos?

Contanto que não afete o tempo de desenvolvimento/iteração, estou bem com um tempo de compilação de lançamento mais longo
Mas lembre-se, os aplicativos .net core já são construídos por mais tempo do que os aplicativos tradicionais do .net framework

Essa tendência de software mais lento a cada ano NÃO é aceitável, temos hardware melhor, não há razão para essa lentidão

8 - Você estaria disposto a fazer um trabalho extra se isso reduzisse o tamanho do seu aplicativo pela metade?

SIM!

Eu realmente espero ter a compilação AOT com CoreRT funcionando algum dia para que possamos ter aplicativos compilados nativamente como golang e rust e outras linguagens compiladas AOT. Da mesma forma que Unity3D faz isso com IL2CPP.

@morganbr Aqui estão algumas informações minhas e de pessoas com quem trabalho. Eu uso muitas linguagens, mas principalmente C#, Go e Python.

Com que tipo de aplicativo você provavelmente usaria? (por exemplo, WPF no Windows? ASP.NET em um contêiner Linux Docker? Outra coisa?)

Programas ASP.NET Core e Console, principalmente no Linux, potencialmente algumas cargas de trabalho do Windows, dependendo se o ML.NET atende às nossas necessidades.

Seu aplicativo inclui código C++/nativo (não-.NET)?

Improvável neste momento.

Seu aplicativo carregaria plug-ins ou outras dlls externas que você não incluiu originalmente na compilação do aplicativo?

Depende. Atualmente, podemos puxar todas as nossas dependências como pacotes, mas pude ver uma situação em que pagamos por uma biblioteca comercial, o que pode significar o uso de DLLs externas.

Você está disposto a reconstruir e redistribuir seu aplicativo para incorporar correções de segurança?

Absolutamente. Esta é uma prioridade para nós.

Você o usaria se seu aplicativo iniciasse 200-500 ms mais lentamente? E 5 segundos?

Eu diria que qualquer coisa menos de 10 segundos é mais do que bom para mim. O tempo de inicialização geralmente não importa, desde que seja idempotente e previsível - ou seja, as bibliotecas são carregadas na mesma ordem, as variáveis ​​são inicializadas na mesma ordem, etc.

Qual é o maior tamanho que você considera aceitável para seu aplicativo? 5MB? 10? 20? 50? 75? 100?

Menor é melhor, principalmente para fins de distribuição. A distribuição pode ser binários baixados pelo usuário, imagens de contêiner com o binário, etc.

Você aceitaria um tempo de compilação de lançamento mais longo para otimizar o tamanho e/ou o tempo de inicialização?

Claro, desde que seja mais rápido que C/C++ e Java, deve ser agradável.

Qual é o maior tempo que você aceitaria? 15 segundos? 30 segundos? 1 minuto? 5 minutos?

Provavelmente 30-90 segundos seria o ideal, mas qualquer coisa abaixo de 3 minutos é provavelmente agradável. Mais de 3 minutos para projetos pequenos/médios (1k-500k LOC) seria um pouco demais. 5 minutos para projetos muito grandes (1M LOC) podem ser aceitáveis.

Você estaria disposto a fazer um trabalho extra se isso reduzisse o tamanho do seu aplicativo pela metade?

Depende. Se o trabalho extra for adicionar código clichê que possa ser modelado, provavelmente não há problema. Se não estiver claro, não é um trabalho simples, potencialmente não.

Obrigado pela resposta @mxplusb , @dark2201 , @RUSshy , @BlitzkriegSoftware

Acredito que o design atual aborda seus cenários da melhor maneira possível na ausência de compilação AOT pura. Se você tiver alguma dúvida sobre o design, sinta-se à vontade para trazê-lo.

Com relação a ter menor tempo de inicialização e tempo de compilação:
Os aplicativos dependentes do framework terão um tempo de inicialização consideravelmente menor (pelo menos nos estágios iniciais do recurso). O tempo de compilação também é menor (porque menos arquivos precisam ser incorporados).
Entrarei em contato com você com o tempo de inicialização, a taxa de transferência de compilação e as medidas de tamanho de arquivo assim que a primeira iteração do desenvolvimento de recursos estiver concluída.

@swaroop-sridhar Qual é o cronograma para isso?

@ayende A primeira iteração está em desenvolvimento ativo, acho que teremos os resultados em algumas semanas.

@swaroop-sridhar

Acredito que o design atual aborda seus cenários da melhor maneira possível na ausência de compilação AOT pura. Se você tiver alguma dúvida sobre o design, sinta-se à vontade para trazê-lo.

Execute: HelloWorld.exe

O aplicativo empacotado e os arquivos de configuração são processados ​​diretamente do pacote.
Os 216 arquivos restantes serão extraídos para o disco na inicialização.

E se eu precisar executar a partir de fs somente leitura, isso é comum para IoT ou se o exe estiver sendo executado dentro de um contêiner ro? por que não há a opção de executá-lo diretamente do exe também?

@etherealjoy O exemplo mostrado nesta seção é a saída esperada para HelloWorld autocontido a partir do Estágio 2 do desenvolvimento desse recurso (conforme mencionado nesta seção ). Nesse estágio, os únicos aplicativos que podem ser executados diretamente do EXE são aplicativos gerenciados puros dependentes da estrutura.

Se estivermos no Estágio 4 ou no Estágio 5 , o aplicativo independente acima pode ser executado diretamente do pacote.

Com que tipo de aplicativo você provavelmente usaria? (por exemplo, WPF no Windows? ASP.NET em um contêiner Linux Docker? Outra coisa?)

Basicamente todos os itens acima. Utilitários de console, WPF, asp.net, jogos

Seu aplicativo inclui código C++/nativo (não-.NET)?

sim. O SQLite é um bom exemplo.

Seu aplicativo carregaria plug-ins ou outras dlls externas que você não incluiu originalmente na compilação do aplicativo?

Provavelmente não.

Você está disposto a reconstruir e redistribuir seu aplicativo para incorporar correções de segurança?

sim.

Você o usaria se seu aplicativo iniciasse 200-500 ms mais lentamente? E 5 segundos?

net.core já está um pouco lento. Mas um aumento de alguns segundos é aceitável. 5 segundos - definitivamente não.

Qual é o maior tamanho que você considera aceitável para seu aplicativo? 5MB? 10? 20? 50? 75? 100?

Não importa hoje em dia

Você aceitaria um tempo de compilação de lançamento mais longo para otimizar o tamanho e/ou o tempo de inicialização? Qual é o maior tempo que você aceitaria? 15 segundos? 30 segundos? 1 minuto? 5 minutos?

Até 5 minutos parece aceitável. Builds de lançamento de 15 a 20 minutos para UWP estão deixando todos loucos.

Você estaria disposto a fazer um trabalho extra se isso reduzisse o tamanho do seu aplicativo pela metade?

Certo.

Obrigado @mjr27

A implementação do estágio 1 agora está concluída e está disponível a partir de 3.0.100-preview5-011568 .

Os aplicativos podem ser publicados em um único arquivo definindo a propriedade PublishSingleFile true conforme explicado aqui .

Nesta versão, todos os arquivos incorporados são extraídos para o disco na primeira execução e reutilizados em execuções subsequentes, conforme explicado aqui .

Capacidade de compilação
Não há diferença significativa no tempo se os arquivos são gravados no diretório de publicação como arquivos individuais ou como um único arquivo.

Tamanho do arquivo e inicialização
A tabela a seguir mostra o desempenho de alguns aplicativos criados como arquivo único.

  • Console: Um aplicativo olá mundo. O tempo relatado é o tempo para executar o aplicativo, medido por meio de tiques de relógio.
  • Aplicativo WPF: aplicativo msix-catalog . O tempo informado é o tempo de inicialização para a tela inicial, medido via cronômetro.
  • Fw: compilações dependentes do framework
  • Self: Builds independentes.
    Todas as execuções foram cronometradas quando as verificações de antivírus são desativadas para minimizar o impacto de fatores externos.

Medição | Console Fw | Console próprio | WPF Fw | WPF próprio
-- | -- | -- | -- | --
Tamanho do arquivo (MB) | 0,32 | 66,5 | 20,69 | 118,9
Execução normal (s) | 0,123 | 0,127 | 3,32 | 3,24
Primeira execução de exe único (s) | 0,127 | 0,565 | 3,67 | 4.14
Execuções subsequentes de exe único (s) | 0,124 | 0,128 | 3,30 | 3,29

Continuarei atualizando os números à medida que a implementação avança nos estágios subsequentes de desenvolvimento.

Excelente notícia. Obrigado

Todas as etapas mencionadas no documento de preparação serão concluídas para 3.0 RTM? Ou teremos que esperar o post 3.0 para que eles sejam implementados?

Conforme mencionado aqui , espera-se que o .net core 3.0 implemente o Estágio 2 de forma que aplicativos msil puros dependentes de estrutura possam ser executados diretamente do pacote. As etapas restantes serão consideradas para novas versões.

Conforme mencionado aqui , espera-se que o .net core 3.0 implemente o Estágio 2 de forma que aplicativos msil puros dependentes de estrutura possam ser executados diretamente do pacote. As etapas restantes serão consideradas para novas versões.

:( esperava que pudesse chegar ao RTM.

@cup Eu pensei que você poderia fazer isso com dotnet publish -o out /p:PublishSingleFile=true /p:RuntimeIdentifier=win-x64 . Acabei de testar criando um aplicativo de console vanilla e executando esse comando. Funcionou com sucesso.

Você pode ir ainda mais longe juntando o arquivo .pdb com dotnet publish -o out /p:PublishSingleFile=true /p:RuntimeIdentifier=win-x64 /p:IncludeSymbolsInSingleFile=true

Obrigado @seeesharprun pelas notas detalhadas. Sim @cup , o método recomendado para publicação de arquivo único de linha de comando é definir a propriedade PublishSingleFile na linha de comando.

@etherealjoy, os repositórios de tempo de execução do coreclr serão bloqueados principalmente para o trabalho do recurso wrt em um mês ou mais. Acho que não há tempo suficiente para todas as etapas. Espero que os estágios subsequentes venham no próximo lançamento.

Eu consegui isso funcionando hoje, mas parece que alterar o diretório base de extração não está funcionando. De acordo com esta seção . Devo ser capaz de adicionar um switch de aplicativo no runtimeconfig chamado ExtractBaseDir para alterar o local para o qual o aplicativo é extraído.

Eu tenho um runtimeconfig.template.json que se parece com isso

{
    "configProperties" : {
        "ExtractBaseDir": "C:\\"
    },
    "ExtractBaseDir": "C:\\"   
}

O app.runtimeconfig.json produzido tem esta aparência:

{
  "runtimeOptions": {
    "configProperties": {
      "ExtractBaseDir": "C:\\"
    },
    "ExtractBaseDir": "C:\\"
  }
}

Ao executar o exe produzido, ele ainda é enviado para a pasta tmp.

Estou assumindo que esse recurso não está pronto no Preview5, mas eu queria ter certeza, pois não está claro onde "ExtractBaseDir" deve ir em runtimeconfig.json e não há exemplos.

@igloo15 Obrigado por trazer isso à tona.

Na visualização atual, apenas a variável de ambiente DOTNET_BUNDLE_EXTRACT_BASE_DIR está habilitada. Isso ocorre porque no estágio atual de implementação todos os arquivos -- incluindo runtimeconfig.json -- são extraídos para o disco antes de serem processados.

Estou trabalhando para adicionar suporte para ExtractBaseDir na próxima visualização.

Existe alguma orientação sobre como fazer isso funcionar com projetos ASP.NET Core? A abordagem recomendada seria usar a reflexão para descobrir de onde meus tipos de sistema estão sendo carregados e definir meu IHostingEnvironment ContentRootPath e WebRootPath em relação a esse diretório?

@keithwill Você pode usar AppContext.BaseDirectory para obter o local onde o app.dll e todos os outros arquivos são extraídos para o disco. Então, você pode usar isso como ContentRootPath .

@igloo15 : Estou curioso para saber se você pode compartilhar qual é o seu cenário para usar essa opção.

  • Você está tentando usá-lo para um cenário de depuração ou implantação de produção?
  • A variável de ambiente seria suficiente neste caso ou você precisa da configuração runtimeconfig.json ?

Estou curioso para ouvir de outros desenvolvedores também, se houver casos de uso específicos para essa configuração.

@swaroop-sridhar

Estou com uma situação bastante estranha. Meu software é armazenado em um compartilhamento de rede e, em seguida, o compartilhamento de rede é vinculado a uma vm aleatória do Windows e executado diretamente do compartilhamento de rede. O Windows vm que é ativado para executar o software do compartilhamento de rede não pode ter nada modificado ou alterado nele. Logs, arquivos de configuração, etc, todos devem ser criados no compartilhamento de rede.

Estranhamente, essa configuração permite que o sistema geral faça o layout de vários softwares em uma pasta e, em seguida, clone essa estrutura de pastas para diferentes configurações/versões. Então, quando uma versão específica precisa ser executada, o software de gerenciamento gira o vms e mapeia as unidades no vms para o compartilhamento de rede da versão específica e executa o software.

1.) É um cenário de implantação de produção
2.) As variáveis ​​de ambiente seriam difíceis, pois o computador que executa o software é constantemente diferente e está sendo destruído/refeito

@cup Eu realmente não concordo que este tópico seja sobre a implementação do documento de design e o documento de design diz especificamente que ExtractBaseDir deve funcionar para esse recurso.

Se este problema não é sobre a implementação deste documento de design, então sobre o que é esse problema?

Obrigado pela explicação @igloo15.

Pergunto sobre a opção de configuração, porque há um custo para analisar os arquivos de configuração no início do código AppHost. Atualmente, o AppHost não é vinculado ao código para analisar os arquivos json (eles são usados ​​por hostfxr/hostpolicy). Adicionar esse recurso no momento tornaria o AppHost um pouco maior/complexo. No entanto, uma vez que a implementação prossegue para os estágios posteriores (onde todos os códigos de hospedagem e de tempo de execução são vinculados), isso não é mais um problema.

@igloo15 o documento de design é uma parte importante dele, mas o núcleo desse problema é o próprio recurso. ExtractBaseDir é um parâmetro opcional e eu prefiro ver o recurso principal ser passado para a versão "atual" em vez de ficar atolado com parâmetros opcionais.

@cup , fiz a pergunta aqui, porque chamei a atenção de desenvolvedores interessados ​​neste tópico.
Embora eu não ache que a pergunta esteja fora do tópico, entendo sua preocupação com a extensão deste tópico. Vou criar um problema vinculado na próxima vez. Obrigado.

@cup @swaroop-sridhar

Algumas coisas que eu diria primeiro é que esse recurso só importa para o estágio 1 do documento de estágio. Todos os outros estágios 2 - 5 são sobre executar as dlls de dentro do pacote e não extrair. Portanto, uma configuração para determinar a localização do diretório base de extração não é realmente útil para os estágios 2-5. Como o NetCore 3.0 pretende desenvolver o estágio 1, eu esperaria que esse recurso estivesse lá, pois seria a única vez que seria útil.

Em segundo lugar, é como estamos diferenciando esse método de outros métodos como dotnet-wrap. Para mim, as principais vantagens desse método é que estamos construindo esse processo na cadeia de construção mais próximo de quando o código está sendo compilado. Além disso, esse é um recurso voltado para o .NET e, portanto, entende as nuances dos aplicativos .NET Core, como suas configurações, processo de compilação etc. não voltado especificamente para net core. Esses seriam recursos como configurar o local de extração que o dotnet-wrap não pode fazer.

No que diz respeito à implementação, não tenho certeza, mas acho que, em vez de ler o ExtractBaseDir da configuração em tempo de execução, não poderíamos incorporar isso no pacote exe em tempo de compilação? Eu não vi o código que faz esse processo de pacote funcionar, mas na minha mente está gerando/construindo um exe nativo com todas as dlls etc dentro dele. Não poderíamos, durante a geração/construção do exe, ler o arquivo runtimeconfig dos aplicativos agrupados, extrair o ExtractBaseDir e defini-lo como uma propriedade no exe do pacote nativo.

Então não está lendo um runtimeconfig incorporado em tempo de execução, mas sim aproveitando uma propriedade internamente para determinar o local de extração. Isso deve ser potencialmente muito mais rápido.

@igloo15 : Extrair o conteúdo do pacote para o disco pode ser útil para determinados aplicativos além do estágio 2.
Por exemplo, se o aplicativo tiver binários nativos personalizados no pacote, eles precisarão ser extraídos até o Estágio 5. O aplicativo pode querer agrupar arquivos de conteúdo de tipo desconhecido para serem extraídos para arquivos em tempo de execução.

Concordo que a utilidade dessa configuração diminui à medida que avançamos para outros estágios de desenvolvimento. No entanto, assim que adicionarmos a opção, provavelmente teremos que mantê-la em todas as versões subsequentes para compatibilidade. Portanto, ele precisa de uma consideração cuidadosa agora.

Concordo que tornar o local de extração uma configuração de tempo de compilação (ex: msbuild-property) em vez de uma opção runtimeconfig é uma implementação wrt mais fácil e eficiente. Obrigado.

Olá, tenho algumas dúvidas sobre o assunto:

Gostaria de usar o recurso Distribuição de arquivo único para empacotar aplicativos .NET Framework 4.7.2 (por exemplo) em um único executável. Essa é a intenção desse recurso ou destina-se apenas a oferecer suporte a aplicativos .NET Core?

Minha segunda pergunta está relacionada à forma como a embalagem é feita. Quando o executável é criado, o tempo de execução das estruturas de destino é empacotado no executável? Ou deve ser pré-instalado na máquina de destino que executará o aplicativo?

@gaviriar Acho que não suporta aplicativos .NET Framework. E sim, ele pode agrupar o tempo de execução do .NET Core em um executável (chamado de publicação "autocontida").

@tomrus88 obrigado pela resposta. Isso é uma verdadeira vergonha em relação aos aplicativos .NET Framework. Existe algum método que você recomendaria que poderia ser usado para agrupar o tempo de execução do .NET Framework em um executável?

Vejo uma lista de ferramentas na seção de trabalho relacionada do documento. Existe algum em particular que poderia ser recomendado para o meu caso de uso proposto acima?

@gaviriar .NET Framework faz parte do sistema operacional Windows. Não há nenhuma maneira suportada de agrupá-lo em um executável.

@gaviriar A necessidade de empacotar o tempo de execução é um dos principais motivos para mudar para o .NET Core.

@jnm2 e @jkotas obrigado pelo esclarecimento, noob aqui para o mundo .NET. Concordo que adoraria mudar para o .NET Core. No entanto, estou enfrentando um caso em que tenho que interagir com uma biblioteca herdada que tem como alvo o .NET Framework. Nesse caso, entendo que não posso alternar meu aplicativo para .NET Core se precisar interagir com essa biblioteca. Ou existe uma abordagem alternativa para que meu aplicativo seja .NET Core, mas ainda possa interagir com a biblioteca .NET Framework herdada?

Depende da biblioteca. Muitas bibliotecas direcionadas ao .NET Framework também funcionam bem no .NET Core. Você já tentou usar a biblioteca no .NET Core? Se você verificou que não funciona, você não pode mudar até que tenha uma solução para isso.

Eu realmente tentei, mas isso não funciona, pois estamos falando de bibliotecas específicas do Windows. Acho que não tenho outra opção a não ser ficar com o direcionamento do .NET Framework e perder a diversão do .NET Core como você diz :/

@gaviriar Isso está fora do tópico, mas você já experimentou o pacote NuGet de compatibilidade do Windows, ele fornece apis do Windows para .NET Core para a finalidade exata que você está descrevendo Microsoft.Windows.Compatibility

@gaviriar : Mais alguns esclarecimentos:

Quando o executável é criado, o tempo de execução das estruturas de destino é empacotado no executável? Ou deve ser pré-instalado na máquina de destino que executará o aplicativo?

Isso depende da construção. Ambos os aplicativos dependentes de estrutura (o tempo de execução é instalado no destino) e os aplicativos independentes (o tempo de execução é empacotado com o aplicativo) podem ser publicados como um único arquivo -- mas apenas aplicativos .net core 3.0.

Vejo uma lista de ferramentas na seção de trabalho relacionada do documento. Existe algum em particular que poderia ser recomendado para o meu caso de uso proposto acima?

Para o framework .net, a melhor solução que posso sugerir é que o aplicativo lide com a extração de arquivos por si mesmo. Por exemplo: incorpore as dependências como recursos gerenciados no binário do aplicativo e, em seguida, extraia explicitamente os recursos na inicialização. Você também pode usar a biblioteca de pacote configurável para o pacote e extração (porque a biblioteca foi criada com o padrão net), mas usar recursos gerenciados é uma solução melhor.

@igloo15 e @swaroop-sridhar Agradeço essas entradas, considerando que estão fora do tópico. Embora eu espero que alguém ache essa discussão útil ao ler algum dia.

Avaliarei as opções que você compartilhou comigo e informarei qual é a melhor abordagem para o meu caso de uso.

Obrigado!

Rápida atualização!

Eu testei este simples aplicativo Hello World . Eu posso construir com sucesso um único arquivo executável. No entanto, como você pode ver na configuração do projeto, ele deve ser autocontido. Mas quando tento executar este executável em uma nova instalação do Windows 7 sem nenhum runtime do .NET core instalado, vejo o seguinte erro:

Failed to load the DLL from [C:\Users\vagrant\AppData\Local\Temp\.net\HelloWorld
\muasjfkf.kyn\hostfxr.dll], HRESULT: 0x80070057
The library hostfxr.dll was found, but loading it from C:\Users\vagrant\AppData\
Local\Temp\.net\HelloWorld\muasjfkf.kyn\hostfxr.dll failed
  - Installing .NET Core prerequisites might help resolve this problem.
     https://go.microsoft.com/fwlink/?linkid=798306

O que posso ver é que construir a publicação do aplicativo como dotnet publish /p:PublishSingleFile=true ou /p:PublishSingleFile=false não altera o tamanho do executável. Esse é o comportamento esperado?

Passos para reproduzir

  1. Publique o projeto para gerar o HelloWorld.exe

  2. Copie o executável para uma instalação do Windows 7 sem qualquer runtime do .NET Core instalado

  3. Execute o HelloWorld.exe

@gaviriar Eu experimentei o exemplo que você publicou e funcionou bem para mim.
Ou seja, ele construiu um single-exe independente para HelloWorld que roda bem.

O que posso ver é que criar a publicação do aplicativo como dotnet publish /p:PublishSingleFile=true ou /p:PublishSingleFile=false não altera o tamanho do executável. Esse é o comportamento esperado?

Isso definitivamente não é esperado. O aplicativo de arquivo único deve ter cerca de 70 MB (para a compilação de depuração indicada em sua página do gitlab). Caso contrário, é um apphost normal, que deve falhar exatamente como você observou acima. Você está publicando de um diretório limpo?

Uma outra observação sobre seu arquivo de projeto -- você tem uma das propriedades escrita incorretamente como IncludeSymbolsInFile em vez de IncludeSymbolsInSingleFile . Mas isso não tem conexão com o problema que você relatou.

@gaviriar quais são seus identificadores de tempo de execução de destino. Se você não defini-lo corretamente, ele não agrupará o hostfxr.dll correto. É possível que o hostfxr.dll exija um redistribuível vc++ específico que não existe em sua máquina Windows 7. Você pode verificar se existe uma dll no caminho fornecido e, se ela tentar carregá-la na ferramenta de dependência walker, para ver se existe uma dll da qual ela depende.

Mesclando IL: Ferramentas como o ILMerge combinam o IL de muitos assemblies em um, mas perdem a identidade do assembly no processo. Este não é um objetivo para o recurso de arquivo único.

perder a identidade de montagem

isso não é um problema.
sou sempre contra o uso de zip/unzip ou outra solução de pacotes para o recurso autônomo Single exe.
porque isso causará os próximos problemas.
Como reduzir o tamanho do arquivo Single exe !?? ou por que o arquivo exe é tão grande, mas é apenas um olá mundo!??

Há sempre o problema, e precisa ser resolvido, por que não fazer um exame minucioso.
Na verdade, desde o início deve usar IL-Merge de maneiras semelhantes.

Na verdade, as pessoas não são tão obcecadas com um único EXE.
exe(1)+dll(1-2) também está OK, mas não 400 dlls de montagem.
ppl não gosta de trazer o código nunca ser executado no disco do servidor e executá-lo nas memórias do servidor.
as pessoas só querem reduzir o custo de implantação. (disco, memória, largura de banda etc...)

Eu removi o marco 3.0, para que esse problema acompanhe a implementação do recurso de arquivo único por meio dos estágios descritos neste documento , que está além do escopo da versão 3.0.

@sgf , concordo que há trabalho necessário para reduzir o tamanho do arquivo. As oportunidades de redução de tamanho são de duas formas, elas não estão especificamente vinculadas ao single-exe:
1) Reduza a quantidade de conteúdo que precisa ser publicado. Por exemplo:
* Use ILLinker como ferramentas para cortar binários desnecessários, partes de montagens, etc.
Este trabalho é rastreado separadamente por questões como dotnet/coreclr#24092 e mono/linker#607
* Podemos considerar outras medidas, como ter um modo de "capacidade reduzida" onde o JITting não é suportado para reduzir o
2) Adicione o recurso de compactação à publicação de arquivo único.

Os recursos acima podem funcionar juntos para reduzir o tamanho de aplicativos de arquivo único (e não de arquivo único):
Por exemplo, para o aplicativo de console HelloWorld,
Tamanho de publicação normal = 67,4 MB
Tamanho aparado = 27 MB
Aparado, arquivo único, compactado (via protótipo) = 12 MB

@swaroop-sridhar Existe alguma maneira de obter o caminho do seu único arquivo exe dentro do seu aplicativo?
O uso de Assembly.GetExecutingAssembly().Location produzirá o diretório de extração em \AppData\Local\Temp.net.

@ chris3713 A melhor maneira de acessar o local do exe atualmente é PInvoke-ing para APIs nativas. Por exemplo: GetModuleFileNameW (Null, <buffer>, <len>)

@ chris3713 A melhor maneira de acessar o local do exe atualmente é PInvoke-ing para APIs nativas. Por exemplo: GetModuleFileNameW (Null, <buffer>, <len>)

Obrigado, acabei usando Process.GetCurrentProcess().MainModule.FileName .

Funciona bem com asp .net core worker/web api como Windows Service publicado com PublishSingleFile=true.

Quando registro o serviço e o executo via sc create testwk binPath="[...]/MyAspNetCoreWorker.exe" , sc start testwk recebo esta mensagem de sucesso:

[...]\bin\publish\x64>sc start testwk

SERVICE_NAME: testwk
        TYPE               : 10  WIN32_OWN_PROCESS
        STATE              : 2  START_PENDING
                                (NOT_STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
        WIN32_EXIT_CODE    : 0  (0x0)
        SERVICE_EXIT_CODE  : 0  (0x0)
        CHECKPOINT         : 0x0
        WAIT_HINT          : 0x7d0
        PID                : 5060
        FLAGS              :

[...]\bin\publish\x64>sc stop testwk

SERVICE_NAME: testwk
        TYPE               : 10  WIN32_OWN_PROCESS
        STATE              : 3  STOP_PENDING
                                (STOPPABLE, NOT_PAUSABLE, ACCEPTS_SHUTDOWN)
        WIN32_EXIT_CODE    : 0  (0x0)
        SERVICE_EXIT_CODE  : 0  (0x0)
        CHECKPOINT         : 0x0
        WAIT_HINT          : 0x0

Mal posso esperar para ver o Estágio 2 com corrida em pacote😀
A propósito, o Estágio 2 ainda será planejado para fazer parte do .Net Core 3.0?

Obrigado pelo seu trabalho, o serviço de publicação no .net core agora é muito simples 👍

CoreRT : ( 2,5 MB )

image

image

Sem zip hack , sem custo oculto , o mecanismo contém apenas o que é necessário e os recursos não usados ​​são desabilitados usando a configuração csproj (reflexão)

É disso que o dotnet precisa, concorre com o GO e até o supera em tamanho e desempenho

Para quem estiver interessado, consulte o repositório:

https://github.com/dotnet/corret

Existem amostras para MonoGame:

https://github.com/dotnet/coret/tree/master/samples/MonoGame

Nenhuma das soluções propostas pode alcançar esse resultado, o CoreRT é realmente o melhor, a MS está cometendo um grande erro ao não focar nele

Eu testei esse recurso no macOS com dotnet-sdk-3.0.100-preview8-013656-osx-x64.tar.gz . Se mudarmos a configuração, de Debug para Release ou vice-versa e compilarmos novamente, adicionamos 10MBs ao tamanho do executável. A recompilação com a configuração antiga não recupera esse aumento de tamanho, a menos que excluamos o diretório bin .

mkdir /tmp/dotnet-preview8
curl -s https://download.visualstudio.microsoft.com/download/pr/a974d0a6-d03a-41c1-9dfd-f5884655fd33/cf9d659401cca08c3c55374b3cb8b629/dotnet-sdk-3.0.100-preview8-013656-osx-x64.tar.gz | tar -xvz -C /tmp/dotnet-preview8
export PATH=/tmp/dotnet-preview8:$PATH

dotnet new console -o /tmp/myApp
pushd /tmp/myApp

# publish with Debug
dotnet publish /p:PublishSingleFile=true,RuntimeIdentifier=osx-x64,Configuration=Debug -o bin/a/b/c/d

bin/a/b/c/d/myApp
# outputs: Hello World!

du -sh bin/a/b/c/d
# outputs  70M  bin/a/b/c/d

# publish with Release
dotnet publish /p:PublishSingleFile=true,RuntimeIdentifier=osx-x64,Configuration=Release -o bin/a/b/c/d

du -sh bin/a/b/c/d
# outputs:  80M bin/a/b/c/d

# publish with Debug again
dotnet publish /p:PublishSingleFile=true,RuntimeIdentifier=osx-x64,Configuration=Debug -o bin/a/b/c/d
# still outputs:  80M   bin/a/b/c/d

Mesmo sem alterar a configuração de compilação, se invocarmos o destino Rebuild subsequentemente ( /t:Rebuild ) após a compilação inicial, o tamanho da saída aumentará.

Obrigado @am11 por dar uma olhada. Publicar retendo binários antigos é um problema conhecido (cc @nguerrera).

Desde o .Net Core 3 preview 8, não consigo iniciar meu aplicativo publicado em um único arquivo.
No visualizador de eventos estou com este erro:

image

Estou no Windows Server 2019

Edit: Este erro ocorreu após a atualização do .Net Core SDK Preview 7 para o .Net Core SDK Preview 8. Após reiniciar o servidor, posso iniciar meu aplicativo corretamente novamente.

@Safirion , fizemos uma alteração no formato interno subjacente em torno dessas visualizações. É possível que você tenha dividido a saída de uma visualização e construído com outra. Não há intervalos entre a visualização 8 e 9.

No linux, o caminho de extração padrão parece ser /var/tmp/.net . No AWS lambda, o caminho /var não é gravável, portanto, a criação dessa pasta falha. Para sair da execução da caixa sem definir DOTNET_BUNDLE_EXTRACT_BASE_DIR, proponho alterar isso para /tmp/.net . Ou talvez deva haver várias tentativas em locais comuns (/var/tmp, /tmp, mesmo diretório que o binário, etc.)

@stevemk14ebr obrigado por relatar esse problema. Você pode registrar um novo problema para acompanhar esse trabalho? https://github.com/dotnet/core-setup

@jeffschwMSFT O mesmo erro foi lançado com a passagem do .Net Core P9 para o RC1.

Description: A .NET Core application failed.
Application: Setup.exe
Path: C:\Users\Administrateur\Desktop\Setup.exe
Message: Failure processing application bundle.
Failed to determine location for extracting embedded files
A fatal error was encountered. Could not extract contents of the bundle

@Safirion obrigado por relatar este problema. Você pode criar um problema separado para reproduzir isso? https://github.com/dotnet/core-setup
cc @swaroop-sridhar

@Safirion você pode enviar instruções de reprodução? Essa falha é determinística?
Por favor, registre um problema no repositório de configuração principal, como @jeffschwMSFT sugerido acima.

Eu olhei para isso e isso só aconteceria se o aplicativo estivesse sendo executado em um ambiente em que o diretório temporário não estivesse acessível. Por exemplo se eu fizer isso:

set "TMP=wrong_path"
myapp.exe

Ele falha com

Failure processing application bundle.
Failed to determine location for extracting embedded files
A fatal error was encountered. Could not extract contents of the bundle

Observe que o pacote procura o diretório temporário usando a API GetTempPath win32. Que usa o env. variável TMP , seguida por TEMP e assim por diante. Se o primeiro com valor contiver um caminho inválido, ele falhará assim.

Você pode corrigir isso certificando-se de que o caminho temporário está definido corretamente ou pode definir explicitamente DOTNET_BUNDLE_EXTRACT_BASE_DIR para um local onde deseja que a extração ocorra.

@Safirion você pode enviar instruções de reprodução? Essa falha é determinística?
Por favor, registre um problema no repositório de configuração principal, como @jeffschwMSFT sugerido acima.

Não consigo reproduzir esse problema após atualizar o .Net Core para RC1 e reiniciar meu servidor. (Tentei desinstalar o RC1 e instalar o Preview 9 novamente para atualizar novamente para o RC1, mas meu aplicativo foi iniciado bem desta vez)

Este erro ocorreu com a passagem do SDK 3.0 P7 para o SDK 3.0 P8 e do SDK 3.0 P9 para o SDK 3.0 RC1.
E nos dois casos, uma simples reinicialização do servidor corrige o problema.

Eu uso um .Net Core Windows Service (Worker usando a pasta C:\Windows\Temp\.net para extrair desde que é iniciado pelo sistema) e um aplicativo .Net Core WPF (inicialização por script de sessão de inicialização do usuário) quando atualizo para RC1. Talvez esse erro seja causado pelo .Net Core Worker sendo executado durante a instalação do .Net Core SDK, não sei...

Observe que eu instalo o SDK e não o tempo de execução porque tenho que implantar o tempo de execução 3 .Core (asp.net core, desktop .net core e core) ao mesmo tempo e https://dot.net não nos dá um "Full Runtime instalador" (nem encontrei o instalador do Desktop Runtime, então não há escolha) ...

Atualização: não importa, encontrei a documentação .

Olá, obrigado por seu trabalho duro!

Como os arquivos de configuração (App.config, por exemplo) devem ser distribuídos? Acabei de criar um aplicativo de console no RHEL 7.6 com a sequência de comandos exata:

dotnet restore -r win-x64 --configfile Nuget.config
dotnet build Solution.sln --no-restore -c Release -r win-x64
dotnet publish --no-build --self-contained -c Release -r win-x64 /p:PublishSingleFile=true -o ./publish ./SomeDir/SomeProj.csproj

onde SomeProj.csproj tem <PublishTrimmed>true</PublishTrimmed> habilitado e obteve 2 arquivos como resultado: SomeProj.exe e SomeProj.pdb , mas não SomeProj.config

@catlion para ter o comportamento padrão para arquivos *.config, eles devem ser excluídos do pacote. Por padrão, todos os arquivos não pdb são incluídos no pacote.
O seguinte excluirá a configuração do arquivo único:

<ItemGroup>
    <Content Update="*.config">
      <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
      <ExcludeFromSingleFile>true</ExcludeFromSingleFile>
    </Content>
  </ItemGroup>

https://github.com/dotnet/designs/blob/master/accepted/single-file/design.md#build -system-interface

@morganbr

Com que tipo de aplicativo você provavelmente usaria? (por exemplo, WPF no Windows? ASP.NET em um contêiner Linux Docker? Outra coisa?)

Aplicativos sem cabeça e WPF para Windows

Seu aplicativo inclui código C++/nativo (não-.NET)?

Sim, nós P/Invocamos uma variedade de dlls nativas, alguns itens de terceiros sobre os quais não temos controle, alguns internos de outras equipes.

Seu aplicativo carregaria plug-ins ou outras dlls externas que você não incluiu originalmente na compilação do aplicativo?

Sim, mas usamos CompilerServices para compilá-los a partir da fonte.

Você está disposto a reconstruir e redistribuir seu aplicativo para incorporar correções de segurança?

sim

Você o usaria se seu aplicativo iniciasse 200-500 ms mais lentamente? E 5 segundos?

Sim, nosso aplicativo geralmente fica aberto por longos períodos de tempo.

Você aceitaria um tempo de compilação de lançamento mais longo para otimizar o tamanho e/ou o tempo de inicialização? Qual é o maior tempo que você aceitaria? 15 segundos? 30 segundos? 1 minuto? 5 minutos?

Sim, podemos esperar vários minutos.

Você estaria disposto a fazer um trabalho extra se isso reduzisse o tamanho do seu aplicativo pela metade?

sim

Obrigado @john-cullen

Vinculando alguns PRs à ramificação single-exe para uma implementação de protótipo de execução de aplicativos de arquivo único sem extração ( Estágio 4 ).

https://github.com/dotnet/coreclr/pull/26197
https://github.com/dotnet/coreclr/pull/26504
https://github.com/dotnet/coreclr/pull/26697
https://github.com/dotnet/coreclr/pull/26904

Deixe-me também fornecer algumas respostas para @morganbr :) Eu não sei se você ainda lê isso :)

  1. Com que tipo de aplicativo você provavelmente usaria? (por exemplo, WPF no Windows? ASP.NET em um contêiner Linux Docker? Outra coisa?)

Aventuras de texto (aplicativos de console) para serem executadas em diferentes plataformas (Linux, MacOS, Windows)

  1. Seu aplicativo inclui código C++/nativo (não-.NET)?

Não.

  1. Seu aplicativo carregaria plug-ins ou outras dlls externas que você não incluiu originalmente na compilação do aplicativo?

Atualmente não, mas seria ótimo a longo prazo oferecer suporte a determinados cenários.

  1. Você está disposto a reconstruir e redistribuir seu aplicativo para incorporar correções de segurança?

sim.

  1. Você o usaria se seu aplicativo iniciasse 200-500 ms mais lentamente? E 5 segundos?

sim.
(Seria ótimo fornecer alguma saída de texto ou caixa de mensagem para notificar o usuário de que algo está acontecendo)

  1. Qual é o maior tamanho que você considera aceitável para seu aplicativo? 5MB? 10? 20? 50? 75? 100?

10-20 MB.

  1. Você aceitaria um tempo de compilação de lançamento mais longo para otimizar o tamanho e/ou o tempo de inicialização? Qual é o maior tempo que você aceitaria? 15 segundos? 30 segundos? 1 minuto? 5 minutos?

1+ minutos está ok.

  1. Você estaria disposto a fazer um trabalho extra se isso reduzisse o tamanho do seu aplicativo pela metade?

Possivelmente.

Obrigado pela resposta, @lenardg.

Esse problema acompanha o progresso no recurso de distribuição de arquivo único do .NET Core 3.0.
Aqui está o documento de design e o plano de preparação para o recurso.

Este é um recurso muito útil para facilitar a implantação e reduzir o tamanho.
Poucas ideias de melhoria no design, especialmente para implantações corporativas, onde o ambiente é rigidamente controlado por meio de políticas de grupo.

  1. Condicionalmente, é possível compactar enquanto empacota o único executável?
    ou seja, adicione a lógica Wrap, para que não precisemos de integração de outras ferramentas.
    O compactado produzirá exe ainda menor.

  2. Ao iniciar o exe, ele expande seu conteúdo para uma pasta temporária. Para nosso produto, em determinados sites de usuários, as máquinas clientes são rigidamente controladas e não permitem que executáveis ​​sejam iniciados a partir da pasta temporária ou mesmo dos locais %userprofile%.

É possível especificar/controlar onde extrair ou pode ser 'extrair no local' em um ".\extract" ou tal subpasta?
Isso permite a conformidade com as políticas de grupo, bem como a capacidade de usar o recurso exe único.

  1. Antes de empacotar, é possível assinar a pasta e depois ele pega os arquivos para empacotar?
    Podemos assinar o único exe, mas os arquivos de extração não são assinados e, portanto, em alguns outros locais do cliente, ele não permite a execução, a menos que os binários sejam assinados.

Só queria que você soubesse, vi o mesmo problema que o @Safirion hoje. Eu tenho um aplicativo dotnetcore 3.0 direcionado ao Ubuntu e fui executá-lo no meu servidor Synology. Não iria extrair - continuou falhando com um erro "Um erro fatal foi encontrado. Não foi possível extrair o conteúdo do pacote". Reconstruído várias vezes, mas a mesma coisa. Depois de reiniciar o host, funcionou bem novamente.

Seria útil entender se há um bloqueio de arquivo ou algo assim aqui, e se há momentos de como depurá-lo.

@RajeshAKumar dotnet/core-setup#7940

Não sei como esse link ajuda.
Ele apenas fala sobre a extração temporária, listei 3 pontos acima.

E eu te dei a solução para um deles. não faço ideia dos outros 2

E eu te dei a solução para um deles. não faço ideia dos outros 2

O extrato temporário não funciona para nós, portanto, essa não é a solução. Minha solicitação foi a capacidade de controlar onde ele extrai ou pode ser extraído no local em uma subpasta.
Os administradores de TI em muitos de nossos computadores clientes não permitem executar executáveis ​​de pastas temporárias ou de perfil de usuário.
Por favor, releia a postagem https://github.com/dotnet/coreclr/issues/20287#issuecomment -542497711

@RajeshAKumar : Você pode definir o DOTNET_BUNDLE_EXTRACT_BASE_DIR para controlar o caminho base onde o host extrai o conteúdo do pacote.
Por favor, veja mais detalhes aqui: https://github.com/dotnet/designs/blob/master/accepted/single-file/extract.md#extraction -location

@RajeshAKumar : A compactação do exe único incluído é um recurso em consideração para .net 5: https://github.com/dotnet/designs/blob/master/accepted/single-file/design.md#compression

Por enquanto, você precisará usar outros utilitários para compactar o single-exe gerado.

@RajeshAKumar , em relação à assinatura, você pode assinar todos os arquivos/binários que são publicados e agrupados no único exe. Quando o host extrai os componentes incorporados para o disco, os arquivos individuais serão assinados. Você também pode assinar o próprio arquivo único criado, como você mencionou.

Isso atende às suas necessidades?

@Webreaper Se você puder reproduzir o problema, pode executar o aplicativo com COREHOST_TRACE ativado (definir a variável de ambiente COREHOST_TRACE 1 ) e compartilhar o log gerado? Obrigado.

@swaroop-sridhar
Este é um recurso muito legal. Você tem um link de quando os diferentes estágios estarão disponíveis em qual versão dotnet?

@RajeshAKumar : Você pode definir o DOTNET_BUNDLE_EXTRACT_BASE_DIR para controlar o caminho base onde o host extrai o conteúdo do pacote.
Por favor, veja mais detalhes aqui: https://github.com/dotnet/designs/blob/master/accepted/single-file/extract.md#extraction -location

Obrigado Swaroop-Sridhar

@RajeshAKumar , em relação à assinatura, você pode assinar todos os arquivos/binários que são publicados e agrupados no único exe. Quando o host extrai os componentes incorporados para o disco, os arquivos individuais serão assinados. Você também pode assinar o próprio arquivo único criado, como você mencionou.

Isso atende às suas necessidades?

Estou tentando descobrir como quebrar as partes 'compilar' e publicar.
Eu uso o seguinte comando atualmente, então ele não me dá a capacidade de assinar.
Definir publishargs=-c Release /p:PublishSingleFile=True /p:PublishTrimmed=True /p:PublishReadyToRun=false
dotnet publish -r win-x64 -o bin\Output\Win64 %publishargs%

Como o acima compila, bem como aparas e pacotes, não tenho certeza de como quebrá-los.
Idealmente, para usar sua abordagem, terei que compilar, assinar e finalmente publicá-lo.

@Webreaper Se você puder reproduzir o problema, pode executar o aplicativo com COREHOST_TRACE ativado (definir a variável de ambiente COREHOST_TRACE 1 ) e compartilhar o log gerado? Obrigado.

Obrigado. Ele desapareceu agora porque eu reiniciei, mas é útil ter isso anotado se acontecer novamente!

@RajeshAKumar , você precisará marcar a assinatura para um destino que é executado logo antes do agrupamento.
Como você está usando PublishReadyToRun e PublishTrimmed você não pode usar os alvos padrão Afterbuild ou BeforePublish .

Você pode adicionar um destino que é executado imediatamente antes BundlePublishDirectory e executa a assinatura.

@swaroop-sridhar
Este é um recurso muito legal. Você tem um link de quando os diferentes estágios estarão disponíveis em qual versão dotnet?

Obrigado @etherealjoy. O trabalho para arquivos únicos executados diretamente do pacote está em andamento, visando a versão .net 5 para meu melhor entendimento.

@RajeshAKumar , você precisará marcar a assinatura para um destino que é executado logo antes do agrupamento.
Como você está usando PublishReadyToRun e PublishTrimmed você não pode usar os alvos padrão Afterbuild ou BeforePublish .

Você pode adicionar um destino que é executado imediatamente antes BundlePublishDirectory e executa a assinatura.

Obrigada.
O link que você forneceu se parece com tarefas do MS Build.
Como crio um manipulador para "BundlePublishDirectory"? Isso está no Studio/Project props/Build Events ou eu tenho que criar algo do zero.

@RajeshAKumar neste caso, você precisará de algo mais refinado do que eventos pós-compilação pré-compilação.
Então, acho que você deve editar o arquivo do projeto e adicionar algo como:

<Target Name="Sign" BeforeTargets="BundlePublishDirectory">
    ... 
</Target>

Qual é o comportamento esperado do comando dotnet pack com isso? Digamos que temos um artefato single-exe enviado para o repositório nuget local. Se eu tentar instalá-lo com chocolatey, ele tentará restaurar todos os deps nuget listados no arquivo do projeto. O que era esperado anteriormente, mas estou em dúvida se esse comportamento está correto para SFD.

É possível adicionar alguma barra de progresso ou indicador de carregamento durante a extração do aplicativo WPF autônomo de arquivo único, pode levar algum tempo com nada acontecendo.
Um aplicativo WPF autônomo básico tem mais de 80Mo e a extração pode levar mais de 5 segundos. Não é muito amigável e recebi reclamações de meus usuários finais.

Edit: Qualquer maneira de limpar a versão antiga automaticamente no lançamento?

@Safirion Não consigo ver como isso poderia estar no escopo desse recurso. Se você precisar, seria melhor ser atendido criando seu próprio aplicativo minúsculo que mostra uma tela inicial e inicia o aplicativo real e, em seguida, faz com que o aplicativo real pare o programa de tela inicial quando ele for iniciado.

@ProfessionalNihilist Acho que você não entende meu ponto.
Um aplicativo WPF vazio autônomo usa 80 meses de armazenamento em disco quando compilado. Você não pode ter um aplicativo WPF menor que 80mo sem compilá-lo como um aplicativo dependente de estrutura 😉
O problema é o tempo de extração da estrutura incluída antes que o aplicativo possa ser iniciado. Então tem que ser feito pelo .Net Core e está totalmente relacionado a esse recurso.

Talvez adicionar a capacidade de ter um png que seria mostrado enquanto o aplicativo é descompactado?

@Safirion @ayende a falta de "feedback da interface do usuário" durante a inicialização é rastreada aqui: https://github.com/dotnet/core-setup/issues/7250

Qual é o comportamento esperado do comando dotnet pack com isso? Digamos que temos um artefato single-exe enviado para o repositório nuget local. Se eu tentar instalá-lo com chocolatey, ele tentará restaurar todos os deps nuget listados no arquivo do projeto. O que era esperado anteriormente, mas estou em dúvida se esse comportamento está correto para SFD.

@catlion a propriedade PublishSingleFile é suportada apenas pelo comando dotnet publish . Portanto, não tem impacto em dotnet pack . Existe uma motivação para usar o arquivo único e o empacotamento?

Edit: Qualquer maneira de limpar a versão antiga automaticamente no lançamento?

@Safirion na versão atual, a limpeza é manual, o host não tenta remover os arquivos extraídos, porque eles podem ser reutilizados em execuções futuras.

Ok, vou fazer meu próprio limpador 😉
Obrigado por sua resposta.

@cup que não tem nada de especial em mono, é apenas uma compilação padrão, agora adicione uma referência nuget e essa solução não é mais um único arquivo. Mono tem mkbundle embora para alcançar arquivos únicos

@Suchiman tem certeza? Meu exemplo acima produz um único arquivo de 3 KB. Embora o tamanho mínimo do dotnet pareça ser 27 MB:

https://github.com/dotnet/coreclr/issues/24397#issuecomment -502217519

@copo sim

C:\Users\Robin> type .\Program.cs
using System;
class Program {
   static void Main() {
      Console.WriteLine("sunday monday");
   }
}
C:\Users\Robin> C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe .\Program.cs
Microsoft (R) Visual C# Compiler version 4.8.3752.0
for C# 5
Copyright (C) Microsoft Corporation. All rights reserved.

This compiler is provided as part of the Microsoft (R) .NET Framework, but only supports language versions up to C# 5, which is no longer the latest version. For compilers that support newer versions of the C# programming language, see http://go.microsoft.com/fwlink/?LinkID=533240

C:\Users\Robin> .\Program.exe
sunday monday
C:\Users\Robin> dir .\Program.exe


    Verzeichnis: C:\Users\Robin


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       10.11.2019     18:36           3584 Program.exe

@Suchiman tem certeza? Meu exemplo acima produz um único arquivo de 3 KB. Embora o tamanho mínimo do dotnet pareça ser 27 MB:

#24397 (comentário)

Seu arquivo 3k só funciona porque o mono já está instalado. O ponto principal da distribuição de arquivo único dotnetcore é que você não precisa do clr instalado, é um único exe autônomo, incluindo o tempo de execução.

@Webreaper na verdade eu acredito que ele usou apenas mcs de mono para compilar, já que ele não escreveu mono sun-mon.exe provavelmente rodava no .NET Framework.

Mas o .NET Core também suporta o cenário de tempo de execução sendo pré-instalado, também conhecido como implantação dependente de estrutura. Ainda não é uma implantação de arquivo único nesse caso, pois existem alguns arquivos adicionais para .NET Core, como .deps.json e .runtimeconfig.json

@ chris3713 A melhor maneira de acessar o local do exe atualmente é PInvoke-ing para APIs nativas. Por exemplo: GetModuleFileNameW (Null, <buffer>, <len>)

Parece que Environment.CurrentDirectory é uma solução melhor, embora eu ainda não tenha tentado as duas abordagens em algo diferente do Windows.

EDIT: Não. Esse caminho está sujeito a alterações em diferentes pontos de entrada no aplicativo. Nada de bom.

Em uma nota um pouco relacionada, encontrei essa regressão na publicação de arquivo único de aplicativos Blazor na visualização mais recente do VS para Mac: https://github.com/aspnet/AspNetCore/issues/17079 - relatei em AspNeCore/Blazor, mas pode ser que isso seja mais relevante para o grupo coreclr - não tenho certeza. Vai deixar para vocês se movimentarem!

@Suchiman cuidado, esse compilador tem problemas:

https://github.com/dotnet/roslyn/issues/39856

@cup exceto que, usando o caminho do arquivo que eu nomeei, você está usando o antigo compilador C# 5 escrito em C++, que não é roslyn e eles provavelmente fecharão esse problema por esse motivo. Mas Roslyn pode fazer a mesma coisa, apenas um caminho diferente...

Em uma nota um pouco relacionada, encontrei essa regressão na publicação de arquivo único de aplicativos Blazor na última visualização do VS para Mac: aspnet/AspNetCore#17079 - relatei em AspNeCore/Blazor, mas pode ser que isso é mais relevante para o grupo coreclr - não tenho certeza. Vai deixar para vocês se movimentarem!

@Webreaper Obrigado por relatar o problema, que parece um problema do ASP.net em relação a ativos estáticos. Então, esse é o lugar certo para arquivá-lo.

* Movendo o post de outra edição para aqui.

@swaroop-sridhar ,

Os tempos de inicialização dos aplicativos WPF de arquivo único DotNet Core são muito mais lentos do que o aplicativo WPF original do ILMerge-ed criado no .net 4.7. Isso é esperado ou isso vai melhorar no futuro?

As compilações vêm do meu ImageOptimizer: https://github.com/devedse/DeveImageOptimizerWPF/releases

| Tipo | Tempo estimado da primeira inicialização | Segundo tempo estimado de inicialização | Tamanho | Links para download |
| -- | -- | -- | -- | -- |
| .NET 4.7.0 + ILMerge | ~3 seg | ~1 seg | 39,3MB | LINK |
| dotnet publish -r win-x64 -c Release --self-contained=false /p:PublishSingleFile=true | ~10 seg | ~3 seg | 49mb | |
| dotnet publish -r win-x64 -c Release /p:PublishSingleFile=true | ~19 seg | ~2 segundos | 201mb | |
| dotnet publish -r win-x64 -c Release /p:PublishSingleFile=true /p:PublishTrimmed=true | ~15 seg | ~3 seg | 136MB | LINK |
| dotnet publicar -r win-x64 -c Versão | ~2,5 segundos | ~1,5 seg | 223kb para exe (+400mb em dlls) | |

@devedse , para ter certeza, a "segunda inicialização" é a média de várias execuções (além da primeira)?
Estou curioso, mas falta qualquer explicação sobre por que a corrida /p:PublishSingleFile=true /p:PublishTrimmed=true deve ser mais lenta do que ` /p:PublishSingleFile=true corrida.

Portanto, antes de investigar, quero ter certeza de que os números na "segunda inicialização" são números estáveis ​​e que a diferença na inicialização é reproduzível,

Além disso, este problema é sobre plugins de arquivo único, você pode mover a discussão de perf para um novo problema ou para dotnet/coreclr#20287? Obrigado.

@swaroop-sridhar , em resposta à sua pergunta sobre ser a média:
É um pouco difícil para mim cronometrar isso com muita precisão, então o tempo foi feito contando enquanto o aplicativo estava iniciando e, em seguida, tentando algumas vezes para ver se há uma diferença significativa no tempo de inicialização. Se você conhece um método melhor, pode reproduzi-lo facilmente criando minha solução: https://github.com/devedse/DeveImageOptimizerWPF

Minha principal pergunta está relacionada ao motivo pelo qual leva mais tempo para um aplicativo agrupado (arquivo único) iniciar em comparação com um arquivo .exe desagrupado.

Posso estar errado aqui, mas faz sentido para mim, pois há sobrecarga com um único arquivo. Essencialmente, você tem um aplicativo que está iniciando outro aplicativo. Enquanto o ILMerge está iniciando diretamente. O ILMerge apenas mesclava dlls referenciadas no exe, mas não embrulhava tudo em outra camada, que é o que está sendo feito atualmente com PublishSingleFile.

@devedse O arquivo único está essencialmente extraindo, verificando as somas de verificação, etc. antes de iniciar a execução do dotnet.
Acho que é por isso que levou esse tempo.
A extração é "armazenada em cache" para que, na próxima execução, não haja sobrecarga de E/S.

@RajeshAKumar , hmm é extrair realmente o caminho a seguir neste cenário? Não seria melhor seguir o caminho do ILMerge e realmente mesclar as DLLs em um único pacote?

Especialmente para arquivos .exe maiores, você também está induzindo o custo do espaço em disco de armazenar todos os arquivos duas vezes.

@devedse Estamos todos aguardando os próximos estágios deste recurso (Executar do Bundle), mas por enquanto, é a única solução. 😉

https://github.com/dotnet/designs/blob/master/accepted/single-file/staging.md

isso é o que você ganha usando JIT na área de trabalho, inicialização lenta, parece que apenas a Apple entendeu isso

(principalmente repetindo o que já foi dito):
Espera-se que o primeiro início seja muito mais lento - ele extrai o aplicativo para o disco - muito IO. A segunda inicialização e as subsequentes devem ser quase idênticas à versão sem arquivo único do aplicativo. Em nossas medições internas não vimos diferença.

Como medir: Usamos rastreamento (ETW no Windows) - há eventos quando o processo é iniciado e há eventos de tempo de execução que podem ser usados ​​para isso - não é exatamente fácil.

Conforme mencionado por @Safirion , estamos trabalhando na próxima melhoria para arquivo único, que deve executar a maior parte do código gerenciado diretamente do .exe (sem extração para o disco). Não posso prometer um trem de lançamento ainda.

JIT: Todo o framework deve ser pré-compilado com Ready2Run (CoreFX, WPF), então na inicialização apenas o código do aplicativo deve ser JIT. Não é perfeito, mas deve fazer uma grande diferença. Dado os tempos de inicialização de ~ 1-2 segundos, acho que já está usando isso em todos os testes.

Obrigado a todos, eu não estava ciente dos próximos passos que estão planejados. Isso esclarece.

Espera-se que o primeiro início seja muito mais lento - ele extrai o aplicativo para o disco - muito IO.

Isso nunca deveria ter acontecido, você torna a experiência do usuário horrível BY DESIGN, escolha horrível, é assim que você faz os usuários odiarem a tecnologia que o desenvolvedor está usando para eles

Conforme mencionado por @Safirion , estamos trabalhando na próxima melhoria para arquivo único, que deve executar a maior parte do código gerenciado diretamente do .exe (sem extração para o disco). Não posso prometer um trem de lançamento ainda.

Por que lançar isso oficialmente agora se vai mudar em breve? deve ser marcado como visualização/experimental

na minha opinião isso é perda de tempo e recursos, concentre-se na compilação AOT e no tree-shaking, coloque todos os seus recursos lá, pare com hacks

@RUSshy Por que tanto ódio? Se você não quiser o atraso de inicialização ao iniciar pela primeira vez, não use a implantação de arquivo único.

Acho que a inicialização é significativamente menor que 10s e, como é apenas a primeira vez que você executa, não há problema algum. Estou implantando um webapp do lado do servidor, o que significa que, na maioria dos casos, ele será inicializado uma vez e executado por dias/semanas, então a extração inicial é insignificante no esquema das coisas - então eu prefiro isso como um stop-gap até que haja uma única imagem compilada, porque isso torna a implantação muito mais fácil do que copiar centenas de DLLs em todo o lugar.

+1 no meu caso, temos um docker de compilação gerando um único exe e um docker separado para executar o aplicativo (usando a imagem do docker Alpine normal sem dotnet). Após a etapa de construção, carregamos o contêiner de tempo de execução uma vez e docker-commit a camada. Posteriormente, não observamos nenhuma regressão de desempenho em comparação com uma implantação dependente de estrutura. Assim que o mecanismo load-from-bundle file for implementado e enviado, removeremos a etapa intermediária de hot-loading.

@vitek-karas, existe um problema de rastreamento do recurso "carregar ativos de tempo de execução do pacote"? interessados ​​em entender que tipo de impedimentos existem. :)

@am11 Estamos atualmente montando o plano detalhado. Você pode ver o protótipo que foi feito em https://github.com/dotnet/coreclr/tree/single-exe. A implementação real provavelmente não será muito diferente (obviamente melhor fatoração e assim por diante, mas a ideia central parece ser sólida).

@Webreaper Para aplicativos da Web, não é um problema, mas talvez porque o .Net Core 3 seja recomendado para desenvolvimento WPF/WinForm agora, e compartilhar um aplicativo de desktop .exe perdido em cem .dll não é uma opção, então eu totalmente entender a frustração relacionada à primeira etapa desse recurso.
;)

E nenhum usuário espera 10 segundos (ou mais de 3 segundos) antes de clicar novamente em um exe hoje. O fato de não haver indicador de carregamento é o segundo grande problema desse recurso. Infelizmente, parece que o indicador de carregamento não fará parte do .Net Core 3.1, então os usuários terão que ser pacientes...

Os desenvolvedores de desktop realmente esperam pelo estágio 2, e espero que isso faça parte do .Net 5 porque, na verdade, o desenvolvimento de desktop no .Net Core é uma experiência muito ruim para os usuários finais.

@RUSshy Por que tanto ódio? Se você não quiser o atraso de inicialização ao iniciar pela primeira vez, não use a implantação de arquivo único.

isso não é ódio, isso é um feedback construtivo e honesto, eu me importo com C# e .net, eu uso os dois todos os dias, não quero que seja substituído por GO ou qualquer outra coisa

recentemente:
https://old.reddit.com/r/golang/comments/e1xri3/choosing_go_at_american_express/
https://old.reddit.com/r/golang/comments/ds2z51/the_stripe_cli_is_now_available_and_its_written/

feedback negativo é tão útil quanto feedback positivo, mas se você considerar isso como "ódio", não posso ajudá-lo

A comunidade .net é silenciosa, passiva e tendenciosa, a ruptura é o único caminho a percorrer

O .NET é objetivamente a melhor plataforma para a maioria dos aplicativos atualmente. Eu gostaria que mais pessoas percebessem isso.

As histórias de guerra que ouço de outras plataformas como Java, Go, Rust, Node, ... são francamente perturbadoras. Essas plataformas são assassinas de produtividade.

na minha opinião isso é perda de tempo e recursos, concentre-se na compilação AOT e no tree-shaking, coloque todos os seus recursos lá, pare com hacks

Eu concordo. .Net tem um ótimo sistema de tipos. Muitas vezes é contornado usando reflexão. O ferramental precisa se concentrar na AOT, mas também na minimização da reflexão. https://github.com/dotnet/coret/issues/7835#issuecomment -545087715 seria um bom começo. O erro de um bilhão de dólares dos nulos está sendo mitigado agora; o mesmo deve ser feito com reflexão com uma configuração ou marcador para código livre de reflexão (ou outro código compatível com linker ou corert).

Eliminar a reflexão seria incrível. Tanto sofrimento poderia ser evitado se a reflexão fosse proibida. Minha história de horror mais recente foi descobrir que eu não conseguia mover o código porque a estrutura usada (SDK do service fabric) achou prudente vincular os bytes serializados ao nome do assembly da implementação do serializador sem substituição possível.

Qualquer progresso no sentido de desencorajar a reflexão seria um progresso.

Btw estava procurando uma maneira de mesclar assemblies para reduzir o tamanho do pacote e os tempos de carregamento, permitir a otimização de todo o programa. Eu entendo que este problema não é realmente direcionado a isso.

Edit: Já que esse post reuniu algumas reações só para esclarecer. Acredito que a metaprogramação deve acontecer em tempo de design, onde o código são os dados e está sob meu controle.

Tipos que gosto de usar para impor invariáveis ​​em que posso confiar. Reflexões de tempo de execução quebram essa confiança.

Por isso, gostaria que a reflexão em tempo de execução fosse substituída pela metaprogramação em tempo de design. Onde também poderia ser mais poderoso sobrepondo-se a casos de uso, como analisadores, correções rápidas e refatoração.

Marcando assinantes nesta área: @swaroop-sridhar
Notifique danmosemsft se você quiser se inscrever.

O design de recurso de arquivo único está neste documento: https://github.com/dotnet/designs/blob/master/accepted/2020/single-file/design.md
Acompanhamento do progresso de aplicativos de arquivo único neste problema: https://github.com/dotnet/runtime/issues/36590.
Obrigado a todos por seus comentários e sugestões nesta edição.

Esta página foi útil?
0 / 5 - 0 avaliações