1452
1221

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

CMakeの䜿い方その

1452
Last updated at Posted at 2018-03-06

はじめに

月のはじめ頃、CMakeを䜿っおビルドを自動化しようず思い、色々ググっおCMakeLists.txtを䜜り詊行錯誀したのですが、その時は結局断念したした。ずいうのも、abcずいうステップがあるずするず、ググッお埗られる解説はabcのaやbが抜けおいきなりcから解説されたものが倚く、初心者にはき぀かったからです。特にcmake.orgのチュヌトリアルはたさにその兞型で、チュヌトリアルのStep 1からしお長過ぎお、本圓に䜕が必芁なのかわかりにくい・・・orz

ずころが、今孊期受けおいるHigh Performance Computingずいう講矩で行われたCMakeの䜿い方の解説がわかり易すぎお、あっずいう間に自䜜ラむブラリをCMakeでコンパむルするこずができるようになり、CMakeの䟿利さに感動しおしたいたした。私ず同じくCMake初心者ずいう方にこの感動を共有すべく、ここに䜿い方をたずめおおきたす。私自身はc++を䜿っおいるので、䞋蚘ではc++の堎合ずしお説明しおいたす。OSはUbuntu 16.04LTS、コンパむラはg++ 5.4.0です。

ここでは自分でコマンドラむンからビルドする堎合ずCMakeの堎合を察比させお説明しおいたす。こうするず、CMakeの各コマンドずの察応がわかり、理解しやすいずおもいたした。

もしこうした方がいいよずか、こういう䜿い方もあるよずいう堎合はコメントを頂ければ幞いです。

関連蚘事

CMakeの䜿い方その
CMakeの䜿い方その

参考資料

本蚘事を曞くにあたりCMakeの公匏ドキュメントを参照したした。
その他にはいろいろなYoutubeの動画を参考にしおいたす。
たた私自身は賌入しおいたせんが、Professional CMake: A Practical Guideずいう本はCMakeのco-maintainerの䞀人が曞いた本だそうで、より高床な内容を勉匷したい堎合はこちらがよさそうです。

ステップ実行ファむルを䜜成

コマンドラむンからビルド

たずg++コンパむラを䜿ったビルドの基本を確認したす。こちらがコンパクトにたずめられおいおわかりやすいかなず思いたす。詳现はgcc.gnu.orgのoutput optionsやoption summaryを読んで勉匷したしょう。

たず䞋蚘のような簡単な゜ヌスコヌドがあるずしたす。

.
├── main.cpp
├── hello.hpp
├── hello.cpp
└── hoge.py
main.cpp
#include "hello.hpp"

int main() {
    hello();
}
hello.hpp
#ifndef HELLO_H
#define HELLO_H

void hello();

#endif
hello.cpp
#include <iostream>
#include "hello.hpp"

void hello() {
    std::cout << "Hello!" << std::endl;
}

たずこれを普通にコマンドを打ち蟌んでビルドしたす。

$ g++ -c main.cpp hello.cpp   # ゜ヌスファむルをコンパむルしおオブゞェクトファむルmain.o, hello.oを生成
$ g++ -o a.out main.o hello.o # オブゞェクトファむルをリンクしおa.outずいう実行ファむルを生成

するずディレクトリの䞭は次のようになっおいるはずです。

.
├── a.out
├── main.cpp
├── main.o
├── hello.hpp
├── hello.cpp
└── hello.o

このa.outを実行すればHello!ず衚瀺されたす。

$ ./a.out
Hello!

あるいは次のようにするず、オブゞェクトファむルは生成されず、盎接実行ファむルが䜜られたす。

$ g++ main.cpp hello.cpp

CMakeを䜿っおみる

では先皋の最も簡単な䟋をCMakeを䜿っおビルドしおみたしょう。CMakeを䜿ったビルドの蚭定はCMakeLists.txtずいうテキストファむルに蚘述したす。ディレクトリの䞭身は次のようになりたす。

.
├── CMakeLists.txt
├── main.cpp
├── hello.hpp
└── hello.cpp

CMakeLists.txtの内容は次のようになりたす。

CMakeLists.txt
# CMakeのバヌゞョンを蚭定
cmake_minimum_required(VERSION 3.13)
# プロゞェクト名ず䜿甚する蚀語を蚭定
project(test_cmake CXX)
# a.outずいう実行ファむルをmain.cppずhello.cppから䜜成
add_executable(a.out main.cpp hello.cpp)

CMakeを䜿っおビルドするずきは、次のように必ず゜ヌスディレクトリずは別にビルド専甚のディレクトリを䜜成し、その䞭でビルドしたす。

$ cmake -S . -B build
$ cmake --build build

最終的なディレクトリ構成は以䞋のようになっおいるはずです。

.
├── CMakeLists.txt
├── main.cpp
├── hello.hpp
├── hello.cpp
└── build/
    ├── a.out
    └── ...

このようなCMakeLists.txtの蚭定の堎合、実行ファむルが生成され、途䞭のオブゞェクトファむルはその他色々のディレクトリの䞭に生成されたす。

さお、ここで最終的に埗られるディレクトリの䞭身を比べおみたしょう。普通にコマンドラむンからビルドした堎合、゜ヌスコヌドずオブゞェクトファむルおよび実行ファむルが混圚し、元の状態に戻すのが面倒です。ずころがCMakeを䜿った堎合、ビルド結果は党おbuildディレクトリ内に保存されおいるため、buildディレクトリを削陀するだけで容易に元の状態に戻すこずができたす。これはout-of-sourceビルドず呌ばれおいたす。こちらの蚘事CMake : out-of-sourceビルドで幞せになるで詳しく解説されおいたす。

ステップ静的・共有ラむブラリを䜜成

コマンドラむンからビルド

さお、ラむブラリを䜜っおいる堎合は、静的・共有ラむブラリ*.a, *.soを䜜成しおおき、必芁に応じお実行ファむルにリンクするこずになりたす。たずこの䜜業をコマンドを打ち蟌んでやっおみたしょう。

ステップの䟋にgood_morning.hpp、good_morning.cppずいうファむルが加わったず考えたす。

.
├── main.cpp
├── hello.hpp
├── hello.cpp
├── good_morning.hpp
└── good_morning.cpp
good_morning.hpp
#ifndef GOOD_MORNING_H
#define GOOD_MORNING_H

void good_morning();

#endif
good_morning.cpp
#include <iostream>
#include "good_morning.hpp"

void good_morning() {
    std::cout << "Good morning!" << std::endl;
}

main.cppを次のように修正したす。

main
#include "hello.hpp"
#include "good_morning.hpp"

int main () {
    hello();
    good_morning();
}

静的ラむブラリを䜜成

hello.cppずgood_morning.cppをコンパむルしおオブゞェクトファむルを生成したあず、静的ラむブラリlibgreetings.aを䜜成し、main.cppをコンパむルする際にリンクしお実行ファむルを䜜成したす。

$ g++ -c hello.cpp good_morning.cpp             # オブゞェクトファむル(hello.o, good_morning.o)の䜜成
$ ar rvs libgreetings.a hello.o good_morning.o  # 静的ラむブラリ(libgreetings.a)の䜜成
$ g++ main.cpp libgreetings.a                   # main.cppをコンパむルしおlibgreetings.aずリンクし実行ファむルa.outを䜜成

最終的なディレクトリ構成は以䞋のようになりたす。

.
├── a.out
├── main.cpp
├── hello.hpp
├── hello.cpp
├── hello.o
├── good_morning.hpp
├── good_morning.cpp
├── good_morning.o
└── libgreetings.a

共有ラむブラリを䜜成

hello.cppずgood_morning.cppをコンパむルしおオブゞェクトファむルを生成したあず、共有ラむブラリlibgreetings.soを䜜成し、main.cppをコンパむルする際にリンクしお実行ファむルを䜜成したす。

$ g++ -fPIC -c hello.cpp good_morning.cpp                 # オブゞェクトファむル(hello.o, good_morning.o)の䜜成
$ g++ -shared hello.o good_morning.o -o libgreetings.so   # 共有ラむブラリlibgreetings.soの䜜成
$ g++ main.cpp -L. -lgreetings -Xlinker -rpath -Xlinker . # main.cppをコンパむルしおlibgreetings.soずリンクし実行ファむルa.outを䜜成

最終的なディレクトリ構成は以䞋のようになりたす。

.
├── a.out
├── main.cpp
├── hello.hpp
├── hello.cpp
├── hello.o
├── good_morning.hpp
├── good_morning.cpp
├── good_morning.o
└── libgreetings.so

どちらのa.outを実行しおも

$ ./a.out
Hello!
Good morning!

ず衚瀺されるはずです。

CMakeを䜿っおみる

さお、同じこずをCMakeを䜿っおやっおみたしょう。ステップず同じく、CMakeLists.txtを加えたす。

.
├── CMakeLists.txt
├── main.cpp
├── hello.hpp
├── hello.cpp
├── good_morning.hpp
└── good_morning.cpp

CMakeLists.txtの内容は次のようになりたす。

静的ラむブラリを䜜成

CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
project(test_cmake CXX)
# hello.cppずgood_morning.cppをコンパむルしお静的ラむブラリlibgreetings.aを䜜成
add_library(greetings STATIC hello.cpp good_morning.cpp)
# a.outずいう実行ファむルをmain.cppから䜜成
add_executable(a.out main.cpp)
# a.outを䜜成する際にlibgreetings.aをリンク
target_link_libraries(a.out greetings)

ステップず同様にbuildディレクトリを䜜りビルドするず、ディレクトリの構成は以䞋のようになるはずです。

.
├── CMakeLists.txt
├── main.cpp
├── hello.hpp
├── hello.cpp
├── good_morning.hpp
├── good_morning.cpp
└── build/
    ├── a.out
    ├── libgreetings.a
    └── ...

共有ラむブラリを䜜成

CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
project(test_cmake CXX)
# hello.cppずgood_morning.cppをコンパむルしお共有ラむブラリlibgreetings.soを䜜成
add_library(greetings SHARED hello.cpp good_morning.cpp)
add_executable(a.out main.cpp)
# a.outを䜜成する際にlibgreetings.soをリンク
target_link_libraries(a.out greetings)

buildディレクトリを䜜りビルドするず、ディレクトリの構成は以䞋のようになるはずです。

.
├── CMakeLists.txt
├── main.cpp
├── hello.hpp
├── hello.cpp
├── good_morning.hpp
├── good_morning.cpp
└── build/
    ├── a.out
    ├── libgreetings.so
    └── ...

CMakeを䜿っおビルドする堎合、静的・共有ラむブラリを䜜成するずきの違いは たったの䞀語STATIC/SHAREDです 。

add_library(greetings [SHARED|STATIC] hello.cpp good_morning.cpp)

たたコマンドラむンでビルドする堎合は、特に共有ラむブラリをリンクするずきにリンクオプションが色々面倒なのですが、 CMakeを䜿うず党郚自動でやっおくれたす 。玠晎らしい。

静的・共有ラむブラリの指定に぀いお

前節ではSTATIC/SHAREDをadd_libraryに盎接指定したしたが無指定にするずどうなるのでしょうか。

add_library(greetings hello.cpp good_morning.cpp)

この堎合、add_libraryで指定されたタヌゲットここではgreetingsが共有たたは静的のどちらでコンパむルされるかは、グロヌバルオプションBUILD_SHARED_LIBSのON/OFFデフォルトはOFFで決たりたす。CMakeを䜿っおビルドする際に次のように指定するこずが可胜です。

$ cmake -S . -B build -DBUILD_SHARED_LIBS=ON
$ cmake --build build

䞀般的には、各ラむブラリごずに静的・共有ラむブラリのどちらでビルドするか遞択できるオプションを䜜成しおおくべきです。

# GREETINGS_BUILD_SHARED_LIBSずいうオプションを䜜成。デフォルトをOFFに蚭定。
option(GREETINGS_BUILD_SHARED_LIBS "build greetings as a shared library" OFF)

if (GREETINGS_BUILD_SHARED_LIBS)
  add_library(greetings SHARED hello.cpp good_morning.cpp)
else()
  add_library(greetings STATIC hello.cpp good_morning.cpp)
endif()

こうするず先ほど同様にコマンドラむンから指定するこずが可胜です。

$ cmake -S . -B build -DGREETINGS_BUILD_SHARED_LIBS=ON
$ cmake --build build

ステップサブディレクトリに゜ヌスが分散しおいる堎合

さお、実際のラむブラリにおいおは、ステップ・のようにラむブラリのルヌトディレクトリ盎䞋に゜ヌスコヌドを䞀括でおいおいるなどずいうこずはありたせん。先ほどのような簡単なものなら別に構わないのですが、もっず倧きなラむブラリの堎合は゜ヌスコヌドをその圹割ごずに別々のディレクトリに眮いおおかないずわけがわからないこずになりたす。

ステップの䟋のディレクトリ構成を䞋蚘のように芋なおしたものを䜿っお解説したす。

.
├── include/
│   ├── hello.hpp
│   └── good_morning.hpp
├── src/
│   ├── hello.cpp
│   └── good_morning.cpp
└── test/
    └── main.cpp

コマンドラむンからコンパむル

さお、これをコマンドラむンからビルドしおみたしょう。ルヌトディレクトリ盎䞋にいるものずし、共有ラむブラリを䜜成しお実行ファむルにリンクするやり方をずりたす。

$ cd src
$ g++ -fPIC -c hello.cpp good_morning.cpp -I../include
$ g++ -shared *.o -o libgreetings.so
$ cd ../test
$ g++ main.cpp -I../include -L../src -lgreetings -Xlinker -rpath -Xlinker ../src

ステップず異なり、今床は-Lオプションに加えお-Iオプションでむンクルヌドファむルを探すディレクトリを指定する必芁がありたす。ステップで-Iオプションが必芁なかったのは、カレントディレクトリはデフォルトでむンクルヌドファむルを探す堎所ずしお認識されるからです。

ディレクトリ構成は以䞋のようになりたす。

.
├── include/
│   ├── hello.hpp
│   └── good_morning.hpp
├── src/
│   ├── hello.cpp
│   ├── hello.o
│   ├── good_morning.cpp
│   ├── good_morning.o
│   └── libgreetings.so
└── test/
    ├── a.out
    └── main.cpp

CMakeを䜿っおみる

さお、サブディレクトリが存圚する堎合、各ディレクトリごずにCMakeLists.txtを䜜りたす。

.
├── CMakeLists.txt
├── include/
│   ├── hello.hpp
│   └── good_morning.hpp
├── src/
│   ├── CMakeLists.txt
│   ├── hello.cpp
│   └── good_morning.cpp
└── test/
    ├── CMakeLists.txt
    └── main.cpp

ルヌトディレクトリにあるCMakeLists.txtではサブディレクトリの登録をしたす。

/CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
project(test_cmake CXX)
# サブディレクトリを登録
add_subdirectory(src)
add_subdirectory(test)

/src/CMakeLists.txtおよび/test/CMakeLists.txtではそれぞれのディレクトリにあるファむルのコンパむル方法を指定しおあげたす。

/src/CMakeLists.txt
add_library(greetings
  SHARED
    hello.cpp
    good_morning.cpp
  )
# greetingラむブラリのむンクルヌドディレクトリを教えおあげる
# PROJECT_SOURCE_DIRはこのプロゞェクトのルヌトディレクトリの絶察パス
target_include_directories(greetings
  PUBLIC ${PROJECT_SOURCE_DIR}/include
  )
/test/CMakeLists.txt
add_executable(a.out main.cpp)
# a.outをコンパむルする際にgreetingsをリンクする
target_link_libraries(a.out greetings)

add_libraryおよびadd_executableの最初の倉数を「タヌゲット」ず呌びたす。
キヌワヌドPRIVATE/PUBLIC/INTERFACEの意味は、タヌゲットgreetingsのみに必芁=PRIVATEなのか、タヌゲットgreetingsおよびそれに䟝存するタヌゲットa.outにも必芁=PUBLICなのか、あるいはタヌゲットには必芁ないがそれに䟝存するタヌゲットには必芁=INTERFACEなのかを指したす。

キヌワヌド タヌゲットに必芁 そのタヌゲットに䟝存するタヌゲットに必芁
PRIVATE :o: :x:
PUBLIC :o: :o:
INTERFACE :x: :o:

この堎合、むンクルヌドディレクトリ${PROJECT_SOURCE_DIR}/includeはgreetingsのコンパむルだけでなく、a.outのコンパむルにも必芁なため、PUBLICを指定したす。
INTERFACEはヘッダヌのみのラむブラリに察しお䜿いたす。

さおこれで準備は完了です。今たでず同様にbuildディレクトリを䜜成しおcmakeを実行しおビルドするこずができたす。

1452
1221
11

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1452
1221

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?