CMake 基础

本文介绍了CMake的基本用法,包括创建CMakeLists.txt脚本,管理源文件、编译设置、库链接,以及如何使用CMake进行跨平台项目的构建。特别强调了添加可执行程序、库、宏定义和处理不同平台的库文件格式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CMake 基础概述

  1. 源代码是怎么变成可执行程序的
    通过编译器处理(一系列工具链),分为四部分:预处理器,编译(gcc或g++),汇编,链接(连接器)

    最后得到一个二进制文件,写 makefile(跨平台性不好),在使用 make 进行批处理,当然还有一种处理方式是 CMake ,创建一个脚本文件CMakeLists.txt,再执行CMake指令

  2. CMake 项目准备工作

    单个注释:

    # 此为单个注释
    
    #[[
        这个为注释块
    ]]
    
    # 这个是使用的 cmake 最低版本,当然也可以指定不写的
    cmake_minimum_required(VERSION 3.0)
    
    # 当然,可以指定多个参数 [项目名称] [项目版本] [项目描述] [web 主页地址] [支持的语言]
    project([] [] [] [] [])
    
    # 定义工程生成一个可执行程序
    # [可执行程序] [源文件名称],可以使用空格和分号进行分隔
    add_executable([] [])
    

    使用 cmake .. 命令执行 CMakweLists.txt,使用 make 执行 makefile

  3. 我们不可能把所有的 cpp 文件都写进去

    set 的使用,set 相当于定义一个变量

    
    set(app ${SRC})
    # 指定程序输出路径
    set(EXECUTABLE_OUTPUT_PATH /home/hoe/hoe-cpp/cmakeStudy/source/build)
    # 指定 C++ 使用标准
    set(CMAKE_CXX_STANTARD 11)
    

    如果需要输出库的路径,则把第一个参数改为LIBRARY_OUTPUT_PATH,其他的不用变化

    在外部设置宏

    cmake .. -DCMAKE_CXX_STANDARD = 11
    
  4. 上面的代码并没有解决本质的问题

    使用aux_source_directory(),会把搜索的所有文件放在第二个参数里面,第一个参数为搜索的路径

    常用宏:PROJECT_SOURCE_DIRCMAKE_CURRENT_SOURCE_DIR

    aux_source_directory(${PROJECT_SOURCE_DIR} SRC)
    

    使用file命令,第一个参数指定搜索方式,如:GLOB/GLOB_RECURSE,此方式为递归搜索,第二个参数用来存放搜索出来的值,第三个参数用来搜索文件路径和文件类型

    CMAKE_CURRENT_SOURCE_DIR

    file(GLOB SRC ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
    
  5. 指定头文件

    当我们创建的项目分为头文件和cpp文件的时候,那么会寻找不到当前的头文件,使用include_directories可以追寻到头文件,如:

    include_directories(headPath)
    
  6. 制作一个库

    add_library([制作库的名字] [指定库的类型:静态库static,动态库:shared] [源文件1] [源文件2] ...)

    注意:动态库是有执行权限的,静态库没有执行权限;发布给使用者需要 .a(静态库)和头文件

    指定库的路径:LIBRARY_OUTPUT_PATH->适用于动态库和静态库,EXCUTABLE_OUTPUT_PATH->适用于动态库,适用方法还是 set

  7. 指定库位置

    link_libraries([静态库] [静态库])
    

    通过这个命令可以链接一个或多个,一般为掐头去尾,如 libxxx.a,去掉头和去掉尾后是 xxx

    如果是系统提供的,那么只需要提供名字就行,如果是自定义的,需要指定路径,如下:

    link_directories([库路径] [库路径])
    

    当然指定路径也可以指定多个路径,相当于添加环境变量

    注意:如果使用的静态库,那么静态库会打包到可执行文件里面去,如果是动态库,并不会打包到可执行文件里面,如果出现undefine_...字样,那么需要检查link_directories是否有写正确

    容易出现的错误:

    1). 库函数于头文件函数不在一个文件夹内(由于库文件引用的头文件造成)

  8. 链接动态库

    target_link_libraries([目标] [库] [库])
    

    目标可能是一个动态库,也可以是静态库,也可能是一个源文件,也可能是可执行文件,但是要注意,使用此关键字需要写在已经生成的文件下或者写在已经存在/生成的可执行文件下

    默认权限是public,且一般写在最后,要保证目标已经存在

    问题:使用动态库会不会造成混乱?多个进程使用同一个动态库。

    每一个进程有一个虚拟地址空间,有一个区域叫做动态加载区,当被加载进来过后,会被映射到对应的虚拟地址空间中,一般是在虚拟地址空间进行操作,而 CPU 里面有一个 MMU,MMU 会把虚拟地址中的数据映射到物理地址上去,每一个进程都对应有一块不同的地址动态库里面的操作函数只有一套,通过这个函数计算出的结果是在不同的物理地址上进程存储,所以说最终的数据是不会错乱的
    
  9. 消息输出

    message([重要级别] [消息])
    

    如果什么都不写,那么说明很重要

    STATUS: 非重要消息

    WARNING: CMake警告,会继续执行

    AUTHOR_WARNING:CMake警告,会继续执行

    SEND_ERROR:CMake错误,继续执行,但是会跳过生成的步骤

    FATAL_ERROR:CMake错误,终止所有处理过程

  10. 字符串的拼接和追加

    • 使用 set,把第二个开始的值拼接到第一个上,但是第一个值会被覆盖

      set(str1 str2 str3)

    • 使用 list, 是一个列表(是一个变量,里面是字符串), 追加的子字符串,底层上是通过分号进行拼接

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

    • 删除字符串,删除指定的文件,其中 SRC 为原字符串,第三个参数为要删除的字符串

      list(REMOVE_ITEM SRC ${PROJECT_SOURCE_DIR}/src/main.cpp)

    • 其他参数,如[LENGTH]为长度(会写入第三个参数中),[GET]获取对应的元素

      list(GET <list> <element index> [<element index> ...])

    • 将列表里面的元素连接成一个字符串,第三个参数是连接符,最后一个参数是新生成的字符串

      list(JOIN <list> <value> <output variable>)

    • 查找列表中的某一个元素,如果没有找到,那么最后一个值是 -1

      list(FIND <list> <value> <output variable>)

    • 向列表中插入元素

      list(INSERT <list> <element_index> <element> [<element> ...])

    • 将元素插入到列表的前面

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

    • 将列表的最后一个元素删除, 为弹出的值

      list(POP_BACK <list> [<out-var> ...])

    • 将列表中第一个元素移除

      list(POP_FRONT <list> [<out-var> ...])

    • 将指定索引的元素从列表中移除(注意:不能超出索引的指定范围)

      list(REMOVE_AT <list> <index> [<value> ...])

    • 移除列表中的重复元素

      list(REMOVE_DUPLICATES <list>)

    • 列表的翻转(倒序)

      list(REVERS <list>)

    • 列表排序

      list(SORT <list> [COMPARE <compare>] [CASE <case>] [ORDER <order>])

    COMPARE: 指定排序方法,如下几种可选
    
        STRING: 按照字母顺序进行排序,默认排序方法
        FILE_BASENAME: 如果是一系列路径名,会使用 basename 进行排序
        NATURE: 使用自然数顺序排序
    
    CASE:指明是否大小写敏感
    
        SENSITIVE:按照大小写敏感的方式进行排序,为默认值
        INSENSITIVE:按照大小写不敏感方式进行排序
    
    ORDER:指明排序的顺序
    
        ASCENDING:按照升序排列,为默认值
        DESCENDING:按照降序排列
    
  11. 宏定义

    add_definitions(-D[宏名字])

  12. 嵌套 CMake

    在父文件里面的变量可以被子文件中使用,但是反向不行

    # 第一个参数一般指向了子节点的 CMakeLists.txt 文件
    add_subdirectory([source_dir [binary_dir] [EXCLUDE_FROM_ALL])
    
    • source_dir: 指定子目录里面的CMakeLists.txt文件中的源文件和代码文件位置
    • binary_dir: 制定了输出文件的路径,一般不需要指定,忽略即可
    • EXCLUDE_FROM_ALL: 在子路径下的目标默认不会被包含到父路径的ALL目标中,并且也会被排除在IDE工程文件之外,用户必须显示构建在子目录下的目标

    库生成思路:如果源文件比较多,生成动态库,如果源文件比较少,那么生成静态库,当然,在链接库的时候还是要注意库文件生成的位置,还有使用库文件的头文件以及使用cmake中引用的路径

其他

Linux 指令的使用:

# 此命令为拷贝整个文件夹
cp file1 file2 -r

# 使用 gcc 定义一个宏
gcc test.c DEBUG -G app

库文件生成

Windows 平台上 MinGW 环境下,生成的动态库是 .dll 结尾的格式,.a 结尾的为静态库,但是如果使用微软环境下(MSVC 环境),生成格式的静态库为 .lib 结尾的格式,其中间还会生成 .pdb 格式文件

注意

  1. windows平台下链接动态库的时候,需要手动放到可执行文件目录下,也可以把当前库放在环境变量中
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值