• CMake (零基础一)


    CMake

    1-外部构建,在build 执行 make

    文件结构如下

    [       4096]  .
    ├── [       4096]  build    目录
    ├── [         92]  CMakeLists.txt    
    └── [        110]  HelloCMake.cpp
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    内容

    CMakeLists.txt 文件名一定不能写成其他

    PROJECT(HELLO_CMAKE)     //项目名
    set (SRC_LIST HelloCMake.cpp)      //设置变量
    add_executable(hello_cmake ${SRC_LIST)})   //添加源文件  ,生成可执行文件 hello_cmake
    
    • 1
    • 2
    • 3

    定义变量的方式 set (变量名 被指定的)

    使用变量的方法是 $(变量名)

    add_executable() 参数一 是生成的可执行文件名(目标文件) 参数二是依赖的源文件

    #include<iostream>
    using namespace std;
    int main()
    {
    cout<<"hello cmake "<<endl;
    return Exit_Success;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在build目录中执行

    cmake .. 
    
    • 1

    截取执行部分效果

    -- Configuring done   //配置完成
    -- Generating done    //生成完成
    -- Build files have been written to: /home/xxx/CMakeProc/HelloCMake/build  //构建文件写入build 文件夹
    
    • 1
    • 2
    • 3

    cmake … 表示从上级目录寻找CMakeList.txt文件,生成 makefile

    生成效果

    xxx@xxx-virtual-machine:~/CMakeProc/HelloCMake/build$ ls
    CMakeCache.txt  CMakeFiles  cmake_install.cmake  Makefile
    
    • 1
    • 2

    此时生成了 makefile ,就可以执行 make 命令

    xxx@xxx-virtual-machine:~/CMakeProc/HelloCMake/build$ make
    [ 50%] Building CXX object CMakeFiles/hello_cmake.dir/HelloCMake.cpp.o
    [100%] Linking CXX executable hello_cmake
    [100%] Built target hello_cmake
    
    • 1
    • 2
    • 3
    • 4

    会多出一个可执行文件,文件名由 executable() 指定

    xxx@xxx-virtual-machine:~/CMakeProc/HelloCMake/build$ ls
    CMakeCache.txt  CMakeFiles  cmake_install.cmake  hello_cmake  Makefile
    
    • 1
    • 2

    2输出自定义信息

    在CMakeLists.txt 中添加

    message(
    [SEND_ERROR | STATUS | FATAL_ERROR]
    "要输出的信息"
    )
    
    • 1
    • 2
    • 3
    • 4

    其中 SEND_ERROR STATUS FATAL_ERROR 是参数

    调整CMakeList.txt 中的内容(注释掉部分)

    如果不加参数

    PROJECT(HELLO_CMAKE)
    set (SRC_LIST HelloCMake.cpp)
    message("this is testMessage null para")
    #	message(STATUS "this is status")
    #	message(SEND_ERROR "this is send_error")
    #	message(FATAL_ERROR "this is fatal_error")
    message("this is lastest info")
    add_executable(hello_cmake ${SRC_LIST})
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    生成Makefile的过程

    this is testMessage null para
    this is lastest info
    -- Configuring done
    -- Generating done
    -- Build files have been written to: /home/xxx/CMakeProc/HelloCMake/build
    
    • 1
    • 2
    • 3
    • 4
    • 5

    加了 status

    PROJECT(HELLO_CMAKE)
    set (SRC_LIST HelloCMake.cpp)
    #message("this is testMessage null para")
    message(STATUS "this is status")
    #message(SEND_ERROR "this is send_error")
    #message(FATAL_ERROR "this is fatal_error")
    message("this is lastest info")
    add_executable(hello_cmake ${SRC_LIST})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    生成Makefile的过程

    -- this is status
    this is lastest info
    -- Configuring done
    -- Generating done
    -- Build files have been written to: /home/xxx/CMakeProc/HelloCMake/build
    
    • 1
    • 2
    • 3
    • 4
    • 5

    没有给message传参数的直接就输出了拾定的信息。而传递了STATUS参数的会在输出的信息前面加–

    加了 SEND_ERROR

    PROJECT(HELLO_CMAKE)
    set (SRC_LIST HelloCMake.cpp)
    #message("this is testMessage null para")
    #message(STATUS "this is status")
    message(SEND_ERROR "this is send_error")
    #message(FATAL_ERROR "this is fatal_error")
    message("this is lastest info")//后面的语句不会执行
    add_executable(hello_cmake ${SRC_LIST})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    生成Makefile的过程

    CMake Error at CMakeLists.txt:5 (message):
      this is send_error
    
    
    this is lastest info
    -- Configuring incomplete, errors occurred!
    See also "/home/xxx/CMakeProc/HelloCMake/build/CMakeFiles/CMakeOutput.log".
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    加了 FATAL_ERROR

    PROJECT(HELLO_CMAKE)
    set (SRC_LIST HelloCMake.cpp)
    #message("this is testMessage null para")
    #message(STATUS "this is status")
    #message(SEND_ERROR "this is send_error")
    message(FATAL_ERROR "this is fatal_error")
    message("this is lastest info")    //后面的语句不会执行
    add_executable(hello_cmake ${SRC_LIST})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    生成Makefile的过程

    CMake Error at CMakeLists.txt:6 (message):
      this is fatal_error
    
    
    -- Configuring incomplete, errors occurred!
    See also "/home/xxx/CMakeProc/HelloCMake/build/CMakeFiles/CMakeOutput.log".
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    可以看到,SEND ERROR和FATAL ERROR的信息都输出了,而且生成过程都跳过了因此,
    SEND_ERROR只是发送错误跳过生成成过程, 它后面的语句还会执行;而FATAL_ ERROR,从字面可以知道,致命错误,会立即中止过程,它后 面的语句也不再执行

    3两个变量

    PROJECT_BINARY_DIR

    PROJECT_SOURCE_DIR

    CMake会为每个项目的二进制目录和源目录
    隐式生成两个变量
    <project name>_ BINARY DIR 
    <project name>_ SOURCE DIR
    同时也存在这样的两个变量:
    PROJECT BINARY DIR = <project name> BINARY DIR
    PROJECT_ SOURCE DIR = <project name>_ SOURCE_ DIR
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    可以修改 CMakeLists.txt ,让其输出 信息

    PROJECT(HELLO_CMAKE)
    set (SRC_LIST HelloCMake.cpp)
    #message("this is testMessage null para")
    #message(STATUS "this is status")
    #message(SEND_ERROR "this is send_error")
    #message(FATAL_ERROR "this is fatal_error")
    #message("this is lastest info")
    
    
    message(${HELLO_CMAKE_BINARY_DIR})
    message(${PROJECT_BINARY_DIR})
    message("#####################")
    message(${PROJECT_SOURCE_DIR})
    message(${HELLO_CMAKE_SOURCE_DIR})
    
    
    add_executable(hello_cmake ${SRC_LIST})
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    声成makefile的信息

    /home/xxx/CMakeProc/HelloCMake/build
    /home/xxx/CMakeProc/HelloCMake/build
    #####################
    /home/xxx/CMakeProc/HelloCMake
    /home/xxx/CMakeProc/HelloCMake
    
    • 1
    • 2
    • 3
    • 4
    • 5

    PROJECT_BINARY_DIR 对应的是执行 cmake命令所在的目录,这里为build 文件夹

    PROJECT_SOURCE_DIR 对应的是源文件所在的目录 就是HelloCmake

    4动态库和静态库的构建和使用

    文件结构

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

    文件内容

    main.cpp

    #include"HelloLibrary.h"
    int main()
    {
    hello_library();
    return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    HelloLibrary.h

    #ifndef __HELLO_LiBRARY__
    #define __HELLO_LiBRARY__
    void hello_library();
    #endif   // __HELLO_LIBRARY
    
    • 1
    • 2
    • 3
    • 4

    HelloLibrary.cpp

    #include"HelloLibrary.h"
    #include<iostream>
    void hello_library()
    {
    std::cout<<"hello Shared Library!"<< std::endl;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    CMakeLists.txt

    cmake_minimum_required(VERSION 3.8 FATAL_ERROR)    #设置CMake所需的最低版本。如果使用的CMake版本低于该版本,则会发出致命错误,第二个参数可选, (VERSION [...][FATAL_ERROR]) 设置该工程的cmake最低支持版本,注意 VERSION 不能写成小写,否则会报cmake_minimum_required called with unknown argument version 
    project (HelloLibrary)
    add_library(hello_library SHARED HelloLibrary.cpp) #生成动态库,第二个参数是源文件。 静态库就是 STATIC
    include_directories(${PROJECT_SOURCE_DIR}) #给出编译器寻找头文件的目录(这个变量是源文件所在目录)
    add_executable(hello_main Main.cpp) 
    target_link_libraries(hello_main hello_library) #链接动态库
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    类比 makefile 我们是使用 .h 和 对应的 .cpp(除了主函数的cpp文件) 生成 .o (目标文件) , 目标文件生成库文件 。

    链接动态库的方法

    生成库(add_library)

    设置头文件的搜索路径

    (include_directories)

    为什么要使用 include_directories()

    include_directories
    是用来提供找头文件路径的,打个比方,我现在想要#include"cv.h",但是这个cv.h的路径是/usr/local/include/opencv,那么我总不能在主函数头前写#include “/usr/local/include/opencv/cv.h”吧,

    这个时候就用到include_directories了,它提供了一个搜索头文件暂时的根目录,即你可以在cmakelists中写上
    include_directories(/usr/local/include)来让库文件搜索以/usr/local/include为基础,即在main函数前写上#include “opencv/cv.h"即可

    如果存在多个头文件搜索路径 需要用空格隔开

    设置库的搜索路径

    LINK_DIRECTORIES

    如果生成的动态库在第三方路径,需要使用 LINK_DIRECTORIES()

    LINK_DIRECTORIES 命令来指定第三方库所在路径,比如,你的动态库在/home/myproject/libs这个路径下,则通过命令:LINK_DIRECTORIES(/home/myproject/libs),把该路径添加到第三方库搜索路径中,这样就可以使用相对路径了,使用TARGET_LINK_LIBRARIES的时候,只需要给出动态链接库的名字就行了。

    也可以set(LINK_DIR /Users/haoran/Downloads/wfdb/lib),LINK_DIRECTORIES({LINK_DIR})

    官方不建议使用该命令,取而代之的为find_package() find_library()

    5同时生成同名的动态静态库

    set_target_properties(target1 target2 ...
                          PROPERTIES prop1 value1
                          prop2 value2 ...)
    
    • 1
    • 2
    • 3

    这个命令是设置目标的属性,该命令的语法是列出想要更改的所有目标,然后提供接下来想要设置的值。您可以使用该命令任何所需的键值对,然后使用get_property()或get_target_property()命令提取它。

    1、建立一个静态库和动态库,提供HelloFunc函数供其他程序编程使用,HelloFunc

    向终端输出Hello World字符串。

    2、安装头文件与共享库。

    3、编写一个程序,来使用创建的共享库(静态库和动态库)。

    首先,新建一个目录,创建MakeList.txt

    /home/code/test/build

    文件内容

    hello.cpp

    #include "hello.h"
     
    using namespace std;
     
    void HelloFunc(){
        cout << "Hello World/n";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    hello.h

    #ifndef HELLO_H
     
    #define HELLO_H
     
    #include <stdio.h>
     
    void HelloFunc();
     
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    同级目录下的CMakeLists.txt

    SET (LIBHELLO_SRC hello.cpp)
     
    # SET (LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
     
    # 添加动态库,关键词为shared,不需要写全称libhello.so,
     
    # 只需要填写hello即可,cmake系统会自动为你生成 libhello.X
     
     
    ADD_LIBRARY (hello SHARED ${LIBHELLO_SRC})
     
     
    # 添加静态库,关键词为static,ADD_LIBRARY (hello STATIC ${LIBHELLO_SRC})
     
    # 仍然用hello作为target名时,是不能成功创建所需的静态库的,
     
    # 因为hello作为一个target是不能重名的, 故把上面的hello修改为hello_static
     
    # 同理,你不需要写全libhello_static.a
     
    # 只需要填写hello即可,cmake系统会自动为你生成 libhello_static.X
     
     
    ADD_LIBRARY (hello_static STATIC ${LIBHELLO_SRC})
     
     
    # 按照一般的习惯,静态库名字跟动态库名字应该是一致的,只是扩展名不同;
     
    # 即:静态库名为 libhello.a; 动态库名为libhello.so ;
     
    # 所以,希望 "hello_static" 在输出时,不是"hello_static",而是以"hello"的名字显示,故设置如下:
     
     
    SET_TARGET_PROPERTIES (hello_static PROPERTIES OUTPUT_NAME "hello")
     
     
    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定义 CLEAN_DIRECT_OUTPUT属性。
     
     
    SET_TARGET_PROPERTIES (hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
     
    SET_TARGET_PROPERTIES (hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)
     
     
    # 按照规则,动态库是应该包含一个版本号的,
     
    # VERSION指代动态库版本,SOVERSION指代API版本。
     
    SET_TARGET_PROPERTIES (hello PROPERTIES VERSION 1.2 SOVERSION 1)
     
     
    # 我们需要将libhello.a, libhello.so.x以及hello.h安装到系统目录,才能真正让其他人开发使用,
     
    # 在本例中我们将hello的共享库安装到<prefix>/lib目录;
     
    # 将hello.h安装<prefix>/include/hello目录。
     
    INSTALL (TARGETS hello hello_static LIBRARY DESTINATION lib
     
    ARCHIVE DESTINATION lib)
     
    INSTALL (FILES hello.h DESTINATION include/hello)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

    在build目录中

    cmake ..
     
    make
    
    • 1
    • 2
    • 3

    这时,你就可以在lib目录得到一个libhello.so,这就是我们期望的共享库。

    如果你要指定libhello.so生成的位置,可以通过:

    在主工程文件CMakeLists.txt中修改ADD_SUBDIRECTORY (lib) 指令来指定一个编译输出位置;
    或者在 lib/CMakeLists.txt中添加SET (LIBRARY_OUTPUT_PATH <路径>) 来指定一个新的位置。

    6添加工程子目录

    将源文件放入SRC 目录中,构建生成可执行文件在build文件夹中。

    文件结构

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

    文件内容

    一级的CMakeLists.txt

    cmake_minimum_required(VERSION 3.1)
    project(SubDirectory)
    add_subdirectory(src bin)  #将 src 加入 工程 ,编译输出的结果放入 bin 目录
    
    • 1
    • 2
    • 3

    二级的 CMakeLists.txt

    add_executable(sub_directory main.cpp) #生成的可执行文件
    
    • 1

    二级的 main.cpp

    #include<iostream>
    using namespace std;
    int main()
    {
    std::cout<<"This is sub directory"<<std::endl;
    return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在build 中执行 cmake …

    bin  CMakeCache.txt  CMakeFiles  cmake_install.cmake  Makefile
    
    • 1

    可以在 bin 中 也可以在当前目录执行 make 执行makefile.

    执行后 ,bin 中出现 可执行文件 sub_directory

    CMakeFiles  cmake_install.cmake  Makefile  sub_directory
    
    • 1

    补充:

    ADD_ SUBDIRECTORY指令
    ADD_ SUBDIRECTORY(src_ dir [bin_ dir]
    [EXCLUDE_ FROM_ ALL]) 
    src_ dir: 把src_ dir这个子目录加入工程
    bin_ dir: 指定编译输出(包含编译的中间结果)的目录
    为bin_ dir ,如果不指定,则默认输出目录为srC_ dir
    即与src_ dir同名的目眼录;指定其实就是把src_ dir
    重命名为bin_ dir
    EXCLUDE_ FROM_ ALL: 将给定的目录从编译中排除,即不编译它
    比如一些示例的目录,需要等 T程构建完
    后再单独构建的
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    CMake指定目标保存目录

    set(EXECUTABLE_OUTPUT_PATH )
    set(LIBRARY_OUTPUT_PATH)

    文件结构

    .
    ├── build
    ├── CMakeLists.txt
    └── src
        ├── CMakeLists.txt
        ├── main.cpp
        ├── SayHello.cpp
        └── SayHello.h
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    一级CMakeLIsts.txt中的内容

    cmake_minimum_required(VERSION 3.1)
    project(OutputPath)
    add_subdirectory(src)
    
    • 1
    • 2
    • 3

    二级CMakeLIsts.txt中的内容

    set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)#在build 文件夹下 创建bin文件夹,并在bin中生成执行文件
    set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)   #在build 文件夹下 创建lib文件夹,并在lib中生成库
    
    add_library(SayHello SayHello.cpp)
    include_directories(${PROJECT_SOURCE_DIR}/src)# 添加 头文件搜索路径
    add_executable(OutputPath main.cpp)
    target_link_libraries(OutputPath SayHello)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    PROJECT_BINARY_DIR 这个变量是执行cmake 的地方 也就是build

    文件内容

    SayHello.h

    #ifndef __SAYHELLO_H__
    #define __SAYHELLO_H__
    void Say_Hello();
    #endif
    
    • 1
    • 2
    • 3
    • 4

    SayHello.cpp

    #include<iostream>
    #include"SayHello.h"
    using namespace std;
    void Say_Hello()
    {
    
    std::cout<<"hello CMake"<<std::endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    进入build 文件夹中

    执行 cmake 生成 makefile

    bin  CMakeCache.txt  CMakeFiles  cmake_install.cmake  lib  Makefile  src
    
    • 1

    但此时 bin 和 lib 中为空目录,src 中存放了中间文件

    执行 make(可以在src 目录内,也可以在build 的同级目录) ,生成可执行文件和库文件

    查看 bin 和 lib 中的文件

    xxx@xxx-virtual-machine:~/CMakeProc/OutputPath/build$ tree bin lib
    bin
    └── OutputPath      # 是可执行文件
    lib
    └── libSayHello.a   # 库文件
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  • 相关阅读:
    生产者与消费者模型:餐厅吃饭问题
    Fabric v2.5区块链应用开发实战大纲
    D. Make It Round(math)
    flask-sqlalchemy 绑定多个数据库并实现多数据库迁移
    Java高级应用——多线程
    Mybatis Plus如何使用自定义方法实现分页呢?
    【Web前端面试】葵花宝典(2022版本)——HTTP浏览器 篇
    荧光染料CY3修饰PPS聚苯硫醚,PBA苯硼酸,PAE聚(β-氨基酯),聚缩醛,透明质酸
    并查集
    2019年数维杯数学建模B题火灾等级评价与快速救援措施优化求解全过程文档及程序
  • 原文地址:https://blog.csdn.net/qq_55125921/article/details/125614627