Cmake小結

一、CMake編譯原理

CMake是一種跨平臺編譯工具,比make更爲高級,使用起來要方便得多。CMake主要是編寫CMakeLists.txt文件,然後用cmake命令將CMakeLists.txt文件轉化爲make所需要的makefile文件,最後用make命令編譯源碼生成可執行程序或共享庫(so(shared object))。因此CMake的編譯基本就兩個步驟:

cmake
make

cmake 指向CMakeLists.txt所在的目錄,例如cmake … 表示CMakeLists.txt在當前目錄的上一級目錄。cmake後會生成很多編譯的中間文件以及makefile文件,所以一般建議新建一個新的目錄,專門用來編譯。

mkdir build
cd build
cmake ..
make

make根據生成makefile文件,編譯程序。

二、Cmake基本命令

2.1 常用命令

  • (1) CMake 最低版本號要求
cmake_minimum_required (VERSION 3.14)
  • (2) 項目信息
project (myProject)
  • (3) aux_source_directory
    作用是發現一個目錄下所有的源代碼文件並將列表存儲在一個變量中,這個指令臨時被用來自動構建源文件列表。因爲目前 cmake 還不能自動發現新添加的源文件。
AUX_SOURCE_DIRECTORY(dir VARIABLE)
//比如:
AUX_SOURCE_DIRECTORY(. SRC_LIST)//查找當前目錄下的所有源文件並將名稱保存到 DIR_SRCS 變量
ADD_EXECUTABLE(main ${SRC_LIST})
  • (4) add_executable 工程生成目標文件
add_executable(Demo ${DIR_SRCS})
  • (5) target_link_libraries 將若干庫鏈接到目標庫文件
target_link_libraries(myProject comm) //連接libhello.so庫,默認優先鏈接動態庫
target_link_libraries(myProject libcomm.a) # 顯示指定鏈接靜態庫
target_link_libraries(myProject libcomm.so) # 顯示指定鏈接動態庫
target_link_libraries(<name> lib1 lib2 lib3)
//鏈接的順序應當符合gcc鏈接順序規則,被鏈接的庫放在依賴它的庫的後面,即如果上面的命令中,lib1依賴於lib2, lib2又依賴於lib3,則在上面命令中必須嚴格按照lib1 lib2 lib3的順序排列,否則會報錯
//也可以自定義鏈接選項, 比如針對lib1使用-WL選項,target_link_libraries(<name> lib1 -WL, lib2 lib3)
  • (6) add_library 生成靜態鏈接庫和動態庫
add_library(libname [SHARED|STATIC] source1 source2 ... sourceN)
add_library (hello STATIC ${LIBHELLO_SRC}) //生成靜態鏈接庫
add_library (hello SHARED ${LIBHELLO_SRC}) //生成動態鏈接庫
  • (7) SET_TARGET_PROPERTIES 設置輸出別名,所以,希望 “hello_static” 在輸出時,不是"hello_static",而是以"hello"的名字顯示,故設置如下
SET_TARGET_PROPERTIES (hello_static PROPERTIES OUTPUT_NAME "hello")
GET_TARGET_PROPERTY (OUTPUT_VALUE hello_static OUTPUT_NAME) //獲取值
  • (8) include_directories 設置頭文件位置,相當於g++ -I,可以用相對或者絕對路徑,也可以用自定義的變量值
include_directories(../../../thirdparty/comm/include)
  • (9) link_directories 添加需要鏈接的庫文件目錄,它相當於g++命令的-L選項的作用
link_directories(directory1 directory2 ...)
//例:link_directories("/home/server/third/lib")
  • (10) link_libraries 添加需要鏈接的庫文件路徑
link_libraries(library1 <debug | optimized> library2 ...)

// 直接是全路徑
link_libraries(/home/server/third/lib/libcommon.a”)
// 下面的例子,只有庫名,cmake會自動去所包含的目錄搜索
link_libraries(iconv)
// 傳入變量
link_libraries(${RUNTIME_LIB})
// 也可以鏈接多個
link_libraries("/opt/MATLAB/R2012a/bin/glnxa64/libeng.so" "/opt/MATLAB/R2012a/bin/glnxa64/libmx.so")
  • (11) SET_TARGET_PROPERTIES 用來設置輸出的名稱
SET_TARGET_PROPERTIES (target1 target2 ...PROPERTIES prop1 value1 prop2 value2 ...)
SET_TARGET_PROPERTIES (hello PROPERTIES VERSION 1.2 SOVERSION 1) 實現動態庫版本號 VERSION指代動態庫版本,SOVERSION指代API版本。
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello") 將libhello_static.a庫名稱輸出爲libhello.a
  • (12) add_library 導入已有的庫
add_library(<name> [STATIC | SHARED | MODULE | UNKNOWN] IMPORTED)

//導入了一個已存在的<name>庫文件,導入庫一般配合set_target_properties使用,這個命令用來指定導入庫的路徑,比如:
add_library(test SHARED IMPORTED)
set_target_properties( test #指定目標庫名稱
PROPERTIES IMPORTED_LOCATION #指明要設置的參數
libs/src/${ANDROID_ABI}/libtest.so #設定導入庫的路徑)
  • (13) set
// 設置可執行文件的輸出路徑(EXCUTABLE_OUTPUT_PATH是全局變量)
set(EXECUTABLE_OUTPUT_PATH [output_path])

// 設置庫文件的輸出路徑(LIBRARY_OUTPUT_PATH是全局變量)
set(LIBRARY_OUTPUT_PATH [output_path])

// 設置C++編譯參數(CMAKE_CXX_FLAGS是全局變量)
set(CMAKE_CXX_FLAGS "-Wall std=c++11")

// 設置源文件集合(SOURCE_FILES是本地變量即自定義變量)
set(SOURCE_FILES main.cpp test.cpp ...)

// 設置編譯類型debug 或者release。 debug 版會生成相關調試信息,可以使用GDB 進行調試;release不會生成調試信息
set(CMAKE_BUILE_TYPE DEBUG)

// 設置編譯器的類型
SET(CMAKE_C_FLAGS_DEBUG “-g -Wall”)

// 設置低版本g++編譯器支持c++11,高版本自動識別
set(CMAKE_CXX_STANDARD 11)

//需要注意的是,在哪裏 ADD_EXECUTABLE 或 ADD_LIBRARY,如果需要改變目標存放路徑,就在哪裏的上面加入上述的定義
  • (14) add_subdirectory
//如果當前目錄下還有子目錄時可以使用add_subdirectory,子目錄中也需要包含有CMakeLists.txt

add_subdirecroty(sub_dir [binary_dir])
// sub_dir指定包含CMakeLists.txt和源碼文件的子目錄位置
//binary_dir是輸出路徑, 一般可以不指定
  • (15) 文件操作命令file
// 將message寫入filename文件中,會覆蓋文件原有內容
file(WRITE filename "message")

// 將message寫入filename文件中,會追加在文件末尾
file(APPEND filename "message")

// 重命名文件
file(RENAME <oldname> <newname>)

// 刪除文件, 等於rm命令
file(REMOVE [file1 ...])

// 創建目錄
file(MAKE_DIRECTORY [dir1 dir2 ...])
file(GLOB_RECURSE SRC_LIST "*.cpp")//這個命令將把該目錄下及所有子文件夾內的所有後綴爲.cpp的文件的路徑,全部放入SRC_LIST這個變量中
file(GLOB_RECURSE HEADERS "*.h")
file(GLOB_RECURSE FORMS "*.ui")
file(GLOB_RECURSE RESOURCES "*.qrc")
  • (16) find_library 查找庫所在目錄,並將查找到路徑放入到變量中
find_library(RUNTIME_LIB_VAR rt /usr/lib /usr/local/lib NO_DEFAULT_PATH)
//cmake會在目錄中查找,如果所有目錄中都沒有,值RUNTIME_LIB_VAR就會被賦爲NO_DEFAULT_PATH
  • (17) add_dependencies
//定義 target 依賴的其他 target,確保在編譯本 target 之前,其他的 target 已經被構建。
add_dependencies(target-name depend-target1 depend-target2 ...)

*(18)message 打印cmake時信息

MESSAGE (STATUS "This is the hello_static OUTPUT_NAME: " ${OUTPUT_VALUE})
  • (19) GET_TARGET_PROPERTY 獲取屬性值
GET_TARGET_PROPERTY (VAR target property) VAR:變量 target:目標 property:屬性
GET_TARGET_PROPERTY (OUTPUT_VALUE hello_static OUTPUT_NAME)
  • (20) INCLUDE 指令
    用來載入 CMakeLists.txt 文件,也用於載入預定義的 cmake 模塊.
INCLUDE(file1 [OPTIONAL])
INCLUDE(module [OPTIONAL])
//OPTIONAL 參數的作用是文件不存在也不會產生錯誤。

可以指定載入一個文件,如果定義的是一個模塊,那麼將在 CMAKE_MODULE_PATH 中搜
索這個模塊並載入。載入的內容將在處理到 INCLUDE 語句是直接執行。

  • (21) FIND_指令
    FIND_系列指令主要包含一下指令:
FIND_FILE(<VAR> name1 path1 path2 ...)//VAR 變量代表找到的文件全路徑,包含文件名
FIND_LIBRARY(<VAR> name1 path1 path2 ...)//VAR 變量表示找到的庫全路徑,包含庫文件名
FIND_PATH(<VAR> name1 path1 path2 ...)//VAR 變量代表包含這個文件的路徑。
FIND_PROGRAM(<VAR> name1 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)將其放入工程的某個目錄中供工程使用,我們在後面的章節會詳細介紹 FIND_PACKAGE 的使用方法和 Find 模塊的編寫。

//FIND_LIBRARY 示例:
FIND_LIBRARY(libX X11 /usr/lib)
IF(NOT libX)
MESSAGE(FATAL_ERROR “libX not found”)
ENDIF(NOT libX)

2.2 語法及變量說明

語法說明

CMakelist中,命令名字是不區分大小寫的,而參數和變量是大小寫相關的。
CMake中使用"#"表示註釋該行代碼。
使用${}進行變量的引用
SET(變量 值) 自定義變量

變量說明

  • (1) PROJECT_NAME
    返回通過 PROJECT 指令定義的項目名稱。

  • (2) PROJECT_SOURCE_DIR == _SOURCE_DIR == CMAKE_SOURCE_DIR
    指向的是<項目目錄>,即:工程頂層目錄

  • (3) PROJECT_BINARY_DIR==_BINARY_DIR == CMAKE_BINARY_DIR
    指向的是<項目目錄>/build目錄。(如果是 in source 編譯(不創建build文件夾, 指得就是工程頂層目錄,如果
    是 out-of-source 編譯,指的是工程編譯發生的目錄。PROJECT_BINARY_DIR 跟其他
    指令稍有區別,現在,你可以理解爲他們是一致的。)

  • (4) CMAKE_CURRENT_SOURCE_DIR
    指的是當前處理的 CMakeLists.txt 所在的路徑,比如上面我們提到的 src 子目錄

  • (5) CMAKE_CURRRENT_BINARY_DIR
    如果是 in-source 編譯,它跟 CMAKE_CURRENT_SOURCE_DIR 一致,如果是 out-of-source 編譯,他指的是 target 編譯目錄。使用我們上面提到的 ADD_SUBDIRECTORY(src bin)可以更改這個變量的值;使用SET(EXECUTABLE_OUTPUT_PATH <新路徑>)並不會對這個變量造成影響,它僅僅修改了最終目標文件存放的路徑。

  • (6) CMAKE_MODULE_PATH
    這個變量用來定義自己的 cmake 模塊所在的路徑。如果你的工程比較複雜,有可能會自己編寫一些 cmake 模塊,這些 cmake 模塊是隨你的工程發佈的,爲了讓 cmake 在處理CMakeLists.txt 時找到這些模塊,你需要通過 SET 指令,將自己的 cmake 模塊路徑設置一下。
    比如:SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
    這時候你就可以通過 INCLUDE 指令來調用自己的模塊了

  • (7) EXECUTABLE_OUTPUT_PATH 和 LIBRARY_OUTPUT_PATH
    分別用來重新定義最終結果的存放目錄,前面我們已經提到了這兩個變量。指定最終的目標二進制的位置(指最終生成的hello(可執行程序名) 或者最終的共享庫,不包含編譯生成的中間文件)

Reference

[1] Cmake知識----編寫CMakeLists.txt文件編譯C/C++程序
[2] Cmake基本命令

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章