前言
代碼變成可執行文件,叫做編譯(compile);先編譯這個,還是先編譯那個(即編譯的安排),叫做構建(build)。CMake是最常用的構建工具,誕生于1977年,主要用于C語言的項目。但是實際上 ,任何只要某個文件有變化,就要重新構建的項目,都可以用CMake構建
cmake是一個根據指定的Shell命令進行構建的工具。它的規則很簡單,你規定要構建哪個文件、它依賴哪些源文件,當那些文件有變動時,如何重新構建它。
cmake 構建命令如下
cmake .. //或者cmake.這里兩個點表示在上層文件夾下尋找cmakelists生成makefile和相關文件
make //執行makefile
make install //(optional)安裝make clean // 清理工程
1、創建項目
括號中的字符不需要用雙引號括起來
project(name)
2、set() 添加、修改變量
添加變量和修改變量用的同一個指令;
# 添加常亮
set(NAME_1 yexindong)# 添加字符串變量
set(NAME_2 "yexindong")# 添加數值變量
set(NAME_3 15)# 設置變量result1的值為"abc",并將該變量的作用域擴展至父目錄。PARENT_SCOPE 變量的作用域升級到父目錄,即在當前目錄的父目錄中也可以使用該變量
set(result1 "abc" PARENT_SCOPE)# 設置全局變量,設置一個名為EXAMPLE_VAR的全局變量,并將其值設置為example_value。INTERNAL 變量定義只在當前的CMakeLists.txt文件中有效,不會傳遞給子目錄
set(EXAMPLE_VAR "example_value" CACHE INTERNAL "")
2.1 清除變量 unset()
unset會清除變量內的值
unset(var CACHE)
3、常用系統變量
// 常用系統變量
${PROJECT_SOURCE_DIR} // 指向項目路徑,也就是src路徑,如果沒有就是項目路徑
${PROJECT_BINSARY_DIR} // 指向編譯路徑,也就是build文件夾,如果沒有則默認跟項目路徑相同set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINSARY_DIR}/bin) // 修改常用系統變量路徑EXECUTABLE_OUTPUT_PATH,也就是可執行文件輸出路徑
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib) // 修改常用系統變量路徑LIBRARY_OUTPUT_PATH,也就是庫文件輸出路徑
set(CMAKE_INSTALL_PREFFIX=/usr) // 修改cmake安裝的默認前綴路徑,默認是/usr/local,也就是默認是針對本用戶/usr/local,可以改為針對所有用戶/usr
4、打印信息
# 打印字符串
message(STATUS "yes")# 打印變量的值
message(STATUS ${name_1})
5、add_difinitions() 增加編譯選項
如果要增加c++11的相關特性,比如nullptr,則增加該句,放在add_executable()前面:
add_definitions(-std=c++11)
6、指定子src目錄
如果要單獨一個src子文件夾放置.cpp和.h文件,則需要在根路徑的cmakelists增加這句,同時src文件夾下增加額外cmakelists文件。
參考Teris源碼。此時,主目錄下的cmakelists主要用來包含src文件夾,并沒有生成可執行文件,而src里邊的cmakelists則會有生成可執行文件。
add_subdirectory(src)
7、include_directories() 添加頭文件路徑
有兩個語句都可以實現添加頭文件路徑,一個是include_directories()這是需要放在生成可執行文件之前。
另一個是target_include_directories()這是需要放在在生成可執行文件之后。
include_directories(./common)target_include_directories(main ./common)
8、link_libraries() 添加.so動態庫文件路徑
實測發現默認的.so路徑只有一個/usr/lib,也就是庫文件如果在這個路徑下則不需要歐手動鏈接。
但如果不是在這個路徑下,或者是在這個路徑下的文件夾中,則需要手動鏈接,即添加
link_libraries(“lib_path.so”)
8.1 動態庫文件路徑第一種方式
有兩個語句都可以實現鏈接庫文件,一個是link_libraries(path)這是在生成可執行文件之前就指定庫文件路徑(必須放在add_executable()的前邊)。
另一個語句也可以實現鏈接庫文件,即用target_link_libraries(main path)這是在生成target可執行文件之后鏈接(必須放在add_executable()的后邊)
兩者的區別就是在定義鏈接文件時實在可執行文件生成之前還是之后,都可以實現功能
8.2 動態庫文件路徑第二種方式
另外一種方法實現鏈接庫文件:就是把該庫文件所在路徑加入到CMAKE_INCLUDE_PATH中然后用find_path()指令。
但注意:加入到CMAKE_INCLUDE_PATH并不代表他會把路徑提供給編譯器,還是需要自己用find_path找到該頭文件。
這種方式的優點在于,只要加入到路徑后,所有find_指令都可以使用
export CMAKE_INCLUDE_PATH=/usr/local/include/test
find_path(myHeader hello.h) // 把頭文件找到,并賦值給變量myHeader
include_directories(${myHeader}) // 把該路徑包含進來
8.3 動態庫文件路徑第三種方式
第三種方法實現鏈接庫文件:就是利用find_package()來查找cmake支持的模塊,或者自定義的模塊來獲得對應頭文件和庫文件路徑。
這種方法參考find_package()的使用。
find_library()和find_path()查找庫文件和頭文件
查找庫文件需提供庫文件名,用find_library(var name HINTS path PATH_SUFFIXES suff1 suff2), 即搜索名稱為name的庫文件(實際名稱是libname.so),找到后存入var中,
并可以帶多個關鍵參數,其中HINTS關鍵參數代表搜索路徑,PATH_SUFFIXES代表路徑后綴
find_library(_NVINFER_LIB nvinfer HINTS ${TRT_LIB} PATH_SUFFIXES lib lib64)
9、find_path 查找文件所在的目錄
CMake中的命令find_path用于查找指定的文件的目錄,如果指定的文件在目錄中找到,則結果將存儲在 var中,除非清除var,否則不會重復搜索。如果沒找到,結果將為var-NOTFOUND。
unset(var CACHE) # 清除變量
find_path(var head.hpp) # 查找文件所在的目錄
message(STATUS ${var})
打印結果
/var/data/ # 若找到,返回文件所在的目錄
var-NOTFOUND # 若未找到,返回的信息
10、find_package() 查找第三方庫的頭文件和鏈接庫文件路徑
注意:采用find_package()命令cmake的模塊查找順序是:先在變量${CMAKE_MODULE_PATH}查找,然后在/usr/shared/cmake/Modules/里邊查找。
find_package(CUDA REQUIRED) # 查找某個第三方庫的cmake module,比如CUDA代表的就是FindCUDA.cmake這個module
find_package(OpenCV REQUIRED) # 多個庫分別查找, 然后統一加到include_directories和link_libraries即可
target_include_directories(tensorrt PUBLIC ${CUDA_INCLUDE_DIRS} ${TENSORRT_INCLUDE_DIR})
target_link_libraries(tensorrt ${CUDA_LIBRARIES} ${TENSORRT_LIBRARY} ${CUDA_CUBLAS_LIBRARIES} ${CUDA_cudart_static_LIBRARY} ${OpenCV_LIBS})
11、file()對文件和文件夾的操作
如果要自動搜索所有支持文件,則可以考慮自動搜索命令
file(GLOB Sources *.cpp) # 從當前目錄查詢所有的.cpp文件 ,將結果賦值到 Sources 變量中
file(GLOB Includes *.h) # 從當前目錄查詢所有的.h 文件,將結果賦值到 Includes 變量中
add_executable(Cars ${Sources} ${Includes}) # 生成可執行文件# 以下2行代碼執行的結果是一樣的
file (GLOB files *.cpp */*.cpp) # 從當前目錄和子目錄查詢所有的.cpp文件 ,將結果賦值到 files 變量中
file(GLOB_RECURSE files *.c) # 添加當前目錄及其子目錄下的所有c文件列表到files變量中# 搜索指定目錄以及子目錄中所有的以.cpp結尾的文件,然后把它們保存到 native_srcs 變量中
file(GLOB_RECURSE native_srcs src/main/cpp/*.cpp)
11.1 創建文件夾
# 創建某個路徑文件夾,MAKE_DIRECTORY 是固定的,path是你需要創建的文件夾的全路徑地址
file(MAKE_DIRECTORY path)
12、add_library() 生成動態或者靜態鏈接庫
可以選擇生成SHARED動態庫, STATIC靜態庫.
注意:庫名稱不需要寫前綴lib,系統會自動在給出的庫名稱前面加lib.
// 創建動態庫
add_library(algorithms SHARED ${src_path}) // 創建動態鏈接庫:庫名稱libalgorithms, SHARED表示為.so動態鏈接庫,src_path是.cpp文件所在路徑
// 創建靜態庫
set_target_properties(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1) // 設置不清除同名動態庫hello.so
set_target_properties(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1) // 設置不清除同名靜態庫hello.a
set_target_properties(hello_static PROPERTIES OUTPUT_NAME "hello") // 設置靜態庫的輸出名稱為hello,從而即使跟動態庫重名也能實現
13、add_executable() 生成可執行文件
第一個參數生成后的文件名稱,
第二個參數是需要生成的源文件路徑,一般都是 .c、 .cpp 或者.h的文件
add_executable(file_name path)
14、install() 安裝生成的頭文件和庫文件:
cmake install
是 CMake 的一個命令,用于安裝構建的項目到指定的目錄中。
在項目的 CMakeLists.txt 文件中,你可以使用 make install 命令來指定要安裝的文件和目錄。install 命令的語法如下:
install(TARGETS <target1> <target2> ...DESTINATION <destination>[COMPONENT <component>][PERMISSIONS permissions...][CONFIGURATIONS [Debug|Release|...]][OPTIONAL]
)
其中,TARGETS 參數用于指定要安裝的目標(可執行文件、庫文件等),DESTINATION 參數用于指定安裝目標的目錄。
例如,假設你的項目生成了一個可執行文件 myapp,你可以使用以下命令將它安裝到 /usr/local/bin 目錄中:
install(TARGETS myappDESTINATION /usr/local/bin
)
你還可以使用 install 命令安裝其他類型的文件,比如庫文件、頭文件、配置文件等。例如,要安裝一個庫文件和相應的頭文件,可以使用以下命令:
install(TARGETS mylibDESTINATION /usr/local/lib
)
install(FILES mylib.hDESTINATION /usr/local/include
)
在構建項目時,可以使用make install
命令來執行安裝操作。這將把構建的目標文件復制到指定的安裝目錄中。
請注意,安裝目錄可能需要管理員權限才能寫入。如果沒有足夠的權限,你可能需要使用 sudo make install
命令來以管理員身份執行安裝。
15、function() 函數
# 定義函數,函數名為 getDir,后面的param1和param2都是參數
function(get_dir param1 param2)message(STATUS "123-" ${param1} "-" ${param2})
endfunction()# 調用函數,兩個參數之間用 空格隔開
get_dir(1 2)
16、STRING() 用法
16.1、 FIND 在字符串中查詢子子字符串是否存在
在CMake中,可以使用字符串操作函數來判斷一個字符串中是否包含另一個字符串。以下是一些常用的函數:
在中查找,如果找到則將其位置保存在<output_variable>中,否則將其賦值為-1。
STRING(FIND <string> <substring> <output_variable>)
示例
# 判斷字符串是否包含 cmake-build-debug ,將結果輸出到indexOfStr變量,若包含返回 > -1的值,若不包含返回-1
STRING(FIND ${dir} cmake-build-debug indexOfStr)
16.2、 SUBSTRING 字符串截取
截取字符串str從索引start開始的長度為length的子字符串。
string(SUBSTRING str start length)
示例
# 截取字符串從索引1開始的長度為3的子字符串
string(SUBSTRING "hello world" 1 3 result)
message(STATUS "result: ${result}") # 輸出 "ell"
16.3、 LENGTH 獲取字符串長度
#獲取字符串str的長度為len。
string(LENGTH str len)# 獲取字符串的長度
string(LENGTH "hello world" len)
message(STATUS "len: ${len}") # 輸出 "11"
16.4、 TOLOWER 將字符串str轉換為小寫字母。
# 將字符串str轉換為小寫字母。
string(TOLOWER str)# 將字符串轉換為小寫字母
string(TOLOWER "HELLO WORLD" result)
message(STATUS "result: ${result}") # 輸出 "hello world"
16.5、TOUPPER 將字符串str轉換為大寫字母。
# 將字符串str轉換為大寫字母。
string(TOUPPER str)# 將字符串轉換為大寫字母
string(TOUPPER "hello world" result)
message(STATUS "result: ${result}") # 輸出 "HELLO WORLD"
16.6、REPLACE:替換字符串中的子字符串
string(REPLACE "world" "CMake" my_replaced_string "hello world")
message("Replaced string is ${my_replaced_string}")# 輸出:Replaced string is hello CMake
16.7、COMPARE:比較兩個字符串
string(COMPARE EQUAL "hello" "hello" my_equal)
message("Strings are equal: ${my_equal}")
string(COMPARE EQUAL "hello" "world" my_equal)
message("Strings are equal: ${my_equal}")# 輸出:
Strings are equal: TRUE
Strings are equal: FALSE
16.8、MATCH:匹配正則表達式
string(MATCH "hello" "hello world" my_match)
message("Matched string is ${my_match}")
# 輸出:
Matched string is hello
16.9、REGEX:正則表達式替換
string(REGEX REPLACE "world" "CMake" my_replaced_string "hello world")
message("Replaced string is ${my_replaced_string}")
# 輸出:
Replaced string is hello CMake
16.10、ASCII:獲取字符的ASCII碼值
string(ASCII "a" my_ascii_value)
message("ASCII value is ${my_ascii_value}")
# 輸出:
ASCII value is 97
16.11、CONFIGURE:在字符串中進行配置
set(my_string "Hello ${CMAKE_PROJECT_NAME}!")
string(CONFIGURE "${my_string}" my_configured_string)
message("Configured string is ${my_configured_string}")# 輸出:
Configured string is Hello <project-name>!
其中,<project-name>會被替換為實際的項目名稱
17、while 循環
CMake 中 while 循環的語法為:
while(<condition>)# 循環體# ...
endwhile()
是循環條件,可以是任何返回布爾值(即 true 或 false)的表達式或變量。當條件成立時,循環體會被執行,循環體可以包含任意 CMake 命令或函數。循環體執行完后,繼續檢查循環條件,如果條件仍然成立,則重復執行循環體,直到循環條件不成立。
下面是一些 while 循環的用法示例:
例子 1
set(i 0)
while(i LESS 5)message("i = ${i}")math(EXPR i "${i} + 1")
endwhile()
輸出:
i = 0
i = 1
i = 2
i = 3
i = 4
例子 2
set(j 10)
while(j GREATER_EQUAL 0)message("j = ${j}")math(EXPR j "${j} - 2")
endwhile()
輸出:
j = 10
j = 8
j = 6
j = 4
j = 2
j = 0
例子 3
set(k 3)
while(k)message("Hello, world!")math(EXPR k "${k} - 1")
endwhile()
輸出:
Hello, world!
Hello, world!
Hello, world!
例子 4
set(str "abc")
while(str MATCHES "a.*")message("${str}")string(REGEX REPLACE "^a" "" str "${str}")
endwhile()
輸出:
abc
bc
c
18、aux_source_directory 添加指定目錄中的源文件到變量
aux_source_directory 是一個 CMake 命令,用于將指定目錄中的源文件自動添加到變量中。這個命令的語法如下:
aux_source_directory(<dir> <variable>)
其中:
<dir>
是要搜索源文件的目錄名稱,<variable>
是一個變量,用于存儲找到的源文件列表。
注意
:.c
和 .cpp
才是源文件,.h
結尾的頭文件不是源文件
aux_source_directory 命令會自動搜索指定目錄下的所有源文件,并將它們的完整路徑添加到 變量中。這個命令會自動為每個源文件生成一個相對路徑,并將它們存儲在 變量中。
aux_source_directory 命令通常用于自動發現源文件,特別是在構建一個大型項目時,手動列出每個源文件可能會很繁瑣和容易出錯。通過使用 aux_source_directory 命令,可以更方便地管理源文件列表。
以下是一個示例,演示了如何使用 aux_source_directory 命令:
# 搜索 src 目錄下的所有源文件,并將它們存儲在 SOURCES 變量中
aux_source_directory(src SOURCES)# 將 SOURCES 變量添加到一個可執行目標中
add_executable(myapp ${SOURCES})
在上面的示例中,aux_source_directory 命令會搜索 src 目錄下的所有源文件,并將它們的完整路徑存儲在 SOURCES 變量中。然后,SOURCES 變量被添加到 add_executable 命令中,作為構建可執行目標 myapp 的源文件列表。
需要注意的是,aux_source_directory 命令只會在第一次調用時將源文件添加到變量中,后續對同一目錄調用該命令不會再次添加源文件。如果你在構建過程中添加了新的源文件,需要重新運行 CMake 來更新源文件列表。