CMake构建项目

一、基本的CMake语法

标准的CMake工程结构应该是这样的:

  • CMakeLists.txt
    此CMakeLists.txt是顶层CMake文件,内容包括:

    #指定版本号
    CMAKE_MINIMUM_REQUIRED(VERSION 3.17.5)
    #指定工程名字,工程语言,定义了PROJECT( DIR ),则此CMake文件所在的目录会被视为全局变量PROJECT_SOURCE_DIR的值
    PROJECT(demo4 CXX)
    
    #添加源文件所在的目录
    ADD_SUBDIRECTORY(${PROJECT_SOURCE_DIR}/mylib)
    ADD_SUBDIRECTORY(${PROJECT_SOURCE_DIR}/src)
    
  • build
    build目录是编译工程文件,我们执行cmake命令生成makefile文件就是在此目录中,生成的内容都是中间文件和编译过后的二进制文件。build文件夹下应该包括bin和lib文件夹,bin文件夹中存放最终的可执行文件,lib文件夹下存放依赖库。
    我们会在此文件夹中执行:

    cmake ..
    #执行完上述命令后,就会生成makefile文件
    make
    #执行完上述命令后,就会编译链接源文件
    #进入build目录下盛放入口函数的目录下,一般是src目录,而后执行入口函数二进制文件
    
  • lib
    lib文件夹盛放库源代码文件,库文件的内容都在此文件夹内,此文件夹内的Cmake文件内容如下:

    #如果需要包含头文件,应该加上INCLUDE_DIRECTORIES(PATH)
    INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/include)
    #设置生成的二进制库的存放位置位于build/lib
    SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
    #将源文件添加进一个变量中
    AUX_SOURCE_DIRECTORY(. DIR_LIB_SRCS)
    #static参数指明是静态库,会在编译的时候将二进制代码嵌入最后唯一的可执行文件,
    #shared参数指明是动态库,编译的时候不会将二进制代码嵌入执行文件,而是生成一个二进制文件,在执行时再动态加载这个二进制文件供别的模块调用
    #此处生成了一个二进制库叫做Mylib
    ADD_LIBRARY(Mylib static/shared ${DIR_LIB_SRCS})
    
  • src
    src文件夹盛放项目的源文件,源文件的Cmake文件内容如下:

    #源文件可能会用到其他文件夹下的头文件,增加头文件寻找路径
    INCLUDE_DIRECTORIES(path)
    #设置产生的二进制文件放在bin文件夹中
    #PROJECT_BINARY_DIR的含义是在那个文件夹中执行cmake命令
    #哪个文件夹就是PROJECT_BINARY_DIR
    SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
    #将当前文件夹下的源文件打包为DIR_SRCS变量
    AUX_SOURCE_DIRECTORY(./ DIR_SRCS)
    #将源文件编译为demo二进制文件
    ADD_EXECUTABLE(demo ${DIR_SRCS})
    #指明demo二进制文件需要链接的二进制库,此处Mylib正是我们在lib目录下的
    #CMake文件中定义的二进制库
    TARGET_LINK_LIBRARIES(demo Mylib)
    
  • include
    此文件夹存放头文件

二、自定义编译选项

通过ccmake实现自定义编译选项

一般情况下,使用宏来确定代码到底如何执行。
CMake支持在编译时动态调整宏。
具体而言:

  1. 假设有一个CMake文件:
    CMakeLists.txt

    OPTION(TEST "define for TEST" ON)
    
    #假设PATH1代表一个文本文件,PATH2代表根据此文本文件生成的头文件
    CONFIGURE_FILE("PAHT1" "PATH2")
    
  2. 假设有PATH1指代的文本文件:

    #cmakedefine TEST
    
  3. 执行cmake .
    会生成PATH1对应的文本文件指定的头文件
    这其中的过程是这样的:
    OPTION中定义的变量TEST和PATH1文件中指定的宏TEST绑定,因为我们默认TEST为ON,所以宏TEST被定义了
    生成的头文件内容为:

    #define TEST
    

    这个头文件可以被其他文件包含,一旦被包含,也就相当于包含了#define TEST,TEST就被宏定义了。我们在源文件中可以通过

    #ifdef TEST
    	xxx
    #else
    	xxx
    #endif
    

    来指定执行部分代码。

  4. 以上我们通过一个OPTION命令指定了头文件中有TEST的宏定义。如何让TEST没有宏呢?
    执行ccmake .
    弹出的界面中使用方向键上下选择宏,回车改变宏的值,ON为开启,OFF为关闭。
    修改完后,按c键保存配置,再按g键生成新的头文件。
    之后重新执行make命令,就可以用新生成的头文件去重新编译代码。

  5. 一般情况下,把所有开关放在options.txt文件中,在顶层CMakeLists.txt文件中使用INCLUDE执行将开关内容包含进来。

  6. 在执行cmake命令的时候,可以通过-Dxxx=ON/OFF来指定宏开关一开始对应的值。

三、使用CMake和VScode搭建Debug环境

  1. 在顶层CMakeLists.txt中添加:

    ADD_DEFINITIONS(-std=c++11)
    SET(CMAKE_BUILD_TYPE "Debug")
    
    #debug模式编译,参数-O0表示完全不进行代码优化 -Wall表示打印警告信息
    #调试的选项:
    #-g 调试信息由系统生成,但是gdb可以合理封装使用
    #-ggdb 专门为gdb生成调试信息
    #-g3 调试级别更高,3级别可以调试宏
    #-ggdb3 调试级别为3,且专门为gdb调试器生成调试信息
    SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -ggdb")
    SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")
    
  2. 在VScode文件夹下,点击运行——调试——C++(DLL/GDB)

  3. 对2中生成的launch.json配置文件,修改以下内容

    "program": "${workspaceFolder}/build/bin/demo6",
    

    关于${workspaceFolder},此变量指向的是.vscode文件夹所在的目录

四、 搭建简易工程

建立工程项目目录如下:
在这里插入图片描述
顶层CMakeLists.txt文件:

cmake_minimum_required(VERSION 3.17.5)

project(Test CXX)

#源文件所在子目录
ADD_SUBDIRECTORY(${PROJECT_SOURCE_DIR}/src)

src/CMakeLists.txt文件:

INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/include)

SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/out)

AUX_SOURCE_DIRECTORY(./ DIR_SRCS)

ADD_EXECUTABLE(main ${DIR_SRCS})

执行过程:

  1. 头文件一律放在include文件夹

  2. 源文件一律放在src文件夹

  3. 到build文件夹中执行:

    #执行之前可以通过:
    scl --list  获取从scl安装的gcc各版本
    scl enable devtoolset-11 bash  可以使用最新版本的gcc作为编译器
    
    cmake ..		->通过顶层CMakeLists.txt文件构建makefile
    make			->执行makefile文件,构建项目
    
  4. 项目的输出文件在build/out文件夹下,一个名为main的可执行文件。

  5. 让简易工程处于可调式模式
    在顶层CMakeLists.txt文件中添加以下内容

    SET(CMAKE_BUILD_TYPE "Debug")
    SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -ggdb")
    SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")
    

    重新cmake,make,生成的可执行文件就是可调试的。
    如果不想调试,则将SET(CMAKE_BUILD_TYPE “Debug”)中的"Debug"修改为“Release”,而后重新cmake,make便可。
    【注1】调试需要安装gdb,配置gdb的环境变量。
    【注2】在vscode中,可以使用launch.json文件配置调试,过程如下:

    1. 删除task.json,因为此文件是配置编译动作的文件。
    2. 将launch.json的“prelaunchtask”参数对应选项删除,这是删除了执行调试前编译工作,因为我们已经使用了CMake编译了工程。
    3. 将launch.json中"program": 后的参数为**"${workspaceFolder}/build/out/main"**,针对我们生成的main可执行文件进行调试
    4. 将launch.json 中的 “miDebuggerPath”:后的参数改为gdb的地址(如果有环境变量,可以直接填写gdb)
    5. 如果添加了新的源文件或者删除了原有的源文件,则可在被修改文件目录中把其对应的CMakeLists.txt文件中添加或者删除一个空格。然后直接make就可以重新编译整个工程,并且编译整个工程的时候可以利用以前的已经编译好的文件。
    6. 为什么?假装修改了CMakeLists.txt文件后,执行make,因为生成的makefile顶层文件中实际的项目目标main,是依赖于CMakeLists.txt项目的,所以一旦修改了CMakeLists.txt再执行make命令,就可以重新解析CMakeLists.txt,从而改变我们正在执行的makefile文件,给makefile文件增添或者减少子目标文件。依赖项CMakeLists.txt执行完成后,开始执行工程文件编译,但是因为已经修改了makefile,则会按照新的makefile来编译。但是因为没有改变原来的makefile缓存目录,所以就针对新添加的文件或者修改的文件进行重新编译,而其他未涉及到的源文件则不需要重新编译。

五、 修改文件之后cmake还是make?

修改了CMakeLists.txt文件,就把build文件夹下的CMakeChache.txt文件删掉,然后重新cmake,重新make。
如果修改的是源文件或者头文件,则直接make就可以。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值