C/C++编译工具:cmake | AI工程化部署

简介: CMake 是一个跨平台的开源构建工具,用于管理软件构建流程。它使用一个名为 CMakeLists.txt 的文本文件来描述构建过程。【1月更文挑战第4天】

1.基本使用

CMake 是一个跨平台的开源构建工具,用于管理软件构建流程。它使用一个名为 CMakeLists.txt 的文本文件来描述构建过程。以下是一个简单的 CMakeLists.txt 文件的示例,用于构建一个简单的 C++ 程序:

# 指定 CMake 最低版本要求
cmake_minimum_required(VERSION 3.10)

# 指定项目名称
project(MyProject)

# 指定生成可执行文件
add_executable(my_program main.cpp)

在这个示例中,我们指定了 CMake 的最低版本要求,并且指定了项目的名称。然后,我们使用 add_executable 命令来告诉 CMake 创建一个名为 my_program 的可执行文件,该可执行文件由 main.cpp 文件构建而成。

为了使用 CMake 构建项目,我们需要执行以下步骤:

  1. 创建一个 CMakeLists.txt 文件来描述构建过程。
  2. 在项目根目录下创建一个 build 文件夹,并进入该文件夹。
  3. 运行 cmake 命令来生成构建系统所需的文件。例如:cmake ..
  4. 运行生成的构建系统,例如使用 make 命令进行构建。

通过这些步骤,CMake 将会根据 CMakeLists.txt 文件生成相应的构建系统文件,并且帮助我们完成项目的构建过程。

2.基本语法

  command(arg1 arg2 ...) # 运行命令
  set(var_name var_value) # 定义变量,或者给已经存在的变量赋值
  command(arg1 ${var_name}) # 使用变量

  # 控制语句
  IF(expression) COMMAND1(ARGS)
  ELSE(expression) COMMAND2(ARGS)
  ENDIF(expression)

  # expression
  IF(var) # 不是空, 0, N, NO, OFF, FALSE, NOTFOUND 或 _NOTFOUND时,为真
  IF(NOT var) # 与上述条件相反。
  IF(var1 AND var2) # 当两个变量都为真是为真。
  IF(var1 OR var2) # 当两个变量其中一个为真时为真。
  IF(COMMAND cmd) # 当给定的cmd确实是命令并可以调用是为真
  IF(EXISTS dir) # 目录名存在
  IF(EXISTS file) # 文件名存在
  IF(IS_DIRECTORY dirname) # 当dirname是目录
  IF(file1 IS_NEWER_THAN file2) # 当file1比file2新,为真
  IF(variable MATCHES regex) # 符合正则

  # 循环
  WHILE(condition) COMMAND1(ARGS) // ...
  ENDWHILE(condition)

  AUX_SOURCE_DIRECTORY(. SRC_LIST)
  FOREACH(one_dir ${SRC_LIST}) MESSAGE(${one_dir})
  ENDFOREACH(onedir)

基本操作

  • add_library: 指定的源文件(CPP文件)生成链接文件,然后添加到工程中去。生成动态库或者静态库

    add_library(<name> [STATIC | SHARED | MODULE]
            [EXCLUDE_FROM_ALL]
            [source1] [source2 ...])
    
    add_library(mod1 SHARED mod1.c mod1_func.c) # 生成动态库 libmod1.so
    add_library(mod2 STATIC mod2.c) # 生成静态库 libmod2.a
    
  • add_subdirectory: 在子文件夹添加了library或者executable之后,在上层目录添加subdirectory, 也可以在同一个CMakeList.txt中使用
  • target_link_libraries: 设置要链接的库文件的名称 具体的动态链接库文件.so, 在生成的可执行文件或动态库中连接进去其他的库
    target_link_libraries(<target> [item1 [item2 [...]]]
                        [[debug|optimized|general] <item>] ...)
    target_link_libraries(main mod1)
    
  • include_directories: 添加头文件目录 g++选项中的-I参数的作用,也相当于环境变量中增加路径到CPLUS_INCLUDE_PATH变量的作用
  • link_directories 添加需要链接的库文件目录。 它相当于g++命令的-L选项的作用,也相当于环境变量中增加LD_LIBRARY_PATH的路径的作用
  • link_libraries 添加需要链接的库文件路径

下面是一个使用 CMake 构建的示例,其中包括了 target_link_libraries,link_directories,include_directories,add_executable 和 add_library 等关键部分,以生成一个可执行文件和一个动态库。

cmake_minimum_required(VERSION 3.5)

project(MyProject)

# 设置编译选项
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Ofast -Wfatal-errors -pthread -fPIC -O3 -mavx")

# 添加动态库的源文件
set(SOURCE_FILES_LIB mylib.cpp)
add_library(mylib SHARED ${SOURCE_FILES_LIB})

# 添加可执行文件的源文件
set(SOURCE_FILES main.cpp)

# 添加包含目录
include_directories(include)

# 添加链接目录
link_directories(${CMAKE_SOURCE_DIR}/lib)

# 添加可执行文件
add_executable(myapp ${SOURCE_FILES})

# 链接动态库
target_link_libraries(myapp mylib)

假设项目的目录结构如下所示:

MyProject/
├── CMakeLists.txt
├── include/
│   └── mylib.h
├── src/
│   ├── main.cpp
│   └── mylib.cpp
└── lib/
    └── libmylib.so

在项目根目录中执行以下命令来生成构建文件和编译项目:

mkdir build
cd build
cmake ..
make

这将生成一个名为 myapp 的可执行文件和一个名为 libmylib.so 的动态库。

3.指定安装目录

我们进行cmake命令,会执行make命令进行编译,最后如果有需要的话会执行make install将编译好的库或者头文件安装到指定的未知。

在CMake中,可以使用install命令来设置安装相关信息,包括安装目标、安装路径等。以下是一个详细的例子,展示了如何在CMake中设置安装相关信息:

cmake_minimum_required(VERSION 3.10)

project(MyProject)

# 添加可执行文件
add_executable(MyExecutable main.cpp)

# 设置安装路径
set(CMAKE_INSTALL_PREFIX /usr/local)  # 安装到/usr/local目录下

# 安装可执行文件
install(TARGETS MyExecutable
        DESTINATION bin)  # 将可执行文件安装到bin目录下

# 安装头文件
file(GLOB HEADER_FILES "*.h")
install(FILES ${HEADER_FILES}
        DESTINATION include/myproject)  # 将头文件安装到include/myproject目录下

# 安装额外文件
install(FILES README.md
        DESTINATION share/myproject)  # 将README.md文件安装到share/myproject目录下

在上面的例子中,我们首先使用add_executable命令添加了一个可执行文件MyExecutable。然后使用set命令设置了安装路径为/usr/local。接下来使用install命令分别安装了可执行文件、头文件和额外文件到指定的安装路径下。

需要注意的是,安装路径可以根据实际需要进行调整。在使用CMake时,可以根据具体的项目需求来设置不同的安装路径和安装目标。

4.区分开发版与发布版

在CMake中,可以通过MAKE_BUILD_TYPE在确定不通类型的棒棒,常用的构建类型包括Debug、Release、MinSizeRel和RelWithDebInfo。

举例来说,假设我们有一个CMakeLists.txt文件,其中需要根据不同的构建类型来设置编译选项。我们可以使用MAKE_BUILD_TYPE来指定构建类型,然后根据不同的构建类型来设置不同的编译选项。

cmake_minimum_required(VERSION 3.10)

project(MyProject)

# 根据构建类型设置编译选项
if (MAKE_BUILD_TYPE STREQUAL "Debug")
    add_definitions(-DDEBUG)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
elseif (MAKE_BUILD_TYPE STREQUAL "Release")
    add_definitions(-DNDEBUG)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
endif()

# 添加源文件
add_executable(MyExecutable main.cpp)

在上面的例子中,根据不同的构建类型,我们设置了不同的预处理宏和编译选项。当构建类型为Debug时,会定义DEBUG宏并启用调试信息;当构建类型为Release时,会定义NDEBUG宏并开启优化。

这样我们就可以main.cpp中,通过条件编译的方式,确定是哪些debug模型的需要编译的,而在release发布版本不需要编译,形如:

#ifndef NDEBUG 
    printf("author: %s, release_date: %s\n", AUTHOR, RELEASE_DATE ); // 只在开发版本编译
#endif
cmake -DMAKE_BUILD_TYPE=Debug ..
cmake -DMAKE_BUILD_TYPE=Release ..

5.大杀器find_package

find_package是CMake中用于查找和加载第三方库的命令。它用于在系统中查找指定的软件包,并将其路径或库的相关信息导入到CMake中,以便在项目中使用。

要配置find_package,你需要在CMakeLists.txt文件中使用find_package命令,并提供要查找的软件包的名称。通常情况下,你还需要指定软件包的版本号。

下面是一个使用find_package的简单例子:

假设你想在CMake项目中使用OpenCV,你需要在CMakeLists.txt文件中添加以下内容:

# 查找OpenCV包
find_package(OpenCV 4.0 REQUIRED)

# 如果找到OpenCV包,将其包含路径和链接库添加到项目中
if(OpenCV_FOUND)
    include_directories(${OpenCV_INCLUDE_DIRS})
    target_link_libraries(your_project_name ${OpenCV_LIBS})
endif()

find_package语法为:FIND_PACKAGE( <name> [version] [EXACT] [QUIET] [NO_MODULE] [ [ REQUIRED | COMPONENTS ] [ componets... ] ] )

在这个例子中,find_package命令用于查找OpenCV 4.0,并将其导入到项目中。如果找到了OpenCV包,将其包含路径添加到项目的include路径中,并将其链接库添加到项目的链接库中。

请注意,在实际项目中,你可能还需要根据你的项目结构和依赖项的不同做一些适当的调整。

那find_package怎么知道从哪里去查找相关依赖库呢?
查找的目录路径:

<package>_DIR
CMAKE_PREFIX_PATH
CMAKE_FRAMEWORK_PATH
CMAKE_APPBUNDLE_PATH
PATH

其中,PATH中的路径如果以bin或sbin结尾,则自动回退到上一级目录。
找到根目录后,cmake会检查这些目录下的

<prefix>/(lib/<arch>|lib|share)/cmake/<name>*/          (U)
<prefix>/(lib/<arch>|lib|share)/<name>*/                (U)
<prefix>/(lib/<arch>|lib|share)/<name>*/(cmake|CMake)/  (U)
cmake找到这些目录后,会开始依次找<package>Config.cmake或Find<package>.cmake文件。找到后即可执行该文件并生成相关链接信息。

最重要的一个是PATH。由于/usr/bin/在PATH中,cmake会自动去/usr/(lib/<arch>|lib|share)/cmake/<name>*/寻找模块
另外一个比较重要的是<package>_DIR。我们可以在调用cmake时将这个目录传给cmake。由于其优先级最高,因此cmake会优先从该目录中寻找,这样我们就可以随心所欲的配置cmake使其找到我们希望它要找到的包。而且除上述指定路径外,cmake还会直接进入<package>_DIR下寻找。如我在3rd_parties目录下编译了一个OpenCV,那么执行cmake时可以使用

OpenCV_DIR=../../3rd-party/opencv-3.3.4/build/ cmake ..

6.多个CMakeLists.txt

在一个复杂的项目中,通常会以模块化的方式来组织项目的框架,统一一个如main.cpp做为入口程序(如ffmpeg)。那么如果所有的程序的编译信息都写在一个CMakeLists.txt会很难维护。cmake里运行多个CMakeLists.txt,每个模块有自己的CMakeList负责编译。如下面的结构

  .
  ├── build
  │   ├── CMakeCache.txt
  │   ├── CMakeFiles
  │   ├── cmake_install.cmake
  │   ├── lib
  │   │   ├── libmod1.so
  │   │   └── mo2_lib
  │   │       ├── libmod2.a
  │   │       └── Makefile
  │   └── Makefile
  ├── CMakeLists.txt
  ├── main.c
  └── mod1
      ├── CMakeLists.txt
      ├── mod1.c
      ├── mod1_func.c
      ├── mod1_func.h
      ├── mod1.h
      └── mod2
          ├── CMakeLists.txt
          ├── mod2.c
          └── mod2.h

具体的,当使用CMake构建一个项目时,add_subdirectory命令可以用来包含子目录中的CMakeLists.txt文件,从而将子目录中的源代码文件添加到主项目中。下面是一个使用add_subdirectory的简单示例:

假设我们有以下项目目录结构:

project/
    CMakeLists.txt
    main.cpp
    subdirectory/
        CMakeLists.txt
        helper.cpp
        helper.h

在主项目的 CMakeLists.txt 文件中,可以包含子目录的 CMakeLists.txt 文件使用 add_subdirectory 命令。示例如下:

# 主项目的 CMakeLists.txt

cmake_minimum_required(VERSION 3.0)

project(MyProject)

add_executable(my_app main.cpp)

# 包含subdirectory中的CMakeLists.txt
add_subdirectory(subdirectory)

# 将子目录中的源文件添加到主项目中
target_sources(my_app PRIVATE subdirectory/helper.cpp)

在子目录的 CMakeLists.txt 文件中,可以定义子目录中的源文件,示例如下:

# 子目录的 CMakeLists.txt

# 将子目录中的源文件添加到一个库中
add_library(helper_lib helper.cpp helper.h)

在这个例子中,add_subdirectory命令被用来引入子目录中的CMakeLists.txt文件,并将子目录中的源文件添加到主项目中。

7.打印信息

cmake可以通过message来打印一些调试信息

    cmake_minimum_required(VERSION 2.8)
    project(find_package_learning)
    find_package(OpenCV 3 REQUIRED)

    message(STATUS "OpenCV_DIR = ${OpenCV_DIR}")
    message(STATUS "OpenCV_INCLUDE_DIRS = ${OpenCV_INCLUDE_DIRS}")
    message(STATUS "OpenCV_LIBS = ${OpenCV_LIBS}")

    include_directories(${OPENCV_INCLUDE_DIRS})  
    add_executable(opencv_test opencv_test.cpp)  
    target_link_libraries(opencv_test ${OpenCV_LIBS})
目录
相关文章
|
24天前
|
人工智能 自然语言处理 开发者
AIGC创作活动 | 跟着UP主秋葉一起部署AI视频生成应用!
本次AI创作活动由 B 站知名 AI Up 主“秋葉aaaki”带您学习在阿里云 模型在线服务(PAI-EAS)中零代码、一键部署基于ComfyUI和Stable Video Diffusion模型的AI视频生成Web应用,快速实现文本生成视频的AI生成解决方案,帮助您完成社交平台短视频内容生成、动画制作等任务。制作上传专属GIF视频,即有机会赢取乐歌M2S台式升降桌、天猫精灵、定制保温杯等好礼!
|
25天前
|
人工智能
一键生成视频!用 PAI-EAS 部署 AI 视频生成模型 SVD 工作流(清晰的实例)
用 PAI-EAS 部署 AI 视频生成模型 SVD 工作流(清晰的实例)
159 2
|
29天前
|
IDE Java Linux
【CMake】CMake构建C++代码(一)
【CMake】CMake构建C++代码(一)
|
24天前
|
人工智能 机器人 编译器
【C++】Windows端VS code中运行CMake工程(手把手教学)
【C++】Windows端VS code中运行CMake工程(手把手教学)
|
3天前
|
运维 Serverless Go
Serverless 应用引擎产品使用之在阿里云函数计算中c++模板,将编译好的C++程序放进去部署如何解决
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
9 1
|
6天前
|
人工智能 Ubuntu 机器人
AI电销机器人系统源码部署之:freeswitch安装Linux
在Linux服务器上安装FreeSWITCH的简要步骤:更新软件包,安装依赖(如build-essential,libssl-dev等),下载v1.10.7源代码,解压并配置,编译,然后运行`./bootstrap.sh -j`,`./configure`,`make`,`make install`。启动FreeSWITCH服务,配置SIP用户和路由,测试连接与通话,并确保防火墙打开SIP(5060)和RTP端口。注意,实际部署可能需按需求调整。
|
8天前
|
人工智能 API 网络架构
【AI大模型应用开发】【LangChain系列】7. LangServe:轻松将你的LangChain程序部署成服务
【AI大模型应用开发】【LangChain系列】7. LangServe:轻松将你的LangChain程序部署成服务
19 0
|
9天前
|
人工智能 安全 机器人
AI电销机器人系统源码部署:freeswitch安装Windows
在Windows上安装FreeSWITCH:访问官网下载安装程序,运行并按提示安装;选择安装路径和组件;等待安装完成;配置FreeSWITCH,修改设置;启动服务;测试其功能;如遇问题,参考官方文档或进行调试故障排除。记得定期更新维护以保证稳定安全。
|
1月前
C/C++test两步完成CMake项目静态分析
通过将C/C++test集成到CMake项目中,并根据项目的需要进行配置,可以在两步内完成CMake项目的静态分析。这样可以帮助开发人员及时发现并修复潜在的代码问题,提高代码质量和可靠性。
8 0
|
1月前
|
人工智能 编解码 对象存储
一键生成视频!用 PAI-EAS 部署 AI 视频生成模型 SVD 工作流
本教程将带领大家免费领取阿里云PAI-EAS的免费试用资源,并且带领大家在 ComfyUI 环境下使用 SVD的模型,根据任何图片生成一个小短视频。