使用模板寫 CMakeLists


簡介

隨着項目源文件增多,以及外部庫、跨平台等需求,會使 makefile 越來越復雜。CMake 工具可以很好的解決這個問題,通過 CMakeLists.txt 文件中的設置項,CMake 可以自動生產 makefile 等控制編譯的文件,然后可以使用 make 對項目進行編譯。

一個最小的 CMakeLists.txt 文件包含以下內容

cmake_minimum_required(VERSION 3.10)
project(project_name)
add_executable(main main.cc)  # 這里指明由 main.cc 生成可執行文件 main

下為一個常用的 CMakeLists 模板:

cmake_minimum_required(VERSION 3.10)
project(CMakeTemplate)

set(CMAKE_CXX_STANDARD 11)

message(STATUS "Start to deal with the project: ${PROJECT_NAME}")

find_package(OpenCV REQUIRED)
if (OpenCV_FOUND)
    include_directories(${OpenCV_INCLUDE_DIRS})
endif (OpenCV REQUIRED)

include_directories(${PROJECT_SOURCE_DIR}/include)

add_library(static_lib ${PROJECT_SOURCE_DIR}/static_src)

add_executable(CMakeTemplate ${PROJECT_SOURCE_DIR}/src)
target_link_libraries(CMakeTemplate PUBLIC static_lib)

CMakeLists 常用指令

對 CMake 程序的最小版本需求

cmake_minimum_required(VERSION 3.10)

設置項目名稱

project(CMakeTemplate)
# project()指令會生成一些變量:
#     PROJECT_NAME
#     <projectname>_BINARY_DIR, <projectname>_SOURCE_DIR
#     PROJECT_BINARY_DIR, PROJECT_SOURCE_DIR
# 在內部編譯時二者皆為工程路徑,
# 在外部編譯時 SOURCE_DIR 為指定的路徑,BINARY_DIR 為 cmake 命令執行所在路徑。
# 這里的內部編譯指在 CMakeLists 文件所在目錄進行編譯。
# 實際上多采用外部編譯,先在項目文件夾下建立 build 文件夾,在 build 文件夾中執行 cmake .. 命令。
# 此時的 PROJECT_BINARY_DIR 指 build 文件夾。

設置變量

set(Template_VERSION_MAJOR 0)
set(Template_VERSION_MINOR 1)
#使用變量的方法:${var}

# 指定 C++ 標准
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

# 設置 C/C++ 的編譯選項
# set(CMAKE_C_FLAGS -g)
# set(CMAKE_CXX_FLAGS -g)

向終端傳遞信息

message(STATUS "Version is ${Template_VERSION_MAJOR}.${Template_VERSION_MINOR}")

引入其他的 CMakeLists 文件

include(filename)

編譯控制

# 在 c/c++ 源代碼中使用 #ifdef, #ifndef 控制不同環境下的代碼編譯,
# 但是 CMake 無法理解 c/cpp/h 文件,需要使用 in 文件保存設置。
# 例如 config.h.in 中的內容
#cmakedefine USE_CUDA
# configure_file() 可以用 in 文件生成 h 文件,通過編寫 in 文件可以控制 c/c++ 源代碼中的宏
configure_file (
    "${PROJECT_SOURCE_DIR}/config.h.in"
    "${PROJECT_SOURCE_DIR}/config.h"
)

# 直接添加選項
option(USE_CUDA "Using CUDA" on) # 默認為開啟
# 使用 CMake 的語法可以控制文件級的編譯
if(USE_CUDA)
    # command of compiling control if USE_CUDA defined
else
    # command of compiling control if USE_CUDA not defined
endif(USE_CUDA)

設置工程的子目錄

add_subdirectory(src bin)  # src 中必須也有 CMakeLists.txt,實現了嵌套。
# src為源文件目錄,bin為編譯目標目錄(bin, lib),后者可不設置。
# src中的編譯,通過其內的 CMakeLists.txt 控制,
# 進入子目錄的 CMakeLists.txt, 不會改變 PROJECT_BINARY_DIR, PROJECT_SOURCE_DIR,
# 但 CMakeLists.txt 產生的另外兩個變量會改變。
# CMAKE_CURRENT_SOURCE_DIR,CMakeLists.txt 所在處
# CMAKE_CURRRENT_BINARY_DIR,內部編譯時為 CMakeLists.txt 所在處,外部編譯時為編譯目錄

# 此外可以對編譯后的可執行文件和庫文件分別指定輸出目錄
#set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
#set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)

添加庫

# 默認為 static
# static 靜態庫,后會打包至可執行文件
aux_source_directory(${PROJECT_SOURCE_DIR}/extra_src STATIC_LIB_SOURCE_DIR)
add_library(static_lib ${STATIC_LIB_SOURCE_DIR})
# shared 動態庫,作為單獨的文件
aux_source_directory(${PROJECT_SOURCE_DIR}/extra_src SHARED_LIB_SOURCE_DIR)
add_library(shared_lib SHARED ${SHARED_LIB_SOURCE_DIR})
# object 中間庫文件,可以鏈接到其他庫
aux_source_directory(${PROJECT_SOURCE_DIR}/object_src OBJECT_LIB_SOURCE_DIR)
add_library(object_lib OBJECT ${OBJECT_LIB_SOURCE_DIR})
target_link_libraries(static_lib object_lib)
# aux_source_directory() 的作用是將目錄下的所有源文件保存為一個變量。
# aux_source_directory() 的替代方案是使用 file(GLOB)
#file(GLOB SOURCE_DIR ${PROJECT_SOURCE_DIR}/src/*.cpp)
# 該指令將所有符合模式的文件名保存在變量中

指定頭文件的搜索路徑

# CMake 有一些默認的頭文件、庫文件的搜索路徑,也就是環境變量,保存在 CMAKE_INCLUDE_PATH 和 CMAKE_LIBRARY_PATH 中,可以使用 set() 進行修改。
# 但通常項目內部的頭文件、庫文件的搜索路徑可以使用下面的指令設定。
# 指定頭文件的搜索路徑
include_directories(${PROJECT_SOURCE_DIR}/include)
# 指定庫文件的搜索路徑
# link_directories(${PROJECT_SOURCE_DIR}/lib)

生成執行文件,並鏈接庫文件

aux_source_directory(${PROJECT_SOURCE_DIR}/src SOURCE_DIR)
add_executable(CMakeTemplate ${SOURCE_DIR})

# 連接庫
target_link_libraries(CMakeTemplate PUBLIC static_lib shared_lib)

# 其它為 target 指定的內容
# 為某個 target 指定頭文件搜索目錄,前面的 include_directories() 可以被所有的 target 使用
target_include_directories(CMakeTemplate "${PROJECT_BINARY_DIR}"/include)
# 為某個 target 指定庫文件搜索目錄,前面的 link_directories() 可以被所有的 target 使用
target_link_directories(CMakeTemplate "${PROJECT_BINARY_DIR}"/lib)
# 注意這兩條指令都必須放在 target 被聲明之后。

使用外部庫

find_package(OpenCV REQUIRED)
# find_package() 會讀取 FindXXXX.cmake 等文件來設置外部庫的屬性。
# CMake 自帶了很多常用庫的 FindXXXX.cmake 文件;
# 如果自己提供 FindXXXX.cmake 文件,需要使用如下指令增加該文件查找目錄,如 ./cmake/modules/
set(CMAKE_MODULE_PATH APPEND "${PROJECT_SOURCE_DIR}/cmake/modules/")
# 很顯然 CMAKE_MODULE_PATH 存放了 .cmake 文件的搜索路徑。

# 查找到外部庫后會返回一些變量
# OpenCV_FOUND, OpenCV_INCLUDE_DIRS, OpenCV_LIBRARIES
if (OpenCV_FOUND)
    include_directories(${OpenCV_INCLUDE_DIRS})
    target_link_libraries(target_name ${OpenCV_LIBRARIES})
endif (OpenCV REQUIRED)

安裝

# 使用 install 可以將程序安裝到系統中,多用於對外發布的程序或庫。
# 不對外不發布的程序可以不寫 install 部分;
# 或者如果不想把系統弄亂,也可以使用 install 將程序和相關文件復制到某個文件夾中。
# cmake 命令的參數 -DCMAKE_INSTALL_PREFIX 用於知道安裝路徑,默認為 /usr/local/

# 安裝目標文件——二進制文件、動態庫、靜態庫
install(TARGETS TargetName SharedLib StaticLib
        RUNTIME DESTINATION ${CMAKE_BINARY_DIR}/release/bin
        LIBRARY DESTINATION ${CMAKE_BINARY_DIR}/release/lib
        ARCHIVE DESTINATION ${CMAKE_BINARY_DIR}/release/lib-static
)
# TARGETS 后跟隨的名稱,都是前面通過 add_executable() 或 add_library() 生成的目標。
# 注意這里會對庫的文件名增加前綴,如 StaticLib 對應的文件為 libStaticLib.a。
# DESTINATION 為安裝路徑。

# 安裝普通文件
install(FILES README.md licence.txt DESTINATION ${CMAKE_BINARY_DIR}/release)
# 默認文件權限 644
# 安裝庫對應的頭文件也是用該指令。

# 安裝腳本等可執行文件,這些文件不是前面使用 add_executable() 或 add_library() 生成的
install(PROGRAMS generator.sh DESTINATION ${CMAKE_BINARY_DIR}/release/bin)
# 默認文件權限 755,這是它與 install(FILE) 的不同之處。

# 安裝整個目錄
install(DIRECTORY res/ DESTINATION ${CMAKE_BINARY_DIR}/release/res)

# 實際的安裝控制可能要復雜的多,在長期項目中可以考慮維護一個較好的安裝腳本,用於 night build

以上內容只是 CMake 的部分內容,其它的內容可以參考官文文檔


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM