CircleCI(など)でJUCEアプリケーションをビルドする

JUCE GUIアプリケーションをCircleCIでビルドしてテスト実行するにはいろいろ面倒事を解決しないといけないのでまとめておく。CircleCIでやっているのはたまたまなので、他にも任意のDocker Imageを使用してビルドできるCI環境があれば応用がきくはず。実行環境は今回はUbuntuを想定している(パッケージ名等を調整すれば任意のLinuxイメージで動くはず)。

アプリケーションを起動してテストを実行しないとしても、Projucerによるビルドファイル生成が含まれている場合は、結局依存パッケージが必要になるので、およそ面倒事は避けられないのではないかと思う。リポジトリに生成済みのビルドファイルまで含めておくのであれば、この限りではない。

docker image

今回は ubuntudesktop/gnome-3-28-1804 を使っている。JUCE GUIアプリケーションはX11で動いており、そのために必要な環境があらかじめ設定されているイメージが望ましい。ただ、依存パッケージの中にlibwebkitgtk-4.0-devがあり、これは実のところかなり多数の追加依存パッケージを巻き込むはずなので、標準的なubuntuイメージでも十分かもしれない。

依存パッケージが増えすぎるとCI実行時間が無駄に長くなるのでなるべく避けたいところではあるけど、CircleCI上で40秒くらいだったので、まあ…仕方ないかな…という気持ちでそれ以上は追求していない。気になるようであれば独自のdocker imageを公開しておいたほうがよいかもしれない。

package deps

依存パッケージは現状これくらい含まれている。

echo y | apt-get install xvfb wget unzip libc6 libcurl3-gnutls-dev  \
    libfreetype6-dev libgcc1 libjpeg-dev libpng-dev libstdc++6 \
    libwebkit2gtk-4.0-dev libx11-6 libxext6 zlib1g  make g++ \
    mesa-common-dev libasound2-dev

Projucerの実行時にlibcurl3-gnutlsとlibfreetype6が必要になるが、その後juce_gui_basicsのモジュールをビルドする際にlibwebkitgtk-4.0-devとその依存関係が必要になる。juce-audio-basicsをLinuxでビルドするにはlibasound2-devも別途必要になる。

JUCEのダウンロード

Ubuntu bionic以降にはjuce-toolsのパッケージが含まれているので、それをそのまま使うという手もあるが、バージョンは5.4.1など多少古いものになる(現時点で5.4.3)。JUCEのAPIはバージョンによって割と変更されていくることがある(筆者は最近juce-coreのXmlAPIが5.4.3+でだいぶ変わってきていることに気づかずにハマった)。公式サイトのリンクから取得するのが安牌ではないかと思う。

xvfbを使用してProjucer, make, アプリケーション(テスト)を実行する

CircleCIのような環境でGUI依存のコードを実行しようとすると、おなじみのcannot open DISPLAY:0のエラーになって終了することになる。chromeなどであればheadlessビルドを使用してテストまで実行できることもあるが、GUIを使用するコードをそのまま実行できるようになってほしい、というのは割と一般的な需要だろう。

Chromiumをheadlessではない状態でビルドして実行するやり方をまとめているブログがあったので、そのやり方を真似してみた。

xvfb-run -a --server-args="-screen 0 1280x800x24 -ac -nolisten tcp -dpi 96 +extension RANDR" 実行したいコマンド

Projucer --set-global-search-path

Projucerには--resaveというオプションがあるので、これを実行すれば*.jucerファイルからLinuxMakefileを生成できるのだけど、ここには一つ罠があって、Projucerはローカル環境で設定されているグローバルパスを必要とする。これがないとJUCEモジュールの参照を適切に設定できずにファイル生成に失敗する。

このグローバル設定は--set-global-search-pathというオプションで指定できる。このオプションは実行するOSと対象パスを引数として要求する。JUCEのパスと、ビルドするコードやモジュール設定によってはVST3 SDKなどのパスも必要になる。

注意すべきは、このオプションと--resaveは排他的だということだ。つまり、先に--set-global-search-path等を別途実行してから、--resaveを実行すればよい。

サンプルconfig.yml

version: 2
jobs:
  build:
    docker:
      - image: ubuntudesktop/gnome-3-28-1804

    steps:
      - checkout

      - run:
          name: update repos
          command: apt-get update

      - run:
          name: install packages
          command: echo y | apt-get install xvfb wget unzip libc6 libcurl3-gnutls-dev libfreetype6-dev libgcc1 libjpeg-dev libpng-dev libstdc++6 libwebkit2gtk-4.0-dev libx11-6 libxext6 zlib1g make g++ mesa-common-dev libasound2-dev

      - run:
          name: get JUCE 5.4.3
          command: wget https://d30pueezughrda.cloudfront.net/juce/juce-5.4.3-linux.zip

      - run:
          name: unzip JUCE
          command: unzip juce-5.4.3-linux.zip

      - run:
          name: build and run tests
          command: JUCE_DIR=`pwd`/JUCE xvfb-run -a --server-args="-screen 0 1280x800x24 -ac -nolisten tcp -dpi 96 +extension RANDR" run.sh

自分のモジュールでは、run.shの中に、Projucerの実行などをまとめている。

$JUCE_DIR/Projucer --set-global-search-path linux defaultJuceModulePath $JUCE_DIR/modules
$JUCE_DIR/Projucer  --resave 自分のコードの.jucer

cd Builds/LinuxMakefile && make && cd ../..

Builds/LinuxMakefile/build/アプリケーション (実行して自動的にテストを実行して終了するオプション)

ビルドだけで用事が済むのであれば、最後のステップは不要だ。