CMakeLists.txt から始めよう

注意：この記事は古くなっています。この記事を書いた人は、現在Windowsでの開発をしていないため、最新の状況を把握していません。

[Visual C++ での CMake プロジェクト](https://docs.microsoft.com/ja-jp/cpp/ide/cmake-tools-for-visual-cpp?view=vs-2017)
という記事がMicrosoftから公開されています。


-----

CMakeLists.txtを書くことで、VisualStudioの設定が楽になることがわかったので、再検索しなくて済むようにメモを書く。

（[Visual Studio の各バージョンごとに vcxproj を用意して… というのが辛くなったため、 CMake によるプロジェクト生成という流派にちょっと入門してみました。](http://blog.techlab-xe.net/archives/5340)2017/6/16 の記事　を見つけました。VisualStudioの設定を楽するためにCMakeを使い出す人が増えているようです。）

(CMakeLists.txtの書き方については、もっと適切な記事があるはずなので、そちらを利用したい)

- [cmake の使い方](http://www.cs.gunma-u.ac.jp/~nagai/wiki/index.php?cmake%20%A4%CE%BB%C8%A4%A4%CA%FD)
- [CMake 簡易まとめ](http://qiita.com/janus_wel/items/a673793d448c72cbc95e)
- [OpenCV3.1.0でSIFTとかSURFとかBINGを使う](http://qiita.com/yizumi1012xxx/items/762005e4b41cafcf1d43)
- [OpenCV3.1.0をCMakeを用いてbuildする](http://qiita.com/yizumi1012xxx/items/124520609e0fe2fbef44)
- [find_packageの動作]
(http://qiita.com/osamu0329/items/bd3d1e50edf37c277fa9)
- [お手軽な xxx-config.cmake の作成方法]
(http://qiita.com/osamu0329/items/134de918c0ffa7f0557b)
- [CMake: 条件分岐](http://qiita.com/mrk_21/items/49d8802dc63a2791bcc3)




#VisualStudioの設定の憂鬱
WindowsでVisual Studioを使ってC++開発をする際に次のわずらわしい点がある。

-  Visual Studioの**バージョンによってソリューションファイルの記述が異なり**、Visual Studioの設定が共有できない。
-  OpenCVのバージョンが異なるたびにVisualStudioの各Projectに対してDebugモード、Releaseモードの両方にわたって、設定します。
[Build][Properties][Debug] [Configuration] [VC++ Directories][Include Directories]
それと[Library Directories]に**OpenCVのインストールされているディレクトリを各人ごとに書く**のが面倒です。
- C++の**ソースコードにライブラリのバージョンを記述**することも避けたいことです。

```
#pragma comment(lib, "xxx.lib")
```

これらの問題点は、CMakeLists.txtを記述してcmakeを使えば解決できます。
そこでCMakeLists.txtの書き方を他のページを参照しながら示します。

#CMakeLists.txtの書き方
この例ではaviDetector.exeを aviDetector.cppから作成する例です。

>${PROJECT_HOME}/
  aviDetector.cpp
の時は以下の2行を書くだけでOK:
cmake_minimum_required(VERSION 2.8)
add_executable(aviDetector aviDetector.cpp)

次に、OpenCVのライブラリを使うための設定を参照します。
Pythonのライブラリとリンクする場合の設定を追加しました。

```cmake:CMakeLists.txt
cmake_minimum_required(VERSION 3.8)
# プロジェクト名を指定する
project(aviDetector)

# 次の行を追加するとMinSizeRelとRelWithDebInfoの選択肢を生成することが抑制できます。
set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "limited configs" FORCE)

add_executable(aviDetector aviDetector.cpp)
# C++11
target_compile_features(aviDetector
  PRIVATE cxx_std_11
  )
# マクロ定数WITH_SOMELIBを追加
target_compile_definitions(aviDetector
  PRIVATE WITH_SOMELIB
  )

# OpenCVのパッケージを探す
find_package(OpenCV REQUIRED)
find_package(PythonLibs REQUIRED)
 
# OpenCVが見つかった場合のみ設定を行う
if(OpenCV_FOUND)
  # インクルードパスを指定
  target_include_directories(aviDetector
    PRIVATE
      ${OpenCV_INCLUDE_DIRS}
      ${PYTHON_INCLUDE_DIRS}
    )
 
  # リンクするライブラリ指定
  target_link_libraries(aviDetector
    ${OpenCV_LIBS}
    ${PYTHON_LIBRARIES}
    )
endif(OpenCV_FOUND)
```
CMakeLists.txtさえ書いてあれば、
あとはCmakeを使って[configure][generate]するだけです。
*.sln, *.vcxprojのファイルができあがりです。
最近のOpenCVをbuildした人にはご存知のとおりです。

追記：OpenCVの公式サイトでのCMakeについての記載
[Using OpenCV with gcc and CMake](http://docs.opencv.org/3.0-beta/doc/tutorials/introduction/linux_gcc_cmake/linux_gcc_cmake.html#linux-gcc-usage)

＜追記：プリプロセッサ用のマクロの定義をCMakeLists.txtで行う＞
次のような記載をCMakeLists.txtに追加します。

add_definitions(-DWITH_OPENCV2)

コンパイラに対して
-DWITH_OPENCV2
オプションをつけて実行することに対応します。


http://stackoverflow.com/questions/9017573/define-preprocessor-macro-through-cmake

＜追記＞
#OpenCV3系統とOpenCV2系統の両立
このようにして書いておけば、CmakeでOpenCV_DIRで今回のターゲットとするバージョンのあるディレクトリを指定すれば、OpenCV2系統、OpenCV3系統にも共通の設定にできる。
OpenCV3系統でbuildすると、C++スタイルになりきっていないコーディングの部分でbuildに失敗します。その場合にはC++スタイルになるように記述の違いを検索して調べて、C++スタイルで書き直します。C++スタイルで書き換えたソースコードは、OpenCV2系統でもbuild できます。
OpenCV2のサンプルコードではC++スタイルとCスタイルとが混在していたり、
using namespace cv;
の記述があったりします。
今後のメンテナンスを考えると using namespace cv;
は使わないようにしておくのがよさそうです。

#### OpenCV4ではC++11が前提に　(追記)
　C++11になることで、より洗練された安全な書き方に集中されるようになっていきます。

次の1行をCMakeLists.txt に含めておく。

add_definitions(-std=c++11)


###**Boostの設定を追加する**
まずはBoostの本家のサイトでBoostをCMakeLists.txtに書く変数のリストをみてみよう。
find_package
をした後に
${Boost_INCLUDE_DIRS}
といった変数がCMakeLists.txtで参照できるようになります。 

FindBoost
https://cmake.org/cmake/help/v3.0/module/FindBoost.html



以下のstackoverflowの記事などを参考にしてみることとしよう。
[How do you add boost libraries in CMakeLists.txt](http://stackoverflow.com/questions/6646405/how-do-you-add-boost-libraries-in-cmakelists-txt)


###**OSの違いをCMakeLists.txtに反映させる**
[CMakeで大きめのプロジェクトを構成するためのメモ](http://qiita.com/shibukawa/items/af7ff2e1178fbb3ab6de)
の中に、
```if(WIN32)```
に始まる記述があります。
```if(APPLE)```
に始まる記述もあります。
```if(UNIX)```もできるようです。

[CMake Useful Variables](https://cmake.org/Wiki/CMake_Useful_Variables#System_.26_Compiler_Information)


以下のような記述もできます。

```
if(EXISTS src/a.cpp) # src/a.cpp というパスが存在しているかどうか
```



####**追記：**
CMakeLists.txtから作ったソリューション、プロジェクトファイルの不満(Visual Studio)
・不満１：vcxprojファイルに書かれるファイル・ディレクトリのpathが絶対pathであること。
・不満２：作ったソリューションの設定が、その後もCMakeLists.txtによって自動更新がかかってしまうこと。
・不満３：プロジェクトの設定のほぼ全てをCMakeLists.txtから行いたいが、まだその書き方を習得していない点
・不満４：何も指定しないとmultibyteの文字コードがVisualStudio用に生成され、Unicodeに手作業で直す必要を生じる。


#####不満１への対策
 次のようなスクリプトで、pathの設定をファイルからの相対パスに書き換える。

```py:relativepath.py
# -*- coding: utf-8 -*-
# pylint: disable-msg=C0103
"""
convert absolute path to relative path
"""
import os, string, glob, shutil
names = glob.glob("*.vcxproj")+glob.glob("*.vcxproj.filters")
cwd = os.getcwd()
cwd2 = string.join(string.split(cwd, os.path.sep), "/")
for p in names:
    print p
    shutil.copy(p, p+".bak")
    data = open(p, "rt").read()
    data = data.replace(cwd, ".")
    data = data.replace(cwd2, ".")
    oname = p
    open(oname, "wt").write(data)
```


以下のページにある解説を参考にすれば、すんなり相対パスの記述ができるようだ。
[RELATIVE_PATH](http://qiita.com/mrk_21/items/5e7ca775b463a4141a58)

[CMake で相対パスを使用する](http://kkkon.sakura.ne.jp/wp3/2012/01/20/cmake-use-relatie-paths/)

> 基準となるパス<directory>からの対象のパス<file>への相対パスを変数<variable>に格納します。



#####不満２への対策
Solution Explorer中の
[CMake Rules]を右クリックで[delete]することで解消する。

#####不満３への対策
インクルードパスを指定するときはinclude_directoriesコマンドを使う。
ライブラリの追加
target_link_libraries(myapp foo)
ライブラリパスの指定
link_directories(/path/to/lib)

#####不満4への対策
以下の行を追加するとVisualStudioでもunicodeのライブラリをリンクするようになる。

add_definitions(-D_UNICODE)

追記：
CMakelists.txt をUTF-8で保存するときにはBOMを**つけない**ことが必要なようです。
[Unable to parse CMakeLists.txt in UTF-8 with BOM encoding]
(https://public.kitware.com/Bug/print_bug_page.php?bug_id=11137)

参照したURL
「ごく簡単なcmakeの使い方」
http://qiita.com/termoshtt/items/539541c180dfc40a1189

「初めてのOpenCV開発 ― Visual Studio／CMake／NuGetでプロジェクト作成【OpenCV 2.4.9】」「CMake設定ファイル作成」
http://www.buildinsider.net/small/opencv/03

「CMakeを使ってOpenCVを楽にセットアップする方法 (for Visual Studio 2010)」
http://sky-y.hatenablog.jp/entry/2014/05/23/143722

「CMakeを使ってみた（2）もう少しまともなプロジェクト」
http://wagavulin.hatenablog.com/entry/2011/11/27/222642

CMake チュートリアル
http://opencv.jp/cmake/cmake_tutorial.html


CMake: 便利なコマンド・変数
http://qiita.com/mrk_21/items/5e7ca775b463a4141a58


[Universal OpenCV プロジェクト](https://qiita.com/tomoaki_teshima/items/cbdee2866b6f344a90d0)

[CMakeスクリプトを作成する際のガイドライン](https://qiita.com/shohirose/items/5b406f060cd5557814e9)