DockerマルチステージおよびマルチプラットフォームでQtをコンパイルする

これは、Screenly.io のCEOであるViktor Peterssonからのゲスト投稿です。 スクリーンリー ラズベリーパイで最も人気のあるデジタルサイネージ製品です。 ツイッター@vpeterssonでヴィクトルを探す。

Qtに慣れていない人のために説明すると、自動車(テスラ)、デジタルサイネージ(Screenly)、飛行機(ルフトハンザ)など、幅広い製品で使用されているクロスプラットフォーム開発フレームワークです。 言うまでもなく、Qtは非常に強力です。 ただし、Qtフレームワークについて言えないことの1つは、少なくとも組み込みデバイスではコンパイルが簡単だということです。 このトピックに関する数え切れないほどのブログ投稿、フォーラムスレッド、およびスタックオーバーフローの投稿は、Qtのコンパイルが一般的な頭痛の種であることを示しています。

Qtの長期ユーザーとして、私たちはScreenlyでQtとの戦いをかなり共有してきました。 数年前に商用 デジタルサイネージソフトウェア のためにQtに移行しましたが、それ以来、そのパフォーマンスと柔軟性の両方に非常に満足しています。 最近、オープンソースのデジタルサイネージソフトウェア(Screenly OSE)もQtに移行することにしました。 これらのプロジェクトはコードベースを共有していないため、これは私たちが新たに始めて、ビルドプロセスのためのエキサイティングな新しいテクノロジーを探求することを可能にするグリーンフィールドの機会でした。

Qt(およびQtWebEngine)のコンパイル は非常に 重い操作であるため、Dockerfileが(インストールプロセスの一部としてコンパイルするのではなく)ダウンロードしてビルドプロセスに含めることができるように、Qtをプリコンパイルして配布する必要があります。

私たちは座って、ビルドプロセスに次の要件を作成しました。

  • プロセスは最初から最後まで完全に自動化する必要があります。
  • サポートされているすべての Raspberry Piボード用にQt/QtWebEngineを(適切なQt device プロファイルで)構築できる必要があります。
  • x86でクロスコンパイルを使用して、意味のあるプロセスを高速化する必要があります。
  • CIで完全なプロセスを実行できる必要があるため、RaspberryPiに依存することはできません。
  • ビルドパッケージでホストを乱雑にしないように、すべてをDockerコンテナ内で実行するように制限する必要があります。

上記の目標を念頭に置いて、Dockerの新しい マルチプラットフォーム サポートを試す絶好の機会を得ました。 マルチステージビルドと組み合わせて使用 することで、両方の長所を得ることができました。

  • クロスコンパイルできない場所でエミュレーションを使用する
  • 重労働のためのクロスコンパイルに切り替える

Dockerのマルチプラットフォームはどのように機能しますか?

Dockerでマルチプラットフォーム機能を使用する最も簡単な方法は、コマンドラインから呼び出すことです。 docker buildxを使用して、新しいベータ機能を利用できます。dを実行すること docker buildx build --platform linux/arm/v7 -t arm-builによって。 このコマンドは、ARMv7 エミュレーションを使用して、現在のディレクトリの 'Dockerfile' に従って Docker イメージをビルドします。 舞台裏では、DockerはQEMU仮想化環境(qemu-user-static 正確には)でDockerビルドプロセス全体を実行します。 これにより、カスタム VM の設定の複雑さが解消されます。 ビルドすると、ARMv7モードでコンテナを自動的に起動するためにも使用できます docker run

マルチプラットフォーム、マルチステージ、Qt

マルチプラットフォーム機能は優れたスタンドアロン機能ですが、マルチステージビルドと組み合わせるとさらに強力になります。 単一のDockerfile内で、プラットフォームを組み合わせて、ステップ間でコピーすることができます。 この機能は、Screenly OSEのQtビルドプロセスで最終的に行ったものとまったく同じです。

ステージ 1: アーム

Balenaの素晴らしい人々のおかげで、最初のステージでRaspbianの基本画像を使用することができました。このステップは、以下を使用して呼び出すことができます。

FROM --platform=linux/arm/v7 balenalib/rpi-raspbian:buster as builder

上記の手順の後、通常どおりにDockerを使用して、パッケージのインストールなどのさまざまな RUN コマンドを実行できます。ビルドがARMv7ハードウェアで実行されていない場合、このコンテナはQEMUを使用してエミュレートされて実行されていることに注意してください。 この場合、コマンドを使用してQtビルド依存関係をインストールします。 上記の手順により、ディスクイメージ( Qt Wiki が提案しているもの)または rsync 物理的なRaspberryPiからファイルをコピーする必要を完全に排除することもできます。 

ステージ 2: x86

ARMステップで依存関係をインストールしたら、ビルダーのネイティブx86アーキテクチャに切り替えてエミュレーションを回避し、次の行でクロスコンパイルを実行できます。

FROM --platform=linux/amd64 debian:buster

今、私たちは興味深い部分にいます。 x86に切り替えた後、前の手順のファイルをコピーできます。 これは、Qtに使用できるシステムルートを作成するために行います。 このステップを完了するには、次のコマンドを実行します。

RUN mkdir -p /sysroot/usr /sysroot/opt /sysroot/lib

COPY --from=builder /lib/ /sysroot/lib/

COPY --from=builder /usr/include/ /sysroot/usr/include/

COPY --from=builder /usr/lib/ /sysroot/usr/lib/

COPY --from=builder /opt/vc/ sysroot/opt/vc/

私たちは今、両方の長所を持っています。 マルチステップ機能とマルチプラットフォーム機能の両方を利用して、Qtの構築に使用できるsysrootを生成します。 前のステップで完全に機能するRaspbianイメージを使用したので、Qtに既存のすべてのライブラリを取得させることもできます。

./configure \
-sysroot /sysroot

冒頭で述べたように、Qtのコンパイルは簡単ではありません。 正常にコンパイルするために必要な手順はたくさんあります。 正確な手順の詳細については、完全な Dockerfile とスクリプト build_qt5.shを参照してください。 

エミュレートするかしないか...

ARMのようなプラットフォームをエミュレートできることは驚くべきことであり、多くの柔軟性を提供します。 ただし、コストがかかります。 大きなパフォーマンスペナルティがあります。 この問題が、エミュレーションを使用してQtを実際にコンパイルしない理由です。 代わりに、クロスコンパイルを使用します。 エミュレートするのではなくクロスコンパイルする機能がある場合は、クロスコンパイルによってパフォーマンスが大幅に向上することを知っておいてください。

スクリーンリーについて

スクリーンリー ラズベリーパイで最も人気のあるデジタルサイネージ製品です。 物理画面を、ダッシュボード、画像、ビデオ、およびWebページを表示できる安全でリモート制御可能なデバイス(UIまたは デジタルサイネージAPI経由)に変えたい場合は、Screenlyを使用するとセットアップが簡単になります。 Screenlyには、 オープンソースバージョン商用バージョンの2つのフレーバーがあります。 

フィードバック

「DockerマルチステージおよびマルチプラットフォームでQtをコンパイルする」に関する0の考え