qmakeで既存のMakefileから環境変数などを取り込みたかった時に行ったこと

この記事は[Qt Advent Calendar 2016](http://qiita.com/advent-calendar/2016/qt)の7日目の記事です．

5日目に@task_jp さんの[qmake で依存ライブラリの有無をチェックし、結果に応じて処理を変えよう](http://qiita.com/task_jp/items/65a1aeba6c7cdccccd98)というqmakeに関係する記事があったのをうけ，そういえば自分もqmakeで最近何かあったなと思い出して記事にしました．

# 想定環境
何かしらの外部のMakefileで管理されているプロジェクトだったりライブラリがある状況に立たされています．
そのライブラリは非常にビルドを行うマシンのハードウェア構成などに依存していて，様々なdefineだったり，ライブラリのリンクが行われています．

この時Qtでこのライブラリをリンクしたりする時，この環境依存の定義などを効率良く集める方法を考えます．
という環境を想定します．

## 方法(Makefileに対して)
### パッと思いつく方法
Makefileで行っているのであるから，makeコマンドで定義される変数を列挙して，それを使えばいいじゃないかという方法．
Makeコマンドの[日本語マニュアル](https://linuxjm.osdn.jp/html/GNU_make/man1/make.1.html)を読んでみると，
> `-p, --print-data-base`
>makefile を読み込んで得られたデータベース(規則と変数の値)を出力する。 特に指定しない限り、その後の動作は通常通りである。> また、 -v オプションで得られるバージョン情報も出力する。 ファイルを全く再構築することなく、データベースの表示だけを行うには > > make -p -f/dev/nul を用いること。

これは使えそうです，Makefileのあるディレクトリで`make -p`でダンプしてみましょう．

```shell-session
CXXFLAGS = -msse -msse2 -Wall -I.. -I$(FSTROOT)/include $(EXTRA_CXXFLAGS)
LDLIBS = $(EXTRA_LDLIBS) $(FSTROOT)/lib/libfst.a $(CUDA_LDLIBS) \
$(foreach dep,$(ADDLIBS), -l$(notdir $(basename $(dep))) )
```
みたいのが出てくるので，じゃあこれをgrepで必要なものを抜き出せば...と思うのですが，よく見てみると
`$(FSTROOT)`や`$(CUDA_LDLIBS)`など変数が混じってしまうので，grepを使っての方法だけでは上手く行かなそうです．

ここで変数も展開した状態でいい感じにやってくれるものはないものか...と調べたところ，[Dumping Every Makefile Variable](https://www.cmcrossroads.com/article/dumping-every-makefile-variable)というこれだよ！という記事が．

### ベターな方法
[Dumping Every Makefile Variable](https://www.cmcrossroads.com/article/dumping-every-makefile-variable)に書かれているように`$(.VARIABLES)`を全て列挙して変数のkeyを取得し，その後にそれからvalueを表示させてしまえば良さそうです．

それでは対象のMakefileにこんなtaskを追加してみましょう．

```   
dump-vars:
    @$(foreach V, \
        $(sort $(.VARIABLES)), \
            $(if \
                $(filter-out environment% default automatic, \
                    $(origin $V)), \
                $(info $V=$($V)) \
            ) \
    )
```

そしてこのMakefileに対して`make dump-vars`をしてみましょう，すると

```shell-session
CXXFLAGS=-msse -msse2 -Wall -I.. -I${ここに展開されたパス}/openfst/include -std=c++0x
LDLIBS= ${ここに展開されたパス}/lib/libfst.a -ldl -lm -lpthread -framework Accelerate
```

のように展開された結果が返ってきました，これで必死にここの変数はなんだっけ...ということをしなくても済みそうです．

## qmakeに取り込もう
さて，上記の方法で展開されたビルドに必要な変数が取り込むことができました．
Qtではqmakeを使うので，qmakeからこれを叩いて必要な追加パラメータを取得する必要があります．

[qmakeのマニュアル](http://doc.qt.io/qt-5/qmake-manual.html)を読んでみると，[_Replace Functions_](http://doc.qt.io/qt-5/qmake-language.html)というものがあって，自分で定義ができそうです，ここに入れ込んでみましょう．

```
defineReplace(fromMakefileFlags) {
    !win:{
        return ($$system(make -f target.mk dump-vars| grep -E '^$$1' |sed s/^$$1=//g))
    }
}
```

こんな感じに書くとsystemのmakeコマンドを叩いてgrepとsedでいい感じに定義を取ってくる，ということができます ~~（win環境が用意できなかったので，この辺Windows向けに誰か書き換えてくれないかなーそもそもnmakeだったりそもそもVSのやつだとコマンドが思いっきり違って，Powershellで文字列比較とかなのかな）~~

そしてこれを

```
QMAKE_CXXFLAGS *= $$fromMakefileFlags(CXXFLAGS)
QMAKE_LFLAGS += $$fromMakefileFlags(LDFLAGS)
```

みたいに呼ぶことで，既存のMakefileに定義されたフラグを回収して，Qtのプロジェクトにいかすことができます．


## 最後に
qmakeで既存のMakefileから環境依存変数を効率良く引っ張ってくることができました．これを使わなければいけない状況っていうのは多分そこまで多くないという感じはするのですが，もしも詰まってしまった時この記事を思い出してくだされば幸いです．
QtCreatorにMakefileのプロジェクトを取り込む機能などもあった気がするのですが，それは試してみませんでした．もしかしたらそっちの方が楽だったのかもしれないなと今更になって思います．

デスクトップ環境のクロスプラットフォームアプリケーションを作るとなった時最近はElectronがだいぶ勢力を増してきましたが，Qtもまだまだイケてる環境だと思うので，是非ともユーザが増えてコミュニティが盛り上がってくれればなと思います．

明日はそういったコミュニティというか[勉強会を開催し始めた](http://nekomatu.blogspot.com/2016/12/qt-nagoya-venue.html) @nekomatu さんの QtCreatorの翻訳をレビューした話 です．こういった多くのユーザに触れるアプリケーションに対して自分の何かしらの貢献が活かされるというのはいいですね，お楽しみに．