一、CMake简介

  • CMake是一个跨平台的编译工具,能够输出各种各样的makefile或者project文件。
  • CMake并不直接构建出最终的软件,而是生成标准的Makefile文件或者VisualStudio项目文件,然后再使用Make或者VisualStudio进行编译。
  • CMake在Windows平台上生成VisualStudio项目文件,在Linux和Unix平台上生成Makefile文件。
  • 二、CMake典型示例

    源代码 demo.cpp

    int main()
        return 0;
    

    cmake脚本 CMakeLists.txt

    # CMake 最低版本号要求
    cmake_minimum_required (VERSION 2.8)
    # 项目信息
    project (demo)
    # 指定生成目标
    add_executable(demo demo.cpp)
    

    Windows

  • 创建build文件夹
  • 在build文件夹中执行 cmake .. 命令
  • 用Visual Studio打开生成的demo.sln,然后编译
  • Linux

    mkdir build
    cd build
    cmake ..
    

    三、CMake常用命令

    CMake中常用的语法包含三类:

    常用命令介绍

  • cmake_minimum_required (VERSION 2.8) 要求cmake的最小版本
  • project(demo) 设置项目名字,windows下会生成demo.sln
  • 设置编译目标类型

  • add_executable 生成可执行文件
  • add_library 生成库文件
  • add_executable(demo demo.cpp)
    add_library(common util.cpp)
    

    add_library默认生成静态库,可以显式的控制生成的库的类型

    add_library(common STATIC util.cpp) #生成静态库
    add_library(common SHARED util.cpp) #生成动态库或共享库
    

    通过以上命令生成文件名字,在Windows下是:

    demo.exe
    common.lib
    common.dll
    

    在Linux下是:

    libcommon.a libcommon.so

    指定编译包含的源文件

  • 明确指定包含的源文件
  • add_executable(demo demo.cpp test.cpp util.cpp)
    
  • 搜索所有的cpp文件
  • aux_source_directory(. SRC_LIST) #搜索当前目录下的所有.cpp文件
    add_executable(demo ${SRC_LIST})
    
  • 自定义搜索规则
  • file(GLOB SRC_LIST "*.cpp" "*.cc")
    add_executable(demo ${SRC_LIST})
    
  • 包含多个文件夹的文件
  • file(GLOB SRC_LIST "*.cpp" "protocol/*.cpp")
    add_executable(demo ${SRC_LIST})
    file(GLOB SRC_LIST "*.cpp")
    file(GLOB SRC_PROTOCOL_LIST "protocol/*.cpp")
    add_executable(demo ${SRC_LIST} ${SRC_PROTOCOL_LIST})
    aux_source_directory(. SRC_LIST)
    aux_source_directory(protocol SRC_PROTOCOL_LIST)
    add_executable(demo ${SRC_LIST} ${SRC_PROTOCOL_LIST})
    
  • 通过模糊搜索的方式包含源文件,当添加新的源文件后,需要通过cmake命令重新生成Makefile或者VS项目文件
  • 设置包含目录

    include_directories(
        ${CMAKE_CURRENT_SOURCE_DIR}
        ${CMAKE_CURRENT_BINARY_DIR}
        ${CMAKE_CURRENT_SOURCE_DIR}/include
    

    Linux下还可以通过如下方式设置包含目录

    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I${CMAKE_CURRENT_SOURCE_DIR}")
    

    设置链接库搜索目录

    link_directories(
        ${CMAKE_CURRENT_SOURCE_DIR}/libs64
    

    Linux下还可以通过如下方式设置包含目录

    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_CURRENT_SOURCE_DIR}/libs64")
    

    设置需要链接的库

    根据链接库目录搜索

    target_link_libraries(demo Math)
    
  • 在Windows下,会根据链接库目录,搜索Math.lib文件
  • 在Linux下,会根据链接库目录搜索如下文件:
  • libMath.so
    libMath.a
    

    默认优先链接动态库,可以在链接时指定动态库或者静态库如:

    target_link_libraries(demo Math.a)  # 链接libMath.a
    target_link_libraries(demo Math.so) # 链接libMath.so
    

    指定全路径

    target_link_libraries(demo ${CMAKE_CURRENT_SOURCE_DIR}/libs64/libMath.a)
    target_link_libraries(demo ${CMAKE_CURRENT_SOURCE_DIR}/libs64/libMath.so)
    

    指定多个链接库

    target_link_libraries(demo 
        ${CMAKE_CURRENT_SOURCE_DIR}/libs64/libMath.a 
        boost_system.a 
        boost_thread 
        pthread)
    
  • set 直接设置变量的值
  • set(SRC_LIST main.cpp test.cpp)
    add_executable(demo ${SRC_LIST})
    
  • set 追加变量的值
  • set(SRC_LIST main.cpp)
    set(SRC_LIST ${SRC_LIST} test.cpp)
    add_executable(demo ${SRC_LIST})
    
  • list 追加或者删除变量的值
  • set(SRC_LIST main.cpp)
    list(APPEND SRC_LIST test.cpp)
    list(REMOVE_ITEM SRC_LIST main.cpp)
    add_executable(demo ${SRC_LIST})
    
  • if else elseif endif
  • if(MSVC)
        set(LINK_LIBS common)
    else()
        set(boost_thread boost_log.a boost_system.a)
    endif()
    target_link_libraries(demo ${LINK_LIBS})
    if(UNIX)
       set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fpermissive -g")
    else()
        add_definitions(-D_SCL_SECURE_NO_WARNINGS 
        -D_CRT_SECURE_NO_WARNINGS 
        -D_WIN32_WINNT=0x601 
        -D_WINSOCK_DEPRECATED_NO_WARNINGS)
    endif()
    if(${CMAKE_BUILD_TYPE} MATCHES "debug")
    else()
    endif()
    
  • while break continue foreach end_while end_foreach
  • 其他常用命令

  • message 显示一些消息
  • message(${MY_VAR})
    message("build with debug mode")
    message(WARNING "this is warnning message")
    message(FATAL_ERROR "this build has many error") # 会导致生成失败
    
  • source_group 在Windows下起作用,设置源文件在项目中的目录结构
  • file(GLOB PROTOCOL_FILES "protocol/*.cpp")
    source_group("协议" FILES ${PROTOCOL_FILES})
    
  • set_target_properties 设置项目属性
  • set_property 设置全局属性、项目属性等
  • set_property(GLOBAL PROPERTY USE_FOLDERS ON)
    #设置项目在解决方案中的目录结构,只在Windows下生效
    set_target_properties(ServerManager PROPERTIES FOLDER "manager")
    
  • include 包含其他cmake文件
  • include(./common.cmake) #指定包含文件的全路径
    include(def) #在搜索路径中搜索def.cmake文件
    set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) #设置include的搜索路径
    

    四、复杂项目示例

    多个目录,多个源文件,多个项目

    项目目录结构如下:

    ./demo
        +--- main.cc
        +--- math/
              +--- MathFunctions.cc
              +--- MathFunctions.h
    

    这种情况还可以通过add_subdirectory的方式添加子目录

    添加cmake脚本后的目录结构如下:

    ./demo
        +--- CMakeLists.txt
        +--- main.cc
        +--- math/
              +--- CMakeLists.txt
              +--- MathFunctions.cc
              +--- MathFunctions.h
    

    demo下的CMakeLists.txt文件如下:

    cmake_minimum_required (VERSION 2.8)
    project(demo)
    aux_source_directory(. DIR_SRCS)
    # 添加math子目录
    add_subdirectory(math)
    # 指定生成目标
    add_executable(demo ${DIR_SRCS})
    # 添加链接库
    target_link_libraries(demo MathFunctions)
    

    math目录中的CMakeLists.txt:

    aux_source_directory(. DIR_LIB_SRCS)
    # 生成链接库
    add_library(MathFunctions ${DIR_LIB_SRCS})
    

    五、常用变量

    cmake默认支持多种构建类型(build type),每种构建类型都有专门的编译参数变量,详情见下表

    CMAKE_BUILD_TYPE 对应的c编译选项变量 对应的c++编译选项变量

    CMAKE_BINARY_DIR,PROJECT_BINARY_DIR,_BINARY_DIR:
    这三个变量内容一致,如果是内部编译,就指的是工程的顶级目录,如果是外部编译,指的就是工程编译发生的目录。

    CMAKE_SOURCE_DIR,PROJECT_SOURCE_DIR,_SOURCE_DIR:
    这三个变量内容一致,都指的是工程的顶级目录。

    CMAKE_CURRENT_BINARY_DIR:外部编译时,指的是target目录,内部编译时,指的是顶级目录

    CMAKE_CURRENT_SOURCE_DIR:CMakeList.txt所在的目录

    CMAKE_CURRENT_LIST_DIR:CMakeList.txt的完整路径

    CMAKE_CURRENT_LIST_LINE:当前所在的行

    CMAKE_MODULE_PATH:如果工程复杂,可能需要编写一些cmake模块-,这里通过SET指定这个变量

    LIBRARY_OUTPUT_DIR,BINARY_OUTPUT_DIR:库和可执行的最终存放目录

  • 使用环境变量:$ENV
  • 写入环境变量:set(ENV{Name} value) #这里没有“$”符号
  • CMAKE_MAJOR_VERSION,CMAKE 主版本号,比如2.4.6 中的2
  • CMAKE_MINOR_VERSION,CMAKE 次版本号,比如2.4.6 中的4
  • CMAKE_PATCH_VERSION,CMAKE 补丁等级,比如2.4.6 中的6
  • CMAKE_SYSTEM ,系统名称,比如Linux-2.6.22
  • CMAKE_SYSTEM_NAME ,不包含版本的系统名,比如Linux
  • CMAKE_SYSTEM_VERSION ,系统版本,比如2.6.22
  • CMAKE_SYSTEM_PROCESSOR,处理器名称,比如i686
  • UNIX ,在所有的类UNIX平台为TRUE,包括OS X 和cygwin
  • WIN32 ,在所有的win32 平台为TRUE,包括cygwin
  • 主要开关选项

  • BUILD_SHARED_LIBS, 这个开关用来控制默认的库编译方式,如果不进行设置,使用add_library 并没有指定库类型的情况下,默认编译生成的库都是静态库。如果set(BUILD_SHARED_LIBS ON) 后,默认生成的为动态库
  • CMAKE_C_FLAGS 设置C编译选项,也可以通过指令add_definitions() 添加
  • CMAKE_CXX_FLAGS 设置C++ 编译选项,也可以通过指令add_definitions() 添加
  • 六、参考资料

  • cmake官网
  • CMake 入门实战
  • CMake使用总结
  • CMake - 优秀的 C/C++ 构建系统
  • cmake实践
  • 在 linux 下使用 CMake 构建应用程序
  • cmake函数参数解析
  •