Building Dart 2 SDK for ARMv6 using Docker and CircleCI

Previously I wrote about building the Dart SDK for ARMv6, so that I could use Dart on a Raspberry Pi Zero.

The problem at the time was that I could build only version 1.24 of the SDK, while Dart 2 has been around for a while now.

I managed to build the latest version of Dart (2.0.0-dev.55.0) too.

I could run Aqueduct and the Flutter tools with that.

I used Docker again but this time I also wanted to play around with CircleCI, mainly to have a more powerful setup during the build process.

You can find everything in my repo: https://github.com/manhluong/dart_sdk_build_raspberry_pi_dockerfile

A zip file containing the binaries is in the output folder: https://github.com/manhluong/dart_sdk_build_raspberry_pi_dockerfile/tree/master/output

So if we copy the binaries to a Raspberry Pi Zero and we switch from Dart 1 to Dart 2 we can see that the analyzer behaves accordingly, recognising types:

It is also possible to run Aqueduct:

The chat example:

Flutter tools are written in Dart and is possible to run them:

Provided that we change the Dart SDK with which Flutter runs:

Otherwise we will get an error because the provided dart-sdk inside Flutter cannot run on ARMv6:

Custom toolchain

The Dart SDK allows you to define a custom toolchain for the build process, in order to cross compile.

However if you try to run ./tools/gn.py inside the sdk codebase passing the -t flag, the toolchain does not appear in ./out/ReleaseXARMV6/args.gn and Ninja cannot build correctly.

Another problem is that in order to build everything with the custom toolchain I need to use the GNU++14 STD instead of the C++14 STD.

I forked out the Dart SDK and got a build successful via this commit.

So I opened an issue and attempted a fix but it seems that changing gn.py is a more complex edit than I thought.

Dockerfile

That said, I went back to my Dockerfile and I sed everything out in order for the build to succeed. In this way I could build without my changes to gn.py and the build configuration.

So I used sed to:

  • Make sure that “toolchain_prefix” is present for Ninja to pick it up.
  • Avoid use clang.
  • Avoid use the default sysroot.
  • Use “std-gnu++14” instead of “std-c++14”.

In the README of my repo you can see how to build the image and retrieve the binaries.

CircleCI

To assist in my build I wanted to try out CircleCI because I keep hearing good things about it.

You can find the config I used here: https://github.com/manhluong/dart_sdk_build_raspberry_pi_dockerfile/blob/master/.circleci/config.yml

I could have a single-job workflow and I started from that but then I wanted to play more with it and I end up with this workflow:

The build job build the docker image, the extract job retrieve the .zip file while the upload_hub upload the docker image to Docker Hub.

After extract has finished, the store job store the .zip file in the Artifacts:

I passed the docker image and the build artifacts between jobs via Workspace and Cache.

Overall I really liked CircleCI: the UI is very good and orchestrating the jobs via the YAML file is relatively simple.

It is not super straightforward to reuse the same built Docker image between jobs but there are work in progress.

I will definitely try to use CircleCI in my next project and I think that even if your company has a devops team, it could benefit in offloading some work on CircleCI.

Improvements

The Docker image is big, too big, because I wanted to wrap all the build process inside the Docker image so to have the binaries running only docker build ..

Also I see all this as an exercise to use Docker and CircleCI.

An immediate improvement is to use Docker volumes, maybe via Docker compose, to use the volume to download all the dependencies / source code and to save the .zip with the binaries, so that the resulting image would be much smaller.

With that, the CircleCI workflow would be also more simple, as it would be possible to save the Artifacts directly, without extracting the .zip file from the image via a container.