查看原文
其他

Dart 2.6 正式发布, 新增利器 dart2native: 将 Dart 编译为自包含的本地可执行文件

Flutter 谷歌开发者 2021-08-05
作者: Michael Thomsen, Dart & Flutter Product Manager, Google

Dart 提供了丰富多样的编译器,全方位助力开发者为移动设备和 web 平台优化生产代码。灵活的编译器技术让我们的框架合作伙伴可以针对不同的目标平台,构建各种类型的应用,例如: Android 和 iOS 平台上的 Flutter 应用、web 端桌面端 Flutter 应用、web 端的 AngularDart 应用以及嵌入式设备上的 Google 助手。


今天,我们正式推出 dart2native。作为现有编译器集合的一个扩展,dart2native 可以将 Dart 程序编译为含有预编译机器码的自包含可执行文件。在它的协助下,您可以使用 Dart 在 macOS, WindowsLinux 上创建命令行工具。下方的功能宣传图就是使用 dart2native 来实现的。

  • 丰富多样的编译器
    https://dart.dev/platforms
  • Android 和 iOS
    https://flutter.dev/docs
  • web 端
    https://flutter.dev/web
  • 桌面端
    https://github.com/flutter/flutter/wiki/Desktop-shells
  • web 端的 AngularDart 应用
    https://angulardart.dev
  • 使用 dart2native 实现
    https://gist.github.com/mit-mit/faec2bfc1d1cef7cd09df917e531c5c0



Dart Native 及 dart2native 编译器


Dart 从数年前就开始支持 AOT 运行前编译,通过 AOT 编译器,开发者可以提前将 Dart 代码编译为本地机器码。几年发展下来,Dart Native 技术也日趋成熟。不过,此前这项功能的应用范围比较局限,我们仅通过 Flutter 向 iOS 和 Android 移动设备投放了该功能。


引入 dart2native 后,我们将本地编译支持扩展至 macOS、Windows 和 Linux 三款传统桌面操作系统。利用 dart2native 创建的可执行文件属于自包含文件,因此在未安装 Dart SDK 的机器上亦能运行。由于可执行文件之前已经通过 Dart 的 AOT 编译器进行过处理,因此几毫秒后就会开始运行。在编译为本地代码的过程中,dart2native 与其它 Dart 编译器和运行时工具一样,也可以使用丰富且一致的 Dart 核心库


许多客户要求我们为桌面操作系统提供 AOT 预编译支持 (Dart AOT 问题专区排名第 6 的功能请求),所以,我们很高兴终于将这项功能带到您身边。


如果您之前使用过 dart2aot,那么,升级至 Dart SDK 2.6 后,您将开始使用 dart2native,它的功能比 dart2aot 更加强大。


  • Dart Native
    https://dart.dev/platforms
  • Flutter
    https://flutter.dev
  • Dart 核心库
    https://dart.dev/guides/libraries
  • 排名第 6 的功能请求
    https://github.com/dart-lang/sdk/issues/36915



使用 dart2native 构建命令行应用


如果您想构建和部署基于 Dart 的命令行应用,那么 dart2native 绝对是您的不二选择。此类应用通常会使用到 dart:io (基础 I/O), package:http (网络) 以及 package:args (参数解析) 这三个库。下面让我们以 "hello, world" (你好,世界) 应用为例,回顾一下如何把应用编译成可执行文件:

源代码 hello.dart
main() { print(‘Hello Dart developers’);}


编译 hello.dart 至 hello 可执行文件:
$ dart2native src/hello.dart -o helloGenerated: /Users/mit/hello


运行 hello 并测量知执行时间:
$ time ./helloHello Dart developersreal 0m0.049suser 0m0.018ssys 0m0.020s


请注意: 在短短 49 毫秒内,这条命令就完成了开始、打印到标准输入、退出整个流程!

之前,我们已经看到有不少 Dart 开发者小试牛刀,尝试利用 dart2native 开发命令行工具:

  • 来自 SASS (一款深受开发者喜爱的 CSS 扩展工具) 团队的 Natalie 在 Github 上留言说,自从团队转用 dart2native 来编译基于 Dart 的 SASS 实现后,该实现的性能大幅提高,即使与 LibSass 这款基于 C++ 的实现相比也毫不逊色。

  • 来自 Dart DevRel 团队的 Filip 使用 dart2native 重编译了自己的网站链接检查工具 linkchecker,二次编译后,小型网站的检查速度提升了整整 27 倍


  • 基于 Dart 的命令行应用
    https://dart.dev/tutorials/server/cmdline
  • dart:io
    https://api.dart.dev/stable/2.6.1/dart-io/dart-io-library.html
  • http
    https://pub.dev/packages/http
  • args
    https://pub.dev/packages/args
  • SASS
    https://sass-lang.com/
  • 在 Github 上留言说
    https://github.com/dart-lang/sdk/issues/32894#issuecomment-513975562
  • linkchecker
    https://github.com/filiph/linkcheck/
  • 提升了整整 27 倍
    https://github.com/filiph/linkcheck/issues/7#issuecomment-496308288



通过 dart:ffi 与 C 代码互操作


原生应用经常会用到由访问由操作系统提供的原生功能。此类系统 API 通常由基于 C 语言的原生库提供,而 Dart 则允许您通过 dart:ffi (外部函数接口) 与这些库实现互操作。dart:ffi 是我们新推出的 C 语言互操作机制,首个技术预览版本已随 Dart 2.5 一同发布。dart2native 编译器与 dart:ffi 兼容,因此您可以直接在本地创建和编译需要用到 dart:ffi 的应用。

我们团队的一名成员最近利用 dart:ffi 创建了一个 dart_console 库,专门用于控制台应用的开发工作。该库涵盖多种功能,如获取窗口尺寸、读取和设定光标位置、颜色管理、读取键盘输入和控制序列等。出色的 dart:ffi 使用能力让 Dart 成为一款强大的控制台应用开发语言。


  • dart_console

    https://pub.dev/packages/dart_console



不到 500 行 Dart 代码就能写出一个 7MB 大小的代码编辑器: kilo


我们利用 Dart 核心库、dart:ffi 以及 dart_console 库开发出了几个十分有趣的控制台应用。我们大约用了 500 行 Dart 代码编写了一个名为 Kilo 的控制台文本编辑器,并将完整版演示封装至 dart_console 包内。Kilo 的命名源于它的前身 kilo.c —— 一款由不到 1,000 行 C 代码实现的简易文本编辑器。


新推出的 dart2native 编译器让打包工作变得十分简单,我们最终得到了一个 7MB 大小的自包含代码编辑器。请查看下方演示动图,了解 Kilo 的编译过程,以及编译后的 Kilo 是如何通过编辑自己的源代码来修复错误的。

正在编辑自己的源代码的 Kilo 编辑器 (该编辑器使用 Dart 编写,并通过 dart2native 编译为可执行文件)

  • 大约用了 500 行 Dart 代码
    https://github.com/timsneath/dart_console/blob/master/example/kilo.dart
  • kilo.c
    https://github.com/antirez/kilo/blob/master/kilo.c



使用 dart2native 构建服务


微服务是 dart2native 编译器另一个潜在用途,比如说: 为前端 Flutter 应用提供支持的后台服务。近年来,无服务器计算的应用趋势持续攀升,越来越多的用户开始使用运行在无服务器计算上的服务。这些服务完全交由供应商托管,支持自动扩缩,基础架构可根据流量从零 (停止运行时) 开始扩容,反向缩容亦可。此外,由于无服务器平台仅在代码运行期间收费,因此可以为开发者大幅削减成本。目前,Google Cloud 已通过 Cloud Run 推出无服务器计算解决方案。

对于无服务器后台而言,快速的服务启动速度至关重要。过去,我们一般通过 JIT (即时) 编译器来运行基于 Dart 的服务,但是基于 JIT 的执行有一个明显的缺点: 代码必须先完成编译和预热两个步骤才能开始执行,所以延迟现象十分严重。通过把服务代码提前编译为本地代码,您不但可以避免延迟问题,而且还能够立即开始运行代码。此外,使用本地代码创建的 Dart 服务属于自包含应用,所占用的磁盘资源也比较小,因此对运行容器的体积要求也会大幅降低。Dart 开发者 Paul Mundt 在《初试 Dart 微服务》这篇文章中,谈到了对 dart2native 的一些心得体会: 之前使用 JIT 编译器时,Docker 镜像文件的体积为 220MB,转用本地代码后,文件体积缩减至 20MB,降幅高达 91%!更多技术细节,请查看官方文档《服务器端应用》《包》


  • 无服务器计算
    https://en.wikipedia.org/wiki/Serverless_computing
  • Cloud Run
    https://cloud.google.com/run/
  • 《初试 Dart 微服务》
    https://itnext.io/experiments-with-dart-microservices-fa117aa408c7
  • 《服务器端应用》
    https://dart.dev/tutorials/server/httpserver
  • 《包》
    https://dart.dev/server/libraries#server-packages


如何获取


您可从 2.6 及以上版本的 Dart SDK 中获取 dart2native 编译器。请前往 https://dart.dev/get-dart 下载最新版本的 Dart SDK。安装完毕后,您可在 bin/ directory 和 PATH 内找到新的编译器。请前往 Dart.dev 获取更多官方文档

通过 Flutter 获取 Dart SDK 的开发者请注意: 当前 Flutter 版本对 dart2native 的支持并不完整。在稳定版 Flutter 提供 Dart 2.6 之前,我们建议您前往 dart.dev/get-dart 下载 Dart 2.6 SDK。


  • 更多官方文档
    https://dart.dev/tools/dart2native
  • dart.dev/get-dart
    https://dart.dev/get-dart


已知限制


初始版本的 dart2native 编译器包含以下已知限制。请前往我们在 Github 上的问题追踪页面,为您关注的问题投票,以便告知我们哪些问题对您最为重要。
  • 不提供交叉编译支持 (Github 问题 28617): dart2native 编译器每次只能为一种运行系统创建机器码,因此,如需同时为 macOS、Windows 和 Linux 创建可执行文件,请您分别运行 3 次编译器。或者,您也可以与支持 3 种操作系统的持续集成 (Continuous Integration) 供应商接洽合作,共同解决这类问题。

  • 不提供签名支持 (Github 问题 39106): 生成的可执行文件的格式与 codesign 和 signtool 等标准签名工具不兼容。

  • 不支持 dart:mirrors 和 dart:developer (请参阅 Dart 核心库)。


  • Github 问题 28617
    https://github.com/dart-lang/sdk/issues/28617
  • Github 问题 39106
    https://github.com/dart-lang/sdk/issues/39106
  • Dart 核心库
    https://dart.dev/guides/libraries



Dart 2.6 中的其它变更


除了 dart2native 编译器之外,Dart SDK 2.6 另外还引入了多项变更。

作为我们新推出的 C 语言互操作机制,dart:ffi 的首个技术预览版本已随 Dart 2.5 一同发布。Dart 2.6 推出了全新的 dart:ffi,内含多个中断性 (Breaking) API 变更,使得 API 的易用性大幅提升,此外,新版本还提供了更多的类型安全,内存访问也更为便利。请阅读《Dart 2.6 变更日志》,进一步了解详情。引入这些变更后,dart:ffi 已顺利迭代至 Beta 版本,我们预计今后 API 的变更频率将逐步放缓,整体稳定性也会有所提高。欢迎大家继续通过 Dart 问题追踪器,向我们提交反馈。

此外,Dart 2.6 还新增了一款超棒的语言特性: 扩展方法 (Extention methods)。此项功能尚处于预览阶段,在性能优化及工具支持方面仍有不足,我们目前正在收尾相关工作,希望能在下个版本的 Dart SDK 中正式推出该功能,届时将为大家带去更为详尽的功能介绍。如果您有兴趣了解它背后的设计理念,请阅读《Dart 扩展方法的基础知识》


  • C 语言互操作机制

    https://dart.dev/guides/libraries/c-interop

  • 《Dart 2.6 变更日志》

    https://github.com/dart-lang/sdk/blob/master/CHANGELOG.md#foreign-function-interface-dartffi

  • Dart 问题追踪器

    https://github.com/dart-lang/sdk/issues

  • 扩展方法

    https://github.com/dart-lang/language/issues/41

  • 《Dart 扩展方法的基础知识》

    https://medium.com/dartlang/extension-methods-2d466cd8b308



下一步


即刻下载 Dart 2.6 SDK (https://dart.dev/get-dart), 使用 dart2native 构建精彩应用,并向我们提交使用反馈。如果您想与我们分享更多使用细节,欢迎您在下方留言评论。期待您的出色表现!



 点击屏末 | 阅读原文 | 即刻进入 Dart 语言官网


  想了解更多 Flutter 内容?


  • 在公众号首页发送关键词 "Flutter",获取相关历史技术文章;

  • 还有更多疑惑?欢迎点击菜单 "联系我们" 反馈您在开发过程中遇到的问题。

推荐阅读




    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存