CMake Tutorial & Example

 Tutorial

CMakeLists用於告訴CMake咱們要對這個目錄下的文件作什麼事情

cmake 的特色主要有:html

1,開放源代碼,使用類 BSD 許可發佈。http://cmake.org/HTML/Copyright.html
2,跨平臺,並可生成 native 編譯配置文件,在 Linux/Unix 平臺,生成 makefile,在蘋果平臺,能夠生成 xcode,在 Windows 平臺,能夠生成 MSVC 的工程文件。
3,可以管理大型項目,KDE4 就是最好的證實。 4,簡化編譯構建過程和編譯過程。Cmake 的工具鏈很是簡單:cmake+make。 5,高效慮,按照 KDE 官方說法,CMake 構建 KDE4 的 kdelibs 要比使用 autotools 來構建 KDE3.5.6 的 kdelibs 快 40%,主要是由於 Cmake 在工具鏈中沒有 libtool。
6,可擴展,能夠爲 cmake 編寫特定功能的模塊,擴充 cmake 功能

T1 cmake中一些預約義變量

經常使用

PROJECT_SOURCE_DIR   工程的根目錄
PROJECT_BINARY_DIR   運行cmake命令的目錄,一般是${PROJECT_SOURCE_DIR}/build
CMAKE_INCLUDE_PATH         環境變量,非cmake變量 CMAKE_LIBRARY_PATH         環境變量 CMAKE_CURRENT_SOURCE_DIR     當前處理的CMakeLists.txt所在的路徑 CMAKE_CURRENT_BINARY_DIR     target編譯目錄
使用ADD_SURDIRECTORY(src bin)  能夠更改此變量的值 SET(EXECUTABLE_OUTPUT_PATH
<新路徑>)並不會對此變量有影響,只是改變了最終目標文件的存儲路徑
CMAKE_CURRENT_LIST_FILE   輸出調用這個變量的CMakeLists.txt的完整路徑 CMAKE_CURRENT_LIST_LINE   輸出這個變量所在的行
CMAKE_MODULE_PATH   定義本身的cmake模塊所在的路徑 SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}
/cmake),而後能夠用INCLUDE命令來調用本身的模塊
EXECUTABLE_OUTPUT_PATH 從新定義目標二進制可執行文件的存放位置
LIBRARY_OUTPUT_PATH 從新定義目標連接庫文件的存放位置
PROJECT_NAME 返回經過PROJECT指令定義的項目名稱
CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS 用來控制IF ELSE語句的書寫方式

 

系統信息

CMAKE_MAJOR_VERSION   cmake主版本號,如2.8.6中的2
CMAKE_MINOR_VERSION   cmake次版本號,如2.8.6中的8
CMAKE_PATCH_VERSION   cmake補丁等級,如2.8.6中的6
CMAKE_SYSTEM      系統名稱,例如Linux
-2.6.22 CAMKE_SYSTEM_NAME 不包含版本的系統名,如Linux CMAKE_SYSTEM_VERSION   系統版本,如2.6.22 CMAKE_SYSTEM_PROCESSOR 處理器名稱,如i686
UNIX 在全部的類UNIX平臺爲TRUE,包括OS X和cygwin WIN32 在全部的win32平臺爲TRUE,包括cygwin

 

開關選項

BUILD_SHARED_LIBS 控制默認的庫編譯方式。若是未進行設置,使用ADD_LIBRARY時又沒有指定庫類型,默認編譯生成的庫都是靜態庫 (可在t3中稍加修改進行驗證)
CMAKE_C_FLAGS     設置C編譯選項
CMAKE_CXX_FLAGS   設置C++編譯選項
CMAKE_BUILD_TYPE 設置編譯模式 None:編譯器預設值 Debug:產生除錯資訊 Release:進行執行速度最佳化

 

T2 cmake經常使用命令

基本語法規則:

cmake變量使用${}方式取值,可是在IF控制語句中是直接使用變量名react

環境變量使用$ENV{}方式取值,使用SET(ENV{VAR} VALUE)賦值正則表達式

指令(參數1 參數2…)   參數使用括弧括起,參數之間使用空格或分號分開。express

 

 

部分經常使用命令列表:

PROJECT
PROJECT(projectname [CXX] [C] [Java])
指定工程名稱,並可指定工程支持的語言。支持語言列表可忽略,默認支持全部語言

SET
SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
定義變量(能夠定義多個VALUE,如SET(SRC_LIST main.c util.c reactor.c))

MESSAGE
MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR] 「message to display」 …)
向終端輸出用戶定義的信息或變量的值
SEND_ERROR, 產生錯誤,生成過程被跳過
STATUS, 輸出前綴爲—的信息
FATAL_ERROR, 當即終止全部cmake過程

ADD_EXECUTABLE
ADD_EXECUTABLE(bin_file_name ${SRC_LIST})
生成可執行文件

ADD_LIBRARY
ADD_LIBRARY(libname [SHARED | STATIC | MODULE] [EXCLUDE_FROM_ALL] SRC_LIST)
生成動態庫或靜態庫
SHARED 動態庫 STATIC 靜態庫
MODULE 在使用dyld的系統有效,若不支持dyld,等同於SHARED
EXCLUDE_FROM_ALL 表示該庫不會被默認構建

SET_TARGET_PROPERTIES
設置輸出的名稱,設置動態庫的版本和API版本

CMAKE_MINIMUM_REQUIRED
CMAKE_MINIMUM_REQUIRED(VERSION version_number [FATAL_ERROR])
聲明CMake的版本要求

ADD_SUBDIRECTORY
ADD_SUBDIRECTORY(src_dir [binary_dir] [EXCLUDE_FROM_ALL])
向當前工程添加存放源文件的子目錄,並能夠指定中間二進制和目標二進制的存放位置
EXCLUDE_FROM_ALL含義:將這個目錄從編譯過程當中排除

SUBDIRS
deprecated,再也不推薦使用
(hello sample)至關於分別寫ADD_SUBDIRECTORY(hello),ADD_SUBDIRECTORY(sample)

INCLUDE_DIRECTORIES
INCLUDE_DIRECTORIES([AFTER | BEFORE] [SYSTEM] dir1 dir2 … )
讓CMake找到個人頭文件, 向工程添加多個特定的頭文件搜索路徑,路徑之間用空格分隔,
若是路徑包含空格,可使用雙引號將它括起來,默認的行爲爲追加到當前頭文件搜索路徑的後面。有以下兩種方式能夠控制搜索路徑添加的位置: CMAKE_INCLUDE_DIRECTORIES_BEFORE,經過SET這個cmake變量爲on,能夠將添加的頭文件搜索路徑放在已有路徑的前面 經過AFTER或BEFORE參數,也能夠控制是追加仍是置前 LINK_DIRECTORIES LINK_DIRECTORIES(dir1 dir2 …) 添加非標準的共享庫搜索路徑 TARGET_LINK_LIBRARIES TARGET_LINK_LIBRARIES(target lib1 lib2 …) 爲target添加須要連接的共享庫 ADD_DEFINITIONS 向C
/C++編譯器添加-D定義 ADD_DEFINITIONS(-DENABLE_DEBUG -DABC),參數之間用空格分隔 ADD_DEPENDENCIES ADD_DEPENDENCIES(target-name depend-target1 depend-target2 …) 定義target依賴的其餘target,確保target在構建以前,其依賴的target已經構建完畢 AUX_SOURCE_DIRECTORY AUX_SOURCE_DIRECTORY(dir VAR) 發現一個目錄下全部的源代碼文件並將列表存儲在一個變量中 把當前目錄下的全部源碼文件名賦給變量DIR_HELLO_SRCS EXEC_PROGRAM EXEC_PROGRAM(Executable [dir where to run] [ARGS <args>][OUTPUT_VARIABLE <var>] [RETURN_VALUE <value>]) 用於在指定目錄運行某個程序(默認爲當前CMakeLists.txt所在目錄),經過ARGS添加參數,經過OUTPUT_VARIABLE和RETURN_VALUE獲取輸出和返回值,以下示例
# 在src中運行ls命令,在src/CMakeLists.txt添加
EXEC_PROGRAM(ls ARGS "*.c" OUTPUT_VARIABLE LS_OUTPUT RETURN_VALUE LS_RVALUE)
IF (not LS_RVALUE)
MESSAGE(STATUS "ls result: " ${LS_OUTPUT}) # 縮進僅爲美觀,語法無要求
ENDIF(not LS_RVALUE)
 
 
INCLUDE
INCLUDE(file [OPTIONAL]) 用來載入CMakeLists.txt文件
INCLUDE(module [OPTIONAL])用來載入預約義的cmake模塊
OPTIONAL參數的左右是文件不存在也不會產生錯誤
能夠載入一個文件,也能夠載入預約義模塊(模塊會在CMAKE_MODULE_PATH指定的路徑進行搜索)
載入的內容將在處理到INCLUDE語句時直接執行

FIND_
    FIND_FILE(<VAR> name path1 path2 …)
    VAR變量表明找到的文件全路徑,包含文件名
    
    FIND_LIBRARY(<VAR> name path1 path2 …)
    VAR變量表明找到的庫全路徑,包含庫文件名
        FIND_LIBRARY(libX X11 /usr/lib)
        IF (NOT libx)
            MESSAGE(FATAL_ERROR "libX not found")
        ENDIF(NOT libX)
 FIND_PATH(<VAR> name path1 path2 …)
    VAR變量表明包含這個文件的路徑

    FIND_PROGRAM(<VAR> name path1 path2 …)
    VAR變量表明包含這個程序的全路徑

    FIND_PACKAGE(<name> [major.minor] [QUIET] [NO_MODULE] [[REQUIRED | COMPONENTS] [componets …]])
    用來調用預約義在CMAKE_MODULE_PATH下的Find<name>.cmake模塊,
你也能夠本身定義Find<name>模塊,經過SET(CMAKE_MODULE_PATH dir)將其放入工程的某個目錄供工程使用

 

 

 

 

IF

語法:windows

 

IF (expression)
    COMMAND1(ARGS ...)
    COMMAND2(ARGS ...)
    ...
ELSE (expression)
    COMMAND1(ARGS ...)
    COMMAND2(ARGS ...)
    ...
ENDIF (expression) # 必定要有ENDIF與IF對應

 

 

IF (expression), expression不爲:空,0,N,NO,OFF,FALSE,NOTFOUND或<var>_NOTFOUND,爲真
IF (not exp), 與上面相反
IF (var1 AND var2)
IF (var1 OR var2)
IF (COMMAND cmd) 若是cmd確實是命令並可調用,爲真
IF (EXISTS dir) IF (EXISTS file) 若是目錄或文件存在,爲真
IF (file1 IS_NEWER_THAN file2),當file1比file2新,或file1/file2中有一個不存在時爲真,文件名需使用全路徑
IF (IS_DIRECTORY dir) 當dir是目錄時,爲真
IF (DEFINED var) 若是變量被定義,爲真
IF (var MATCHES regex) 此處var能夠用var名,也能夠用${var}
IF (string MATCHES regex)
xcode

 

當給定的變量或者字符串可以匹配正則表達式regex時爲真。好比:
IF ("hello" MATCHES "ell")
    MESSAGE("true")
ENDIF ("hello" MATCHES "ell")

 

 

 

數字比較表達式
IF (variable LESS number)
IF (string LESS number)
IF (variable GREATER number)
IF (string GREATER number)
IF (variable EQUAL number)
IF (string EQUAL number)工具

 

按照字母表順序進行比較
IF (variable STRLESS string)
IF (string STRLESS string)
IF (variable STRGREATER string)
IF (string STRGREATER string)
IF (variable STREQUAL string)
IF (string STREQUAL string)oop

一個小例子,用來判斷平臺差別:
IF (WIN32)
    MESSAGE(STATUS 「This is windows.」)
ELSE (WIN32)
    MESSAGE(STATUS 「This is not windows」)
ENDIF (WIN32)
上述代碼用來控制在不一樣的平臺進行不一樣的控制,可是,閱讀起來卻並非那麼舒服,ELSE(WIN32)之類的語句很容易引發歧義。
能夠SET(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS ON)
這時候就能夠寫成:
IF (WIN32)
ELSE ()
ENDIF ()
配合ELSEIF使用,可能的寫法是這樣:
IF (WIN32)
    #do something related to WIN32
ELSEIF (UNIX)
    #do something related to UNIX
ELSEIF(APPLE)
    #do something related to APPLE
ENDIF (WIN32)

 

WHILE

其真假判斷條件能夠參考IF指令優化

WHILE(condition)
    COMMAND1(ARGS ...)
    COMMAND2(ARGS ...)
    ...
ENDWHILE(condition)

 

FOREACH

FOREACH指令的使用方法有三種形式:ui

1.列表
語法:
FOREACH(loop_var arg1 arg2 ...)
     COMMAND1(ARGS ...)
     COMMAND2(ARGS ...)
 ...
ENDFOREACH(loop_var)
示例:
AUX_SOURCE_DIRECTORY(. SRC_LIST)
FOREACH(F ${SRC_LIST})
     MESSAGE(${F})
ENDFOREACH(F)
2.範圍 語法: FOREACH(loop_var RANGE total) COMMAND1(ARGS ...) COMMAND2(ARGS ...) ... ENDFOREACH(loop_var) 示例: 從0到total以1爲步進 FOREACH(VAR RANGE
10) MESSAGE(${VAR}) ENDFOREACH(VAR) 輸出: 012345678910

3.範圍和步進 語法: FOREACH(loop_var RANGE start stop [step]) COMMAND1(ARGS ...) COMMAND2(ARGS ...) ... ENDFOREACH(loop_var) 從start開始到stop結束,以step爲步進, 注意:直到遇到ENDFOREACH指令,整個語句塊纔會獲得真正的執行。 FOREACH(A RANGE 5 15 3) MESSAGE(${A}) ENDFOREACH(A) 輸出: 581114

 

 

cmake中如何生成動態庫和靜態庫

參考ADD_LIBRARY和SET_TARGET_PROPERTIES用法

cmake中如何使用動態庫和靜態庫(查找庫的路徑)

參考INCLUDE_DIRECTORIES, LINK_DIRECTORIES, TARGET_LINK_LIBRARIES用法
t4示例使用動態庫或靜態庫
t5示例如何使用cmake預約義的cmake模塊(以FindCURL.cmake爲例演示)
t6示例如何使用自定義的cmake模塊(編寫了自定義的FindHELLO.cmake)
注意讀t5和t6的CMakeLists.txt和FindHELLO.cmake中的註釋部分

cmake中如何指定生成文件的輸出路徑

如上ADD_SUBDIRECTORY的時候指定目標二進制文件輸出路徑(推薦使用下面這種)
使用SET命令從新定義EXECUTABLE_OUTPUT_PATH和LIBRARY_OUTPUT_PATH變量來指定最終的二進制文件的位置
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
上面的兩條命令一般緊跟ADD_EXECUTABLE和ADD_LIBRARY,與其寫在同一個CMakeLists.txt便可
cmake中如何增長編譯選項

使用變量CMAKE_C_FLAGS添加C編譯選項
使用變量CMAKE_CXX_FLAGS添加C++編譯選項
使用ADD_DEFINITION添加

cmake中如何增長頭文件路徑

參考INCLUDE_DIRECTORIES命令用法

cmake中如何在屏幕上打印信息

參考MESSAGE用法

cmake中如何給變量賦值

參考SET和AUX_SOURCE_DIRECTORY用法

建議:在Project根目錄先創建build,而後在build文件夾內運行cmake ..,這樣就不會污染源代碼, 若是不想要這些自動生成的文件了,只要簡單的刪除build文件夾就能夠

 

 

 

e.g 1.0 

 

# 聲明要求的 cmake 最低版本
cmake_minimum_required( VERSION 2.8 )

# 聲明一個 cmake 工程
project( HelloSLAM )

# 設置編譯模式 	None:編譯器預設值  	Debug:產生除錯資訊		Release:進行執行速度最佳化
set( CMAKE_BUILD_TYPE "Debug" )

# 添加一個庫:hello庫
#靜態庫.a  :每次被調用都生成一個副本      在build裏生成的是 libhello_static.a
add_library( hello_static libHelloSLAM.cpp )
#共享庫.so :不論被調用幾回都只有一個副本  在build裏生成的是 libhello_shared.so
add_library( hello_shared SHARED libHelloSLAM.cpp )
 # 添加一個可執行程序useHello # 語法:add_executable( 程序名 源代碼文件 ) add_executable( HelloSLAM useHello.cpp ) # 將庫文件連接到可執行程序HelloSLAM上 target_link_libraries( HelloSLAM hello_shared )

 

e.g 1.1

1.添加Eigen頭文件

Eigen經過apt-get安裝以後,咱們要怎麼使用這個依賴庫呢?

Eigen有一點很奇怪,Eigen庫只有頭文件,因此咱們在CMakeLists.txt中是不須要寫target_link_libraries,

由於咱們是經過apt安裝的,因此很容易的知道Eigen庫的位置就是在/usr/include/eigen3

#添加頭文件
include_directories("/usr/include/eigen3")

由於咱們已經知道Eigen具體的位置,就不用使用find_package來尋找了,雖然有些粗暴,可是簡單有效。

 

2.添加Pangolin依賴 

Pangolin的安裝也很簡單,功能主要就是作三維的可視化顯示,主要依賴庫是OpenGL,經過apt也很好安裝。

find_package(Pangolin)

include_directories(${Pangolin_INCLUDE_DIRS})

add_executable(project_name filename.cpp)

target_link_libraries(project_name ${Pangolin_LIBRARIES})

 

3.添加Sophus依賴

Sophus其實是Eigen庫的擴展模塊,Eigen中雖然有幾何模塊,可是沒有提供李代數的支持,因此Sophus算是一個比較好的李代數庫,安裝參考以前博文。

find_package(Sophus REQUIRED)
include_directories(${Sophus_INCLUDE_DIRS})
add_executable(project_name project_name.cpp)
target_link_libraries(project_name ${Sophus_LIBRARIES})

 

 

4.添加OpenCV依賴 

OpenCV常常會出現版本不兼容的問題,LZ同時安裝了OpenCV2和OpenCV3兩個版本,因此在CMakeLists.txt要指定OpenCV的版本。

#指定OpenCV的版本是3.1
find_package(OpenCV 3.1 REQUIRED)

include_directories(${OpenCV_INCLUDE_DIRS})

add_executable(project_name filename.cpp)

target_link_libraries(project_name ${OpenCV_LIBS})

 

添加OpenCV要注意一個問題,大小寫!很重要!大小寫!

 

5.添加PCL依賴

這個點雲庫在SLAM應用中仍是蠻重要的,一般狀況下好像也都有安裝,安裝很簡單,這裏仍是講一下CMakeLists.txt中怎麼寫:

find_package(PCL REQUIRED COMPONENT common io)

include_directories(${PCL_INCLUDE_DIRS})

add_definitions(${PCL_DEFINITIONS})

target_link_libraries(project_name ${PCL_LIBRARIES})

 

 

6.添加Ceres依賴

Ceres是Google出品的一個優化庫,安裝編譯都在LZ以前寫過一個SLAM安裝大全裏都有。由於Ceres不是經常使用的庫,因此須要添加一個cmake_modules。

#這行代碼就是添加查找Ceres的一個文件
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake_modules)

find_package(Ceres REQUIRED)

include_directories(${CERES_INCLUDE_DIRS})

target_link_libraries(project_name ${CERES_LIBRARIES})

 

 

7添加G2O的依賴

 

list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake_modules)

find_package(G2O REQUIRED)

include_directories(${G20_INCLUDE_DIRS})

target_link_libraries(project_name g2o_core g2o_stuff)
相關文章
相關標籤/搜索