【Embedded System】【CMake】Windows下CMake+VSCode的开发环境搭建以及初步认识

一、安装

1.1 CMake

Download CMake

1.2 MinGw(自行配置gcc/g++的系统环境变量)

Release Release of 15.2.0-rt_v13-rev0 · niXman/mingw-builds-binaries

1.3 VSCode

Visual Studio Code - Code Editing. Redefined

1.4 VSCode插件

二、初步使用

2.1 创建一个工程文件夹,然后simple_math.h,simple_math.cpp,main.cpp,编写代码

simple_math.h

cpp 复制代码
#ifndef __SIMPLE_MATH_H__
#define __SIMPLE_MATH_H__



class SimpleMath {
public:
    // 1. 构造函数
    SimpleMath() = default;
    // 2. 析构函数
    ~SimpleMath() = default;
    // 3. 拷贝构造函数(从另一个对象复制成员)
    SimpleMath(const SimpleMath& other) = default;  // 用默认实现(复制base_)
    // 4. 移动构造函数(从临时对象"窃取"资源,这里无资源,仅演示)
    SimpleMath(SimpleMath&& other) noexcept = default;  // 默认实现(移动base_)
    // 5. 拷贝赋值运算符(用另一个对象赋值给当前对象)
    SimpleMath& operator=(const SimpleMath& other) = default;  // 默认实现(复制base_)
    // 6. 移动赋值运算符(用临时对象赋值给当前对象)
    SimpleMath& operator=(SimpleMath&& other) noexcept = default;  // 默认实现(移动base_)

    // 原有运算方法
    double add(double a, double b);
    double subtract(double a, double b);
    double multiply(double a, double b);
    double divide(double a, double b);

private:
    double base_ = 0.0;  // 新增一个成员变量(用于演示构造函数的作用)
};




#endif

simple_math.cpp

cpp 复制代码
#include "simple_math.h"
#include <stdexcept>

double SimpleMath::add(double a, double b) {
    return a + b;
}

double SimpleMath::subtract(double a, double b) {
    return a - b;
}

double SimpleMath::multiply(double a, double b) {
    return a * b;
}

double SimpleMath::divide(double a, double b) {
    if (b == 0) {
        throw std::invalid_argument("除数不能为0!");
    }
    return a / b;
}

main.cpp

cpp 复制代码
#include <iostream>
#include <string>
#include <cstdint>

#include "simple_math.h"

using namespace std;

int main() {
    SimpleMath math;  // 使用默认构造函数
    cout << "加法: " << math.add(5, 3) << endl;
    cout << "减法: " << math.subtract(5, 3) << endl;
    cout << "乘法: " << math.multiply(5, 3) << endl;
    try {
        cout << "除法: " << math.divide(5, 1) << endl;
    } catch (const std::invalid_argument& e) {
        cerr << "错误: " << e.what() << endl;
    }
    return 0;
}

2.2 编写主要文件 CMakeLists.txt

cpp 复制代码
cmake_minimum_required(VERSION 3.10)

project(E7_test_project)

add_executable(E7_test_project main.cpp simple_math.cpp)

2.3 在vscode里按下ctrl+shift+p,输入下面的指令,配置cmake构建工程里编译代码的编译器,选择mingw

复制代码
CMake: configure

或者在CMakeLists.txt里指定

cpp 复制代码
set(CMAKE_C_COMPILER "D:/mingw64/bin/gcc.exe")  # 替换为你MinGW的gcc路径
set(CMAKE_CXX_COMPILER "D:/mingw64/bin/g++.exe")  # 替换为你MinGW的g++路径
set(CMAKE_SYSTEM_NAME Windows)

cmake_minimum_required(VERSION 3.10)

project(E7_test_project)

add_executable(E7_test_project main.cpp simple_math.cpp)

2.4 新建文件夹build,cd build 在命令行进入文件夹build,使用cmake .. 构建以及编译

cpp 复制代码
PS E:\VSCode_Projects\VSCode_Examples_Workspaces\E7_test_cmake> cd build
PS E:\VSCode_Projects\VSCode_Examples_Workspaces\E7_test_cmake\build> cmake ..
-- The C compiler identification is GNU 15.2.0
-- The CXX compiler identification is GNU 15.2.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: D:/mingw64/bin/gcc.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: D:/mingw64/bin/g++.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: E:/VSCode_Projects/VSCode_Examples_Workspaces/E7_test_cmake/build

2.5 使用cmake --build . --config Release(如果不使用--config Release,默认的是Debug模式),产生可执行文件

cpp 复制代码
PS E:\VSCode_Projects\VSCode_Examples_Workspaces\E7_test_cmake\build> cmake --build . --config Release
[ 33%] Building CXX object CMakeFiles/E7_test_project.dir/main.cpp.obj
[ 66%] Building CXX object CMakeFiles/E7_test_project.dir/simple_math.cpp.obj
[100%] Linking CXX executable E7_test_project.exe
[100%] Built target E7_test_project
PS E:\VSCode_Projects\VSCode_Examples_Workspaces\E7_test_cmake\build> ls


    目录: E:\VSCode_Projects\VSCode_Examples_Workspaces\E7_test_cmake\build


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----        2025/10/28     14:09                .cmake
d-----        2025/10/28     14:12                CMakeFiles
-a----        2025/10/28     14:12          16872 CMakeCache.txt
-a----        2025/10/28     14:12           1646 cmake_install.cmake
-a----        2025/10/28     14:12            718 compile_commands.json
-a----        2025/10/28     14:13          84570 E7_test_project.exe
-a----        2025/10/28     14:12           6617 Makefile


PS E:\VSCode_Projects\VSCode_Examples_Workspaces\E7_test_cmake\build> .\E7_test_project.exe  
加法: 8
减法: 2
乘法: 15
除法: 5
PS E:\VSCode_Projects\VSCode_Examples_Workspaces\E7_test_cmake\build> ls


    目录: E:\VSCode_Projects\VSCode_Examples_Workspaces\E7_test_cmake\build


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----        2025/10/28     14:09                .cmake
d-----        2025/10/28     14:13                CMakeFiles
-a----        2025/10/28     14:12          16872 CMakeCache.txt
-a----        2025/10/28     14:12           1646 cmake_install.cmake
-a----        2025/10/28     14:12            718 compile_commands.json
-a----        2025/10/28     14:13          84570 E7_test_project.exe
-a----        2025/10/28     14:12           6617 Makefile


PS E:\VSCode_Projects\VSCode_Examples_Workspaces\E7_test_cmake\build>

三、构建多文件夹组织结构以及制作静态库/动态库

3.1 在工程文件夹下创建子文件夹hello_world,simple_math,build

3.2 hello_world 这个作为静态库

CMakeLists.txt

cpp 复制代码
# 如果不指定,默认生产静态库. 动态库使用 SHARED 关键字
# 静态库:Windows 下在 Debug/Release 目录找 helloworld.lib
# 动态库:Windows 会同时生成 helloworld.dll(运行时库)和 helloworld.lib(导入库)
add_library(helloworld STATIC hello_world.cpp)
# 导出头文件路径;${CMAKE_CURRENT_SOURCE_DIR}指向 当前 CMakeLists.txt 所在目录,而非工程根目录
# CMAKE_SOURCE_DIR	工程根目录(最顶层 CMakeLists.txt 所在目录)
# CMAKE_CURRENT_SOURCE_DIR	当前处理的 CMakeLists.txt 所在目录
target_include_directories(helloworld PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

hello_world.cpp

cpp 复制代码
#include "hello_world.h"
#include <iostream>

HelloWorld::HelloWorld() {
    // 可选:构造时的逻辑,如打印日志
    std::cout << "HelloWorld 构造函数被调用" << std::endl;
}

HelloWorld::~HelloWorld() {
    // 可选:析构时的逻辑,如释放资源
    std::cout << "HelloWorld 析构函数被调用" << std::endl;
}

void HelloWorld::sayHello() {
    std::cout << "Say: Hello World!" << std::endl;
}

hello_world.h

cpp 复制代码
#ifndef __HELLO_WORLD_H__
#define __HELLO_WORLD_H__

// #ifdef __cplusplus
// extern "C" {
// #endif 

class HelloWorld {
public:
    HelloWorld();
    ~HelloWorld();
    void sayHello();
private:
    // 私有成员变量或方法可在此添加
};

// #ifdef __cplusplus
// }
// #endif

#endif

3.3 simple_math 这个作为动态库(注意:作为动态库时,执行可执行程序时才会链接动态库,则需要将.dll动态库放到可执行程序所在的同一文件夹下,修改顶层camke设置自动拷贝动态库)

CMakeLists.txt

cpp 复制代码
add_library(simplemath SHARED simple_math.cpp)
# PUBLIC 表示头文件路径对链接该库的目标可见
target_include_directories(simplemath PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

simple_math.cpp

bash 复制代码
#include "simple_math.h"
#include <stdexcept>
#include <iostream>

// 1. 无参构造函数:初始化 base_ 为默认值 0.0
SimpleMath::SimpleMath() : base_(0.0) {
    // 可选:添加构造时的日志或额外逻辑(如初始化其他资源)
    // 例如:std::cout << "无参构造函数被调用,base_ 初始化为 0.0" << std::endl;
    std::cout << "无参构造函数被调用,base_ 初始化为 0.0" << std::endl;
}

// 2. 带参数构造函数:用传入的 base 初始化 base_
SimpleMath::SimpleMath(double base) : base_(base) {
    // 可选:对参数进行校验(如不允许负数,根据业务需求)
    if (base < 0) {
        throw std::invalid_argument("带参数构造函数初始化失败,base 不能为负数!");
    } else {
        std::cout << "带参数构造函数初始化成功,base_ 初始化为 " << base << std::endl;
    }
}

// 3. 析构函数:无动态资源(如 new 的内存、文件句柄)时,空实现即可
SimpleMath::~SimpleMath() {
    // 若有动态资源,需在此释放(如 delete 指针)
    // 例如:delete ptr_; (本类无,所以为空)
}

// 4. 拷贝构造函数:复制 other 的 base_ 到当前对象
SimpleMath::SimpleMath(const SimpleMath& other) : base_(other.base_) {
    // 核心:将 other 的成员变量值复制到当前对象
    // 若有动态资源(如指针),需深拷贝(本类是基本类型,直接复制即可)
    // 例如:ptr_ = new int(*other.ptr_); (本类无,所以直接复制 base_)
}

// 5. 移动构造函数:"窃取" other 的 base_,并将 other 的 base_ 置空(语义上的移动)
SimpleMath::SimpleMath(SimpleMath&& other) noexcept : base_(other.base_) {
    other.base_ = 0.0;  // 置空原对象的资源(基本类型可直接设为默认值)
    // 若有动态资源(如指针),需转移所有权:
    // ptr_ = other.ptr_;
    // other.ptr_ = nullptr; (避免原对象析构时重复释放)
}

// 6. 拷贝赋值运算符:先释放当前资源(若有),再复制 other 的资源
SimpleMath& SimpleMath::operator=(const SimpleMath& other) {
    // 防止自我赋值(如 math1 = math1;)
    if (this != &other) {
        base_ = other.base_;  // 复制 other 的 base_
        // 若有动态资源,需先释放当前资源,再深拷贝:
        // delete ptr_;
        // ptr_ = new int(*other.ptr_);
    }
    return *this;  // 返回当前对象的引用(支持链式赋值:math1 = math2 = math3;)
}

// 7. 移动赋值运算符:先释放当前资源(若有),再"窃取" other 的资源
SimpleMath& SimpleMath::operator=(SimpleMath&& other) noexcept {
    // 防止自我赋值
    if (this != &other) {
        base_ = other.base_;  // 窃取 other 的 base_
        other.base_ = 0.0;    // 置空原对象的资源

        // 若有动态资源,需先释放当前资源,再转移所有权:
        // delete ptr_;
        // ptr_ = other.ptr_;
        // other.ptr_ = nullptr;
    }
    return *this;  // 支持链式赋值
}


// 原有运算方法的实现(不变)
double SimpleMath::add(double a, double b) {
    return a + b;
}

double SimpleMath::subtract(double a, double b) {
    return a - b;
}

double SimpleMath::multiply(double a, double b) {
    return a * b;
}

double SimpleMath::divide(double a, double b) {
    if (b == 0) {
        throw std::invalid_argument("除数不能为0!");
    }
    return a / b;
}

simple_math.h

bash 复制代码
#ifndef __SIMPLE_MATH_H__
#define __SIMPLE_MATH_H__

// #ifdef __cplusplus
// extern "C" {
// #endif 

class SimpleMath {
public:
    // 1. 自定义无参构造函数(替代 default)
    SimpleMath();

    // 2. 自定义带参数构造函数(初始化 base_ 为指定值)
    explicit SimpleMath(double base);  // explicit 避免隐式类型转换

    // 3. 自定义析构函数(替代 default,无动态资源时可空实现)
    ~SimpleMath();

    // 4. 自定义拷贝构造函数(复制 other 的 base_)
    SimpleMath(const SimpleMath& other);

    // 5. 自定义移动构造函数(移动 other 的 base_,并置空 other 的资源)
    SimpleMath(SimpleMath&& other) noexcept;

    // 6. 自定义拷贝赋值运算符(复制 other 的 base_ 到当前对象)
    SimpleMath& operator=(const SimpleMath& other);

    // 7. 自定义移动赋值运算符(移动 other 的 base_,并置空 other 的资源)
    SimpleMath& operator=(SimpleMath&& other) noexcept;


    // 原有运算方法
    double add(double a, double b);
    double subtract(double a, double b);
    double multiply(double a, double b);
    double divide(double a, double b);

private:
    double base_;  // 去掉类内默认初始化,改为在构造函数中初始化
};


// #ifdef __cplusplus
// }
// #endif

#endif

3.4 根目录下

CMakeLists.txt

bash 复制代码
# 1. 先设置工具链(用户原有代码)
set(CMAKE_C_COMPILER "D:/mingw64/bin/gcc.exe")    # C 编译器(MinGW GCC)
set(CMAKE_CXX_COMPILER "D:/mingw64/bin/g++.exe")  # C++ 编译器(MinGW G++)
set(CMAKE_SYSTEM_NAME Windows)

set(CMAKE_CXX_STANDARD 11)

# 2. 初始化项目(必须在 project 后打印,部分变量需 project 初始化)
cmake_minimum_required(VERSION 3.10)
project(E8_test_cmake_multi LANGUAGES CXX)

# -------------------------- 核心:打印工具链相关路径 --------------------------
# 打印 1:C 编译器(GCC)路径(即用户设置的 CMAKE_C_COMPILER)
message(STATUS "【工具链】C 编译器(GCC)路径: ${CMAKE_C_COMPILER}")

# 打印 2:C++ 编译器(G++)路径(即用户设置的 CMAKE_CXX_COMPILER)
message(STATUS "【工具链】C++ 编译器(G++)路径: ${CMAKE_CXX_COMPILER}")

# 打印 3:CMake 自身可执行文件路径(可选,了解 CMake 工具位置)
message(STATUS "【工具链】CMake 自身路径: ${CMAKE_COMMAND}")

# 打印 4:当前使用的工具链目标系统(验证是否为 Windows)
message(STATUS "【工具链】目标系统: ${CMAKE_SYSTEM_NAME}")
# -----------------------------------------------------------------------------

# 3. 原有逻辑(添加子目录、生成可执行文件、链接库)
add_subdirectory(hello_world)
add_subdirectory(simple_math)

add_executable(E8_test_cmake_multi main.cpp)

# ========== 新增:自动复制动态库到 exe 目录 ==========
add_custom_command(
  TARGET E8_test_cmake_multi POST_BUILD  # 编译 exe 后执行
  COMMAND ${CMAKE_COMMAND} -E copy_if_different  # 只在文件变化时复制
  "${CMAKE_CURRENT_BINARY_DIR}/simple_math/libsimplemath.dll"  # 动态库源路径
  "${CMAKE_CURRENT_BINARY_DIR}/"  # 动态库目标路径(exe 所在目录,即 build/ 根目录)
)
# =====================================================

target_link_libraries(E8_test_cmake_multi helloworld simplemath)

main.cpp

bash 复制代码
#include <iostream>
using namespace std;

#include "hello_world.h"
#include "simple_math.h"

int main()
{
    HelloWorld hw;
    hw.sayHello();
    SimpleMath sm;
    cout << "5 + 1 = " << sm.add(5, 1) << endl;
    cout << "5 - 1 = " << sm.subtract(5, 1) << endl;
    cout << "5 * 1 = " << sm.multiply(5, 1) << endl;
    cout << "5 / 1 = " << sm.divide(5, 1) << endl;
    return 0;
}

3.5 配置工具链,ctrl+shift+p,选择工具链mingw:c++

bash 复制代码
CMake:Configure
bash 复制代码
[main] 正在配置项目: E8_test_cmake_multi 
[proc] 正在执行命令: E:\BasicDocuments\Desktop\RT58X_SDK\Rafael-SDK-SuBG-RT584S\toolchain\cmake-3.31.8\Windows\bin\cmake.EXE -DCMAKE_BUILD_TYPE:STRING=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS:BOOL=TRUE -DCMAKE_C_COMPILER:FILEPATH=D:\mingw64\bin\gcc.exe -DCMAKE_CXX_COMPILER:FILEPATH=D:\mingw64\bin\g++.exe --no-warn-unused-cli -S E:/VSCode_Projects/VSCode_Examples_Workspaces/E8_test_cmake_multi -B e:/VSCode_Projects/VSCode_Examples_Workspaces/E8_test_cmake_multi/build -G "MinGW Makefiles"
[cmake] Not searching for unused variables given on the command line.
[cmake] -- 【工具链】C 编译器(GCC)路径: D:/mingw64/bin/gcc.exe
[cmake] -- 【工具链】C++ 编译器(G++)路径: D:/mingw64/bin/g++.exe
[cmake] -- 【工具链】CMake 自身路径: E:/BasicDocuments/Desktop/RT58X_SDK/Rafael-SDK-SuBG-RT584S/toolchain/cmake-3.31.8/Windows/bin/cmake.exe
[cmake] -- 【工具链】目标系统: Windows
[cmake] -- Configuring done (0.2s)
[cmake] -- Generating done (0.6s)
[cmake] -- Build files have been written to: E:/VSCode_Projects/VSCode_Examples_Workspaces/E8_test_cmake_multi/build

3.6 打开vscode终端进入build文件夹下

bash 复制代码
PS E:\VSCode_Projects\VSCode_Examples_Workspaces\E8_test_cmake_multi\build> cmake ..
-- The CXX compiler identification is GNU 15.2.0
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: D:/mingw64/bin/g++.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- 【工具链】C 编译器(GCC)路径: D:/mingw64/bin/gcc.exe
-- 【工具链】C++ 编译器(G++)路径: D:/mingw64/bin/g++.exe
-- 【工具链】CMake 自身路径: D:/WM_IDE/WM_Dev_Suite/wmtools/cmake/bin/cmake.exe
-- 【工具链】目标系统: Windows
-- Configuring done
-- Generating done
-- Build files have been written to: E:/VSCode_Projects/VSCode_Examples_Workspaces/E8_test_cmake_multi/build
PS E:\VSCode_Projects\VSCode_Examples_Workspaces\E8_test_cmake_multi\build>

3.7 使用 cmake --build . 构建产生可执行程序,乱码由于vscode默认支持UTF-8,但是系统终端支持GBK,这里暂时忽略该问题。

bash 复制代码
PS E:\VSCode_Projects\VSCode_Examples_Workspaces\E8_test_cmake_multi\build> cmake --build .
[ 16%] Building CXX object hello_world/CMakeFiles/helloworld.dir/hello_world.cpp.obj
[ 33%] Linking CXX static library libhelloworld.a
[ 33%] Built target helloworld
[ 50%] Building CXX object simple_math/CMakeFiles/simplemath.dir/simple_math.cpp.obj
[ 66%] Linking CXX shared library libsimplemath.dll
[ 66%] Built target simplemath
[ 83%] Building CXX object CMakeFiles/E8_test_cmake_multi.dir/main.cpp.obj
[100%] Linking CXX executable E8_test_cmake_multi.exe
[100%] Built target E8_test_cmake_multi
PS E:\VSCode_Projects\VSCode_Examples_Workspaces\E8_test_cmake_multi\build> .\E8_test_cmake_multi.exe
HelloWorld 鏋勯€犲嚱鏁拌璋冪敤
Say: Hello World!
鏃犲弬鏋勯€犲嚱鏁拌璋冪敤锛宐ase_ 鍒濆鍖栦负 0.0
5 + 1 = 6
5 - 1 = 4
5 * 1 = 5
5 / 1 = 5
HelloWorld 鏋愭瀯鍑芥暟琚皟鐢
PS E:\VSCode_Projects\VSCode_Examples_Workspaces\E8_test_cmake_multi\build>

或者按下ctrl+shift+p,使用cmake的vscode插件来进行配置(cmake ..)、编译(cmake --build .)

补充说明:

  1. 解决编码问题:

(1)vscode的右下角,将.h/.c/.cpp代码的编码格式改为UTF-8

(2)将cmake的vscode插件里cmake使用时的编码固定为UTF-8,进入vscode的设置搜索encoding,找到扩展的cmake,更改编码

(3)将vscode里的powershell代理终端切换为UTF-8编码格式,在vscode的总设置文件setting(使用ctrl+shift+p搜索setting)

bash 复制代码
"terminal.integrated.profiles.windows": {  
        "PowerShell": {
            "source": "PowerShell",
            "icon": "terminal-powershell",
            "args": ["-NoExit","chcp 65001"]
        },
        "Command Prompt": {
            "path": [
                "${env:windir}\\Sysnative\\cmd.exe",
                "${env:windir}\\System32\\cmd.exe"
            ],
            "args": ["/K","chcp 65001"],
            "icon": "terminal-cmd"
        },
}

效果:

bash 复制代码
Active code page: 65001
PS E:\VSCode_Projects\VSCode_Examples_Workspaces\E8_test_cmake_multi> cd build
PS E:\VSCode_Projects\VSCode_Examples_Workspaces\E8_test_cmake_multi\build> ls


    目录: E:\VSCode_Projects\VSCode_Examples_Workspaces\E8_test_cmake_multi\build


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----        2025/10/28     20:34                .cmake
d-----        2025/10/28     20:34                CMakeFiles
d-----        2025/10/28     20:34                hello_world
d-----        2025/10/28     20:34                simple_math
-a----        2025/10/28     20:34          15256 CMakeCache.txt
-a----        2025/10/28     20:34           2515 cmake_install.cmake
-a----        2025/10/28     20:34           1749 compile_commands.json
-a----        2025/10/28     20:34           6966 Makefile


PS E:\VSCode_Projects\VSCode_Examples_Workspaces\E8_test_cmake_multi\build> cmake --build .
[ 16%] Building CXX object hello_world/CMakeFiles/helloworld.dir/hello_world.cpp.obj
[ 33%] Linking CXX static library libhelloworld.a
[ 33%] Built target helloworld
[ 50%] Building CXX object simple_math/CMakeFiles/simplemath.dir/simple_math.cpp.obj
[ 66%] Linking CXX shared library libsimplemath.dll
[ 66%] Built target simplemath
[ 83%] Building CXX object CMakeFiles/E8_test_cmake_multi.dir/main.cpp.obj
[100%] Linking CXX executable E8_test_cmake_multi.exe
[100%] Built target E8_test_cmake_multi
PS E:\VSCode_Projects\VSCode_Examples_Workspaces\E8_test_cmake_multi\build> .\E8_test_cmake_multi.exe
HelloWorld 构造函数被调用
Say: Hello World!
无参构造函数被调用,base_ 初始化为 0.0
5 + 1 = 6
5 - 1 = 4
5 * 1 = 5
5 / 1 = 5
HelloWorld 析构函数被调用
PS E:\VSCode_Projects\VSCode_Examples_Workspaces\E8_test_cmake_multi\build>

四、讲解常用命令以及访问权限修饰词

4.1 注释

bash 复制代码
CMake 使用 # 进行行注释
# 注释演示

CMake 使用 #[[ ]] 形式进行块注释
#[[
    注释演示
]]

4.2 三个基础命令

cpp 复制代码
# 限制cmake的最小版本要求 (可选,非必须,如果不加可能会有警告)
cmake_minimum_required(VERSION 3.10)
# 定义目标主工程名称
project(E7_test_project)
# 定义工程生成一个可执行程序
add_executable(E7_test_project main.cpp simple_math.cpp)

(1)PROJECT 指令的语法

可指定工程的版本、工程描述、web主页地址、支持的语言(默认情况支持所有语言)

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

例如

cpp 复制代码
# 项目名称 "NetworkTool",版本 1.0.2,描述、主页,支持 C 和 Python
project(NetworkTool
  VERSION 1.0.2
  DESCRIPTION "A tool for network packet analysis"
  HOMEPAGE_URL "https://example.com/networktool"
  LANGUAGES C Python
)

(2)add_executable 语法

cpp 复制代码
add_executable(app a.c b.c c.c)

add_executable(app a.c;b.c;c.c)

4.3 cmake执行命令

cpp 复制代码
# 如果当前就在CMakeLists.txt文件所在路径就直接使用cmake
# 最好不要污染源代码文件,即创建build文件夹(产生的副文件放在这里),在build文件夹下使用cmake ..
# .. 是上一级目录
cmake <顶层CMakeLists.txt文件所在路径>

4.4 定义变量

SET 指令的语法是:

cpp 复制代码
# [] 中的参数为可选项, 如不需要可以不写
# VAR:变量名
# VALUE:变量值
SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])

例如:

cpp 复制代码
# 方式1: 各个源文件之间使用空格间隔
# set(SRC_LIST a.c  b.c   c.c)

# 方式2: 各个源文件之间使用分号 ; 间隔
set(SRC_LIST a.c;b.c;c.c)
add_executable(app  ${SRC_LIST})

4.5 指定编译器标准

cpp 复制代码
$ g++ *.cpp -std=c++11 -o app

cmake中:

(1)在cmake文件里

cpp 复制代码
#增加-std=c++11
set(CMAKE_CXX_STANDARD 11)
#增加-std=c++14
set(CMAKE_CXX_STANDARD 14)
#增加-std=c++17
set(CMAKE_CXX_STANDARD 17)

(2)在cmake执行时

cpp 复制代码
# -D 是一个宏相关的指令
# 增加-std=c++11
cmake <CMakeLists.txt文件路径> -DCMAKE_CXX_STANDARD=11
# 增加-std=c++14
cmake <CMakeLists.txt文件路径> -DCMAKE_CXX_STANDARD=14
# 增加-std=c++17
cmake <CMakeLists.txt文件路径> -DCMAKE_CXX_STANDARD=17

# 强制开启
cmake . -DCMAKE_CXX_STANDARD=17 -DCMAKE_CXX_STANDARD_REQUIRED=ON

4.6 指定可执行程序的输出的路径

bash 复制代码
# CMAKE_CURRENT_SOURCE_DIR 表示当前 CMakeLists.txt 所在的源码目录(通常是项目源代码存放的地方)
set(EXE $(CMAKE_CURRENT_SOURCE_DIR)/build/bin)
set(EXECUTABLE_OUTPUT_PATH $(EXE))

# 或者
# CMAKE_BINARY_DIR 表示当前的构建目录(即执行 cmake 命令时所在的目录)
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)

4.7 搜索文件

4.7.1 aux_source_directory命令可以查找某个路径下的所有源文件

auxauxiliary 的缩写,意为 "辅助的、辅助性的"

bash 复制代码
# dir:目录  variable:变量名
aux_source_directory(< dir > < variable >)

4.7.2 file搜索

GLOB: 将指定目录下搜索到的满足条件的所有文件名生成一个列表,并将其存储到变量中。

GLOB_RECURSE:递归搜索指定目录,将搜索到的满足条件的文件名生成一个列表,并将其存储到变量中。

bash 复制代码
file(GLOB/GLOB_RECURSE 变量名 要搜索的文件路径和文件类型)

例如:

bash 复制代码
# 关于要搜索的文件路径和类型可加双引号,也可不加

file(GLOB MAIN_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
file(GLOB MAIN_HEAD ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h)

4.8 包含头文件

bash 复制代码
include_directories(<含头文件的目录>)

例如:

bash 复制代码
# 执行 project(CALC) 后,PROJECT_SOURCE_DIR 会被自动设置为包含该 CMakeLists.txt 的目录(即项目根目录),
# 因此 ${PROJECT_SOURCE_DIR}/include 就是 "项目根目录下的 include 文件夹",用于指定头文件搜索路径。

project(CALC)  # 定义项目名称为 CALC
include_directories(${PROJECT_SOURCE_DIR}/include)  # 使用 PROJECT_SOURCE_DIR

4.9 制作动态库或静态库

4.9.1 制作静态库

bash 复制代码
add_library(库名称 STATIC 源文件1 [源文件2] ...) 

4.9.2 制作动态库

bash 复制代码
add_library(库名称 SHARED 源文件1 [源文件2] ...) 

4.9.3 指定库输出的路径

(1)方式1 - 适用于动态库

对于生成的库文件来说和可执行程序一样都可以指定输出路径。由于在Linux下生成的动态库默认是有执行权限的,所以可以按照生成可执行程序的方式去指定它生成的目录:

bash 复制代码
# PROJECT_SOURCE_DIR必须在project()指令后使用

set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
(2)方式2 - 都适用

由于在Linux下生成的静态库默认不具有可执行权限,所以在指定静态库生成的路径的时候就不能使用EXECUTABLE_OUTPUT_PATH宏了,而应该使用LIBRARY_OUTPUT_PATH,这个宏对应静态库文件和动态库文件都适用。

bash 复制代码
cmake_minimum_required(VERSION 3.0)
project(CALC)
include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
# 设置动态库/静态库生成路径
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
# 生成动态库
#add_library(calc SHARED ${SRC_LIST})
# 生成静态库
add_library(calc STATIC ${SRC_LIST})

4.10 包含库文件

在编写程序的过程中,可能会用到一些系统提供的动态库或者自己制作出的动态库或者静态库文件,cmake中也为我们提供了相关的加载动态库的命令。

4.10.1 链接静态库

bash 复制代码
# 参数1:指定出要链接的静态库的名字
#  可以是全名 libxxx.a
#  也可以是掐头(lib)去尾(.a)之后的名字 xxx
# 参数2-N:要链接的其它静态库的名字

link_libraries(<static lib> [<static lib>...])

4.10.2 指出库的路径

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

bash 复制代码
link_directories(<lib path>)

4.10.3 链接动态库

bash 复制代码
# target:指定要加载的库的文件的名字

# 该文件可能是一个源文件
# 该文件可能是一个动态库/静态库文件
# 该文件可能是一个可执行文件


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

PRIVATE|PUBLIC|INTERFACE:动态库的访问权限,默认为PUBLIC

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

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

bash 复制代码
target_link_libraries(A B C)
target_link_libraries(D A)
bash 复制代码
PUBLIC:在public后面的库会被Link到前面的target中,并且里面的符号也会被导出,提供给第三方使用。
PRIVATE:在private后面的库仅被link到前面的target中,并且终结掉,第三方不能感知你调了啥库
INTERFACE:在interface后面引入的库不会被链接到前面的target中,只会导出符号。

4.10.4 链接系统动态库

bash 复制代码
target_link_libraries(app pthread)

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

静态库会在生成可执行程序的链接阶段被打包到可执行程序中,所以可执行程序启动,静态库就被加载到内存中了。

动态库在生成可执行程序的链接阶段不会被打包到可执行程序中,当可执行程序被启动并且调用了动态库中的函数的时候,动态库才会被加载到内存。

4.10.5 链接第三方动态库

bash 复制代码
link_directories(path)

target_link_libraries(app pthread calc)

4.10.5 指定目标链接库

一般放在add_executable()后

bash 复制代码
target_link_libraries(target_name [item1 [item2 [...]]]
                      [<debug|optimized|general> <lib1> [<lib2> [...]]])

4.11 信息打印

bash 复制代码
message([STATUS|WARNING|AUTHOR_WARNING|FATAL_ERROR|SEND_ERROR] "message to display" ...)

(无) :重要消息

STATUS :非重要消息

WARNING:CMake 警告, 会继续执行

AUTHOR_WARNING:CMake 警告 (dev), 会继续执行

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

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

4.12 追加拼接

4.12.1 set追加

bash 复制代码
set(变量名1 ${变量名1} ${变量名2} ...)

例如:

bash 复制代码
file(GLOB SRC_1 ${PROJECT_SOURCE_DIR}/src1/*.cpp)
file(GLOB SRC_2 ${PROJECT_SOURCE_DIR}/src2/*.cpp)
# 追加(拼接)
set(SRC_1 ${SRC_1} ${SRC_2})

如果第一个参数中原来有数据会对原数据就行覆盖。

4.12.2 list拼接

bash 复制代码
list(APPEND <list> [<element> ...])

list(APPEND SRC_1 ${SRC_1} ${SRC_2}) 不会 "覆盖" SRC_1 原来的值,而是会在原有值的基础上追加新内容 ,但由于命令中包含 ${SRC_1} 自身,会导致 SRC_1 的原有内容被重复添加一次

bash 复制代码
file(GLOB SRC_1 ${PROJECT_SOURCE_DIR}/src1/*.cpp)
file(GLOB SRC_2 ${PROJECT_SOURCE_DIR}/src2/*.cpp)
# 追加(拼接)
list(APPEND SRC_1 ${SRC_1} ${SRC_2})

4.13 字符串移除

bash 复制代码
list(REMOVE_ITEM <list> <value> [<value> ...])

通过 file 命令搜索源文件的时候得到的是文件的绝对路径(在list中每个文件对应的路径都是一个item,并且都是绝对路径),在移除的时候也要将该文件的绝对路径指定出来才可以

4.14

bash 复制代码
在 CMake 中,list 命令用于对列表(以分号分隔的字符串)进行各种操作,包括增删改查、排序、拼接等。以下是 list 命令的全部子命令整理,包含语法、参数说明和功能描述:
1. list(LENGTH <list> <output variable>)
功能:获取列表的长度(元素数量)。参数:
<list>:目标列表(变量名,无需 ${} 包裹)。
<output variable>:存储长度的变量(会被创建或覆盖)。
示例:
cmake
set(my_list a b c)
list(LENGTH my_list len)  # len 会被赋值为 3
message(${len})  # 输出:3

2. list(GET <list> <index> [<index>...] <output variable>)
功能:获取列表中指定索引的元素(支持多个索引)。参数:
<list>:目标列表。
<index>:元素索引(从 0 开始;负数表示从末尾计数,-1 为最后一个元素)。
<output variable>:存储结果的变量(多个元素会组成新列表)。
注意:索引超出范围会报错。
示例:
cmake
set(my_list a b c d)
list(GET my_list 1 -1 out)  # 获取索引 1(b)和 -1(d)
message(${out})  # 输出:b;d

3. list(JOIN <list> <glue> <output variable>)
功能:用指定字符串(glue)连接列表元素,生成单个字符串。参数:
<list>:目标列表。
<glue>:连接符(字符串,如 ,、- 等)。
<output variable>:存储结果字符串的变量。
示例:
cmake
set(my_list a b c)
list(JOIN my_list ", " str)  # 用 ", " 连接
message(${str})  # 输出:a, b, c

4. list(FIND <list> <value> <output variable>)
功能:查找元素在列表中的索引(首次出现的位置)。参数:
<list>:目标列表。
<value>:要查找的元素。
<output variable>:存储结果的变量(找到则返回索引,未找到返回 -1)。
示例:
cmake
set(my_list a b c b)
list(FIND my_list b idx)  # 查找 "b" 首次出现的位置
message(${idx})  # 输出:1

5. list(APPEND <list> [<element>...])
功能:向列表末尾追加一个或多个元素。参数:
<list>:目标列表(若不存在则创建)。
<element>...:要追加的元素(多个元素用空格分隔)。
示例:
cmake
set(my_list a b)
list(APPEND my_list c d)  # 追加 c、d
message(${my_list})  # 输出:a;b;c;d

6. list(INSERT <list> <index> <element> [<element>...])
功能:在列表指定索引位置插入一个或多个元素。参数:
<list>:目标列表。
<index>:插入位置(0 表示开头,超出范围则插入末尾)。
<element>...:要插入的元素。
示例:
cmake
set(my_list a b c)
list(INSERT my_list 1 x y)  # 在索引 1 处插入 x、y
message(${my_list})  # 输出:a;x;y;b;c

7. list(PREPEND <list> [<element>...])
功能:向列表开头(索引 0 位置)插入一个或多个元素。参数:
<list>:目标列表(若不存在则创建)。
<element>...:要插入的元素(顺序保留,先加的元素更靠前)。
示例:
cmake
set(my_list c d)
list(PREPEND my_list a b)  # 开头插入 a、b
message(${my_list})  # 输出:a;b;c;d

8. list(POP_BACK <list> [<out-var>...])
功能:移除列表最后一个元素(可选:将移除的元素存入变量)。参数:
<list>:目标列表(若为空则无操作)。
<out-var>...(可选):存储被移除元素的变量(仅保留最后一个被移除的元素,多变量时多余变量为空)。
示例:
cmake
set(my_list a b c)
list(POP_BACK my_list last)  # 移除最后一个元素 c,存入 last
message(${my_list})  # 输出:a;b
message(${last})     # 输出:c

9. list(POP_FRONT <list> [<out-var>...])
功能:移除列表第一个元素(可选:将移除的元素存入变量)。参数:与 POP_BACK 类似,区别为移除第一个元素。示例:
cmake
set(my_list a b c)
list(POP_FRONT my_list first)  # 移除第一个元素 a,存入 first
message(${my_list})  # 输出:b;c
message(${first})    # 输出:a

10. list(REMOVE_ITEM <list> <value> [<value>...])
功能:移除列表中所有与 <value> 匹配的元素(精确匹配)。参数:
<list>:目标列表。
<value>...:要移除的元素(可多个)。
示例:
cmake
set(my_list a b c b a)
list(REMOVE_ITEM my_list a)  # 移除所有 "a"
message(${my_list})  # 输出:b;c;b

11. list(REMOVE_AT <list> <index> [<index>...])
功能:移除列表中指定索引的元素(支持多个索引)。参数:
<list>:目标列表。
<index>...:要移除的元素索引(超出范围则无操作)。
示例:
cmake
set(my_list a b c d)
list(REMOVE_AT my_list 1 3)  # 移除索引 1(b)和 3(d)
message(${my_list})  # 输出:a;c

12. list(REMOVE_DUPLICATES <list>)
功能:移除列表中的重复元素(保留首次出现的元素)。参数:
<list>:目标列表。
示例:
cmake
set(my_list a b a c b)
list(REMOVE_DUPLICATES my_list)  # 去重
message(${my_list})  # 输出:a;b;c

13. list(REVERSE <list>)
功能:反转列表中元素的顺序。参数:
<list>:目标列表。
示例:
cmake
set(my_list a b c)
list(REVERSE my_list)  # 反转
message(${my_list})  # 输出:c;b;a

14. list(SORT <list> [COMPARE <compare>] [CASE <case>] [ORDER <order>])
功能:对列表元素进行排序(默认按字符串升序、大小写敏感)。可选参数:
COMPARE <compare>:排序方式(STRING:字符串排序,默认;FILE_BASENAME:按文件名排序;NATURAL:自然数排序)。
CASE <case>:大小写敏感性(SENSITIVE:敏感,默认;INSENSITIVE:不敏感)。
ORDER <order>:排序方向(ASCENDING:升序,默认;DESCENDING:降序)。
示例:
cmake
set(my_list c A b)
list(SORT my_list CASE INSENSITIVE)  # 大小写不敏感升序
message(${my_list})  # 输出:A;b;c

4.14 宏定义

bash 复制代码
add_definitions(-D宏名称)

4.15 预定义宏

功能描述
PROJECT_SOURCE_DIR 使用 cmake 命令时指定的源码目录(通常是工程根目录,即最顶层 CMakeLists.txt 所在目录)
PROJECT_BINARY_DIR 执行 cmake 命令的目录(构建目录,用于存放构建过程中生成的文件)
CMAKE_CURRENT_SOURCE_DIR 当前正在处理的 CMakeLists.txt 文件所在的路径
CMAKE_CURRENT_BINARY_DIR 当前目标(target)的编译目录(存放当前目标的中间编译产物,如 .o 文件)
EXECUTABLE_OUTPUT_PATH 重新定义目标二进制可执行文件的存放位置(需通过 set 命令手动设置)
LIBRARY_OUTPUT_PATH 重新定义目标链接库文件(静态库、动态库)的存放位置(需通过 set 命令手动设置)
PROJECT_NAME 返回通过 project() 指令定义的项目名称
CMAKE_BINARY_DIR 项目实际构建路径(与 PROJECT_BINARY_DIR 一致,通常为执行 cmake 命令的目录,如 build 目录)

4.16 添加子目录

add_subdirectory() 的完整语法如下(方括号 [] 表示可选参数):

cmake

复制代码
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])

参数详解

  1. source_dir(必选) 指定包含 CMakeLists.txt 的子目录路径(源目录)。

    • 路径可以是相对路径 (相对于当前 CMakeLists.txt 所在的目录),或绝对路径
    • 要求该目录下必须存在 CMakeLists.txt,否则 CMake 会报错(提示 "找不到子目录的 CMakeLists.txt")。

    示例:若主目录 CMakeLists.txt 所在路径为 ./project,子目录 ./project/moduleCMakeLists.txt,则可写为:

    cmake

    复制代码
    add_subdirectory(module)  # 相对路径,等价于 add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/module)
  2. binary_dir(可选) 指定子目录构建产物(如 .o 目标文件、可执行文件、库文件等)的存放目录(二进制目录)。

    • 若不指定,默认与 source_dir 同名,即构建产物会放在构建目录(build)下与源目录同名的子目录 中(例如:build/module)。
    • 若指定,则构建产物会放在该目录下(路径同样可以是相对路径或绝对路径,相对路径基于当前构建目录)。

    示例:希望 module 的构建产物放在 build/my_output 目录下:

    cmake

    复制代码
    add_subdirectory(module my_output)  # source_dir=module,binary_dir=my_output
  3. EXCLUDE_FROM_ALL(可选) 一个关键字参数,用于指定子目录的构建目标是否排除在默认构建流程之外

    • 若添加此参数,子目录的目标(如 module 中的库)不会被默认构建(即执行 makecmake --build 时不会自动编译),需手动指定目标(如 make module_lib)才会构建。
    • 适用于 "可选组件" 场景(如子目录是一个可选插件,默认不需要编译)。

    示例:子目录 plugin 是可选组件,默认不构建:

    cmake

    复制代码
    add_subdirectory(plugin EXCLUDE_FROM_ALL)

参考链接(如有侵权,请联系删除):

https://subingwen.cn/cmake/CMake-primer/

相关推荐
不如摸鱼去9 小时前
从 Wot UI 出发谈 VSCode 插件的自动化发布
前端·vscode·开源·自动化
oioihoii11 小时前
在VSCode中配置Rust开发环境的详尽指南
ide·vscode·rust
李少兄11 小时前
如何将 TRAE IDE 的插件市场源切换至 VS Code 官方市场
ide
路由侠内网穿透.14 小时前
本地部署集成全能平台 Team.IDE 并实现外部访问
运维·服务器·数据库·ide·远程工作
2501_938782091 天前
《UE4 蓝图基础:蓝图编辑器打开与节点连接的核心逻辑》
编辑器·ue4
better11201 天前
IDE热键冲突的解决
ide
JosieBook1 天前
【软件安装】在 Visual Studio 2022 中安装 RDLC 报表插件的详细教程
ide·visual studio
qc17521 天前
PyCharm + 远程调试路径映射总结(以 diffusers 为例)
ide·python·pycharm
哲此一生9841 天前
Vscode中选择Conda环境
ide·vscode·编辑器