本节内容的学习:基本掌握CMakeLists.txt文件的编写以及Makefile文件的构建

简介

CMake是一个跨平台的管理源码构建的工具。

安装

  1. 在ubantu上使用apt install cmake命令进行安装
  2. 安装后使用cmake -version查看cmake的版本号

使用CMake构建软件包

前提:发布的软件源码使用CMake进行管理,使用CMake进行管理的项目通常在项目根目录下都有CMakeLists.txt这个文件

  1. 使用CMake user interaction tool进行构建软件包的步骤如下:
cd 源码根目录
mkdir build
cd build
cmake .. -DCMAKE_INSTALL_PREFIX=安装的目标位置
# --build flag enables a particular mode of operation for the cmake tool
cmake --build .
# 安装
cmake --build . --target install
  1. 使用CMake user interaction tool构建时,可以传递一些变量
    1. CMAKE_PREFIX_PATH:指定搜索依赖包的路径。通常一个项目依赖的同一个包在系统中存在多个,需要显示指定。
    2. CMAKE_MODULE_PATH:指定搜索CMake modules的路径
    3. CMAKE_BUILD_TYPE:Build configuration, such as Debug or Release
    4. CMAKE_INSTALL_PREFIX:指定安装软件的位置
    5. BUILD_SHARED_LIBS:其值有ON和OFF。当使用add_library没有指定构建的类型时,这个选项用于标识是否构建shared library

CMake的基本语法

1.PROJECT关键字
  1. 可以用来指定工程的名称、支持的语言、工程的版本号。如果不指定语言,默认是支持所有语言的
    1. PROJECT(SERVER):指定了工程的名称,并支持所有语言
    2. PROJECT(SERVER CXX):指定了工程的名字,并且支持语言是C++
    3. PROJECT(SERVER CXX C):指定了工程的名字,并且支持语言是C++和C
2.SET关键字
  1. 可以用来为变量设置值,例如:SET(SRC_LIST main.cpp),SRC_LIST变量的值就包括main.cpp
3.MESSAGE关键字
  1. 可以用来向终端用户输出自定义的信息。主要包括三种信息:
    1. SEND_ERROR:产生错误,生成过程被跳过。
    2. SATUS:输出前缀为--的信息。
    3. FATAL_ERROR:将会终止CMake构建过程
4.ADD_EXECUTABLE关键字
  1. 用于指定生成可执行文件的名称,例如:ADD_EXECUTABLE(server ${SRC_LIST})
5.ADD_SUBDIRECTORY关键字
  1. 用于向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置
  2. 语法格式为:ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
    1. source_dir:指定向当前工程添加存放源文件的子目录
    2. binary_dir:指定CMake构建过程中中间二进制和目标二进制存放的位置
  3. 使用示例:
    1. 项目目录为:

    2. 项目根目录下的CMakeLists.txt内容为:

    project(test2)
    # 向当前工程加入src目录,并指定构建的输出目录为bin(如果在test2/build目录下执行cmake ..)
    # 则输出目录为build/bin
    add_subdirectory(src bin)
    
    1. 源码目录src下的CMakeLists.txt内容为:
    set(src_list main.cpp)
    add_executable(target ${src_list})
    
    1. 在build目录下执行cmake ..,即可在build目录下产生Makefile
6.ADD_LIBRARY关键字
  1. 用来构建动态库或者静态库。
  2. 语法示例:ADD_LIBRARY(库名称 SHARED|STATIC ${LIBHELLO_SRC})
    1. SHARED:指定构建的是动态库
    2. STATIC:指定构建的是静态库
    3. ${LIBHELLO_SRC}指定构建库所需要的源文件
7.INSTALL关键字
  1. 使用INSTALL关键字可以用来安装二进制文件、动态库、静态库、文件、目录、脚本等
  2. CMAKE中预定义的变量:CMAKE_INSTALL_PREFIX,其值默认为 /usr/local/
  3. 该指令的使用示例:
    1. 项目目录如下:

    2. 项目根目录下的CMakeLists.txt内容为:

    project(test3)
    add_subdirectory(src bin)
    # 安装文件COPYRIGHT和README
    install(FILES COPYRIGHT README DESTINATION share/cmake/)
    # 安装脚本test3.sh
    install(PROGRAMS test3.sh DESTINATION share/bin)
    # 安装目录,安装doc目录下的所有内容
    install(DIRECTORY doc/ DESTINATION share/doc)
    
    
    1. 在build目录下执行cmake ..即可在build目录下产生Makefile
    2. make
    3. 安装:make install,即可在${CMAKE_INSTALL_PREFIX}/<DESTINATION 定义的路径>查看安装的内容
  1. 用来添加需要链接的共享库
9.CMake中的变量
  1. CMake中预定义了很多变量,比如说:
    1. PROJECT_BINARY_DIR:使用CMake构建输出的目录
    2. PROJECT_SOURCE_DIR:项目源码目录
  2. 变量取值:使用${变量名称}
  3. 指令的参数之间使用空格或者分号分隔,例如
add_executable(fileserver ${net_srcs} ${fileserver_srcs} ${utils_srcs})
  1. 指令名称大小写无关,例如ADD_EXECUTABLE和add_executable都行的

简单示例

1.CMake构建静态库和动态库
  1. 静态库和动态库的区别:
    1. 静态库的扩展名一般为.a或者.lib,动态库的扩展名一般为.dll或者.so。
    2. 静态库在编译时会直接整合到目标程序中,编译成功的可执行文件可独立运行
    3. 动态库在编译时不会放到目标程序中,即可执行文件无法单独运行。它需要在运行时进行链接动态库
  2. 使用CMAKE单独构建静态库或者动态库示例:
    1. 项目目录如下:

    2. 项目根目录下的CMakeLists.txt内容为:

    project(test4)
    add_subdirectory(lib bin)
    
    1. 源码目录src下的CMakeLists.txt内容为:
    set(libhello_src hello.cpp)
    add_library(hello SHARED ${libhello_src})
    
    1. 在build目录下执行cmake ..即可在build目录下产生Makefile
    2. 执行make即可产生动态库libhello.so
  3. 静态库和动态库的同时构建
set(libhello_src hello.cpp)
# 构建静态库
add_library(hello_static STATIC ${libhello_src})
# 对hello_static进行重命名为hello
set_target_properties(hello_static PROPERTIES OUTPUT_NAME "hello")
# 清理掉hello_static这个库
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. 安装共享库和头文件:示例将hello这个共享库安装到${CMAKE_INSTALL_PREFIX}/lib目录下,将头文件hello.h安装到${CMAKE_INSTALL_PREFIX}/include/hello目录下,完整示例如下:

    1. 项目目录如下:

    2. 项目根目录下的CMakeLists.txt内容为:

    project(test4)
    add_subdirectory(lib bin)
    
    1. 源码目录src下的CMakeLists.txt内容为:
    set(libhello_src hello.cpp)
    # 构建静态库
    add_library(hello_static STATIC ${libhello_src})
    # 对hello_static进行重命名为hello
    set_target_properties(hello_static PROPERTIES OUTPUT_NAME "hello")
    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)
    
    # 将头文件hello.h放到${CMAKE_INSTALL_PREFIX}/include/hello目录下
    install(FILES hello.h DESTINATION include/hello)
    # 二进制、静态库、动态库安装的第一个参数都使用TARGETS
    # ARCHIVE指的是静态库
    # LIBRARY指的是动态库
    install(TARGETS hello hello_static LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)
    
    1. 在build目录下执行cmake ..即可在build目录下产生Makefile
    2. 执行make即可产生动态库libhello.so和静态库libhello.a
    3. 安装:
  2. 共享库和头文件的使用,示例如下

    1. 项目目录如下:

    2. main.cpp内容如下:

    #include <hello.h>
    
    int main() {
        // 调用动态库中的函数
        func();
    }
    
    1. 项目根目录下的CMakeLists.txt内容为:
    project(test5)
    # 加入头文件搜索路径
    include_directories(/usr/local/include/hello)
    # 添加非标准的共享库搜索路径
    link_directories(/usr/local/lib)
    add_subdirectory(src bin)
    
    1. 源码目录src下的CMakeLists.txt内容为:
    set(src_list main.cpp)
    add_executable(target ${src_list})
    # 添加需要链接的共享库
    target_link_libraries(target libhello.so)
    
    1. 在build目录下执行cmake ..即可在build目录下产生Makefile
    2. 执行make即可生成目标可执行文件
    3. 查看可执行文件所依赖的共享库列表:
    # ldd全称list dynamic dependencies
    ldd target
    

参考:参考