Qt のソースコードを取得してビルドをする方法について

## はじめに

この記事は [Qt Advent Calendar 2018](https://qiita.com/advent-calendar/2018/qt) 2日目のエントリです。

最近 [The Qt Company を退職した](http://qt5.jp/left-the-qt-company.html) @task_jp です。仕事的に近しい人々が予想以上に歓迎してくれるので、正しい選択をしたかな？と思えるようになってきた今日この頃で、現在は転職先で必要となる勉強をあれこれしている真っ最中です。

昨日は @kanryu さんによる [Qbs でRuleやProductを自作する方法(ざっくり解説)](https://qiita.com/kanryu/items/4c0074a04c7f33e3ba89) という記事でした。Qt 6 に向けて、ビルドシステムをどうするかという議論が Qt の開発者のメーリングリストで色々なされていますが、どれも一長一短でなかなか難しいですね。

今日は、今更ですが、Qt 自体のビルド方法を解説したいと思います。

**Qt のフルビルドにはとても時間がかかります。少しでも作業を効率化して、本当に自分がやりたいことに時間を費やしましょう。**

公式のドキュメントは以下を参照してください。
* http://code.qt.io/cgit/qt/qt5.git/tree/README.git
* https://wiki.qt.io/Building_Qt_5_from_Git#Getting_the_source_code

## 動機

元弊社で、同じオフィスにいたエンジニアが誰も Qt のビルドの仕方を理解していなかったので、色々不安になりまして、良い機会なので Qt のビルドの仕方を日本語で紹介しておこうと思いました。

## Qt のソースコード周りの状況の整理

Qt 3 までは１つのライブラリに全ての機能が詰め込まれていました。
Qt 4 では、ライブラリの分割がなされました。
Qt 5 では、リポジトリの分割もなされました。

というわけで、現在の Qt は複数のモジュールから構成された巨大なプロジェクトになっています。本日時点で、[qt5](http://code.qt.io/cgit/qt/qt5.git) というメインのリポジトリの下に **46個もの submodule** が存在しています。

* [qtbase](http://code.qt.io/cgit/qt/qtbase.git)
* [qtsvg](http://code.qt.io/cgit/qt/qtsvg.git)
* [qtdeclarative](http://code.qt.io/cgit/qt/qtdeclarative.git)
* [qtactiveqt](http://code.qt.io/cgit/qt/qtactiveqt.git)
* [qtscript](http://code.qt.io/cgit/qt/qtscript.git)
* [qtmultimedia](http://code.qt.io/cgit/qt/qtmultimedia.git)
* [qttools](http://code.qt.io/cgit/qt/qttools.git)
* [qtxmlpatterns](http://code.qt.io/cgit/qt/qtxmlpatterns.git)
* [qttranslations](http://code.qt.io/cgit/qt/qttranslations.git)
* [qtdoc](http://code.qt.io/cgit/qt/qtdoc.git)
* [qtrepotools](http://code.qt.io/cgit/qt/qtrepotools.git)
* [qtqa](http://code.qt.io/cgit/qt/qtqa.git)
* [qtlocation](http://code.qt.io/cgit/qt/qtlocation.git)
* [qtsensors](http://code.qt.io/cgit/qt/qtsensors.git)
* [qtsystems](http://code.qt.io/cgit/qt/qtsystems.git)
* [qtfeedback](http://code.qt.io/cgit/qt/qtfeedback.git)
* [qtdocgallery](http://code.qt.io/cgit/qt/qtdocgallery.git)
* [qtpim](http://code.qt.io/cgit/qt/qtpim.git)
* [qtconnectivity](http://code.qt.io/cgit/qt/qtconnectivity.git)
* [qtwayland](http://code.qt.io/cgit/qt/qtwayland.git)
* [qt3d](http://code.qt.io/cgit/qt/qt3d.git)
* [qtimageformats](http://code.qt.io/cgit/qt/qtimageformats.git)
* [qtgraphicaleffects](http://code.qt.io/cgit/qt/qtgraphicaleffects.git)
* [qtquickcontrols](http://code.qt.io/cgit/qt/qtquickcontrols.git)
* [qtserialbus](http://code.qt.io/cgit/qt/qtserialbus.git)
* [qtserialport](http://code.qt.io/cgit/qt/qtserialport.git)
* [qtx11extras](http://code.qt.io/cgit/qt/qtx11extras.git)
* [qtmacextras](http://code.qt.io/cgit/qt/qtmacextras.git)
* [qtwinextras](http://code.qt.io/cgit/qt/qtwinextras.git)
* [qtandroidextras](http://code.qt.io/cgit/qt/qtandroidextras.git)
* [qtwebsockets](http://code.qt.io/cgit/qt/qtwebsockets.git)
* [qtwebchannel](http://code.qt.io/cgit/qt/qtwebchannel.git)
* [qtwebengine](http://code.qt.io/cgit/qt/qtwebengine.git)
* [qtcanvas3d](http://code.qt.io/cgit/qt/qtcanvas3d.git)
* [qtwebview](http://code.qt.io/cgit/qt/qtwebview.git)
* [qtquickcontrols2](http://code.qt.io/cgit/qt/qtquickcontrols2.git)
* [qtpurchasing](http://code.qt.io/cgit/qt/qtpurchasing.git)
* [qtcharts](http://code.qt.io/cgit/qt/qtcharts.git)
* [qtdatavis3d](http://code.qt.io/cgit/qt/qtdatavis3d.git)
* [qtvirtualkeyboard](http://code.qt.io/cgit/qt/qtvirtualkeyboard.git)
* [qtgamepad](http://code.qt.io/cgit/qt/qtgamepad.git)
* [qtscxml](http://code.qt.io/cgit/qt/qtscxml.git)
* [qtspeech](http://code.qt.io/cgit/qt/qtspeech.git)
* [qtnetworkauth](http://code.qt.io/cgit/qt/qtnetworkauth.git)
* [qtremoteobjects](http://code.qt.io/cgit/qt/qtremoteobjects.git)
* [qtwebglplugin](http://code.qt.io/cgit/qt/qtwebglplugin.git)

主に組み込み製品向けや、Qt 自体の開発を目的とした際に自分で Qt をビルドすることになるのですが、すべてのリポジトリからソースコードを取得してビルドをすると膨大な時間がかかります。**効率よく作業をする** ために、必要最小限のものからはじめることをお勧めします。

# 基本的な流れ

## Qt のソースコードの取得方法

### トップのリポジトリの取得

公式な方法は以下の通り

```
$ git clone git://code.qt.io/qt/qt5.git
```
日本国内のネットワークからは、[日本Qtユーザー会](http://qt-users.jp/) が提供するミラー [git://git.qt-users.jp/mirror/qt/qt5.git](http://git.qt-users.jp/git/?p=mirror/qt/qt5.git) を利用するとよいかもしれません。 

5.12 の開発ブランチを取得する場合

```
$ git clone git://git.qt-users.jp/mirror/qt/qt5.git --branch 5.12
```

5.9.7 のリリースを取得する場合

```
$ git clone git://git.qt-users.jp/mirror/qt/qt5.git --branch v5.9.7
```

### サブモジュールの取得

```
$ cd qt5
```

公式な方法は以下の通り

```
$ perl init-repository
```

これにより、前述の46個のサブモジュールが取得できます。

しかし、ほとんどのユースケースにおいて、本当に必要なサブモジュールの数はたかだか数個です。

Qt Quick で何かを作りたい場合

```
$ ./init-repository --module-subset=qtbase,qtdeclarative
```

Wayland 上で動かす場合

```
$ ./init-repository --module-subset=qtbase,qtdeclarative,qtwayland
```

Qt Multimedia で何かを作りたい場合

```
$ ./init-repository --module-subset=qtbase,qtdeclarative,qtmultimedia
```

Qt 3D で何かを作りたい場合

```
$ ./init-repository --module-subset=qtbase,qtdeclarative,qt3d
```

このように、qtbase + qtdeclarative をベースに、自分が必要とするものだけを明示的に指定して取得し、無駄に全部をビルドするのはやめましょう。

## Qt のビルドの設定

### 公式の方法

```
$ ./configure -developer-build -opensource -nomake examples -nomake tests
```

wiki の configure のオプションはなんかこんな感じになっていて謎です。

ソースツリー内でビルドしてしまうと不都合が多いので、ソースツリーの隣にビルド用のディレクトリを作成することを **強くお勧めします**。

```
$ cd ..
$ mkdir build
$ cd build
$ ../qt5/configure
```

オプションを何も渡さない場合、すべてデフォルトのものを利用してビルドがされます。
`configure` に `-help` を渡すとほぼすべてのオプションについての選択肢が表示されますが、個人的によく使うものをここで紹介したいと思います。

- -opensource/-commercial
    - ライセンスの種類を設定します。これらを渡さない場合には、`configure` の実行時に選択をする必要があります。
- -confirm-license
    - ライセンスに合意します。これを渡さない場合は `configure` が確認のメッセージを出します。
- -prefix /opt/qt/5.12
    - Qt のインストール先のパスを指定します。デフォルトは /usr/local/Qt-*$QT_VERSION* です。
- -ccache
    - [ccache](https://ja.wikipedia.org/wiki/Ccache) を利用します。繰り返し Qt のコードをビルドする場合に高速化が期待できます。
- -make libs
    - Qt で何をビルドするかの設定で、ライブラリのみをビルドするように明示的に指定します。何も指定しない場合は、libs, examples, tools がビルドされます。-make libs は -no-make examples -nomake tools と同等です。
- -no-pch
    - [precompiled header](https://en.wikipedia.org/wiki/Precompiled_header) の使用を抑制します。Qt のソースを変更しビルドを繰り返す場合に pch は問題となりやすいので外すことが多いです。
- -no-widgets
    - QtWidgets のビルドをやめます。Qt Quick のみを使用する場合は、これによりビルド時間を短縮することができます。
- -qt-zlib
    - Gentoo で Qt3D をビルドする際の問題([QTBUG-68467](https://bugreports.qt.io/browse/QTBUG-68467))を回避する場合に使用します。

`configure` の出力結果は、config.summary というファイルに保存されるので、確認することが可能です。

## Qt のビルド

```
$ nice make -j4; make
```

Qt のソースコードを変えるなどで、失敗が想定される場合は、並列ビルドをした後で、単に make をし、エラーの箇所を発見しやすくします。

## Qt のインストール

```
$ sudo make install -j4
```

# 応用編

## Qt のソースコードを更新する場合
```
$ cd qt5
$ git pull --rebase
$ ./init-repository -f --module-subset=...
```
qt5.git を最新にし、init-repository に上書きを強制する `-f` オプションを渡します。

## インストールをしないで使いたい

configure のオプションに開発者向けのオプションである `-developer-build` を指定すると、make install なしに使うことができるようになります。
この場合、qmake は qtbase/bin/ 以下のものを使います。
-make/-no-make を指定しないと、tests もビルドされるようになるので、必要のない場合には -nomake tests を追加するなどの対応をしましょう。

## サブモジュールの依存関係

例えば、QtQuickControls2 を使おうと、

```
$ ./init-repository --module-subset=qtbase,qtdeclarative,qtquickcontrols2
```
とし、configure をしても qtquickcontrols2 はビルドされません。これはそのモジュールの依存関係が満たされていないためです。モジュールの依存関係は、[qt5/.gitmodules](http://code.qt.io/cgit/qt/qt5.git/tree/.gitmodules) という隠しファイルに記載されています。

```ini:.gitmodules
[submodule "qtquickcontrols2"]
	depends = qtgraphicaleffects
	recommends = qtimageformats
	path = qtquickcontrols2
	url = ../qtquickcontrols2.git
	branch = 5.11
	status = addon
```

というわけで、この場合は

```
$ ./init-repository --module-subset=qtbase,qtdeclarative,qtgraphicaleffects,qtquickcontrols2
```

としましょう。

## configure のオプションを修正して再実行する

configure 実行後のビルドディレクトリには、configure を再度実行するための仕組みが用意されています。
`config.opt` ファイルには、configure に設定されたオプションの一覧が記載されています。
`config.status` スクリプトを実行することで、`config.opt` を引数に再度 configureを実行することが可能です。

というわけで、
1. config.opt でオプションを変更
2. $ ./config.status を実行
という手順で、configure の再実行が可能になります。

`config.cache` というファイルに前回実行時の結果がキャッシュされていますので、キャッシュ自体を使用しない場合はこのファイル自体を削除してください。
また、見つからなかった外部の依存ライブラリをインストールした際など、キャッシュの一部を無効化するには config.cache を開き、中の該当しそうなエントリを削除するということも可能です。

## 特定のライブラリ、プラグインのみを再ビルドする場合
```
$ make -C qtbase/src/plugins/platforms/eglfs && scp qtbase/plugins/platforms/libqeglfs.so root@192.168.2.1:/opt/qt/5.12/plugins/platforms/
```
のように、最低限のものをビルドし、デバイスに転送するように工夫することがよくあります。

## 特定の examples をビルドしたい

-make libs(= -nomake examples) で、基本的に examples はビルドしないけれど、特定のものをビルドしたいということがよくあります。qtdeclarative の場合、

```
$ cd qtdeclarative
$ make sub-examples-qmake_all
```
を実行することで、examples 以下の個々のサンプルの Makefile が生成されます。

```
$ cd quick/text/
$ make
$ ./text
```

とすることで、基本的には examples はビルドをしないけれど、特定のものをビルドすることが可能になります。

# まとめ

* ..../qt5.12/
    * qt5 (ソースツリー)
    * init-repository.sh
    * developer-build (開発ビルド)
    * raspberry-pi (ラズパイ用ビルド)
    * m3ulcb (R-Car M3用ビルド)

のようなディレクトリ構成で作業をするのをお勧めします（誰にだ）

複数回 init-repository を実行する予感がする場合には以下のようなスクリプトを用意しておくと、近い将来の自分に感謝されるでしょう。

```sh:init-repository.sh
#/bin/sh
./init-repository -f \
    --codereview-username=tasuku.suzuki \
    --modules-subset=qtbase,qtdeclarative,qt3d,qtmultimedia,qtlocaiton
```

```
$ cd developer-build
$ ../qt5/configure -opensource -confirm-license -developer-build -no-pch -no-widgets -make libs
```

作業はこういった感じで行いましょう。


**Qt のフルビルドにはとても時間がかかります。少しでも作業を効率化して、本当に自分がやりたいことに時間を費やしましょう。**


明日は現時点では空いているので、なんでもいいので、書いてみたい人がいれば、お気軽にご参加くださいね！