CMake学习心得

  不知道抽什么风,突然想起要学学CMake了,不学不知道,一学吓一跳,CMake功能真心强大,有各种寻找库包的支持,而且适合对跨平台打包

CMake Variables

1. cmake_source_dir vs project_source_dir

  可以首先看看stackoverflow上的一个回答, 这两个变量有些细微的区别,cmake_source_dir的确指向CMakeLists.txt被定义的那个顶层目录,而project_source_dir指向的是CMakeLists.txt含有的那个最近的project()命令的目录
  举个例子说吧,比如有一个顶层项目,名称为Outer,它含有一个子目录,该子目录有它自己的项目Inner,Outer的CMakeLists.txt内容为

project(Outer)
add_subdirectory(Inner)

Inner的CMakeLists.txt内容为

project(Inner)

  这两个CMakeLists文件中,两个CMakeLists.txt的cmake_source_dir都指向Outer的源目录,但Outer的project_source_dir也指向Outer的源目录,Inner的project_source_dir指向Inner的源目录(含有project()), 这两个变量的区别与其他PROJECT_<var>CMAKE_<var> 一致.

2. find_package

  在CMake官网浏览时才发现,原来CMake支持多个库包的搜索,再也不用为这些库包做些额外的include操作了,比如FindOpenGL、FindOpenCL、FindVulkan等,可以在官网cmake-modules查看,或者你可以通过执行命令

cmake --help-module-list

查看,在Ubuntu上一般都在/usr/local/share/cmake/Modules目录
  具体使用可以见CMake-Wiki-How to find library, 这里做简单介绍

2.1 如何使用CMake官方支持find的库

  先考虑一个例子,现在要用bzip2库,编译器需要知道bzlib.h文件在哪,链接器需要知道bzip2库在哪(假如是动态链接,windows平台会找libbz2.dll,Unix平台会找libbz2.so),如何去Find呢?下面是CMakeLists.txt内容

cmake_minimum_required(VERSION 2.8)
project(helloworld)
add_executable(helloworld hello.c)
find_package (BZip2)
if (BZIP2_FOUND)
  include_directories(${BZIP_INCLUDE_DIRS})
  target_link_libraries (helloworld ${BZIP2_LIBRARIES})
endif (BZIP2_FOUND)

  通过include_directoriestarget_link_libraries的使用,就可以来查系统上的bzlib.h及bzip2库了

2.2 如何使用CMake官方暂时不支持find的库

  假设你想使用LibXML++库,但CMake没有一个libXML++的find模块,但你在网上google到一个文件FindLibXML++.cmake, 在CMakeLists.txt里可以写

find_package(LibXML++ REQUIRED)
include_directories(${LibXML++_INCLUDE_DIRS})
set(LIBS ${LIBS} ${LibXML++_LIBRARIES})

  如果这个包可选, 你可以省略REQUIRED关键字,可以通过boolean变量LibXML++_FOUND看是否有被found, 然后

target_link_libraries(exampleProgram ${LIBS})

  为了让这起作用,还要将FindLibXML++.cmake文件放在CMake模块路径下,
当前的CMake那边走不通,自然需要我们这边在project里设置路径,在project根目录,创建一个路径为cmake/Modules/文件夹,而后把FindLibXML++.cmake放在该文件夹下,根目录的CMakeLists.txt里包含如下代码

set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")

这样CMake将自动的去find.

3. find_path & find_library

分别用来寻找头文件和库文件的路径

4. source_group

  之前遇到用CMake构建VS项目,得到的VS项目缺少头文件列表信息,这个命令可以用来干这事,参考Listing header files in Visual Studio C++ project generated by cmake, 官方对该命令source_group解释

5. file

  该命令可以对文件做许多操作,如重命名、移除、新建目录, 例如,使用正则表达式将同后缀的文件名打包

file(GLOB MODULES_HEADER "${CMAKE_SOURCE_DIR}/*.h" "${CMAKE_SOURCE_DIR}/vulkan/*.h")

小功能

CMake保证项目目录结构

通过source_group的应用,保证源目录及头文件目录,均显示在生成的目录结构中

set(ALL_FILES
  src/SomeClass.cpp
  include/SomeClass.h
  ...)

add_library(MyLibrary ${ALL_FILES})

foreach(FILE ${ALL_FILES}) 
  get_filename_component(PARENT_DIR "${FILE}" PATH)

  # skip src or include and changes /'s to \\'s
  string(REGEX REPLACE "(\\./)?(src|include)/?" "" GROUP "${PARENT_DIR}")
  string(REPLACE "/" "\\" GROUP "${GROUP}")

  # group into "Source Files" and "Header Files"
  if ("${FILE}" MATCHES ".*\\.cpp")
    set(GROUP "Source Files\\${GROUP}")
  elseif("${FILE}" MATCHES ".*\\.h")
    set(GROUP "Header Files\\${GROUP}")
  endif()

  source_group("${GROUP}" FILES "${FILE}")
endforeach()

set_property

set_property()在特定作用于下设置多种属性

exclude file or folder

解决方案

aux_source_directory(src _srcFiles)
list(REMOVE_ITEM _srcFiles "src/f4.cpp")

推荐阅读

  • 递归添加子目录