目录
1.语法简介
1.1注释
1.2命令
1.2.1格式
1.2.2参数
1.2.3备注
1.3变量
2.常用命令
2.1add_executable-添加可执行目标
2.2add_library-添加库文件目标
2.3add_subdirectory-指定源码子目录
2.4aux_source_directory-找到目录所有源文件
2.5get_target_property
2.6set_target_properties
2.7include_directories-设置头文件搜索路径
2.8link_directories (库文件路径)和 link_libraries(库文件)
2.9 list(列表)
2.10 message(打印)
2.11project(指定工程名称)
2.12set(设置变量)
2.13target_include_directories(头文件路径) 和 target_link_libraries(库文件)
#
# 这是注释信息
#
cmake_minimum_required(VERSION 3.5)
project(HELLO)
command(参数 1 参数 2 参数 3 ...)
set( ... [PARENT_SCOPE])
project(HELLO) #小写
PROJECT(HELLO) #大写
# 设置变量 MY_VAL
set(MY_VAL "Hello World!")
#引用变量 MY_VAL
message(${MY_VAL})
cmake 提供了很多命令,通过官网可以查询到所有的命令及其相应的介绍、使用方法等等
https://cmake.org/cmake/help/v3.5/manual/cmake-commands.7.html
常用命令如下表
1)作用
用于添加一个可执行程序目标,并设置目标所需的源文件
2)格式
add_executable( [WIN32] [MACOSX_BUNDLE] [EXCLUDE_FROM_ALL] source1 [source2 ...])
(1)name:目标名
(2)source:源文件
其他的,暂时不关心
3)例子
#生成可执行文件 hello
add_executable(hello 1.c 2.c 3.c)
备注:源文件路径既可以使用相对路径、也可以使用绝对路径,相对路径被解释为相对于当前源码路径。
1)作用
2)格式
add_library( [STATIC | SHARED | MODULE]
[EXCLUDE_FROM_ALL]
source1 [source2 ...])
(1)name:目标名
(2)source:源文件列表
(3)选项:默认生成的库文件是静态库文件(STATIC),通过 SHARED 选项可使其生成动态库文件(SHARED)。
3)例子
#生成静态库文件 libmylib.a
add_library(mylib STATIC 1.c 2.c 3.c)
#生成动态库文件 libmylib.so
add_library(mylib SHARED 1.c 2.c 3.c)
备注:
1)作用
2)格式
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
(1)source_dir :指定一个目录,告诉cmake 去该目录下寻找 CMakeLists.txt文件并执行它;
(2)binary_dir:指定了一个路径,该路径作为子源码(调用 add_subdirectory 命令的源码称为当前源码或父源码,被执行的源码称为子源码)的输出文件(cmake 命令所产生的中间文件)目录,binary_dir 参数是一个可选参数,如果没有显式指定,则会使用一个默认的输出文件目录;为了后续便于表述,我们将输出文件目录称为BINARY_DIR。
3)例子1
├── build
├── CMakeLists.txt
└── src
├── CMakeLists.txt
└── main.c
# 顶层 CMakeLists.txt
cmake_minimum_required("VERSION" "3.5")
project("HELLO")
# 告诉 cmake 去 src 目录下寻找 CMakeLists.txt
add_subdirectory(src)
# src 下的 CMakeLists.txt
add_executable(hello main.c)
(1)执行过程
进入build目录,执行
cmake ../
然后通过make编译。
(2)分析
├── build
│ ├── CMakeCache.txt
│ ├── CMakeFiles
│ ├── cmake_install.cmake
│ ├── Makefile
│ └── src
│ ├── CMakeFiles
│ ├── cmake_install.cmake
│ ├── hello
│ └── Makefile
├── CMakeLists.txt
└── src
├── CMakeLists.txt
└── main.c
顶层源码对应的输出文件会存放在 build 目录,也就是执行 cmake 命令所在目录;
子源码(src 目录下的 CMakeLists.txt)对应的输出文件会存放在 build/src 目录,包括生成的可执行文件默认会与这些中间文件放置在同一个目录。
综上所述:当前源码调用add_subdirectory命令执行子源码时,若没有为子源码指定BINARY_DIR,默认情况下,会在当前源码的 BINARY_DIR 中创建与子目录(子源码所在目录)同名的文件夹,将其作为子源码的 BINARY_DIR。
1)作用
将一个目录中的所有源文件添加到一个变量中。
2)格式
aux_source_directory( )
(1)dir:要搜索源文件的目录名称
(2)variable:变量,用于存储找到的源文件列表
3)例子
aux_source_directory
函数会搜索指定目录(dir)下的所有源文件,并将它们的文件名(包括路径)存储在变量variable
中。
这个函数会自动将所有符合条件的源文件添加到变量中,所以不需要手动一个一个地列举所有的源文件。
├── build
├── CMakeLists.txt
└── src
├── 1.c
├── 2.c
├── 2.cpp
└── main.c
# 顶层 CMakeLists.txt
cmake_minimum_required("VERSION" "3.5")
project("HELLO")
# 查找 src 目录下的所有源文件
aux_source_directory(src SRC_LIST)
message("${SRC_LIST}") # 打印 SRC_LIST 变量
进入到 build 目录下,执行 cmake ..命令,打印信息如下所示:
1)作用
2)格式
include_directories([AFTER|BEFORE] [SYSTEM] dir1 [dir2 ...])
(1)dir:默认情况下会将指定目录dir添加到头文件搜索列表。
(2)AFTER|BEFORE:会将dir添加到头文件搜索列表(可以认为每一个 CMakeLists.txt 源码都有自己的头文件搜索列表)的最后面,可以通过设CMAKE_INCLUDE_DIRECTORIES_BEFORE 变量为 ON 来改变它 默认行为,将目录添加到列表前面。也可以在每次调用 include_directories 命令时使用 AFTER 或 BEFORE 选项来指定是添加到列表的前面或者后面。
(3)如果使用 SYSTEM 选项,会把指定目录当成系统的搜索目录。 既可以使用绝对路径来指定头文件搜索目录、也可以使用相对路径来指定,相对路径被解释为当前源码路径的相对路径。
3)例子1
├── build
├── CMakeLists.txt
├── include
│ └── hello.h
└── main.c
# 顶层 CMakeLists.txt
cmake_minimum_required("VERSION" "3.5")
project("HELLO")
include_directories(include)
add_executable(hello main.c)
默认情况下,include 目录被添加到头文件搜索列表的最后面,通过 AFTER 或 BEFORE 选项可显式指 定添加到列表后面或前面。
# 添加到列表后面
include_directories(AFTER include)
# 添加到列表前面
include_directories(BEFORE include)
4)例子2
├── build
├── CMakeLists.txt
├── include
│ └── hello.h
└── src
├── CMakeLists.txt
└── main.c
# 顶层 CMakeLists.txt
cmake_minimum_required("VERSION" "3.5")
project("HELLO")
include_directories(include)
add_subdirectory(src)
# src 目录 CMakeLists.txt
add_executable(hello main.c)
进入到 build 目录,进行构建、编译,整个编译过程是没有问题的。
1)作用
link_libraries 命令用于设置需要链接的库文件,相当于 gcc 编译器的-l 选项。
2)格式
link_directories(directory1 directory2 ...)
link_libraries([item1 [item2 [...]]] [[debug|optimized|general] - ] ...)
(1)link_directories
会将指定目录添加到库文件搜索列表(可以认为每一个 CMakeLists.txt 源码都有自己的库文件搜索列表)中;
(2)link_libraries 命令会将指定库文件添加到链接库列表。link_directories 命令可以使用绝对路径或相对路径指定目录,相对路径被解释为当前源码路径的相对路径。
3)例子1
├── build
├── CMakeLists.txt
├── include
│ └── hello.h
├── lib
│ └── libhello.so
└── main.c
在 lib 目录下有一个动态库文件 libhello.so,编译链接 main.c 源文件时需要链接 libhello.so;CMakeLists.txt文件内容如下所示:
# 顶层 CMakeLists.txt
cmake_minimum_required("VERSION" "3.5")
project("HELLO")
include_directories(include)
link_directories(lib)
link_libraries(hello)
add_executable(main main.c)
库文件名既可以使用简写,也可以库文件名的全称
# 简写
link_libraries(hello)
# 全称
link_libraries(libhello.so)
cmake_minimum_required("VERSION" "3.5")
project("HELLO")
include_directories(include)
link_libraries(${PROJECT_SOURCE_DIR}/lib/libhello.so)
add_executable(main main.c)
与 include_directories 命令相同,当调用 add_subdirectory 命令加载子源码时,会将 link_directories 命令包含的目录列表以及 link_libraries 命令包含的链接库列表向下传递给子源码(子源码从父源码中继承过来)。
1)作用
2)格式
list(LENGTH
3)例子
1)作用
2)格式
message([] "message to display" ...)
(1)mode
3)例子
# 打印"Hello World"
message("Hello World!")
1)作用
用于指定cmake工程的名称;
2)格式
3)例子
# 设置工程名称为 HELLO
project(HELLO)
# 顶层 CMakeLists.txt
cmake_minimum_required("VERSION" "3.5")
project("HELLO")
message(${HELLO_SOURCE_DIR})
message(${HELLO_BINARY_DIR})
1)作用
2)格式
set( ... [PARENT_SCOPE])
设置变量的值,可选参数 PARENT_SCOPE 影响变量的作用域
3)例子1
# 顶层 CMakeLists.txt
cmake_minimum_required("VERSION" "3.5")
project("HELLO")
# set 命令
set(VAR1 Hello) #设置变量 VAR1=Hello
set(VAR2 World) #设置变量 VAR2=World
# 打印变量
message(${VAR1} " " ${VAR2})
4)例子2
# 字符串列表
set(SRC_LIST 1.c 2.c 3.c 4.c 5.c)
此时 SRC_LIST 就是一个列表,它包含了 5 个元素(1.c、2.c、3.c、4.c、5.c),列表的各个元素使用分号“;”分隔
# 顶层 CMakeLists.txt
cmake_minimum_required("VERSION" "3.5")
project("HELLO")
# set 命令
set(SRC_LIST 1.c 2.c 3.c 4.c 5.c)
# 打印变量
message(${SRC_LIST})
双引号的作用:
1)在命令参数中添加 “ ”,他会把“参数 ”当做一个整体;
2)在引用变量时添加“ ”,也会把变量当做一个整体;
具体可以参考《双引号的作用》介绍。
1)作用
include_directories用于设置头文件的搜索路径,相当于 gcc 的-I 选项
link_libraries 命令用于设置需要链接的库文件,相当于 gcc 编译器的-l 选项。
与上面的 include_directories和link_libraries有轻微区别。通过例子说明。
2)格式
target_include_directories( [SYSTEM] [BEFORE]
[items1...]
[ [items2...] ...])
target_link_libraries(
- ...
[
- ...]...)
(1)
(2)SYSTEM 、 BEFORE 这两个选项与include_directories 命令中SYSTEM、BEFORE选项的意义相同。
3)例子
目录结构
├── build //build 目录
├── CMakeLists.txt
├── hello_world //生成 libhello_world.so,调用 libhello.so 和 libworld.so
│ ├── CMakeLists.txt
│ ├── hello //生成 libhello.so
│ │ ├── CMakeLists.txt
│ │ ├── hello.c
│ │ └── hello.h //libhello.so 对外头文件
│ ├── hello_world.c
│ ├── hello_world.h //libhello_world.so 对外头文件
│ └── world //生成 libworld.so
│ ├── CMakeLists.txt
│ ├── world.c
│ └── world.h //libworld.so 对外头文件
└── main.c
调用关系:
├────libhello.so
可执行文件────libhello_world.so
├────libworld.so
INTERFACE、PUBLIC、PRIVATE参数区别:
(1)PRIVATE:私有的。
main.c 程序调用了 libhello_world.so;
生成 libhello_world.so 时,只在 hello_world.c中包含了 hello.h,libhello_world.so 对外的头文件(hello_world.h) 中不包含 hello.h。
而且 main.c 不会调用hello.c 中的函数,或者说 main.c 不知道 hello.c 的存在,它只知道 libhello_world.so 的存在。
那么在 hello_world/CMakeLists.txt 中应该写入:
target_link_libraries(hello_world PRIVATE hello)
target_include_directories(hello_world PRIVATE hello)
生成 libhello_world.so 时,只在 libhello_world.so 对外的头文件(hello_world.h)中包含了 hello.h,hello_world.c 中不包含 hello.h,即 libhello_world.so 不使用 libhello.so 提供的功能;
但是main.c 需要使用 libhello.so 中的功能。那么在 hello_world/CMakeLists.txt 中应该写入
target_link_libraries(hello-world INTERFACE hello)
target_include_directories(hello-world INTERFACE hello)
target_link_libraries(hello-world PUBLIC hello)
target_include_directories(hello-world PUBLIC hello)
综上所述:
(1)当使用 PRIVATE 关键字修饰时,意味着包含目录列表仅用于当前目标;
(2)当使用 INTERFACE 关键字修饰时,意味着包含目录列表不用于当前目标、只能用于依赖该目标
的其它目标,也就是说 cmake 会将包含目录列表传递给当前目标的依赖目标;
(3)当使用 PUBLIC 关键字修饰时,这就是以上两个的集合,包含目录列表既用于当前目标、也会传
递给当前目标的依赖目标。
综上所述:
(1)target_include_directories() 、target_link_libraries()的功能完全可以使用 include_directories() 、 link_libraries() 来实现。(2) 强烈建议使用target_include_directories()和 target_link_libraries() , 保持代码清晰!include_directories() 、 link_libraries()是针对当前源码中的所有目标,并且还会向下传递(譬如通过 add_subdirectory 加载子源码时,也会将其传递给子源码)。在一个大的工程当中,这通常不规范、有时还会编译出现错误、混乱。