CMake系统学习笔记
基础操作
最基本的案例
// code
#include <iostream>
int main()
{
std::cout << "hello world " << std::endl;
return 0;
}
// CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
# 定义当前工程名称
project(demo)
add_executable(test main.cpp)
// 命令行编译执行 在Windows平台 build默认会生成visual studio项目工程
cmake -S . -B build
// 编译方式1 build目录下生成test可执行文件
cd build && make
// 编译方式2 32个线程编译
cmake --build build -j32
// 利用cmake生成xcode工程项目
cmake -S . -B xcode -G "Xcode"
分步编译演示
# mac 平台分步编译命令
cmake --build build --target help
The following are some of the valid targets for this Makefile:
... all (the default if no target is provided)
... clean
... depend
... edit_cache
... rebuild_cache
... test
... main.o
... main.i
... main.s
# 预处理
cmake --build build --target main.i
# 汇编
cmake --build build --target main.s
# 链接
cmake --build build --target main.o
# 清理
cmake --build build --target clean
# windows平台需要用namke
# 利用vs的控制台生成
cmake -S . -B nmake -G "NMake Makefiles"
多行注释
# 多行注释
#[[
注释内容
message(arg1 arg2 arg3)
cmake -S . -B build >log 2>&1
]]
message
# message基础使用
message("参数1") #测试message
message("参数p1" "参数p2" #[[注释在message中]] "p3" 123 测试)
# message高级使用-指定日志级别
# FATAL_ERROR 进程退出,生成退出 打印代码路径和行号 stderr
message(FATAL_ERROR "TEST FATAL_ERROR")
# SEND_ERROR 进程继续,生成退出 不会生成 add_executable add_library stderr
# 打印错误代码路径和行号
message(SEND_ERROR "TEST SEND_ERROR")
# WARNING 打印代码路径和行号 stderr
message(WARNING "TEST WARNING")
#NOTICE等同于 none也就是不加 message("TEST NOTICE") stderr
message("TEST none")
message(NOTICE "TEST NOTICE")
#STATUS 打印信息加前缀 -- 用户可能感兴趣 stdout
message(STATUS "TEST STATUS")
#VERBOSE 加前缀 -- 默认不显示 用户需要的详细信息 stdout
message(VERBOSE "TEST VERBOSE")
# 设置日志显示级别 显示VERBOSE 信息
cmake -S . -B build --log-level=VERBOSE
# 标准输出重定向到文件log.txt
cmake -S . -B build --log-level=VERBOSE > log.txt
# 标准错误输出重定向到标准输出
cmake -S . -B build --log-level=VERBOSE > log.txt 2>&1
#DEBUG 加前缀 --
message(DEBUG "test DEBUG")
cmake -S . -B build --log-level=DEBUG
#TRACE 加前缀 --
message(TRACE "test TRACE")
cmake -S . -B build --log-level=TRACE
# message Reporting checks查找库日志
#[[
CHECK_START 开始记录将要执行检查的消息
CHECK_PASS 记录检查的成功结果
CHECK_FAIL 记录不成功的检查结果
]]
#开始查找
message(CHECK_START "查找xcpp")
# 查找库xcpp的代码
# message消息缩进
set(CMAKE_MESSAGE_INDENT "--")
#嵌套查找
message(CHECK_START "查找xlog")
#查找xlog代码
message(CHECK_PASS "成功")
message(CHECK_START "查找xthreadpool")
message(CHECK_FAIL "失败")
#取消缩进
set(CMAKE_MESSAGE_INDENT "")
#结束查找 查找失败
message(CHECK_FAIL "失败")
# message 显示颜色
#[[
\033[1;31;40m <!--1-高亮显示 31-前景色红色 40-背景色黑色-->
\033[0m <!--采用终端默认设置,即取消颜色设置-->
显示方式
0 终端默认设置
1 高亮显示
4 使用下划线
5 闪烁
7 反白显示
8 不可见
前景色 背景色 颜色
---------------------------------------
30 40 黑色
31 41 红色
32 42 绿色
33 43 黃色
34 44 蓝色
35 45 紫红色
36 46 青蓝色
37 47 白色
]]
string(ASCII 27 Esc)
# Esc[0;31m
set(R "${Esc}[0;31m") #红色
#Esc[0m
set(E "${Esc}[m" ) #结束颜色设置
set(B "${Esc}[1;34m") #蓝色高亮
set(RB "${Esc}[1;31;40m") #红色字体黑色背景
message("${R}红色内容${E} 默认颜色")
message("${B}蓝色内容${E} 默认颜色")
message("${RB}红色字体黑色背景${E} 默认颜色")
变量
set(VAR1 "测试变量VAR1的值")
message("VAR1=" ${VAR1})
message("VAR1 in string ${VAR1}")
message("\${VAR1}=${VAR1}")
# 嵌套取值
set(VAR2 "VAR1")
message("VAR2=" ${VAR2})
message("VAR2=" ${${VAR2}})
# 销毁
unset(VAR1)
message("\${VAR1}=${VAR1}")
cmake文件包含
include("cmake/test_cmake.cmake")
调试打印指令
# 方法一 build加上 -v参数,可以看到生成的详细命令
cmake --build build -v
# 方法二 cmake中开启
set(CMAKE_VERBOSE_MAKEFILE ON)
# 方法三 打印库的指令
# 不能打印继承的属性
# 打印 INCLUDE_DIRECTORIES、INTERFACE_INCLUDE_DIRECTORIES属性
cmake_print_properties(TARGETS test PROPERTIES
INCLUDE_DIRECTORIES
INTERFACE_INCLUDE_DIRECTORIES
)
条件控制以及循环
#[[
if(<常量>) #constant 常量
如果常量是1, ON, YES, TRUE,Y或非零数(包括浮点数),则为真True。
如果常量是0, OFF, NO, FALSE, N, IGNORE, NOTFOUND, 空字符串,或以-NOTFOUND结尾则为假False
命名布尔常量不区分大小写
if(<string>)
带引号的字符串计算为 false,除非字符串的值是真正的常量之一
if(<variable>)
如果给定一个定义为非假常量的值的变量,则为真否则为 False,包括变量未定义时。
宏参数不是变量。环境变量if(ENV{some_var})总是会评估为假。
]]
if(1)
message("1 is true")
endif()
if(OFF) #false
message("OFF is true?")
elseif(NO) #false
message("NO is true?")
else()
message("OFF NO is false!")
endif()
#[[
逻辑运算符
if(NOT <condition>)
如果条件不为真,则为真。
if(<cond1> AND <cond2>)
如果两个条件都是真的,则为真。
if(<cond1> OR <cond2>)
如果任一条件是真的,则为真。
if((condition) AND (condition OR (condition)))
首先评估括号内的条件,然后评估其余条件。
]]
#案例
set(VAR_OFF OFF)
if(NOT VAR_OFF) #如果条件不为真,则为真。
message("NOT VAR_OFF (true)")
endif()
if(TRUE AND ON)
message("TRUE and ON is true")
endif()
if(TRUE OR OFF)
message("TRUE OR OFF is true?")
else()
message("TRUE OR OFF is false?")
endif()
缓存变量
#[[
set(<variable> <value>... CACHE <type> <docstring> [FORCE])
type
BOOL
ON/OFF 选择框
FILEPATH
文件选择
PATH
目录选择
STRING
字符串
INTERNAL
内部变量
docstring
变量说明
FORCE
强制修改缓存,不设置第二次调用值不改
]]
#设置缓存变量 字符串类型
set(VAR1 "CACHE VAR1 VALUE1-1 " CACHE STRING "cache doc")
# 强制修改缓存 FORCE
set(VAR1 "CACHE VAR1 VALUE1-3 FORCE" CACHE STRING "cache doc" FORCE)
# CACHE变量作用域是全局的
# 普通变量的作用域 自身和子模块
文件属性
#[[
set_property(<GLOBAL |
DIRECTORY [<dir>] |
TARGET [<target1> ...] |
SOURCE [<src1> ...]
[DIRECTORY <dirs> ...]
[TARGET_DIRECTORY <targets> ...] |
INSTALL [<file1> ...] |
TEST [<test1> ...] |
CACHE [<entry1> ...] >
[APPEND] [APPEND_STRING]
PROPERTY <name> [<value1> ...])
get_property(<variable>
<GLOBAL |
DIRECTORY [<dir>] |
TARGET <target> |
SOURCE <source>
[DIRECTORY <dir> | TARGET_DIRECTORY <target>] |
INSTALL <file> |
TEST <test> |
CACHE <entry> |
VARIABLE >
PROPERTY <name>
[SET | DEFINED | BRIEF_DOCS | FULL_DOCS])
define_property(<GLOBAL | DIRECTORY | TARGET | SOURCE |
TEST | VARIABLE | CACHED_VARIABLE>
PROPERTY <name> [INHERITED]
[BRIEF_DOCS <brief-doc> [docs...] ]
[FULL_DOCS <full-doc> [docs...] ]
[INITIALIZE_FROM_VARIABLE <variable>])
]]
#设置全局属性
set_property(GLOBAL PROPERTY TEST_GLOBAL "test global 001")
#获取全局属性 可以获取子目录中cmake定义的全局属性
get_property(val GLOBAL PROPERTY TEST_GLOBAL)
message("TEST_GLOBAL = ${val}")
#APPEND APPEND_STRING
# APPEND 数组方式添加 TEST_APPEND = append 001;append 002;append 003
set_property(GLOBAL APPEND PROPERTY TEST_APPEND "append 001")
set_property(GLOBAL APPEND PROPERTY TEST_APPEND "append 002")
set_property(GLOBAL APPEND PROPERTY TEST_APPEND "append 003")
get_property(val GLOBAL PROPERTY TEST_APPEND)
message("TEST_APPEND = ${val}")
# APPEND_STRING 字符串拼接 append string 001 append string 002 append string 003
set_property(GLOBAL APPEND_STRING PROPERTY TEST_APPEND_STRING "append string 001 ")
set_property(GLOBAL APPEND_STRING PROPERTY TEST_APPEND_STRING "append string 002 ")
set_property(GLOBAL APPEND_STRING PROPERTY TEST_APPEND_STRING "append string 003 ")
get_property(val GLOBAL PROPERTY TEST_APPEND_STRING)
message("TEST_APPEND_STRING = ${val}")
set_property(GLOBAL PROPERTY P1 "p1")
get_property(var GLOBAL PROPERTY P1)
message("P1 SET = ${var}")
if(var)
message("P1 is set")
else()
message("P1 not set")
endif()
#只有调用define_property之后才会为1
get_property(var GLOBAL PROPERTY P1 DEFINED)
message("P1 DEFINED = ${var}")
if(NOT var)
# 目前var是0
message("P1 not defined")
endif()
#目录属性
set_property(DIRECTORY . PROPERTY DIR1 "dir001")
get_property(var DIRECTORY . PROPERTY DIR1)
message("DIR1 = ${var}")
#文件属性
set_property(SOURCE main.cpp PROPERTY S1 "s1 value")
get_property(var SOURCE main.cpp PROPERTY S1)
message("SOURCE S1 = ${var}")
# cmake传递变量给c++
# cmake 预置属性 COMPILE_DEFINITIONS 类似 传递预处理变量(宏变量) -DPARA1 1234
set_property(SOURCE main.cpp PROPERTY COMPILE_DEFINITIONS "PARA1=1234")
环境变量
#环境变量使用 全局无缓存变量
#自定义环境变量
set(ENV{MYENV} "test env value")
message("MYENV = $ENV{MYENV}")
#系统环境变量 命令行 env 回车查看系统环境变量
message("USER NAME = $ENV{USER}")
message("PATH = $ENV{PATH}")
string查找和字符串相关
# 取出begin 和end之间的内容test cmake string
set(STR1 " begin test cmake string end ")
# 查找的开头字符串
set(BSTR "begin")
#在${STR1}中 查找${BSTR}的位置存入start
# cmake位置以0开始
string(FIND ${STR1} ${BSTR} startIndex)
message("FIND ${BSTR} POS ${startIndex}")
string(FIND ${STR1} "end" endIndex)
message("FIND end POS ${endIndex}")
#去掉begin字符位置
#获取字符串长度
string(LENGTH ${BSTR} size)
message("size = ${size}")
math(EXPR startIndex "${startIndex} + ${size}")
message("startIndex = ${startIndex}")
# endIndex等于开始到结束的长度
math(EXPR length "${endIndex} - ${startIndex}")
message("length = ${length}")
#获取字串 在${STR1字符串${startIndex}位置取${length}长度字符串写入substr
string(SUBSTRING ${STR1} ${startIndex} ${length} substr)
message("SUBSTRING substr = [${substr}]")
#去掉头尾 空格 \t \n \r
string(STRIP ${substr} substr)
message("STRIP substr = [${substr}]")
#转成大写 test cmake string
string(TOUPPER ${substr} substr)
message("TOUPPER substr = [${substr}]")
#字符串追加 TEST CMAKE STRING
string(APPEND substr " append01 " " append02 ")
message("APPEND substr = [${substr}]")
#字符串替换 STRINGappend01append02
# string(REPLACE <match-string> <replace-string> <out-var> <input>...)
string(REPLACE "append" "REPLACE" substr ${substr})
message("REPLACE substr = [${substr}]")
# string 操作json字符串
#测试用json
# json对象 {} json 数组 []
# 格式 key:value
set( tjson
[=[
{
"webs":{
"web":[
{
"name":"cmake",
"url":"cmake.org.cn"
},{
"name":"ffmpeg",
"url":"ffmpeg.club"
}
,{
"name":"tt",
"url":"tt.club"
}
]
}
}
]=]
)
message(${tjson})
#[[
string(JSON <out-var> [ERROR_VARIABLE <error-variable>]
GET <json-string> <member|index> [<member|index> ...])
]]
# 访问webs->web[0]->name
string(JSON var ERROR_VARIABLE evar
GET ${tjson} webs web 0 name
)
# 错误信息
if(evar)
message("error = ", ${evar})
endif()
message("webs web 0 = ${var}")
string(JSON var ERROR_VARIABLE evar
GET ${tjson} webs web 1 url
)
message("webs web 1 = ${var}")
#读取json数组长度
#[[
string(JSON <out-var> [ERROR_VARIABLE <error-variable>]
LENGTH <json-string> [<member|index> ...])
]]
string(JSON web_count ERROR_VARIABLE evar
LENGTH ${tjson} webs web
)
message("JSON LENGTH = ${web_count}")
#json的增加和修改
#[[
string(JSON <out-var> [ERROR_VARIABLE <error-variable>]
SET <json-string> <member|index> [<member|index> ...] <value>)
value必须是json
]]
#json的增加
string(JSON set_out SET ${tjson} webs web ${web_count} [=[{
"name":"cpp",
"url":"cppds.com"
}]=])
message("set_out = ${set_out}")
#json的修改 SET
string(JSON set_out2 SET ${set_out} webs web 1 [=[{
"name":"ffmpeg2",
"url":"ffmpeg.club"
}]=])
message("set_out2 = ${set_out2}")
#json的删除 REMOVE
#[[
string(JSON <out-var> [ERROR_VARIABLE <error-variable>]
REMOVE <json-string> <member|index> [<member|index> ...])
]]
string(JSON remove_out REMOVE ${set_out2} webs web 0)
string(JSON remove_out REMOVE ${remove_out} webs web 1)
message("remove_out = ${remove_out}")
list相关操作
#list变量初始化
set(src "a" "b" "c" "d;e;f")
message("src = ${src}")
#获取list变量长度
list(LENGTH src length)
message("src length = ${length}")
#list追加写入
list(APPEND src "g")
list(APPEND src "h" "i")
message("src = ${src}")
# list访问 下标 0,1,2 。。。 结尾往前 -1 -2 -3
# src = a;b;c;d;e;f;g;h;i
list(GET src 1 var)
message("GET src 1 = ${var}")
list(GET src 5 var)
message("GET src 5 = ${var}")
list(GET src -1 var)
message("GET src -1 = ${var}")
list(GET src -2 var)
message("GET src -2 = ${var}")
# 拼接所有的节点
list(JOIN src "|" var) # 输出 a|b|c|d|e|f|g|h|i
message("JOIN ${var}")
list(JOIN src "" var)
message("JOIN ${var}")
# 取其中一部分数组
list(SUBLIST src 0 3 var)
message("SUBLIST ${var}")
# 查找内容
# src = a;b;c;d;e;f;g;h;i
list(FIND src "d" b) # 3
message("FIND ${b}")
#插入节点
# src = a;b;c;d;e;f;g;h;i
list(INSERT src 4 "d1")
list(INSERT src 2 "b1")
list(INSERT src 2 "b")
message("INSERT src ${src}")
#通过节点值删除
list(REMOVE_ITEM src "b")
message("REMOVE src = ${src}")
#删除指定位置 e
list(REMOVE_AT src 5)
#重新插入 相当于修改 E
list(INSERT src 5 "E")
message("REMOVE_AT INSERT src = ${src}")
#双向队列操作方式
#后端出队 b1;c;d;d1;E;f;g;h
list(POP_BACK src var)
message("POP_BACK ${var}")
message("src = ${src}")
#前端出队 先进先出 b1;c;d;d1;E;f;g;h
list(POP_FRONT src var)
message("POP_FRONT ${var}")
message("src = ${src}")
#去掉重复元素
set(rsrc "a;b;c;d;d;b;a;z;t;f")
message("rsrc = ${rsrc}")
list(REMOVE_DUPLICATES rsrc)
message("REMOVE_DUPLICATES rsrc = ${rsrc}")
# 数据排序
list(SORT rsrc)
message("SORT rsrc = ${rsrc}")
set(arr "3;23;122;157;1;5;7")
#默认字符串排序
list(SORT arr)
message("arr = ${arr}")
#自然数排序
list(SORT arr COMPARE NATURAL)
message("NATURAL arr = ${arr}")
foreach/while使用
#[[
foreach(<loop_var> <items>)
<commands>
endforeach()
]]
# 遍历范围 从0开始 到stop结束 0 1 2 3 4 5
# #foreach(<loop_var> RANGE <stop>)
set(out "")
foreach(var RANGE 5) # 0 1 2 3 4 5
message("var = ${var}")
string(APPEND out ${var} " ")
endforeach()
message("out = ${out}")
# foreach(<loop_var> RANGE <start> <stop> [<step>])
foreach(var RANGE 1 3)
message(${var})
endforeach()
set(out "")
# 遍历范围从 0 到50 每次加5
foreach(var RANGE 0 50 5) # 0 5 10 15 20 25 30 35 40 45 50
string(APPEND out ${var} " ")
endforeach()
message("out = ${out}")
# foreach(<loop_var> IN [LISTS [<lists>]] [ITEMS [<items>]])
# 遍历list数组
set(args "a" "b" "c" "d" "e")
foreach(var IN LISTS args)
message(${var})
endforeach()
foreach(var IN ITEMS ${args}) # 必须是list值
message(${var})
endforeach()
foreach(var IN ITEMS 1;2;3;4)
message(${var})
endforeach()
#按次序遍历多个list 可实现list按条件拼接
set(A "0;1;2;3")
set(B "4;5;6;7")
set(out "")
foreach(var IN LISTS A B)
string(APPEND out ${var} " ")
endforeach()
message("LISTS:${out}")
# 同步遍历多个list
# foreach(<loop_var>... IN ZIP_LISTS <lists>)
set(arr1 "1;2;3;4;5;6;7")
set(arr2 "A;B;C;D;E;F;G")
foreach(var IN ZIP_LISTS arr1 arr2)
message("${var_0}:${var_1}")
endforeach()
foreach(var1 var2 IN ZIP_LISTS arr1 arr2)
message("${var1} - ${var2}")
endforeach()
# break() continue()
foreach(var RANGE 100) # 0...100
#取余为3显示数字,其他显示.
math(EXPR re "${var} % 3") # 0是false 其他是true
if(NOT re) #=0
message(${var})
continue()# 本次循环结束
endif()
#大于50退出
if(var GREATER 50)
break() #整个foreach循环结束
endif()
message(".")
endforeach()
#[[
while(<condition>)
<commands>
endwhile()
]]
# 防止死循环
set(var 1)
while(var)
math(EXPR var "${var}+1")
math(EXPR re "${var} % 10 ")
if(re) # !=0
continue()
endif()
message(${var})
if(var GREATER 100)
set(var 0) # break();
endif()
endwhile()
宏的使用
macro(my_macro)
message("in my macro")
set(RET 1)
endmacro()
#宏名称大小写不敏感,尽量只用小写
# 调用是将宏代码赋值过来
my_macro()
My_macro()
message("RET = ${RET}")
#固定参数,实参数量要等于或者超过形参
macro(foo arg1 arg2)
message("arg1 = ${arg1} arg2=${arg2}")
if(arg1) # 宏参数不是变量
message("== arg1 true ==")
endif()
if(${arg1})
message("arg1 true")
endif()
endmacro()
foo(TRUE "test")
macro(foo2)
#参数数量
message("ARGC = ${ARGC}")
#参数list
message("ARGN = ${ARGN}")
#ARGN不是变量只能取值使用
foreach(var IN ITEMS ${ARGN})
message("var = ${var}")
endforeach()
message("ARGV0 = ${ARGV0}")
message("ARGV1 = ${ARGV1}")
message("ARGV2 = ${ARGV2}")
endmacro()
foo2(1 "test" True)
macro(my_parse)
message("ARGN = ${ARGN}")
cmake_parse_arguments(
"MY" #前缀
"LOG;FILE" #option
"BIN;LIB" # 单值
"TARGETS" # 多值
${ARGN} #参数数组
)
message("MY_LOG = ${MY_LOG}")
message("MY_FILE = ${MY_FILE}")
message("MY_BIN = ${MY_BIN}")
message("MY_TARGETS = ${MY_TARGETS}")
#参数类型传递错误
message("MY_UNPARSED_ARGUMENTS = ${MY_UNPARSED_ARGUMENTS}" )
#未传值错误
message("MY_KEYWORDS_MISSING_VALUES = ${MY_KEYWORDS_MISSING_VALUES}")
endmacro()
my_parse(
LOG "mylog"
BIN ../../bin
TARGETS "tar1" "tar1"
LIB
)
函数
function(fun1 arg1 arg2)
if(arg1)
message("arg1 is true")
return()
endif()
message("IN function fun1 arg1 = ${arg1} arg2 = ${arg2}")
if(arg2) #函数区别于宏可以当作变量
message("arg2 = TRUE")
endif()
foreach(var IN LISTS ARGN) #遍历所有变参
message("var = ${var}")
endforeach()
endfunction()
fun1(OFF TRUE "testfunc1" 1 3 ON)
fun1(ON TRUE "testfunc1" 1 3 ON)
function(fun_var arg1)
# arg1 参数遍历如果有全局遍历名字一样还是取局部的变量
message("in fun_var ${arg1}")
message("var1 = ${var1}")
set(var1 "fun var") #不能修改外部变量 这一步调用相当于创建了一个局部变量
set(RET "1" PARENT_SCOPE) # 设定变量作用域到父 (调用函数者、父目录)
endfunction()
set(var1 "main")
set(arg1 "main arg1")
fun_var(123)
message("var1 = ${var1}")
message("RET = ${RET}")
生成表达式
#[[
逻辑运算符
$<BOOL:string>
转换string为0或1。评估0以下任何一项是否为真:
string是空的,
string是不区分大小写的等于 0, FALSE, OFF, N, NO, IGNORE, or NOTFOUND, or
string以后缀结尾-NOTFOUND(区分大小写)。
否则计算为1。
$<NOT:condition> 取反 0变1 1变0
$<AND:conditions>
$<OR:conditions>
条件表达式
$<condition:true_string> 0返回空串 1 返回true_string
]] # $<BOOL:OFF> ==》 0 $<0:TEST1=123>
target_compile_definitions(cmake_exp PUBLIC "$<$<BOOL:OFF>:TEST1=123>")
#### 测试生成表达式的方式 ###############
### 利用cmake的错误来查看
# $<AND:1,0>" 通过设置头文件目录错误信息查看表达式的值
#target_include_directories(cmake_exp PUBLIC "$<AND:1,0>")
target_include_directories(cmake_exp PUBLIC "$<OR:0,1>")
set(LIB ON)
# LIB等于OFF时显示STATIC ON显示空
target_include_directories(cmake_exp PUBLIC "$<$<NOT:$<BOOL:${LIB}>>:STATIC>")
#在配置阶段不处理生成表达式
message($<$<NOT:$<BOOL:${LIB}>>:STATIC>)
# 字符串比较
# $<STREQUAL:string1,string2>
# $<EQUAL:value1,value2>
#target_include_directories(cmake_exp PUBLIC "$<STREQUAL:string1,string1>")
#target_include_directories(cmake_exp PUBLIC "$<EQUAL:123,1>")
# 变量查询
# $<CONFIG:cfgs>
# $<CONFIG> Debug Release 。。
#target_include_directories(cmake_exp PUBLIC "$<CONFIG>")
#$<CONFIG:Debug,Release>配置项式Debug,Release之一返回1
target_include_directories(cmake_exp PUBLIC "$<CONFIG:Debug,Release>")
target_include_directories包含目录详解
#[[
target_include_directories(<target> [SYSTEM] [AFTER|BEFORE]
<INTERFACE|PUBLIC|PRIVATE> [items1...]
[<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
]]
#案例
cmake_minimum_required(VERSION 3.20)
project(cmake_target_include)
# WRITE 写文件 清空原数据,如果文件不存在则创建
file(WRITE a.cpp [=[
#include <iostream>
void A()
{
std::cout<<"In A "<<std::endl;
}
]=]
)
# 静态库
add_library(A STATIC a.cpp)
# 打印结果
#Properties for TARGET A:
# A.INCLUDE_DIRECTORIES = "/A_PUBLIC"
# A.INTERFACE_INCLUDE_DIRECTORIES = "/A_PUBLIC"
# PUBLIC 改变 INCLUDE_DIRECTORIES INTERFACE_INCLUDE_DIRECTORIES 依赖者和自己都引用
# target_include_directories(A PUBLIC "/A_PUBLIC")
# 打印结果
#Properties for TARGET A:
# A.INCLUDE_DIRECTORIES = "/A_PUBLIC"
# A.INTERFACE_INCLUDE_DIRECTORIES = <NOTFOUND>
# PRIVATE 改变 INCLUDE_DIRECTORIES 只有自己用
# target_include_directories(A PRIVATE "/A_PUBLIC")
# 打印结果
#Properties for TARGET A:
# A.INCLUDE_DIRECTORIES = <NOTFOUND>
# A.INTERFACE_INCLUDE_DIRECTORIES = "/A_PUBLIC"
# INTERFACE 改变 INTERFACE_INCLUDE_DIRECTORIES 只有依赖者引用
target_include_directories(A INTERFACE "/A_PUBLIC")
#打印输出属性
include(CMakePrintHelpers)
cmake_print_properties(TARGETS A PROPERTIES
INCLUDE_DIRECTORIES
INTERFACE_INCLUDE_DIRECTORIES
)
target_compile_definitions 设置编译宏
#设定A库的宏 代码中A_VAR = 123 INTERFACE PUBLIC PRIVATE 参考 target_include_directories
target_compile_definitions(A PUBLIC A_VAR=123)
# 导入依赖库配置
target_link_libraries()
# 编译参数
target_compile_options()
动态库生成版本号和符号链接
cmake_minimum_required(VERSION 3.0)
# 定义当前工程名称
project(demo)
add_library(demo SHARED test.cpp)
set_target_properties(demo PROPERTIES
VERSION "2.0.1"
SOVERSION "15" # 对执行程序无效,只有动态库可以设置
NO_SONAME OFF #OFF生成 ON不生成库的符号链接
)
Debug/Release配置
#Linux mac 控制方法,vs项目不可用
#CMAKE_BUILD_TYPE
#linux默认为空,既不是debug也不是release
# set(CMAKE_BUILD_TYPE Debug) cmake文件中设置debug模式
# 命令行设置模式
#cmake -S . -B build -D CMAKE_BUILD_TYPE=DEBUG
#cmake -S . -B build -D CMAKE_BUILD_TYPE=Release
#cmake -S . -B build -D CMAKE_BUILD_TYPE=RelWithDebInfo
#cmake -S . -B build -D CMAKE_BUILD_TYPE=MinSizeRel
# windows vs nmake
# vs在生成阶段无法控制配置(自动生成4种)
# cmake --build win --config Release
# cmake --build win --config RelWithDebInfo
cmake_minimum_required(VERSION 3.0)
# 定义当前工程名称
project(demo)
add_library(demo test.cpp)
set(OUT_LIB_PATH ${CMAKE_SOURCE_DIR}/lib)
set(OUT_EXE_PATH ${CMAKE_SOURCE_DIR}/bin) # 执行程序和dll
set_target_properties(demo PROPERTIES
# 静态库和lib文件文件的输出
ARCHIVE_OUTPUT_DIRECTORY ${OUT_LIB_PATH}
ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${OUT_LIB_PATH}/debug
ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${OUT_LIB_PATH}/release
)
add_library(dlib SHARED test.cpp test.h)
target_include_directories(dlib PUBLIC include)
#动态库属性
set_target_properties(dlib PROPERTIES
# windows lib文件文件的输出
ARCHIVE_OUTPUT_DIRECTORY ${OUT_LIB_PATH}
ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${OUT_LIB_PATH}/debug
ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${OUT_LIB_PATH}/release
# windos dll文件输出路径
RUNTIME_OUTPUT_DIRECTORY ${OUT_EXE_PATH}
RUNTIME_OUTPUT_DIRECTORY_DEBUG ${OUT_EXE_PATH}/debug
RUNTIME_OUTPUT_DIRECTORY_RELEASE ${OUT_EXE_PATH}/release
#linux .so 和mac
LIBRARY_OUTPUT_DIRECTORY ${OUT_LIB_PATH}
LIBRARY_OUTPUT_DIRECTORY_DEBUG ${OUT_LIB_PATH}/debug
LIBRARY_OUTPUT_DIRECTORY_RELEASE ${OUT_LIB_PATH}/release
#windows pdb调试文件
PDB_OUTPUT_DIRECTORY ${OUT_LIB_PATH}/pdb
PDB_OUTPUT_DIRECTORY_DEBUG ${OUT_LIB_PATH}/pdb
#debug 版本加后缀
DEBUG_POSTFIX "d"
)
add_executable(main main.cpp)
target_link_libraries(main)
set_target_properties(main PROPERTIES
# windos linux执行文件输出路径
RUNTIME_OUTPUT_DIRECTORY ${OUT_EXE_PATH}
RUNTIME_OUTPUT_DIRECTORY_DEBUG ${OUT_EXE_PATH}/debug
RUNTIME_OUTPUT_DIRECTORY_RELEASE ${OUT_EXE_PATH}/release
#调试路径 工作目录
#VS_DEBUGGER_WORKING_DIRECTORY ${OUT_EXE_PATH}
# $<CONFIG:Debug> 0 1
#$<IF:1,debug,release> 满足条件 返回debug
#$<IF:0,debug,release> 不满足条件 返回release
VS_DEBUGGER_WORKING_DIRECTORY $<IF:$<CONFIG:Debug>,debug,release>
)
if(MSVC)
set_target_properties(main PROPERTIES
#debug 版本加后缀
DEBUG_POSTFIX "d"
)
endif()
install 安装
### 安装目标 DESTINATION指定相对 CMAKE_INSTALL_PREFIX 的输出路径
## 默认安装路径
# linux /usr/local
# windows C:/Program Files (x86)/
# Linux
# cmake -S . -B build -DCMAKE_INSTALL_PREFIX=./out
# Windows 默认安装 Release版本
# cmake -S . -B win -DCMAKE_INSTALL_PREFIX=win_out
# cmake --build win
# cmake --install win --config Debug
# cmake --build win --config Release
# cmake --install win
install(TARGETS slib dlib ${PROJECT_NAME} DESTINATION bin)
#[[
目标分类输出
RUNTIME
由add_executable创建执行程序
windows动态链接库dll文件
ARCHIVE
windows动态库库导出符号 .lib
静态库
add_library添加STATIC 参数
windows是 .lib, Unix、Linux和MinGW是.a
LIBRARY
动态库
add_library 使用SHARED 参数
linux、unix
.so
mac
dylib
PUBLIC_HEADER、PRIVATE_HEADER
]]
install(TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION test_install/bin # 执行程序和dll文件输出
)
install(TARGETS slib dlib
RUNTIME DESTINATION test_install/bin # 执行程序和dll文件输出
ARCHIVE DESTINATION test_install/lib # 静态库和windows动态库导出符号 .lib
LIBRARY DESTINATION test_install/lib # linux和mac的动态库 .so .dylib
#头文件的安装
PUBLIC_HEADER DESTINATION test_install/include #公开头文件
PRIVATE_HEADER DESTINATION test_install/inc #内部头文件
)
#[[
debug release 不同输出路径
windows 编译过程
cmake -S . -B win -DCMAKE_INSTALL_PREFIX=win_out
cmake --build win --config Debug
cmake --install win --config Debug
Linux 编译过程 Debug, Release, RelWithDebInfo and MinSizeRel,
cmake -S . -B build -DCMAKE_INSTALL_PREFIX=out -DCMAKE_BUILD_TYPE=Debug
cmake --build build
cmake --install build --config Debug
cmake -S . -B build -DCMAKE_INSTALL_PREFIX=out -DCMAKE_BUILD_TYPE=Release
cmake --build build
cmake --install build --config Release
]]
install(TARGETS ${PROJECT_NAME}
CONFIGURATIONS Debug
RUNTIME DESTINATION debug/bin
)
install(TARGETS ${PROJECT_NAME}
CONFIGURATIONS Release RelWithDebInfo MinSizeRel
RUNTIME DESTINATION release/bin
)
# 1、install 文件类型安装和权限
cmake_minimum_required(VERSION 3.20)
project(install_file)
file(WRITE a.h "")
file(WRITE b.h "")
file(WRITE c.h "")
# 文件安装到指定目录
install(FILES a.h b.h DESTINATION include)
# 目标可选 OPTIONAL 目标不存在不出错
install(FILES d.h DESTINATION inc OPTIONAL)
# 文件类型 TYPE DOC LIB INCLUDE
include(GNUInstallDirs)
message("CMAKE_INSTALL_DATAROOTDIR = ${CMAKE_INSTALL_DATAROOTDIR}")
install(FILES a.h TYPE DOC) # <DATAROOT dir>/doc
install(FILES b.h TYPE LIB) # lib
install(FILES c.h TYPE INCLUDE) # include
# 文件权限 windows目录无效
# 默认 权限 OWNER_WRITE, OWNER_READ, GROUP_READ, WORLD_READ
install(FILES a.h DESTINATION pub
PERMISSIONS
OWNER_READ OWNER_WRITE OWNER_EXECUTE
GROUP_READ GROUP_WRITE GROUP_EXECUTE
WORLD_READ WORLD_WRITE WORLD_EXECUTE
)
# 2、过滤git和指定后缀文件
cmake_minimum_required(VERSION 3.22)
project(install_dir)
file(WRITE doc/index.html "")
file(WRITE doc/doc.html "")
file(WRITE doc/doc2.htm "")
file(WRITE doc/doc.cc "")
file(WRITE doc/doc.c "")
file(WRITE doc/sub/doc.html "")
file(WRITE doc/include/doc.h "")
file(WRITE doc/.svn/config "")
file(WRITE doc/.git/config "")
# DOC类型指定安装路径 share/doc
# 安装doc目录下所有文件,包含子目录中,空子目录也创建
install(DIRECTORY doc TYPE DOC)
install(DIRECTORY doc DESTINATION doc2)
# 过滤只复制*.html *.htm文件 包含子目录
install(DIRECTORY doc DESTINATION html_doc
FILES_MATCHING
PATTERN "*.html"
PATTERN "*.htm"
)
# 排除 .git 和.svn目录
install(DIRECTORY doc DESTINATION no_git_doc
PATTERN ".git" EXCLUDE
PATTERN ".svn" EXCLUDE
)
# 排除 .git 和.svn目录
install(DIRECTORY doc DESTINATION src
FILES_MATCHING
PATTERN "*.cc"
PATTERN "*.c"
PATTERN ".git" EXCLUDE
PATTERN ".svn" EXCLUDE
)
# 3、install期间执行代码
project(install_code)
FILE(WRITE a.h "")
FILE(WRITE b.h "")
install(CODE "message(\"begin install\")")
install(FILES a.h TYPE INCLUDE)
install(CODE "message(\"a.h install success!\")")
install(FILES b.h TYPE INCLUDE)
install(CODE "message(\"b.h install success!\")")
# 写入安装的时间
# string(TIMESTAMP now "%Y-%m-%d %H:%M:%S")
# 获取当前时间戳,并转换时间格式,写入now变量
install (CODE [=[
string(TIMESTAMP now "%Y-%m-%d %H:%M:%S")
message(${now})
FILE(APPEND install_log.txt "${now}\n")
]=])
# 4、分组安装
project(cmake_component)
FILE(WRITE "a.cpp" "")
FILE(WRITE "doc.html" "")
install(FILES a.cpp
DESTINATION src COMPONENT src
)
install(FILES doc.html
DESTINATION doc COMPONENT doc
)
# 执行过程
# cmake -S . -B build -DCMAKE_INSTALL_PREFIX=out
# cd build
# cmake -DCOMPONENT=src -P cmake_install.cmake
# -- Install component: "doc"
# cmake -DCOMPONENT=doc -P cmake_install.cmake
交叉编译
# toolchain.camke
# 工具链里面一般需要配置的数据
# CMAKE_SYSTEM_NAME 必填,系统名称:Linux、 Windows、
# CMAKE_SYSTEM_PROCESSOR 可选,处理器或者硬件名称
# CMAKE_C_COMPILER c编译器全路径
# CMAKE_CXX_COMPILER C++编译器全路径
# CMAKE_SYSROOT 系统头文件路径 可选
# CMAKE_TOOLCHAIN_FILE 指定工具链路径
# 编译命令
cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=name_toolchain.camke
# linux arm toolchain.camke 案列
# cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=linux_arm_toolchain.cmake
set(CMAKE_SYSTEM_NAME Linux)
# 安装工具链
# tar -xvf gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu.tar.xz
# /home/xcj/code/tools/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin
set(tools /home/xcj/code/tools/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu)
set(CMAKE_C_COMPILER ${tools}/bin/aarch64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER ${tools}/bin/aarch64-linux-gnu-g++)
# Android ndk 编译案例
SDK路径
# mac /Users/san/Library/Android/sdk
# windows D:\Android\Sdk
# ANDROID_ABI x86 x86_64 armeabi-v7a arm64-v8a
CMAKE_TOOLCHAIN_FILE
# mac /Users/30san/Library/Android/sdk/ndk/24.0.8215888/build/cmake/android.toolchain.cmake
# windows C:/Users/xiaca/AppData/Local/Android/Sdk/ndk/21.4.7075529/build/cmake/android.toolchain.cmake
ANDROID_NDK C:/Users/xiaca/AppData/Local/Android/Sdk/ndk/21.4.7075529
ANDROID_PLATFORM android-30
# windows 进入vs自带控制台开发环境
cmake -S . -B build -G "NMake Makefiles" -DANDROID_ABI=x86 -DANDROID_PLATFORM=android-30 -DCMAKE_TOOLCHAIN_FILE=C:/Users/xiaca/AppData/Local/Android/Sdk/ndk/21.4.7075529/build/cmake/android.toolchain.cmake -DANDROID_NDK=C:/Users/xiaca/AppData/Local/Android/Sdk/ndk/21.4.7075529
cmake -S . -B build -G "NMake Makefiles" -DANDROID_ABI=x86_64 -DANDROID_PLATFORM=android-30 -DCMAKE_TOOLCHAIN_FILE=C:/Users/xiaca/AppData/Local/Android/Sdk/ndk/21.4.7075529/build/cmake/android.toolchain.cmake -DANDROID_NDK=C:/Users/xiaca/AppData/Local/Android/Sdk/ndk/21.4.7075529
cmake -S . -B build -G "NMake Makefiles" -DANDROID_ABI=armeabi-v7a -DANDROID_PLATFORM=android-30 -DCMAKE_TOOLCHAIN_FILE=C:/Users/xiaca/AppData/Local/Android/Sdk/ndk/21.4.7075529/build/cmake/android.toolchain.cmake -DANDROID_NDK=C:/Users/xiaca/AppData/Local/Android/Sdk/ndk/21.4.7075529
cmake -S . -B build -G "NMake Makefiles" -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=android-30 -DCMAKE_TOOLCHAIN_FILE=C:/Users/xiaca/AppData/Local/Android/Sdk/ndk/21.4.7075529/build/cmake/android.toolchain.cmake -DANDROID_NDK=C:/Users/xiaca/AppData/Local/Android/Sdk/ndk/21.4.7075529
# 鸿蒙编译方法
# 要进入vs2022 或者2019的 x64编译控制台,保证能运行nmake
# armeabi-v7a
#[[
cmake -S . -B build -G "NMake Makefiles" -DOHOS_ARCH=armeabi-v7a -DCMAKE_TOOLCHAIN_FILE=D:\openharmony_sdk\native\3.1.6.6\build\cmake\ohos.toolchain.cmake
cmake -S . -B build -G "NMake Makefiles" -DCMAKE_TOOLCHAIN_FILE=D:\openharmony_sdk\native\3.1.6.6\build\cmake\ohos.toolchain.cmake
]]
CMake基础使用语法
# CMakeLists.txt 编写
# cmake版本
cmake_minimum_required(VERSION 3.0)
# 定义当前工程名称
project(demo)
#添加头文件目录
include_directories(${PROJECT_SOURCE_DIR}/include)
include_directories(${PROJECT_SOURCE_DIR}/thirdparty)
include_directories(
"./include"
"./config/"
)
# 设置debug编译选项
set(CMAKE_BUILD_TYPE "Debug")
# 设置C++11标准
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
# linux上配置调试信息
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
# 添加对应的debug宏 #if defined(OS_LINUX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOS_LINUX ")
# 编译的时候打印信息 FATAL_ERROR:错误会停止后续执行
message(STATUS "message: " ${PROJECT_SOURCE_DIR})
# CMAKE_CXX_FLAGS 匹配指定宏
if (${CMAKE_CXX_FLAGS} MATCHES "OS_LINUX")
message(STATUS "LINUX")
else()
message(STATUS "other")
endif()
# 设置自定义变量,并匹配
set(PLATFORM "iOS")
if (${PLATFORM} MATCHES "iOS")
message(STATUS "iOS编译对应的库")
endif()
# 设置可执行文件最终存储的路径
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
# 设置静态库或者动态库输出目录
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
# 加载子目录
add_subdirectory(src/server)
add_subdirectory(other_dir)
子目录的CMakeLists.txt编写
# 需要编译的源文件
file(GLOB SERVER_SRC_LIST
"*.cpp"
"db/*.cpp"
)
# 定义变量,存储当前目录下的所有源文件
# aux_source_directory(. SERVER_SRC_LIST)
# 从源文件列表中移除某个文编不参与编译
list(REMOVE_ITEM SERVER_SRC_LIST
"./client/test.cpp"
)
# 编译动态库
add_library(server_static SHARED ${SERVER_SRC_LIST})
# 生成静态库 (同时生成静态库和动态库输出库名称不能相同)
add_library(server STATIC ${SERVER_SRC_LIST})
# 生成可执行文件
add_executable(ChatServer ${SERVER_SRC_LIST})
# 设置链接库的寻找路径
link_directories(${CMAKE_CURRENT_SOURCE_DIR}/lib)
# 可执行文件链接库
target_link_libraries(ChatServer muduo_net muduo_base mysqlclient pthread)
# 设置目标属性,此例子是给ChatServer 设置属性 PUBLIC_HEADER = "public.h"
set_target_properties(ChatServer PROPERTIES
PUBLIC_HEADER "public.h"
)
# make install 设置库文件和头文件的安装路径 DESTINATION:安装路径
INSTALL(TARGETS ChatServer
ARCHIVE DESTINATION ${CMAKE_SOURCE_DIR}/bin
LIBRARY DESTINATION ${CMAKE_SOURCE_DIR}/bin
PUBLIC_HEADER DESTINATION ${CMAKE_SOURCE_DIR}/bin/include
)
# 给编译目标添加一些编译配置
target_compile_options(ChatServer PUBLIC -Wall -DLINUX -lpthread)
CMake常用的预定义变量
PROJECT_NAME : 通过 project() 指定项目名称
PROJECT_SOURCE_DIR : 工程的根目录
PROJECT_BINARY_DIR : 执行 cmake 命令的目录
CMAKE_CURRENT_SOURCE_DIR : 当前 CMakeList.txt 文件所在的目录
CMAKE_CURRENT_BINARY_DIR : 编译目录,可使用 add subdirectory 来修改
EXECUTABLE_OUTPUT_PATH : 二进制可执行文件输出位置
LIBRARY_OUTPUT_PATH : 库文件输出位置
BUILD_SHARED_LIBS : 默认的库编译方式 ( shared 或 static ) ,默认为 static
CMAKE_C_FLAGS : 设置 C 编译选项
CMAKE_CXX_FLAGS : 设置 C++ 编译选项
CMAKE_CXX_FLAGS_DEBUG : 设置编译类型 Debug 时的编译选项
CMAKE_CXX_FLAGS_RELEASE : 设置编译类型 Release 时的编译选项
CMAKE_GENERATOR : 编译器名称
CMAKE_COMMAND : CMake 可执行文件本身的全路径
CMAKE_BUILD_TYPE : 工程编译生成的版本, Debug / Release
CMAKE_LIBRARY_OUTPUT_DIRECTORY : linux 动态库so输出路径
CMAKE_ARCHIVE_OUTPUT_DIRECTORY : 归档输出路径(windows静态库lib、windows动态库lib文件、linux静态库.a)
CMAKE_RUNTIME_OUTPUT_DIRECTORY : 执行程序和dll动态库
结合Shell脚本
#!/bin/bash
# 编译选项
BUILD_TYPE=Release
# 编译平台
PLATFORM=OS64
show_help() {
cat << EOF
usage: ${0##*/} [-h] [-p PLATFORM]
-h display help
-d build debug
-s build sdk
-p PLATFORM [OS|OS64|SIMULATOR|universal|ft|lx|amd|ft_uos|lx_uos|amd_uos|MAC]
EOF
}
buildTest() {
echo "buildTest start"
echo "BUILD_TYPE = ${BUILD_TYPE}"
echo "PLATFORM = ${PLATFORM}"
}
# p: 说明后续可以跟参数, $OPTARG是传入的参数值
while getopts "hdsp:" opt
do
case $opt in
p)
echo "-p 选项的值是:$OPTARG"
PLATFORM="$OPTARG"
buildTest
;;
d)
BUILD_TYPE=Debug
;;
s)
echo "build sdk"
;;
h)
echo "发现 -h 参数"
show_help
exit
;;
\?)
echo "未知选项:$opt"
show_help
;;
esac
done
rm -rf ./build
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE
# 结合cmake toolchain使用
cmake .. -DCMAKE_TOOLCHAIN_FILE=$TOOLCHAIN_PATH -DCMAKE_BUILD_TYPE=$BUILD_TYPE