CMake用法笔记

CMake使用教程笔记

笔记说明:

整理自苏丙榅的博客,链接: https://subingwen.cn/cmake/CMake-primer/

符号说明:

  • “<>”代表要填充的内容
  • "[]"代表可追加的内容或选项

目录

1. 使用方法

  1. 项目目录下新建文件CMakeLists.txt
  2. 文件必须名为CMakeLists.txt
  3. 在CMakeLists.txt同级目录下创建build文件夹
  4. 在build目录下,执行"cmake .."命令构建项目,生成可执行程序或库文件

2. 基础功能

2.1 指定最小CMake版本

cmake_minimum_required(VERSION 3.0.0)

ps:在CMakeLists.txt中可写可不写,若CMake版本小于指定的版本,则build报错

2.2 注释

# 被注释的行
cmake_minimum_required(VERSION 3.0.0)
#[[被注释的块
被注释的块
被注释的块]]
cmake_minimum_required(VERSION 3.0.0)

2.3 定义工程名称

project(<工程名称>)

project:定义工程名称,并可指定工程的版本、工程描述、web主页地址、支持的语言(默认情况支持所有语言),如果不需要这些都是可以忽略的,只需要指定出工程名字即可.

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

2.4 添加可执行程序

add_executable(<可执行程序名> <源文件名>)

源文件名可以有多个,例:

add_executable(app add.cpp div.cpp main.cpp)

2.5 定义与使用变量

SET(<变量名> [VALUE] [CACHE TYPE DOCSTRING [FORCE]])

例:

set(SRC_LIST add.c;div.c;main.c;mult.c;sub.c)
add_executable(app  ${SRC_LIST})

2.6 指定C++标准

  1. 方法1:在CMakeLists.txt中写入

    set(CMAKE_CXX_STANDARD 11)
    #set(CMAKE_CXX_STANDARD 14)
    #set(CMAKE_CXX_STANDARD 17)
    
  2. 方法2:在执行cmake时指定CMAKE_CXX_STANDARD宏的值

    cmake CMakeLists.txt文件路径 -DCMAKE_CXX_STANDARD=11
    

2.7 CMakeLists.txt的路径

有两种宏来获取CMakeLists.txt文件所在目录的路径

PROJECT_SOURCE_DIR
CMAKE_CURRENT_SOURCE_DIR

2.8 指定输出路径

set(HOME /home/zzh/code)
set(EXECUTABLE_OUTPUT_PATH ${HOME}/bin)
  • 如果这个路径中的子目录不存在,会自动生成,无需自己手动创建

2.9 搜索文件

  1. 方法1:aux_source_directory

    aux_source_directory(<路径> <变量名>)
    

    例:

    aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src SRC_LIST)
    add_executable(app  ${SRC_LIST})
    
  2. 方法2:file

    file(GLOB/GLOB_RECURSE <变量名> <要搜索的文件路径和文件类型>)
    
    • GLOB: 将指定目录下搜索到的满足条件的所有文件名生成一个列表,并将其存储到变量中。
    • GLOB_RECURSE:__递归__搜索指定目录,将搜索到的满足条件的文件名生成一个列表,并将其存储到变量中。

    例:

    file(GLOB MAIN_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
    file(GLOB MAIN_HEAD ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h)
    
    • 要搜索的文件路径和文件类型可加双引号,也可不加
    file(GLOB MAIN_HEAD "${CMAKE_CURRENT_SOURCE_DIR}/include/*.h")
    

2.10 包含头文件

include_directories(<头文件路径>)

2.11 制作动态库或静态库

2.11.1 静态库

add_library(<库名称> STATIC <源文件1> [源文件2] ...)
  • 在Linux中,静态库名字分为三部分:lib+库名字+.a,此处只需要指定出库的名字就可以了,另外两部分在生成该文件的时候会自动填充。在Windows中虽然库名和Linux格式不同,但也只需指定出名字即可。

例:

cmake_minimum_required(VERSION 3.0)
project(CALC)
include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
add_library(calc STATIC ${SRC_LIST})

可生成静态库文件libcalc.a

2.11.2 动态库

add_library(<库名称> SHARED <源文件1> [源文件2] ...)
  • 在Linux中,动态库名字分为三部分:lib+库名字+.so,此处只需要指定出库的名字就可以了,另外两部分在生成该文件的时候会自动填充。在Windows中虽然库名和Linux格式不同,但也只需指定出名字即可。

例:

cmake_minimum_required(VERSION 3.0)
project(CALC)
include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
add_library(calc SHARED ${SRC_LIST})

可生成动态库文件libcalc.so

2.11.3 指定库文件的输出路径

  1. 适用于动态库

    由于在Linux下生成的动态库默认是有执行权限的,所以可以按照生成可执行程序的方式去指定它生成的目录:

    set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
    
  2. 都适用

    set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
    

3. 引入库文件

3.1 指定库目录

如果库不是系统提供的(自己制作或者使用第三方库)可能出现静态/动态库找不到的情况,此时需指定库的路径:

link_directories(<库的路径>)

3.2 链接静态库

link_libraries(<static lib> [<static lib>...])
  • 参数1:指定出要链接的静态库的名字
    可以是全名 libxxx.a
    也可以是掐头(lib)去尾(.a)之后的名字 xxx
  • 参数2-N:要链接的其它静态库的名字

3.3 链接动态库

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

用于指定一个目标(如可执行文件或库)在编译时需要链接哪些库,编译器会在链接阶段将这些库与目标文件合并,确保目标文件能够正确调用这些库中的函数。它支持指定库的名称、路径以及链接库的顺序。

  1. target:指定目标文件的名称

    • 该文件可以是一个源文件
    • 该文件可以是一个动态库/静态库文件
    • 该文件可以是一个可执行文件
  2. PRIVATE|PUBLIC|INTERFACE:动态库的访问权限,默认为PUBLIC

    • 如果各个动态库之间没有依赖关系,无需做任何设置,三者没有没有区别,一般无需指定,使用默认的 PUBLIC 即可。

    • 动态库的链接具有传递性,如果动态库 A 链接了动态库B、C,动态库D链接了动态库A,此时动态库D相当于也链接了动态库B、C,并可以使用动态库B、C中定义的方法。

      target_link_libraries(A B C)	# A链接B、C
      target_link_libraries(D A)		# D链接A
      
    • PUBLIC:在public后面的库会被Link到前面的target中,并且里面的符号也会被导出,提供给第三方使用。

    • PRIVATE:在private后面的库仅被link到前面的target中,并且终结掉,第三方不能感知你调了啥库

    • INTERFACE:在interface后面引入的库不会被链接到前面的target中,只会导出符号。

  3. item:库名称

3.4 链接动态库的细节

  1. 链接语句在生成可执行文件语句之后

    动态库的链接和静态库是完全不同的:

    • 静态库会在生成可执行程序的链接阶段被打包到可执行程序中,所以可执行程序启动,静态库就被加载到内存中了。
    • 动态库在生成可执行程序的链接阶段__不会__被打包到可执行程序中,当可执行程序被启动并且调用了动态库中的函数的时候,动态库才会被加载到内存
      因此,在cmake中指定要链接的动态库的时候,应该将命令写到生成了可执行文件之后
    add_executable(app ${SRC_LIST})
    target_link_libraries(app pthread)
    
  2. 指定第三方库的路径

    在链接第三方库时,而不是链接系统提供的库(如libpthread.so),需要使用 link_directories 单独指定第三方库目录。如果不指定,程序执行就不知道动态库被放在什么位置

    cmake_minimum_required(VERSION 3.0)
    project(TEST)
    file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
    # 指定源文件或者动态库对应的头文件路径
    include_directories(${PROJECT_SOURCE_DIR}/include)
    # 指定要链接的动态库的路径
    link_directories(${PROJECT_SOURCE_DIR}/lib)
    # 添加并生成一个可执行程序
    add_executable(app ${SRC_LIST})
    # 指定要链接的动态库
    target_link_libraries(app pthread calc)
    

3.5 引入库文件总结

target_link_libraries 和 link_libraries 是 CMake 中用于链接库的两个命令,都可以用于链接动态库和静态库,但它们的使用场景和功能有所不同。

  1. target_link_libraries

    用于指定一个目标(如可执行文件或库)在编译时需要链接哪些库。它支持指定库的名称、路径以及链接库的顺序。

    优点:

    • 更精确地控制目标的链接库。
    • 可以指定库的不同链接条件(如调试版本、发布版本)。
    • 支持多个目标和多个库之间的复杂关系。
    • 更加灵活和易于维护,特别是在大型项目中。
  2. link_libraries

    用于设置全局链接库,这些库会链接到之后定义的所有目标上。它会影响所有的目标,适用于全局设置,但不如 target_link_libraries 精确。

    缺点:

    • 缺乏针对具体目标的控制,不适合复杂的项目结构。
    • 容易导致意外的依赖关系,因为它对所有目标都生效。
    • 一旦设置,全局影响可能导致难以追踪的链接问题。

4. 区分操作系统平台

利用宏CMAKE_SYSTEM_NAME来识别Windows、Linux、Macos

IF (CMAKE_SYSTEM_NAME MATCHES "Linux") 
    MESSAGE(STATUS "current platform: Linux ") 
ELSEIF (CMAKE_SYSTEM_NAME MATCHES "Windows") 
    MESSAGE(STATUS "current platform: Windows") 
ELSEIF (CMAKE_SYSTEM_NAME MATCHES "Darwin") 
    MESSAGE(STATUS "current platform: Mac OS X") 
ELSE () 
    MESSAGE(STATUS "other platform: ${CMAKE_SYSTEM_NAME}") 
ENDIF (CMAKE_SYSTEM_NAME MATCHES "Linux") 

5. 区分release和debug

cmake的默认编译方式是release

单配置生成器下,使用CMAKE_BUILD_TYPE变量就能拿到当前是debug还是release

  1. 方法1:判断CMAKE_BUILD_TYPE

    if(CMAKE_BUILD_TYPE STREQUAL "Debug")
        # 使用 Debug 库
    elseif(CMAKE_BUILD_TYPE STREQUAL "Release")
        # 使用 Release 库
    endif()
    
  2. 方法2:配置CMAKE_BUILD_TYPE

    if("${CMAKE_BUILD_TYPE}" STREQUAL "")
        set(CMAKE_BUILD_TYPE "Release")
    endif()
    
posted @ 2025-05-23 14:45  学习仍将继续  阅读(139)  评论(0)    收藏  举报