54 Star 234 Fork 92

极简美 / mult_timer

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
cmake_template.md 13.14 KB
一键复制 编辑 原始数据 按行查看 历史
极简美 提交于 2023-08-28 07:13 . 导入最新cmake最新框架

组件模板使用方法

一、 组件介绍

组件的目标是代码复用!

  • 对应“功能下移,业务上移”的逻辑,组件应该是对应到具体的一个个功能,不应该包含任何业务逻辑;
  • 从发布形式来看,组件是可以编译成lib库(*.a或者*.so)对外发布的通用模块。

使用cmake组件构建模板的目标,是为了:

  • 统一组件的组织形式,便于查阅与维护;
  • 统一组件的构建方法,便于不通平台的移植;
  • 统一组件的发布方法,便于发布组件化平台;
  • 统一组件的文档总结,便于知识资产的沉淀。

二、快速构建命令

2.1 获取并运行构建镜像

# 获取构建镜像
$ docker pull harbor.simpost.com/cross-compile-image/x86_64:latest
# 修改镜像TAG
$ docker tag harbor.simpost.com/cross-compile-image/x86_64:latest x86_64:latest
# 运行镜像(若镜像没有运行)
$ docker run -it x86_64:latest /bin/bash
# 连接容器(若镜像已经运行), xxx 是容器的名字
$ docker exec -it xxx /bin/bash

注意:

  1. 拉取镜像时,始终拉取最新版本的镜像(latest);
  2. 当前支持的平台镜像有:x86_64,可以根据需要拉取对应的镜像(注意使用小写);
  3. 后续命令的执行,都是在docker容器中执行。

2.2 获取并构建源码

# 获取组件源码
$ git clone http://gitlab.simpost.com/Common/Templates/component_template.git
$ cd component_template
# 根据当前构建类型,拉取依赖:Release Debug
$ ./prepare.sh -t Release -l https://nexus3.simpost.com/repository/simpost-artifacts-conan-local/
# 执行构建
$ ./build.sh -p x86_64 -t Release

注意:

  1. -p参数指定构建平台,可选的平台有x86_64(注意有的字母要大写);
  2. 拉取依赖命令只需要执行一次即可,后续多次构建直接执行build.sh脚本的命令即可自动识别并拉取依赖;
  3. 如果需要构建后本地验证,可以添加-P选项(大写),这样会在源码的output/package目录下生成当前组件的完整二进制包。

备注:这里的平台英文需要大写,而前面拉取镜像的平台名需要小写(因为docker镜像的TAG不支持大写)

2.3 构建脚本简介

prepare.sh脚本参数:

$ ./prepare.sh -h
SYNOPSIS
    ./prepare.sh -t <Debug | Release> -l <artifactory url>
OPTIONS
    -h: show help for prepare.sh
    -t: specified build type, alternative Debug and Release
    -l: specified artifactory url, like Nexus or JFrog repo-url

build.sh脚本参数:

$ ./build.sh -h
SYNOPSIS
    ./build.sh -p <platform> -t <Debug | Release> -v [build_version]
OPTIONS
    -h: show help for build.sh
    -P: package the distribution files(install) for publication
    -p: specified platform, like X86, MINI2440 ...
    -t: specified build type, alternative Debug and Release
    -v: build version, it's an integer data
    -c: clean the temporary build files
    -b: just build, print all build information for debug
    -d: only delete build files and release files
    -D: delete all files(build, release), and deinit submodules

三、目录及文件介绍

本仓库是基于cmake构建模块代码的模板,目录与核心文件结构如下:

$ tree -l
.
├── build.sh
├── cmake
│   ├── cmake_uninstall.cmake.in
│   ├── commit.cmake
│   ├── conanfile.py
│   ├── config.h.in
│   ├── cpack.cmake
│   ├── deploy.sh.in
│   ├── option.cmake
│   └── version.yaml.in
├── CMakeLists.txt
├── docs
│   ├── cmake_template.md
│   └── images
│       ├── create_project.png
│       └── use_template.png
├── example
│   ├── CMakeLists.txt
│   └── example.c
├── LICENSE
├── prepare.sh
├── README.md
├── source
│   ├── CMakeLists.txt
│   └── source.c
├── tools
│   ├── commit-msg
│   ├── package.sh
│   └── prepare-commit-msg
└── unittest
    └── CMakeLists.txt

7 directories, 24 files
  • source目录: 用于存放本模块的所有源码均;
    • source.c文件: 源码空文件,适配构建模板的使用;
    • CMakeLists.txt文件: 构建库文件模板,只需要添加要构建的源码即可;
  • unittest目录: 用于存放本模块单元测试的所有源码;
    • CMakeLists.txt文件: 单元测试构建模板,待引入单元测试框架后完善;
  • example目录: 用于存放本模块对外提供的示例程序,便于模块使用者快速上手,模块发布时代源码发布;
    • example.c文件: 示例程序,描述如何使用构建模板生成的*_config.h文件;
    • CMakeLists.txt文件: 示例构建模板,描述如何构建和发布示例程序;
  • config目录: 用于存放模块配置文件,比如jsoniniyamlxml文件等;
  • docs目录: 用于存放模块技术总结、设计文档、使用说明文档等,使用markdown编码,便于统一发布htmlpdf文档;
    • cmake_template.md文件: 介绍组件模板库的使用方法,通用的代码组件化指导说明;
  • cmake目录: 用于存放构建相关文件;
    • config.h.in文件: 组件配置文件, 包含版本和宏定义,可用于平台的头文件、函数、库检查,实现跨平台支持;cmake利用此配置文件生成源码使用的config.h文件;
    • option.cmake文件: 编译选项,包括版本配置、编译参数、链接参数、宏定义、平台检查选项等;
    • conanfile.py文件:conan包管理工具配置文件,记录了自己打包的方法和依赖信息;
    • deploy.sh.in文件:制品上传脚本,用于与Nexus制品库交互;
    • version.yaml.in文件:记录了当前构建的详细信息,含pipeline的详细信息;
    • cpack.cmake文件:本地大包方法,含cmake和自己的大包方法;
    • commit.cmake文件: 提取git仓库当前构建状态的分支名和commit编号,分别保存在GIT_BRANCH_NAMEGIT_COMMIT_HASH变量中,不要修改此文件内容;
    • cmake_uninstall.cmake.in文件: 用于提供模块编译后的头文件、库文件和工具的安装和卸载的方法,不需要对其进行修改。
  • tools:常用工具,包括代码提交规范脚本、打包脚本、代码规范检查等等;
  • .gitignoregit过滤文件,不需要对其进行修改;
  • LICENSE文件: MIT开源协议,保留版权。
  • CMakeLists.txt文件: 模块构建总入口,非必要不要修改此文件;
  • README.md文件: 使用markdown语法编辑的模块说明文档。
  • build.sh:构建脚本入口,对构建命令进行了封装,更易于使用;
  • prepare.sh:拉取依赖包脚本,需要传输制品库的地址。

四、模板基础使用

在创建项目时,选择Create from template,再选择Instance,即可使用模板创建仓库。

image

image

即可基于模板创建一个仓库。

4.1 修改项目名称(默认不修改)

在顶层CMakeLists.txt文件中,默认使用的当前目录名作为组件的项目名,构建组件库时,库的名称也会使用项目名命名。若组件目录名与项目名不一致时,可修改${component_name}为实际的项目名称:

#Component name, default the same as directory name
get_filename_component(component_dir ${CMAKE_PARENT_LIST_FILE} DIRECTORY)
get_filename_component(component_name ${component_dir} NAME)
project(${component_name})

4.2 修改组件名称(默认不修改)

默认情况下,项目名称与组件名称是一样的,都是当前组件的目录名,若组件名与项目名不同(比如libcurl组件,目录名和项目名是libcurl,但其组件名应为curl),则修改顶层CMakeLists.txt文件中如下两行即可:

set(COMPONENT_NAME template)

4.3 修改组件版本号

cmake/option.cmake文件前面几行,就是设置组件的版本信息的,可根据组件版本进行配置:

  • COMPONENT_VERSION_MAJOR: 主版本号;
  • COMPONENT_VERSION_MINOR: 次版本号;
  • COMPONENT_VERSION_PATCH: 修订版本号;
  • COMPONENT_VERSION_BUILD: 构建版本号。

4.4 添加头文件路径

若模块比较复杂,头文件都会出现多层目录,编译头文件依赖的路径统一在cmake/option.cmake中添加即可:

#############################################################################################
## include/library directories, link library
#############################################################################################
include_directories(${CMAKE_BINARY_DIR})
include_directories(${PROJECT_SOURCE_DIR}/source)

4.5 组件库构建

组件源码存放在source目录下,添加目录下所有源码的方法:

aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} ${COMPONENT_NAME}_SRCS)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/your_source_dir ${COMPONENT_NAME}_SRCS)

其中BUILD_STATIC_LIBSBUILD_SHARED_LIBS选项用于控制是构建动态库还是静态库。

4.6 文件发布与安装

组件一般对外发布库文件、头文件,以及示例程序文件等,可能还包含一些工具文件、配置文件等。要发布一个目标或者文件的方法如下:

# 库文件统一安装到lib目录下
install(TARGETS ${COMPONENT_NAME} DESTINATION lib)
install(TARGETS ${COMPONENT_NAME}_static DESTINATION lib)

# 头文件统一安装到include的${COMPONENT_NAME}目录下
install(FILES ${head_file.h} DESTINATION include/${COMPONENT_NAME})
install(DIRECTORY ${dir} DESTINATION include/${COMPONENT_NAME})

# example统一安装到example的${COMPONENT_NAME}目录下
install(TARGETS example DESTINATION example/${COMPONENT_NAME})
install(FILES example.c DESTINATION example/${COMPONENT_NAME})

4.7 示例程序构建

一个完整的模块,需要有对应的示例程序,便于使用者快速上手和使用。示例程序统一放在example目录中,若模块包含很多子模块,也可以在example目录下再创建子目录。示例程序的构建可参考如下方法:

add_executable(xxx_example xxx_example.c)
target_link_libraries(xxx_example ${COMPONENT_LIBRARY})
install(TARGETS xxx_example DESTINATION example/${COMPONENT_NAME})
install(FILES xxx_example.c DESTINATION example/${COMPONENT_NAME})

其中COMPONENT_LIBRARY会根据构建的是动态库还是静态库,连接正确的库文件。

4.8 单元测试程序构建

一个完整的模块,同样需要有对应的单元测试程序,以保障模块的高质量的交付。建议所有的单元测试程序全部放在unittest目录中,根据使用的单元测试框架的不同,使用对应的构建方法自动化的进行构建。

五、模板高级使用

此部分介绍核心聚焦在cmake/option.cmakecmake/config.h.in两个文件的配置上面。

5.1 添加编译宏

比如添加OPTION_ENABLE编译选项,对应在cmake/option.cmake中增加如下一行:

option(OPTION_ENABLE "Enable option" ON)

并在cmake/config.h.in中增加如下一行:

#cmakedefine OPTION_ENABLE

执行cmake命令时,就会在组件的config.h文件中生成如下宏定义:

#define OPTION_ENABLE

在程序源码中,就可以包含组件配置文件并使用宏编译选项了:

#include "**_config.h"

#ifdef OPTION_ENABLE
    ......
#else
    ......
#endif

5.2 添加宏定义

添加编译宏与编译选项的方法类似,直接在cmake/option.cmake中使用add_compile_definitions()命令添加宏定义:

add_compile_definitions(-DOPTION_INT=1234)
add_compile_definitions(-DOPTION_STR="Hello world!")

源码中即可直接使用如上宏定义了:

#include "**_config.h"

......
printf("option int: %d\n", OPTION_INT);
printf("option str: %s\n", OPTION_STR);
......

为了让程序在各种场景下都能正确的编译,还需要为宏定义添加默认定义,对应在cmake/config.h.in文件中增加:

#ifndef OPTION_INT
#define OPTION_INT      2345
#endif

#ifndef OPTION_STR
#define OPTION_STR      "Hey you!"
#endif

5.3 平台检测

有时候,我们编写的功能依赖于具体平台相关的头文件和函数,当我们在做移植时,首先需要检查目标平台是否具有相关的能力和特性。通过不同平台能力与特性的检测,也有利于编写构建时跨平台的通用组件代码。下面是cmake提供的标准头文件、函数和库检测的方法,并生成对应的编译宏,以供程序源码中使用:

# C头文件检测方法
include(CheckIncludeFiles)
CHECK_INCLUDE_FILES(stdint.h HAVE_STDINT_H)

# C++头文件检测方法
include(CheckIncludeFileCXX)
CHECK_INCLUDE_FILE_CXX(queue HAVE_QUEUE_H)

# 函数检测方法
include(CheckFunctionExists)
CHECK_FUNCTION_EXISTS(poll HAVE_POLL)

# 符号检测方法
include(CheckSymbolExists)
CHECK_SYMBOL_EXISTS(alloca "alloca.h" HAVE_ALLOCA)

# 库检测方法
find_library(HAVE_LIBRT rt)

include(CheckLibraryExists)
CHECK_LIBRARY_EXISTS(rt timer_gettime "" HAVE_LIBRT)
C
1
https://gitee.com/simpost/mult_timer.git
git@gitee.com:simpost/mult_timer.git
simpost
mult_timer
mult_timer
master

搜索帮助