C++使用小教程01--CMakeList

文章目录

  • 学习前言
  • 相关概念
    • 编译
      • GCC
      • LLVM(后端)
      • Clang(前端)
    • make
      • makefile
    • CMake
      • CMakeList.txt
  • 基本用法
    • 构建和链接静态库和动态库
      • add_library
      • target_link_libraries
    • 检测外部库:I. 使用pkg-config
    • 检测外部库:II. 自定义find模块
      • find_package
      • 总而言之,有四种方式可用于找到依赖包
  • 链接自己写的库的例子
  • 参考

学习前言

最近经常要编译github上clone下来的工程,大部分是基于cmake构建。要是自己想改一改,加一加就需要自己写CMakelist.txt。其中有几个问题始终云里雾里,花了很多时间才搞定。现在主要对找包的问题总结一下。
C++使用小教程01--CMakeList_第1张图片

相关概念

C++使用小教程01--CMakeList_第2张图片

编译

GCC

GNU编译器套件gcc可以编译C,C++,Fortran,Java等等编程语言。当程序只有一个源文件时,直接用gcc就可以编译。

LLVM(后端)

刚进入 Apple,Chris Lattner 就大展身手:首先在 OpenGL 小组做代码优化,把 LLVM 运行时的编译架在 OpenGL 栈上,这样 OpenGL 栈能够产出更高效率的图形代码。如果显卡足够高级,这些代码会直接扔入 GPU 执行

同时,LLVM 的链接优化被直接加入到 Apple 的代码链接器上,而 LLVM-GCC 也被同步到使用 GCC4.0 代码。

Clang(前端)

Clang是LLVM 的前端,可以用来编译 C,C++,ObjectiveC 等语言。Clang 则是以 LLVM 为后端的一款高效易用,并且与IDE 结合很好的编译前端

make

makefile

makefile,即自动化编译脚本,提供了编译和链接的指令,由make工具实现这些指令。makefile中包括了调用gcc或其他编译器编译源代码的命令。

CMake

CMake是一个构建生成器,提供了强大的领域特定语言(DSL)来描述构建系统应该实现的功能。这是CMake的主要优势之一,它允许使用相同的CMake脚本集生成平台原生构建系统。CMake软件工具集,使开发人员可以完全控制给定项目的生命周期:
• CMake是描述如何在所有主要硬件和操作系统上配置、构建和安装项目,无论是构建可执行文件、库,还是两者都要构建。
• CTest定义测试、测试套件,并设置应该如何执行。
• CPack为打包需求提供了DSL。
• CDash将项目的测试结果在面板中展示。

CMakeList.txt

CMakeList.txt(组态档)为cmake提供了生成makefile的指令。类似makefile与make工具的关系。其中组态档是由人工完成的。

基本用法

构建和链接静态库和动态库

当有Message.cpp 和Message.h文件后, 先把它们编译成一个库,而不是直接编译成可执行文件

1.创建目标-静态库。库的名称和源码文件名相同,如下:

add_library(message
  STATIC
    Message.hpp
    Message.cpp
  )

2.创建hello-world可执行文件的目标部分不需要修改:

add_executable(hello-world hello-world.cpp)

3.最后,将目标库链接到可执行目标:

target_link_libraries(hello-world message)

顺序不要错

add_library

add_library(message STATIC Message.hpp Message.cpp) 生成必要的构建指令,将指定的源码编译到 中。add_library的第一个参数是目标名。整个CMakeLists.txt中,可使用相同的名称来引用库。生成的库的实际名称将由CMake通过在前面添加前缀lib和适当的扩展名作为后缀来形成 。生成库是根据第二个参数(STATIC或SHARED)和操作系统确定的。
- STATIC: 用于创建静态库,即编译文件的打包存档,以便在链接其他目标时使用,例如:可执行文件。
- SHARED: 用于创建动态库,即可以动态链接,并在运行时加载的库。可以在CMakeLists.txt中使用add_library(message SHARED Message.hpp Message.cpp)从静态库切换到动态共享对象(DSO)。

target_link_libraries

target_link_libraries(hello-world message): 将库链接到可执行文件。此命令还确保hello-world可执行文件可以正确地依赖于消息库。因此,在消息库链接到hello-world可执行文件之前,需要完成消息库的构建。

检测外部库:I. 使用pkg-config

1.使用CMake附带的find-module,查找pkg-config。这里在find_package中传递了QUIET参数。只有在没有找到pkg-config时,CMake才会报错:

find_package(PkgConfig REQUIRED QUIET)

2.找到pkg-config时,我们将使用pkg_search_module函数,以搜索任何附带包配置.pc文件的库或程序。该示例中,我们查找ZeroMQ库:

pkg_search_module(
  ZeroMQ
  REQUIRED
      libzeromq libzmq lib0mq
  IMPORTED_TARGET
  )

检测外部库:II. 自定义find模块

1.将当前源目录CMAKE_CURRENT_SOURCE_DIR,添加到CMake将查找模块的路径列表CMAKE_MODULE_PATH中。这样CMake就可以找到,我们自定义的FindZeroMQ.cmake模块:

list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR})

2.现在FindZeroMQ.cmake模块是可用的,可以通过这个模块来搜索项目所需的依赖项。由于我们没有使用QUIET选项来查找find_package,所以当找到库时,状态消息将自动打印:

find_package(ZeroMQ REQUIRED)

3.我们继续添加hwserver可执行目标。头文件包含目录和链接库是使用find_package命令成功后,使用ZeroMQ_INCLUDE_DIRS和ZeroMQ_LIBRARIES变量进行指定的:

add_executable(hwserver hwserver.c)
target_include_directories(hwserver
  PRIVATE
      ${ZeroMQ_INCLUDE_DIRS}
  )
target_link_libraries(hwserver
  PRIVATE
      ${ZeroMQ_LIBRARIES}
  )

find_package

  1. 检查用户是否为所需的包提供了自定义位置。

  2. 使用find_家族中的命令搜索所需包的必需组件,即头文件、库、可执行程序等等。我们使用find_path查找头文件的完整路径,并使用find_library查找库。CMake还提供find_file、find_program和find_package。这些命令的签名如下:

    find_path(<VAR> NAMES name PATHS paths)
    
  3. 如果搜索成功,将保存搜索结果;如果搜索失败,则会设置为-NOTFOUND。NAMES和PATHS分别是CMake应该查找的文件的名称和搜索应该指向的路径。

  4. 初步搜索的结果中,可以提取版本号。示例中,ZeroMQ头文件包含库版本,可以使用字符串操作和正则表达式提取库版本信息。

  5. 最后,调用find_package_handle_standard_args命令。处理find_package命令的REQUIRED、QUIET和版本参数,并设置ZeroMQ_FOUND变量。

总而言之,有四种方式可用于找到依赖包

  1. 使用由包供应商提供CMake文件Config.cmake ,ConfigVersion.cmake和Targets.cmake,通常会在包的标准安装位置查找。
  2. 无论是由CMake还是第三方提供的模块,为所需包使用find-module。
  3. 使用pkg-config,如本节的示例所示。
  4. 如果这些都不可行,那么编写自己的find模块。

链接自己写的库的例子

  1. 写一个库,在cmake文件夹下新建一个FindAdd.cmake的文件。我们的目标是找到库的头文件所在目录和共享库文件的所在位置。

    # 在指定目录下寻找头文件和动态库文件的位置,可以指定多个目标路径
    find_path(ADD_INCLUDE_DIR libadd.h /usr/include/ /usr/local/include ${CMAKE_SOURCE_DIR}/ModuleMode)
    find_library(ADD_LIBRARY NAMES add PATHS /usr/lib/add /usr/local/lib/add ${CMAKE_SOURCE_DIR}/ModuleMode)
    
    if (ADD_INCLUDE_DIR AND ADD_LIBRARY)
        set(ADD_FOUND TRUE)
    endif (ADD_INCLUDE_DIR AND ADD_LIBRARY)
    

    这时我们便可以像引用标准库一样引入我们自定义的库了。

  2. 在CMakeList.txt中添加

# 将项目目录下的cmake文件夹加入到CMAKE_MODULE_PATH中,让find_pakcage能够找到我们自定义的函数库
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}")
add_executable(addtest addtest.cc)
find_package(ADD)
if(ADD_FOUND)
    target_include_directories(addtest PRIVATE ${ADD_INCLUDE_DIR})
    target_link_libraries(addtest ${ADD_LIBRARY})
else(ADD_FOUND)
    message(FATAL_ERROR "ADD library not found")
endif(ADD_FOUND)

参考

https://zhuanlan.zhihu.com/p/97369704
https://www.bookstack.cn/books/CMake-Cookbook
https://zhuanlan.zhihu.com/p/64373941
https://zhuanlan.zhihu.com/p/357803433

你可能感兴趣的:(C++使用小教程,c++)