cmake 模板

Getone超發表於2024-03-14

目錄
  • 一、cmake模板
  • 二、引數設定
  • 三、命令解釋
    • 3.1 find命令
    • 3.2 file 執行與檔案和目錄相關的操作
    • 3.3 自定義命令
    • 3.4 配置檔案
  • 四、自動化測試
  • 五、安裝
    • 5.1 Linux的rpath機制
    • 5.2 CMAKE_INSTALL_RPATH的使用案例
    • 5.3 CMAKE_BUILD_RPATH的使用案例
  • 六、閉源包引用
  • 七、vcpkg包管理
    • 6.1 安裝
    • 6.2 vcpkg.json
    • 6.3 多包管理器共存

一、cmake模板

|--CMakeLists.txt
|--extern
|--src
|--|--subsrc1
|--|--|--CMakeLists.txt
|--|--subsrc2
|--|--|--CMakeLists.txt
|--|--main.cc
|--|--CMakeLists.txt
|--test
|--|--CMakeLists.txt
|--vcpkg.json

根目錄的CMakeLists.txt

cmake_minimum_required(VERSION 3.15)

project(PROJECT_XXX VERSION 0.0.0 )

#C/C++標準
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 14)


#設定編譯器
set (CMAKE_C_COMPILER "/usr/bin/gcc")
set (CMAKE_CXX_COMPILER "/usr/bin/g++")

######### build 變數 #####
set(CMAKE_BUILD_TYPE Debug#[[Release | Debug| RelWithDebInfo |MinSizeRel]])
set(CMAKE_BUILD_PARALLEL_LEVEL 4)#編譯處理器數量
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)#clang
set(CMAKE_GENERATOR "Unix Makefiles")#“Ninja”、“Unix Makefiles”、“Visual Studio”
#add_compile_options()#等同CMAKE_CXXFLAGS_RELESE,前者可以對所有的編譯器設定,後者只能是C++編譯器

########## vcpkg #######
#set(CMAKE_TOOLCHAIN_FILE  $ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake)
#set(ENV{PKG_CONFIG_PATH}  "$ENV{PKG_CONFIG_PATH};${CMAKE_SOURCE_DIR}/vcpkg_installed/x64-linux/lib/pkgconfig")


#lib&&bin輸出目錄
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/build/bin)#可執行檔案
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/build/lib)#動態庫
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/build/lib/static)#靜態庫


###### sub directory #####
add_subdirectory(src)
#add_subdirectory(external)

########## TEST ##########
if(FALSE)
	enable_testing()
	add_subdirectory(test)
	add_test(NAME test COMMAND ${PROJECT_NAME} -arg1 -arg2)
endif()

########## istanll #######
set(CMAKE_INSTALL_PREFIX ${PROJECT_SOURCE_DIR}/install)
# 1.target  放到 DESTINATION 指定的目錄
#install(TARGETS ... RUNTIME DESTINATION bin)#exe
#install(TARGETS ... LIBRARY DESTINATION lib)#*.so
#install(TARGETS ... ARCHIVE DESTINATION lib/static)#*.lib
#install(TARGETS ... PUBLIC_HEADER DESTINATION include)#公共標頭檔案的安裝路徑
#install(TARGETS ... RESOURCE 	   DESTINATION <dir>)#私有標頭檔案的安裝路徑
# 2.普通檔案 放置放到 DESTINATION 指定的目錄,eg:readme.md config.ini
#install(FILES ... DESTINATION etc)
# 3.目錄
#install(DIRECTORY ... DESTINATION ...)
# 4.指令碼    放置到 DESTINATION 指定的目錄,eg:install.sh
#install(PROGRAMS ... DESTINATION ...)
# 5.target集合
#install(TARGETS ...  EXPORT export_name RUNTIME DESTINATION bin)#exe
#install(EXPORT export_name NAMESPACE namespace DESTINATION <dir>)

########## PACK ##########
if(FALSE)
# 安裝包名稱
set(CPACK_PACKAGE_NAME ${PROJECT_NAME})
# 版本號                       
set(CPACK_PACKAGE_VERSION "1.0.0") 
# 描述資訊
set(CPACK_PACKAGE_DESCRIPTION "My awesome application")
# 許可證                                  
set(CPACK_RPM_PACKAGE_LICENSE "Apache 2.0 + Common Clause 1.0")
# vendor                             
set(CPACK_PACKAGE_VENDOR "vesoft")  
# 安裝包圖示
#set(CPACK_PACKAGE_ICON )

#配置軟體包型別和生成器ZIP、TGZ、RPM、NSIS
set(CPACK_GENERATOR ZIP)#二進位制包
#set(CPACK_SOURCE_GENERATOR ZIP)#原始碼包

#安裝系統依賴庫
include(InstallRequiredSystemLibraries)

#安裝安裝包時的依賴關係
#set(CPACK_DEBIAN_PACKAGE_DEPENDS "")#Debian自動安裝依賴
#set(CPACK_RPM_PACKAGE_REQUIRES "")

#新增指令碼和配置
#set(CPACK_PRE_INSTALL_SCRIPTS "${CMAKE_CURRENT_SOURCE_DIR}/pre_install_script.sh")#安裝前,目錄許可權
#set(CPACK_POST_INSTALL_SCRIPTS "${CMAKE_CURRENT_SOURCE_DIR}/post_install_script.sh")#安裝後,systemctl自啟動指令碼

# 設定支援指定安裝目錄的控制為 ON;設定安裝到的目錄路徑                                   
#set(CPACK_SET_DESTDIR ON)
#set(CPACK_INSTALL_PREFIX )   
include(CPack)
endif()

LIB

######### Target LIB #########
aux_source_directory(目錄 LIBS_SRC_LISTS)

set(LIBS_NAME )
add_library(${LIBS_NAME } "")#預設是STATIC
target_include_directories(${LIBS_NAME} PRIVATE )
target_sources(${LIBS_NAME} PRIVATE ${LIBS_SRC_LISTS})
#target_link_libraries(${LIBS_NAME} )
target_compile_options(${LIBS_NAME} PRIVATE -Wall
                                          -O3 -std=c++11 )
target_compile_definitions(${LIBS_NAME} PRIVATE
                                          CMAKE_BUILD_TYPE=Release
                                           CMAKE_EXPORT_COMPILE_COMMANDS=ON)

EXE

######### Target EXE #########
aux_source_directory(目錄 EXE_SRC_LISTS)

add_executable(${PROJECT_NAME} )
#target_include_directories(${PROJECT_NAME} RIVATE )
target_sources(${PROJECT_NAME} PRIVATE  ${EXE_SRC_LISTS})
#target_link_libraries(${PROJECT_NAME} )
target_compile_options(${PROJECT_NAME} RRIVATE -Wall
                                            -O3 -std=c++11 )
target_compile_definitions(${PROJECT_NAME} PRIVATE
                                          CMAKE_BUILD_TYPE=Release
                                           CMAKE_EXPORT_COMPILE_COMMANDS=ON)

FIND

######### FIND FILE   #######
#find_package(Eigen3 REQUIRED)
#find_path (<VAR> name1 [path1 path2 ...])
#find_file (<VAR> name1 [path1 path2 ...])
#find_library (<VAR> name1 [path1 path2 ...])

vcpkg

                                           
#########  VCPKG #########
set(CMAKE_TOOLCHAIN_FILE $ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake)

#增加*.cmake的尋找路徑
#list(APPEND CMAKE_PREFIX_PATH ${CMAKE_SOURCE_DIR}/vcpkg_installed/x64-linux/share/gtest)
find_package(Boost REQUIRED)
include_directories(${Boost_INCLUDE_DIRS})
target_link_libraries(${PROJECT_NAME} ${Boost_LIBRARIES})

####### 清單pkgconfig模式 ###
find_package(PkgConfig REQUIRED)

pkg_check_modules(LIBRARY_NAME REQUIRED libname)#變數 libname.pc
target_include_directories(${TARGET_NAME}  ${LIBRARY_NAME_INCLUDE_DIRS})#標頭檔案目錄
target_link_directories(${PROJECT_NAME} PRIVATE ${LIBRARY_NAME_INCLUDE_DIRS})#連結目錄
target_link_libraries(${TARGET_NAME} ${LIBRARY_NAME_LIBRARIES})#連結庫

閉源庫

######### 閉源庫 ##########
add_library(${LIBNAME} STATIC IMPORTED)
set_property(TARGET ${LIBNAME} PROPERTY IMPORTED_LOCATION ${CMAKE_CURRENT_LIST_DIR}/extern/{LIBNAME}/lib-vc2019/glfw3.lib)
target_include_directories( ${LIBNAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR}/extern/${LIBNAME}/include)

test

           

二、引數設定

編譯選項

CMAKE_BUILD_TYPE Release | Debug| RelWithDebInfo |MinSizeRel

三、命令解釋

3.1 find命令

find_path:用於找到指定檔案或目錄路徑的命令(安裝ini檔案)

find_path(<VAR> name1 [path1 path2 ...])

eg:
find_path(STDIO_H_INCLUDE_DIR stdio.h
    /usr/include
    /usr/local/include)
    
   

其中,<VAR>是用於儲存找到的路徑的變數名。name1是要查詢的檔案或目錄的名稱。path1path2等是可選的搜尋路徑。

find_path命令特別適用於需要在構建過程中動態查詢標頭檔案路徑的情況

find_file:用於查詢指定檔案的路徑

find_file(<VAR> name1 [path1 path2 ...])

eg:
find_file(EXAMPLE_FILE example.txt)
執行上述命令後,如果找到了example.txt檔案,路徑將儲存在EXAMPLE_FILE變數中,否則該變數將為空。

find_file(EXAMPLE_FILE example.txt
    /usr/data
    /home/user/data
)
這將在/usr/data和/home/user/data目錄下搜尋example.txt檔案

其中,<VAR>是一個變數,用於儲存找到的檔案路徑。name1是要查詢的檔案的名稱。path1path2等是可選的搜尋路徑。

find_library:用於查詢指定庫檔案的路徑

find_library(<VAR> name1 [path1 path2 ...])

其中,<VAR>是用於儲存找到的庫檔案路徑的變數名。name1是要查詢的庫檔案的名稱(不包括字首lib和副檔名)。

使用案例

cmake_minimum_required(VERSION 3.12)
project(MyProject)

# 查詢名為 mylibrary 的庫檔案
find_library(MYLIBRARY_LIB mylibrary)

# 如果找到了庫檔案
if(MYLIBRARY_LIB)
    message("Found mylibrary at: ${MYLIBRARY_LIB}")
    # 新增庫檔案的路徑到連結器
    target_link_libraries(MyExecutable ${MYLIBRARY_LIB})
else()
    message(FATAL_ERROR "mylibrary not found")
endif()

find_program:用於查詢指定可執行程式的路徑

find_program(<VAR> name1 [path1 path2 ...])

eg:
find_program(MYPROGRAM_EXECUTABLE myprogram
    /usr/bin
    /usr/local/bin
)

這將在/usr/bin/usr/local/bin目錄下搜尋myprogram可執行程式。

使用案例

cmake_minimum_required(VERSION 3.12)
project(MyProject)

# 查詢名為 myprogram 的可執行程式
find_program(MYPROGRAM_EXECUTABLE myprogram)

# 如果找到了可執行程式
if(MYPROGRAM_EXECUTABLE)
    message("Found myprogram at: ${MYPROGRAM_EXECUTABLE}")
    # 在構建過程中使用可執行程式
    add_custom_target(run_myprogram COMMAND ${MYPROGRAM_EXECUTABLE})
else()
    message(FATAL_ERROR "myprogram not found")
endif()

在這個案例中,假設我們的專案想要執行名為myprogram的可執行程式。

透過find_program(MYPROGRAM_EXECUTABLE myprogram)命令,CMake會嘗試在系統的預設可執行程式搜尋路徑中找到名為myprogram的可執行程式。

如果成功找到了可執行程式,CMake會將路徑儲存在變數MYPROGRAM_EXECUTABLE中,並透過add_custom_target命令定義一個自定義目標,使得我們可以在構建過程中使用該可執行程式。

如果未找到可執行程式,則會輸出錯誤訊息並終止構建過程

find_package: CMake中用於查詢和載入第三方庫的命令

使用案例

cmake_minimum_required(VERSION 3.12)
project(MyProject)

# 查詢並載入 OpenCV
find_package(OpenCV 4 REQUIRED)

# 檢查是否找到庫
if(OpenCV_FOUND)
    message("Found OpenCV version ${OpenCV_VERSION}")
    include_directories(${OpenCV_INCLUDE_DIRS})
    add_executable(MyApp main.cpp)
    target_link_libraries(MyApp ${OpenCV_LIBS})
else()
    message(FATAL_ERROR "OpenCV not found")
endif()

# 新增其他專案配置和構建指令...

變數

  1. <PackageName>_FOUND
    表示是否找到了指定的庫。它是一個布林值,如果找到了庫,則為 TRUE,否則為 FALSE
  2. <PackageName>_VERSION
    當找到庫時,它表示所找到的庫的版本號。該值可能是一個字串或一個列表。
  3. <PackageName>_INCLUDE_DIRS
    包含著所找到的庫的標頭檔案路徑的變數。這允許您在專案中包含庫的標頭檔案。
  4. <PackageName>_LIBRARIES
    儲存了所找到的庫的完整庫檔案路徑的變數。透過這個變數,您可以將所需的庫連結到專案的可執行檔案或庫中。

這些變數的命名約定在不同的 Find 模組中可能會有所不同,因此請查閱庫的相關文件來獲取詳細的變數名稱和用途。

3.2 file 執行與檔案和目錄相關的操作

它可以用於建立、複製、刪除、重新命名、讀取檔案內容等操作。以下是一些 file() 命令的常見用法:

  1. file(COPY ...)
    將指定的檔案或目錄複製到指定的目標目錄下。該命令可以用於將檔案複製到構建目錄或安裝位置。

    file(COPY source_file DESTINATION destination_directory)
    
  2. file(REMOVE ...)
    刪除指定的檔案或目錄。

    file(REMOVE file_path)
    
  3. file(RENAME ...)
    將檔案或目錄重新命名。

    file(RENAME old_name new_name)
    
  4. file(READ ...)
    讀取檔案內容到變數中。

    file(READ file_path variable_name)
    
  5. file(WRITE ...)
    將字串內容寫入檔案中。

    file(WRITE file_path "content")
    
  6. file(APPEND ...)
    將字串內容追加到檔案末尾。

    file(APPEND file_path "content")
    

這只是 file() 命令的一些常見用法示例,實際上它還有很多其他用法,例如建立目錄、查詢檔案、獲取檔案屬性等。您可以在 CMake 官方文件中查詢 file() 命令的完整參考以獲取更詳細的資訊和用法示例。

3.3 自定義命令

在很多時候,需要在cmake中建立一些目標,如cleancopy等等,這就需要透過add_custom_target來指定。同時,add_custom_command可以用來完成對add_custom_target生成的target的補充。

區別

在CMake中,"add_custom_command"和"add_custom_target"是兩個常用的命令,用於定義自定義編譯命令和自定義構建目標。它們之間的區別如下:

  1. add_custom_command:
    • add_custom_command用於定義在構建時執行的自定義命令。它可以用來生成檔案、生成程式碼、執行指令碼等。add_custom_command通常被用作目標的依賴項。
    • add_custom_command並不會建立真正的構建目標,它僅僅是一個構建過程中執行的命令。因此,預設情況下,add_custom_command不會導致重新構建整個專案。
  2. add_custom_target:
    • add_custom_target用於定義一個構建目標,該目標不與實際的檔案或輸出相關聯,而是與一組其他規則相關聯。
    • add_custom_target可以用來執行一系列自定義命令或構建步驟。它對於組織和管理構建過程非常有用。
    • add_custom_target通常用作構建系統的入口點,透過依賴其他目標和規則來執行自定義構建任務。

總結來說,add_custom_command用於定義構建過程中的自定義命令,而add_custom_target用於定義自定義構建目標。兩者可以結合使用,以實現更復雜的構建邏輯。

add_custom_target:自定義構建目標

add_custom_target(Name [ALL] [command1 [args1...]]
                  [COMMAND command2 [args2...] ...]
                  …
)
# Name:定義的target的名字
# COMMAND:該target要執行的命令

add_custom_target(
    target_name
    [COMMAND command1 [ARGS] [args1...]]
    [COMMAND command2 [ARGS] [args2...] ...]
    [DEPENDS [depend1...]]
    [WORKING_DIRECTORY dir]
    [COMMENT comment]
    [VERBATIM]
    [USES_TERMINAL]
)

其中,常用的引數和選項包括:

target_name:自定義構建目標的名稱。
COMMAND:指定要執行的命令。
DEPENDS:指定自定義目標所依賴的其他目標或檔案。
WORKING_DIRECTORY:設定命令的工作目錄。
COMMENT:新增對命令或目標的註釋。
VERBATIM:保留命令中的跳脫字元,確保按原樣傳遞給底層的構建工具。
USES_TERMINAL:指示命令是否會使用終端。

透過使用 add_custom_target,可以在構建過程中定義各種自定義的構建目標,例如執行測試、生成文件、執行程式碼檢查等。這些目標可以依賴其他目標或檔案,並根據需要執行自定義的命令或操作

add_custom_target 命令不會生成實際的構建產物(如可執行檔案或庫),它只是定義了一個自定義的構建目標。您可以透過 add_dependencies 命令將這個自定義目標與其他目標關聯起來,以確保在構建過程中執行相關的自定義操作。

使用案例

add_custom_target(RunTests
    COMMAND run_tests.sh
    DEPENDS test_files
    WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
    COMMENT "Running tests..."
)

add_dependencies(RunTests MyApp)

在這個示例中,RunTests 是一個自定義目標,透過執行 run_tests.sh 指令碼來執行測試。它依賴於 test_files 目標(或檔案),並在 ${CMAKE_BINARY_DIR} 目錄下執行。透過 COMMENT 可以新增註釋,說明正在執行的操作。最後,透過 add_dependencies 將 RunTests 目標與 MyApp 目標關聯起來,以確保在構建 RunTests 目標時先構建 MyApp 目標。

使用教程

  1. 建立自定義目標:
    首先,使用 add_custom_target 命令建立一個自定義構建目標並指定它的名稱。

    add_custom_target(MyTarget)
    

    在上述示例中,我們建立了一個名為 MyTarget 的自定義目標,這個目標還沒有定義任何操作。

  2. 新增命令:
    使用 COMMAND 選項來新增要執行的命令或操作。

    add_custom_target(MyTarget
        COMMAND echo "Hello, World!"
    )
    

    在這個例子中,我們在 MyTarget 目標中新增了一個命令,即輸出 “Hello, World!”。您可以根據實際需求新增更多的命令或操作。

  3. 新增依賴項:
    使用 DEPENDS 選項來指定 MyTarget 目標所依賴的其他目標或檔案。

    add_custom_target(MyTarget
        COMMAND echo "Hello, World!"
        DEPENDS other_target file.txt
    )
    

    在上述示例中,我們將 MyTarget 目標設定為依賴於 other_target 目標和 file.txt 檔案。這意味著在構建 MyTarget 之前,CMake 將確保先構建 other_target 目標和檢查 file.txt 檔案的更新。

  4. 設定工作目錄和註釋:
    使用其他選項如 WORKING_DIRECTORYCOMMENT 來設定工作目錄和新增註釋。

    add_custom_target(MyTarget
        COMMAND echo "Hello, World!"
        DEPENDS other_target file.txt
        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
        COMMENT "Running custom commands..."
    )
    

    在上述示例中,我們設定了 MyTarget 的工作目錄為 ${CMAKE_BINARY_DIR},並新增了一個註釋以描述正在執行的操作。

  5. 新增到其他目標的依賴項:
    使用 add_dependencies 命令將自定義目標與其他目標關聯起來。

    add_executable(MyApp main.cpp)
    add_custom_target(MyTarget
        COMMAND echo "Hello, World!"
        DEPENDS other_target file.txt
        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
        COMMENT "Running custom commands..."
    )
    
    add_dependencies(MyApp MyTarget)
    

    在這個例子中,我們將自定義目標 MyTarget 新增為可執行檔案 MyApp 的依賴項。這意味著在構建 MyApp 時,CMake 將確保先構建 MyTarget

add_custom_command:自定義編譯命令

add_custom_command(OUTPUT output1 [output2 ...]
                   COMMAND command1 [ARGS] [args1...]
                   [COMMAND command2 [ARGS] [args2...] ...]
                   [MAIN_DEPENDENCY depend]
                   [DEPENDS [depends...]]
                   [BYPRODUCTS [files...]]
                   [IMPLICIT_DEPENDS <lang1> depend1
                                    [<lang2> depend2] ...]
                   [WORKING_DIRECTORY dir]
                   [COMMENT comment]
                   [DEPFILE depfile]
                   [JOB_POOL job_pool]
                   [VERBATIM] [APPEND] [USES_TERMINAL]
                   [COMMAND_EXPAND_LISTS]
                   [DEPENDS_EXPLICIT_ONLY])

其中,常用的引數和選項包括:

  • TARGET:指定目標(可執行檔案或庫),表示該自定義命令與構建目標相關聯。
  • PRE_BUILDPRE_LINKPOST_BUILD:指定自定義命令在何時執行,分別表示前置構建、前置連結和後置構建。
  • COMMAND:指定要執行的命令。
  • WORKING_DIRECTORY:設定命令的工作目錄。
  • COMMENT:新增對命令的註釋。
  • MAIN_DEPENDENCY:指定自定義命令所依賴的主檔案。
  • DEPENDS:指定自定義命令所依賴的其他檔案。
  • BYPRODUCTS:指定由命令生成的副產品檔案。
  • IMPLICIT_DEPENDS:指定命令的隱式依賴關係,這些依賴關係可能是根據原始檔的語言推斷出來的。
  • USES_TERMINAL:指示命令是否會使用終端(僅在可執行檔案的情況下)。

透過使用 add_custom_command,可以在構建過程中執行各種自定義的命令,如生成程式碼、複製檔案、執行指令碼等。這允許您在構建過程中執行與編譯和連結無關的任意操作。

請注意,add_custom_command 命令必須與 add_custom_targetadd_executable / add_library 配合使用,以將其與構建目標關聯起來。

使用案例

add_custom_command 命令用於在構建過程中執行自定義的命令。下面是一個簡單的教程,介紹瞭如何使用 add_custom_command

  1. 新增生成檔案的自定義命令:
    首先,使用 add_custom_command 命令定義一個用於生成檔案的自定義命令。

    add_custom_command(
        OUTPUT generated_file.txt
        COMMAND generate_file.py
        DEPENDS generate_file.py
    )
    

    在上述示例中,我們定義了一個自定義命令,它使用 Python 指令碼 generate_file.py 來生成一個名為 generated_file.txt 的檔案。DEPENDS 引數指定生成檔案的依賴項,例如指令碼檔案本身。

  2. 將生成的檔案作為構建目標使用:
    可以將生成的檔案作為構建目標的一部分使用,例如作為原始檔或目標檔案。

    add_executable(MyApp main.cpp generated_file.txt)
    

    在上述示例中,我們將生成的檔案 generated_file.txt 作為一個原始檔之一與 main.cpp 一起使用,構建一個名為 MyApp 的可執行檔案。

  3. 宣告生成檔案的依賴關係:
    使用 add_dependencies 命令將自定義命令與其他目標關聯起來。

    add_custom_command(
        OUTPUT generated_file.txt
        COMMAND generate_file.py
        DEPENDS generate_file.py
    )
    
    add_executable(MyApp main.cpp)
    add_dependencies(MyApp generated_file.txt)
    

    在這個例子中,我們在自定義命令與目標之間建立了依賴關係。透過 add_dependencies 命令,MyApp 目標將在構建之前先構建 generated_file.txt

使用 add_custom_command,您可以執行各種自定義的命令或操作,並將其與構建目標進行關聯。這使得您可以在構建過程中執行額外的操作,例如生成程式碼、複製檔案、預處理資源等。根據自己的專案需求,您可以根據需要實現適合的自定義命令,並根據構建邏輯設定相關的依賴關係。

3.4 配置檔案

configure_file命令是CMake提供的一個常用命令,用於在構建過程中根據模板檔案生成配置檔案

四、自動化測試

當使用CTest來執行測試時,通常需要按照以下步驟進行配置和執行:

  1. 建立測試目錄:在專案中建立一個用於存放測試指令碼和測試資料的目錄。可以將該目錄命名為tests或者其他合適的名稱。

  2. 編寫測試指令碼:在測試目錄中建立一個或多個測試指令碼檔案,用於描述測試用例和測試步驟。測試指令碼可以使用CTest的命令和語法來定義測試行為、驗證結果等。以下是一個簡單的示例:

    # tests/mytest.cmake
    
    # 定義一個測試用例
    add_test(NAME MyTest COMMAND my_program input_data.txt output_data.txt)
    
    # 驗證測試結果
    set_tests_properties(MyTest PROPERTIES PASS_REGULAR_EXPRESSION "Expected output")
    
  3. 建立CTest配置檔案:在專案的根目錄中建立一個名為CTestTestfile.cmake的配置檔案,用於配置測試設定、測試套件和測試驅動程式等細節。該檔案會被CTest自動載入。以下是一個簡單的示例:

    # CTestTestfile.cmake
    
    # 新增測試目錄
    add_subdirectory(tests)
    
  4. 構建專案:使用CMake構建專案,此時確保CTest被啟用並已成功整合到專案中。

  5. 執行CTest:在專案構建完成後,在構建目錄中執行ctest命令來執行測試。可以使用ctest --verbose來顯示詳細的測試輸出。

    $ ctest
    

    或者使用圖形介面工具來執行CTest,例如執行ccmakecmake-gui命令來檢視和執行CTest。

  6. 檢視測試結果:CTest將測試結果輸出到終端或CTest的GUI前端。你將看到每個測試的狀態(透過、失敗、跳過等),以及相關的錯誤訊息和日誌檔案。

這只是一個簡單的CTest使用示例,你可以根據專案的特定需求和測試要求自定義和擴充套件CTest的功能。參閱CTest文件以獲取更多詳細資訊和更高階的CTest配置選項。

記住,在編寫測試指令碼時,應該儘可能涵蓋專案的各方面,並驗證預期的行為和結果。測試是質量保證過程的重要組成部分,能夠提供反饋以確保專案的正確性和可靠性。

五、安裝

5.1 Linux的rpath機制

在 CMake 中,可以透過使用 CMAKE_INSTALL_RPATH 或者 CMAKE_BUILD_RPATH 屬性來設定可執行檔案的 rpath。

  1. CMAKE_INSTALL_RPATH:用於指定在安裝過程中可執行檔案的 rpath。可執行檔案會被安裝到目標目錄,同時 rpath 會被設定為 CMAKE_INSTALL_RPATH 指定的路徑。可以透過在 CMakeLists.txt 檔案中設定該屬性來達到目的。
set(CMAKE_INSTALL_RPATH <path>)

其中 <path> 是要設定的 rpath 的路徑。

  1. CMAKE_BUILD_RPATH:用於指定在構建過程中可執行檔案的 rpath。可執行檔案在構建過程中會被放置在構建目錄,同時 rpath 會被設定為 CMAKE_BUILD_RPATH 指定的路徑。可以透過在 CMakeLists.txt 檔案中設定該屬性來達到目的。
set(CMAKE_BUILD_RPATH <path>)

同樣,<path> 是要設定的 rpath 的路徑。

注意:

  • <path> 可以是多個路徑的列表。可以使用 ; 分隔路徑。
  • 在設定 CMAKE_INSTALL_RPATHCMAKE_BUILD_RPATH 時,可以使用 CMake 的 generator expressions,以便根據不同的配置和平臺展開不同的路徑。
  • CMAKE_INSTALL_RPATHCMAKE_BUILD_RPATH 可以同時設定,並且它們的優先順序會根據具體的情況決定。

設定 CMAKE_INSTALL_RPATHCMAKE_BUILD_RPATH 後,重新執行 CMake 構建過程,可執行檔案的 rpath 會被相應地設定。這樣,在執行時可執行檔案會使用 rpath 指定的路徑來查詢動態庫,從而確保動態庫能夠正確載入。

需要注意的是,rpath 機制在不同的作業系統上有所不同,具體的設定和行為可能會有所差異。確保根據目標平臺和作業系統的要求進行適當的配置和測試。

5.2 CMAKE_INSTALL_RPATH的使用案例

以下是一個使用 CMAKE_INSTALL_RPATH 的簡單示例:

cmake_minimum_required(VERSION 3.12)
project(MyApp)

# 設定可執行檔案的原始檔
set(SOURCES main.cpp)

# 設定生成可執行檔案
add_executable(myapp ${SOURCES})

# 設定動態庫的搜尋路徑
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")

# 安裝規則
install(TARGETS myapp
    RUNTIME DESTINATION bin
    DESTINATION "${CMAKE_INSTALL_PREFIX}"
    # 設定 rpath 為 CMAKE_INSTALL_RPATH 變數的值
    INSTALL_RPATH "${CMAKE_INSTALL_RPATH}"
)

在這個示例中,假設專案目錄結構如下:

.
├── CMakeLists.txt
├── main.cpp
└── lib
    └── mylib.so
  • main.cpp 是可執行檔案的原始檔。
  • lib 目錄下包含一個名為 mylib.so 的動態庫檔案。

在 CMakeLists.txt 中,我們首先定義了可執行檔案的原始檔,並透過 add_executable() 命令新增了一個名為 myapp 的可執行目標。

接下來,我們透過 set() 命令設定了 CMAKE_INSTALL_RPATH 的值為 $ORIGIN/lib。這裡使用了 $ORIGIN 變數,它表示可執行檔案所在的目錄。

最後,在安裝規則中,我們使用 install() 命令將可執行檔案安裝到指定的目錄,並透過 INSTALL_RPATH 屬性將 CMAKE_INSTALL_RPATH 的值傳遞給 rpath。

在構建並執行專案時,可執行檔案 myapp 將被安裝到目標目錄(例如安裝到 /usr/local/bin),同時 rpath 將被設定為 /usr/local/bin/lib。這樣在執行時,可執行檔案就能夠找到並載入位於 lib 目錄下的動態庫檔案。

請注意,實際的 rpath 設定可能因作業系統、CMake 版本和專案結構而有所不同,上述示例僅為了說明如何使用 CMAKE_INSTALL_RPATH。根據具體的需求和情況,可能需要進行適當的調整。

5.3 CMAKE_BUILD_RPATH的使用案例

以下是一個使用 CMAKE_BUILD_RPATH 的簡單示例:

cmake_minimum_required(VERSION 3.12)
project(MyApp)

# 設定可執行檔案的原始檔
set(SOURCES main.cpp)

# 設定生成可執行檔案
add_executable(myapp ${SOURCES})

# 設定動態庫的搜尋路徑
set(CMAKE_BUILD_RPATH "$ORIGIN/lib")

# 構建規則
set_target_properties(myapp PROPERTIES
    # 設定 rpath 為 CMAKE_BUILD_RPATH 變數的值
    BUILD_RPATH "${CMAKE_BUILD_RPATH}"
)

在這個示例中,假設專案目錄結構如下:

.
├── CMakeLists.txt
├── main.cpp
└── lib
    └── mylib.so
  • main.cpp 是可執行檔案的原始檔。
  • lib 目錄下包含一個名為 mylib.so 的動態庫檔案。

在 CMakeLists.txt 中,我們首先定義了可執行檔案的原始檔,並透過 add_executable() 命令新增一個名為 myapp 的可執行目標。

接下來,我們透過 set() 命令設定了 CMAKE_BUILD_RPATH 的值為 $ORIGIN/lib。這裡使用了 $ORIGIN 變數,它表示構建目錄。

最後,使用 set_target_properties() 命令,將 BUILD_RPATH 屬性設定為 CMAKE_BUILD_RPATH 的值。這樣,在構建過程中,可執行檔案 myapp 的 rpath 將被設定為構建目錄下的 lib 目錄。

在構建專案時,生成的可執行檔案 myapp 將具有指定的 rpath,以便在執行時正確載入位於構建目錄下的動態庫檔案。

請注意,實際的 rpath 設定可能因作業系統、CMake 版本和專案結構而有所不同,上述示例僅為了說明如何使用 CMAKE_BUILD_RPATH。根據具體的需求和情況,可能需要進行適當的調整。

六、閉源包引用

#glfw
add_library(glfw STATIC IMPORTED)
set_property(TARGET glfw PROPERTY IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/extern/glfw/lib-vc2019/glfw3.lib)
target_include_directories(glfw INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/extern/glfw/include)


#引用
target_link_libraries(main glfw)

七、vcpkg包管理

6.1 安裝

官方連結

  1. 開啟終端。

  2. 克隆 Vcpkg 儲存庫:

    git clone https://github.com/microsoft/vcpkg.git
    
  3. 進入 Vcpkg 目錄:

    cd vcpkg
    
  4. 執行 bootstrap-vcpkg.sh 指令碼以初始化和構建 Vcpkg:

    ./bootstrap-vcpkg.sh
    
    sudo ln -s $HOME/vcpkg/vcpkg /usr/bin
    
    cat "EXPORT VCPKG_ROOT=/home/chao/vcpkg" &>> ~/.bashrc
    
  5. 執行以下命令將 Vcpkg 安裝到系統目錄 /usr/local

    sudo ./vcpkg integrate install
    

    輸入您的密碼以進行身份驗證。

  6. 現在,Vcpkg 已成功安裝到您的 Ubuntu 系統上。

  7. 使用 Vcpkg 安裝和管理庫。

    • 在終端中,使用以下命令安裝所需的庫:

      ./vcpkg install <library-name>
      

      <library-name> 替換為您要安裝的庫的名稱。

    • 安裝完成後,您可以在程式碼中使用 Vcpkg 安裝的庫來進行開發和構建。

請注意,使用 Vcpkg 在 Ubuntu 上安裝庫可能需要滿足一些依賴項和構建工具的要求。在某些情況下,您可能需要在 Ubuntu 上預先安裝一些依賴項,以便成功安裝和使用特定的庫

6.2 vcpkg.json

  1. 在專案的根目錄下建立一個名為 vcpkg.json 的檔案。

  2. 開啟 vcpkg.json 並編輯檔案,按照 JSON 格式的語法來定義您的庫和其設定。下面是一個示例:

    {
      "name": "myproject",
      "version": "0.1",
      "dependencies": [
        {
          "name": "library1",
          "version": "1.2"
        },
        {
          "name": "library2",
          "version": "2.0"
        }
      ]
    }
    

    上述示例中,“name” 指定專案名稱,“version” 指定專案版本,“dependencies” 下列出了專案所依賴的庫。

  3. 定義庫的依賴項。每個依賴項都需要指定名稱 (“name”) 和版本 (“version”)。此外,您還可以指定特定的庫功能(如果有)。

  4. 儲存 vcpkg.json 檔案。

  5. 在終端中,導航到專案的根目錄。

  6. 執行以下命令,使用 Vcpkg 安裝專案依賴項:

    vcpkg install
    

    Vcpkg 將根據 vcpkg.json 檔案中定義的庫和版本資訊,自動下載、安裝和構建所需的庫。

  7. 安裝完成後,您可以在專案中使用已安裝的庫進行開發和構建。

vcpkg.json 是一個方便的方法,可以在專案級別上配置 Vcpkg。透過使用該檔案,可以輕鬆地與其他人共享專案依賴項和配置,並確保每個人都能夠使用相同的庫版本。

6.3 多包管理器共存

if(USE_VCPKG)
    find_package(<VCPKG_PACKAGE> REQUIRED)
    include_directories(${<VCPKG_PACKAGE>_INCLUDE_DIRS})
    target_link_libraries(MyProject ${<VCPKG_PACKAGE>_LIBRARIES})
else()
    find_package(<APT_PACKAGE> REQUIRED)
    include_directories(${<APT_PACKAGE>_INCLUDE_DIRS})
    target_link_libraries(MyProject ${<APT_PACKAGE>_LIBRARIES})
endif()