• Cmake的安装与使用


    一、CMake的安装

    大多数的linux系统是预装cmake的,windows我们不做讨论

    sudo apt-get install camke
    
    • 1

    二、构建一个Hello World项目

    我们在当前目录下写一个cpp程序

    #main.cpp
    
    #include 
    int main(){
    	std::cout <<  "hello word" << std::endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    写一个CMakeLists.txt,也就是Cmake的配置文件

    #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})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在当前目录下执行cmake,生成makefile

    cmake .
    
    • 1

    使用make命令进行编译

    make
    
    • 1

    我们发现在当前的目录下生成了很多中间文件和一个最终生成的二进制可执行文件,hello。

    三、CMake语法简介

    我们在上面的CMakeList.txt中有一些语法我们是不清楚的,现在我们来解释一下。

    首先CMakeList中的指令是不区分大小写的,但是指令中的内容是区分大小写的。也就是PROJECT和project是等价的。但是PROJECT(HELLO)和PROJECT(hello)并不等价。

    3.1、PROJECT

    可以用来指定工程的名字和支持的语言,默认支持所有语言

    PROJECT (HELLO)          # 指定了工程的名字,并且支持所有语言—建议
    PROJECT (HELLO CXX)      # 指定了工程的名字,并且支持语言是C++
    PROJECT (HELLO C CXX)    # 指定了工程的名字,并且支持语言是C和C++
    
    _BINARY_DIR  # 本例中是 HELLO_BINARY_DIR
    _SOURCE_DIR  # 本例中是 HELLO_SOURCE_DIR
    # 在实际使用的时候,会自动定义两个变量PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR,所以其实不用理会
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3.2、SET

    更像是宏定义,或者参数设置

    SET(SRC_LIST main.cpp)    # SRC_LIST变量就代表了main.cpp
    SET(SRC_LIST "main.cpp")  # 和上面等价,但是如果文件名中有空格,那么只能使用这种方式
    SET(SRC_LIST main)        # 会自动寻找main.cpp,但是不要这样写,因为会有main.cpp和main同时存在的情况
    
    • 1
    • 2
    • 3

    3.3、MESSAGE

    向终端输出用户自定义的信息

    主要包含三种信息:

    SEND_ERROR     产生错误,生成过程被跳过。
    SATUS          输出前缀为—的信息。
    FATAL_ERROR    立即终止所有 cmake 过程.
    
    • 1
    • 2
    • 3

    3.4、ADD_EXECUTABLE

    生成可执行文件

    ADD_EXECUTABLE(hello ${SRC_LIST}) 
    ADD_EXECUTABLE(hello main.cpp)
    ADD_EXECUTABLE(hello main1.cpp main1.cpp)  # 多个参数用空格分隔
    ADD_EXECUTABLE(hello main1.cpp;main1.cpp)  # 多个参数用分号分隔
    
    • 1
    • 2
    • 3
    • 4

    3.5、ADD_SUBDIRECTORY

    这个指令用于向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置

    ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
    # source_dir 源文件目录
    # binary_dir 二进制文件目录
    # EXCLUDE_FROM_ALL 将那些目录从编译中排除
    
    • 1
    • 2
    • 3
    • 4

    3.6、ADD_LIBRARY

    ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
    
    # hello:就是正常的库名,生成的名字前面会加上lib,最终产生的文件是libhello.so
    # SHARED,动态库    STATIC,静态库
    # ${LIBHELLO_SRC} :源文
    
    • 1
    • 2
    • 3
    • 4
    • 5

    四、内部构建和外部构建

    上面的例子就是内部构建,虽然也可以用,但是存在一些问题,就是很乱,不像一个工程,一旦文件变多不好清理,我们可以使用外部构建的方式使得文件目录变得清晰起来。

    我们新建一个工程目录,在工程目录下创建以下文件

    为工程添加一个子目录 src,用来放置工程源代码
    添加一个子目录 doc,用来放置这个工程的文档 hello.txt
    在工程目录添加文本文件 COPYRIGHT(版权信息), README(调用须知)
    在工程目录添加一个脚本,用来调用 hello 二进制
    将构建后的目标文件放入构建目录的 bin 子目录
    将 doc 目录 的内容以及 COPYRIGHT/README 安装到/usr/share/doc/cmake/
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    我们创建完的工程目录如下

    ├── build
    ├── CMakeLists.txt
    ├── COPYRIGHT
    ├── doc
    │   └── hello.txt
    ├── README
    ├── runhello.sh
    └── src
        ├── CMakeLists.txt
        └── main.cpp
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    我们修改我们主目录的CMakeLists.txt

    PROJECT(HELLO)
    
    # 链接src 链接bin 但是这个bin目录是会在build中生成的,所以我们其实可以写成../bin,这样的话就是在主目录生成了
    ADD_SUBDIRECTORY(src bin)
    
    # 安装文件
    INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake)
    # 安装脚本
    INSTALL(PROGRAMS runhello.sh DESTINATION bin)
    # 安装目录
    INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    我们修改我们src目录的CMakeLists.txt

    ADD_EXECUTABLE(hello main.cpp)
    
    • 1

    我们进入build目录

    camke ..
    
    • 1

    make程序

    make
    
    • 1

    我们进入工程的主目录bin目录下就可以找到我们的二进制文件了。这时候我们的项目已经有点正经项目的样子了。

    五、安装

    安装可以简单理解为将一些生成的文件或者自有的文件放在计算机的指定目录中方便调用。

    5.1、安装文件

    INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake/)
    
    • 1

    FILES:文件

    DESTINATION:相对路径:CMAKE_INSTALL_PREFIX 默认是在 /usr/local/,当然也可以写绝对路径,或者设置CMAKE_INSTALL_PREFIX参数,cmake -DCMAKE_INSTALL_PREFIX=/usr

    5.2、安装脚本

    INSTALL(PROGRAMS runhello.sh DESTINATION bin)
    
    • 1

    说明:实际安装到的是 /usr/bin

    5.3、安装doc下的文本

    INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake)
    
    • 1

    DIRECTORY 后面连接的是所在 Source 目录的相对路径

    注意:doc 和 doc/有很大的区别

    目录名不以/结尾:这个目录将被安装为目标路径下的

    目录名以/结尾:将这个目录中的内容安装到目标路径

    5.4、安装过程

    cmake ..
    make
    make install
    
    • 1
    • 2
    • 3

    六、静态库和动态库的构建

    动态库的静态库的区别

    • 静态库的扩展名一般为“.a”或“.lib”;动态库的扩展名一般为“.so”或“.dll”。
    • 静态库在编译时会直接整合到目标程序中,编译成功的可执行文件可独立运行
    • 动态库在编译时不会放到连接的目标程序中,即可执行文件无法单独运行。

    6.1、构建实例

    ├── build
    ├── CMakeLists.txt
    └── lib
        ├── CMakeLists.txt
        ├── hello.cpp
        └── hello.h
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    hello.h中的内容

    #ifndef HELLO_H
    #define Hello_H
    
    void HelloFunc();
    
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    hello.cpp中的内容

    #include "hello.h"
    #include 
    void HelloFunc(){
        std::cout << "Hello World" << std::endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    项目中的cmake内容

    PROJECT(HELLO)
    ADD_SUBDIRECTORY(lib bin)
    
    • 1
    • 2

    lib中CMakeLists.txt中的内容

    SET(LIBHELLO_SRC hello.cpp)
    ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
    
    • 1
    • 2

    6.2、同时构建静态库和动态库

    // 如果用这种方式,只会构建一个动态库,不会构建出静态库,虽然静态库的后缀是.a
    ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
    ADD_LIBRARY(hello STATIC ${LIBHELLO_SRC})
    
    // 修改静态库的名字,这样是可以的,但是我们往往希望他们的名字是相同的,只是后缀不同而已
    ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
    ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    SET_TARGET_PROPERTIES这条指令可以用来设置输出的名称,对于动态库,还可以用来指定动态库版本和 API 版本

    SET(LIBHELLO_SRC hello.cpp)
        
    ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})
    //对hello_static的重名为hello
    SET_TARGET_PROPERTIES(hello_static PROPERTIES  OUTPUT_NAME "hello")
    //cmake 在构建一个新的target 时,会尝试清理掉其他使用这个名字的库,因为,在构建 libhello.so 时, 就会清理掉 libhello.a
    SET_TARGET_PROPERTIES(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
    
    ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
    SET_TARGET_PROPERTIES(hello PROPERTIES  OUTPUT_NAME "hello")
    SET_TARGET_PROPERTIES(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    6.3、安装共享库和头文件

    本例中我们将 hello 的共享库安装到/lib目录,

    将 hello.h 安装到/include/hello 目录

    //文件放到该目录下
    INSTALL(FILES hello.h DESTINATION include/hello)
    
    //二进制,静态库,动态库安装都用TARGETS
    //ARCHIVE 特指静态库,LIBRARY 特指动态库,RUNTIME 特指可执行目标二进制。
    INSTALL(TARGETS hello hello_static LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    安装的时候,指定一下路径,放到系统下

    cmake -DCMAKE_INSTALL_PREFIX=/usr ..

    6.4、使用外部共享库和头文件

    准备工作,新建一个目录来使用外部共享库和头文件

    ├── build
    ├── CMakeLists.txt
    └── src
        ├── CMakeLists.txt
        └── main.cpp
    
    • 1
    • 2
    • 3
    • 4
    • 5

    main.cpp

    #include 
    
    int main(){
    	HelloFunc();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    我们发现,报错了,找不到头文件,这个很好解决,就是我们的hello.h放在了hello/hello.h这里,所以我们可以

    #include 
    
    • 1

    但是很不方便,我们需要区分目录,很麻烦,所以我们可以在CMakeLists.txt中加入头文件搜索路径

    INCLUDE_DIRECTORIES(/usr/include/hello)
    
    • 1

    cmake一下,还是错的,undefined reference to `HelloFunc()',没有找到函数,这个其实也很好解释,你是搞了头文件了,但是没有库啊,你并没有链接库,所以怎么能找到函数呢。我们可以添加一下这个链接库

    # 一定要写在add_executable后面
    # 添加静态库,直接整合到程序中
    TARGET_LINK_LIBRARIES(hello libhello.a)
    # 添加动态库,不整合,需要时调用
    TARGET_LINK_LIBRARIES(hello libhello.so)
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • 相关阅读:
    移动单点输入连杆夹持机构
    Android 11.0 禁止弹出系统simlock的锁卡弹窗功能实现
    物联网开发系列(一)·阿里云平台产品的创建以及设备的添加
    湘潭大学信息与网络安全复习笔记2(总览)
    百数标准应用——转运隔离数字化,降低感染风险
    Vue组件(二)父组件、子组件通信/传值
    C++DAY49
    公司新来了个00后卷王,一副毛头小子的样儿,哪想到...
    如何与 TENNECO 建立 EDI 连接?
    基于Html+Python+Django+Sqlite的机票预订系统
  • 原文地址:https://blog.csdn.net/weixin_43903639/article/details/126691319