深入理解计算机基础第七章-CMake学习笔记
说明
对Cmake的学习笔记
CMake官网
CMake手册详解
CMake学习例子
个人路径显示工具
CMake基本语法
CMake内置环境变量
一些技巧
file命令获取路径下所有的文件
预定义变量
PROJECT_SOURCE_DIR 工程的根目录
PROJECT_BINARY_DIR 运行cmake命令的目录,通常是${PROJECT_SOURCE_DIR}/build
CMAKE_INCLUDE_PATH 环境变量,非cmake变量
CMAKE_LIBRARY_PATH 环境变量
CMAKE_CURRENT_SOURCE_DIR 当前处理的CMakeLists.txt所在的路径
CMAKE_CURRENT_BINARY_DIR target编译目录
使用ADD_SURDIRECTORY(src bin)可以更改此变量的值
SET(EXECUTABLE_OUTPUT_PATH <新路径>)并不会对此变量有影响,只是改变了最终目标文件的存储路径
CMAKE_CURRENT_LIST_FILE 输出调用这个变量的CMakeLists.txt的完整路径
CMAKE_CURRENT_LIST_LINE 输出这个变量所在的行
CMAKE_MODULE_PATH 定义自己的cmake模块所在的路径
SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake),然后可以用INCLUDE命令来调用自己的模块
EXECUTABLE_OUTPUT_PATH 重新定义目标二进制可执行文件的存放位置
LIBRARY_OUTPUT_PATH 重新定义目标链接库文件的存放位置
PROJECT_NAME 返回通过PROJECT指令定义的项目名称
CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS 用来控制IF ELSE语句的书写方式
cmake语法
多行注释
#[=3[ (我是...]]....注释) ]===]
#[===[ (我是...]]....注释) ]===]
#[=3[ (我是...]]....注释) ]===]
#[[ (我是注释) ]]
常量意义
PROJECT_BINARY_DIR /demo/build
PROJECT_SOURCE_DIR 包含peojrct的最顶层CMakeLists.txt的路径
变量类型
所有值都会转换成String。所有的参数会被封装成List
这里有点混乱,好像布尔变量也是字符串
类型 | 值 |
---|---|
true | 1, ON, YES, TRUE, Y, 非0的值 |
false | 0, OFF, NO, FALSE, N, IGNORE, NOTFOUND, 空字符串””, 以-NOTFOUND 结尾的字符串 |
set(var hello)
message(${var}) // ${var} $ENV{var}
```IF(DEFINEDENV{ARM_ARCHITECTURE}) #如果是ARM机器```
set(var 1)
unset(var) // 移除变量
set(list_var 1 2 3 4) # list_var = 1;2;3;4
set(list_foo "5;6;7;8") # list_foo = 5;6;7;8 ;分割列表
list操作
list
list(LENGTH <list> <output variable>)
list(GET <list> <element index> [<element index> ...] <output variable>)
list(APPEND <list> [<element> ...])
list(FIND <list> <value> <output variable>)
list(INSERT <list> <element_index> <element> [<element> ...])
list(REMOVE_ITEM <list> <value> [<value> ...])
list(REMOVE_AT <list> <index> [<index> ...])
list(REMOVE_DUPLICATES <list>)
list(REVERSE <list>)
list(SORT <list>)
if
if(VAR1 MATCHES "Hello")
message("this is hello")
message("this is hello2")
elseif(VAR1 MATCHES "world")
message("this is world")
message("this is world2")
else()
message("else")
endif()
while
支持break()和continue()
while(condition)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
endwhile() // 括号里面不用管,是为了兼容保留
for
支持break()和continue()
set(mylist "a" "b" "c" "d")
foreach(_var ${mylist})
message("当前变量是:${_var}")
endforeach()
经典\(\sum_{i = 0}^{100}{i}\)
set(result 0)
foreach(_var RANGE 0 100)
math(EXPR result "${result}+${_var}")
endforeach()
message("from 0 plus to 100 is:${result}")
自动变量
$@ 规则的生成目标
$% 档案文件成员结构中的文件名元素
$< 第一个依赖文件名
$^ 所有的依赖文件名(已经消重),以空格分隔
$+ 所有的依赖文件名(未经消重),以空格分隔
$* 所有除掉后缀的依赖文件名,以空格分隔,仅适用于模式规则。注:文件名包含stem和suffix,去掉suffix就剩下了stem。比如hello.cpp的stem是hello,suffix就是cpp。
$? 比目标文件新的依赖文件。
缓存变量
1.可以缓存用户选择,下次不必再输入
2.缓存高消耗时间的额值
创建缓存变量并放在缓存文件中
option (USE_JPEG "Hello world")
set (USE_JPEG ON CACHE BOOL "Hello world") # set需要指定变量类型
变量作用域 + 函数定义
set(var1 1)
add_subdirectory(dir1) #dir1中可见var1
set(var 2)
add_subdirectory(dir2) #dir2中可见var1,var2
# 传址调用
function (foo)
message(${test}) #输出 1
set(test 2 PARENT_SCOPE) #影响父作用域中的值
message(${test}) #输出 2
endfunction()
set(test 1)
foo()
message(${test}) #输出 2
# 传值调用
function (foo)
message(${test}) #输出 1
set(test 2)
message(${test}) #输出 2
endfunction()
set(test 1)
foo()
message(${test}) #输出 1
函数体内自带参数
| 名称 | 意义 |
| ---- | ---- | ---- |
| ARGC | 参数个数 |
| ARGV | 参数列表 |
| ARGV0 | 参数0 |
| ARGV1 | 参数1 |
| ARGV2 | 参数2 |
| ARGN | 超出最后一个预期参数的参数列表 |
宏
macro(Moo arg)
message("arg = ${arg}")
set(arg "abc")
message("# After change the value of arg.")
message("arg = ${arg}")
endmacro()
和define差不多,都是字符串替换
CMake常用操作
CMAKE_C(XX)_FLAGS
CMAKE_C_FLAGS存放的内容会被传给C编译器,作用在所有的编译组态上
可以只针对特定组态如
CMAKE_C_FLAGS_RELEASE
CMAKE_C_FLAGS_DEBUG
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -O2")
还可以在cmake命令中使用,如
cmake -DBUILD_SHARED_LIBS=OFF
CHECK_CXX_COMPILER_FLAG
检查CXX编译器是否支持某个特性
比如check支持的语言版本
CMAKE_MODULE_PATH
cmake进行搜索的modules的list
find_package
咕咕咕
添加编译选项
add_compile_options(-std=c++11) 针对所有编译器
ADD_DEFINITIONS("-Wall -Werror -Wstrict-prototypes -Wmissing-prototypes) 针对所有编译器
set(CMAKE_CXX_FLAGS "-std=c++11 -Wall") 针对c++编译器,
set(CMAKE_C_FLAGS "") 针对c编译器
头文件、库包含
include_directories
增加头文件的搜索路径,相当于指定gcc的-I参数
在某些神秘的情况下,该命令会失效(需要包含多个目录)
此时需要在最近公共CMakeLists中包含多个路径,会递归应用到所有子CMakeLists中
link_directories()
去哪找库文件(.so/.dll/.lib/.dylib/...),-L(GCC)
LINK_LIBRARIES(库名称即可)
需要链接的库文件的名字:-l(GCC)
对于那些在 /lib, /usr/lib 和 /usr/local/lib 路径下的库, 我们可以使用更加简单的命令 −l 而无需添加库文件路径
target_include_directories 指定需要编译的目标的默认搜索路径
目标必须由 add_executable() or add_library()创建
target(也就是cmake产生的中间文件)
targer的属性可以通过set_target_property,get_target_property访问
target_link_libraries
用于指定target所需要连接的库,还可以有不同的选项
add_library(foo,STATIC/SHARED, foo.c)
没有指定STATIC/SHARED则根据BUILD_SHARED_LIBS变量的值判断,若无,默认STAIC
add_definitions
添加编译参数
add_definitions(-std=c++11)
add_executable
如果可执行目标名后面指定了WIN32,则生成入口为WinMain的windows应用程序
嵌入shell命令
execute_process(
WORKING_DIRECTORY <这句shell命令执行的工作目录>
COMMAND <一句shell命令>
)
还可以获取返回结果,比如判断git的某个设置
自定义中间文件
add_custom_target
add_custom_command
两个命令结合起来可以调用bash
cmake --build . --target xxx
其中cmake --build . = make,相当于一个平台无关的指令
CMake构建配置
默认支持
1.Debug CMake会打开一些基本的调试开关进行构建
2.Release CMake在构建时会打开一些基本的优化选项开关
3.MinSizeRel CMake会使得构建出来的目标代码体积尽可能小
4.RelWithDeblnfo CMake构建出带有调试信息并且经过编译优化后的版本
设置c++编译器
set(CMAKE_C_COMPILER "gcc-4.2")
set(CMAKE_CXX_COMPILER "/usr/bin/g++-4.2") // 具体的版本打开/user/bin目录查看
1.hello world!
+--- demo
|
+--- CMakeList.txt
+--- build
|
+--- hello.exe
+--- main.c
CMakeList.txt
project(demo) #不强制,但最好都加上
set(SRC_LIST main.c) #设置变量
add_executable(hello ${SRC_LIST}) #生成一个可执行文件
#add_library 生成一个库文件
注意project会引入变量<PROJECT_NAME>_BINARY_DIR(编译路径,即demo/build) 和 <PROJECT_NAME>_SOURCE_DIR(工程路径,即demo)
我们采用cmake的out-of-source方式来构建(即生成中间产物与源代码分离),并坚持这种办法
在build下调用cmake
cmake .. -G"MinGW Makefiles" #MakeFile文件位置
make
2.拆分成多个.h .c
+--- demo
|
+--- CMakeLists.txt
+--- build
|
+--- hello.exe
+--- hello.c
+--- hello.h
+--- main.c
hello.h
#program once
void hello(const char * name);
hello.c
#include "hello.h"
void hello(const char * name)
{
printf("Hello %s!",name);
}
main.c
#include "hello.h"
int main()
{
hello("world");
return 0;
}
CMakeList.txt
project(demo)
set(SRC_LIST main.c hello.c)
add_executable(hello ${SRC_LIST})
然后cmake一下就行了
3.生成lib文件
+--- demo
|
+--- CMakeList.txt
+--- build
|
+--- hello.exe
+--- lib_hello.lib
+--- hello.c
+--- hello.h
+--- main.c
hello.h
#program once
void hello(const char * name);
hello.c
#include "hello.h"
void hello(const char * name)
{
printf("Hello %s!",name);
}
main.c
#include "hello.h"
int main()
{
hello("world");
return 0;
}
CMakeList.txt
project(demo)
set(LIB_SRC hello.c)
set(APP_SRC main.c)
add_library(lib_hello ${LIB_SRC})
add_executable(hello ${APP_SRC})
target_link_libraries(hello lib_hello)
如何给lib取名hello而不是lib_hello
set_target_properties(lib_hello PROPERTIES OUTPUT_NAME "hello")
4.正规的工程目录
+--- demo
|
+--- CMakeList.txt
+--- build
|
+--- hello.exe
+--- lib_hello
|
+--- CMakeList.txt
+--- hello.c
+--- hello.h
+--- src
|
+--- CMakeList.txt
+--- main.c
/demo/CMakeList.txt
project(demo)
add_subdirectory(src)
add_subdirectory(lib_hello)
/demo/src/CMakeList.txt
include_directories(${PROJECT_SOURCE_DIR}/lib_hello)
set(APP_SRC main.c)
add_executable(hello ${APP_SRC})
target_link_libraries(hello lib_hello)
/demo/lib_hello/CMakeList.txt
set(LIB_SRC hello.c)
add_library(lib_hello ${LIB_SRC})
set_target_properties(lib_hello PROPERTIES OUTPUT_NAME "hello")
然后在build下cmake,用/demo/CMakeList.txt即可
5.分类可执行文件和库文件
+--- demo
|
+--- CMakeList.txt
+--- build
|
+--- bin
|
+--- hello.exe
+--- lib_hello
|
+--- CMakeList.txt
+--- hello.c
+--- hello.h
+--- hello.lib
+--- src
|
+--- CMakeList.txt
+--- main.c
/demo/src/CMakeList.txt
include_directories(${PROJECT_SOURCE_DIR}/lib_hello)
set(APP_SRC main.c)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
add_executable(hello ${APP_SRC})
target_link_libraries(hello lib_hello)
/demo/build/lib_hello/CMakeList
set(LIB_SRC hello.c)
add_library(lib_hello ${LIB_SRC})
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
set_target_properties(lib_hello PROPERTIES OUTPUT_NAME "hello")
6.最终的工程结构示范
所有的代码正常include头文件即可(静态库、动态库不需要在代码里额外的设置)
+--- CMakeTemplate
|
+--- CMakeLists.txt
+--- build
|
+--- bin
+--- lib
+--- so
+--- src
|
+--- CMakeLists.txt
+--- main.cpp
+--- libHello
|
+--- CMakeLists.txt
+--- Hello.h
+--- Hello.cpp
+--- soPrint
|
+--- CMakeLists.txt
+--- Print.h
+--- Print.cpp
CMakeTemplate/CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
set(CMAKE_CXX_COMPILER "/usr/bin/g++-4.8")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall")
project(CMakeTemplate)
add_subdirectory(src)
CMakeTemplate/src/CMakeLists.txt
add_subdirectory(libHello)
add_subdirectory(soPrint)
include_directories(${PROJECT_SOURCE_DIR}/src/libHello)
include_directories(${PROJECT_SOURCE_DIR}/src/soPrint)
set(APP_SRC main.cpp)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
add_executable(${PROJECT_NAME} ${APP_SRC})
target_link_libraries(${PROJECT_NAME} libHello soPrint)
CMakeTemplate/src/libHello/CMakeLists.txt
set(LIB_SRC Hello.cpp)
add_library(libHello ${LIB_SRC})
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
set_target_properties(libHello PROPERTIES OUTPUT_NAME "Hello")
CMakeTemplate/src/soPrint/CMakeLists.txt
set(SO_SRC Print.cpp)
add_library(soPrint SHARED ${SO_SRC})
set_target_properties(soPrint PROPERTIES OUTPUT_NAME "Print")
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/build/so)
本文来自博客园,作者:XDU18清欢,转载请注明原文链接:https://www.cnblogs.com/XDU-mzb/p/15358339.html