CMake實例講解

實例目錄

1、CMake helloworld 簡單工程
2、CMake輸出自定義信息
3、CMake項目的二進制目錄和源目錄
4、CMake構建靜態庫和動態庫
5、CMake添加工程子目錄
6、CMake指定目標保存目錄
7、CMake定義安裝
8、CMake設置生成庫的屬性
9、CMake調用外部庫
10、CMake查詢主機系統特定信息
11、CMake設置版本最低要求
12、CMake策略管理
13、CMake配置文件
14、CMake函數
15、CMake宏
16、CMake的foreach循環
17、CMake的條件判斷指令
18、CMake的while循環指令
19、CMake的math指令
20、CMake列表的操作之一(讀取列表)
21、CMake列表的操作之二(搜索列表)
22、CMake列表的操作之三(修改列表)
23、CMake列表的操作之四(排序列表)
24、CMake列表的操作之五(轉換列表)
25、CMake的變量監測
26、CMake添加項目樹之外的子目錄
























一、 CMake helloworld 簡單工程

  1. 新建文件 HelloCMake.cpp

#include

int main(int argc, char** argv) {

std::cout <<"Hello CMake!" << std::endl;
return 0;

}

// 編輯CMakeLists.txt文件, 這個文件是CMake構建定義文件,名字必須是這個名字,大小寫也要一致,
// 如果項目有多個目錄,則每個目錄下都要有一個CMakeLists.txt文件。

  1. CMakeLists.txt

project(HelloCMake)
set(SRC_LIST HelloCMake.cpp)
add_executable(hello_cmake $(SRC_LIST))

project函數, 用於定義項目名稱,這裏定義項目名稱爲HelloCMake,CMake 裏函數是不分大小寫的,寫成PROJECT也是可以的

set函數, 用於定義變量並賦值,這裏定義一個SRC_LIST變量,並且賦值爲HelloCMake.cpp

add_executable函數, 用於添加一個可執行的目標,第一個參數是生成的目標文件名(即可執行的程序名),
第二個參數是依賴的源文件,這裏傳入了SRC_LIST, 引用變量的內容要用${VAR_NAME}這樣的語法

使用cmake 生成Makefile, 傳入的參數爲一個點(.), 這個點是指當前目錄,也就是告訴cmake,
從當前目錄下的CMakeLists.txt文件作爲入口,如果沒有CMakeLists.txt,則會報錯。

執行完cmake命令後,沒有報錯的情況下,除了生成了Makefile, 還生成了一些其他的文件,其它的文件可以先不管它。

有了Makefile, 就可以執行make命令來生成程序了, 最終生成的程序名稱爲在CMakeLists.txt裏指定的名稱, 即hello_cmake

//---------------------------------------------------------

二、CMake輸出自定義信息

CMake輸出自定義信息,可以使用message函數 調用格式爲:

message([SEND_ERROR | STATUS | FATAL_ERROR] “要輸出的信息”)
SEND_ERROR , STATUS , FATAL_ERROR 三個參數有什麼區別? 不給參數又是如何?

  1. 新建一個build 子目錄,然後進入build子目錄來執行cmake, 這樣cmake生成的文件就會在build子目錄中,
    從而不會"污染"了源文件。

  2. 修改一下 CMakeLists.txt文件, 演示message的使用
    任何參數都不給,直接輸出
    message(“This is a message”)

    傳遞STATUS參數
    message(STATUS “This is another message”)

  3. 執行cmake, 後面跟着兩個點(…) ,兩個點是代表上一層目錄,即告訴cmake到上一層目錄去找CMakeLists.txt

    沒有給message傳參數的,直接就輸出了給定的信息。而傳遞了STATUS參數的,會在輸出的信息前面加–

    輸出:
    This is a message
    – This is another message

    cmake 生成的文件全在build這個子目錄裏,可以直接全部刪除也不會對源文件有影響。

  4. 再看 SEND_ERROR是怎樣的?
    message(SEND_ERROR “This is SEND_ERROR message”)

    輸出:
    CMake Error at CMakeLists.txt 5 (message):
    This is SEND_ERROR message

    從輸出的信息來看,SEND_ERROR 發送了錯誤信息, 並且跳過了生成的過程

  5. 再看 FATAL_ERROR是怎樣的?
    message(FATAL_ERROR “This is FATAL_ERROR message”)

    輸出:
    CMake Error at CMakeLists.txt 6 (message):
    This is FATAL_ERROR message

    從輸出的信息來看好像跟SEND_ERROR 沒什麼區別, 但它們本質上是有區別的,SEND_ERROR 只是發送錯誤,
    跳過生成過程,它後面的語句還會執行; 而FATAL_ERROR , 從字面可以知道,
    致命錯誤,會立即中止過程,它後面的語句也不再執行。

//---------------------------------------------------------

三、CMake項目的二進制目錄和源目錄

CMake 會爲每個項目的二進制目錄和源目錄隱式生成兩個變量:
<project_name>_BINARY_DIR
<project_name>_SOURCE_DIR

同時也存在這樣的兩個變量:
PROJECT_BINARY_DIR = <project_name>_BINARY_DIR
PROJECT_SOURCE_DIR = <project_name>_SOURCE_DIR

  1. 這裏項目名稱爲HelloCMake, 因此隱式變量爲:
    HelloCMake_BINARY_DIR
    HelloCMake_SOURCE_DIR

    同時查看一下:
    PROJECT_BINARY_DIR
    PROJECT_SOURCE_DIR

    cmake 代碼:
    message(STATUS ${HelloCMake_BINARY_DIR})
    message(STATUS ${PROJECT_BINARY_DIR})
    message(STATUS ${HelloCMake_SOURCE_DIR})
    message(STATUS ${PROJECT_SOURCE_DIR})



    輸出:
    – E:/ProjectCMake/3、CMake項目的二進制目錄和源目錄/build
    – E:/ProjectCMake/3、CMake項目的二進制目錄和源目錄/build
    – E:/ProjectCMake/3、CMake項目的二進制目錄和源目錄
    – E:/ProjectCMake/3、CMake項目的二進制目錄和源目錄



    PROJECT_BINARY_DIR 對應的是執行cmake 命令所在的目錄,這裏是build 這個子目錄的路徑,
    而PROJECT_SOURCE_DIR 則對應源文件的目錄,這裏是build 的上一層目錄,
    即CMakeList.txt對應的目錄。

    結果也顯示了:
    HelloCMake_BINARY_DIR = PROJECT_BINARY_DIR
    HelloCMake_SOURCE_DIR = PROJECT_SOURCE_DIR

    實際使用時,用PROJECT_XXX這個變量比較好,因爲這兩個變量不會因爲項目名稱的改變而改變,
    <project_name>_XXX 這個變量則要隨項目名稱的改變而更改

//---------------------------------------------------------

四、CMake構建靜態庫和動態庫

CMake 中要生成靜態庫或動態庫,可以使用
add_library 指令(函數) ,該指令使用方法:

add_library(lib_name [SHARED|STATIC|MODULE] [EXCLUDE_FROM_ALL] src1 src2 … srcN)

參數說明: lib_name : 指定生成庫的名稱

SHARED: 生成動態庫
STATIC: 生成靜態庫

MODULE: 使用dyld(the dynamic link editor, 蘋果的動態鏈接器)的系統有效,如果不支持dyld,
則爲SHARED

EXCLUDE_FROM_ALL: 指定此參數,則該庫默認情況下不被構建,當有其他組件用到該庫或手動構建時,
才被構建

src1 src2 … srcN: 用於構建庫用到的源文件

  1. 新建程序文件
    先來個頭文件 HelloLibrary.h, 聲明一個函數

    #ifndef HELLO_LIBRARY
    #define HELLO_LIBRARY

    #include
    void hello_library();

    #endif // HELLO_LIBRARY

    頭文件對應的源文件,實現頭文件裏聲明的函數,簡單的輸出字符串

    #include “HelloLibrary.h”

    void hello_library(){
    std::cout << “Hello Shared Library!” << std::endl;
    }

    主程序 ,用於調用生成庫裏的函數

    #include “HelloLibrary.h”
    int main(){
    hello_library();
    return 0;
    }



  2. 編輯CMakeLists.txt

    cmake_minimum_required指令,用於檢測CMake的版本,指定最小版本至少爲3.8

    項目名稱爲HelloLibrary

    add_library指令,指定庫名稱爲hello_library,由於指明生成SHARED庫(動態庫),
    實際生成的庫文件名爲libhello_library.dll ,用於生成該庫的源文件爲HelloLibrary.cpp

    include_directories指令,用於增加包含頭文件的路徑,這裏是把項目源目錄增加到包含頭文件的路徑中

    add_executable 指令,用於指定生成可執行文件,生成的可執行文件名爲hello_main ,
    用到的源文件爲Main.cpp

    target_link_libraries指令,指定某個目標的生成所依賴的庫,這裏要生成的可執行文件hello_main要
    依賴hello_library這個庫,也就是add_library指令生成的那個庫

    cmake_minimum_required(VERSION 3.8)
    project(HelloLibrary)
    add_library(hello_library SHARED HelloLibrary.cpp)
    include_directories(${PROJECT_SOURCE_DIR})
    add_executable(hello_main Main.cpp)
    target_link_libraries(hello_main hello_library)




    一共四個文件, 一個頭文件,用於聲明函數,一個源文件用於生成庫,一個源文件用於生成可執行文件,
    最後一個是CMake工程文件

  3. 修改CMakeLists.txt 項目文件,add_library 指令中,把SHARED 改爲STATIC
    SHARED : 用於生成動態庫 win 下爲 dll文件,linux 下爲 so文件
    STATIC : 用於生成靜態庫 win 下爲 lib文件,linux 下爲 a 文件

//---------------------------------------------------------

五、CMake添加工程子目錄

CMake 提供了ADD_SUBDIRECTORY 指令,用於向當前工程添加子目錄,並且可以指定中間二進制和目標二進制文件
存放的位置,還可以將指定目錄排除出編譯過程(這個功能這裏並沒有演示)。

  1. 創建項目的源文件目錄 src, 並編輯cmakelists.txt
    cmake_minimum_required(VERSION 3.8)
    project(SubDirectory)
    add_subdirectory(src)


    指定最小版本 3.8
    項目名稱爲SubDirectory
    add_subdirectory 指令,指定源文件子目錄爲src

  2. 創建程序文件
    main.cpp 文件

    #include
    int main(){
    std::cout << “This is sub directory project!” << std::endl;
    return 0;
    }



  3. src 子目錄也要有自己的CMakeLists.txt
    add_executable(sub_directory main.cpp)

  4. 爲add_subdirectory 指令指定bin_dir ,爲bin,即編譯輸出到build/bin目錄下
    cmake_minimum_required(VERSION 3.8)
    project(SubDirectory)
    add_subdirectory(src bin)


//---------------------------------------------------------

六、CMake指定目標保存目錄

雖然ADD_SUBDIRECTORY 指令可以指定編譯輸出目錄,但是也可以通過SET指令來設置EXECUTABLE_OUTPUT_PATH
和LIBRARY_OUTPUT_PATH 來指定最終目標的存放目錄(只是最終目標文件,編譯生成的中間文件不在此列)。

EXECUTABLE_OUTPUT_PATH : 最終可執行文件存放的目錄。
LIBRARY_OUTPUT_PATH : 最終的庫文件(靜態庫、共享庫) 存放的目錄。

  1. 新建src目錄,在源文件目錄中,新增相關的源文件,用於生成庫和可執行目標。

    SayHello.h 用於生成庫的頭文件
    #ifndef SAY_HELLO_H
    #define SAY_HELLO_H

    #include

    void say_Hello();

    #endif // SAY_HELLO_H

    SayHello.cpp 文件

    #include “SayHello.h”

    void say_Hello(){
    std::cout << “Say Hello!” << std::endl;
    }

    main.cpp 用於可執行目標
    #include “SayHello.h”

    int main(){
    std::cout << “This is outputPath project!” << std::endl;
    say_Hello(); // 調用將要生成的庫裏的函數

     return 0;
    

    }

  2. 編輯src/CMakeLists.txt
    設置EXECUTABLE_OUTPUT_PATH, 把可執行文件生成於項目編譯目錄下的bin子目錄
    設置LIBRARY_OUTPUT_PATH, 把庫文件生成於項目編譯目錄下的lib子目錄

    添加生成庫目標。名爲SayHello, 依賴源文件爲SayHello.cpp

    把源文件所在目錄加入包含頭文件目錄中,如果不加,會找不到SayHello.h 這個頭文件

    添加可執行目標,名稱爲OutputPath, 依賴的源文件爲main.cpp

    設置可執行目標的依賴庫,即OutputPath 依賴SayHello這個庫

    cmakelists.txt 代碼:
    set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
    set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)

    add_library(SayHello SayHello.cpp)
    include_directories(${PROJECT_SOURCE_DIR}/src)
    add_executable(OutputPath main.cpp)
    target_link_libraries(OutputPath SayHello)


  3. 編輯項目目錄下CMakeLists.txt
    把src 這個源文件目錄加入到項目工程中,第二個參數可以不給了,給了也沒用,因爲已經設置了輸出目錄。

    cmakelists.txt 代碼:
    cmake_minimum_required(VERSION 3.8)
    project(OutputPath)
    add_subdirectory(src)


  4. cmake 之後,在編譯目錄下生成了bin、lib 和src子目錄,其實bin和lib還是空的,src存放了中間文件
    生成目標,包括庫文件和可執行文件
    bin文件夾 : 可執行文件OutputPath.exe
    lib文件夾 : 庫SayHello.lib


//---------------------------------------------------------

七、CMake定義安裝

CMake 中 可通過INSTALL 指令來定義安裝規則,並配合CMAKE_INSTALL_PREFIX 變量來指定安裝的路徑。
即執行cmake命令時指定:

mac: cmake -DCMAKE_INSTALL_PREFIX= /tmp.
win: cmake -DCMAKE_INSTALL_PREFIX= D:/tmp

定義安裝的規則內容可以是可執行的二進制目標、動態庫(共享庫)、靜態庫、文件、目錄、腳本等。

定義安裝規則包括以下幾方面:
1. 二進制目標文件(可執行、動態庫、靜態庫)的安裝
2. 普通文件的安裝
3. 非目標可執行程序(如 腳本)的安裝。
4. 目錄的安裝。
5. 安裝時執行cmake腳本




  1. 二進制目標文件(可執行、動態庫、靜態庫)的安裝
    INSTALL(TARGETS targets… [[ARCHIVE|LIBRARY|RUNTIME]
    [DESTINATION

    ][PERMISSIONS permissions…]
    [CONFIGURATIONS [Debug|Release|…]]
    [COMPONENT ][OPTIONAL]][…])

    TARGETS: 後面的targets就是add_executable或add_library定義的目標,二進制目標或庫
    ARCHIVE|LIBRARY|RUNTIME: 分別指靜態庫、動態庫、可執行二進制目標
    DESTINATION

    : 定義要安裝到哪個路徑,結合CMAKE_INSTALL_PREFIX,
    路徑爲${CMAKE_INSTALL_PREFIX}/DESTINATION, 如果以/開頭,則從根目錄開始,這時CMAKE_INSTALL_PREFIX就不起作用了,所以一般寫相對路徑。

    PERMISSIONS: 指定權限
    Debug|Release:指定版本

  2. 舉個例子:
    INSTALL(TARGETS exe sharedlib staticlib
    RUNTIME DESTINATION bin
    LIBRARY DESTINATION lib
    ARCHIVE DESTINATION static_lib
    )




    exe 安裝到 C M A K E I N S T A L L P R E F I X / b i n 下 s h a r e d l i b 安 裝 到 {CMAKE_INSTALL_PREFIX}/bin 下 sharedlib 安裝到 CMAKEINSTALLPREFIX/binsharedlib{CMAKE_INSTALL_PREFIX}/lib 下
    staticlib 安裝到${CMAKE_INSTALL_PREFIX}/static_lib 下

  3. 普通文件的安裝
    INSTALL(FILES files… DESTINATION


    [PERMISSIONS permissions…]
    [CONFIGURATIONS [Debug|Release|…]]
    [COMPONENT]
    [RENAME][OPTIONAL]
    )
    用於安裝一般文件,可指定訪問權限,文件名是此指令所在路徑下的相對路徑。如果不定義權限PERMISSIONS,安裝後的權限爲
    : OWNER_WRITE OWNER_READ|GROUP_READ|WORLD_READ ,即644 權限






  4. 非目標可執行程序(如腳本)的安裝
    INSTALL(PROGRAMS files… DESTINATION


    [PERMISSIONS permissions…]
    [CONFIGURATIONS [Debug|Release|…]]
    [COMPONENT]
    [RENAME][OPTIONAL]
    )




    與普通文件安裝差不多,只是默認權限不一樣:
    OWNER_EXECUTE OWNER_WRITE OWNER_READ|GROUP_EXECUTE GROUP_READ|WORLD_EXECUTE WORLD_READ, 即 755權限

  5. 目錄的安裝
    INSTALL(DIRECTORY dirs… DESTINATION


    [FILE_PERMISSIONS permissions…]
    [DIRECTORY_PERMISSIONS permissions…]
    [USE_SOURCE_PERMISSIONS]
    [CONFIGURATIONS [Debug|Release|…]]
    [COMPONENT]
    [[PATTERN | REGEX]
    [EXCLUDE] [PERMISSIONS permissions…]] […]
    )







    DIRECTORY: 後面接的是所在源目錄的相對路徑,目錄後有沒有/,區別很大,如dir 與dir/是不一樣的, dir是將dir這個目錄安裝到目標路徑下的dir, 而dir/是將這個目錄下的內容安裝到目標目錄,但不包括這個目錄本身。

    PATTERN:使用正則表達式進行過濾
    PERMISSIONS: 用於指定PATTERN 過濾後的文件權限

  6. 舉個例子:
    INSTALL(DIRECTORY samples modules/ DESTINATION share
    PATTERN “TXT” EXCLUDE
    PATTERN “modules/*”
    PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ|GROUP_EXECUTE GROUP_READ
    )




    將samples目錄安裝到 C M A K E I N S T A L L P R E F I X / s h a r e 目 錄 下 , 將 m o d u l e s / 中 的 內 容 安 裝 到 {CMAKE_INSTALL_PREFIX}/share目錄下,將modules/中的內容安裝到 CMAKEINSTALLPREFIX/sharemodules/{CMAKE_INSTALL_PREFIX}/share目錄下。
    不包含目錄名爲TXT的目錄,對modules/目錄下的文件指定權限爲 OWNER_EXECUTE OWNER_WRITE OWNER_READ|GROUP_EXECUTE
    GROUP_READ

  7. 安裝時執行cmake 腳本
    INSTALL([[SCRIPT ][CODE ]][…])

    SCRIPT: 用於在安裝時調用cmake腳本文件,即 xxxx.cmake文件
    CODE: 行cmake指令, 要用雙引號,如:INSTALL(CODE “MESSAGE(“Sample install message.”)”)

  8. 創建示例工程
    新建src doc 文件夾 ,copyright, readme 兩個文件

    src/main.cpp 主程序文件
    #include

    int main(){
    std::cout << “customize install!” << std::endl;

     return 0;
    

    }

    src/cmakelists.txt

    set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
    add_executable(customize_install main.cpp)
    install(TARGETS customize_install DESTINATION CustomizeInstall/bin)

    設置可執行目標輸出的路徑
    生成可執行二進制
    指定這個可執行二進制要安裝到的目錄,這裏爲 /${CMAKE_INSTALL_PREFIX}/CustomizeInstall/bin

    項目主目錄下的CMakeLists,txt文件

    cmake_minimum_required(VERSION 3.8)
    project(CustomizeInstall)
    add_subdirectory(src bin)
    install(FILES copyright.txt readme.txt DESTINATION CustomizeInstall)
    install(PROGRAMS run_customize_install.sh DESTINATION CustomizeInstall/bin)
    install(DIRECTORY doc/ DESTINATION CustomizeInstall/share/doc)




    指定最小的CMake版本
    指定項目名稱
    把src源文件子目錄加入到項目中
    把copyright, readme 這兩個文件安裝到目錄: / C M A K E I N S T A L L P R E F I X / C u s t o m i z e I n s t a l l 下 把 r u n c u s t o m i z e i n s t a l l . s h ( 還 沒 有 創 建 ) 文 件 安 裝 到 目 錄 : / {CMAKE_INSTALL_PREFIX}/CustomizeInstall 下 把run_customize_install.sh (還沒有創建) 文件安裝到目錄:/ CMAKEINSTALLPREFIX/CustomizeInstallruncustomizeinstall.sh()/{CMAKE_INSTALL_PREFIX}/CustomizeInstall/bin下
    把doc/目錄下的文件(不包含這個目錄本身) 安裝到目錄: /${CMAKE_INSTALL_PREFIX}/CustomizeInstall/share/doc下



    //創建run_customize_install.sh , 簡單執行 ./customize_install
    //進入doc 子目錄,簡單來個文件代表爲文檔文件

    //執行cmake命令 , 指定CMAKE_INSTALL_PREFIX爲 E:/tmp
    // cmake -DCMAKE_INSTALL_PREFIX=E:/tmp …

    //執行make install 來安裝項目,提示信息看到所有的文件都按指定的目錄進行安裝了

    // 指定安裝 本質上時拷貝一些文件到一個指定目錄下, 這個功能可以用在生成庫時,拷貝頭文件和庫文件到指定目錄

//---------------------------------------------------------

八、CMake設置生成庫的屬性
指令SET_TARGET_PROPERTIES 可設置目標的屬性

SET_TARGET_PROPERTIES(target1 target2 ...
	PROPERTIES prop1 value1 prop2 value2 ...)

target1 target2: 指定要設置屬性的目標
prop1 value1: 指定屬性名和屬性值,這裏將演示 OUTPUT_NAME、VERSION、SOVERSION 三個屬性

有設置屬性的指令,自然也有獲取屬性的指令:
GET_TARGET_PROPERTY(VAR target prop)

VAR: 獲取的屬性存放的變量
target: 目標
prop: 要獲取的屬性名
  1. 編輯cmakelists.txt

    cmake_minimum_required(VERSION 3.8)
    project(LibraryProperty)
    add_subdirectory(src)

    指定最小的CMake版本
    項目名稱爲LibraryProperty
    添加src子目錄

  2. 添加src 子目錄 並添加要生成庫的頭文件和源文件

    linear.h

    #ifndef LINEAR_H
    #define LINEAR_H

    void linear_fit();

    #endif

    linear.cpp

    #include
    #include “linear.h”

    void linear_fit(){
    std::cout << “linear_fir called” << std::endl;
    }

    src/CMakeLists.txt

    set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
    add_library(linear SHARED linear.cpp)
    add_library(static_linear STATIC linear.cpp)

    設置生成庫的存放目錄, 爲編譯目錄下的lib 子目錄
    添加生成動態庫目標,目標名爲linear
    再添加生成靜態庫目標,目標名也是linear

  3. 上面生成兩個庫文件,但更期望生成的動態庫和靜態庫分別爲 liblinear.dll 和 liblinear.lib , 這樣才統一,而不是像現在這樣。

    修改 src/CMakeLists.txt

    set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
    add_library(linear SHARED linear.cpp)
    add_library(static_linear STATIC linear.cpp)

    set_target_properties(static_linear PROPERTIES OUTPUT_NAME “linear”)

    get_target_property(OUTPUT_VALUE static_linear OUTPUT_NAME)
    message(STATUS “OUTPUT_NAME = ${OUTPUT_VALUE}”)

    get_target_property(UNDEFINE_VALUE static_linear UNDEFINE_NAME)
    message(STATUS “UNDEFINE_NAME = ${UNDEFINE_VALUE}”)

    設置生成庫的存放目錄, 爲編譯目錄下的lib 子目錄
    添加生成動態庫目標,目標名爲linear
    再添加生成靜態庫目標,目標名也是linear
    設置靜態庫目標的輸出名稱爲 linear
    同時,獲取一下這個屬性值看看
    如果試圖獲取未定義的屬性,會發生什麼?
    (運行cmake 可以看到,如果屬性沒設置,得到的值爲NOTFOUND)
    (最後運行的結果 輸出的兩個庫文件名稱爲期待的結果)






  4. 對於動態庫,一般都有版本號,VERSION 爲版本號, SOVERSION 爲API版本號。

    修改 src/CMakeLists.txt

    set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
    add_library(linear SHARED linear.cpp)
    add_library(static_linear STATIC linear.cpp)

    set_target_properties(static_linear PROPERTIES OUTPUT_NAME “linear”)

    get_target_property(OUTPUT_VALUE static_linear OUTPUT_NAME)
    message(STATUS “OUTPUT_NAME = ${OUTPUT_VALUE}”)

    get_target_property(UNDEFINE_VALUE static_linear UNDEFINE_NAME)
    message(STATUS “UNDEFINE_NAME = ${UNDEFINE_VALUE}”)

    set_target_properties(linear PROPERTIES VERSION 1.0 SOVERSION 1)

    install(TARGETS linear static_linear LIBRARY DESTINATION lib ARCHIVE DESTINATION lib/ P R O J E C T N A M E ) i n s t a l l ( F I L E S l i n e a r . h D E S T I N A T I O N i n c l u d e / {PROJECT_NAME}) install(FILES linear.h DESTINATION include/ PROJECTNAME)install(FILESlinear.hDESTINATIONinclude/{PROJECT_NAME})

    設置生成庫的存放目錄, 爲編譯目錄下的lib 子目錄
    添加生成動態庫目標,目標名爲linear
    再添加生成靜態庫目標,目標名也是linear
    設置靜態庫目標的輸出名稱爲 linear
    同時,獲取一下這個屬性值看看
    如果試圖獲取未定義的屬性,會發生什麼?
    (運行cmake 可以看到,如果屬性沒設置,得到的值爲NOTFOUND)
    (最後運行的結果 輸出的兩個庫文件名稱爲期待的結果)






    生成的動態庫帶有版本號, 並生成兩個鏈接文件

    設置安裝規則,動態庫安裝到/lib下,靜態庫安裝到/lib/LibraryProperty下
    把頭文件安裝到/include/LibraryProperty下

    // 執行cmake 時 指定 CMAKE_INSTALL_PREFIX 爲 E:/tmp
    // cmake -DCMAKE_INSTALL_PREFIX=E:/tmp …

//---------------------------------------------------------

九、CMake調用外部庫

INCLUDE_DIRECTORIES([AFTER|BEFORE]
[SYSTEM] dir1 dir2 …)

添加頭文件搜索路徑,多個路徑用空格分開,如果路徑有空格,路徑用雙引號引起來。
默認是添加到搜索路徑的後面,可以通過AFTER和BEFORE來控制是添加到前面還是後面。
也可以把CMake變量 CMAKE_INCLUDE_DIRECTORIES_BEFORE設置爲on來把路徑添加到前面。

LINK_DIRECTORIES(dir1 dir2 …)

添加非系統(非標準)的庫搜索路徑。此指令要放在目標之前,比如要放在add_executable或add_library之前。

LINK_LIBRARIES([item1 [item2 […]]]
[[debug|optimized|general]] ]…)

爲目標添加需要鏈接的庫。此指令要放在目標之前,比如要放在add_executable或add_library之前。指定的庫名稱要全路徑。

對於不是系統內部的庫,CMake如何調用?

CMake主要提供了以下幾個指令來實現:
INCLUDE_DIRECTORIES([AFTER|BEFORE]
[SYSTEM] dir1 dir2 …)

LINK_DIRECTORIES(dir1 dir2 …)

LINK_LIBRARIES([item1 [item2 […]]]
[[debug|optimized|general]] ]…)

TARGETS_LINK_LIBRARIES(target lib1
<debug|optimized> lib2 …)

  1. 編輯cmakelists.txt

    cmake_minimum_required(VERSION 3.8)
    project(ExternalProperty)
    add_subdirectory(src)

    指定最小的CMake版本
    項目名稱爲ExternalProperty
    添加src子目錄

  2. 新建src 目錄 添加main.cpp CMakeLists.txt ,這裏鏈接的是靜態庫

    main.cpp

    #include
    #include “linear.h”

    int main(){
    std::cout << “Main called!” << std::endl;
    linear_fit();

     return 0;
    

    }

    CMakeLists.txt

    set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
    include_directories(E:/tmp/include/LibraryProperty)
    link_libraries(E:/tmp/lib/LibraryProperty/linear.lib)
    add_executable(ExternalLibrary main.cpp)


    設置二進制目標文件的輸出目錄
    把對應頭文件的目錄加進來 (使用 八、CMake設置生成庫的屬性 )
    添加鏈接庫,注意,這是放在add_executable前面的,用了全路徑
    添加二進制目標,依賴Main.cpp 這個源文件


  3. 修改 CMakeLists.txt,另一種鏈接庫方式

    CMakeLists.txt

    set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
    link_directories(E:/tmp/lib/LibraryProperty)
    add_executable(ExternalLibrary main.cpp)
    include_directories(E:/tmp/include/LibraryProperty)
    target_link_libraries(ExternalLibrary linear.dll)



    使用link_directories 指定庫對應的目錄
    使用target_link_libraries ,指定目標和庫名稱

//---------------------------------------------------------

十、CMake查詢主機系統特定信息
CMake 提供下面的指令用於查詢主機系統特定信息:
cmake_host_system_information(RESULT QUERY …)

: 用於保存查詢到的信息的變量
: 查詢信息的關鍵詞,多個關鍵詞之間用空格分開,返回到中多個信息之間用分號分隔

可查詢的信息關鍵詞有很多,如:INSTALL
NUMBER_OF_LOGICAL_CORES,
NUMBER_OF_PHYSICAL_CORES,
HOSTNAME,
FQDN,
TOTAL_VIRTUAL_MEMORY,
AVAILABLE_VIRTUAL_MEMORY,
TOTAL_PHYSICAL_MEMORY,
AVAILABLE_PHYSICAL_MEMORY…
等等








  1. 編輯cmakelists.txt 演示一下各個關鍵詞查詢的信息

    cmake_minimum_required(VERSION 3.8)
    project(HostSystemInfo)
    add_subdirectory(src)

  2. 新建src目錄 添加main.cpp CMakeLists.txt文件
    main.cpp

    #include

    int main(){
    std::cout << “Host System Info Output!” << std::endl;

     return 0;
    

    }

    CMakeLists.txt

    cmake_host_system_information(RESULT NumberOfLogicalCores QUERY NUMBER_OF_LOGICAL_CORES)
    message(STATUS “NUMBER_OF_LOGICAL_CORES = ${NumberOfLogicalCores}”)

    cmake_host_system_information(RESULT NumberOfPhysicalCores QUERY NUMBER_OF_PHYSICAL_CORES)
    message(STATUS “NUMBER_OF_PHYSICAL_CORES = ${NumberOfPhysicalCores}”)

    cmake_host_system_information(RESULT HostName QUERY HOSTNAME FQDN)
    message(STATUS “HOSTNAME,FQDN = ${HostName}”)

    cmake_host_system_information(RESULT VirtualMemory QUERY TOTAL_VIRTUAL_MEMORY AVAILABLE_VIRTUAL_MEMORY)
    message(STATUS “TOTAL_VIRTUAL_MEMORY,AVAILABLE_VIRTUAL_MEMORY = ${VirtualMemory}”)

    cmake_host_system_information(RESULT PhysicalMemory QUERY TOTAL_PHYSICAL_MEMORY AVAILABLE_PHYSICAL_MEMORY)
    message(STATUS “TOTAL_PHYSICAL_MEMORY,AVAILABLE_PHYSICAL_MEMORY = ${PhysicalMemory}”)

    cmake_host_system_information(RESULT Is64Bit QUERY IS_64BIT)
    message(STATUS “IS_64BIT = ${Is64Bit}”)

    cmake_host_system_information(RESULT HasFPU QUERY HAS_FPU)
    message(STATUS “HAS_FPU = ${HasFPU}”)

    cmake_host_system_information(RESULT processor QUERY PROCESSOR_NAME PROCESSOR_DESCRIPTION)
    message(STATUS “PROCESSOR_NAME, PROCESSOR_DESCRIPTION = ${processor}”)

    cmake_host_system_information(RESULT os QUERY OS_NAME OS_RELEASE OS_VERSION OS_PLATFORM)
    message(STATUS “OS_NAME,OS_RELEASE,OS_VERSION,OS_PLATFORM = ${os}”)

    set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
    add_executable(HostSystemInfo Main.cpp)

    查詢NUMBER_OF_LOGICAL_CORES, 邏輯核心數
    查詢NUMBER_OF_PHYSICAL_CORES, 物理核心數
    查詢HOSTNAME和FQDN, 主機名和主機域名全稱
    查詢TOTAL_VIRTUAL_MEMORY 和 AVAILABLE_VIRTUAL_MEMORY,總虛擬內存和可用虛擬內存
    查詢TOTAL_PHYSICAL_MEMORY 和 AVAILABLE_PHYSICAL_MEMORY,總物理內存和可用物理內存
    查詢IS_64BIT ,是否爲64位
    查詢HAS_FPU, 處理器是否有浮點單元
    查詢PROCESSOR_NAME 和 PROCESSOR_DESCRIPTION , 處理器名稱和處理器全描述






    查詢OS_NAME、OS_RELEASE、 OS_VERSION 和 OS_PLATFORM,操作系統相關的信息
    設置可執行二進制輸出目錄
    添加可執行二進制生成目標

    // cmake 之後,即可看到輸出的相關信息

//---------------------------------------------------------
十一、CMake設置版本最低要求

cmake_minimum_required 指令: 

cmake_minimum_required(VERSION <min> [...<max>][FATAL_ERROR])

<min> 和 <max> 指定CMake的版本號,版本號的格式爲: major.minor[.patch[.tweak]]

[...<max>]說明: 這裏的...就真的是三個點,直接寫三個點

如果安裝的CMake版本低於指定的<min>, 則停止項目的處理並報錯。

如果指定了...<max> ,則會影響cmake_policy(VERSION <min>[...<max>]) 指定的策略

在CMake3.12之前的版本,...三個點被認爲是版本號組成的各部分分隔符。因此,如果CMake的版本小於3.12, ... <max>
部分會被忽略,於是對應的策略行爲會保持3.12之前那種基於<min>的形式。

FATAL_ERROR: 在CMake2.6 或更高版本中,此參數會被忽略,因此傳不傳都是一樣的。
  1. cmake_minimum_required() 使用注意:
    a. 在頂層的CMakeLists.txt最開始調用,甚至先於project()調用,這樣可以一開始建立版本策略,從而對於一些受策略影響
    的指令比較友好。
    b. 如果在function()裏調用, 則不要期望能在全局調用的效果,畢竟會受到函數域的限制。


  2. 編輯CMakeLists.txt

    cmake_minimum_required(VERSION 3.18.5)
    #cmake_minimum_required(VERSION 3.8)
    #cmake_minimum_required(VERSION 3.8…3.17)

    指定最小版本爲3.18.5 ,運行會報錯
    指定最小版本爲3.8 ,運行正確
    指定最小版本爲3.8, 最大版本爲 3.17 ,運行正確

//---------------------------------------------------------

十二、CMake策略管理

CMake 的策略管理用於維護版本的向後兼容,從版本2.6開始引入。

每個新發布版本一般都會引入一些新的策略,每個策略都有一個標識號,格式爲CMP,對應四個0到9的整數。
每個策略都在文檔中描述了OLD和NEW的行爲,以及引入的原因。

CMake的策略是一種棄用機制,並不是可靠特性的切換,因此,策略的OLD行爲一般不可取,因爲在未來版本中可能被拋棄。

  1. CMake 可以通過版本來設置策略,指令爲
    cmake_policy(VERSION […])

    : 最小版本,至少爲2.4, 至多爲當前安裝的版本號
    : 最大版本,至少爲, 可以大於當前安裝版本號
    如果當前安裝版本小於3.12,…被認爲是版本組成的分隔符,從而會忽略…,因此會按3.12之前的版本基於
    的策略來執行。


    調用cmake_minimum_required 會隱式調用cmake_policy, 因此,如果沒有特別的策略要指定,可以直接通過調用
    cmake_minimum_required就夠了。

  2. CMake 可以顯示設置策略的行爲,爲OLD或爲New
    cmake_policy(SET CMP NEW)
    cmake_policy(SET CMP OLD)

    OLD 行爲爲棄用定義,在未來的版本中可能被移除。

  3. CMake獲取策略的行爲
    cmake_policy(SET CMP )
    獲取指定的策略行爲是OLD還是NEW, 的值設爲OLD 或NEW, 如果指定的策略沒有設置,則爲空。

  4. CMake的策略棧
    cmake_policy(PUSH)
    cmake_policy(POP)
    CMake 用棧來保存策略設置,cmake_policy的操作隻影響棧頂。對每一個子目錄,策略棧自動管理新入口,從而保證孩子
    目錄的父目錄和兄弟目錄不被破壞。
    對於include()和find_package()指令的腳本,也有新的入口,除非指定了NO_POLICY_SCOPE選項。




  5. 編輯CMakeLists.txt

    cmake_minimum_required(VERSION 3.8)
    #cmake_policy(VERSION 2.3)
    cmake_policy(VERSION 2.4)
    project(policy)


    指定最小版本爲 3.8
    策略最小版本爲 2.3, 運行會報錯
    策略最小版本爲 2.4, 運行正常

//---------------------------------------------------------

十三、CMake配置文件

CMake 通過以下指令來複制一個文件到指定地方,並可修改這個文件的內容,也就是配置文件。修改的內容爲輸入文件中
以@VAR@或${VAR} 指定的CMake變量佔位符, 佔位符被相應變量的內容替換:

configure_file(
[COPYONLY][ESCAPE_QUOTES][@ONLY]
[NEWLINE_STYLE [UNIX|DOS|WIN32|LF|CRLF]])

: 要複製的文件,如果用的是相對路徑,則基於CMAKE_CURRENT_SOURCE_DIR目錄

: 要複製到的目標,可以指定文件名或目錄,如果是已經存在的目錄,則用的文件名。
如果用相對路徑來指定,則基於CMAKE_CURRENT_BINARY_DIR目錄
COPYONLY:原封不動複製文件,不作任何的變量替換
ESCAPE_QUOTES: 迴避反斜槓轉義,即C分格對的轉義
@ONLY: 只替換@VAR@的變量,$(VAR)的不作替換
NEWLINE_STYLE: 輸出文件中換行的方式。UNIX和LF是以\n換行,而DOS, WIN32和CRLF是以\r\n換行。
因爲輸出文件與輸入文件內容有變動,所以不能與COPYONLY一起使用。





  1. 幾種替換說明:

    a. 輸入文件中的內容爲:
    #cmakedefine VAR …
    如果VAR 被定義,替換的內容爲:
    #define VAR …
    如果VAR未被定義,則替換爲:
    /* #undef VAR */




    b. 輸入文件中的內容爲:
    #cmakedefine01 VAR
    如果VAR被定義,替換的內容爲:
    #define VAR 1
    否則替換的內容爲:
    #define VAR 0




    c. ESCAPE_QUOTES的使用
    輸入文件中的內容爲:
    “@QUOTE@”
    CMakeLists.txt 有定義:
    set(QUOTE ““bb””)
    如果不使用ESCAPE_QUOTES,則輸入文件中的佔位符被替換爲 ““bb””, 如果使用了 ESCAPE_QUOTES,則替換爲"“bb”"




  2. 編輯input.h 文件

    #cmakedefine VAR 3.14
    #cmakedefine VAR1 “Good”
    #cmakedefine01 VAR3
    #cmakedefine01 VAR4
    #cmakedefine NAME “@NAME@”
    #cmakedefine NEW_NAME “${NEW_NAME}”
    #cmakedefine QUOTE “@QUOTE@”





    #include

    void $(my_print)(){
    std::cout << “print something” <<std::endl;
    }

    //#cmakedefine 的替換
    //#cmakedefine01 的替換
    //#cmakedefine NAME “@NAME@” 變量替換@類型
    //#cmakedefine NEW_NAME " N E W N A M E " 變 量 替 換 {NEW_NAME}" 變量替換 NEWNAME"{VAR}類型
    //#cmakedefine QUOTE “@QUOTE@” 用於演示 ESCAPE_QUOTES 的替換



    // void $(my_print)() 隨意位置的替換

  3. 編輯cmakelists.txt 文件
    cmake_minimum_required(VERSION 3.8)
    project(ConfigureFile)

    set(VAR1 123)
    set(VAR3 “abc”)

    set(NAME “haha”)
    set(NEW_NAME “new haha”)

    set(QUIET ““bb””)
    set(my_print “print_something”)

    configure_file(input.h input_default.h)
    configure_file(input.h input_copyonly.h COPYONLY)
    configure_file(input.h input_escape_quotes.h ESCAPE_QUOTES)
    configure_file(input.h input_at_only.h @ONLY)
    configure_file(input.h input_newline_style_dos.h NEWLINE_STYLE DOS)
    configure_file(input.h input_newline_style_unix.h NEWLINE_STYLE LF)




    定義VAR1, 注意沒有定義VAR
    定義NAME, NEW_NAME
    定義QUOTE, my_print

    按可用的參數分別輸出不同的配置文件,包括默認的、COPYONLY、ESCAPE_QUOTES、@ONLY、 NEWLINE_STYLE

  4. 執行cmake
    生成文件 input_at_only.h input_copyonly.h input_default.h 等等…其文件中都有相應的替換

//---------------------------------------------------------

十四、CMake函數

CMake 中用一下指令定義函數:
function(<name> [<arg1> ...])
	<commands>
endfunction()

function指令, 指定函數的名稱,參數名稱
endfunction指令,用於結束函數的定義,可以傳入與function指令中同樣的函數名稱作爲參數,也可以不傳。

這兩個指令中間就是函數要執行的命令。
  1. 函數的調用:
    如果定義的函數爲:
    function(func)

    endfunction



    則可以這樣調用:
    func()
    FUNC()
    不區分大小寫


  2. 函數中變量的作用域:
    a. 在函數中調用set(var xxxx)
    則var在函數的作用域內的值爲xxxx,如果var在函數的父層作用域有定義,值爲yyyy,則出了函數作用域,
    var的值仍然爲yyyy,並不會變爲xxxx。


    b. 在函數中調用set(var xxxx PARENT_SCOPE)
    在函數的父層作用域中設置var的值爲xxxx,如果在函數調用之前,var存在,值爲yyyy,則在函數中var的值爲yyyy,
    並不會變成xxxx,調用函數之後,var的值就變成xxxx。如果調用之前var不存在,則在函數內var也是不存在。

  3. 函數的參數:
    a. ARGC : 參數的個數
    b. ARGV0,ARGV1,ARGV2, …,ARGV#:第#個參數的值
    c. ARGV: 所有傳入的參數值的列表
    d. ARGN: 如果在定義函數時,顯式的指定了兩個參數,而在調用時,傳入了五個參數,
    則後面的三個參數的值就存在在這個變量裏




  4. 編輯cmakelists.txt

    cmake_minimum_required(VERSION 3.8)
    project(Function)

    function(func1)
    message(STATUS “func1 called”)
    endfunction(func1)

    message(STATUS “======================”)
    func1()
    FUNC1()

    set(Var1 “Var1: init”)

    function(func2)
    set(Var1 “Var1: value in func2”)
    message(STATUS ${Var1})
    endfunction()


    message(STATUS “======================”)
    message(STATUS “Var1 before call func2: ${var1}”)
    func2()
    message(STATUS “Var1 after call func2: ${var1}”)


    function(func3)
    set(Var1 “Var1 : Value in func3” PARENT_SCOPE)
    message(STATUS ${Var1})
    set(Var2 “Var2 : init in func3” PARENT_SCOPE)
    message(STATUS ${Var2})
    endfunction()




    message(STATUS “======================”)
    message(STATUS “Var1 before call func3: ${var1}”)
    message(STATUS “Var2 before call func3: ${var2}”)
    func3()
    message(STATUS “Var1 after call func3: ${var1}”)
    message(STATUS “Var2 after call func3: ${var2}”)




    function(func4 arg1 arg2)
    message(STATUS “arg1 = ${arg1}”)
    message(STATUS “arg2 = ${arg2}”)

    message(STATUS "ARGC = ${ARGC}")
    message(STATUS "${ARGV0} - ${ARGV1}")
    message(STATUS "ARGV = ${ARGV}")
    message(STATUS "ARGN = ${ARGN}")
    message(STATUS "ARGV2 = ${ARGV2}")
    

    endfunction()

    message(STATUS “======================”)

    func4(“aaa” “bbb” “ccc” “ddd”)

    定義一個函數

    調用函數,函數名稱不區分大小寫
    設置變量Var1

    設置變量var1 在函數中的值
    查看Var1在調用func2之前的值
    調用函數func2
    查看Var1在調用func2之後的值


    在func3中設置Var1在函數的上一層作用域的值

    查看Var1在func3中的值
    在func3中設置Var2在父作用域中的值
    查看Var2在func3中的值

    調用func3之前,看一下Var1和Var2都是什麼值
    調用func3
    再查看Var1 和Var2的值

    定義func4 ,帶兩個顯示參數
    查看兩個顯示參數的值
    查看傳入的參數個數
    通過ARGV#查看前兩個參數值
    查看所有參數值得列表
    查看未在函數定義中聲明的參數值列表
    通過ARGV2查看第三個參數值





    調用func4, 傳入4個參數

    // cmake 一下 查看輸出結果

    // 變量Var1在 func2中改變了,但在func2的父作用域不變

    // 在調用func3前, Var1有值, var2 不存在,在func3中,設置了Var1、Var2在父作用域中的值,因此,在func3中,
    // Var1、Var2 的值就是調用func3之前的值;調用func3後,Var1、Var2的值就是在func3中設置的值

//---------------------------------------------------------

十五、CMake宏

CMake 中的宏與函數很相似,但也有區別
a. 相同: 調用方式相同, 同樣是不區分名稱大小寫,參數的引用同樣可用,如${ARGC}, ${ARGV#}, ${ARGV#}, ${ARGN}。
b. 區別: CMake 的宏跟C語言的宏相似,原地替換。宏的參數相關的引用,並不是真實的變量,而函數中對參數的引用,則
是真實的變量。函數有自身的域,而宏沒有。


  1. 編輯cmakelists.txt

    cmake_minimum_required(VERSION 3.8)
    project(Macro)

    function(print_line)
    message(STATUS “=======================”)
    endfunction()

    macro(macro1)
    message(STATUS “macro1 called”)
    endmacro(macro1)

    print_line()
    macro1()
    MACRO1()

    set(Var1 “Var1 : init”)

    macro(macro2)
    set(Var1 “Var1: set Var1 in macro2”)
    message(STATUS “Var1 in macro2 = ${Var1}”)
    endmacro(macro2)


    print_line()
    message(STATUS “before call macro2: Var1 = ${Var1}”)
    macro2()
    message(STATUS “after call macro2: Var1 = ${Var1}”)


    macro(macro3 arg1 arg2)
    message(STATUS “arg1 = ${arg1}”)
    message(STATUS “arg2 = ${arg2}”)
    message(STATUS “number of arguments = ${ARGC}”)
    message(STATUS “arg0 = ${ARGV0}, arg1 = ${ARGV1}”)
    message(STATUS “arguments list = ${ARGV}”)
    message(STATUS “ARGN = ${ARGN}”)
    message(STATUS “ARGV2 = ${ARGV2}”)
    endmacro(macro3)







    print_line()

    macro3(“aaa”, “bbb”, “ccc”, “ddd”)

    function(func4)
    if(ARGV1)
    message(STATUS “ARGV1 = ${ARGV1}”)
    endif()
    if(DEFINED ARGV2)
    message(STATUS “ARGV2 = ${ARGV2}”)
    endif()





     if(ARGC GREATER 2)
     	message(STATUS "ARGC GREATER 2")
     else()
     	message(STATUS "ARGC NOT GREATER 2")
     endif()
     
     foreach(var IN LISTS ARGN)
     	message(STATUS "var = ${var}")
     endforeach()
    

    endfunction(func4)

    print_line()

    func4(“aaa”, “bbb”, “ccc”, “ddd”)

    macro(macro4)
    if(ARGV1)
    message(STATUS “ARGV1 = ${ARGV1}”)
    endif()
    if(DEFINED ARGV2)
    message(STATUS “ARGV2 = ${ARGV2}”)
    endif()





     if(ARGC GREATER 2)
     	message(STATUS "ARGC GREATER 2")
     else()
     	message(STATUS "ARGC NOT GREATER 2")
     endif()
     
     foreach(var IN LISTS ARGN)
     	message(STATUS "var = ${var}")
     endforeach()
     
     set(list_var "${ARGN}") #這裏把ARGN轉成列表,再用foreach 
     foreach(var IN LISTS list_var) 
     	message(STATUS "var in list_var = ${var}")
     endforeach()
    

    endmacro(macro4)

    print_line()

    macro4(“aaa”, “bbb”, “ccc”, “ddd”)

    macro(macro5)
    foreach(arg IN LISTS ARGN)
    message(STATUS “arg = ${arg}”)
    endforeach()
    endmacro(macro5)



    function(func5)
    macro5(xx yy zz)
    endfunction(func5)

    print_line()

    func5(“aa”, “bb”, “cc”)

    宏的定義用macro 和 endmacro 兩個指令
    定義一個函數輸出分割線

    調用宏,名稱不區別大小寫

    宏對變量的操作,先定義一個變量
    定義一個宏,並在宏裏修改Var1的值

    調用宏前Var1的值
    調用宏

    // cmake 一下 查看輸出結果

    //調用宏後Var1 的值,可以看到,Var1的值在宏裏修改了,修改後的值在調用宏後保留下來了, 這一點與函數時不一樣的,
    //因爲宏是原地替換的,沒有自身的域,這是比較容易理解的。
    //相關參數的引用,與函數對參數的引用是一樣的

    //定義一個宏對參數相關的引用

    函數中的參數是真實的變量,ARGV1 是變量,爲True
    ARGV2 也是變量,DEFINED 的,也爲True
    ARGC 也是真實變量,可用於與2比較
    ARGN 是列表變量,可以直接進行foreach循環


    函數中的同樣操作用於宏,看下結果
    這裏把ARGN轉成列表,再用foreach

    在函數宏調用宏,宏傳入了三個未定義的參數

    調用函數,傳入了三個參數

    // cmake 一下 查看輸出結果
    // 函數裏, 參數的引用都是真實的變量,而在宏中,並不是變量,因此,在宏中,ARGN、ARGC、ARGV、ARGV#這些都是
    // 未定義的。

//---------------------------------------------------------

十六、CMake的foreach循環
CMake的foreach 指令用於循環列表中的每一項:
foreach(<loop_var> )

endforeach()



loop_var : 用於存放每一輪循環時列表中該項的值
items: 列表的項
commands: 第一輪循環執行的指令
endforeach(): 是foreach循環結束指令,爲了兼容,可以把loop_var作爲參數傳入


foreach 指令的幾種使用方法:
foreach(<loop_var> RANGE <stop>)

foreach(<loop_var> RANGE <start> <stop> [<step>])

foreach(<loop_var> IN [LISTS [<lists>]] [ITEMS[<items>]])

下面的用法要3.17或以上版本:
foreach(<loop_var> ... IN ZIP_LISTS <lists>)

foreach(<loop_var> RANGE <stop>)
循環值從0 到指定的stop, 包含stop,stop 不能爲負值

foreach(<loop_var> RANGE <start> <stop> [<step>])
循環從start 到 stop 的值,步長爲step,如果step不指定則爲1,三個參數都不能爲負數,而且stop不能比start小。


foreach(<loop_var> IN [LISTS [<lists>]] [ITEMS[<items>]])
循環列表lists中的項,或直接指定項 items。


foreach(<loop_var> ... IN ZIP_LISTS <lists>)
這種用法至少要3.17版本,是組合多個列表一起循環,如果指定的多個列表項數不相等,項數少的,
在loop_var中的值爲空值。
  1. 編輯cmakelists.txt
    第一種用法: 直接指定stop值
    第二種用法: 指定start, stop, step
    第三種用法: 指定列表的變量,多個變量可以空格分開(第三種用法,也可以只指定一個列表變量)


    第三種用法, 通過ITEMS來指定列表的項,這裏用${B}, 即等於把B變量的項在這裏展開
    第四種用法: 多個列表組合,只用一個變量取多個列表的項,則通過"變量名_#"來取各個列表的項值
    第四種用法, 多個列表組合,也可以每個列表都用一個變量來取項值

//---------------------------------------------------------

十七、 CMake的條件判斷指令
CMake 的條件判斷指令如下:

if(<條件>)
<命令>
elseif(<條件>)
<命令>
else()
<命令>
endif()





其中 elseif可按實際情況有或者無,也可以有多個,else則按實際情況可有可無。

多個條件組合時, 順序優先級爲:
a. 有括號的,先檢測括號內
b. 然後是一元檢測,如EXISTS, COMMAND, DEFINED
c. 接着是二元檢測,如EQUAL, LESS, LESS_EQUAL, GREATER, GREATER_EQUAL, STREQUAL, STRLESS,
d. 最後是布爾操作,首先是NOT,然後是AND, 最後纔到OR



  1. 條件的類型:
    a. if(<常量>)
    真的常量有: 1, ON, YES, TRUE, Y, 非零數
    假的常量有:0, OFF, NO, FALSE, N, IGNORE, NOTFOUND,空字符串等


    b. if(<變量|字符串>)
    檢測變量或字符串的值是否爲真,這裏要注意: 宏的參數不是變量

    c. if(NOT <條件>), if(<條件1> AND <條件2>), if(<條件1> OR <條件2>)
    分別爲取反,與操作,或操作

    d. if(COMMAND 命令名)
    判斷給的命令名是否爲可調用的命令、宏、函數

    e. if(POLICY 策略ID)
    給定的策略是否存在

    f. if(TARGET <目標名>)
    檢測目標是否存在,這個目標是由add_executable, add_library 或add_custom_target創建

    g. if(TEST <測試名>)
    檢測測試名是否存在,由 add_test創建

    h. if(EXISTS 文件或目錄)
    文件或目錄存在則爲真,要用全路徑

    i. if(文件1 IS_NEWER_THAN 文件2)
    判斷文件1是否比文件2新,要用全路徑

    g. if(IS_DIRECTORY <目錄>)
    給定的全路徑目錄是否爲目錄

    k. if(IS_SYMLINK <文件名>)
    給定的全路徑文件名是否爲符號連接文件

    l. if(IS_ABSOLUTE 路徑)
    給定的路徑是否爲絕對路徑

    m. if(<變量|字符串> MATCHES 正則表達式)
    是否匹配給定的正則表達式

    n. if(<變量|字符串> LESS<變量|字符串>)
    數字比較,LESS 爲小於,GREATER爲大於, EQUAL爲等於,

  2. 編輯cmakelists.txt
    cmake_minimum_required(VERSION 3.17) # 本地安裝的是3.18
    project(IfElse)
    add_executable(main main.cpp)


    if(ON)
    message(“ON”)
    endif()

    if(NOT IGNORE)
    message(“NOT IGNORE”)
    endif()

    if(ON AND NO)
    message(“ON AND NO”)
    elseif(OFF OR 0)
    message(“OFF OR 0”)
    else()
    message(“else()”)
    endif()





    if(NOT “var”)
    message(“NOT var”)
    endif()

    set(var1 “var1”)
    if(var1)
    message(“var1”)
    endif()


    function(func1)
    message(“func1”)
    endfunction()

    if(COMMAND func1)
    message(“COMMAND func1”)
    endif()

    if(NOT COMMAND func2)
    message(“NOT COMMAND func2”)
    endif()

    if(POLICY CMP0054)
    message(“POLICY CMP0054”)
    endif()

    if(TARGET main)
    message(“TARGET main”)
    endif()

    if(EXISTS E:/tmp)
    message(“EXISTS E:/tmp”)
    endif()

    判斷常量
    常量取反
    與操作
    elseif 分支,或操作
    else 分支
    字符串常量,會認爲是假,取反則爲真




    變量判斷,已定義一個值,爲真
    定義函數
    判斷函數是否存在

    指定策略是否存在

    目標是否存在

    指定路徑(目錄或文件)是否存在

//---------------------------------------------------------

十八、 CMake的while循環指令

CMake 的while 循環指令使用方法:

while(<條件>)
<命令>
endwhile()

當<條件>爲真時, 執行<命令>。<條件>的語法形式,跟if條件判斷指令的形式一樣

  1. 編輯cmakelists.txt
    cmake_minimum_required(VERSION 3.17)
    project(While)

    set(var 10)
    while(var GREATER 0)
    message(“var = v a r " ) m a t h ( E X P R v a r " {var}") math(EXPR var " var")math(EXPRvar"{var} - 1”)

     if(var LESS 3)
     	message("break()")
     	break()
     endif()
     
     if(var EQUAL 5)
     		message("continue()")
     	continue()
     endif()
    

    endwhile()

    設置變量var 爲10
    while 指令,只要var大於0就執行循環區間裏的命令(while 到 endwhile之間的命令)
    輸出var當前的值
    var的值減1
    如果var的值小於3, 則退出循環,通過調用break指令退出



    如果var的值等於5, 則跳過此輪循環,去執行新的一輪循環,通過continus指令實現

    // 執行cmake 查看輸出結果

//---------------------------------------------------------

十九、CMake的math指令

CMake的math 指令的使用方法:
math(EXPR “” [OUTPUT_FORMAT ])

計算expression 表達式的值, 表達式中如要使用16進制的數,此數要以0x開頭,得到值後把它賦給變量
variable。結果必須爲64位的有符號整數。

支持的運算符有:
+、-、*、/、%、|、&、^、~、<<、>>、()

format: 用於設置輸出的格式,支持兩種格式,DECIMAL 和 HEXADECIMAL。

DECIMAL: 十進制的輸出方式。
HEXADECIMAL: 十六進制的輸出方式。

  1. 編輯cmakelists.txt
    cmake_minimum_required(VERSION 3.17)
    project(Math)

    math(EXPR val1 “100 * 0xA”)
    math(EXPR val2 “100 * 0xA” OUTPUT_FORMAT DECIMAL)
    math(EXPR val3 “100 * 0xA” OUTPUT_FORMAT HEXADECIMAL)

    message(“val1 = ${val1}”)
    message(“val2 = ${val2}”)
    message(“val3 = ${val3}”)

    計算100* 0xA ,默認格式輸出到val1
    計算100* 0xA ,指定十進制格式輸出到val2
    計算100* 0xA ,指定十六進制格式輸出到val3

    分別輸出val1, val2, val3

//---------------------------------------------------------

二十、CMake列表的操作之一(讀取列表)

CMake 中的列表是以分號(😉 分隔的一組字符串,可以通過set指令來定義。

set(var a b c d e),這就定義了一個列表a;b;c;d;e, 注意set(var “abcde”)只是定義了一個字符串,並不是列表。

列表(屬性) 讀取包括以下幾個方面:
a. 列表的長度
list(LENGTH <列表><輸出變量>)
把列表的長度讀取到輸出變量


b. 讀取列表中的元素
list(GET <元素索引> [<元素索引>…] 輸出變量)
元素索引可正可負,當爲正數時,從列表開頭開始索引,0開始爲第1個元素;當爲負數時,從列表結尾開始索引,-1爲
最後一個元素,讀取的元素值放到輸出變量


c. 列表練級成字符串
list(JOIN <列表> <連接符> <輸出變量>)
把列表的元素連接成一個字符串,元素之間以指定的連接符分隔。

d.讀取子列表
list(SUBLIST <列表> <開始索引> <子列表個數> 輸出變量)
從列表的開始索引起讀取指定個數作爲子列表返回給輸出變量。如果子列表個數爲-1, 或開始索引+子列表個數大於列表的
元素數,則從開始索引起到列表結尾一起作爲子列表返回 給輸出變量。


  1. 編輯cmakelists.txt
    cmake_minimum_required(VERSION 3.17)
    project(List)

    set(var a b c d e)
    message(“var = ${var}”)

    list(LENGTH var varlen)
    message(“varlen = ${varlen}”)

    list(GET var 1 3 -1 varElems)
    message(“varElems = ${varElems}”)

    list(JOIN var “–>” varJoin)
    message(“varJoin = ${varJoin}”)

    list(SUBLIST var 1 2 varSub1)
    list(SUBLIST var 1 -1 varSub2)
    list(SUBLIST var 1 10 varSub3)

    message(“varSub1 = ${varSub1}”)
    message(“varSub2 = ${varSub2}”)
    message(“varSub3 = ${varSub3}”)

    定義一個列表
    獲取列表長度
    取得列表第2、4 和最後一個元素

    用–> 把列表元素連接成字符串
    獲得子列表,從第2個元素開始,共2個元素
    獲得子列表,從第2個元素開始,到列表結尾

    // 執行cmake

//---------------------------------------------------------

二十一、CMake列表的操作之二(搜索列表)

CMake 中對列表的搜索可以用下面的指令:
list(FIND<列表> <值> <輸出變量>)

在列表中搜索給定的值,如果有匹配的,對應索引值存入輸出變量,如果有多個匹配,只取第一個匹配的索引,如果沒有匹配的,
輸出變量爲-1。

  1. 編輯cmakelists.txt
    cmake_minimum_required(VERSION 3.16)
    project(List)

    set(var a b c d e b f)
    message(“var = ${var}”)

    list(FIND var “d” find1)
    message(“find1 = ${find1}”)

    list(FIND var b find2)
    message(“find2 = ${find2}”)

    list(FIND var g find3)
    message(“find3 = ${find3}”)

    定義列表
    查找d
    查找b, 可以不用雙引號,有兩個匹配,只取第一個
    查找g, 沒有匹配的值


    // 執行cmake

//---------------------------------------------------------

二十二、CMake列表的操作之三(修改列表)

CMake 中對列表的修改有增、刪、改:

a. 增加元素:
list(APPEND […]) —>在列表末尾添加給定元素
list(PREPEND […]) —>在列表開頭添加給定元素
list(INSERT […]) —>在列表指定索引位置插入元素


b. 刪除元素
list(REMOVE_ITEM […]) —> 刪除指定元素
list(REMOVE_AT […]) —> 刪除指定索引位置的元素

list(POP_BACK […]) —> 刪除列表末尾的元素,out-var參數沒有的話,只刪除一個,否則根據out-var
的個數刪除相應個數,並把刪除的元素賦給out-var

list(POP_FRONT […]) —> 與POP_BACK相似,只是在列表開頭刪除

list(REMOVE_DUPLICATES ) —> 刪除重複的元素,只保留第一次發現的那個

c. 按正則表達式保留或去除元素
list(FILTER {INCLUDE | EXCLUDE} REGEX ) -->INCLUDE的話,匹配regex的元素被保留,
EXCLUDE的話,匹配regex的元素被刪除。

  1. 編輯cmakelists.txt
    cmake_minimum_required(VERSION 3.16)
    project(List)

    set(var a b c d e b f)
    message(“var = ${var}”)

    list(APPEND var aa bb cc b f)
    message(“APPEND ${var}”)

    list(PREPEND var dd ee ff)
    message(“PREPEND ${var}”)

    list(INSERT var 5 gg hh ii)
    message(“INSERT ${var}”)

    list(REMOVE_ITEM var ee ff bb)
    message(“REMOVE_ITEM ${var}”)

    list(REMOVE_AT var 2 3)
    message(“REMOVE_AT ${var}”)

    list(REMOVE_DUPLICATES var)
    message(“REMOVE_DUPLICATES ${var}”)

    list(POP_BACK var)
    message(“POP_BACK ${var}”)

    list(POP_FRONT var var1 var2)
    message(“POP_FRONT ${var}”)
    message(“var1 = ${var1}”)
    message(“var2 = ${var2}”)


    list(FILTER var INCLUDE REGEX “[a-z][a-z]”)
    message(“FILTER INCLUDE ${var}”)

    list(FILTER var INCLUDE REGEX “[a-h][a-h]”)
    message(“FILTER INCLUDE ${var}”)

    定義列表
    列表末尾添加元素
    列表開頭添加元素
    插入元素
    移除指定元素
    移除指定索引位置元素
    移除重複元素





    刪除末尾一個元素
    刪除開頭兩個元素,並賦值給var1和var2

    保留由兩個字符組成的元素
    刪除由兩個字符組成的元素,並且兩個字符是從a到h的

    // 執行cmake

//---------------------------------------------------------

二十三、CMake列表的操作之四(排序列表)

CMake 中對列表的倒序指令爲:
list(REVERSE )

CMake中對列表的排序指令爲:
list(SORT [COMPARE ][CASE ][ORDER ])

compare 可以指定比較的方式: STRING —> 按字母表順序,
FILE_BASENAME —> 列表中的元素爲路徑,按路徑的基礎名排序,
NATURAL —> 按數字的順序排序(要3.18版本)

case 指定是否區分大小寫: SENSITIVE —>區別大小寫,
INSENSITIVE —> 不區別大小寫

order指定升序還是降序: ASCENDING —> 升序, DESCENDING —> 降序

  1. 編輯cmakelists.txt
    cmake_minimum_required(VERSION 3.16)
    project(List)

    set(var a B C d)
    message(“var = ${var}”)

    list(TRANSFORM var APPEND e)
    message(“APPEND – ${var}”)

    list(TRANSFORM var PREPEND F OUTPUT_VARIABLE outvar)
    message(“PREPEND – ${var}”)
    message(“outvar – ${outvar}”)

    list(TRANSFORM var TOUPPER)
    message(“TOUPPER – ${var}”)

    list(TRANSFORM var TOLOWER)
    message(“TOLOWER – ${var}”)

    set(var a b)
    list(APPEND var " c" "d " " e ")
    message(“var = ${var}”)

    list(TRANSFORM var STRIP)
    message(“STRIP – ${var}”)

    set(var a $BOOL:FALSE $BOOL:YES b c)
    message(“var = ${var}”)

    list(TRANSFORM var GENEX_STRIP)
    message(“GENEX_STRIP – ${var}”)

    set(var a b cc dd e f)
    message(“var = ${var}”)

    list(TRANSFORM var REPLACE “[a-z][a-z]” xx)
    message(“REPLACE – ${var}”)

    list(TRANSFORM var TOUPPER AT 0 1 3 4)
    message(“TOUPPER AT – ${var}”)

    list(TRANSFORM var TOLOWER FOR 1 3)
    message(“TOLOWER FOR – ${var}”)

    list(TRANSFORM var TOUPPER REGEX “[a-z][a-z]”)
    message(“TOUPPER REGEX – ${var}”)

    列表倒序
    按字母表順序排, 不區分大小寫,升序,
    按字母表順序排, 不區分大小寫,降序
    按字母表順序排, 區分大小寫,降序
    按字母表順序排, 默認區分大小寫,默認升序,



    按數字順序排,區分大小寫,升序(NATURAL這個選項要3.18才能使用)

    // 執行cmake

//---------------------------------------------------------

二十四、CMake列表的操作之五(轉換列表)

CMake 中對列表元素進行轉換的指令爲:

list(TRANSFORM <列表> <操作> [<選擇器>] [OUTPUT_VARIABLE <輸出變量>])

所有的操作都是對元素進行操作,如果指定輸出變量,則原列表不變,操作後的列表賦
給輸出變量。

操作有:
a. list(TRANSFORM <列表> <APPEND|PREPEND><值>…)
在元素的後面或前面添加指定字符

b. list(TRANSFORM <列表> <TOLOWER|TOUPPER>…)
把元素變爲小寫或大寫

c. list(TRANSFORM <列表> STRIP …)
刪除元素頭尾的空白

d. list(TRANSFORM <列表> GENEX_STRIP …)
如果元素有生成器表達式,則把生成器表達式刪除

e. list(TRANSFORM <列表> REPLACE <正則表達式> <替換值>…)
把與指定正則表達式匹配的元素進行替換

選擇器有:
a. list(TRANSFORM <列表> <操作> AT <索引> [<索引>…]…)
通過索引指定要變換的元素

b. list(TRANSFORM <列表> <操作> FOR <開始> <結束> [<步長>]…)
通過範圍來指定要變換的元素,步長不指定則爲1

c. list(TRANSFORM <列表> <操作> REGEX <正則表達式>…)
與正則表達式匹配的元素被變換

  1. 編輯cmakelists.txt
    cmake_minimum_required(VERSION 3.16)
    project(List)

    set(var a b c d e b f)
    message(“var = ${var}”)

    list(REVERSE var)
    message(“var REVERSE ${var}”)

    set(var A b E d C f)
    list(SORT var COMPARE STRING CASE INSENSITIVE ORDER ASCENDING)
    message(“SORT STRING INSENSITIVE ASCENDING - ${var}”)

    set(var A b E d C f)
    list(SORT var COMPARE STRING CASE INSENSITIVE ORDER DESCENDING)
    message(“SORT STRING INSENSITIVE DESCENDING - ${var}”)

    set(var A b E d C f)
    list(SORT var COMPARE STRING CASE SENSITIVE ORDER DESCENDING)
    message(“SORT STRING SENSITIVE DESCENDING - ${var}”)

    set(var 10.0 1.1 2.1 8.0 2.0 3.1)
    list(SORT var COMPARE STRING)
    message(“SORT STRING - ${var}”)

    set(var 10.0 1.1 2.1 8.0 2.0 3.1)
    list(SORT var COMPARE NATURAL)
    message(“SORT NATURAL - ${var}”)

    定義變量
    每個元素後面都加個e
    每個元素前面加個F,並把變換後的列表賦給outvar
    原列表不變
    元素全變成大寫
    所有元素變小寫
    設置一個元素前後有空白的元素
    把元素前後的空白刪除
    定義一個元素中有生成器表達式的列表
    把列表元素中的生成器表達式刪除
    定義一個新列表
    把由兩個字母組成的元素替換爲XX










    把列表中位於0 1 3 4的元素變爲大寫
    把列表中從索引1到3(包含)的元素變爲小寫
    把由兩個字母組成的元素變爲大寫

    // 執行cmake

//---------------------------------------------------------

二十五、CMake的變量監測

CMake 中對變量的監測指令爲;
variable_watch(<變量>[<指令>])

如果變量有變化,則會輸出該變量變化的相關信息,如果給出指令,則會執行給出的指令,
該指令可以是一個函數,接受的參數有變量、訪問方式、變量值、當前文件列表和棧信息。

  1. 編輯cmakelists.txt
    cmake_minimum_required(VERSION 3.16)
    project(VariableWatch)

    set(var 1)
    variable_watch(var)
    set(var 3)

    function(func variable access value current_list_file stack)
    message(“variable = ${variable}”)
    message(“access = ${access}”)
    message(“value = ${value}”)
    message(“current_list_file = ${current_list_file}”)
    message(“stack = ${stack}”)
    endfunction()





    set(var1 “var1”)
    variable_watch(var1 func)
    set(var1 “var1 changed”)

    設置一個變量
    監測該變量
    改變此變量的值
    定義一個函數,參數爲: 變量、訪問方式、值、當前文件列表、棧
    簡單的把參數都輸出



    創建一個新的變量,並監測此變量,如有變化,則執行前面定義的func這個函數。
    改變變量的值

    // 執行cmake

//---------------------------------------------------------

二十六、CMake添加項目樹之外的子目錄

如何把不是項目目錄之下的目錄添加到項目中去?

  1. 建立 a 、b 兩個文件夾 在b 文件夾中嵌套建立 d、e、f 文件夾

  2. 在f目錄中,一個頭文件,一個源文件,用於生成演示用的靜態庫
    在f目錄中, 創建 CMakeLists.txt 用於生成庫目標

    回到f目錄的上一層e目錄,添加CMakeLists.txt,用於添加f 目錄到項目
    同樣的回到e目錄的上一層d目錄,創建CMakeLists.txt
    繼續回到d的上一層b, 創建CMakeLists.txt

    sub.h 文件
    #include

    void my_print();

    sub.cpp 文件

    #include “sub.h”

    void my_print(){

     std::cout << "my_print" << std::endl;
    

    }

    cmakelists.txt 文件

    add_library(sub STATIC sub.cpp)

  3. 回到目錄a, 添加項目的源代碼 main.cpp ,包含靜態庫的頭文件
    main.cpp 文件
    #include “sub.h”

    int main(){
    std::cout << “main called” << std::endl;
    my_print();
    }


  4. 在a目錄中, 創建項目的 CMakeLists.txt
    cmake_minimum_required(VERSION 3.16)
    project(a)

    set(CMAKE_CXX_FLAGS “${CMAKE_CXX_FLAGS} -std=c++11 -03”)
    add_subdirectory(…/b b.out)
    include_directories(…/b/d/e/f)
    add_executable(main main.cpp)
    target_link_libraries(main sub)



    設置CMAKE_CXX_FLAGS變量
    添加項目目錄外的目錄b爲子目錄,注意,這裏第二個參數一個要指定,表示輸出的目錄
    添加頭文件所在目錄
    生成的目標
    生成目標依賴的庫



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