• 跨平台编译工具--CMake上手教程


    一、引入

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

    二、基本关键字

    1.PROJECT

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

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

    执行该条指令的时候隐式定义了两个CMAKE变量,分别是_BINARY_DIR和_BINARY,其中就是项目名,也就是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) #一个变量中可以包含多个源文件
    
    • 1
    • 2
    • 3

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

    3.MESSAGE

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

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

    4.ADD_EXECUTABLE

    ADD_EXECUTABLE:生成可执行文件。

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

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

    5.ADD_SUBDIRECTORY

    (1)使用

    ADD_SUBDIRCTORY:

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

    该指令会自动创建一个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/)
    
    • 1
    • 其中FILES表示安装文件,README是文件名,DESTINATION后面跟的是安装路径。
    • 这个路径可以是一个绝对路径,也可以是一个相对路径。
    • 如果是相对路径会使用CMake的一个变量CMAKE_INSTALL_PREFIX,他表示的是/usr/local/。因此我们安装的绝对路径是/usr/local/share/doc/cmake。

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

    安装脚本:

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

    (3)安装目录

    安装目录:

    INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake)
    
    • 1
    • 其中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)
    
    • 1
    • 其中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})
    
    • 1
    • 2
    • 3
    • 4
    • 5

    假设CMakeLists.txt和test.cc在同一个路径下:
    执行:cmake .
    可以看到生成了Makefile文件,此时make,就可以完成对test.cc的编译。
    在这里插入图片描述

    五、内部构建与外部构建

    上面的例子就是内部构建,而外部构建与之的区别是新建一个build目录,在build目录中进行CMake的操作。我们可以将上面CMake生成的文件进行删除,然后在build中对上一级目录进行CMake。
    在这里插入图片描述
    在这里插入图片描述
    此时在build目录下执行make,就可以在build下生成可执行文件了。

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

    强烈推荐使用外部构建。

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

    1.整体结构

    在这里插入图片描述

    2.编写两个CMakeLists.txt

    #外部的CMakeLists.txt
    PROJECT(TEST) #工程名
    ADD_SUBDIRECTORY(src bin) #将src目录下的文件加入工程,并指定编译生成的文件在bin中
    
    #内部的CMakeLists.txt
    ADD_EXECUTABLE(hello test.cc) #对test.cc进行编译(因为已经将其加入工程,所以可以找到),生成的hello会放在bin中
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

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

    3.安装HelloWorld

    首先构建目录树,为了方便演示,先将build中内容删除。
    在这里插入图片描述
    其中新增了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)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在执行make之后,还需要执行make install来进行安装。
    在这里插入图片描述
    此时可以看到文件安装成功了。

    七、建立动态库和静态库

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

    1.结构

    在这里插入图片描述

    2.CMakeLists编写

    #外层
    PROJECT(HELLO)
    ADD_SUBDIRECTORY(lib bin)
    
    #内层
    SET(LIBHELLO_SRC hello.cc)
    ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

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

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

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

    ADD_LIBRARY(hello SHARED $(LIBHELLO_SRC))
    ADD_LIBRARY(hello STATIC $(LIBHELLO_SRC))
    
    • 1
    • 2

    虽然后缀不同,但是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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    最后两行代码的具体工作说实话我也没太搞懂,有无大佬解释一下。。。
    在这里插入图片描述
    此时可以看到生成了一个动态库和静态库。

    (3)动静态库的安装

    使用INSTALL指令来进行安装:

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

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

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

    此时就将其修改为了usr路径了。
    在这里插入图片描述
    使用make install来完成安装。

    八、使用动态库与静态库

    1.链接头文件

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

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

    2.链接动态库

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

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

    注意,链接动态库需要写在ADD_EXECULABLE之下。他的第一个参数表示的就是要将库中内容加载进的可执行文件。
    在这里插入图片描述
    此时执行make可以生成可执行文件hello。

  • 相关阅读:
    观察者模式
    纯粹的四位奇数
    Domino服务器SSL证书安装指南
    Ernie-gram, 显式、完备的 n-gram 掩码语言模型,实现了显式的 n-gram 语义单元知识建模。
    面试常问框架知识(动手做才能深刻)
    Pod控制器-ReplicaSet(RS)
    axios使用
    基于EPICS stream模块的直流电源的IOC控制程序实例
    【计算机毕业设计】7.线上花店系统maven源码
    RStudio学习笔记(一)
  • 原文地址:https://blog.csdn.net/qq_51492202/article/details/127983156