Anunciando o Dart 2.6 com dart2native: compile Dart para executáveis nativos autocontidos

Anunciando o Dart 2.6, com o dart2native para compilar aplicações Dart em executáveis nativos autocontidos para Windows, macOS e Linux

O Dart já oferece um amplo conjunto de compiladores para construir código otimizado para produção em dispositivos móveis e na web. Esses compiladores flexíveis permitem que nossos parceiros de framework alcancem uma ampla gama de formatos: aplicações Flutter em Android e iOS, aplicações Flutter na web e no desktop, aplicações AngularDart na web e o Google Assistant em dispositivos embarcados.

Hoje anunciamos o dart2native, uma extensão do nosso conjunto atual de compiladores, com a capacidade de compilar programas Dart para executáveis autocontidos que contêm código de máquina compilado ahead-of-time. Com o dart2native, você pode criar ferramentas de linha de comando para macOS, Windows ou Linux usando Dart. A imagem de anúncio deste recurso foi implementada usando o próprio recurso :-)

An animation of a bash terminal showing the command line interface.

Dart Native e o compilador dart2native

#

O Dart já oferece suporte à compilação AOT (ahead-of-time) para código de máquina nativo há vários anos, e o Dart Native é, portanto, uma tecnologia bastante madura. No entanto, até agora expúnhamos essa capacidade apenas em dispositivos móveis iOS e Android, por meio do Flutter.

Com o dart2native, estamos estendendo o suporte à compilação nativa para sistemas operacionais de desktop tradicionais, como macOS, Windows e Linux. Como os executáveis criados com o dart2native são autocontidos, eles podem rodar em máquinas que não têm o SDK do Dart instalado. E, por serem compilados com o compilador AOT do Dart, os executáveis começam a rodar em apenas alguns milissegundos. Assim como nos outros compiladores e runtimes do Dart, o mesmo conjunto rico e consistente de bibliotecas core está disponível no Dart quando ele é compilado para código nativo.

Ouvimos muitos clientes pedirem compilação AOT para sistemas operacionais de desktop (a sexta issue mais bem avaliada no nosso issue tracker), então estamos muito felizes em disponibilizar este recurso.

Se você já usava o dart2aot, a partir da 2.6 você vai usar o dart2native. Ele oferece um superconjunto das funcionalidades do dart2aot.

Construindo apps de linha de comando com o dart2native

#

O compilador dart2native é uma ótima escolha para construir e distribuir aplicações Dart para linha de comando. Essas aplicações costumam usar bibliotecas como dart:io (I/O básico), package:[http](https://pub.dev/packages/http) (rede) e package:[args](https://pub.dev/packages/args) (parsing de argumentos). Vamos revisar o básico de compilar uma aplicação "hello, world" para um executável:

O código-fonte hello.dart:

main() {
  print(‘Hello Dart developers’);
}

Compile hello.dart para um executável hello:

$ dart2native src/hello.dart -o hello
Generated: /Users/mit/hello

Execute hello medindo o tempo de execução:

$ time ./hello
Hello Dart developers

real 0m0.049s
user 0m0.018s
sys 0m0.020s

Repare como o comando inicia, imprime na saída padrão e termina em um tempo combinado de apenas 49 milissegundos!

Já vimos alguns desenvolvedores Dart experimentando o dart2native para ferramentas de linha de comando:

  • Natalie, da equipe do SASS (uma ferramenta popular de extensão de CSS), relata que, depois de mudar a implementação do SASS em Dart para compilar com o dart2native, ela passou a ser competitiva em desempenho com o LibSass, uma implementação em C++.

  • Filip, da equipe de DevRel do Dart, recompilou a ferramenta linkchecker com o dart2native e viu uma aceleração de 27x ao verificar sites pequenos.

Interoperabilidade com código C via dart:ffi

#

Aplicações nativas frequentemente precisam acessar funcionalidades nativas do sistema operacional ao redor. Essas APIs de sistema normalmente são expostas em bibliotecas nativas baseadas em C, e o Dart oferece suporte à interoperabilidade com essas bibliotecas por meio do dart:ffi, nosso novo mecanismo para interoperabilidade com C, que lançamos em preview no Dart 2.5. O compilador dart2native é compatível com o dart:ffi, então você pode criar e compilar aplicações Dart nativas que o utilizam.

Um membro da nossa equipe usou recentemente o dart:ffi para criar a biblioteca dart_console para desenvolvimento de aplicações de console, que tem funcionalidades como obter as dimensões da janela, ler e definir a posição do cursor, gerenciar cores e ler teclas e sequências de controle. A capacidade de usar o dart:ffi torna o Dart uma linguagem muito poderosa para aplicações de console.

kilo: um editor de código de 7 MB escrito em menos de 500 linhas de código Dart

#

Usando as bibliotecas core do Dart, o dart:ffi e a biblioteca dart_console, podemos criar aplicações de console bem interessantes. O pacote dart_console inclui uma demonstração completa do kilo, um editor de texto de console escrito em apenas ~500 linhas de código Dart. O nome kilo vem da sua origem, o kilo.c, que era uma implementação em C com cerca de 1000 linhas de código.

Com o novo compilador dart2native, podemos empacotar isso facilmente, e chegamos a um editor de código autocontido de 7 MB. Aqui está uma demonstração da compilação do editor e, em seguida, do uso do editor compilado para editar o próprio código-fonte e corrigir um bug:

The kilo editor written in Dart and compiled to an executable with dart2native editing its own source code.
The kilo editor written in Dart and compiled to an executable with dart2native editing it’s own source code

Construindo serviços com o dart2native

#

Outro uso potencial do compilador dart2native é para serviços pequenos, por exemplo, um backend que dá suporte a um app frontend escrito em Flutter. Nos últimos anos, uma tendência crescente tem sido o uso de serviços rodando em computação serverless. São serviços totalmente gerenciados que escalam automaticamente, inclusive escalando de zero (sem execução) para cima e de volta para zero, o que pode reduzir muito os custos, já que são cobrados apenas quando estão de fato rodando. O Google Cloud disponibiliza a computação serverless por meio do Cloud Run.

Para backends serverless, é fundamental que o serviço inicie rapidamente. Tradicionalmente, serviços baseados em Dart rodavam com o nosso compilador JIT (just-in-time), mas a execução baseada em JIT tem alta latência na inicialização, já que o código precisa ser compilado e aquecido antes de começar a executar. Ao compilar o código do seu serviço ahead-of-time para código nativo, você evita essa latência e começa a rodar imediatamente. Além disso, com código nativo você pode criar serviços em Dart com uma pegada pequena em disco e autocontidos, reduzindo bastante o tamanho do contêiner em que o serviço roda. O desenvolvedor Dart Paul Mundt documentou recentemente suas experiências com o compilador dart2native; ele conseguiu reduzir o tamanho da sua imagem Docker em 91%, de 220 MB com código compilado em JIT para apenas 20 MB usando código nativo! Veja a nossa documentação para mais detalhes sobre aplicações server-side e pacotes.

Disponibilidade

#

O compilador dart2native está disponível no SDK do Dart a partir da versão 2.6, que está disponível a partir de hoje em dart.dev/get-dart. Depois de instalar o SDK, você deve ver o novo compilador dentro do diretório bin/ e no seu PATH. O dart.dev tem mais documentação.

Se você obtiver o SDK do Dart pelo Flutter, observe que as builds atuais do Flutter têm suporte incompleto ao dart2native. Recomendamos que você instale o SDK do Dart 2.6 em dart.dev/get-dart.

Limitações conhecidas

#

Esta versão inicial do compilador dart2native tem algumas limitações conhecidas, listadas abaixo. Você pode nos dizer quais problemas são importantes para você dando um "joinha" na issue em nosso issue tracker do GitHub.

  • Sem suporte a cross-compilation (issue 28617): o compilador dart2native só cria código de máquina para o sistema operacional em que está rodando. Assim, você vai precisar executar o compilador três vezes, no macOS, no Windows e no Linux, se quiser criar executáveis para os três. Uma forma de fazer isso é usar um provedor de CI (Integração Contínua) que dê suporte aos três sistemas operacionais.

  • Sem suporte a assinatura (issue 39106): os executáveis produzidos usam um formato que não é compatível com ferramentas padrão de assinatura, como codesign e signtool.

  • Sem suporte a dart:mirrors e dart:developer (veja as bibliotecas core do Dart).

Outras mudanças no Dart 2.6

#

A versão 2.6 do SDK do Dart também traz algumas outras mudanças.

Lançamos um preview do dart:ffi, nosso novo mecanismo para interoperabilidade com C, no Dart 2.5. O Dart 2.6 tem uma nova versão do dart:ffi. Essa nova versão traz várias mudanças incompatíveis na API, para torná-la mais fácil de usar, oferecer mais segurança de tipos e dar acesso conveniente à memória. Para mais detalhes, veja o changelog do Dart 2.6. Com essas mudanças, o dart:ffi avança para o beta, e esperamos que as mudanças na API sejam bem menos frequentes daqui para frente, com alta estabilidade geral. Continue nos enviando feedback pelo issue tracker.

O Dart 2.6 também contém um preview de um novo recurso de linguagem empolgante, os métodos de extensão. Ainda temos um pouco de acabamento e trabalho em ferramentas para concluir esse recurso, mas esperamos lançá-lo formalmente na nossa próxima versão do SDK do Dart. Vamos falar muito mais sobre os métodos de extensão nessa ocasião; por enquanto, você pode ler sobre as considerações de design por trás do recurso.

Próximos passos

#

Baixe o SDK do Dart 2.6 (dart.dev/get-dart), crie algo legal com o dart2native e depois nos conte sobre isso. Se você estiver disposto a compartilhar os detalhes, deixe uma resposta no final deste artigo. Estamos animados para ver o que você vai criar!

Mais do Dart