CMakeLists.txt から始めよう


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

Visual C++ での CMake プロジェクト
という記事がMicrosoftから公開されています。


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

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

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

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のライブラリとリンクする場合の設定を追加しました。

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

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

add_definitions(-DWITH_OPENCV2)

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

<追記>

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

OSの違いをCMakeLists.txtに反映させる

CMakeで大きめのプロジェクトを構成するためのメモ
の中に、
if(WIN32)
に始まる記述があります。
if(APPLE)
に始まる記述もあります。
if(UNIX)もできるようです。

CMake Useful Variables

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

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

追記:

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

不満1への対策

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

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

CMake で相対パスを使用する

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

不満2への対策

Solution Explorer中の
[CMake Rules]を右クリックで[delete]することで解消する。

不満3への対策

インクルードパスを指定するときは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

参照した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 プロジェクト

CMakeスクリプトを作成する際のガイドライン