在上文中,我们已经学会用CMake去构建一个项目。接来下我对这个工程进一步的完善,使其能应用在更加的项目开发当中。构建一个项目工程,首先要知道自己的要用这个工程来做什么。我是一名嵌入式软件开发工程师。日常的开发有如下需求:
要优化好上面的脚本也是很大的工作量,接下来我将给大家介绍一下我自己常用的模型是怎么样的。
为了简介,我删除了很多我自己定义的文件夹,留下的都是自己定义的
.
├── 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.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
存放修改的详细内容
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()
.
├── CMakeLists.txt
├── main.cpp
└── System_Frame
├── print_cmake.cpp
└── print_cmake.h
# 查找当前目录下的所有源文件
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. 定义可执行文件的安装位置
unit_test:这里相当于一个独立的单元,完全由自己编写,不影响主程序,在最外层CMakeLists.txt开关调为ON就编译的这里的程序
.
├── board_test
│ ├── gpio
│ │ └── CMakeLists.txt
│ └── uart
│ └── CMakeLists.txt
└── net_test
# 1. add_subdirectory()使用的就是增加子目录的方式,然后每个子目录干自己的事
# 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
# 通过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