• 使用Cmake编译项目从入门到精通


    cmake 的特点主要有:
    1,开放源代码,使⽤类BSD 许可发布。http://cmake.org/HTML/Copyright.html
    2,跨平台,并可⽣成native 编译配置⽂件,在Linux/Unix 平台,⽣成 makefile,在苹果平台,可以⽣
    成xcode,在 Windows 平台,可以⽣成 MSVC 的⼯程⽂件。
    3,能够管理⼤型项⽬,KDE4 就是最好的证明。
    4,简化编译构建过程和编译过程。Cmake 的⼯具链⾮常简单:cmake+make。
    5,⾼效虑,按照KDE 官⽅说法,CMake 构建KDE4 的 kdelibs 要⽐使⽤autotools 来构建
    KDE3.5.6 的 kdelibs 快40%,主要是因为 Cmake 在⼯具链中没有libtool。
    6,可扩展,可以为cmake 编写特定功能的模块,扩充cmake 功能。

    1、安装

    # 1、卸载老版本的cmake
    apt-get autoremove cmake
    
    # 2、文件下载解压
    wget https://cmake.org/files/v3.9/cmake-3.9.1-Linux-x86_64.tar.gz
    tar zxvf cmake-3.9.1-Linux-x86_64.tar.gz
    
    # 3、创建软链接
    mv cmake-3.9.1-Linux-x86_64 /opt/cmake-3.9.1
    ln -sf /opt/cmake-3.9.1/bin/* /usr/bin/
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2、使用CMake生成makefile

    2.1、项目结构介绍

    当前环境
    在这里插入图片描述

    创建5个cpp文件分别是加减乘除函数,和main函数,最简单的方式是g++ *.cpp -o app 这种方式进行编译生成可执行程序,现在我们通过cmake来进这个项目进行编译
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    2.2、编写CMakeLists.txt文件

    在项目同级目录下创建CMakeLists.txt文件,通过下面三条命令就可以构建出我们的makefile文件了,为了让我们项目目录干净整洁,我们在当前目录下创建一个build目录,然后进入build目录下,执行cmake …

    # 指定cmake最低版本
    cmake_minimum_required(VERSION 3.0)
    # 指定项目名字
    project(myadd_test)
    
    # 生成项目的执行程序名字,和源文件(多个可以通过空格和;进行区分)
    add_executable(app add.cpp;div.cpp;main.cpp;mult.cpp;sub.cpp)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    执行cmake … 之后在build目录下就生成了Makefile文件了,然后执行make就生成我们的可执行程序了
    在这里插入图片描述

    2.3、优化写法1

    上面的写法要是源文件特别多的情况下就会不美观,诞生出通过set命令给变量SRC_LIST进行初始化,然后add_executable通过${}调用这个变量

    # 通过set对变量进行初始化
    set(SRC_LIST add.cpp;div.cpp;main.cpp;mult.cpp;sub.cpp)
    add_executable(app ${SRC_LIST})
    
    • 1
    • 2
    • 3

    这种写法还是不完善,源文件一旦多起来容易出错,并且也不人性化,通过搜索函数去目录下进行搜索,然后保存到变量中(两个函数都是用来去命令进行遍历)

    # 搜索目录下的源文件(.c.cpp),存储到后面一个变量中
    # 执行cmake后面跟随的路径PROJECT_SOURCE_DIR
    aux_source_directory(${PROJECT_SOURCE_DIR} SRC)
    
    # file命令用来搜索目录下源文件,搜索目录
    # CMAKE_CURRENT_SOURCE_DIR CMakeLists.txt的路径
    # GLOB遍历当前目录
    file(GLOB SRC_C ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2.4、给可执行程序设置路径

    可以通过set对宏进行初始化,进行环境设置,表示设置编译器为c++11,下面是给生成的可执行程序指定目录

    # 指定c++ 11 c++17  std=c++11
    set(CMAKE_CXX_STANDARD 11)
    # set(CMAKE_CXX_STANDARD 17)
    
    # 给可执行程序指定路径
    set(HOME /root/netmap_test/cmake_stu/build)
    set(EXECUTABLE_OUTPUT_PATH ${HOME}/bin)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2.5、指定头文件目录

    一般项目中.cpp目录一般存放在src,头文件一般存放在include目录下,下面创建include和src目录,将cpp文件和头文件放入到对应的目录下,并指定头文件目录
    在这里插入图片描述

    # 指定cmake最低版本
    cmake_minimum_required(VERSION 3.0)
    # 指定项目名字
    project(myadd_test)
    
    aux_source_directory(${PROJECT_SOURCE_DIR}/src SRC)
    
    # 指定头文件路径
    include_directories(${PROJECT_SOURCE_DIR}/include)
    add_executable(app ${SRC})
    
    # 指定c++ 11 c++17  std=c++11
    set(CMAKE_CXX_STANDARD 11)
    # set(CMAKE_CXX_STANDARD 17)
    
    # 给可执行程序指定路径
    set(HOME /root/netmap_test/cmake_stu/build)
    set(EXECUTABLE_OUTPUT_PATH ${HOME}/bin)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    2.6、制作库以及使用

    将main.cpp拷贝出来,制作成动态库不需要测试程序

    在这里插入图片描述

    # 指定cmake最低版本
    cmake_minimum_required(VERSION 3.0)
    # 指定项目名字
    project(myadd_test)
    
    # 搜索目录下的源文件(.c.cpp),存储到后面一个变量中
    # 执行cmake后面跟随的路径PROJECT_SOURCE_DIR
    aux_source_directory(${PROJECT_SOURCE_DIR}/src SRC)
    
    # 指定头文件路径
    include_directories(${PROJECT_SOURCE_DIR}/include)
    
    # 指定c++ 11 c++17  std=c++11
    set(CMAKE_CXX_STANDARD 11)
    
    
    # 生成库,指定库名称,指定为动态库,指定需要生产库的文件
    # add_library(calc SHARED ${SRC})
    
    # 指定生产库的路径
    set(LIBRARY_OUTPUT_PATH /root/netmap_test/cmake_stu/build)
    # 静态库
    add_library(calc STATIC ${SRC})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在这里插入图片描述

    在这里插入图片描述
    静态库和动态库调用
    在这里插入图片描述

    # 指定cmake最低版本
    cmake_minimum_required(VERSION 3.0)
    # 指定项目名字
    project(myadd_test)
    
    # 搜索目录下的源文件(.c.cpp),存储到后面一个变量中
    # 执行cmake后面跟随的路径PROJECT_SOURCE_DIR
    aux_source_directory(${PROJECT_SOURCE_DIR} SRC)
    
    # 指定头文件路径
    include_directories(${PROJECT_SOURCE_DIR}/include)
    
    # 指定c++ 11 c++17  std=c++11
    set(CMAKE_CXX_STANDARD 11)
    
    # 指定静态库
    link_libraries(calc)
    # 指定库目录地址
    link_directories(${CMAKE_CURRENT_SOURCE_DIR}/build)
    add_executable(app ${SRC})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    3、项目演练

    3、字符串拼接

    实际项目中,一般都是以模块化的形式出现的,现在我们进行模拟实际项目的情况,项目目录下有calc、include、sort、test1、test2文件夹和CMakeLists.txt文件,calc是计算相关的接口,sort是排序相关的接口,test1和test2分别是测试程序,include是项目的头文件

    在这里插入图片描述

    3.1、项目目录下的CMakeList.txt编写

    主要做了两件事:
    1、定义对应的变量提供给后面的子节点使用
    2、将子目录添加到项目中来

    cmake_minimum_required(VERSION 3.0)
    project(Mytest)
    
    # 定义变量
    
    # 初始化,静态库生成路径
    set(LIBPATH ${PROJECT_SOURCE_DIR}/lib)
    
    # 可执行程序目录
    set(EXECPATH ${PROJECT_SOURCE_DIR}/bin)
    
    # 头文件路径
    set(HEADPATH ${PROJECT_SOURCE_DIR}/include)
    
    # 库文件的名字
    set(CALCLIB calc)
    set(SORLIB sort)
    
    # 可执行程序的名字
    set(APPNAME1 app1)
    set(APPNAME2 app2)
    
    # 添加子目录
    add_subdirectory(calc)
    add_subdirectory(sort)
    add_subdirectory(test1)
    add_subdirectory(test2)
    
    • 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

    3.2、calc目录下的CMakeLists.txt编写

    搜索当前目录下的源文件,将其编译成库,库名是父节点中定义的,指定生成库的路径

    cmake_minimum_required(VERSION 3.0)
    project(calc)
    
    # 搜索当前目录下的所有源文件,将其编译成库
    aux_source_directory(./ SRC)
    
    # 包含头文件目录(子节点可以用父节点中定义的变量,父节点用不了子节点的变)
    include_directories(${HEADPATH})
    
    # 指定静态库生成的目录
    set(LIBRARY_OUTPUT_PATH ${LIBPATH})
    
    # 源文件变成静态库
    add_library(${CALCLIB} STATIC ${SRC})
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    3.3、sort目录下的CMakeLists.txt编写

    搜索当前目录下的源文件,将其编译成库,库名是父节点中定义的,指定生成库的路径

    cmake_minimum_required(VERSION 3.0)
    project(sort)
    
    # 搜索当前目录下的所有源文件,将其编译成库
    aux_source_directory(./ SRC)
    
    # 包含头文件目录(子节点可以用父节点中定义的变量,父节点用不了子节点的变)
    include_directories(${HEADPATH})
    
    # 指定静态库生成的目录
    set(LIBRARY_OUTPUT_PATH ${LIBPATH})
    
    # 源文件变成静态库
    add_library(${SORLIB} STATIC ${SRC})
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    3.4、test1目录下的CMakeLists.txt编写

    搜索当前目录下的源文件,包含头文件路径,包含库文件路径,指定库文件,生成可执行程序路径和名字

    cmake_minimum_required(VERSION 3.0)
    project(test1)
    
    # 搜索当前目录下的所有源文件,将其编译成库
    aux_source_directory(./ SRC)
    
    # 包含头文件目录(子节点可以用父节点中定义的变量,父节点用不了子节点的变)
    include_directories(${HEADPATH})
    
    # 链接静态库
    link_libraries(${CALCLIB})
    
    # 指定静态库文件目录
    link_directories(${LIBPATH})
    
    # 指定可执行程序目录
    set(EXECUTABLE_OUTPUT_PATH ${EXECPATH})
    
    # 生成可执行程序
    add_executable(${APPNAME1} ${SRC})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    3.5、test2目录下的CMakeLists.txt编写

    搜索当前目录下的源文件,包含头文件路径,包含库文件路径,指定库文件,生成可执行程序路径和名字

    cmake_minimum_required(VERSION 3.0)
    project(test2)
    
    # 搜索当前目录下的所有源文件,将其编译成库
    aux_source_directory(./ SRC)
    
    # 包含头文件目录(子节点可以用父节点中定义的变量,父节点用不了子节点的变)
    include_directories(${HEADPATH})
    
    # 链接静态库
    link_libraries(${SORLIB})
    
    # 指定静态库文件目录
    link_directories(${LIBPATH})
    
    # 指定可执行程序目录
    set(EXECUTABLE_OUTPUT_PATH ${EXECPATH})
    
    # 生成可执行程序
    add_executable(${APPNAME2} ${SRC})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    执行完之后结果
    在这里插入图片描述

    3.6、在静态库中链接静态库

    在以上项目中, sort中调用了calc中的函数,而calc将其编译成一个静态库了,所以在将sort编译成静态库之前需要先引入calc静态库,然后将其生成静态库

    cmake_minimum_required(VERSION 3.0)
    project(sort)
    
    # 搜索当前目录下的所有源文件,将其编译成库
    aux_source_directory(./ SRC)
    
    # 包含头文件目录(子节点可以用父节点中定义的变量,父节点用不了子节点的变)
    include_directories(${HEADPATH})
    
    # 指定静态库生成的目录
    set(LIBRARY_OUTPUT_PATH ${LIBPATH})
    
    # 链接静态库
    link_libraries(${CALCLIB})
    
    # 指定静态库文件目录
    link_directories(${LIBPATH})
    
    # 源文件变成静态库
    add_library(${SORLIB} STATIC ${SRC})
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    3.7、在静态库中链接动态库

    将calc生成为动态库

    cmake_minimum_required(VERSION 3.0)
    project(calc)
    
    # 搜索当前目录下的所有源文件,将其编译成库
    aux_source_directory(./ SRC)
    
    # 包含头文件目录(子节点可以用父节点中定义的变量,父节点用不了子节点的变)
    include_directories(${HEADPATH})
    
    # 指定静态库生成的目录
    set(LIBRARY_OUTPUT_PATH ${LIBPATH})
    
    # 源文件变成静态库
    add_library(${CALCLIB} SHARED ${SRC})
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在sort中链接calc生成的动态库

    cmake_minimum_required(VERSION 3.0)
    project(sort)
    
    # 搜索当前目录下的所有源文件,将其编译成库
    aux_source_directory(./ SRC)
    
    # 包含头文件目录(子节点可以用父节点中定义的变量,父节点用不了子节点的变)
    include_directories(${HEADPATH})
    
    # 指定静态库生成的目录
    set(LIBRARY_OUTPUT_PATH ${LIBPATH})
    
    # 链接静态库
    # link_libraries(${CALCLIB})
    
    # 指定静态库文件目录
    link_directories(${LIBPATH})
    
    # 源文件变成静态库
    add_library(${SORLIB} STATIC ${SRC})
    
    # 指定链接的动态库
    target_link_libraries(${SORLIB} ${CALCLIB})
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    4、字符串拼接

    set(tmp hello world)
    set(tmp1 ${tmp} ${SRC})
    message(${tmp})
    message(${tmp1})
    
    # 向tmp字符串后面追加了三个子字符串
    list(APPEND tmp "11" "22" "33")
    message(${tmp})
    
    # 删除src里面的子字符串main.cpp
    message(${SRC})
    list(REMOVE_ITEM SRC ${PROJECT_SOURCE_DIR}/src/main.cpp)
    message(${SRC})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    4.1、自定义宏

    在软件发布之前很多地方都使用了调试信息,但是为了提高效率,在软件发布的时候要去掉这些调试信息,可以通过自定义宏来解决

    # 添加自定义宏DEBUG
    add_definitions(-DDEBUG)
    
    • 1
    • 2
    #include 
    
    int main()
    {
    #ifdef DEBUG
    	printf("hello world\n");
    #endif
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    5、使用练习

    # 1、创建文件夹
    mkdir -r cmake_test/t1
    cd cmake_test/t1
    vi main.c
    
    内容:
    #include 
    int main()
    {
    	printf(“Hello World from t1 Main!\n”); return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    CMakeLists.txt

    # cmake最低版本号要求
    cmake_minimum_required(VERSION 2.8)
    # 工程,他不是执行文件名
    project(hello-world)
    # 手动加入文件
    set(SOURCE_FILES main.c)
    
    message(STATUS "This is BINARY dir " ${PROJECT_BINARY_DIR})
    #message(SEND_ERROR "darren is error")
    message(STATUS "This is SOURCE dir " ${PROJECT_SOURCE_DIR})
    
    # 生产执行文件名hello-world hello
    add_executable(hello-world ${SOURCE_FILES})
    ADD_EXECUTABLE(hello ${SOURCE_FILES})
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    使用cmake生成makefile

    # 1、(注意命令后⾯的点号,代表本⽬录)。
    cmake . 
    # 再让我们看⼀下⽬录中的内容, 你会发现,系统⾃动⽣成了:
    # CMakeFiles, CMakeCache.txt, cmake_install.cmake 等⽂件,并且⽣成了 Makefile.
    
    # 2、生成可执行文件
    make
    # 如果你需要看到make 构建的详细过程,可以使⽤make VERBOSE=1 或者VERBOSE=1 make 命令来进⾏构建。
    
    # 3、执行
    ./hello
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
  • 相关阅读:
    2、CAS详解
    FFmpeg源代码简单分析-其他-libswscale的sws_getContext()
    【OpenCV学习】第6课:图像模糊(中值滤波,高斯双边滤波)
    微整形SDK解决方案,为企业节省大量开发成本
    第2关:节点删除与创建
    Typora免费版下载【Mac、Windows】
    JAVA中常用的10个Lambda表达式
    海量小文件传输对于企业选用文件传输软件的重要意义
    oracle学习89-oracle之基本的sql_select语句之课后练习
    《Head First HTML5 javascript》第3章 探索浏览器 知识点总结
  • 原文地址:https://blog.csdn.net/weixin_45715405/article/details/131414147