cmake学习笔记备忘

前言

学习目的

1) SLAM中需要使用cmake管理代码

2)Linux开发中管理C程序项目代码必须要使用的工具

学习路线

1)Cjacker的《Cmake 实践》

2)Ubuntu16.04下运行cmake

3)用到哪部分内容就学哪部分,后期不断补全新内容

在这里插入图片描述

cmake学习

一 简介

Cmake是Linux下的一款可以用来编译c程序和管理项目的工具。

英文分类是:build generator

特点:

在这里插入图片描述

二 内容

2.1 简单入门

2.1.1 下载cmake

Linux的终端中输入:

cmake

它就会提示你怎么安装,很简单。

2.1.2 hello world

A 准备工作

  • 建立一个工作目录
mkdir backup/cmake
cd backup/cmake
mkdir t1
cd t1
  • 然后在t1目录下建立main.cCMakeLists.txt(注意文件名大小写)

    • main.c内容
    // main.c
    #include <stdio.h>
    int main()
    {
        printf("Hello World from t1 Main!\n");
        return 0;
    }
    
    • CMakeLists.txt内容
    PROJECT (HELLO)
    SET (SRC_LIST main.c)
    MESSAGE (STATUS "This is BINARY dir" ${HELLO_BINARY_DIR})
    MESSAGE (STATUS "This is SOURCE dir" ${HELLO_SOURCE_DIR})
    ADD_EXECUTABLE(hello ${SRC_LIST})
    

B 开始构建

  • 所有的文件创建完成后,t1目录中应该存在main.c和CMakeLists.txt两个文件,接下来我们构建这个工程,在这个目录运行:
cmake .

输出大概是这个样子

在这里插入图片描述

t1目录会多一些文件:

CMakeFiles, CMakeCache.txt, cmake_install.cmake等文件,并且生成了Makefile

这些文件大部分没用,最关键的是,它自动生成了Makefile

  • 再进行工程的实际构建,在这个目录输入make命令:
make

输出大概是这个样子

在这里插入图片描述

  • 这时候,我们需要的目标文件hello已经构建完成,位于当前目录,尝试运行一下:
./hello

得到输出:

Hello World from Main

2.1.3 简单的解释

解析CMakeLists.txt,该文件名大小写相关,如果工程存在多个目录,需要确保每个要管理的目录都存在一个CMakeLists.txt。(主工程文件夹通过CMakeLists.txt来连接各个子文件夹

PROJECT (HELLO)
  • 作用:你可以用这个指令定义工程名称,并可指定工程支持的语言,支持的语言列表是可以忽略的,默认情况表示支持所有语言 。
  • 注:这个指令隐式的定义了两个 cmake 变量:
    <projectname>_BINARY_DIR以及<projectname>_SOURCE_DIR,这里就是
    HELLO_BINARY_DIRHELLO_SOURCE_DIR(所以 CMakeLists.txt中两个MESSAGE
    指令可以直接使用了这两个变量),因为采用的是内部编译,两个变量目前指的都是工程所
    在路径/backup/cmake/t1,后面我们会讲到外部编译,两者所指代的内容会有所不同。
  • 同时 cmake 系统也帮助我们预定义了PROJECT_BINARY_DIRPROJECT_SOURCE_DIR
    变量,他们的值分别跟 HELLO_BINARY_DIRHELLO_SOURCE_DIR 一致。
  • 为了统一起见,建议以后直接使用 PROJECT_BINARY_DIRPROJECT_SOURCE_DIR,即
    使修改了工程名称,也不会影响这两个变量。如果使用了
    <projectname>_SOURCE_DIR,修改工程名称后,需要同时修改这些变量。
SET(SRC_LIST main.c)
  • 作用:定义变量
  • 指令:SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
  • 特点:比如我们用到的是 SET(SRC_LIST main.c),如果有多个源文件,也可以定义成:
    SET(SRC_LIST main.c t1.c t2.c)
MESSAGE(STATUS "This is BINARY dir" ${HELLO_BINARY_DIR})
MESSAGE(STATUS "This is SOURCE dir" ${HELLO_SOURCE_DIR})
  • 作用:用于向终端输出用户定义的信息,包含三种类型:SEND_ERRORSATUSFATAL_ERROR
  • 指令:MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR] "message to display" ...)
ADD_EXECUTABLE(hello ${SRC_LIST})
  • 作用:定义了这个工程会生成一个文件名为hello的可执行文件,相关的源文件是 SRC_LIST 中
    定义的源文件列表, 本例中你也可以直接写成 ADD_EXECUTABLE(hello main.c)

2.1.4 基本语法规则

1,变量使用${}方式取值,但是在IF控制语句中是直接使用变量名
2,指令(参数 1 参数 2…)
参数使用括弧括起,参数之间使用空格或分号分开。
以上面的 ADD_EXECUTABLE 指令为例,如果存在另外一个 func.c 源文件,就要写成:
ADD_EXECUTABLE(hello main.c func.c)或者
ADD_EXECUTABLE(hello main.c;func.c)
3,指令是大小写无关的,参数和变量是大小写相关的。但,推荐你全部使用大写指令。

4, 这里需要特别解释的是作为工程名的 HELLO 和生成的可执行文件 hello 是没有任何关系的。
hello 定义了可执行文件的文件名,你完全可以写成:
ADD_EXECUTABLE(t1 main.c)
编译后会生成一个 t1 可执行文件。

5,清理工程:
跟经典的 autotools 系列工具一样,运行:

make clean

即可对构建结果进行清理。

2.2 更好一点的Hello World

2.2.1 更好表现在:

更好一点的Hello World,就是要让:

  • 编译结果都放在build文件夹内:cmake ..
  • 源文件放在src文件夹下
  • 将构建后的目标文件放入build目录的 bin 子目录 : add_subdirectory(src bin)
  • 利用install指令安装文件于特定文件夹下

2.2.2 指令学习

A 指令add_subdirectory

该指令是构建文件联系的关键指令,在工程文件的根目录的CMakeLists.txt加入该指令,指向存放源文件的文件夹。

20200422223202578

B 指令install

INSTALL 指令用于定义安装规则,安装的内容可以包括目标二进制、动态库、静态库以及文件、目录、脚本等。

学习时,需要安装什么样的文件,直接去查看指令例子,跟随安装即可

B.1 目标文件的安装
指令介绍
install(  )

在这里插入图片描述

20200422223327271

举个栗子

在这里插入图片描述

B.2 普通文件的安装

在这里插入图片描述

B.3 非目标文件的可执行程序安装(比如脚本之类)

在这里插入图片描述

C 注意

想要完成安装,在执行完cmakemake之后,还需执行make install

cmake ..
make 
make install

2.2.3 问题

A cmake的整体流程

需要看哔哩哔哩的视频

  • 工程中的各个文件夹之间,通过CMakeLists.txt文件联系起来
  • cmake想要执行一个main.c的程序,应该包括
    • 指令add_executable用于生成可执行二进制文件
    • 指令add_subdirectory包含下一级文件夹,放在工程根目录的CMakeLists.txt中。有了这个指令,你就可以把源文件main.c放到其他文件夹里去啦。
    • 指令install安装的意思,我目前理解,就是把想要的文件,复制到你想要存放的目标文件夹中去。
  • cmake编译和链接指令
    • cmake .
    • make
  • 库的创建
    • add_library
    • 其他辅助指令:set_target_properties get_target_property
  • 库的使用
    • include_directories加入头文件搜索路径,这样头文件就不用放在.c文件一起了
    • link_directories加入库文件搜索路径
    • target_link_libraries将库文件链接到目标文件中

2.3 静态库和动态库

2.3.1 简述静态库和动态库

库文件是干啥的,库文件就是,一个项目程序,不止一个main.c程序,还有函数文件程序,比如hello.c文件和对应的hello.h文件。

库要利用hello.c文件制作静态库文件.a或动态库文件.so。电

动态库和静态库的区别在于:静态库在编译的时候会直接整合到目标程序中,生成的可执行文件不需要依赖额外的库来运行,它一个人就能行。动态函数库在编译的时候,在程序里只有一个“指向”的位置而已,也就是说当可执行文件需要使用到函数库的机制时,程序才会去读取函数库来使用;也就是说可执行文件无法单独运行。

更详细内容见问题B

2.3.2 指令学习

lib目录下建立CMakeLists.txt,内容如下

set(LIBHELLO_SRC hello.c)
add_library(hello SHARED ${LIBHELLO_SRC}) #建立动态库
add_library(hello_static STATIC ${LIBHELLO_SRC}) #建立静态库
set_target_properties(hello_static PROPERTIES OUTPUT_NAME "hello") #使用libhello.a的名字而不是libhello_static.a
# 与set_target_properties对应的指令
get_target_property(OUTPUT_VALUE hello_static OUTPUT_NAME)
message(STATUS "This is the hello_static
OUTPUT_NAME:" ${OUTPUT_VALUE})
# cmake在构建一个新的target时,会尝试清理掉其他使用这个名字的库,因而在建立libhello.a时,就会清理libhello.so,为不清楚,应
set_target_properties(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)
set_target_properties(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
# 实现动态库版本号
set_target_properties(hello PROPERTIES VERSION 1.2 SOVERSION 1)
# 安装共享库和文件
install(TARGETS hello hello_static
	LIBRARY DESTINATION lib
	ARCHIVE DESTINATION lib) #注意静态库要使用ARCHIVE关键字
install(FILES hello.h DESTINATION include/hello)

bash中通过:

cmake -DCMAKE_INSTALL_PREFIX=/usr ..
make 
make install

A 指令add_library

  • 指令

    add_library(libname [SHARED|STATIC|MODULE]
    			[EXCLUDE_FROM_ALL]
    				source1 source2 .. sourceN)
    
  • 库类型

    • SHARED, 动态库
    • STATIC, 静态库
    • MODULE, 在使用dyld的系统有效,如果不支持dyld,则被当做SHARED对待
    • 动态库和静态库的区别:静态库是编译的时候就加入程序,动态库是调用的时候加入程序

在这里插入图片描述

B 指令set_target_properties

  • 指令

    set_target_properties(target1 target2 ...
    					PROPERTIES prop1 value1
    						prop2 value2 ...)
    

在这里插入图片描述

C 指令get_target_property

  • 指令

    get_target_property(VAR target property)
    
  • 举例

    get_target_property(OUTPUT_VALUE hello_static OUTPUT_NAME)
    message(STATUS "This is the hello_static
    OUTPUT_NAME:"${OUTPUT_VALUE})
    

在这里插入图片描述

2.3.3 问题

A 安装共享库和头文件问题

问题描述
# lib/CMakeLists.txt 中内容
install(TARGETS hello hello_static
	LIBRARY DESTINATION lib
	ARCHIVE DESTINATION lib)
install(FILES hello.h DESTINATION include/hello)

# bash中输入
cmake -DCMAKE_INSTALL_PREFIX=/usr ..
make 
make install

在运行到make install时出错。

ayzp@ayzp-Lenovo-G40-45:~/backup/cmake/t3/build$ make install
[ 50%] Built target hello
[100%] Built target hello_static
Install the project...
-- Install configuration: ""
-- Up-to-date: /usr/lib/libhello.so.1.2
CMake Error at lib/cmake_install.cmake:48 (file):
  file INSTALL cannot set permissions on "/usr/lib/libhello.so.1.2"
Call Stack (most recent call first):
  cmake_install.cmake:37 (include)
解决

有些安装需要root密码,因而使用指令:

sudo make install

B 静态库和动态库的区别

引:最近做了算法产品化相关的一些工作,其中涉及到算法库封装的相关工作,封装为动态库。总结动态库和静态库区别和对应使用原则。

区别:静态库和动态库最本质的区别就是:该库是否被编译进目标(程序)内部。

分别介绍:

静态(函数)库
一般扩展名为(.a或.lib),这类的函数库通常扩展名为libxxx.a或xxx.lib 。
这类库在编译的时候会直接整合到目标程序中,所以利用静态函数库编译成的文件会比较大,这类函数库最大的优点就是编译成功的可执行文件可以独立运行,而不再需要向外部要求读取函数库的内容;但是从升级难易度来看明显没有优势,如果函数库更新,需要重新编译。
动态函数库
动态函数库的扩展名一般为(.so或.dll),这类函数库通常名为libxxx.so或xxx.dll 。
与静态函数库被整个捕捉到程序中不同,动态函数库在编译的时候,在程序里只有一个“指向”的位置而已,也就是说当可执行文件需要使用到函数库的机制时,程序才会去读取函数库来使用;也就是说可执行文件无法单独运行。这样从产品功能升级角度方便升级,只要替换对应动态库即可,不必重新编译整个可执行文件。
总结:综上,不能看出:

从产品化的角度,发布的算法库或功能库尽量使动态库,这样方便更新和升级,不必重新编译整个可执行文件,只需新版本动态库替换掉旧动态库即可。
从函数库集成的角度,若要将发布的所有子库(不止一个)集成为一个动态库向外提供接口,那么就需要将所有子库编译为静态库,这样所有子库就可以全部编译进目标动态库中,由最终的一个集成库向外提供功能。
————————————————
版权声明:本文为CSDN博主「wonengguwozai」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wonengguwozai/article/details/93195827

2.4 如何使用外部共享库和头文件

2.4.1 简述

通过add_library构建好动态库、静态库以后。怎么使用这些库呢?

include_directories() #加入非标准的头文件搜索路径
link_directories() #加入非标准的库文件搜索路径
target_link_libraries() #库或可执行二进制加入库链接

2.4.2 指令学习

A 引入头文件搜索路径

如何通过include_directories指令加入非标准的头文件搜索路径

  • 作用:用于搜索系统头文件.h

  • 指令说明

    include_directories( dir1 dir2 ... )
    

在这里插入图片描述

  • 举例

    现在我们在src/CMakeLists.txt中添加一个头文件搜索路径、方式很简单,加入:

    include_directories(/usr/include/hello)
    

B 为target添加共享库

将目标文件链接到libhello。(也就是将hello.c构建的动态库或静态库链接到hello.h)

  • 链接的是.c文件,目的是将.c文件,链接到你main函数要执行的文件中。
B.1 如何通过link_directories指令加入非标准的库文件搜索路径
link_directories(dir1 dir2 ...)

在这里插入图片描述

B.2 如何通过target_link_libraries为库或可执行二进制加入库连接
target_link_libraries(target library1 
					<debug | optimized> library2
					...)

在这里插入图片描述

  • 作用:将目标文件链接到libhello

  • 举例:

    例1:链接到动态库

    target_link_libraries(main hello)
    #或者
    target_link_libraries(main libhello.so)
    # 这里main是文件夹src/下的文件main.c,主函数main(){}就在main.c中。
    # libhello.so是上一讲创建的动态库,里面有hello.c这个函数。
    

在这里插入图片描述

在这里插入图片描述

​ 例2:链接到静态库target_link_libraries(main libhello.a)

在这里插入图片描述

参考文献

[1] 《Cmake Practice》 – Cjacker

[2] wonengguwozai. 通俗理解动态库与静态库区别. CSDN. 2019. https://blog.csdn.net/wonengguwozai/article/details/93195827

猜你喜欢

转载自blog.csdn.net/ALexander_Monster/article/details/106457975