cmake学习

979 阅读4分钟

基本命令

  • CMakeLists.txt. 保存cmake的命令,当cmake在一个目录下运行时,就会自动执行这个文件.

  • cmake .. 解析执行cmakelists配置,并生成用于告诉构建系统如何编译的文件。

  • cmake --build. 使用相应的生成器编译项目。

  • cmake --help。查看生成器名单。

  • cmake -G "MinGW Makefiles" .. 该命令可以指定生成器.注意第二个参数需要指到cmakelists所在的目录。

  • cmake .. -DCMAKE_C_COMPILER=clang-3.6 -DCMAKE_CXX_COMPILER=clang++-3.6.设置编译器.

  • cmake -H. -Bbuild. -H表示再当前目录搜索txt文件,-B表示将生成的文件放在build文件夹下.

  • cmake_minimum_required. 指定cmake的最低版本.

  cmake_minium_required(VERSION 3.5 FATAL_ERROR) # cmake版本号不满足要求则报错
  • project. 指定项目名称,在构建多个项目时,可以方便的指定一些变量.给命令会产生一个变量PROJECT_NAME.

    project(hello LANGUAGES CXX) # 命名项目名称,并表明项目使用c++语言
    
  • add_executable. 第一个参数是生成可执行文件的名称,之后跟着所有的头文化以及cpp文件,以空格分隔.一般都是将源文件添加在这个命令中,但是clion将所有头文件也添加在其中.

  • CMAKE_BINARY_DIR.指的是cmake生成文件的目录.在哪运行cmake命令那么该变量的值就是那个目录.

    • 原地生成. 直接在源代码的目录cmake.不推荐.
    • 源码外生成.一般建立build文件夹.在build中使用相对路径执行cmake命令.
  • mingw32-make.exe. windows下没有make命令.装了MinGW之后可以使用该命令.

  • 一些预置的目录变量.

    • CMAKE_SOURCE_DIR. 项目的根目录.也就是cmake执行时,首次所使用的cmakelists所在的目录(最顶层).
    • CMAKE_BINARY_DIR. 上边已经介绍.是cmake构建处理的文件的目录.同样是最顶层的build目录.
    • CMAKE_CURRENT_SOURCE_DIR.在有子项目的时候,也就是多级cmakelists,那么该变量就是cmake当前执行的层级目录.
    • PROJECT_SOURCE_DIR. 我的理解上同上一个一致.
    • CMAKE_CURRENT_BINARY_DIR.当前层级的构建目录.
    • PROJECT_BINARY_DIR.同上.
    • PROJECT_NAME.project命令指定的.
    • name_SOURCE_DIR/name_BINARY_DIR.
  • set(SOURCES src/h.cpp). 使用变量的形式去添加源文件.modern cmake让避免这么使用.

  • file(GLOB SOURCES "src/*.cpp"). 使用匹配的方式添加.官方文档建议不要使用。

  • target_include_directories(target PRIVATE ${PROJECT_SOURCE_DIR}/indclude). 引入需要被包含的文件,应该指的是头文件. 即使用此命令指定了一个目录后,那么在代码中就可以使用这个目录下的头文件。

    • PRIVATE. 目录只被添加到指定target的包含目录.
    • INTERFACE.所有连接这个库的target,都会包含这个目录.但是库本身不包含.
    • public. 库本身也包含.对于公共的头文件.最好能在其外出包一个文件夹,起到命名空间的作用.
  • target_link_libraries( hello_binary PRIVATE hello_library). 将库连接到可执行文件.并且将具有public和interface的包含目录也传播到可执行文件.

  • add_library(target STATIC src/Helo.cpp).相对于add_executable,创建一个静态的链接库.

  • add_library(hello_library SHARED src/Hello.cpp). SHARED表示创建动态链接库.

  • add_library(hello::library ALIAS hello_library).alias target 作用是给连接库起一个别名,在连接的时候可以使用别名.

  • cmake .. -DCMAKE_INSTALL_PREFIX=/install/location. 指定程序的安装目录.

  • make install.作用是将编译后的文件:可执行文件/库文件/头文件/其他的任意文件.移动到指定的目录.起到安装的作用.

# Binaries
install (TARGETS cmake_examples_inst_bin
    DESTINATION bin)

# Library
# Note: may not work on windows
install (TARGETS cmake_examples_inst
    LIBRARY DESTINATION lib)

# Header files
install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/ 
    DESTINATION include)

# Config
install (FILES cmake-examples.conf
    DESTINATION etc)
  • cmake .. -DCMAKE_BUILD_TYPE=Release. 设置cmake的打包类型, 例如debug/releas/RelWithDebInfo 等.

  • 默认情况下,cmake不会是定任何用于优化的选项.

  • CMAKE_MODULE_PATH.在使用find_package时cmake会在该变量列出来的路径上去找FindXXX.cmake.

  • find_package(Boost 1.46.1 REQUIRED COMPONENTS filesystem system).表示寻找boost且版本大于1.46.1,required表示该库时必须的,没有找到就报错,components库中需要包括的模块.具体库叫什么,这些都是和库本身的FindXXX.cmake相关.一般找到后,会有XXX_FOUND的变量.同时也会导出一些变量,例如Boost_INCLUDE_DIRS.

  • add_subdirectory(sublibrary1).引入包含cmakelists的子目录.

使用场景

cmake与构建系统

什么是构建系统?例如:Unix Makefile、Ninja、Visual Studio等等就是构建系统。前两者是命令行的构建工具,而后者是集成开发环境。

什么是cmake?cmake就是构建系统的生成器,通过对cmakelists的执行,能够生成各种构建系统的文件。

include_directories vs target_include_directories

include_directories: 会让其后声明的所有子模块中都包含其引入的包含路径。

target_include_directories:只会在当前的target上引入。

另外set_target_properties中利用PUBLIC_HEADER也可以达到设置包含路径的目的。

理解 private public interface

zhuanlan.zhihu.com/p/82244559

cmake-test/                 工程主目录,main.c 调用 libhello-world.so
├── CMakeLists.txt
├── hello-world             生成 libhello-world.so,调用 libhello.so 和 libworld.so
│   ├── CMakeLists.txt
│   ├── hello               生成 libhello.so 
│   │   ├── CMakeLists.txt
│   │   ├── hello.c
│   │   └── hello.h         libhello.so 对外的头文件
│   ├── hello_world.c
│   ├── hello_world.h       libhello-world.so 对外的头文件
│   └── world               生成 libworld.so
│       ├── CMakeLists.txt
│       ├── world.c
│       └── world.h         libworld.so 对外的头文件
└── main.c

PRIVATE:私有的。生成 libhello-world.so时,只在 hello_world.c 中包含了 hello.h,libhello-world.so 对外的头文件——hello_world.h 中不包含 hello.h。而且 main.c 不会调用 hello.c 中的函数,或者说 main.c 不知道 hello.c 的存在,那么在 hello-world/CMakeLists.txt 中应该写入:

target_link_libraries(hello-world PRIVATE hello)
target_include_directories(hello-world PRIVATE hello)

INTERFACE:接口。生成 libhello-world.so 时,只在libhello-world.so 对外的头文件——hello_world.h 中包含 了 hello.h, hello_world.c 中不包含 hello.h,即 libhello-world.so 不使用 libhello.so 提供的功能,只使用 hello.h 中的某些信息,比如结构体。但是 main.c 需要使用 libhello.so 中的功能。那么在 hello-world/CMakeLists.txt 中应该写入:

target_link_libraries(hello-world INTERFACE hello)
target_include_directories(hello-world INTERFACE hello)

PUBLIC:公开的。PUBLIC = PRIVATE + INTERFACE。生成 libhello-world.so 时,在 hello_world.c 和 hello_world.h 中都包含了 hello.h。并且 main.c 中也需要使用 libhello.so 提供的功能。那么在 hello-world/CMakeLists.txt 中应该写入:

target_link_libraries(hello-world PUBLIC hello)
target_include_directories(hello-world PUBLIC hello)

需不需要将头文件加入到 add_library/add_executable

Why add header files into ADD_LIBRARY/ADD_EXECUTABLE command in CMake

如果不加也可以正常运行,但是生成基于IDE的项目时,头文件不会再IDE中显示。

add_library操作/生成不同类型的库

add_library 的第二个参数表示要生成或者操作库的类型。其取值有:STATIC/SHARED/OBJECT/MODULE以及IMPORETED/INTERFACE/ALIAS。前四种是会生成的库,而后三种则不会在构建系统中产出。

OBJECT:表示add_liabray中的源码不生成静态或者动态库,而是生成中间态的东西,可以用它去继续生成静态后者动态库,对于同时生成静态和动态库很有用。

$<TARGET_OBJECTS:message-objs> 为生成器表达式语法。

add_library(message-shared
  SHARED
    $<TARGET_OBJECTS:message-objs>
    )
# 使用set_target_properties设置输出的名称
set_target_properties(message-shared
    PROPERTIES
        OUTPUT_NAME "message"
    )
add_library(message-static
    STATIC
        $<TARGET_OBJECTS:message-objs>
    )
set_target_properties(message-static
    PROPERTIES
        OUTPUT_NAME "message"
    )