程序员的资源宝库

网站首页 > gitee 正文

CMake在Windows环境下Visual Studio Code的使用

sanyeah 2024-03-29 17:24:07 gitee 5 ℃ 0 评论

在Windows环境下,使用CMake可以帮助我们更方便地管理和构建C++项目。而在使用CMake的过程中,我们可以使用任何一个编辑器,包括VSCode,来编辑和构建我们的代码。

本文将介绍如何在Windows环境下使用VSCode编辑器和CMake构建C++项目,包括从最简单的单文件工程到多文件、多子文件夹的工程如何使用CMake,以及如何添加第三方库。

推荐的Cmake教程:苏丙榅 Cmake教程

CMake构建

前置要求

在开始之前,您需要安装以下软件:

  • Visual Studio Code及插件
  • CMake
  • C++编译器

安装配置CMake

下载链接

选择一个后缀为.msi的Windows安装包下载,尽量选最新的

安装时记得勾选“Add CMake to the system PATH for all users”,这样就不用自己再配置环境变量了

安装后在cmd输入以下目录验证是否安装成功

 cmake -version

安装配置MinGw

  • MinGw中包含gcc,g++等多种编译器,可以在windows上使用(windows无法直接安装gcc),下载链接:sourceforge
    解压时尽量解压在某个盘的根目录。
  • 将MinGW下bin目录的路径添加到环境变量中
C:\mingw64\bin
  • 进入bin文件夹,找到mingw32-make.exe,复制一份,将其中一份重命名为make.exe(依旧保存在bin文件夹中

  • 验证是否配置成功,cmd中输入

    gcc -v
    make -v
    

    可以判断是否安装成功

安装VSCode及插件

在vscode官网下载最新安装包,而非便携包

安装时注意勾选以下选型

1:在桌面创建快捷方式:勾选

2:将VSCode添加到右键菜单,支持打开文件:勾选

3:将VSCode添加到右键菜单,支持打开目录:勾选

4:勾选后会把很多文本格式改为用VSCode打开,勾选

5:添加到PATH(环境变量),自动添加,无需手动配置:勾选

安装完成后打开VScode,安装一个插件C/C++ Extension Pack

以下4个插件均为其依赖

单文件工程

在本节中,我们将创建一个简单的单文件工程,并使用CMake构建它。

创建项目

首先,我们需要创建一个文件夹,将其命名为hello-world。在该文件夹中,创建一个名为main.cpp的文件,并将以下内容复制到其中:

#include <iostream>

int main()
{
    std::cout << "Hello, world!" << std::endl;
    return 0;
}

创建CMakeLists.txt文件

接下来,我们需要在hello-world文件夹中创建一个名为CMakeLists.txt的文件,并将以下内容复制到其中:

cmake_minimum_required(VERSION 3.10)

project(hello-world)

add_executable(hello-world main.cpp)

该文件告诉CMake如何构建我们的项目。首先,我们指定了CMake的最低版本为3.10。然后,我们指定了项目名称为hello-world。最后,我们使用add_executable命令将main.cpp文件添加到我们的项目中。

构建项目

A:使用VSCode插件构建

接下来,我们需要使用CMake插件构建我们的项目。

打开VSCode,将文件夹hello-world打开为工作区。

按下ctrl+shift+P,输入以下命令并回车,选择电脑上安装好的编译器即可完成配置

CMake: configure

配置完成后会在目录下生成一个build文件夹

随后按下F7,或按下ctrl+shift+P,输入以下命令并回车即可完成编译

Cmake: build

该命令将会编译并链接我们的程序,最终生成一个名为hello-world的可执行文件。

最后,我们可以在终端中运行该可执行文件:

./build/hello-world

B:使用命令行cmake构建

  • 手动创建build文件夹
mkdir build
  • 进入至build文件夹中
cd build
  • cmake 配置

如果已安装了VS,可能会调用MS的MSVC编译器,使用下面命令来代替 cmake ..即可

cmake -G "MinGW Makefiles" ..

只有第一次使用cmake时使用以上命令,后续便可直接使用cmake ..

  • 编译文件
make

该命令将会编译并链接我们的程序,最终生成一个名为hello-world的可执行文件。

  • 运行exe

最后,我们可以在终端中运行该可执行文件:

./hello-world

结果

当我们运行hello-world可执行文件时,将会输出以下内容:

Hello, world!

多文件工程

在本节中,我们将创建一个包含多个文件的工程,并使用CMake构建它。

创建项目

首先,我们需要创建一个文件夹,将其命名为multi-file。在该文件夹中,创建一个名为main.cpp的文件,并将以下内容复制到其中:

#include <iostream>
#include "hello.hpp"

int main()
{
    std::cout << hello() << std::endl;
    return 
}

在同一个文件夹中,创建一个名为hello.cpp的文件,并将以下内容复制到其中:

#include "hello.hpp"

std::string hello()
{
    return "Hello, world!";
}

还需在同一文件夹中,创建一个名为hello.hpp的文件,并将以下内容复制到其中:

#pragma once
#include <string>

std::string hello();

创建CMakeLists.txt文件

接下来,我们需要在multi-file文件夹中创建一个名为CMakeLists.txt的文件,并将以下内容复制到其中:

cmake_minimum_required(VERSION 3.10)

project(multi-file)

add_executable(multi-file main.cpp hello.cpp)

该文件与单文件工程的文件类似,我们添加了hello.cpp文件并将其与main.cpp一起添加到我们的项目中。

构建项目

接下来,我们需要使用CMake构建我们的项目。打开VSCode,将文件夹multi-file打开为工作区。

按下ctrl+shift+P,输入以下命令并回车即可完成配置

CMake: configure

配置完成后会在目录下生成一个build文件夹

随后按下F7,或按下ctrl+shift+P,输入以下命令并回车即可完成编译

Cmake: build

最后,我们可以在终端中运行该可执行文件:

./build/multi-file

结果

当我们运行multi-file可执行文件时,将会输出以下内容:

Hello, world!

多子文件夹工程

在本节中,我们将创建一个包含多个子文件夹的工程,并使用CMake构建它。

创建项目

首先,我们需要创建一个名为multi-folder的文件夹。在该文件夹中,创建一个名为main.cpp的文件,并将以下内容复制到其中:

#include <iostream>
#include "hello.hpp"

int main()
{
    std::cout << hello() << std::endl;
    return 0;
}

然后,创建一个名为hello的子文件夹,在该子文件夹中,创建一个名为hello.cpp的文件,并将以下内容复制到其中:

#include "hello.hpp"

std::string hello()
{
    return "Hello, world!";
}

还需在hello子文件夹中,创建一个名为hello.hpp的文件,并将以下内容复制到其中:

#pragma once
#include <string>

std::string hello();

创建CMakeLists.txt文件

接下来,我们需要在multi-folder文件夹中创建一个名为CMakeLists.txt的文件,并将以下内容复制到其中:

cmake_minimum_required(VERSION 3.10)

project(multi-folder)

add_subdirectory(hello)

add_executable(multi-folder main.cpp)

target_link_libraries(multi-folder hello)

该文件包含了以下几个步骤:

  • 我们使用add_subdirectory命令将hello子文件夹添加到我们的项目中。
  • 我们使用add_executable命令将main.cpp文件添加到我们的项目中。
  • 我们使用target_link_libraries命令将multi-folder可执行文件链接到hello库。

创建子文件夹CMakeLists.txt文件

hello子文件夹中,我们还需要创建一个名为CMakeLists.txt的文件,并将以下内容复制到其中:

add_library(hello hello.cpp)

target_include_directories(hello PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

该文件包含了以下几个步骤:

  • 我们使用add_library命令将hello.cpp文件编译为一个库。
  • 我们使用target_include_directories命令将hello库的头文件路径添加到编译器的包含路径中。

构建项目

接下来,我们需要使用CMake构建我们的项目。

打开VSCode,按下ctrl+shift+P,输入以下命令并回车即可完成配置

CMake: configure

配置完成后会在目录下生成一个build文件夹

随后按下F7,或按下ctrl+shift+P,输入以下命令并回车即可完成编译

CMake: build

最后,我们可以在终端中运行该可执行文件:

./build/multi-folder

结果

当我们运行multi-folder可执行文件时,将会输出以下内容:

Hello, world!

添加第三方库

在本节中,我们将介绍如何在我们的项目中添加第三方库。

下载第三方库

我们将使用一个名为fmt的库作为示例。fmt是一个格式化字符串库,可以帮助我们轻松地格式化输出。首先,我们需要从fmt的GitHub页面上下载该库。在终端中,输入以下命令:

git clone https://github.com/fmtlib/fmt.git

这将在当前目录中创建一个名为fmt的文件夹。

创建项目

我们将使用之前创建的multi-file项目作为示例。在该项目中,我们将添加fmt库并使用它格式化输出。

将库添加到项目中

fmt库添加到我们的项目中有几种不同的方法。

一种方法是将fmt库的源代码直接添加到我们的项目中。

但是,在这里,我们将使用另一种方法。我们将使用CMake的ExternalProject模块来构建fmt库,并将其作为依赖项添加到我们的项目中。

multi-file项目的根目录中,创建一个名为cmake的文件夹,并在其中创建一个名为ExternalProject的文件夹。在ExternalProject文件夹中,创建一个名为CMakeLists.txt的文件,并将以下内容复制到其中:

include(ExternalProject)

set(FMT_ROOT ${CMAKE_BINARY_DIR}/fmt)

ExternalProject_Add(
    fmt
    GIT_REPOSITORY https://github.com/fmtlib/fmt.git
    GIT_TAG master
    PREFIX ${FMT_ROOT}
    INSTALL_COMMAND ""
)

add_library(fmt INTERFACE)

target_include_directories(fmt INTERFACE ${FMT_ROOT}/src/fmt/include)

add_dependencies(fmt fmt)

该文件包含了以下几个步骤:

  • 我们使用ExternalProject_Add命令将fmt库添加到我们的项目中。在该命令中,我们指定了该库的Git仓库地址和版本号,以及该库的安装路径。

  • 我们使用add_library命令将一个名为fmt的接口库添加到我们的项目中。该库不会包含任何源代码,而是包含fmt库的头文件路径。

  • 我们使用target_include_directories命令将fmt库的头文件路径添加到fmt库的接口库中。

  • 我们使用add_dependencies命令将fmt库添加到我们的项目中的依赖项列表中。

修改CMakeLists.txt文件

接下来,我们需要修改multi-file项目的CMakeLists.txt文件。我们需要将fmt库添加到我们的项目中,并使用它格式化输出。我们将添加以下内容到该文件中:

# 添加fmt库
add_subdirectory(cmake/ExternalProject)
include_directories(${FMT_ROOT}/src/fmt/include)

# 将hello库添加到项目中
add_subdirectory(hello)

add_executable(multi-folder main.cpp)

# 链接hello库
target_link_libraries(multi-folder hello)

# 链接fmt库
target_link_libraries(multi-folder fmt::fmt)

该文件包含了以下几个步骤:

  • 我们使用add_subdirectory命令将ExternalProject文件夹添加到我们的项目中。
  • 我们使用include_directories命令将fmt库的头文件路径添加到编译器的包含路径中。
  • 我们使用add_executable命令将main.cpp文件编译为可执行文件。
  • 我们使用target_link_libraries命令将hello库和fmt库链接到multi-folder可执行文件中。

构建项目

接下来,我们需要使用CMake构建我们的项目。

打开VSCode,按下ctrl+shift+P,输入以下命令并回车即可完成配置

CMake: configure

配置完成后会在目录下生成一个build文件夹

随后按下F7,或按下ctrl+shift+P,输入以下命令并回车即可完成编译

CMake: build

结果

当我们运行multi-folder可执行文件时,将会输出以下内容:

Hello, world! The answer is 42.

该输出中包含了我们使用fmt库格式化的内容。

实例:以第三库方式使用CSerialPort

CSerialPort项目是基于C++的轻量级开源跨平台串口类库,用于实现跨平台多操作系统的串口读写。

CSerialPort已经在以下平台做过测试

  • Windows ( x86, x86_64, arm64 )
  • Linux ( x86, x86_64, arm, arm64/aarch64, mips64el, riscv, s390x, ppc64le )
  • macOS ( x86_64 )
  • Raspberry Pi ( armv7l )
  • FreeBSD ( x86_64 )

1. 使用cmake生成CSerialPort动态库

git clone https://github.com/itas109/CSerialPort
# 或使用国内码云地址 
git clone https://gitee.com/itas109/CSerialPort.git

cd CSerialPort

mkdir bin

cd bin

cmake .. -G "MinGW Makefiles" -DCMAKE_INSTALL_PREFIX=install -DCMAKE_BUILD_TYPE=Debug -DBUILD_SHARED_LIBS=ON 

cmake --build . --config Debug

cmake --install . --config Debug

2. 以cmake方式引用CSerialPort的动态库

通过find_package自动搜索CSerialPort头文件及动态库

CMakeLists.txt

cmake_minimum_required(VERSION 2.8.12)

project(DapensonUart LANGUAGES CXX)

find_package(CSerialPort)
if (CSerialPort_FOUND)
	include_directories(${CSerialPort_INCLUDE_DIR})
	add_executable( ${PROJECT_NAME} main.cpp)
	target_link_libraries (${PROJECT_NAME} ${CSerialPort_LIBRARY})
else()
	message(STATUS "Not found system CSerialPort")
endif ()

注意:出现如下错误,可设置CMAKE_PREFIX_PATH指定搜索路径,如

cmake .. -DCMAKE_PREFIX_PATH="D:/CommConsole/CSerialPort/bin/install"
注意,路径需设置为上一步操作中生成的CSerialPort/bin/install目录

3. 新建项目编译测试

在需要的地方新建一个项目文件夹,

新建文件

文件夹目录如下

CMakeLists.txt
main.cpp

其中CMakeLists.txt内容见上一步骤

main.cpp

#include <iostream>

#ifdef _WIN32
#include <Windows.h>
#define imsleep(microsecond) Sleep(microsecond) // ms
#else
#include <unistd.h>
#define imsleep(microsecond) usleep(1000 * microsecond) // ms
#endif

#include <vector>

#include "CSerialPort/SerialPort.h"
#include "CSerialPort/SerialPortInfo.h"
using namespace itas109;

std::string char2hexstr(const char *str, int len)
{
    static const char hexTable[17] = "0123456789ABCDEF";

    std::string result;
    for (int i = 0; i < len; ++i)
    {
        result += "0x";
        result += hexTable[(unsigned char)str[i] / 16];
        result += hexTable[(unsigned char)str[i] % 16];
        result += " ";
    }
    return result;
}

int countRead = 0;

class MyListener : public CSerialPortListener
{
public:
    MyListener(CSerialPort *sp)
        : p_sp(sp){};

    void onReadEvent(const char *portName, unsigned int readBufferLen)
    {
        if (readBufferLen > 0)
        {
            char *data = new char[readBufferLen + 1]; // '\0'

            if (data)
            {
                // read
                int recLen = p_sp->readData(data, readBufferLen);

                if (recLen > 0)
                {
                    data[recLen] = '\0';
                    std::cout << portName << " - Count: " << ++countRead << ", Length: " << recLen << ", Str: " << data << ", Hex: " << char2hexstr(data, recLen).c_str()
                              << std::endl;

                    // return receive data
                    // p_sp->writeData(data, recLen);
                }

                delete[] data;
                data = NULL;
            }
        }
    };

private:
    CSerialPort *p_sp;
};

int main()
{
    CSerialPort sp;
    std::cout << "Version: " << sp.getVersion() << std::endl
              << std::endl;

    MyListener listener(&sp);

    std::vector<SerialPortInfo> m_availablePortsList = CSerialPortInfo::availablePortInfos();

    std::cout << "availableFriendlyPorts: " << std::endl;

    for (size_t i = 1; i <= m_availablePortsList.size(); ++i)
    {
        SerialPortInfo serialPortInfo = m_availablePortsList[i - 1];
        std::cout << i << " - " << serialPortInfo.portName << " " << serialPortInfo.description << " " << serialPortInfo.hardwareId << std::endl;
    }

    // 检查是否有可用串口,如果没有则退出
    if (m_availablePortsList.size() == 0)
    {
        std::cout << "No valid port" << std::endl;
        return 0;
    }

    std::cout << std::endl;
    const char *portName = "COM10";
    std::cout << "Port Name: " << portName << std::endl;

    sp.init(portName,                // windows:COM1 Linux:/dev/ttyS0
            itas109::BaudRate115200, // baudrate
            itas109::ParityNone,     // parity
            itas109::DataBits8,      // data bit
            itas109::StopOne,        // stop bit
            itas109::FlowNone,       // flow
            4096                     // read buffer size
    );
    sp.setReadIntervalTimeout(0); // read interval timeout 0ms

    sp.open();
    std::cout << "Open " << portName << (sp.isOpen() ? " Success" : " Failed") << std::endl;

    // 连接读取事件
    sp.connectReadEvent(&listener);

    // 第一次写入十六进制数据
    char hex[5];
    hex[0] = 0x31;
    hex[1] = 0x32;
    hex[2] = 0x33;
    hex[3] = 0x34;
    hex[4] = 0x35;
    sp.writeData(hex, sizeof(hex));

    // 写入字符串数据
    sp.writeData("Dapenson", 8);

    // 循环等待
    for (;;)
    {
        // 延时1S
        imsleep(1000);
        // 写入字符串数据
        const char *data_send = "Dapenson\n";
        sp.writeData(data_send, strlen(data_send));
    }

    return 0;
}

编译运行

完成以上2个文件新建编辑以后,在文件路径下打开cmd,运行以下命令

mkdir bin

cd bin

cmake .. -G "MinGW Makefiles" -DCMAKE_PREFIX_PATH="D:/CommConsole/CSerialPort/bin/install"

make 

运行后即可在文件夹中的bin目录下生成一个main.exe文件,运行即可。

如果报错需要dll,则将install目录下的dll复制到exe同目录即可

至此,就完成了CSerialPort第三方库的demo,可用虚拟串口软件vspd或是串口硬件进行数据收发的回环测试。

总结

本文介绍了如何在Windows环境下,在VSCode编辑器中使用CMake构建项目。我们从最简单的单文件项目开始,并逐步扩展到多文件、多子文件夹的项目,并向其中添加第三方库。

除了本文所介绍的基础使用之外,CMake还有很多其他的特性和用法。下面是一些其他的注意事项和建议:

  • CMake支持交叉编译,可以在一个系统上构建并运行另一个不同架构或操作系统的应用程序。
  • CMake支持构建多个目标,包括库、可执行文件和测试程序。
  • CMake可以集成到许多不同的IDE中,包括Visual Studio、Xcode和Qt Creator等。
  • CMake支持许多不同的第三方库,包括Boost、OpenCV和Qt等。在使用这些库时,可以使用find_package命令查找和配置它们。
  • CMake也支持许多其他的配置选项和变量,可以根据具体需求进行调整。

总之,CMake是一个非常强大和灵活的构建系统,可以帮助开发者轻松地管理和构建项目。学习和掌握CMake的使用,对于开发者来说是非常有用的技能。

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表