技术分享(一)- 简单谈谈工程构建工具之CMake

目录

 

前言

出现背景

工作方式

特点

应用情况

编辑器推荐

个人见解

基本用法

project

add_library

add_executable

add_subdirectory

include_directories

install

添加第三方库

option


前言

以下内容为我在工作中使用cmake的一些理解及经验分享,原文为个人对cmake的一些使用笔记,目前对其进行一些整理。从背景、工作方式、特点、应用情况以及常见的几个用法,这几个维度去分享cmake的一些经验。由于文章为较早之前的总结文档,若有参考到其他一些文章的内容,请联系我添加相关链接。

出现背景

  • 编译C/C++有好几种 Make 工具,例如 GNU Make ,QT 的 qmake ,微软的 MS nmake,BSD Make(pmake),Makepp,等等。这些 Make 工具遵循着不同的规范和标准,所执行的 Makefile 格式也千差万别。

  • 问题:如果软件想跨平台,必须要保证能够在不同平台编译。而如果使用上面的 Make 工具,就得为每一种标准写一次 Makefile ,这将是一个灾难。

  • CMake就是针对上面问题所设计的工具,提供统一的工程构建规范,再进行Makefile的构建。(除了cmake,像AutoConfig也是常见的工程构建工具,比如常见到的执行configure文件进行工程构建)

工作方式

  • 首先开发者编写一种平台无关的 CMakeList.txt 文件来定制整个编译流程

  • 然后再根据目标用户的平台进一步生成所需的本地化 Makefile 和工程文件,如 Unix 的 Makefile 或 Windows 的 Visual Studio 工程。

特点

  • 开放源代码, 使用类 BSD 许可发布。

  • 跨平台, 并可生成 native 编译配置文件, 在 Linux/Unix 平台, 生成 makefile, 在苹果平台, 可以生成 xcode, 在 Windows 平台, 可以生成 MSVC 的工程文件。

  • 能够管理大型项目, KDE4 就是最好的证明。

  • 简化编译构建过程和编译过程。 CMake 的工具链非常简单: cmake+make。

  • 可扩展, 可以为 cmake 编写特定功能的模块, 扩充 cmake 功能。

应用情况

  • CMake 是一个比make 更高级的编译配置工具,统一了构建方式。

  • 使用 CMake 作为项目架构系统的知名开源项目有 VTK、ITK、KDE、OpenCV、OSG 等,包括OpenGL的开源项目glfw。

编辑器推荐

  • CLion,对cmake工程有较为不错的支持,同时跨平台,配置可多平台共用(个人常用,主要使用系统为ubuntu)。
  • Visual Studio,debug功能非常强悍,可通过交叉编译解决跨平台问题,适用于使用windows开发的场景。
  • Visual Studio Code,文本编辑器的基本优点均有,可配置性高,主要还是启动快,快速完成编译,可搭配CLion与vs一起使用。缺点是跳转略差。

个人见解

由于目前工作内容,难免会使用到嵌入式的编译工具链,交叉编译就成为了C++工程构建中必须考虑的点。事实上,目前的C/C++工程构建工具还是挺多的,选择cmake,主要是考虑到易用性、跨平台这两大方面,同时业内使用的较多,整体支持也不错。我这边也有参与基于docker的交叉编译工具开发,因此,对统一build(构建)工具的重要性感受颇深。 

在编译一些第三方库时,有使用统一构建工具的,能带来极大的编译,目前很多开源库都逐步开始支持cmake编译,比如curl。也有一些使用了其他构建工具,如AutoConfig。也有不少只使用GNU Make,一个Makefile文件,拥有较高的灵活度,传入参数也不够稳定,因此,只能针对不同库编写不同的脚本,没法做到统一的交叉编译。

总的来说,cmake是目前较为流行C/C++构建工具,提供了不少优秀的特性,可以在工程构建中带来极大的便利,也非常推荐使用。个人习惯来说:

  • 单文件,不想打开IDE,只想做些简单的验证,一般可以直接考虑使用g++/gcc
  • 多文件构建工程,一般直接使用cmake了,很少会考虑直接编写makefile

基本用法

这边同时总结了一些cmake的基本用法,可供参考,更多细节直接百度/google一下,或者直接查看官网文档。

注意:

项目名称_SOURCE_DIR指代工程目录,如/home/myname/mycpp/

项目名称_BINARY_DIR指代编译路径,如果使用内部构建(在项目根目录使用cmake构建),这项目名称一样;如果使用外部构建(新建一个build目录,使用cmake ..构建),则/home/myname/mycpp/build/

查看细节可以是make VERBOSE=1去编译

CMakelists.txt如果做了修改,最好是清空build路径再进行重新cmake

project

声明项目名称为mycpp

project(mycpp)

使用项目名称

${PROJECT_NAME}

链接:https://cmake.org/cmake/help/latest/command/project.html

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

add_library

添加生成一个库, SHARED动态库,STATIC静态库,MODULE在使用dyld的系统有效,否则当做为SHARED对待

ADD_LIBRARY(libname     # 库名,不需要写后缀
[SHARED|STATIC|MODULE]  # 库类型
[EXCLUDE_FROM_ALL]      # 这个库不会被默认构建,除非有其他的组件依赖或者手工构建
source1 source2 ... sourceN) # 依赖的cpp文件

添加一个动态库编译,会生成一个login_shared.so文件,但是编译过程中不会去检查引用错误,只检查基本的语法错误

add_library(login_shared SHARED ./network/Login.cpp)

添加一个静态库编译,会生成一个login_shared.a文件

add_library(login_shared STATIC ./network/Login.cpp)

SET_TARGET_PROPERTIES可以生成一个名字相同的动态库和静态库,同时project可以指定版本号

add_executable

第一个参数为生成的可执行文件名称,可以随意定义,例子中使用了项目名称

add_executable(${PROJECT_NAME} 
    src/main.cpp
    src/network/Login.cpp
)

如果不想每次都手动添加cpp文件,可借助file指令进行操作

file(GLOB_RECURSE PROJECT_SOURCES ${PROJECT_SOURCE_DIR}/src "*.cpp")
add_executable(${PROJECT_NAME} ${PROJECT_SOURCES})

add_subdirectory

把src编译出来的二进制文件放到bin目录中,如果在build里面cmake ..,则可执行文件中在build/bin中

ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
​
add_subdirectory(src bin)

include_directories

添加头文件目录,如CMakelists.txt同一目录下的include文件夹

include_directories(
    ./include
)

 

install

添加安装的目标路径

install(TARGETS ${PROJECT_NAME}
    DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/output
)

参数中的 TARGETS 后面跟的就是我们通过 ADD_EXECUTABLE 或者 ADD_LIBRARY 定义的目标文件,可能是可执行二进制、动态库、静态库。

目标类型也就相对应的有三种,ARCHIVE 特指静态库,LIBRARY 特指动态库,RUNTIME特指可执行目标二进制。

INSTALL(TARGETS targets...
    [[ARCHIVE|LIBRARY|RUNTIME]
    [DESTINATION <dir>]
    [PERMISSIONS permissions...]
    [CONFIGURATIONS
    [Debug|Release|...]]
    [COMPONENT <component>]
    [OPTIONAL]
    ] [...]
)

INSTALL(TARGETS myrun mylib mystaticlib
    RUNTIME DESTINATION bin
    LIBRARY DESTINATION lib
    ARCHIVE DESTINATION libstatic
)

安装

make DESTDIR=/install/directory install

 

添加第三方库

以curl库和自己写的一个简单的hello库,输出一个hello world添加为例子

首先把头文件目录添加进来,可以直接使用具体路径

include_directories(
    ${CURL_INCLUDE_DIR}
    /src/local/hello # 该目录下有hello.h
)

链接第三方库,特别注意:target的可执行文件/库,其add_executable指令必须在同一个文件,报错

target_link_libraries(<target> ... <item>... ...)
add_executable(
    ${PROJECT_NAME}
    main.cpp
    network/Login.cpp
)
​target_link_libraries(${PROJECT_NAME} curl hello) # 不写后缀默认为so动态库,静态库添加.a后缀即可

find_library查找yaml-cpp和curl库,并分别赋值给YAML_LIBRARY和CURL_LIBRARY

find_library(YAML_LIBRARY yaml-cpp)
find_library(CURL_LIBRARY curl)
target_link_libraries(${PROJECT_NAME} ${CURL_LIBRARY} ${YAML_LIBRARY})

target_link_libraries主要是链接库的作用,因此如果是编译库的话,不一定需要使用,一般是在编译可执行文件的路径中使用,将其链接进可执行文件中。

添加系统库直接使用target_link_libraries写库名就可以,不需要find_library

target_link_libraries(
    ${PROJECT_NAME}
    pthread
    rt
)

添加自定义路径第三方库/自己的库,如在当前目录下的lib目前有自己要用的库文件

find_library(IOT_SDK_LIB NAME iot_sdk PATHS ${CMAKE_CURRENT_SOURCE_DIR}/lib)
find_library(IOT_HAL_LIB NAME iot_hal P正常模式切换到光强模式,有一定概率出现,2s时间后频率依然不够稳定(雷达本身)
雷达接收到启动指令时,有较低几率出现,雷达没有转,却发送了启动成功的指令(雷达本身)ATHS ${CMAKE_CURRENT_SOURCE_DIR}/lib)
find_library(IOT_TLS_LIB NAME iot_tls PATHS ${CMAKE_CURRENT_SOURCE_DIR}/lib)
​
target_link_libraries(
    ${PROJECT_NAME}
    ${IOT_SDK_LIB}
    ${IOT_HAL_LIB}
    ${IOT_TLS_LIB}
    pthreadW
    rt
)

注意:

  1. 不在同一个目录的话,cmake的时候就会报错

 

如果target_link_libraries编写有问题的话,会报出大量引用错误

option

  1. 编译脚本传入参数 传入一个cmake option TEST_DEBUG

cmake -DTEST_DEBUG=ON .
cmake --build .
  1. CMake脚本接收option cmake 脚本定义TEST_DEBUG 默认关闭OFF

project(test)
option(TEST_DEBUG "option for debug" OFF)
if (TEST_DEBUG)
    add_definitions(-DTEST_DEBUG)
endif()
...

参考链接1:https://www.jianshu.com/p/59d93a996290

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值