当前位置: 首页 > news >正文

CMake入门

1. CMake的介绍:

当多人开发同一个项目时,最终要输出一个可执行文件或者共享库(dll、so等),就可以使用cmake了
所有操作都是通过 CMakeLists.txt(严格区分大小写) 来完成的 ----> 简单

2. CMake的helloworld编译:

步骤一,写一个helloworld:

#include <iostream>

int main() {
	std::cout << "hello world" << std::endl;
	return 0;
}

步骤二,写CMakeLists.txt

PROJECT (HELLO)
  
SET(SRC_LIST main.cpp)

MESSAGE(STATUS "This is BINARY dir " ${HELLO_BINARY_DIR})

MESSAGE(STATUS "This is SOURCE dir " ${HELLO_SOURCE_DIR})

ADD_EXECUTABLE(hello ${SRC_LIST})

步骤三,使用cmake,生成makefile文件

cmake .

输出:

cmake .
-- The C compiler identification is AppleClang 12.0.0.12000032
-- The CXX compiler identification is AppleClang 12.0.0.12000032
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /Library/Developer/CommandLineTools/usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /Library/Developer/CommandLineTools/usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- This is BINARY dir /Users/xuesong/SONGSONG
-- This is SOURCE dir /Users/xuesong/SONGSONG
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/xuesong/SONGSONG

步骤四,使用make命令编译:

make

输出:
[ 50%] Building CXX object CMakeFiles/hello.dir/main.cpp.o
[100%] Linking CXX executable hello
[100%] Built target hello

步骤五,最终生成了hello的可执行程序

3. CMakeLists语法介绍:

PROJECT 关键字:

可以用来指定工程的名字和支持的语言,默认支持所有语言;
PROJECT (HELLO) 指定了工程的名字,并且支持所有语言(建议)
PROJECT (HELLO CXX) 指定了工程的名字,并且支持语言是C++
PROCECT (HELLO C CXX) 指定了工程的名字,并且支持语言是C和C++

该指令隐式定义了两个CMAKE的变量:
<projectname>_BINARY_DIR 本例中是 HELLO_BINARY_DIR
<projectname>_SOURCE_DIR 本例中是 HELLO_SOURCE_DIR

MESSAGE关键字就可以直接使用这两个变量,当前都指向当前的工作目录,后面会涉及到外部编译。

此时有一个问题,如果改了工程名,这两个变量名也会改变,所以又定义了两个预定义变量:PROJECT_BINARY_DIRPROJECT_SOURCE_DIR,这两个变量和 HELLO_BINARY_DIR、HELLO_SOURCE_DIR是一致的。

SET关键字:

用来显式的指定变量,例如

SET(SRC_LIST main.cpp)

即使用来指定源文件列表

MESSAGE关键字:

MESSAGE关键字类似于C语言中的printf函数,用于将变量的值显示到终端,这样就可以查看cmake构建工程时CMakeLists.txt中某些变量的值是否正确。但是,MESSAGE要比printf函数的功能强大,它可以 终止 编译系统的构建,而且这通常是我们想要的效果。

语法格式:

MESSAGE([<mode>] "message text" ...)

mode的值包括 FATAL_ERRORWARNINGAUTHOR_WARNINGSTATUSVERBOSE

FATAL_ERROR:产生CMake Error,会停止编译系统的构建过程;
STATUS:最常用的命令,常用于查看变量值,类似于编程语言中的DEBUG级别信息。

“message text”为显示在终端的内容。

ADD_EXECUTABLE关键字:

生成可执行文件,例如:

ADD_EXECUTABLE(hello ${SRC_LIST})

生成的可执行文件名是“hello”,源文件读取变量SRC_LIST中的内容。

语法的基本规则:

  1. 使用 ${} 获取某个变量的值,例如 ${SRC_LIST}获取SRC_LIST变量的值;
  2. 指令中的参数使用()小括号括起来,参数之间使用空格或分号分隔开;
  3. 指令是大小写无关的,参数和变量 是大小写相关的,CMake中推荐指令全部大写。

4. CMake内部构建和外部构建:

内部构建就是在项目内部,有CMakeLists.txt的地方,直接 cmake .,比如上面的方式就是内部构建,它的缺点是会在项目目录下生成许多临时文件。

外部构建就是不直接在项目下面运行cmake,而是自己建立一个接收cmake之后产生的临时文件的文件夹(如 build/),然后在该文件下面调用 cmake <CMakeList_path> 来构建。

例如:

mkdir build

cd build

cmake ..

5. 让HelloWorld更像一个工程:

  • 为工程添加一个子目录src,用来放置工程源代码
  • 添加一个子目录doc,用来放置这个工程的文档hello.txt
  • 在工程目录添加文本文件COPYRIGHT,README
  • 在工程目录添加一个 runhello.sh 脚本,用来调用hello二进制
  • 将构建后的目标文件放入构建目录的bin子目录
  • 将doc目录的内容以及COPYRIGHT/README安装到/usr/share/doc/cmake

在这里插入图片描述

然后在 build/ 目录下执行 cmake ..

ADD_SUBDIRECTORY命令:

命令格式:

ADD_SUBDIRECTORY([source_dir] [binary_dir] [EXCLUDE_FROM_ALL])

[source_dir]: 源文件路径;
[binary_dir]: 中间二进制与目标二进制存放路径;
[EXCLUDE_FROM_ALL]:将这个目录从编译过程中排除。

在项目中CMake配置文件一般为 树形结构,项目总的CMake配置文件调用各模块的CMake配置文件;

6. 使用CMake来进行安装:

make:编译程序、库、文档等
make install:安装已经编译好的程序、复制文件树中的文件到指定的位置
make unistall:卸载已经安装的程序
make clean:删除由make命令产生的文件
make distclean:删除由./configure产生的文件
make check:测试刚刚编译的软件(某些程序可能不支持)
make installcheck:检查安装的库和程序(某些程序可能不支持)

cmake INSTALL 命令:INSTALL用于指定在安装时运行的规则,
通俗的说,就是在运行cmake生成makefile之后,make intall 时都需要安装哪些内容。
它可以用来安装很多内容,可以包括 目标二进制、动态库、静态库、文件、目录、脚本等。

INSTALL(TARGETS <target> ... [...])					// TARGETS: 库(.a, .so)
INSTALL({FILES | PROGRAMS} <file> ... [...]) 		// FILES: 文件 PROGRAMS: 二进制(可执行)文件
INSTALL(DIRECTORY <dir> ... [...]) 					// DIRECTORY: 目录
INSTALL(SCRIPT <file> [...]) 						// SCRIPT: 脚本
INSTALL(CODE <code> [...]) 							//
INSTALL(EXPORT <export-name> [...]) 				//

举例:

在 项目的顶层目录下先手动创建COPYRIGHT、README、runhello.sh文件
在这里插入图片描述
顶层的CMakeLists.txt中加入INSTALL命令:
在这里插入图片描述
然后在 build/ 目录下 cmake .., make, make install
在这里插入图片描述
在share目录下就拷贝了上述几个文件:
在这里插入图片描述

7. CMake构建动态库和静态库:

顶层目录:
在这里插入图片描述
顶层CMakeLists.txt的内容:
在这里插入图片描述
lib/ 目录下的内容:
在这里插入图片描述
lib目录下的CMakeLists.txt的内容:
在这里插入图片描述
STATIC 表示生成静态库,SHARED 表示生成动态库,生成的库文件格式是 libXXX.alibXXX.so

在 build/ 目录下执行 cmake ..make,就可以在 build/lib/ 目录下查看到生成的库文件:
在这里插入图片描述

ADD_LIBRARY 命令:

命令格式:

ADD_LIBRARY(<name> [ STATIC | SHARED | MODULE ]
							[EXCLUDE_FROM_ALL]
							[source1] [source2][...])

添加名为 name 的库,库的文件件可指定,也可用 target_sources() 后续指定。
库的类型是 STATIC(静态库)/SHARED(动态库)/MODULE(模块库) 之一。

如何同时构建动态库和静态库:

其他的不需要改动,lib/ 目录下的CMakeLists.txt 文件:
在这里插入图片描述
将动态库和静态库文件编译的名字都设置为“hello”即可。
编译后 在 build/lib/ 目录下就同时生成了 libhello.a 和 libhello.so:
在这里插入图片描述

SET_TARGET_PROPERTIES 命令:

命令格式:

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

这个命令是设置目标的属性,设置之后可以使用 GET_PROPERTY()或者GET_TARGET_PROPERTY()命令获取目标的属性值。

SET_TARGET_PROPERTIES 命令除了可以实现同时生成动态库和静态库,还可以用来 设置动态库的版本号:

SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)

VERSION指代动态库版本,SOVERSION指代API版本。

8. 共享库与头文件的安装和使用:

使用CMake安装库文件:

在 lib/ 目录下的CMakeLists.txt中 加入INSTALL命令:
在这里插入图片描述

执行 cmake .., make, make install,安装头文件和共享库:

在这里插入图片描述

INSTALL命令在安装库文件时:

INSTALL(TARGETS MyLib
				EXPORT MyLibTargets
				LIBRARY DESTINATION lib 	# 动态库安装路径
				ARCHIVE DESTINATION lib		# 静态库安装路径
				RUNTINE DESTINATION bin		# 可执行文件安装路径
				PUBLIC_HEADER DESTINATION include	# 头文件安装路径
				)

PS:为什么Linux安装程序都要放到 “/usr/local” 目录下:

Linux的软件安装目录也是有讲究的,理解这一点,在对系统管理是有益的。

/usr: 系统级的目录,可以理解为C:/Windows/,/usr/lib 可以理解为 C:/Windows/System32;

/usr/local: 用户级的程序目录,可以理解为 C:/Program Files/,用户自己编译的软件默认会安装到这个目录下

/opt:用户级的程序目录,可以理解为 D:/Software,opt有可选的意思,这里可以用于放置第三方大型软件(或游戏等),当你不需要时,直接 rm -rf 删除即可。在硬盘容量不够时,也可将 /opt 单独挂载到其他磁盘上使用。

源码放哪里:
/usr/src:系统级的源码目录
/usr/local/src: 用户级的源码目录

在这里插入图片描述

使用CMake链接库文件:

在另外的目录下编写一个main.cpp,其中包含上面的hello.h头文件:

// main.cpp
#include <hello.h>
int main() {
	func();
	return 0;
}

此时运行make编译时会报错,找不到hello.h头文件,这是因为上面安装hello.h时指定的安装目录是 “include/hello”:
在这里插入图片描述

直接的解决方法有两种:

  1. hello.h 在cmake install安装时直接安装到 /usr/local/include 目录下,取消 hello/这一级目录;
  2. main.cpp 源文件中使用 #include <hello/hello.h> 的方式指名包含的路径。

然而,在项目中,更普遍的做法是使用 cmake中的 INCLUDE_DIRECTORIES 命令解决这个问题:

在这里插入图片描述

INCLUDE_DIRECTORIES 命令:

命令格式:

INCLUDE_DIRECTORIES([AFTER | BEFORE] [SYSTEM] dir1 [dir2 ...])

将指定目录添加到编译器的头文件搜索路径之下,指定的目录被解释成当前源码路径的相对路径。

PS:#include <> 与 #include “” 的区别:

  1. 使用场景不同:
    #include <> 一般用于包含系统头文件,例如 stdlib.h,stdio.h 等;
    #include "" 一般用于包含自定义的头文件,例如 test.h,declare.h 等;

  2. 查找的目录不同:
    #include <> 编译器直接从系统类库目录里查找头文件,例如 在Linux gcc编译环境下,一般为 /usr/include/usr/local/include 目录;如果类库目录下查找失败,编译器会终止查找,直接报错:No such file or directory
    #include "" 默认从项目当前目录查找头文件,即项目工程文件所在的目录。如果在项目当前目录下查找失败,再从项目配置的头文件引用目录查找头文件。

在加入 INCLUDE_DIRECTORIES 命令之后,就可以解决 hello.h头文件找不到的问题了:

在这里插入图片描述
这是因为没有链接 hello.so 库,所以找不到func()函数的定义。
解决方法有两种:

  1. LINK_DIRECTORIES 命令,添加非标准共享库的搜索路径(某个路径);
  2. TARGET_LINK_LIBRARIES 命令,添加需要链接的共享库(没某个具体的库)(注意此关键字在CMakeListst.txt中必须位于 ADD_EXECUTABLE 之后)。

在这里插入图片描述
命令格式:

TARGET_LINK_LIBRARIES(<target>
											<PRIVATE | PUBLIC | INTERFACE> <item> ...
											[<PRIAVTE | PUBLIC | INTERFACE> <item> ...] ...)

对于静态库也是相同的方式:

TARGET_LINK_LIBRARIES(main libhello.a)

此时解决了make时找不到func()函数定义的问题,并可以成功生成main可执行文件。
但是在执行 ./main 时,又报错找不到 libhello.so 动态库:

在这里插入图片描述
使用 ldd 查看main的依赖中抓不到libhello.so的路径:
在这里插入图片描述

这是因为,默认情况下,编译器只会使用 /lib/usr/lib 这两个目录下的库文件,如果不指定 --prefix,会将库安装在 /usr/local/lib 目录下;当运行程序需要链接动态库时,提示找不到相关的.so库,就会报错。

解决方法有两个:

  1. 手动将 /usr/local/lib 目录下的 libhello.so 拷贝到 /usr/lib 目录下;
  2. 在CMakeListsts.txt 中加入prefix,指定so库的安装路径。

CMAKE_INSTALL_PREFIX 命令:

CMAKE_INSTALL_PREFIX 是cmake内置变量,用于指定cmake执行install目标时,安装的路径前缀。

方法一: 在执行cmake时指定:

cmake -D CMAKE_INSTALL_PREFIX=<目标路径>

方法二: 在 CMakeLists.txt 中设置变量:

SET(CMAKE_INSTALL_PREFIX <install_path>)

在编译.so库文件时指定install安装路径:

在这里插入图片描述
make install 时可以看到成功安装到了 /usr/lib 路径下:
在这里插入图片描述

重新回到main工程下,执行./main,可以正常执行了:

在这里插入图片描述

相关文章:

  • 个人网页制作成品简单/湖南seo网站策划
  • 天津品牌网站建设公司/中国最新消息新闻
  • 定制开发电商网站建设/旅游最新资讯 新闻
  • 企业门户网站需求分析/亚马逊排名seo
  • 建站哪个网站比较好/动态网站设计毕业论文
  • 如何做网站拉动条/广告投放网站
  • automake使用感悟
  • 数据库范式
  • Linux中ssh基于密匙的安全验证过程是怎样的?
  • 跟着pink老师学习第二天的学习总结(1)
  • 白话说Java虚拟机原理系列【第四章】:内存结构之方法区详解
  • 什么是网络监控?OpManager 网络监控解决方案
  • 《中国数字安全能力图谱》:美创科技,数据安全代表者!
  • 洛谷——P5015 [NOIP2018 普及组] 标题统计
  • keras的模型可视化
  • 植物激素基因高级分析来啦~
  • Splunk Enterprise 存在任意代码执行漏洞
  • 「另类」图达通,还缺一个二次进化