使用cmake构建一个大型项目框架

2023-05-16

文章目录

  • 使用CMake构建一个大型项目工程
  • 1. 大型工程目录结构介绍
    • 1.1 工程目录结构介绍
    • 1.2 工程目录说明(我是这样设计的,你们也可以参考类似这样设计)
    • 1.3 最外层CMakeLists.txt说明
    • 1.4 业务逻辑中的CMakeLists.txt说明
      • 1.4.1 source中的目录结构
      • 1.4.2 CMakeLists.txt说明
    • 1.5 测试模型中的的CMakeLists.txt说明
      • 1.51 unit_test中的目录结构
  • 2. 编译脚本说明
  • 3. 工程地址

使用CMake构建一个大型项目工程

在上文中,我们已经学会用CMake去构建一个项目。接来下我对这个工程进一步的完善,使其能应用在更加的项目开发当中。构建一个项目工程,首先要知道自己的要用这个工程来做什么。我是一名嵌入式软件开发工程师。日常的开发有如下需求:

  1. 开发的平台经常更换,有时使用st的芯片,有时使用nxp的芯片,还有国产的芯片等,这些开发平台是经常需要改变。使用这些芯片的时候,我要使用他们的外设接口(如 spi i2c uart )去操作各种传感器。各种传感器都各种类型品牌,比如加速传感器就有几十种,经常需要测试那种比较好和便宜,各种传感器芯片都要编写一个测试单元去测试,除了板载的测试,我们还需要经常网络的测试,各种类型库的测试,,最终单元测试完成后才能加入我的源码中,进行商务逻辑编写。
  2. 在开发过程中,难免会使用一些库,比如mqtt,curl,json等
  3. 经常会使用交叉编译工具链编译之后传到对应的硬件平台。这里弄一个编译之后自动传到对应的脚本

要优化好上面的脚本也是很大的工作量,接下来我将给大家介绍一下我自己常用的模型是怎么样的。

1. 大型工程目录结构介绍

1.1 工程目录结构介绍

为了简介,我删除了很多我自己定义的文件夹,留下的都是自己定义的

.
├── bin
│   └── CMake_Project
├── build
│   ├── CMakeCache.txt
│   ├── CMakeFiles
│   ├── cmake_install.cmake
│   ├── install_manifest.txt
│   ├── Makefile
│   └── source
├── buildcfg
├── build.sh
├── CMakeLists.txt
├── doc
│   └── README.md
├── log
│   └── README.md
├── README.md
├── script
│   └── README.md
├── source
│   ├── CMakeLists.txt
│   ├── main.cpp
│   └── System_Frame
├── third_lib
│   └── mqtt_lib
└── unit_test
    ├── board_test
    └── net_test

1.2 工程目录说明(我是这样设计的,你们也可以参考类似这样设计)

1.bin
		该目录存放着程序的执行文件,如单元测试的执行文件以及总工程项目的执行文件。

2.build
		该目录存放CMake构建后的缓存文件。

3.doc
		该目录存放工程相关说明文档。

4.script
		该目录存放其他辅助的脚本。

5.source
		该目录存放主体业务源码。

6.third_lib
		该目录存放第三方使用的库。

7.unit_test
		该目录存放单元测试工程。

8.CMakeLists.txt
		总工程构建CMake文件。

9.build.sh
		总工程编译脚本文件。

10.buildcfg
		build.sh的配置文件。

11. log
     存放修改的详细内容

1.3 最外层CMakeLists.txt说明

cmake_minimum_required(VERSION 3.2.0)

#set the project name
set(LISIN_MTR "CMake_Project")

set(CMAKE_BUILD_TYPE "Debug") #Release 如果是发行版本就更换Debug为Release 
if(CMAKE_BUILD_TYPE STREQUAL Debug)
    add_definitions(-D_DEBUG) #添加D_DEBUG编译参数
endif()

#设置编译参数
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -lpthread -std=gnu99 -g -o0") #
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}  -W -Wall -Wextra -g2 -ggdb -o0 -lpthread")

# 设置编译工具链
#set(COMPILER_PREFIX "arm-linux-")
#set(CMAKE_C_COMPILER ${COMPILER_PREFIX}gcc)
#set(CMAKE_CXX_COMPILER ${COMPILER_PREFIX}g++)

#下面这部分是可修改部分,根据自己的实际工程要做什么去定义,我下面只是给你们一个参考
#################################################################
# 定义头文件路径对应的名字,方便等下去设定
#通用与辅助
set(SYS_FRAME_H     "${CMAKE_SOURCE_DIR}/source/System_Frame")

#库文件路径添加
file(GLOB_RECURSE Mqtt_lib ${THIRLD_LIB_PATH}/mqtt_lib/lib/*.so*)

#项目编译选择,通过选择开关来进行编译选项,通过这种方式去选择编译测试模块中的哪一个
# 之后需要添加,
option(Main_Source "main pro"     ON)
option(BOARD_TEST  "board_test"   OFF)

#添加子工程目录
if(Main_Source)
add_subdirectory(${CMAKE_SOURCE_DIR}/source)
endif()

#测试程序
if(BOARD_TEST)
option(IIC_TEST  "UART"   OFF)
option(GPIO_TEST "GPIO"  OFF)
add_subdirectory(${CMAKE_SOURCE_DIR}/unit_test/board_test)
endif()

1.4 业务逻辑中的CMakeLists.txt说明

1.4.1 source中的目录结构

.
├── CMakeLists.txt
├── main.cpp
└── System_Frame
    ├── print_cmake.cpp
    └── print_cmake.h

1.4.2 CMakeLists.txt说明

# 查找当前目录下的所有源文件
aux_source_directory("${PROJECT_SOURCE_DIR}/source/System_Frame" DIR_SOURCE_SYS_FRAME)
aux_source_directory("${PROJECT_SOURCE_DIR}/source" DIR_SOURCE_SRCS)

# 指定生成目标
add_executable( ${LISIN_MTR} 
                ${DIR_SOURCE_SYS_FRAME}
                ${DIR_SOURCE_SRCS}
              )

message(STATUS "SYS_FRAME_H: " ${SYS_FRAME_H})

#头文件包含
target_include_directories(${LISIN_MTR} PRIVATE
                           ${SYS_FRAME_H}
                           )

#连接库                         
target_link_libraries(${LISIN_MTR} PUBLIC ${Mqtt_lib})

#执行文件的安装位置
install(TARGETS ${LISIN_MTR} RUNTIME DESTINATION ${PROJECT_SOURCE_DIR}/bin)

## 主要做了三件事
# 1. 设置源文件的目录,获取所有源文件
# 2. 设置头文件目录
# 3. 链接库
# 4. 定义可执行文件的安装位置

1.5 测试模型中的的CMakeLists.txt说明

1.51 unit_test中的目录结构

unit_test:这里相当于一个独立的单元,完全由自己编写,不影响主程序,在最外层CMakeLists.txt开关调为ON就编译的这里的程序

.
├── board_test
│   ├── gpio
│   │   └── CMakeLists.txt
│   └── uart
│       └── CMakeLists.txt
└── net_test

# 1. add_subdirectory()使用的就是增加子目录的方式,然后每个子目录干自己的事
# 2. 里面的每个文件夹都有单独的测试单元,只有测试好了才放到主程序,

2. 编译脚本说明

这个脚本主要的作用是一键编译工程,解决了局域网网内文件的传输,可以可以执行文件放到局域网的目标设备中,对于嵌入式开发会很方便

 #用于工程编译以及传输执行固件到局域网下的目标板,使用如下:
 #
 # ./build,sh -u root -p root -i 192.168.1.20 -s lisinmtr -h /usr/local/lisin/bin
 #
 # -u :ssh用户
 # -p :ssh用户密码
 # -i :ssh用户地址
 # -s :需要传输的源文件
 # -h :ssh用户的传输目标地址 
 #
 # -u -p -i -s -h 参数都不加上去的情况下使用默认参数:
 #login_usr_default, login_pswd_default, login_ip_default, exe_bin_default, lisin_bin_path_default
 #
 #注意:修改后的参数会保存在[buildcfg]配置文件里,每次运行都会先读取配置文件里的信息!!!!
 #
 # @*******: ********************************************************
### 

#!/bin/bash

#开发主机相关信息,当前build.sh放的地方
dirCurrent=`pwd`

#放CMake编译生成文件的地方
dirCompile=$dirCurrent/build

#默认要传输的文件设置,传输的文件名
exe_bin_default=lisinmtr

#局域网的设备 信息
#login_user ssh的登录账户
login_usr_default="root"
#login_passwd ssh的登录密码
login_pswd_default="root"
#login_ip ssh的ip
login_ip_default="192.168.1.20"
#工作目录 上面传输的文件要放的位置
lisin_bin_path_default="/usr/local/lisin/bin"

# Initial directory
rm -rf $dirCompile  #每次都重新编译,删除build文件
mkdir -p $dirCompile #创建build文件
cd $dirCompile

# Run cmake
cmake $dirCurrent  #编译cmake

# Run make
cmd_res=$? #判断编译的时候是否跟着参数

if [ $cmd_res -eq 0 ];then
    echo "Make \`Makefile\` complete."
    make -j4
else
    echo "[Err:$cmd_res] \`cmake\` faild."
    exit 1
fi

# make install
cmd_res=$?

if [ $cmd_res -eq 0 ];then
    echo "Compilation complete."
    make install #生成的文件安装
else
    echo "[Err:$cmd_res] \`make\` faild."
    exit 2
fi

# Copy executable to NUC980
cmd_res=$?

if [ $cmd_res -eq 0 ];then

    cd $dirCurrent

    # 读取配置文件
    cfg_file="$dirCurrent/buildcfg"

		# 检测配置文件是否存在
    if [ ! -f $cfg_file ]; then
				
				#如果不存在,赋值上面默认的数据
        echo "touch $cfg_file!!!"
        echo "login_user=$login_usr_default" > $cfg_file
        echo "login_pswd=$login_pswd_default" >> $cfg_file
        echo "login_host=$login_ip_default" >> $cfg_file
        echo "exe_bin=$exe_bin_default" >> $cfg_file
        echo "dest_path=$lisin_bin_path_default" >> $cfg_file

        _exe_bin=$exe_bin_default
        _login_user=$login_usr_default
        _login_pswd=$login_pswd_default
        _login_host=$login_ip_default
        _dest_path=$lisin_bin_path_default

    else
				#如果存在读取这些值
        echo "read buildcfg..."

				#找出配置文件中的值
        _login_user=`sed '/^login_user=/!d;s/.*=//;s/\r//g' $cfg_file`
        _login_pswd=`sed '/^login_pswd=/!d;s/.*=//;s/\r//g' $cfg_file`
        _login_host=`sed '/^login_host=/!d;s/.*=//;s/\r//g' $cfg_file`
        _exe_bin=`sed '/^exe_bin=/!d;s/.*=//;s/\r//g' $cfg_file`
        _dest_path=`sed '/^dest_path=/!d;s/.*=//;s/\r//g' $cfg_file`

				#如果某个值为空,使用默认的值,保存默认的值到配置文件
        if [ "$_exe_bin" = "" ]; then
            echo "login_user=$login_usr_default" >> $cfg_file
            _exe_bin=$exe_bin_default
        fi
        

        if [ "$_login_user" = "" ]; then
            echo "login_pswd=$login_pswd_default" >> $cfg_file
            _login_user=$login_usr_default
        fi

        if [ "$_login_pswd" = "" ]; then
            echo "login_host=$login_ip_default" >> $cfg_file
            _login_pswd=$login_pswd_default
        fi

        if [ "$_login_host" = "" ]; then
            echo "exe_bin=$exe_bin_default" >> $cfg_file
            _login_host=$login_ip_default
        fi

        if [ "$_dest_path" = "" ]; then
            echo "dest_path=$lisin_bin_path_default" >> $cfg_file
            _dest_path=$lisin_bin_path_default
        fi

    fi

    #参数输入
    exe_bin="${_exe_bin}"
    login_user="${_login_user}"
    login_pswd="${_login_pswd}"
    login_host="${_login_host}"
    dest_path="${_dest_path}"
  

    #读取外部输入参数
    while getopts ":u:p:i:s:h:" opt
    do
        case $opt in
            u)
            login_user=$OPTARG
            ;;
            p)
            login_pswd=$OPTARG
            ;;
            i)
            login_host=$OPTARG
            ;;
            s)
            exe_bin=$OPTARG
            ;;
            h)
            dest_path=$OPTARG
            ;;
            ?)
            echo "args error,using default args!!!!"
            ;;
        esac
    done

    #更新配置文件
    if [ "$exe_bin" != "$_exe_bin" ]; then
        echo "replace exe_bin!"
        sed -i "/^exe_bin=/c\exe_bin=${exe_bin}" $cfg_file
    fi

    if  [ "$login_user" != "$_login_user" ]; then
        echo "replace login_user!"
        sed -i "/^login_user=/c\login_user=${login_user}" $cfg_file
    fi

    if  [ "$login_pswd" != "$_login_pswd" ]; then
        echo "replace login_pswd!"
        sed -i "/^login_pswd=/c\login_pswd=${login_pswd}" $cfg_file
    fi

    if  [ "$login_host" != "$_login_host" ]; then
        echo "replace login_host!"
        sed -i "/^login_host=/c\login_host=${login_host}" $cfg_file
    fi
       
    if  [ "$dest_path" != "$_dest_path" ]; then
        echo "replace dest_path!"
        sed -i "/^dest_path=/c\dest_path=${dest_path}" $cfg_file
    fi

    #传输的路径一般为主目录下的bin目录,因为cmake会把生成的执行文件通过make install安装到固定的位置bin目录下。
    dirbin=${dirCurrent}/bin/${exe_bin} 
    exe_bin_bkp=${exe_bin}_bkp

    # 判断是否安装 sshpass , 若未安装则安装
    which "sshpass" > /dev/null
    if [ $? == 0 ]; then
        echo "\`sshpass\` Installed. Start copy file..."
    else
        sudo apt-get -y install sshpass 
    fi

#这是sshpass的使用
## 免密码登录
#$ sshpass -p password ssh username@host

# 远程执行命令
#$ sshpass -p password ssh username@host <cmd>

# 通过scp上传文件
#$ sshpass -p password scp local_file root@host:remote_file 

# 通过scp下载文件
#$ sshpass -p password scp root@host:remote_file local_file

    echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
    echo "Copying $dirbin to $login_user@$login_host[$login_pswd]:$dest_path ..."
    echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
    

    # CmdKill="ps | grep "${exe_bin}" | grep -v grep | awk '{print $1}' | xargs kill -9" 

    # sshpass -p $login_pswd ssh -o StrictHostKeyChecking=no $login_user@$login_host "$CmdKill"
		# 执行创建文件夹指令
    sshpass -p $login_pswd ssh -o StrictHostKeyChecking=no $login_user@$login_host "mkdir -p $dest_path"
		# 把之前的可执行文件修改名字
    sshpass -p $login_pswd ssh -o StrictHostKeyChecking=no $login_user@$login_host "mv $dest_path/$exe_bin $dest_path/$exe_bin_bkp"
		# 把之前的可执行文件修改名字,把可执行文件传输过去
    sshpass -p $login_pswd scp -o StrictHostKeyChecking=no "$dirbin" $login_user@$login_host:"$dest_path"

else
    echo "[Err:$cmd_res] \`make\` faild."
    exit 3

fi

3. 工程地址

  • 我在github上已经分享了我的工程,欢迎指出问题也可以提出优化空间,文档中可能讲的不系,但是CMake的语法比较简单,不懂的可以查一下资料,或者看看我上一篇文章,基本都有介绍到。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用cmake构建一个大型项目框架 的相关文章

随机推荐

  • 多目标跟踪-DeepSort分析(一)论文解读及代码架构

    先引入多目标跟踪DeepSort的论文地址及代码链接 xff08 Python版 xff09 xff1a 论文地址 xff1a https arxiv org pdf 1703 07402 pdf 代码链接 xff1a https gith
  • stm32f4xx 加密

    文章参考http www 61ic com Technology embed 201311 50853 html xff0c 一 ID获取 34 设备电子签名 电子签名存储在 Flash 区 可以使用 JTAG SWD 或 CPU 对其进行
  • C++为什么要学习STL和Boost库

    最近一年我电话面试了数十位 C 43 43 应聘者 xff0c 惯用的暖场问题是 工作中使用过 STL 的哪些组件 xff1f 使用过 Boost 的哪些组件 xff1f 得到的答案大多集中在 vector map 和 shared ptr
  • 智能硬件新产品开发逻辑

  • 移动机器人入门介绍

    移动机器人技术应用 xff1a 天上飞的 xff0c 水里游的 xff0c 地上跑的 xff0c 都可以应用移动机器人领域的技术 比如说 xff0c 1 工业机器人 xff0c 搬运机器人 xff08 AGV xff09 xff1b 2 商
  • 占据栅格地图(occupancy grid maps) -- 二值贝叶斯滤波应用

    其实 xff0c 我想讲的关键点是二值状态的最优估计问题 xff0c 而不仅仅是栅格地图 Anyway xff0c that is a good example 机器人的地图表示方式有多种 xff0c 如拓扑地图 特征地图 直接表征法 栅格
  • 粒子滤波实现及推导

    一 应用 example xff1a 机器人全局定位 粒子滤波实现过程解读 二 推导 从贝叶斯到粒子滤波 三 工程化细节探讨 这是草稿 xff0c 待完善
  • 卫星信号的上行下行学习笔记

    gt gt gt gt gt gt 我的博客目录导航 最近被卫星电视信号的发射接收过程弄的头疼 xff0c 于是潜心找了本书 卫星电视接收DIY 细细看看下 1 地面信号上星 以前在家弄个 锅 看看电视 xff0c 电视里主持人录节目 xf
  • 信息与不确定性

    最近学习了吴军的 信息论40讲 xff0c 深有感触 xff0c 有感而发 xff01 本博文想要给你传递 xff08 或者说洗脑 xff09 两个主要观点 xff1a 信息是可以量化的 xff0c 用事件的不确定性表示 xff0c 增加相
  • AlexeyAB/darknet (YOLO)的编译 和 作为动态库进行使用 以及 训练 自定义 数据(检测网络和分类网络)

    ubuntu 编译 https github com AlexeyAB darknet 这个库很贴心了 xff0c 当然 xff0c 主要是 darknet的实现也很硬核 xff0c 自带图像编解码 xff0c 各种造轮子 文档 把 各种用
  • LoRa和NB-IoT有什么区别?LoRa的优势在哪些方面?

    对于LoRa技术 xff0c 行业内人士都不会陌生 xff0c 它也经常会被拿来和NB IoT技术比较 作为低功耗广域网 xff08 LPWAN xff09 的新兴技术 xff0c 两种技术都备受关注 对于LoRa技术 xff0c 行业内人
  • Source Insight 4.0最好看的主题

    推荐一款sourceinsight主题 xff0c 4 0适用 xff0c 配色本人觉得非常舒服 使用方法 xff1a 1 安装Sourceinsight 2 安装字体 xff1a YaHei Consolas Hybrid 1 12 tt
  • Notepad++最好看的主题

    Notepad 43 43 最好看的主题 xff0c 收藏了很久 xff0c 现在拿出来和大家分享 配色如下 xff1a 使用方法 xff1a 1 安装Notepad 43 43 2 将KamiTheme xml放到 Notepad 43
  • DTLS协议中client/server的认证过程和密钥协商过程

    1 DTLS介绍 1 1 DTLS的作用 互联网先驱们最开始在设计互联网协议时主要考虑的是可用性 xff0c 安全性是没有考虑在其中的 xff0c 所以传输层的TCP UDP协议本身都不具备安全性 SSL TLS协议是基于TCP socke
  • Ubuntu18.04 实现串口通信

    最近由于项目需要 xff0c 研究了关于在ubuntu下串口通信的功能实现 期间遇到一些问题 xff0c 跟大家分享下 1 代码 comm service h ifndef comm service h define comm servic
  • [STM32] 串口数据帧处理(第一弹)

    文章目录 1 串口使用的常用场景2 字节帧处理总结 1 串口使用的常用场景 使用串口的主要目的是实现数据的交互 xff0c 数据的交互的方法脱身于常用的场景 这里描述一个比较典型的场景 xff1a MCU作为主控制器通过串口和外部的设备或者
  • 串口编程—(1)串口基本知识

    串口是用来干什么的 xff1f 串行接口 简称串口 xff0c 也称 串行通信 接口或 串行通讯接口 xff08 通常指 COM接口 xff09 xff0c 是采用 串行通信 方式的扩展接口 串行接口 Serial Interface 是指
  • C语言操作redis数据库

    文章目录 1 开发环境2 C语言redis库 hiredi安装配置2 1 下载并且解压hiredis2 2 hiredis安装 3 hiredis简单测试4 运行出错解决办法5 验证5 1 运行程序5 2 redis客户端验证 1 开发环境
  • 5.0 NuttX File System

    转载请注明出处 xff1a 5 0 NuttX File System Alvin Peng的博客 CSDN博客 文章均出自个人理解 前言 前一段时间折腾了几个驱动 xff08 PWM Serial I2C xff09 xff0c 这次来折
  • 使用cmake构建一个大型项目框架

    文章目录 使用CMake构建一个大型项目工程1 大型工程目录结构介绍1 1 工程目录结构介绍1 2 工程目录说明 xff08 我是这样设计的 xff0c 你们也可以参考类似这样设计 xff09 1 3 最外层CMakeLists txt说明