CMake学习笔记(待补充)

CMake概述

不同平台编译的问题

GNU Make,MS nmake,这些Make工具遵循着不同的规范和标准,所执行的Makefile格式千差万别。为了跨平台生成Makefile,CMake应时而生。

Cmake组成

CMake是一个构建系统,始于1999年,其开发公司Kitware设计其的目的是提供一组工具,实现在不同平台上配置、构建、测试和部署项目。
CMake软件工具集,主要包括:

  • CMake:对可执行文件和库的构建
  • CTest:测试相关
  • CPack:打包
  • CDash:测试结果面板展示

这些软件工具集构成的项目时序图如下所示:
在这里插入图片描述

CMake的使用分为两部分:

  • 程序管理:书写CMakeList.txt这个文件
  • 程序构建:通过CMake生成makefile等平台支持的构建文件
    在linux平台下使用CMake生成Makefile并编译的流程如下:
    在这里插入图片描述

CMake源文件组织结构

由如下三部分组成

  1. 目录(CMakeLists.txt),编译一个项目时的入口点就是CMakeLists.txt,可以由add_subdirectory()增添子目录,每个子目录也要有CMakeLists.txt,以此构成层级的源码树,编译时也会产生相应的构建树。
  2. 脚本(< Scripts>.cmake) 由cmake命令行工具直接运行(-P),不产生构建文件
  3. 模块(< module>.cmake)目录和脚本可以使用include()命令包含模块

CMake构建系统

CMake构建系统是一系列高级别的逻辑目标(targets)组成的,这些构建目标有如下三种:

  1. 可执行文件
  2. 包含特定命令的自定义目标

源文件语法

所有的源文件都是由命令调用组成的

基本概念

  • 命令:即CMake中的函数,大部分情况下是设置“变量”,大小不敏感,形式为 name ( arguments)例如add_executable(hello world.c)
  • 命令参数:如下三种
 - 方括号参数,一般作为一个整体参数,如message([=[This is bracket argument]=])
 - 引用参数,一般作为一个参数,如message(“This is a quoted argument”)
 - 非引用参数,作为多个参数,foreach(arg
    NoSpace
    Escaped\ Space
    This;Divides;Into;Five;Arguments
    Escaped\;Semicolon
    )
  message("${arg}")
endforeach()
  • 变量引用
  ${ < variable > }
  $ENV(< variable>)
  $CACHE{< variable>}
  • 注释,以#开头,包括如下三种
    中括号注释,如#[[ xxx
    xxxxx]]
    行注释,以#开头的一行

控制结构

  1. 条件:if()/elseif()/else()/endif()
  2. 循环:foreach()/endforeach()
  3. 宏:macro()/endmacro()
  4. 函数:function()/endfunction()

变量

  1. 存储空间的基本单位
  2. set() unset()命令来设置或者销毁变量
  3. 大小写敏感
  4. 有四种范围,函数变量,目录变量和持久缓存变量,环境变量

构建系统语法

二进制目标

可执行文件和库分别使用add_executable()和add_library()命令来定义,它们之间的依赖由target_link_libraries()来指明

#将archive.cpp zip.cpp lzma.cpp打包为archive静态库
#默认情况下定位一个STATIC库,除非使用SHARED显示声明
add_library(archive archive.cpp zip.cpp lzma.cpp)
#将zipapp.cpp定义为可执行文件
add_executable(zipapp zipapp.cpp)
#生成zipapp可执行文件时,需要用archive库
target_link_libraries(zipapp archive)

Object Libraries

Object Libraries定义了给定源文件编译后输出的非存档目标文件集合,其可用于其他目标的源文件输入,也可以被连接到其他目标

add_library(archive OBJECT archive.cpp zip.cpp lzma.cpp)

add_library(archiveExtras STATIC $<TARGET_OBJECTS:archive> extras.cpp)

add_executable(test_exe $<TARGET_OBJECTS:archive> test.cpp)

add_library(archive OBJECT archive.cpp zip.cpp lzma.cpp)

add_library(archiveExtras STATIC extras.cpp)
target_link_libraries(archiveExtras PUBLIC archive)

add_executable(test_exe test.cpp)
target_link_libraries(test_exe archive)

个性化构建和使用需求

target_include_directories(),target_compile_definitions()和target_compile_options()命令用来满足个性化构建和使用需求,他们分别用来设置目标的

  • INCLUDE_DIRECTORIES
  • COMPILE_DEFINATIONS
  • COMPILE_OPTIONS
    它们也有PRIVATE,PUBLIC和INTERFACE三种模式,被用来控制这些目标属性对依赖项的传递。
    通常,如果依赖项只在库的实现中使用,而不是在头文件中使用,则应该在target_link_libraries()中指定PRIVATE关键字。如果在库的头文件中额外使用了一个依赖项(例如用于类继承),那么它应该被指定为PUBLIC依赖项。一个不被库的实现使用,而只被它的头文件使用的依赖应该被指定为一个接口依赖。
target_compile_definitions(archive
  PRIVATE BUILDING_WITH_LZMA
  INTERFACE USING_ARCHIVE_LIB
)

因为通常需要源目录和构建目录被添加到INCLUDE_DERECTORIES,可以启用CMAKE_INCLUDE_CURRENT_DIR变量,方便地将相应的目录添加到所有目标的INCLUDE_DIRECTORIES中

简单实例

  1. 最基本的CMakeLists.txt应该包含的东西
# 设置最低CMake版本
cmake_minimum_required(VERSION 3.5)
#      工程名称 版本号      使用的语言(c++project(test VERSION 0.1 LANGUAGES CXX)
# 非必须,设置工程包含当前目录
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
# 设置c++标准为c++11,且强制c++标准
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 查找qt5中的库文件,REQUIRED表示为必须的
find_package(QT5 COMPONENTS Widgets REQUIRED)

# 设置自定义变量,将四个文件设置为PROJECT_SOURCES变量
set(PROJECT_SOURCES
		main.cpp
		widget.cpp
		widget.h
		widget.ui
)

#添加一个可执行文件,名字是test
add_executable(test
	${PROJECT_SOURCES}
)

# test这个可执行文件需要一个第三方库,即Qt5中的Widgets
target_link_libraries(test 	Qt5::Widgets)
  1. 库文件怎么搞?

# 这是一个名为add的库文件
cmake_minimum_required(VERSION 3.14)

project(add LANGUAGES CXX)

set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

#写两个是因为要兼容QT5和QT6
find_package(QT NAMES QT6 QT5 COMPONETNTS Core REQUIRED)
find_package(QT${QT_VERSION_MAJOR} COMPONENTS Core REQUIRED)

#与add_executable不同,此处library明显告诉是在生成一个库文件
add_library(add SHARED
	add_global.h
	add.cpp
	add.h
)

target_link_libraries(add PRIVATE Qt${QT_VERSION_MAJOR}::core)

#额外的宏定义   在库add中增加ADD_LIBRARY
target_compile_definitions(add PRIVATE ADD_LIBRARY)

在库中增加宏定义的原因是因为一些头文件有一些条件宏定义,如下

#add_globar.h

#ifndef ADD_GLOBAL_H
#define ADD_GLOBAL_H

#include<QtCore/qglobar.h>
#if defined(ADD_LIBRARY)
#	define ADD_EXPORT Q_DECL_EXPORT
#else
# define ADD_EXPORT Q_DECL_IMPORT
#endif

#endif 

如上有一个自定义的库,但由于不是包,不能使用find_package命令,不必须通过如下方式引用

cmake_minimum_required(VERSION 3.5)
project(test VERSION 0.1 LANGUAGES CXX)

set(CMAKE_INCLUDE_CURRENT_DIR ON)

set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_compile_options(-finput-charset=GBK)

find_package(Qt5 COMPONENTS Widgets REQUIRED)

set(PROJECT_SOURCES
		main.cpp
		widget.cpp
		widget.h
		widget.ui
)
# 告诉编译器头文件要去哪里找
#${CMAKE_CURRENT_SOURCE_DIR}就是指当前CMakeLists.txt所在目录
include_directories(
	${CMAKE_CURRENT_SOURCE_DIR}/../
)
# 告诉编译器库文件本身要去哪里找
link_directories(
	${CMKAE_CURRENT_SOURCE_DIR}/../build-add-unknown-Debug/
)

add_executable(test
	${PROJECT_SOURCES}
)

target_link_libraries(test Qt5::Widgets)
# 指定要加载的libadd.so库即可
target_link_libraries(test libadd.so)
  1. 多库之间存在依赖问题
    如上有两个程序,一个可执行文件test和一个库add,且test依赖于add
    在这里插入图片描述
    如何考虑这种情况的编译
cmake_minimum_required(VERSION 3.14)

project(ProgramOne Languages CXX)
set(CMAKE_INCLUDE_CURRENT_DIR ON)

set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
#通过add_subdirectory实现多程序管理
add_subdirectory(add)
add_subdirectory(test)

可以看到不管是根目录还是子目录,都重复出现了变量的设置。但父目录设置的一些变量会被应用于子目录,对于target_开头的命令其不会被子目录共享(因此其只适用于子对象)。

  1. make install
    为了使所需文件不分散放置在各个源代码目录中,通常希望自动将编译好的文件放置在一个统一的目录中,可以
#通过FILES指定要安装哪些头文件,通过DESTINATION指定文件要放置的目录,如果不使用DESTINATION,则默认安装到默认目录相对于(CMAKE_INSTALL_PREFIX的lib目录)
install(FILES add_global.h add.h
		DESTINATION include/add
		)
#通过TARGEST指定要安装生成的库文件
install(TARGETS add
		)
#不使用DESTINATION则表示安装到默认目录(相对于CMAKE_INSTALL_PREFIX的bin目录)
install(TARGETS test
)
#根据文件的类型指定放置的位置
install(TARGET add
	RUNTIME DESTINATION bin
	LIBRARY DESTINATION lib
	ARCHIVE DESTINATION lib
)
  1. target_开头命令
    target_开头命令不是一个全局命令,是一个只针对当前当前特定编译目标的设置
target_include_directories(test PUBLIC
	${CMAKE_CURRENT_SOURCE_DIR}/../
)
target_compile_options(test PRIVATE -finput-charset=GBK)

补充的一些函数

# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_SRCS 变量
aux_source_directory(. DIR_SRCS)

   |
    +--- main.cc
    |
    +--- MathFunctions.cc
    |
    +--- MathFunctions.h

#法一
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)

# 项目信息
project (Demo2)

# 指定生成目标
add_executable(Demo main.cc MathFunctions.cc)

#法二
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)

# 项目信息
project (Demo2)

# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_SRCS 变量
aux_source_directory(. DIR_SRCS)

# 指定生成目标
add_executable(Demo ${DIR_SRCS})

find_package

目的:用来查找外部包

查找的两种方式:

  1. Module mode:寻找Find< PackageName>.cmake
  2. Config mode:寻找< lowercasePackageName>-config[-version].cmake 或者< PackageName> Config.cmake,相较于Module mode,其查找方式更加复杂

基本签名

find_package(<PackageName> [version] [EXACT] [QUIET] [MODULE]
             [REQUIRED] [[COMPONENTS] [components...]]
             [OPTIONAL_COMPONENTS components...]
             [NO_POLICY_SCOPE])

该基本签名适用于Module和Config两种模式,< PackageName>_FOUND变量的会被自动生设置以表明包被是否找到

全签名

find_package(<PackageName> [version] [EXACT] [QUIET]
             [REQUIRED] [[COMPONENTS] [components...]]
             [OPTIONAL_COMPONENTS components...]
             [CONFIG|NO_MODULE]
             [NO_POLICY_SCOPE]
             [NAMES name1 [name2 ...]]
             [CONFIGS config1 [config2 ...]]
             [HINTS path1 [path2 ... ]]
             [PATHS path1 [path2 ... ]]
             [PATH_SUFFIXES suffix1 [suffix2 ...]]
             [NO_DEFAULT_PATH]
             [NO_PACKAGE_ROOT_PATH]
             [NO_CMAKE_PATH]
             [NO_CMAKE_ENVIRONMENT_PATH]
             [NO_SYSTEM_ENVIRONMENT_PATH]
             [NO_CMAKE_PACKAGE_REGISTRY]
             [NO_CMAKE_BUILDS_PATH] # Deprecated; does nothing.
             [NO_CMAKE_SYSTEM_PATH]
             [NO_CMAKE_SYSTEM_PACKAGE_REGISTRY]
             [CMAKE_FIND_ROOT_PATH_BOTH |
              ONLY_CMAKE_FIND_ROOT_PATH |
              NO_CMAKE_FIND_ROOT_PATH])

配置模式试图去定位目标包< PackageName>_DIR目录中的配置文件< PackageName>Config.cmake 或者< lowercasePackageName>-config.cmake

target_include_directories

target_include_directories(hello_library
    PUBLIC
        ${PROJECT_SOURCE_DIR}/include
)
  • PRIVATE - the directory is added to this target’s include directories

  • INTERFACE - the directory is added to the include directories for any targets that link this library.

  • PUBLIC - As above, it is included in this library and also any targets that link this library.

Set

set命令可以设置普通变量、缓存条目、环境变量三种变量的值

  1. 普通变量
  • 命令格式:set( … [PARENT_SCOPE])
  • 命令含义:将变量variable设置为值…,变量variable的作用域为调用set命令的函数或者当前目录,如果使用了PARENT_SCOPE选项,意味着该变量的作用域会传递到上一层(也就是上一层目录或者当前函数的调用者,如果是函数则传递到函数的调用者,如果是目录则传递到上一层目录),并且在当前作用域该变量不受带PARENT_SCOPE选项的set命令的影响(如果变量之前没有定义,那么在当前作用域仍然是无定义的;如果之前有定义值,那么值和之前定义的值保持一致)。
  1. 缓存条目
  • 命令格式:set( … CACHE [FORCE])
  • 命令含义:将缓存条目variable设置为值…,除非用户进行设置或使用了选项FORCE,默认情况下缓存条目的值不会被覆盖。缓存条目可以通过CMAKE的GUI界面的add entry按钮来增加。缓存条目的实质为可以跨层级进行传递的变量,类似于全局变量。
    缓存条目的主要有以下几类:
    BOOL:布尔值ON/OFF,CMAKE的GUI界面对此类缓存条目会提供一个复选框。
    FILEPATH:文件路径,CMAKE的GUI界面对此类缓存条目会提供一个文件选择框。
    PATH:目录路径,CMAKE的GUI界面对此类缓存条目会提供一个目录选择框。
    STRING / STRINGS:文本行,CMAKE的GUI界面对此类缓存条目会提供一个文本框(对应STRING)或下拉选择框(对应STRINGS)。
    INTERNAL:文本行,但是只用于内部,不对外呈现。主要用于运行过程中存储变量,因此使用该type意味着使用FORCE。
  1. 环境变量
  • 命令格式:set(ENV{} [])
  • 命令含义:将环境变量设置为值(注意没有…),接着使用$ENV{}会得到新的值。cmake中的环境变量可以参考:环境变量。
      环境变量设置的几个注意事项:
    1)该命令设置的环境变量只在当前的cmake进程生效,既不会影响调用者的环境变量,也不会影响系统环境变量。
    2)如果值为空或者ENV{}后没有参数,则该命令会清除掉当前环境变量的值。
    3)后的参数会被忽略。

get_property

get_property(
<GLOBAL |
DIRECTORY [dir] |
TARGET |
SOURCE |
TEST |
CACHE |
VARIABLE>
PROPERTY
[SET | DEFINED | BRIEF_DOCS | FULL_DOCS])
 获取在某个域中一个对象的某种属性值。第一个参数指定了存储属性值的变量。第二个参数确定了获取该属性的域。域的选项仅限于:

GLOBAL 域是唯一的,它不接受域名字。
DIRECTORY域默认为当前目录,但是其他的路径(已经被CMake处理过)可以以相对路径或完整路径的方式跟在该域后面。
TARGET域后面必须跟有一个已有的目标名。
SOURCE域后面必须跟有一个源文件名。
TEST域后面必须跟有一个已有的测试。
CACHE域后面必须跟有一个cache条目。
VARIABLE域是唯一的,它不接受域名字。

add_custom_target

在很多时候,经常需要在cmake中创建一些目标,如clean、copy,这就需要通过add_custom_target来指定。同时,add_custom_command可以用来完成对add_custom_target生成的target的补充

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

add_custom_command

  1. 生成文件
    在这里插入图片描述

在这里插入图片描述
2. 构建事件
为某一个目标添加一个定制命令
在这里插入图片描述

cmake_minimum_required(VERSION 3.0)
project(test)

add_custom_target(CopyTask)

add_custom_command(TARGET CopyTask
  POST_BUILD
  COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/config ${CMAKE_CURRENT_SOURCE_DIR}/etc
  COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/log.txt ${CMAKE_CURRENT_SOURCE_DIR}/etc
  )


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值