CMake官方文档:https://cmake.org/cmake/help/v3.17/guide/tutorial/index.html#adding-system-introspection-step-5
CMake的优点
高效 、强大 、开发团队的首选
今天给大家分享的是工程管理工具cmake,没有先介绍makefile工程管理工具(坦白来说,这两者都差不多,cmake最终还是会生成Makefile的,只是说cmake语法稍微比较简单一些,没有Makefile那么复杂!)。
就自己个人经历,现在一般公司去写Makefile和cmake的比较少(当然去招聘网站上,有的时候还是可以看到有这个技能要求会写的,所以说能够自己写出来是最为完美的!),一般都是直接使用厂家的Makefile或者Cmake;但作为学习,还是要认真学习里面的原理,比如出现了错误,你要能够定位到错误并把它解决掉,因为可能错误就出现在配置好的Makefile或者Cmake里面,所以你要看的懂里面代码的意思(也就是说,你知道这个工具是这样用,但是也要明白它的原理机制,做到之知其然,知其所以个然来!),这样才能把问题解决掉。
而且就个人见解,在传统的linux工程管理用Makefile的比较多(Uboot里面也是大量使用Makefile来进行管理工程);在新型领域,比如物联网开发(特别是一些开源项目等),用Cmake的比较多(当然也有可能是例外哈!);好了,废话就不多说了,开始来学习了:
一、Cmake学习使用:
1、安装Cmake管理工具:
一般实际嵌入式linux开发,几乎都是用Ubuntu来开发的,因为那啥,安装啥应用程序的非常方便,只需一个命令“apt install + 应用程序名称” 大部分都直接搞定,不用再去配置(特殊的,就例外,还要一些其他相关配置!),安装Cmake就是一条命令直接搞定:
root@txp-virtual-machine:/home/txp# apt install cmake
Reading package lists… Done
Building dependency tree
2、先从一个简单示例,来得出一般书写步骤规律:
这里先写一个简单的代码工程main.c,然后再使用我们的cmake来管理代码工程:
#include <stdio.h>
int main(void)
{
printf("嵌入式\n");
return 0;
}
然后开始写cmake工程管理文件,我在当前目录建立一个CMakeLists.txt文件,然后再往里面开始工程管理代码
root@txp-virtual-machine:/home/txp/test# pwd
/home/txp/test
root@txp-virtual-machine:/home/txp/test# touch CMakeLists.txt
root@txp-virtual-machine:/home/txp/test# ls
CMakeLists.txt main.c
CMakeLists.txt文件里面的内容如下:
cmake_minimum_required (VERSION 2.8)
project (main)
add_executable(main main.c)
解释一下这三条语句分别代表什么意思:
- 1、表示cmake最低执行版本是2.8才有效来管理我们的工程项目。
- 2、表示整个工程名为main
- 3、表示最终要生成的elf文件的名字叫main,使用的源文件是main.c
现在我们来实现cmake的功能,在当前目录下,使用命令"cmake ."(.表示当前目录,而…表示上一级目录),生成makefile等相关文件;然后再执行一下make命令进行编译工程,就能生成可执行文件main了,同时也会生成makefile文件,最后就可以执行可执行main文件,就能得到我们所要的结果:
root@txp-virtual-machine:/home/txp/test# cmake .
-- The C compiler identification is GNU 4.8.4
-- The CXX compiler identification is GNU 4.8.4
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/txp/test
root@txp-virtual-machine:/home/txp/test# ls
CMakeCache.txt CMakeFiles cmake_install.cmake CMakeLists.txt main main.c Makefile
root@txp-virtual-machine:/home/txp/test# make
Scanning dependencies of target main
[100%] Building C object CMakeFiles/main.dir/main.c.o
Linking C executable main
[100%] Built target main
root@txp-virtual-machine:/home/txp/test# ./main
嵌入式
如果你再好奇一下的话,可以打开看看Makefile里面的内容是啥,很容易想到是用makefile的方式来实现对工程main的管理,这里我就不画蛇添足把源代码贴出来了。
cmake_install.cmake 是一些相关配置选项:
# Install script for directory: /home/txp/test
# Set the install prefix
IF(NOT DEFINED CMAKE_INSTALL_PREFIX)
SET(CMAKE_INSTALL_PREFIX "/usr/local")
ENDIF(NOT DEFINED CMAKE_INSTALL_PREFIX)
STRING(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}")
# Set the install configuration name.
IF(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME)
IF(BUILD_TYPE)
STRING(REGEX REPLACE "^[^A-Za-z0-9_]+" ""
CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}")
ELSE(BUILD_TYPE)
SET(CMAKE_INSTALL_CONFIG_NAME "")
ENDIF(BUILD_TYPE)
MESSAGE(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"")
ENDIF(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME)
# Set the component getting installed.
IF(NOT CMAKE_INSTALL_COMPONENT)
IF(COMPONENT)
MESSAGE(STATUS "Install component: \"${COMPONENT}\"")
SET(CMAKE_INSTALL_COMPONENT "${COMPONENT}")
ELSE(COMPONENT)
SET(CMAKE_INSTALL_COMPONENT)
ENDIF(COMPONENT)
ENDIF(NOT CMAKE_INSTALL_COMPONENT)
# Install shared libraries without execute permission?
IF(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE)
SET(CMAKE_INSTALL_SO_NO_EXE "1")
ENDIF(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE)
IF(CMAKE_INSTALL_COMPONENT)
SET(CMAKE_INSTALL_MANIFEST "install_manifest_${CMAKE_INSTALL_COMPONENT}.txt")
ELSE(CMAKE_INSTALL_COMPONENT)
SET(CMAKE_INSTALL_MANIFEST "install_manifest.txt")
ENDIF(CMAKE_INSTALL_COMPONENT)
FILE(WRITE "/home/txp/test/${CMAKE_INSTALL_MANIFEST}" "")
FOREACH(file ${CMAKE_INSTALL_MANIFEST_FILES})
FILE(APPEND "/home/txp/test/${CMAKE_INSTALL_MANIFEST}" "${file}\n")
ENDFOREACH(file)
~
而CMakeFiles是一个文件夹,里面文件内容如下:
root@txp-virtual-machine:/home/txp/test/CMakeFiles# ls
2.8.12.2 cmake.check_cache CMakeDirectoryInformation.cmake CMakeOutput.log
CMakeTmp main.dir Makefile2 Makefile.cmake progress.marks TargetDirectories.txt
小结:从上面简单的示例我们可以看出,Cmake所有的语句都是写在一个名为"CMakeLists.txt"的文本文件里面,所以完成一个工程用cmake管理的完整步骤如下:
(1)、先建立CMakeLists.txt(这跟我们用makefile来管理工程一样,都有一个特殊的文件来专门书写工程管理代码的,cmake也不例外,使用CMakeLists.txt文本文件来专门书写代码管理工程项目)。
(2)、然后进行往CMakeLists.txt写配置,具体简单配置参考上面的形式写,复杂的配置,我们会在下面进行演示讲解的。
(3)、接着使用命令"cmake .",这里必须是CMakeLists.txt同级目录下执行这个命令哈,生成makefile等文件
(4)、最后使用make命令进行工程编译,进而生成可执行文件。
3、复杂案例:同一个目录有多个源文件
这里我们在当前目录再添加两个文件:test1.c和test1.h。
test1.c文件内容如下:
#include <stdio.h>
#include "test1.h"
void func(int a)
{
printf("a=%d\n",a);
}
test1.h里面的内容如下:
#ifndef _TEST1_H
#define _TEST1_H
void func(int a);
#endif /* _TEST1_H */
然后在main。c里面进行调用func()这个函数:
#include <stdio.h>
#include "test1.h"
int main(void)
{
func(6);
printf("嵌入式\n");
return 0;
}
然后这个时候我们的CMakeLists.txt里面书写形式肯定要改了:
cmake_minimum_required (VERSION 2.8)
project (main)
add_executable(main main.c test1.c)
最终结果如下:
root@txp-virtual-machine:/home/txp/test# cmake .
-- Configuring done
-- Generating done
-- Build files have been written to: /home/txp/test
root@txp-virtual-machine:/home/txp/test# make
Scanning dependencies of target main
[ 50%] Building C object CMakeFiles/main.dir/main.c.o
[100%] Building C object CMakeFiles/main.dir/test1.c.o
Linking C executable main
[100%] Built target main
root@txp-virtual-machine:/home/txp/test# ls
1 CMakeCache.txt CMakeFiles cmake_install.cmake CMakeLists.txt main main.c Makefile test1.c test1.h
root@txp-virtual-machine:/home/txp/test# ./main
a=6
嵌入式
小结:上面的CMakeLists.txt的写法,你会注意到add_executable(main main.c test1.c)多了一个源文件(你可以把源码看作成加工的原材料,在makefile里面也是这样来理解目标文件的生成!)
所以如果你还要往当前目录下添加源文件的话,在书写CMakeLists.txt文本文件时,直接在第三个语句里面添加源文件;
不过这种方式有缺陷,比如说,如果当前文件下有几百个源文件,这个时候你不可能一个个去手写敲吧,不然这样就又回到了原始社会,就不能体现cmake的优越性出来了!