qmakeで大規模プロジェクト

[QtAdventCalender](http://qiita.com/advent-calendar/2015/qt)15日目の記事です。

#はじめに
Qtの中でもqmakeというややマニアックな分野の紹介なので(よく使われるけど使い込む人は少ない印象)、興味がなければあんまり読む価値はないと思います。
日本語情報少ないのもあって、思いつくものを雑多に並べてみました。



ネット上だとシンプルなqmakeのサンプルなどはありますが、複雑化した場合のノウハウが少なく、ちょっと苦労したのでまとめてみます。
[マニュアル](http://doc.qt.io/qt-5/qmake-manual.html)(英語)は良くまとまってますが、makeを忘れた人間には読解が辛い……
日本語でqmakeの良いまとめだと[こちら](http://www.slideshare.net/ShingoIshida/qmake)とかでしょうか。

というわけでqmakeとQtCreatorを駆使してそれなりに大きなプロジェクトを管理するノウハウについて紹介します。
本格的に大規模なやつはOSSとして培われた文化だったり、お高い管理ソフトがあったり、自社製独自管理だったりすると思うので、
そこまで大きくないけど管理が大変になってくる、位の規模を想定してます。
経験からまとめたものであり、正解では無いと思いますのでもっと良い方法があればぜひ教えて下さい。


#基本構成
大きなプロジェクトって言っても具体的に基準はどこ？って話になると思うのですが、とりあえず複数のQtプロジェクトには分かれていると思います。
だいたい以下のようなプロジェクト群に分かれて、複数人によって開発されているという想定です。

* 実行用プロジェクト
* ライブラリプロジェクト群
* ユニットテストプロジェクト群
* モジュール開発用プロジェクト群(自動ユニットテストよりもう少し自由な形式)

#整理する
プロジェクト構成が一目瞭然で、モジュールの追加やリファクタリングが発生しても混乱なく変更できると嬉しいです。
##階層化する
プロジェクトは以下を使っていくらでも階層化できます。

```
TEMPLATE = subdirs
```
あまり深すぎると良くないのでルールを決めたほうが良いかもしれません。
ちなみにQtCreator3.6からincludeした設定ファイルも階層化されるようになります。(複雑な設定には便利)

##設定ファイルの分割
includeを使うことで、他の設定ファイルを読み込むことが出来ます。
複数のプロジェクトから共通で参照されるべき設定などをこれで書きましょう。
例えばQtCreatorからライブラリリンク生成したりすると、やや複雑な(1行では終わらない)処理が追加されるので、こういうのもファイル分割しておいてincludeすることでコピペトラップにはまりにくくなります。

```
 #共有設定を読み込み
include(config/_config.pri)
```

この共有設定でプロジェクト全体のパスを変数に入れておくと便利だと思います。
```
 #他のプロジェクトからパスを辿りたいときは$$PROJECTで参照できるようにする
PROJECT = $$PWD/..
```

##コンパイルファイル一覧の設定
共通設定を抜き出したとしても、残りの必要な設定を一つのqmakeファイルに書くと後で見たときに解読困難になるし、コピペが発生しやすいです。
そこで、project_A.proがあったときにHEADERSとSOURCESをsrc_project_A.priなどにまとめておいてincludeする方法がおすすめです。
共有もしやすくなるのでライブラリ用とテスト用で同じファイル一覧を参照したりできるようになります。

```
project_A.pro //プロジェクト本体
 ↓ include
src_project_A.pri //コンパイルファイル一覧。ファイルが追加されても一元管理出来る
 ↑ include
test_project_A.pro //テスト用プロジェクト
```

##関数を使う

文字列の基本操作とかには[組み込み関数](http://doc.qt.io/qt-5/qmake-function-reference.html)が用意されています。
[確認用の関数](http://doc.qt.io/qt-5/qmake-test-function-reference.html)もそれとは別に用意されています。
独自の関数も定義できます。

例。QtCreatorからライブラリリンク追加したときに自動生成される項目を関数化

```
defineReplace(libFunc) {
    lib = $$1
    dir = $$2

    libs =

    win32:CONFIG(release, debug|release): libs = -L$$dir/release/ -l$$lib
    else:win32:CONFIG(debug, debug|release): libs = -L$$dir/debug/ -l$$lib
    else:unix: libs = -L$$dir/ -l$$lib

    return ($$libs)
}
```

```
LIBS +=$$libFunc( project_A, $$MY_LIB_OUT_PATH)
```

#共有する
複数人で編集するので、まずは影響範囲がわかりやすい状態であって欲しいと思います。
#QtCreatorのバージョン
メジャーバージョンがあってれば細かく言う必要はないかなと思ってたんですが、priファイルの階層の見え方とかちょっと変わったりするので、良かれと思って直したことが古いバージョンのユーザには迷惑になったりします。

##共有しない設定
共有設定が_config.priとかだとして、_config_user.priのような個人設定も作っておくと便利です。
これがあると環境依存する設定とか好き勝手弄れるようになります。やり過ぎるとあとで共有するときに結局辛いけど。
バージョン管理からは無視するようにしておきましょう。

#デバッグ
qmakeのデバッグはmessageを使った出力デバッグが基本だと思います。
QtCreatorだと全体メッセージの項目に出力されるので、確認しながら調整しましょう。

##状態のメッセージ
Qt関連のログ出力は以下のようなものを置いておくと誰かが使うかもしれません。
```
message(Qt version: $$[QT_VERSION])
message(Qt is installed in $$[QT_INSTALL_PREFIX])
message(Documentation: $$[QT_INSTALL_DOCS])
message(Header files: $$[QT_INSTALL_HEADERS])
message(Libraries: $$[QT_INSTALL_LIBS])
message(Binary files (executables): $$[QT_INSTALL_BINS])
message(Plugins: $$[QT_INSTALL_PLUGINS])
message(Data files: $$[QT_INSTALL_DATA])
message(Translation files: $$[QT_INSTALL_TRANSLATIONS])
message(Settings: $$[QT_INSTALL_SETTINGS])
message(Examples: $$[QT_INSTALL_EXAMPLES])
message(Demonstrations: $$[QT_INSTALL_DEMOS])
```

環境変数の確認はこんな感じ。$$() で環境変数へのアクセスになります。
```
message(MY_PROJECT_WORKSPACE: $$(EUCLID_WORKSPACE))
message(BOOST_ROOT: $$(BOOST_ROOT))
```
##エラーチェック
指定の環境変数がない場合のエラーとかは以下のようになります。
環境変数に頼り過ぎるのは良くないと思うけど、やむを得ず必要なときは結構あると思うので、忘れた人がちゃんと引っかかるようにチェックしましょう。

```
isEmpty( MY_PROJECT_WORKSPACE ) {
    # 環境変数を設定してください。
    error( !!!! Please set MY_PROJECT_WORKSPACE . !!!!)
}
```
##ライブラリとコードの切り替え
lib(dll)としてリンクするのか、ソースコードを直接使うのかを切り替える場合にはCONFIGに独自定義のオプションを加えるのが便利です。

```
 #テスト用にdllではなくて、ソースコード直接使いたい
CONFIG += direct_project_A
```

```
INCLUDEPATH += $$PROJECT/direct_project_A

direct_project_A{
 #	直接ソースを使う場合(テスト、デバッグなど)
	include($$E_CORE/direct_project_A/src_project_A.pri)
}
else {
 #	ライブラリとしてリンクする場合
	LIBS += $$PROJECT/project_A
	PRE_TARGETDEPS+= $$PROJECT/project_A
	DEPENDPATH += $$PROJECT/project_A
}


```
ちなみに最初の{の前に改行を挟むとelseが認識されないとかよくわからない文法になってます。


#何かおかしいとき
##再リンクされてないかも？
実行できるけど、変更したコードが反映されていないように見える時など。リビルドすると直るやつ。
多分Windows限定だと思うのですが、依存するライブラリに更新があったときにうまくリンクしてくれないようです。
実行ファイルのlibを削除するとリンクだけやり直してくれるのでリビルドよりは早いです。

##改行文字忘れてるかも？
マージしたときとかに起こりやすいのですが、以下のように改行時のバックスラッシュ忘れるとよくわからないエラーになるときがあります。

```
SOURCES += \
	$$PWD/a.cpp \
	$$PWD/b.cpp 
	$$PWD/c.cpp \
```

#まとめ
qmakeについて思いつくことを書いてみました。
伝わる人に伝われば良いという感じで書いてるので、あんまりまとまってない気がします。
質問とかあればコメントでどうぞ。(どちらかというと私が教わりたいけど)