组件的目标是代码复用!
lib
库(*.a
或者*.so
)对外发布的通用模块。使用cmake
组件构建模板的目标,是为了:
# 获取构建镜像
$ 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
注意:
latest
);x86_64
,可以根据需要拉取对应的镜像(注意使用小写);docker
容器中执行。# 获取组件源码
$ 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
注意:
-p
参数指定构建平台,可选的平台有x86_64
(注意有的字母要大写);build.sh
脚本的命令即可自动识别并拉取依赖;-P
选项(大写),这样会在源码的output/package
目录下生成当前组件的完整二进制包。备注:这里的平台英文需要大写,而前面拉取镜像的平台名需要小写(因为docker镜像的TAG不支持大写)。
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目录
: 用于存放模块配置文件,比如json
、ini
、yaml
、xml
文件等;docs目录
: 用于存放模块技术总结、设计文档、使用说明文档等,使用markdown
编码,便于统一发布html
或pdf
文档;
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_NAME
和GIT_COMMIT_HASH
变量中,不要修改此文件内容;cmake_uninstall.cmake.in文件
: 用于提供模块编译后的头文件、库文件和工具的安装和卸载的方法,不需要对其进行修改。tools
:常用工具,包括代码提交规范脚本、打包脚本、代码规范检查等等;.gitignore
:git
过滤文件,不需要对其进行修改;LICENSE文件
: MIT
开源协议,保留版权。CMakeLists.txt文件
: 模块构建总入口,非必要不要修改此文件;README.md文件
: 使用markdown
语法编辑的模块说明文档。build.sh
:构建脚本入口,对构建命令进行了封装,更易于使用;prepare.sh
:拉取依赖包脚本,需要传输制品库的地址。在创建项目时,选择Create from template
,再选择Instance
,即可使用模板创建仓库。
即可基于模板创建一个仓库。
在顶层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})
默认情况下,项目名称与组件名称是一样的,都是当前组件的目录名,若组件名与项目名不同(比如libcurl
组件,目录名和项目名是libcurl
,但其组件名应为curl
),则修改顶层CMakeLists.txt
文件中如下两行即可:
set(COMPONENT_NAME template)
在cmake/option.cmake
文件前面几行,就是设置组件的版本信息的,可根据组件版本进行配置:
COMPONENT_VERSION_MAJOR
: 主版本号;COMPONENT_VERSION_MINOR
: 次版本号;COMPONENT_VERSION_PATCH
: 修订版本号;COMPONENT_VERSION_BUILD
: 构建版本号。若模块比较复杂,头文件都会出现多层目录,编译头文件依赖的路径统一在cmake/option.cmake
中添加即可:
#############################################################################################
## include/library directories, link library
#############################################################################################
include_directories(${CMAKE_BINARY_DIR})
include_directories(${PROJECT_SOURCE_DIR}/source)
组件源码存放在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_LIBS
和BUILD_SHARED_LIBS
选项用于控制是构建动态库还是静态库。
组件一般对外发布库文件、头文件,以及示例程序文件等,可能还包含一些工具文件、配置文件等。要发布一个目标或者文件的方法如下:
# 库文件统一安装到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})
一个完整的模块,需要有对应的示例程序,便于使用者快速上手和使用。示例程序统一放在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
会根据构建的是动态库还是静态库,连接正确的库文件。
一个完整的模块,同样需要有对应的单元测试程序,以保障模块的高质量的交付。建议所有的单元测试程序全部放在unittest
目录中,根据使用的单元测试框架的不同,使用对应的构建方法自动化的进行构建。
此部分介绍核心聚焦在cmake/option.cmake
和cmake/config.h.in
两个文件的配置上面。
比如添加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
添加编译宏与编译选项的方法类似,直接在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
有时候,我们编写的功能依赖于具体平台相关的头文件和函数,当我们在做移植时,首先需要检查目标平台是否具有相关的能力和特性。通过不同平台能力与特性的检测,也有利于编写构建时跨平台的通用组件代码。下面是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)
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。