CMake编写规则

CMake是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程)。Cmake 并不直接建构出最终的软件,而是产生标准的建构档(如 Unix 的 Makefile 或 Windows Visual C++ 的 projects/workspaces),然后再依一般的建构方式使用。

1.安装

cmake下载地址:[https://cmake.org/download/]

本文使用的是Ubuntu Linux环境,通过apt安装cmake:

$ sudo apt update
$ sudo apt install cmake

2.构建和运行

构建如下文件:

.
├── build
└── src
    ├── CMakeLists.txt
    └── main.c

main.c

1 #include <stdio.h>
2 
3 
4 int main(void)
5 {
6     printf("hello world.\n");
7 
8     return 0;
9 }

CMakeLists.txt

# 运行此文件的cmake最低版本
cmake_minimum_required(VERSION 3.10)

# 设置项目名Tutorial
project(Tutorial)

# 增加可执行文件Tutorial,由如下文件关联:main.c
add_executable(Tutorial main.c)

注:CMakeLists.txt中的关键字大小写均可,这里默认使用小写。

有两种构建方式(二选一,推荐第二种):

1).内部构建

$ cd ./src
$ cmake .

2).外部构建

$ cd ./build
$ cmake ../src/

无论是内部构建还是外部构建,都会生成一系列文件,由于平台是Linux,所以会生成Makefile文件。

3).编译

一般使用如下方式:

$ cmake --build .

因为是在Linux环境下,所以也可以使用make编译,如下:

$ make

4).运行

本例编译后生成叫"Tutorial"的可执行文件,直接运行:

$ ./Tutorial

3.说明

1).cmake命令不区分大小写,但是参数、变量区分大小写;
2).参数用空格或分号隔开;
3).使用${VAR}引用变量;
4).引号可加可不加,但如果字符串中有空格则要加引号;

4.概念

1).目标文件(target): 可执行文件(add_executable)、库文件(add_library);
2).命令(cmake-command):用来设置数据的命令;
3).变量(cmake-variable):以"CMAKE_"开头的变量名;
4).属性(cmake-properties):文件/文件夹都有各自的属性;

5.命令

5.1 cmake_minimum_required

# 设置最低cmake版本
cmake_minimum_required(VERSION <min>)

例:

cmake_minimum_required(VERSION 3.10)

5.2 project

# 设置项目名
project(<PROJECT-NAME> [<language-name>...])
project(<PROJECT-NAME>
        [VERSION <major>[.minor[.<patch>[.<tweak>]]]]
        [DESCRIPTION <project-description-string>]
        [HOMEPAGE_URL <url-string>]
        [LANGUAGES <language-name>...])

# 项目名会被存储在变量PROJECT_NAME和CMAKE_PROJECT_NAME中
# PROJECT_SOURCE_DIR等价于<PROJECT-NAME>_SOURCE_DIR
# PROJECT_BINARY_DIR等价于<PROJECT-NAME>_BINARY_DIR

# 如果定义了版本号
# 版本号被保存在PROJECT_VERSION和<PROJECT-NAME>_VERSION中
# 主版本号被保存在PROJECT_VERSION_MAJOR和<PROJECT-NAME>_VERSION_MAJOR中
# 次版本号被保存在PROJECT_VERSION_MINOR和<PROJECT-NAME>_VErSION_MINOR中

例:

project(Tutorial)
project(Tutorial C CXX)
project(Tutorial VERSION 2.3 LANGUAGES CXX)

5.3 add_executable

# 用指定的源文件为项目添加可执行文件
add_executable(<name> [WIN32] [MACOSX_BUNDLE]
                [EXCLUDE_FROM_ALL]
                [sourcel] [source2] ...)

# <name>即生成可执行文件的名字(与项目名没有关系),在一个项目中必须唯一

例:

add_executable(Tutorial tutorial.cxx)

5.4 message

# 打印信息
message([<mode>] "message text" ...)

# STATUS 前缀为"--"的信息
# SEND_ERROR产生错误,跳过生成过程
# FATAL_ERROR产生错误,终止运行

例:

message(STATUS "${PROJECT_VERSION_MAJOR}")

5.5 set

# 将变量设置为指定值
set(<variable> <value>)

例:

# 设置C++标准
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REOUIRED True)

# 设置输出文件位置
# 设置运行时目标(exe、dll、so)的输出位置
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

# 设置存档目标文件(lib、a)的输出位置
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)

5.6 option

# 定义一个开关
option(<variable> "<help_text>" [value])

# value的值为ON或OFF,默认为OFF
# option的值可以通过命令行改变 -D <variable>=ON/OFF

例:

option(VERSION_ENABLE "output version" ON)

5.7 configure_file

# 将输入文件进行替换并生成输出文件
configure_file(<input> <output>)

# 输入文件中形如@VAR@或${VAR}的字符串会被替换为这些变量的当前值,如果未定义则被替换为空字符串
#cmakedefine VAR ...
// 会被替换为以下两行之一,取决于VAR是否被设置
1.被设置会替换成:#define VAR ...
2.没有被设置替换成:/* #undef VAR */

例:

目录树

.
├── build
└── src
    ├── CMakeLists.txt
    ├── config.h.in
    └── main.c

main.c

 1 #include <stdio.h>
 2 #include "config.h"
 3 
 4 int main(void)
 5 {
 6     printf("hello world.\n");
 7     printf("Version %d.%d.\n", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR);
 8 
 9     return 0;
10 }

config.h.in

1 #cmakedefine PROJECT_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
2 #cmakedefine PROJECT_VERSION_MINOR @PROJECT_VERSION_MINOR@

CMakeLists.txt

 1 # 设置运行此文件的最低cmake版本
 2 cmake_minimum_required(VERSION 3.10)
 3 
 4 # 设置项目名和版本号
 5 # 版本1.2,其中1是Major号,2是Minor号
 6 # Major号有效,会设置PROJECT_VERSION_MAJOR
 7 # Minor号有效,会设置PROJECT_VERSION_MINOR
 8 project(Tutorial VERSION 1.2)
 9 
10 # 将输入文件进行替换并生成输出文件
11 # 由于设置了版本号1.2,在输出文件config.h中
12 # 会定义并替换Major和Minor真实的数字
13 configure_file(config.h.in config.h)
14 
15 # 增加可执行文件,关联main.c
16 add_executable(Tutorial main.c)
17 
18 # 指定目标的头文件路径
19 # 用来指明本例生成的config.h会在目标路径中
20 target_include_directories(Tutorial PUBLIC "${PROJECT_BINARY_DIR}")

运行方式

$ cd build
$ cmake ../src/
$ cmake --build .
$ ./Tutorial
hello world.
Version 1.2.

生成的config.h的内容如下:

1 #define PROJECT_VERSION_MAJOR 1
2 #define PROJECT_VERSION_MINOR 2

5.8 include_directories

# 指定所有目标的头文件路径
include_directories(dir1 [dir2 ...])
# 目录会被添加到当前文件的INCLUDE_DIRECTORIES属性中
# 当前文件的每一个目标文件的INCLUDE_DIRECTORIES属性中也会田间该目录

例:

include_directories(${PROJECT_BINARY_DIR})

5.9 target_include_directories

# 指定目标的头文件路径
target_include_directories(<target>
                            <INTERFACE|PUBLIC|PRIVATE> [items1...]
                            [<INTERFACE|PUBLIC|PRIVATE> [items2] ...])

例:

target_include_directories(Tutorial PUBLIC "${PROJECT_BINARY_DIR}")

5.10 add_subdirectory

# 添加源文件目录
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FORM_ALL])

# binary_dir 指定编译结果存放的位置

目录树

.
├── build
└── src
    ├── CMakeLists.txt
    ├── calc
    │   ├── CMakeLists.txt
    │   ├── add.c
    │   ├── add.h
    │   ├── sub.c
    │   └── sub.h
    ├── config.h.in
    └── main.c

例(src/CMakeLists.txt):

add_subdirectory(calc)

6.库

6.1 add_library

# 用指定的原为见生成库
add_library(<name> [STATIC | SHARED | MODULE]
            [EXCLUDE_FORM_ALL]
            [<source>...])
# STATIC 静态库
# SHARED 动态库
# 生成的库文件名为lib<name>.xxx

6.2 target_link_libraries

# 为目标链接库
target_link_libraries(<target>
                    <PRIVATE|PUBLIC|INTERFACE> <item>...
                    [<PRIVATE|PUBLIC|INTERFACE> <item>...]...)
# item可以是target名,绝对路径(必须保证文件存在)

6.3 区分

# 头文件目录
include_directories()
target_include_directories()

# 链接时库目录
link_directories()
target_link_directories()

# 链接库
link_libraries()
target_link_libraries()

# 默认推荐使用以"target_"开头的关键字,因为不带"target_"的关键字是全局包含、向下传递的

例:

目录树

.
├── build
└── src
    ├── CMakeLists.txt
    ├── calc
    │   ├── CMakeLists.txt
    │   ├── add.c
    │   ├── add.h
    │   ├── sub.c
    │   └── sub.h
    ├── config.h.in
    └── main.c

src/main.c

 1 #include <stdio.h>
 2 
 3 #include "config.h"
 4 #include "add.h"
 5 #include "sub.h"
 6 
 7 
 8 int main(void)
 9 {
10     printf("hello world.\n");
11     printf("Version %d.%d.\n", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR);
12     printf("3 + 2 = %d.\n", add(3, 2));
13     printf("3 - 2 = %d.\n", sub(3, 2));
14 
15     return 0;
16 }

src/config.h.in

1 #cmakedefine PROJECT_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
2 #cmakedefine PROJECT_VERSION_MINOR @PROJECT_VERSION_MINOR@

src/CMakeLists.txt

 1 # 运行此文件的最低cmake版本
 2 cmake_minimum_required(VERSION 3.10)
 3 
 4 # 可执行文件存放位置
 5 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
 6 
 7 # 动态库存放位置
 8 set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
 9 
10 # 静态库存放位置
11 set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
12 
13 # 设置项目名
14 project(Tutorial VERSION 1.2)
15 
16 # 替换并输出config.h
17 configure_file(config.h.in config.h)
18 
19 # 增加子目录
20 add_subdirectory(calc)
21 
22 # 添加可执行文件,关联main.c
23 add_executable(Tutorial main.c)
24 
25 # 链接目标库
26 target_link_libraries(Tutorial PUBLIC addition)
27 target_link_libraries(Tutorial PUBLIC subtract)
28 
29 # 添加搜索头文件路径的位置
30 target_include_directories(Tutorial PUBLIC
31                         ${PROJECT_BINARY_DIR}
32                         ${PROJECT_SOURCE_DIR}/calc
33                         )

src/calc/add.h

1 #ifndef __ADD_H__
2 #define __ADD_H__
3 
4 
5 int add(int a, int b);
6 
7 #endif /* __ADD_H__ */

src/calc/add.c

1 #include "add.h"
2 
3 int add(int a, int b)
4 {
5     return a + b;
6 }

src/calc/sub.h

1 #ifndef __SUB_H__
2 #define __SUB_H__
3 
4 
5 int sub(int a, int b);
6 
7 #endif /* __SUB_H__ */

src/calc/sub.c

1 #include "sub.h"
2 
3 
4 int sub(int a, int b)
5 {
6     return a - b;
7 }

src/calc/CMakeLists.txt

1 # 生成名为addition的静态库,关联add.c
2 # Linux下为libaddition.a
3 add_library(addition STATIC add.c)
4 
5 # 生成名为subtract的动态库,关联sub.c
6 # Linux下为libsubtract.so
7 add_library(subtract SHARED sub.c)

运行测试

$ cd build
$ cmake ../src/
$ cmake --build .
$ ./bin/Tutorial
hello world.
Version 1.2.
3 + 2 = 5.
3 - 2 = 1.

7.安装

# 安装目标
install(TARGETS <target> DESTINATION <dir>)
# 安装文件
install(FILES <file> DESTINATION <dir>)
# 安装非目标文件的可执行程序,如脚本
install(PROGRAMS <非目标文件的可执行程序> DESTINATION <dir>)
# 安装目录
install(DIRECTORY <dir> DESTINATION <dir>)

例:

# CMakeLists.txt
install(TARGETS Tutorial DESTINATION lib)
install(FILES config.h DESTINATION include)
install(DIRECTORY docs/ DESTINATION doc)

安装命令

# 安装到默认目录CMAKE_INSTALL_PREFIX
$ cmake --install .

# 安装到指定目录
$ cmake --install . --prefix <dir>

 

posted @ 2023-03-15 14:26  this毛豆  阅读(132)  评论(0编辑  收藏  举报