文章目录

  • 一、引入
  • 二、基本关键字
  • 1.PROJECT
  • 2.SET
  • 3.MESSAGE
  • 4.ADD_EXECUTABLE
  • 5.ADD_SUBDIRECTORY
  • (1)使用
  • (2)CMakeLists执行顺序
  • (3)输出文件的位置
  • 6.INSTALL
  • (1)安装文件
  • (2)安装非目标文件可执行文件
  • (3)安装目录
  • (4)安装指令
  • 7.ADD_LIBRARY
  • 8.SET_TARGET_PROPERTIES
  • 三、语法的基本规则
  • 四、使用CMake编写一个Hello CMake
  • 五、内部构建与外部构建
  • 六、让Hello world看起来更像一个工程
  • 1.整体结构
  • 2.编写两个CMakeLists.txt
  • 3.安装HelloWorld
  • 七、建立动态库和静态库
  • 1.结构
  • 2.CMakeLists编写
  • 3.静态库与动态库的构建
  • (1)动态库与静态库重名构建失败
  • (2)解决办法
  • (3)动静态库的安装
  • 八、使用动态库与静态库
  • 1.链接头文件
  • 2.链接动态库


一、引入

当多个人要使用不同的语言或者编译器来开发一个项目的时候,最终要输出一个可执行文件或者共享库(dll,so等等),CMake使得所有的操作都是通过编译CMakeLists.txt来实现的。
学习CMake的目的是为了将来处理大型的C/C++项目,可以先看下面的例子参照上面的关键字来进行学习。

二、基本关键字

1.PROJECT

PROJECT:用来指定工程的名字以及支持的语言,支持的语言不填的话,默认是支持所有语言。

PROJECT(HELLO)       #默认支持所有语言
PROJECT(HELLO C CXX) #指定支持C和C++

执行该条指令的时候隐式定义了两个CMAKE变量,分别是<projectname>_BINARY_DIR和<orojectname>_BINARY,其中<orojectname>就是项目名,也就是HELLO。这两个变量下面介绍的MESSAGE关键字都是可以直接使用的,在外部编译的部分介绍两者的区别。

2.SET

SET:用于显示的指定变量。

SET(SRC_LIST test.cc)           #SRC_LIST变量中就包含了test.cc
SET(SRC_LIST "test.cc")         #也可以加双引号         
SET(SRC_LIST test1.cc test2.cc) #一个变量中可以包含多个源文件

在SET指令中,如果源文件名中含有空格(比如ma in.cc),则必须加双引号,如果没有加不加都可以。

3.MESSAGE

MESSAGE:向终端输出用户自定义的信息。
主要包含三种信息:

  • SEND_ERROR,产生错误,生成过程被跳过。
  • STATUS,输出前缀为–的信息。
  • FATAL_ERROR,立即终止所有CMake的过程。
MESSAGE(STATUS "This is BINARY dir" ${HELLO_BINARY_DIR}) #此时的输出带有前缀--

4.ADD_EXECUTABLE

ADD_EXECUTABLE:生成可执行文件。

ADD_EXECUTABLE(test ${SRC_LIST}) #将变量SRC_LIST中文件编译成可执行文件test
ADD_EXECUTABLE(test test.cc)     #这样写也可以,不使用变量

其中test.cc的后缀可以不写,会自动去找.c或者.cc,但是不推荐这样写。

5.ADD_SUBDIRECTORY

(1)使用

ADD_SUBDIRCTORY:

  • 这个指令用来向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制的存放位置。
  • EXCLUDE_FROM_ALL函数是将写的目录从编译中排除,如程序中的example。
ADD_SUBDIRECTORY(src bin) #将src的子目录加入工程,并指定编译结果输出路径为bin目录。

该指令会自动创建一个bin目录,如果不指定则结果会放在build/src即SOURCE_DIR目录。

(2)CMakeLists执行顺序

CMakeLists是逐层进行执行的:

  • 首先执行cmake指定路径下的CMakeLists。
  • 当在CMakeLists中遇到ADD_SUBDIRECTORY的时候跳转到该关键字指定的路径下执行该路径下的CMakeLists。
  • 执行之后返回跳转位置的下一条指令,然后继续按顺序执行。

(3)输出文件的位置

ADD_SUBDIRECTORY可以识别出指定路径中CMakeLists中的ADD_EXECUTABLE生成的可执行文件是一个输出文件,并会将该文件添加到指定的输出文件路径。这个输出文件也可以是一个动态库或者静态库。

6.INSTALL

INSTALL:为安装关键字,他可以用来安装一个文件,也可以用来安装脚本等。

(1)安装文件

安装文件:

INSTALL(FIELS README DESTINATION share/doc/cmake/)
  • 其中FILES表示安装文件,README是文件名,DESTINATION后面跟的是安装路径。
  • 这个路径可以是一个绝对路径,也可以是一个相对路径。
  • 如果是相对路径会使用CMake的一个变量CMAKE_INSTALL_PREFIX,他表示的是/usr/local/。因此我们安装的绝对路径是/usr/local/share/doc/cmake。

(2)安装非目标文件可执行文件

安装脚本:

INSTALL(PROGRAMES runhello.sh DESTINATION bin)
  • 其中PROGRAMES表示的是非目标文件的可执行文件,bin还是一个相对路径。
  • 此时文件绝对路径为/usr/local/bin

(3)安装目录

安装目录:

INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake)
  • 其中DIRECTORY表示目录。
  • 注意doc/和doc是有区别的,doc/表示将整个目录安装到对应路径下,二doc表示将目录中的内容安装到对应路径下。

(4)安装指令

注意仅仅cmake是不能够进行安装的,而是在生成makefile之后还需要进行make install操作。才算安装成功。

7.ADD_LIBRARY

ADD_LIBRARY:用来将源文件创建成动态库,即将test.cc编程libtest.so

ADD_LIBRARY(hello SHARED test.cc)
  • 其中hello为库名,生成的名字后会加上前缀和后缀,及libhello.so。
  • SHARED表示动态库,STATIC表示静态库。
  • test.cc:源文件。

8.SET_TARGET_PROPERTIES

SET_TARGET_PROPERTIES:用来设置输出的名称,对于动态库,还可以指定动态库版本和API版本。
这个在动静态库中结合场景具体介绍。

三、语法的基本规则

  • 变量使用${}来进行取值,比如上述的ADD_EXECUTABLE中的${SRC_LIST},就是获取该变量中的值,该变量中可以是多个值也可以是一个值。
  • 关键字指令的参数是使用空格或者分号来进行隔开的。
  • 指令无关大小写,参数和变量有关。

四、使用CMake编写一个Hello CMake

这里主要编写CMakeLists.txt

PROJECT(TEST)
SET(SRC_LIST test.cc)
MESSAGE(STATUS "This is a BINARY dir" ${TEST_BINARY_DIR})
MESSAGE(STATUS "This is a SOURCE dir" ${TEST_SOURCE_DIR})
ADD_EXECUTABLE(test ${SRC_LIST})

假设CMakeLists.txt和test.cc在同一个路径下:

执行:cmake .

可以看到生成了Makefile文件,此时make,就可以完成对test.cc的编译。

cmake 设置 Android 编译版本 cmake编译步骤_静态库

五、内部构建与外部构建

上面的例子就是内部构建,而外部构建与之的区别是新建一个build目录,在build目录中进行CMake的操作。我们可以将上面CMake生成的文件进行删除,然后在build中对上一级目录进行CMake。

cmake 设置 Android 编译版本 cmake编译步骤_动态库_02


cmake 设置 Android 编译版本 cmake编译步骤_c++_03


此时在build目录下执行make,就可以在build下生成可执行文件了。

同时我们可以发现BINARY的路径变为了build路径,而SOURCE没有发生变化。

强烈推荐使用外部构建。

六、让Hello world看起来更像一个工程

1.整体结构

cmake 设置 Android 编译版本 cmake编译步骤_windows_04

2.编写两个CMakeLists.txt

#外部的CMakeLists.txt
PROJECT(TEST) #工程名
ADD_SUBDIRECTORY(src bin) #将src目录下的文件加入工程,并指定编译生成的文件在bin中

#内部的CMakeLists.txt
ADD_EXECUTABLE(hello test.cc) #对test.cc进行编译(因为已经将其加入工程,所以可以找到),生成的hello会放在bin中

还是进入build进行CMake操作,然后进行make操作,会发现的build目录下生成了一个bin文件夹,进入该文件夹有可执行文件test。

cmake 设置 Android 编译版本 cmake编译步骤_静态库_05

3.安装HelloWorld

首先构建目录树,为了方便演示,先将build中内容删除。

cmake 设置 Android 编译版本 cmake编译步骤_动态库_06


其中新增了COPYRIGHT为版权文件,README为说明文件,runhello.sh为一个运行脚本。

增加安装的命令:

PROJECT(TEST)
ADD_SUBDIRECTORY(src bin)
INSTALL(FILES README COPYRIGHT DESTINATION share/doc/cmake)
INSTALL(PROGRAMS runhello.sh DESTINATION bin)
INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake)

在执行make之后,还需要执行make install来进行安装。

cmake 设置 Android 编译版本 cmake编译步骤_动态库_07


此时可以看到文件安装成功了。

七、建立动态库和静态库

目标:建立一个HelloFunc函数来提供给其他函数使用,该函数可以输出Hello World。建立该函数的头文件以及共享库。

1.结构

cmake 设置 Android 编译版本 cmake编译步骤_静态库_08

2.CMakeLists编写

#外层
PROJECT(HELLO)
ADD_SUBDIRECTORY(lib bin)

#内层
SET(LIBHELLO_SRC hello.cc)
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})

其中外层的ADD_SUBDIRECTORY会识别内存的输出文件即动态库,并将其放在执行cmake的文件(build下),执行make之后就可以在bin中看到该动态库libhello.so了。

cmake 设置 Android 编译版本 cmake编译步骤_静态库_09

3.静态库与动态库的构建

在写这里的时候发生了一个让我震惊的事情,所有博客对于这里的描述都是一模一样的,而我没有理清他的逻辑。
所以我经过查阅文档,大概给出了一个可以自洽的逻辑。

(1)动态库与静态库重名构建失败

ADD_LIBRARY(hello SHARED $(LIBHELLO_SRC))
ADD_LIBRARY(hello STATIC $(LIBHELLO_SRC))

虽然后缀不同,但是cmake认为两者是相同的,会构建失败。

(2)解决办法

使用SET_TARGET_PROPERTIES

#建立不同名的动态库和静态库
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
#将静态库改名
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello")
#这两行我也不知道干啥的,反正要写,好像是清理之前的名字???经过我的测试,不加这两行也可以
SET_TARGET_PROPERTIES(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
SET_TARGET_PROPERTIES(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)

最后两行代码的具体工作说实话我也没太搞懂,有无大佬解释一下。。。

cmake 设置 Android 编译版本 cmake编译步骤_开发语言_10


此时可以看到生成了一个动态库和静态库。

(3)动静态库的安装

使用INSTALL指令来进行安装:

#安装头文件和动静态库
INSTALL(FILES hello.h DESTINATION include/hello)
INSTALL(TARGETS hello hello_static LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)

注意这里指定的还是相对路径,他的前缀是usr/local,如果想修改这个前缀,那么可以使用如下指令:

cmake -D CMAKE_INSTALL_PREFIX=/usr
还有一个指令也想介绍一下,那就是生成debug版本的方式
cmake -D CMAKE_BUILD_TYPE=debug

此时就将其修改为了usr路径了。

cmake 设置 Android 编译版本 cmake编译步骤_windows_11


使用make install来完成安装。

八、使用动态库与静态库

1.链接头文件

当使用include<>包含头文件的时候会发生报错,CMake有专门的函数来帮助找到这个头文件。

INCLUDE_DIRECTORIES(/usr/local/include/hello) #头文件搜索路径

2.链接动态库

TARGET_LINK_LIBRARY:如果你的库在标准的库搜索路径,直接在这里写上库名就好。如果不是可以使用绝对路径。

TARGET_LINK_LIBRARIES(hello /usr/local/lib/libhello.so)

注意,链接动态库需要写在ADD_EXECULABLE之下。他的第一个参数表示的就是要将库中内容加载进的可执行文件。

cmake 设置 Android 编译版本 cmake编译步骤_开发语言_12


此时执行make可以生成可执行文件hello。