Cmake语法学习1:语法+常用命令

目录

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.9 list(列表)

2.10 message(打印)

2.11project(指定工程名称)

2.12set(设置变量)


1.语法简介

1.1注释

        在 CMakeLists.txt 文件中,使用“ # ”号进行单行注释,譬如:
#
# 这是注释信息
#
cmake_minimum_required(VERSION 3.5)
project(HELLO)

1.2命令

        通常在 CMakeLists.txt 文件中,使用最多的是命令,譬如 cmake_minimum_required project 都是命。

1.2.1格式

        命令的使用方式有点类似于 C 语言中的函数,因为命令后面需要提供一对括号,并且通常需要我们提供参数,多个参数使用空格 分隔而不是逗号“ , ”,这是与函数不同的地方。
        命令的语法格式如下所示:
command(参数 1 参数 2 参数 3 ...)

 1.2.2参数      

        不同的命令所需的参数不同,需要注意的是,参数可以分为必要参数和可选参数(通常称为选项),很多命令都提供了这两类参数,必要参数使用<参数 > 表示,而可选参数使用 [ 参数 ] 表示。
        例如
set( ... [PARENT_SCOPE])
        set 命令用于设置变量,第一个参数 和第二个参数 是必要参数,在参数列表( 表示参数个数没有限制)的最后可以添加一个可选参数 PARENT_SCOP PARENT_SCOPE 选项),根据实际使用情况确定是否需要添加。

1.2.3备注

        在 CMakeLists.txt 中,命令名不区分大小写,可以使用大写字母或小写字母书写命令名。作用是一样的。
project(HELLO) #小写
PROJECT(HELLO) #大写

1.3变量

        在 CMakeLists.txt 文件中可以使用变量,使用 set 命令可以对变量进行设置。
        例如, 通过 set 命令对变量 MY_VAL 进行设置,将其内容设置为 "Hello World!",可以通过${MY_VAL}的方式引用变量。
# 设置变量 MY_VAL
set(MY_VAL "Hello World!")
#引用变量 MY_VAL
message(${MY_VAL})

2.常用命令

        cmake 提供了很多命令,通过官网可以查询到所有的命令及其相应的介绍、使用方法等等

https://cmake.org/cmake/help/v3.5/manual/cmake-commands.7.html

         常用命令如下表

Cmake语法学习1:语法+常用命令_第1张图片

2.1add_executable-添加可执行目标

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)

        备注:源文件路径既可以使用相对路径、也可以使用绝对路径,相对路径被解释为相对于当前源码路径。

2.2add_library-添加库文件目标

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)与 add_executable 命令相同, add_library 命令中源文件既可以使用相对路径指定、也可以使用绝对路径指定,相对路径被解释为相对于当前源码路径。
        (2)不管是 add_executable 、还是 add_library ,它们所定义的 目标名在整个工程中必须是唯一 的,不可出现两个目标名相同的目标。
        (3) 不包含前缀和后缀;在 Linux 系统中,库文件的前缀是 lib。动态库文件的后
缀是 .so ,而静态库文件的后缀是.a。所以,意味着最终生成的库文件对应的名字会自动添加上前缀和后缀。 对于上面的例子,生成的动态库和静态库名字分别是:libmylib.a和llibmylib.so。

2.3add_subdirectory-指定源码子目录

1)作用

        告诉 cmake 去指定的目录中寻找源码并执行它

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

2.4aux_source_directory-找到目录所有源文件

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 ..命令,打印信息如下所示:

Cmake语法学习1:语法+常用命令_第2张图片

        同理,aux_source_directory 既可以使用相对路径,也可以使用绝对路径,相对路径是相对于当前源码路
径。

2.5get_target_property

2.6set_target_properties

2.7include_directories-设置头文件搜索路径

1)作用

        用于设置头文件的搜索路径,相当于 gcc -I 选项        

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 

        当调用 add_subdirectory 命令加载子源码时,会将 include_directories 命令包含的目录列表向下传递给子源码(子源码从父源码中继承过来)
├── 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)
        顶层 CMakeLists.txt 源码中调用了 include_directories include 目录添加到当前源码的头文件搜索列表中,接着调用 add_subdirectory 命令加载、执行子源码; src 目录下 CMakeLists.txt 内容如下所示:
# src 目录 CMakeLists.txt
add_executable(hello main.c)

        进入到 build 目录,进行构建、编译,整个编译过程是没有问题的。

1)作用

link_directories 命令用于设置库文件的搜索路径,相当于 gcc 编译器的 -L 选项;

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.soCMakeLists.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)
        link_libraries 命令也可以指定库文件的全路径(绝对路径 / 开头),如果不是 / 开头, link_libraries 会认 为调用者传入的是库文件名,而非库文件全路径。
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 命令包含的链接库列表向下传递给子源码(子源码从父源码中继承过来)。

2.9 list(列表)

1)作用

        是一个关于列表操作的命令,譬如获取列表的长度、从列表中返回由索引值指定的元素、将元素追加到列表中等等。类似数组。

2)格式

list(LENGTH  )
list(GET   [ ...] )
list(APPEND  [ ...])
list(FIND   )
list(INSERT    [ ...])
list(REMOVE_ITEM   [ ...])
list(REMOVE_AT   [ ...])
list(REMOVE_DUPLICATES )
list(REVERSE )
list(SORT )
(1)LENGTH 选项用于返回列表长度;
(2)GET 选项从列表中返回由索引值指定的元素;
(3)APPEND 选项将元素追加到列表后面;
(4)FIND 选项将返回列表中指定元素的索引值,如果未找到,则返回 -1
(5)INSERT 选项将向列表中的指定位置插入元素。
(6)REMOVE_AT REMOVE_ITEM 选项将从列表中删除元素,不同之处在于 REMOVE_ITEM 将删除给定的元素,而 REMOVE_AT 将删除给定索引值的元素。
(7)REMOVE_DUPLICATES 选项将删除列表中的重复元素。
(8)REVERSE 选项就地反转列表的内容。
(9)SORT 选项按字母顺序对列表进行排序。

3)例子

2.10 message(打印)

1)作用

        用于打印、输出信息,类似于 Linux echo 命令

2)格式

message([] "message to display" ...)

(1)mode

Cmake语法学习1:语法+常用命令_第3张图片

3)例子

# 打印"Hello World"
message("Hello World!")

2.11project(指定工程名称)

1)作用

        用于指定cmake工程的名称;

2)格式

3)例子

# 设置工程名称为 HELLO
project(HELLO)
        执行这个之后会引入两个变量:HELLO_SOURCE_DIR HELLO_BINARY_DIR ,注意这两个变量名的前缀就是工程名称。
        HELLO_SOURCE_DIR 变量指的是 HELLO 工程源码目录;
        HELLO_BINARY_DIR 变量指的是 HELLO 工程源码的输出文件目录;
        我们可以使用 message 命令打印变量
# 顶层 CMakeLists.txt
cmake_minimum_required("VERSION" "3.5")
project("HELLO")
message(${HELLO_SOURCE_DIR})
message(${HELLO_BINARY_DIR})

Cmake语法学习1:语法+常用命令_第4张图片

        如果不加入 project(HELLO) 命令,这两个变量是不存在的;工程源码目录指的是顶层源码所在目录;
        cmake 定义了两个等价的变量 PROJECT_SOURCE_DIR PROJECT_BINARY_DIR ,通常在 CMakeLists.txt源码中都会使用这两个等价的变量。
        通常只需要在顶层 CMakeLists.txt 源码中调用 project 即可!

2.12set(设置变量)

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})

Cmake语法学习1:语法+常用命令_第5张图片

4)例子2 

        通过 set 命令实现字符串列表
# 字符串列表
set(SRC_LIST 1.c 2.c 3.c 4.c 5.c)

        此时 SRC_LIST 就是一个列表,它包含了 5 个元素(1.c2.c3.c4.c5.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})

Cmake语法学习1:语法+常用命令_第6张图片

        从上图看,SRC_LIST 就是一个普通的变量( SRC_LIST=1.c2.c3.c4.c5.c) ,并不是列表呢?事实并非如此,我们可以修改 message 命令,将 ${SRC_LIST} 放置在双引号中,如下:
# 打印变量
message("${SRC_LIST}")
结果变成了下图:   Cmake语法学习1:语法+常用命令_第7张图片

双引号的作用:

1)在命令参数中添加 “  ”,他会把“参数 ”当做一个整体;

2)在引用变量时添加“ ”,也会把变量当做一个整体;

具体可以参考《双引号的作用》介绍。

1)作用

target_include_directories 命令为指定目标设置头文件搜索路径
target_link_libraries 命令为指定目标设置链接库文件;

include_directories用于设置头文件的搜索路径,相当于 gcc -I 选项        

link_libraries 命令用于设置需要链接的库文件,相当于 gcc 编译器的-l 选项。

与上面的 include_directories和link_libraries有轻微区别。通过例子说明。

2)格式

target_include_directories( [SYSTEM] [BEFORE]
                            [items1...]
                           [ [items2...] ...])
target_link_libraries(
                       ...
                      [ ...]...)

(1) :目标,这个目标指的就是譬如 add_executableadd_library 命令所创建的目标。

(2)SYSTEM 、 BEFORE 这两个选项与include_directories 命令中SYSTEMBEFORE选项的意义相同。

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、PUBLICPRIVATE参数区别:

(1)PRIVATE:私有的。

        main.c 程序调用了 libhello_world.so;

        生成 libhello_world.so 时,只在 hello_world.c中包含了 hello.hlibhello_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)
  (2)INTERFACE:接口。 
   

        生成 libhello_world.so 时,只在 libhello_world.so 对外的头文件(hello_world.h)中包含了 hello.hhello_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)
(3)PUBLIC: 公开的。
        PUBLIC = PRIVATE + INTERFACE
        生成 libhello_world.so 时,在 hello_world.c 和hello_world.h 中 都 包 含 了 hello.h
        并 且 main.c 中 也 需 要 使 用 libhello.so 提 供 的 功 能 。 那么hello_world/CMakeLists.txt 中应该写入:
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 加载子源码时,也会将其传递给子源码)。在一个大的工程当中,这通常不规范、有时还会编译出现错误、混乱。

你可能感兴趣的:(Linux软件,PC端软件,计算机底层架构+原理,学习)