CMake2: CMakeLists.txt的常用命令

參考鏈接:

愛編程的大丙 | CMake教程
CMakeLists指令以及常用方法
現代 CMake 教程

文章目錄

  • 1. cmake_minimum_required( )
  • 2. project( )
  • 3. add_executable( )
  • 4. set()
  • 5. aux_source_directory( )
  • 6. file( )
  • 7. include_directories( )
  • 8. add_library( )
  • 9. link_libraries()與link_directories()
  • 10. target_link_libraries( )
  • 11. message()
  • 12. list()
  • 13. add_definitions()
  • 14. find_package()
  • 15. install( )
  • 16. add_subdirectory( )
  • 17. target_include_directories()


1. cmake_minimum_required( )

用于規定cmake的最低版本(可選,非必須,如果不加可能會有警告)

cmake_minimum_required(VERSION 2.8)

2. project( )

定義工程名稱,并可指定工程的版本、工程描述、web 主頁地址、支持的語言(默認情況支持所有語言),如果不需要這些都是可以忽略的,只需要指定出工程名字即可。

語法:

project(<PROJECT-NAME> )  
project(<PROJECT-NAME>[VERSION <major>[.<minor>[.<patch>[.<tweak>]]]][DESCRIPTION <project-description-string>][HOMEPAGE_URL <url-string>][LANGUAGES <language-name>...])

一般僅會用到一個參數,如:

project(myslam)

3. add_executable( )

工程會生成一個可執行程序,語法:

add_executable(可執行程序名 源文件名稱)

例子,生成的可執行文件名稱為myslam

add_executable(myslam  main.cpp) 

4. set()

在cmake中使用set來定義變量,變量的值均為字符串類型,同時該命令也可用于拼接字符串,語法是:

SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
  • VAR:變量名
  • VALUE:變量值

取出變量的值時使用${ VAR }進行

# 方式1: 各個源文件之間使用空格間隔
set(SRC_LIST add.c  div.c   main.c  mult.c  sub.c)# 方式2: 各個源文件之間使用分號 ; 間隔
# 多次對同一變量set以最后一次為準
set(SRC_LIST add.c;div.c;main.c;mult.c;sub.c)
add_executable(app  ${SRC_LIST})

常用場景:

(1) 用于指定C++標準

C++標準在cmake中對應的宏叫做DCMAKE_CXX_STANDARD。同時,在CMake要指定C++標準有兩種方式:

  • 在 CMakeLists.txt 中通過 set 命令指定
#增加-std=c++11
set(CMAKE_CXX_STANDARD 11)
#增加-std=c++14
set(CMAKE_CXX_STANDARD 14)
#增加-std=c++17
set(CMAKE_CXX_STANDARD 17)
  • 在執行 cmake 命令的時候指定出這個宏的值
#增加-std=c++11
cmake CMakeLists.txt文件路徑 -DCMAKE_CXX_STANDARD=11
#增加-std=c++14
cmake CMakeLists.txt文件路徑 -DCMAKE_CXX_STANDARD=14
#增加-std=c++17
cmake CMakeLists.txt文件路徑 -DCMAKE_CXX_STANDARD=17# build目錄下
cmake .. -DCMAKE_CXX_STANDARD=11

注:-D表示指定宏名,其緊接著宏名并進行賦值

(2)用于指定輸出的路徑

在CMake中指定可執行程序輸出的路徑,也對應一個宏,叫做EXECUTABLE_OUTPUT_PATH,它的值還是通過set命令進行設置

set(HOME /home/robin/Linux/Sort)
set(EXECUTABLE_OUTPUT_PATH ${HOME}/bin)
  • 第一行:定義一個變量用于存儲一個絕對路徑
  • 第二行:將拼接好的路徑值設置給EXECUTABLE_OUTPUT_PATH宏

注: 如果這個路徑中的子目錄不存在,會自動生成,無需自己手動創建;并且由于可執行程序是基于 cmake 命令生成的 makefile 文件然后再執行 make 命令得到的,所以如果此處指定可執行程序生成路徑的時候使用的是相對路徑 ./xxx/xxx,那么這個路徑中的 ./ 對應的就是 makefile 文件所在的那個目錄,即build目錄;故輸出路徑一般寫成絕對路徑

(3)拼接字符串

如果使用set進行字符串拼接,對應的命令格式如下:

set(變量名1 ${變量名1} ${變量名2} ...)

關于上面的命令其實就是將從第二個參數開始往后所有的字符串進行拼接,最后將結果存儲到第一個參數中,如果第一個參數中原來有數據會對原數據就行覆蓋。

5. aux_source_directory( )

在 CMake 中使用aux_source_directory 命令可以查找某個路徑下的所有源文件,命令格式為:

aux_source_directory(< dir > < variable >)
  • dir:要搜索的目錄
  • variable:將從dir目錄下搜索到的源文件列表存儲到該變量中
cmake_minimum_required(VERSION 3.0)
project(CALC)
include_directories(${PROJECT_SOURCE_DIR}/include)
# 搜索 src 目錄下的源文件
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src SRC_LIST)
add_executable(app  ${SRC_LIST})

注:此處的宏PROJECT_SOURCE_DIR與CMAKE_CURRENT_SOURCE_DIR均為訪問CMakeLists.txt所在的路徑,即工程的根目錄

6. file( )

在CMake中使用file命令完成搜索文件的任務

file(GLOB/GLOB_RECURSE 變量名 要搜索的文件路徑和文件類型)
  • GLOB: 將指定目錄下搜索到的滿足條件的所有文件名生成一個列表,并將其存儲到變量中。
  • GLOB_RECURSE:遞歸搜索指定目錄,將搜索到的滿足條件的文件名生成一個列表,并將其存儲到變量中。

搜索當前目錄的src目錄下所有的源文件,并存儲到變量中

file(GLOB MAIN_HEAD ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h)
# 等價于 file(GLOB MAIN_HEAD "${CMAKE_CURRENT_SOURCE_DIR}/src/*.h")
file(GLOB MAIN_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)

7. include_directories( )

在編譯項目源文件的時候,需要將源文件對應的頭文件路徑指定出來,這樣才能保證在編譯過程中編譯器能夠找到這些頭文件,并順利通過編譯,使用include_directories來包含頭文件目錄

include_directories(headpath)

例子,目錄結構如下:

$ tree
.
├── build
├── CMakeLists.txt
├── include
│   └── head.h
└── src├── add.cpp├── div.cpp├── main.cpp├── mult.cpp└── sub.cpp3 directories, 7 files

CMakeLists.txt文件內容如下:

cmake_minimum_required(VERSION 3.0)
project(CALC)
set(CMAKE_CXX_STANDARD 11)
set(HOME /home/robin/Linux/calc)
set(EXECUTABLE_OUTPUT_PATH ${HOME}/bin/)
include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
add_executable(app  ${SRC_LIST})

8. add_library( )

有些時候我們編寫的源代碼并不需要將他們編譯生成可執行程序,而是生成一些靜態庫或動態庫提供給第三方使用,在cmake中使用add_library命令實現:

(1)生成靜態庫

在Linux中,靜態庫名字分為三部分:lib+庫名字+.a,此處只需要指定出庫的名字就可以了,另外兩部分在生成該文件的時候會自動填充。

add_library(庫名稱 STATIC 源文件1 [源文件2] ...) 

(2)生成動態庫

在Linux中,動態庫名字分為三部分:lib+庫名字+.so,此處只需要指定出庫的名字就可以了,另外兩部分在生成該文件的時候會自動填充。

add_library(庫名稱 SHARED 源文件1 [源文件2] ...) 

(3) 設置路徑生成

由于在Linux下生成的靜態庫默認不具有可執行權限,所以在指定靜態庫生成的路徑的時候就不能使用EXECUTABLE_OUTPUT_PATH宏了,而應該使用LIBRARY_OUTPUT_PATH,這個宏對應靜態庫文件和動態庫文件都適用。

cmake_minimum_required(VERSION 3.0)
project(CALC)
include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
# 設置動態庫/靜態庫生成路徑
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
# 生成動態庫
#add_library(calc SHARED ${SRC_LIST})
# 生成靜態庫
add_library(calc STATIC ${SRC_LIST})

9. link_libraries()與link_directories()

在cmake中,鏈接靜態庫的命令為link_libraries

link_libraries(<static lib> [<static lib>...])
  • 參數1:指定出要鏈接的靜態庫的名字。可以是全名 libxxx.a,也可以是掐頭(lib)去尾(.a)之后的名字 xxx
  • 參數2-N:要鏈接的其它靜態庫的名字

如果該靜態庫不是系統提供的(自己制作或者使用第三方提供的靜態庫)可能出現靜態庫找不到的情況,此時可以使用link_directories命令指定靜態庫或動態庫的路徑

link_directories(<lib path>)

例子:

cmake_minimum_required(VERSION 3.0)
project(CALC)
# 搜索指定目錄下源文件
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
# 包含頭文件路徑
include_directories(${PROJECT_SOURCE_DIR}/include)# 包含靜態庫路徑
link_directories(${PROJECT_SOURCE_DIR}/lib)
# 鏈接靜態庫
link_libraries(calc)add_executable(app ${SRC_LIST})

10. target_link_libraries( )

在cmake中鏈接庫文件的命令為target_link_libraries,該命令既可以鏈接動態庫,也可以鏈接靜態庫文件。

target_link_libraries(<target> <PRIVATE|PUBLIC|INTERFACE> <item>... [<PRIVATE|PUBLIC|INTERFACE> <item>...]...)

target:指定要加載動態庫文件的名字。該文件可能是一個源文件;該文件可能是一個動態庫文件;該文件可能是一個可執行文件。

PRIVATE|PUBLIC|INTERFACE:動態庫的訪問權限,默認為PUBLIC

  • PUBLIC:在public后面的庫會被Link到前面的target中,并且里面的符號也會被導出,提供給第三方使用。
  • PRIVATE:在private后面的庫僅被link到前面的target中,并且終結掉,其它庫如果鏈接到該target,則無法獲知private后面的庫信息
target_link_libraries(A B C)
target_link_libraries(D A)
#D 可訪問 A B Ctarget_link_libraries(A private B C)
target_link_libraries(D A)
#D 可訪問 A, 但 不可訪問 B 和 C
  • INTERFACE:在interface后面引入的庫不會被鏈接到前面的target中,只會導出符號;即當前的target只知道鏈接到了某一函數,但不知道該函數來自哪個庫

如果各個動態庫之間沒有依賴關系,無需做任何設置,三者沒有沒有區別,一般無需指定,使用默認的 PUBLIC 即可。

動態庫的鏈接和靜態庫是完全不同的:

  • 靜態庫會在生成可執行程序的鏈接階段被打包到可執行程序中,所以可執行程序啟動,靜態庫就被加載到內存中了。
  • 動態庫在生成可執行程序的鏈接階段不會被打包到可執行程序中,當可執行程序被啟動并且調用了動態庫中的函數的時候,動態庫才會被加載到內存

因此,在cmake中指定要鏈接的動態庫的時候,應該將命令寫到生成了可執行文件之后

11. message()

在CMake中可以用戶顯示一條消息,該命令的名字為message:

message([STATUS|WARNING|AUTHOR_WARNING|FATAL_ERROR|SEND_ERROR] "message to display" ...)
  • (無) :重要消息
  • STATUS :非重要消息
  • WARNING:CMake 警告, 會繼續執行
  • AUTHOR_WARNING:CMake 警告 (dev), 會繼續執行
  • SEND_ERROR:CMake 錯誤, 繼續執行,但是會跳過生成的步驟
  • FATAL_ERROR:CMake 錯誤, 終止所有處理過程

CMake的命令行工具會在stdout上顯示STATUS消息,在stderr上顯示其他所有消息。CMake的GUI會在它的log區域顯示所有消息。

CMake警告和錯誤消息的文本顯示使用的是一種簡單的標記語言。文本沒有縮進,超過長度的行會回卷,段落之間以新行做為分隔符。

注:該命令也可用于打印定義的變量信息,方便進行調試

12. list()

該指令用于操作字符串,由如下子屬性以實現不同的字符串功能。

子命令功能描述編號
LENGTH用于讀取列表長度(3)
GET獲取索引對應元素的子命令,可以同時指定多個索引(4)
JOIN連接列表中的元素,并在元素間添加連接符的子命令(5)
SUBLIST用于獲取列表中的一部分(子列表)(15)
FIND查找指定元素(6)
APPEND將數據追加到列表中,也可進行字符串拼接(1)
INSERT在指定位置將元素(一個或多個)插入到列表中(7)
PREPEND在列表的頭部(索引為0處)插入(一個或多個)元素(8)
POP_BACK將列表中最后元素移除(可將移除元素存入變量)(9)
POP_FRONT將列表中第一個元素移除(可將移除元素存入變量)(10)
REMOVE_ITEM將指定的元素從列表中移除(2)
REMOVE_AT將指定索引的元素從列表中移除(11)
REMOVE_DUPLICATES移除列表中的重復元素(12)
REVERSE將整個列表反轉(13)
SORT將整個列表元素排序(14)

(1)字符串拼接

如果使用list進行字符串拼接,對應的命令格式如下:

list(APPEND <list> [<element> ...])
  • APPEND : 表示進行數據追加
  • list : 為列表
  • element : 為字符串,也可用${變量名}
cmake_minimum_required(VERSION 3.0)
project(TEST)
set(TEMP "hello,world")
file(GLOB SRC_1 ${PROJECT_SOURCE_DIR}/src1/*.cpp)
file(GLOB SRC_2 ${PROJECT_SOURCE_DIR}/src2/*.cpp)
# 追加(拼接)
list(APPEND SRC_1 ${SRC_1} ${SRC_2} ${TEMP})
message(STATUS "message: ${SRC_1}")

注意:在CMake中,使用set命令可以創建一個list。一個在list內部是一個由分號;分割的一組字符串。例如,set(var a b c d e)命令將會創建一個list:a;b;c;d;e,但是最終打印變量值的時候得到的是abcde。

(2) 字符串刪除

如果使用list移除指定的字符串,對應命令:

list(REMOVE_ITEM <list> <value> [<value> ...])
  • REMOVE_ITEM: 表示進行字符串移除
  • list : 為列表
  • value: 為字符串,也可用${變量名}
cmake_minimum_required(VERSION 3.0)
project(TEST)
set(TEMP "hello,world")
file(GLOB SRC_1 ${PROJECT_SOURCE_DIR}/*.cpp)
# 移除前日志
message(STATUS "message: ${SRC_1}")
# 移除 main.cpp
list(REMOVE_ITEM SRC_1 ${PROJECT_SOURCE_DIR}/main.cpp)
# 移除后日志
message(STATUS "message: ${SRC_1}")

值得注意的是,list指令移除的是一個元素,即用分號分隔的一個字符串,而不是元素的一部分。

(3)獲取 list 的長度

list(LENGTH <list> <output variable>)
  • LENGTH:子命令LENGTH用于讀取列表長度
  • list:當前操作的列表
  • output variable:新創建的變量,用于存儲列表的長度,也是字符串類型

(4)讀取列表中指定索引的的元素,可以指定多個索引

list(GET <list> <element index> [<element index> ...] <output variable>)
  • GET : 獲取索引對應元素的子命令
  • list:當前操作的列表
  • element index:列表元素的索引。從0開始編號,索引0的元素為列表中的第一個元素;索引也可以是負數,-1表示列表的最后一個元素,-2表示列表倒數第二個元素,以此類推;當索引(不管是正還是負)超過列表的長度,運行會報錯
  • output variable:新創建的變量,存儲指定索引元素的返回結果,也是一個列表。

(5)將列表中的元素用連接符(字符串)連接起來組成一個字符串

list (JOIN <list> <glue> <output variable>)
  • JOIN : 連接列表中的元素,并在元素間添加連接符的子命令
  • list :當前操作的列表
  • glue :指定的連接符(字符串)
  • output variable :新創建的變量,存儲返回的字符串

(6)查找列表是否存在指定的元素,如果未找到,返回-1

list(FIND <list> <value> <output variable>)
  • FIND : 查找指定元素
  • list:當前操作的列表
  • value:需要再列表中搜索的元素
  • output variable:新創建的變量。如果列表中存在,那么返回在列表中的索引;如果未找到則返回-1。

(7)在list中指定的位置插入若干元素

list(INSERT <list> <element_index> <element> [<element> ...])
  • INSERT: 插入指定元素
  • list:當前操作的列表
  • element_index:列表中元素的索引;插入其element_index的前一索引之后,其后的元素往后移
  • element:插入的元素

(8)將元素插入到列表的0索引位置

list (PREPEND <list> [<element> ...])

(9)將列表中最后元素移除

list (POP_BACK <list> [<out-var>...])
  • list :欲移除最后元素的列表名稱
  • out-var :輸出變量

如果未指定,則僅僅只將原列表中最后一個元素移除;如果指定,會將列表最后一個元素賦值給輸出變量,然后再將列表最后一個元素移除。

如果指定多個輸出變量,則會依次將列表中最后一個元素賦值到輸出變量中。

如果輸出變量個數大于列表長度,則超出部分的輸出變量未定義。

(10)將列表中第一個元素移除

list (POP_FRONT <list> [<out-var>...])
  • list :欲移除第一個元素的列表名稱
  • out-var :輸出變量

如果未指定,則僅僅只將列表中第一個元素移除;如果指定,會將列表第一個元素賦值給輸出變量,然后再將列表第一個元素移除。

如果指定多個輸出變量,則會依次將列表中第一個元素賦值到輸出變量中。

如果輸出變量個數大于列表長度,則超出部分的輸出變量未定義。

(11)將指定索引的元素從列表中移除

list (REMOVE_AT <list> <index> [<index> ...])
  • list :欲移除元素的列表名稱
  • index :指定的索引

當指定的索引不存在的時候,會提示錯誤;如果指定的索引存在重復,如 list(REMOVE_AT MYLIST 1 1),則只會執行一次移除動作。

(12)移除列表中的重復元素

list (REMOVE_DUPLICATES <list>)
  • list :欲移除元素的列表名稱

如果列表中沒有重復元素,則列表不會改變。

如果列表中所有元素都是一樣的,則會保留一個。

如果有多個重復元素,則保留第一個。

(13)列表翻轉

list(REVERSE <list>)
  • list :欲反轉元素列表名稱

(14)列表排序

list (SORT <list> [COMPARE <compare>] [CASE <case>] [ORDER <order>])
  • list :欲排序列表名稱
  • COMPARE :指定排序方法
    • SRING :按照字母順序進行排序(默認)
    • FILE_BASENAME :如果是一系列路徑名,會使用basename進行排序
    • NATURAL :使用自然數順序排序
  • CASE :指明是否大小寫敏感
    • SENSITIVE :按大小寫敏感的方式進行排序(默認)
    • INSENSITIVE :按大小寫不敏感方式進行排序
  • ORDER :排序的順序
    • ASCENDING :按照升序排序(默認)
    • DESCENDING :按照降序排序

(15)獲取列表中的一部分子列表

list(SUBLIST <list> <begin> <length> <out-var>)
  • list 為欲獲取子列表的父列表名稱;
  • begin 開始索引;
  • length 子列表長度。
  • out-var 為新創建的變量,用于存儲獲取結果子列表。

若length值為0,返回子列表為空列表;

若length值為-1 或 父列表長度小于begin+length值(即索引越界或超出長度),將會把父列表中從begin索引開始的所有剩余元素返回。

13. add_definitions()

使用該命令為程序添加宏定義:

add_definitions(-D宏名稱)

例子:

cmake_minimum_required(VERSION 3.0)
project(TEST)
# 自定義 DEBUG 宏
add_definitions(-DDEBUG)
add_executable(app ./test.c)

可配合debug的日志文件使用,在編寫C++程序時,可使用預處理命令判斷宏是否存在來決定Debug的日志文件輸出。

#include <stdio.h>
#define NUMBER  3int main()
{int a = 10;
#ifdef DEBUGprintf("Debugging information...\n");
#endiffor(int i=0; i<NUMBER; ++i){printf("hello, GCC!!!\n");}return 0;
}

14. find_package()

find_package(<Package> [version] [EXACT] [QUIET] [MODULE] [REQUIRED][[COMPONENTS] [components...]][OPTIONAL_COMPONENTS components...][NO_POLICY_SCOPE])
  • <Package>:要查找的軟件包的名稱,例如 OpenCV、Boost 等。
  • version:可選參數,用于指定所需的軟件包版本。
  • EXACT:可選參數,要求版本號完全匹配。
  • QUIET:可選參數,如果找不到軟件包,則不產生錯誤。
  • MODULE:可選參數,指定使用模塊方式查找軟件包。
  • REQUIRED:可選參數,如果找不到軟件包,則產生致命錯誤。
  • COMPONENTS:可選參數,指定要查找的軟件包的組件。
  • OPTIONAL_COMPONENTS:可選參數,指定可選的組件。
  • NO_POLICY_SCOPE:可選參數,指定不將策略設置限定在調用的目錄。

如果找到了相應的庫,則會自動定義以下幾個變量:

  • <LibraryName>_FOUND:

    • 這個變量用于指示是否找到了指定的庫。

    • 如果找到了,這個變量的值為 TRUE;否則,值為 FALSE。

  • <LibraryName>_INCLUDE_DIR 或 <LibraryName>_INCLUDES:

    • 這個變量用于存儲庫的頭文件所在目錄的路徑。

    • 如果庫提供了多個頭文件目錄,可能是一個路徑列表。

  • <LibraryName>_LIBRARY 或 <LibraryName>_LIBRARIES:

    • 這個變量用于存儲庫文件(通常是靜態庫或動態庫)的路徑或名稱。

    • 如果庫提供了多個庫文件,可能是一個路徑列表。

    • 在鏈接目標時,應使用這些變量指定要鏈接的庫。

find_package具有兩種模式,一種是Module模式,另一種叫做Config模式。

  • 在Module模式中,cmake需要找到一個叫做Find<LibraryName>.cmake的文件。這個文件負責找到庫所在的路徑,為我們的項目引入頭文件路徑和庫文件路徑。cmake搜索這個文件的路徑有兩個,一個cmake安裝目錄下的/usr/share/cmake-<version>/Modules目錄,另一個使我們指定的CMAKE_MODULE_PATH的所在目錄。

  • 在Config模式中,cmake主要通過<LibraryName>Config.cmake or <lower-case-package-name>-config.cmake這兩個文件來引入我們需要的庫。一般在/usr/local/lib/cmake可找到一些配置文件,有些庫的config文件則在share中,如/usr/share/OpenCV/OpenCVConfig.cmake。

對于原生支持Cmake編譯和安裝的庫通常會安裝Config模式的配置文件到對應目錄,這個配置文件直接配置了頭文件庫文件的路徑以及各種cmake變量供find_package使用。而對于非由cmake編譯的項目,我們通常會編寫一個Find<LibraryName>.cmake,通過腳本來獲取頭文件、庫文件等信息。通常,原生支持cmake的項目庫安裝時會拷貝一份XXXConfig.cmake到系統目錄中,因此在沒有顯式指定搜索路徑時也可以順利找到。

示例:

cmake_minimum_required(VERSION 3.10)project(Demo)find_package(OpenCV REQUIRED)add_executable(demo main.cpp)target_link_libraries(demoPRIVATE ${OpenCV_LIBS}
)

15. install( )

該指令用于將生成的文件安裝到系統目錄

(1)安裝目標庫文件以及可執行文件

 install(TARGETS targets... [EXPORT <export-name>][[ARCHIVE|LIBRARY|RUNTIME|OBJECTS|FRAMEWORK|BUNDLE|PRIVATE_HEADER|PUBLIC_HEADER|RESOURCE][DESTINATION <dir>][PERMISSIONS permissions...][CONFIGURATIONS [Debug|Release|...]][COMPONENT <component>][NAMELINK_COMPONENT <component>][OPTIONAL] [EXCLUDE_FROM_ALL][NAMELINK_ONLY|NAMELINK_SKIP]] [...][INCLUDES DESTINATION [<dir> ...]])
  • targets:指定要安裝的目標名稱。
  • EXPORT <export-name>:指定一個導出集,安裝的目標將被添加到該集合中。
  • [ARCHIVE|LIBRARY]:指定正在安裝的目標的類型。
    • 靜態庫: ARCHIVE
    • 動態庫: LIBRARY
    • 可執行二進制文件: RUNTIME
    • 與庫關聯的PUBLIC頭文件: PUBLIC_HEADER
    • 與庫關聯的PRIVATE頭文件: PRIVATE_HEADER
  • [DESTINATION <dir>]:指定目標的安裝目錄。
  • PERMISSIONS:指定安裝文件的權限;有效權限包括: OWNER_READ,OWNER_WRITE,OWNER_EXECUTE,GROUP_READ,GROUP_WRITE,GROUP_EXECUTE,WORLD_READ,WORLD_WRITE,WORLD_EXECUTE,SETUID和SETGID;
  • CONFIGURATIONS:指定安裝規則適用的構建配置列表(DEBUG或RELEASE等);
  • COMPONENT:指定了該安裝規則相關的一個安裝部件的名字,如“runtime”;
  • EXCLUDE_FROM_ALL:指定該文件從完整安裝中排除,僅作為特定于組件的安裝的一部分進行安裝;
  • OPTIONAL:如果要安裝的文件不存在,則指定不是錯誤。

使用案例:

# 將 myslam 庫安裝到指定目錄
install(TARGETS myslam#安裝動態庫使用這行指令 DESTINATION指定安裝目錄LIBRARY DESTINATION /usr/local/lib#安裝靜態庫使用這行指令ARCHIVE DESTINATION /usr/local/lib
)

(2)安裝頭文件目錄或其它類型的文件目錄

install(DIRECTORY dirs...TYPE <type> | DESTINATION <dir>[FILE_PERMISSIONS permissions...][DIRECTORY_PERMISSIONS permissions...][USE_SOURCE_PERMISSIONS] [OPTIONAL] [MESSAGE_NEVER][CONFIGURATIONS [Debug|Release|...]][COMPONENT <component>] [EXCLUDE_FROM_ALL][FILES_MATCHING][[PATTERN <pattern> | REGEX <regex>][EXCLUDE] [PERMISSIONS permissions...]] [...])
  • dirs:目錄名。不以/結尾,包含目錄本身,目錄名以/結尾,目錄下的內容,不包括目錄本身。
  • DESTINATION <dir>:目錄的安裝位置
  • FILES_MATCHING:進行文件匹配,匹配方式由PATTERN|REGEX確定
  • PATTERN|REGEX:以精細的粒度控制目錄的安裝,可以指定一個通配模式或正則表達式對輸入目錄過濾;
  • PERMISSIONS:覆蓋匹配文件或目錄的權限設置。
# 將 include 目錄下的所有頭文件安裝到指定目錄
install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/DESTINATION /usr/local/include/myslam# 匹配${PROJECT_SOURCE_DIR}/include 下的所有.h文件進行安裝FILES_MATCHING PATTERN "*.h")

16. add_subdirectory( )

使用add_subdirectory命令添加子目錄,執行子目錄下的CMakeLists.txt

add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
  • source_dir:指定了CMakeLists.txt源文件和代碼文件的位置,其實就是指定子目錄。
  • binary_dir:指定了輸出文件的路徑,一般不需要指定,忽略即可。
  • EXCLUDE_FROM_ALL:在子路徑下的目標默認不會被包含到父路徑的ALL目標里,并且也會被排除在IDE工程文件之外。用戶必須顯式構建在子路徑下的目標。

例子:

$ tree
.
├── build
├── CMakeLists.txt
├── include
│   └── sort.h
├── sort
│   ├── CMakeLists.txt
│   ├── insert.cpp
│   └── select.cpp
└── test├── CMakeLists.txt└── sort.cpp6 directories, 15 files

根目錄下的CMakeLists.txt

cmake_minimum_required(VERSION 3.0)
project(test)
# 定義變量
# 靜態庫生成的路徑
set(LIB_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lib)
# 測試程序生成的路徑
set(EXEC_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin)
# 頭文件目錄
set(HEAD_PATH ${CMAKE_CURRENT_SOURCE_DIR}/include)
# 靜態庫的名字
set(SORT_LIB sort)
# 可執行程序的名字
set(APP_NAME test)
# 添加子目錄
add_subdirectory(sort)
add_subdirectory(test)

sort子目錄下的CMakeLists.txt

cmake_minimum_required(VERSION 3.0)
project(SORTLIB)
aux_source_directory(./ SRC)
include_directories(${HEAD_PATH})
set(LIBRARY_OUTPUT_PATH ${LIB_PATH})
add_library(${SORT_LIB} SHARED ${SRC})

test子目錄下的CMakeLists.txt

cmake_minimum_required(VERSION 3.0)
project(SORTTEST)
aux_source_directory(./ SRC)
include_directories(${HEAD_PATH})
set(EXECUTABLE_OUTPUT_PATH ${EXEC_PATH})
link_directories(${LIB_PATH})
add_executable(${APP_NAME} ${SRC})
target_link_libraries(${APP_NAME} ${SORT_LIB})

17. target_include_directories()

在 CMake 中,target_include_directories 是一個非常重要的命令,用于為特定目標(如可執行文件或庫)指定頭文件的搜索路徑。它的主要作用是告訴編譯器在編譯該目標時應該從哪些目錄中查找頭文件。基本語法:

target_include_directories(<target> [SYSTEM] [BEFORE]<INTERFACE|PUBLIC|PRIVATE> [items1...][<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
  • <target>:指定要配置的目標名稱(由 add_executable 或 add_library 創建的目標)
  • 作用域關鍵字(必須至少指定一個):
    • PRIVATE:頭文件路徑僅對當前目標的源文件可見,不會傳遞給依賴該目標的其他目標
    • PUBLIC:頭文件路徑對當前目標可見,同時也會傳遞給依賴該目標的其他目標
    • INTERFACE:頭文件路徑不用于當前目標,僅傳遞給依賴該目標的其他目標
  • 其他可選參數:
    • SYSTEM:將目錄標記為系統目錄,編譯器可能會對系統目錄下的頭文件采取不同的處理方式(如忽略某些警告)
    • BEFORE:使指定的目錄在已有的包含目錄之前被搜索

示例:

# 創建一個庫目標
add_library(my_lib src/my_lib.cpp)# 為庫目標指定頭文件目錄
target_include_directories(my_libPUBLIC# 生成器表達式(generator expressions),馬上講解$<INSTALL_INTERFACE:include>$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>PRIVATE${CMAKE_CURRENT_SOURCE_DIR}/src
)# 創建可執行文件目標,依賴于my_lib
add_executable(my_app src/main.cpp)# 為可執行文件指定頭文件目錄
target_include_directories(my_appPRIVATE${CMAKE_CURRENT_SOURCE_DIR}/include
)# 鏈接庫
target_link_libraries(my_app PRIVATE my_lib)

生成器表達式(generator expressions)

這是一種特殊語法,用于在構建時根據不同條件(如構建階段、目標類型等)動態指定路徑。

生成器表達式的格式是

 $<條件:內容>

它不會在 CMake 解析階段(cmake … 時)立即生效,而是在生成構建系統(如 Makefile 或 Visual Studio 項目)時才確定最終值。

示例中用到了兩個最常用的生成器表達式:

  • $<BUILD_INTERFACE:路徑>
    • 含義:僅在「構建當前項目時」生效的路徑。
    • 示例中 ${CMAKE_CURRENT_SOURCE_DIR}/include 是當前項目源碼中的 include 目錄(比如 project/include)。
    • 作用:編譯 my_lib 自身或依賴它的本地目標(如同一項目中的可執行文件)時,編譯器會從這個目錄找頭文件。
  • $<INSTALL_INTERFACE:include>
    • 含義:僅在「安裝項目后」生效的路徑。
    • 示例中 include 是安裝后的相對路徑(比如最終會被安裝到 /usr/local/include 或 C:\Program Files\my_lib\include)。
    • 作用:當其他人安裝你的庫后,用它來編譯自己的項目時,編譯器會從安裝目錄的 include 文件夾找頭文件。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/94258.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/94258.shtml
英文地址,請注明出處:http://en.pswp.cn/web/94258.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Ansible入門:自動化運維基礎

Ansible 基礎概念與安裝1. 自動化動機 (Motivation for Automation)概念解釋&#xff1a; 指為什么要用Ansible等工具來替代手動管理服務器。核心動機包括&#xff1a;效率與速度&#xff1a; 同時在上百甚至上千臺服務器上執行任務&#xff0c;秒級完成&#xff0c;遠非人工可…

【測試】基于博客系統的測試報告

前言 本篇博客對簡易的博客系統做的測試總結一份測試報告&#xff0c;包含功能測試&#xff0c;自動化測試&#xff0c;性能測試 &#x1f493; 個人主頁&#xff1a;zkf ? 文章專欄&#xff1a;測試 若有問題 評論區見&#x1f4dd; &#x1f389;歡迎大家點贊&#x1f44d;…

Oracle:配置讓插入語句時id自動輸入

Oracle:配置讓插入語句時id自動輸入無需手動指定&#xff0c;核心是利用 序列&#xff08;Sequence&#xff09; 或 自增列&#xff08;Identity Column&#xff09; 來自動生成唯一值。以下是兩種常用方案&#xff1a;方案 1&#xff1a;使用序列&#xff08;Sequence&#xf…

秒殺服務的回調方案

在秒殺場景中&#xff0c;用戶點擊“搶購”后&#xff0c;后端需要通過異步處理應對高并發&#xff08;避免請求阻塞&#xff09;&#xff0c;同時需通過實時回調機制將最終結果&#xff08;成功/失敗&#xff09;推送給客戶端并展示。核心方案是&#xff1a;“前端發起請求→后…

php apache無法接收到Authorization header

Apache 默認不傳遞 Authorization頭到后端環境&#xff08;如 PHP&#xff09;。其表現是&#xff1a;print_r($_SERVER)時&#xff0c; 沒有 [Authorization] &#xff1a;Array ([Accept-Language] > zh,en;q0.9,zh-CN;q0.8,en-US;q0.7[Accept-Encoding] > gzip, defla…

當我們想用GPU(nlp模型篇)

在個人設備上“把 GPU 真正用起來”做 NLP&#xff0c;分五步&#xff1a;準備 → 安裝 → 驗證 → 訓練/推理 → 踩坑排查。下面每一步都給出可復制命令和常見錯誤。 ────────────────── 1. 硬件準備 ? 一張 NVIDIA GPU&#xff0c;算力 ≥ 6.1&#xff08…

CryptSIPVerifyIndirectData函數分析

可以使用此函數從SIP接口對應的文件中提取簽名信息 CryptSIPVerifyIndirectData&#xff1a;將當前文件的哈希結果做為“指紋”&#xff0c;并與從CryptSIPGetSignedDataMsg中提取的簽名信息進行比較。 如果哈希結果相同&#xff0c;則意味著當前文件與之前簽名的文件相同&…

20250823解決榮品RD-RK3588-MID開發板在充電的時候大概每10s屏幕會像水波紋閃爍一下

20250823解決榮品RD-RK3588-MID開發板在充電的時候大概每10s屏幕會像水波紋閃爍一下 2025/8/23 17:58【結論】&#xff1a;使用直流電源供電&#xff0c;給電池【快速】充電&#xff0c;但是直流電源的電壓穩定&#xff0c;電流抖動導致的&#xff01;那個是2.4G 已經知道了我司…

CANN安裝

前提條件 請參考本文檔正確安裝和使用CANN軟件,不建議將CANN安裝在共享磁盤后,通過掛載的方式使用CANN,因為CANN對文件系統有文件鎖的依賴,部分共享存儲不支持文件鎖,可能導致任務拉起失敗。 root用戶和非root用戶安裝CANN軟件包的步驟一致,當前示例步驟以非root用戶為例…

docker的基礎配置

目錄 數據卷 數據卷容器 端口映射與容器互聯 互聯機制實現便捷互訪(基于容器搭建論壇) 數據卷 1.創建數據卷 [rootopenEuler-1 /]# docker volume create test test [rootopenEuler-1 /]# docker volume ls DRIVER VOLUME NAME local test [ro…

VSCode Import Cost:5 分鐘學會依賴瘦身

一句話作用&#xff1a;在代碼里 import 時&#xff0c;實時顯示包大小&#xff0c;幫你一眼揪出體積炸彈。1?? 30 秒安裝 & 啟動 打開 VSCode → 擴展商店搜索 Import Cost → 安裝重啟 VSCode&#xff0c;立即生效&#xff0c;零配置。2?? 使用方式&#xff08;開箱即…

TCP/UDP詳解(一)

UDP報文源端口16bit 目的端口16bit校驗和checksum16bit 總長度16bit--------------------------------------------------------------------------------------------------------------------------源目端口用于標識應用層協議&#xff0c;分為知名端口&#x…

數據庫優化提速(一)之進銷存庫存管理—仙盟創夢IDE

從存儲過程到通用 SQL&#xff1a;進銷存系統的數據操作優化在進銷存系統的開發與維護中&#xff0c;數據庫查詢語句的編寫方式對系統的性能、兼容性和可維護性有著深遠影響。本文將圍繞給定的三段 SQL 代碼展開&#xff0c;深入探討將存儲過程轉換為通用 SQL 在進銷存場景下的…

Redis面試精講 Day 28:Redis云原生部署與Kubernetes集成

【Redis面試精講 Day 28】Redis云原生部署與Kubernetes集成 在當今微服務與容器化浪潮中&#xff0c;Redis作為高性能緩存和消息中間件&#xff0c;已從單機部署逐步演進為云原生環境下的核心組件。Day 28 聚焦“Redis云原生部署與Kubernetes集成”&#xff0c;深入解析如何在…

leetcode刷題記錄03——top100題里的6道簡單+1道中等題

leetcode刷題記錄03——top100題里的6道簡單1道中等題上一篇博客&#xff1a; leetcode刷題記錄01——top100題里的7道簡單題 leetcode刷題記錄02——top100題里的7道簡單題 有效的括號 看懂需要用棧了&#xff0c;但是不知道怎么去寫&#xff0c;看了題解mark下正確答案。 cla…

求單位球內滿足邊界條件 u = z3 的調和函數

問題 6&#xff1a;在區域 {x2y2z2≤1}\{x^{2}y^{2}z^{2}\leq 1\}{x2y2z2≤1} 內找到一個調和函數 uuu&#xff0c;使得在邊界 x2y2z21x^{2}y^{2}z^{2}1x2y2z21 上&#xff0c;uuu 等于 gz3gz^{3}gz3。 提示&#xff1a;根據第8.1節&#xff0c;解必須是一個三次調和多項式&…

AAA 服務器與 RADIUS 協議筆記

一、AAA 服務器概述1. 核心定義AAA 是認證&#xff08;Authentication&#xff09;、授權&#xff08;Authorization&#xff09;和計費&#xff08;Accounting&#xff09; 的簡稱&#xff0c;是網絡安全領域中實現訪問控制的核心安全管理機制&#xff0c;通過整合三種服務確保…

Vue3源碼reactivity響應式篇之數組代理的方法

概覽 vue3中對于普通的代理包含對象和數組兩類&#xff0c;對于數組的方法是重寫了許多方法&#xff0c;具體實現參見packages\reactivity\src\arrayInstrumentations.ts arrayInstrumentations實際上就是一個對象&#xff0c;對象的屬性就是數組的方法&#xff0c;屬性值就是重…

如何玩轉K8s:從入門到實戰

一、K8S介紹及部署 1 應用的部署方式演變 部署應用程序的方式上&#xff0c;主要經歷了三個階段&#xff1a; 傳統部署&#xff1a;互聯網早期&#xff0c;會直接將應用程序部署在物理機上 優點&#xff1a;簡單&#xff0c;不需要其它技術的參與 缺點&#xff1a;不能為應用…

綜合測驗:配置主dns,dhcp,虛擬主機,nfs文件共享等

綜合實驗(所有設備關閉防火墻和selinux)在appsrv上部署主dns&#xff0c;為example.com提供域名解析 安裝bind bind-chroot rootappsrv ~]# yum install bind bind-chroot -y編輯主配置文件&#xff0c;全局配置文件&#xff0c;正向解析文件 [rootappsrv ~]# vim /etc/named.c…