CMake是一个开源跨平台的自动化构建工具
第一章、CMake语法
1.1、打印——message
message("hello")
message([[hello]])
1.2、变量操作set、list
- CMake的变量分为CMake提供与自定义两种
- CMake变量的命名区分大小写
- CMake中的变量在存储时都是字符串
- CMake获取变量:${变量名}
- 变量的基本操作是set()与unset(),但也可以利用list或者string操作变量
1.2.1、Set方法:
- set(<variable><variable>[PARENT_SCOPE])
- set可以给一个变量设多个值
- 变量内部存储时使用“;”分割,但显示是只进行连接处理
#设置单个值
set(Var1 zzz)
message(${Var1})
set([[my Var1]] zzz)
message(${my\ Var1})
#设置多个值
set(Var1 v1 v2)
message(${Var1})
set(Var1 v1;v2)
message(${Var1})
#环境变量PATH
message($ENV{PATH})
#创建新的环境变量
set(ENV{CXX} "g++")
message($ENV{CXX})
#unset
unset(ENV{CXX})
message($ENV{CXX})
1.2.2、List方法:
- list(APPEND <list> [<element>....]) 列表添加元素
- list(REMOVE_ITEM <list> <value> [value.....]) 列表删除元素
- list(LENGTH <list> <output variable>) 获取列表元素个数
- list(FIND <list> <value> <out-var>) 在列表中查找元素返回索引
- list(INSERT <list> <index> [<element>...]) 在index位置插入
- list(REVERES <list>) 反转list
- list(SORT <list> [....]) 排序list
#创建var
list(APPEND port p1 p2 p3)
#添加
list(INSERT port 3 p4)
message(${port})
#获取长度
list(port LISTVALUE len)
message(${len})
#查找索引
list(FIND port p2 index)
message(${index})
#删除
list(REMOVE_ITEM port p1)
message(${port})
#反转
list(REVERSE port)
message(${port})
#排序
list(SORT port)
message(${port})
1.3、流程控制
1.3.1、if条件流程控制
set(VARBOOL TRUE)
#打印TRUE
if(VARBOOL)
message(TRUE)
else()
message(FALSE)
endif()
#打印FALSE
if(NOT VARBOOL)
message(TRUE)
else()
message(FALSE)
endif()
#打印TRUE
if(NOT VARBOOL OR VARBOOL )
message(TRUE)
else()
message(FALSE)
endif()
#打印FALSE
if(NOT VARBOOL AND VARBOOL )
message(TRUE)
else()
message(FALSE)
endif()
#打印 1 LESS 2
if(1 LESS 2 )
message("1 LESS 2")
endif()
#不打印
if("ok" LESS 2 )
message("ok LESS 2")
endif()
#打印EQUAL
if("2" EQUAL 2 )
message("EQUAL ")
endif()
1.3.2、loop循环流程控制
foreach(VAR RANGE 3)
message(${VAR})
endforeach()
#先打印VAR再打印4,最后再打印f
set(MY_LIST 1 2 3)
foreach(VAR IN LISTS MYLIST ITEM 4 f)
message($VAR)
endforeach
#两个列表
set(L1 one two three four)
set(L2 1 2 3 4 5)
foreach(num IN ZIP_LISTS L1 L2)
message("word= ${num_0},num=${num1}")
endforeach()
1.4、Function函数
- function(<name>[<argument>...])<commands>endfunction()
function(MYFunc FirstArg)
#CMAKE_CURRENT_FUNCTION函数名称
message("MYFunc Name:${CMAKE_CURRENT_FUNCTION}")
message("FirstArg ${FirstArg}")
set(FirstArg "New Value")
message("FirstArg again: ${FirstArg}")
#第0个参数ARGV0
message("ARGV0 ${ARGV0}")
message("ARGV1 ${ARGV1}")
message("ARGV2 ${ARGV2}")
endfunction()
set(FirstArg "first value")
MyFunc(${FirstArg} "value")
message("FirstArg ${FirstArg}")
#输出结果
MYFunc Name: MYFunc
FirstArg first value
FirstArg again: New Value
ARGV0 first value
ARGV1 value
ARGV2
1.5、Scope作用域
CMake有两种作用域
- Function scope 函数作用域
- Directory scope:当从add_subdirectory()命令执行嵌套目录中的CMakeLists.txt列表文件时,注意父CMakeLists.txt其中的变量可以被子CMakeLists.txt使用
1.5.1、Function scope
function(OutFunc)
message("-> Out: ${Var}")
set(Var 2)
InFunc()
message("<- Out: ${Var}")
endfunction()
function(InFunc)
message("-> In:${Var}")
set(Var 3)
message("<- In:${Var}")
endfunction()
set(Var 1)
message("-> Global: ${Var}")
OutFunc()
message("<- Global: ${Var}")
#输出结果
-> Global:1
-> Out:1
-> In:2
<- In:3
<- Out:2
<- Global:1
1.6、宏
注意:尽量不要写宏,只要会读就行
macro(Test myVar)
set(myVal "new value")
message("argument: ${myVar}")
endmacro()
set(myVar "First value")
message("myVar: ${myVar}")
Test("value")
message("myVar: ${myVar}")
#输出结果
myVar: First value
argument: value
myVar: new value
第二章、CMake构建项目的四种方式
2.1、直接写入源码路径的方式
- add_executable中直接写入相对路径
- 在源码中引入头文件时需要写相对路径
cmake_minimum_required(VERSION 3.14)
#指定项目名称
project(ProjectName VERSION 0.1 LANGUAGES CXX)
add_executable(ProjectName
main.cpp
pro.cpp
)
2.2、调用子目录中的CMake脚本的方法
- include方法可以引入子目录中的cmake后缀的配置文件
- 将配置加入add_executable中
cmake_minimum_required(VERSION 3.14)
#指定项目名称
project(ProjectName VERSION 0.1 LANGUAGES CXX)
#如果调用其他cmake中的使用include(xxxxx.camke)添加进去
set(pro_sources pro.cpp)
add_executable(ProjectName main.cpp ${pro_sources})
2.3、CMakeLists嵌套
- target_include_directories头文件目录的声明
- target_link_libraries连接库文件
- add_subdirectory添加子目录
- add_library生成库文件
#子
add_library(ProLib pro.cpp)
#父
cmake_minimum_required(VERSION 3.14)
project(ProjectName VERSION 0.1 LANGUAGES CXX)
add_subdirectory("子文件夹名")
add_executable(ProjectName main.cpp)
target_link_libraries(ProjectName PUBLIC ProLib)
#PROJECT_BINARY_DIR表示二进制文件目录,PROJECT_SOURCE_DIR为源码目录
target_include_directories(ProjectName PUBLIC "${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/子文件夹名")
2.4、Object Libraries
注意:最低版本为3.12才能使用
- add_library OBJECT
Object Libraries是一个特殊的库类型,它将目标文件编译成一个库,但不会生成最终的连接文件。这意味着你可以在后续的add_library()或add_executable()命令中,将Object Libraries作为源文件进行链接,从而生成最终的可执行文件或库文件
- 将target_include _directories移入到子CMakeLists中
#子
#方式一
add_library(ProLib OBJECT pro.cpp)
target_include_directories(ProLib PUBLIC .)
#方式二
add_library(cat OBJECT cat.cpp)
target_include_directories(cat PUBLIC .)
add_library(dog OBJECT dog.cpp)
target_include_directories(dog PUBLIC .)
#父
#方式一
cmake_minimum_required(VERSION 3.14)
project(ProjectName VERSION 0.1 LANGUAGES CXX)
add_subdirectory("子文件夹名")
add_executable(ProjectName main.cpp)
target_link_libraries(ProjectName PUBLIC ProLib)
#方式二
cmake_minimum_required(VERSION 3.14)
project(ProjectName VERSION 0.1 LANGUAGES CXX)
add_subdirectory("子文件夹名")
add_executable(ProjectName main.cpp)
target_link_libraries(ProjectName PUBLIC cat dog)
第三章、CMake与库
3.1、CMake如何生成静态库/动态库
动态库:lib<name>.so/dll
静态库:lib<name>.a/lib
命令:
- file()用于搜索源文件
- add_library(xxxx STATIC ${SRC})生成静态库
- add_library(xxxx SHARED ${SRC})生成动态库
- ${LIBRARY_OUTPUT_PATH}导出目录
cmake_minimum_required(VERSION 3.14)
project(ProjectName VERSION 0.1 LANGUAGES CXX)
#将所有cpp文件加入到SRC变量中
file(GLOB SRC ${PROJECT_SOURCE_DIR}/src/*.cpp)
#引入头文件
include_directories(${PROJECT_SOURCE_DIR}/include)
#静态
#输出文件位置
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/a)
add_library(aniaml STATIC ${SRC})
#动态
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/so)
add_library(aniaml SHARED ${SRC})
3.2、CMake调用动态库与静态库
静态库调用流程
- 引入头文件
- 连接静态库
- 生成可执行二进制文件
cmake_minimum_required(VERSION 3.14)
project(ProjectName VERSION 0.1 LANGUAGES CXX)
#引入头文件
include_directories(${PROJECT_SOURCE_DIR}/include)
#指定路径
link_directories(${PROJECT_SOURCE_DIR}/a)
#链接库
link_libraries(animal)
#生成二进制可执行文件
add_executable(app main.cpp)
动态库调用流程
- 引入头文件
- 声明库目录
- 生成可执行二进制文件
- 连接动态库
cmake_minimum_required(VERSION 3.14)
project(ProjectName VERSION 0.1 LANGUAGES CXX)
#引入头文件
include_directories(${PROJECT_SOURCE_DIR}/include)
#指定路径
link_directories(${PROJECT_SOURCE_DIR}/so)
#生成二进制可执行文件
add_executable(app main.cpp)
#链接库
target_link_libraries(app PUBLIC animal)
第四章、CMake与源文件的交互
config.h.in 文件
相当于模板
在这个文件中可以获取CMake文件中的变量
#define CMAKE_CXX_STANDARD ${CMAKE_CXX_STANDARD}
cmake文件
cmake_minimum_required(VERSION 3.14)
project(ProjectName VERSION 0.1 LANGUAGES CXX)
#设置C++标准
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
configure_file(config.h.in config.h)
add_subdirectory(animal)
add_executable(ProjectName main.cpp)
target_link_libraries(ProjectName PUBLIC AnimalLib)
target_include_directories(ProjectName PUBLIC "${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/animal")
main.cpp
#include "config.h"
int main()
{
std::cout<<CMAKE_CXX_STANDARD<<std:endl;
return 0;
}
第五章、CMake的条件编译
步骤:
- 用option定义变量
- 在子CMakeLists.txt中根据变量ON还是OFF来修改SRC(源文件)以及target_compile_defintions
- 修改源文件根据变量选择代码
- 执行命令时-D<变量>=ON/OFF来进行条件编译
注意:
- PUBLIC :本目标需要用,依赖这个目标的其他目标也需要
- INTERFACE: 本目标不需要,依赖本目标的其他目标需要
- PRIVATE:本目标需要,依赖这个目标的其他目标不需要
option(USE_CATTWO "Use cat two" ON)
if(USE_CATTWO)
set(SRC cat.cpp dog.cpp cattwo.cpp)
else
set(SRC cat.cpp dog.cpp)
endif()
add_library(AnimalLib ${SRC})
if(USE_CATTWO)
target_compile_definitions(AnimalLib PRIVATE "USE_CATTWO")
endif()
#ifdef USE_CATTWO
#include "cattwo.h"
#endif()
std::string Cat::barking()
{
#ifdef USE_CATTWO
retrun cattwo::two()
#else
return "cat mi mi"
#endif
}