¿Qué es CMake?
CMake permite a los desarrolladores escribir un archivo CMakeList.txt independiente de la plataforma para personalizar todo el proceso de compilación y luego generar aún más los archivos Makefile y de proyecto localizados necesarios de acuerdo con la plataforma del usuario de destino, como Unix Makefile o el proyecto Windows Visual Studio. Consiguiendo así “Escribir una vez, ejecutar en todas partes”.
El proceso de usar CMake para generar Makefile y compilar en la plataforma Linux es el siguiente:
- Escriba el archivo de configuración de CMake CMakeLists.txt.
- Ejecute el comando cmake PATH o ccmake PATH para generar Makefile (la diferencia entre ccmake y cmake es que el primero proporciona una interfaz interactiva). Entre ellos, PATH es el directorio donde se encuentra CMakeLists.txt.
1
2
3
4
5
|
# 一般是创建一个build目录进行编译,CMakeLists.txt在build目录的上一层
mkdir ./build
cd build
cmake ..
make
|
- Utilice el comando make para compilar.
Plantilla 1 archivo fuente único
Es adecuado para compilar el archivo fuente especificado (test01.cpp) en un directorio determinado sin llamar a una biblioteca de terceros y, finalmente, compilarlo en un archivo ejecutable.
MANTENER_1
MANTENER_2
Proceso en ejecución
MANTENER_3
El archivo ejecutable test01 se generará en el directorio de compilación.
Plantilla 2 Múltiples archivos fuente en un directorio
Es adecuado para compilar varios archivos fuente en el mismo directorio sin llamar a bibliotecas de terceros y, finalmente, compilarlos en un archivo ejecutable.
Los archivos de origen .c .cpp .cc y los archivos de inclusión se colocan en un directorio. Sólo hay un directorio y no hay subdirectorios.
MANTENER_4
MANTENER_5
Puede haber más archivos fuente y archivos de inclusión, siempre que estén ubicados en un directorio
Proceso en ejecución
MANTENER_6
El archivo ejecutable test02 se generará en el directorio de compilación.
Los archivos fuente de la plantilla 3 y los archivos incluidos están en directorios diferentes
Es adecuado para situaciones en las que el archivo cpp está en una carpeta (src/), el archivo de encabezado está en otra carpeta (include/), no se llama a ninguna biblioteca de terceros y finalmente se compila en un archivo ejecutable.
MANTENER_7
Estructura del directorio
MANTENER_8
Proceso en ejecución
MANTENER_9
El archivo ejecutable test03 se generará en el directorio de compilación.
Los archivos fuente de la plantilla 4 y los archivos incluidos se encuentran en directorios diferentes y llaman a bibliotecas de terceros instaladas en el sistema.
Es adecuado para situaciones en las que el archivo cpp está en una carpeta (src/), el archivo de encabezado está en otra carpeta (include/), se llama a una biblioteca de terceros (como opencv ya instalada en el sistema) y finalmente se compila en un archivo ejecutable.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
# 1,设置工程名称,叫“Demo4”,在Linux下可以随便设置
project( Demo4 )
# 2,设置 CMake 最低版本号,我电脑装的是3.5
cmake_minimum_required( VERSION 3.5 )
# 3,设定编译参数
set(CMAKE_CXX_STANDARD 11) # 指定 C++ 版本
set(CMAKE_BUILD_TYPE "Release") # 调试使用Debug,可以查看中间变量;发布使用Release,运行速度快
# 4,设定源码列表,查找指定目录(都放在./src/中)中的所有源文件,并将名称保存到 DIR_SRCS 变量中
aux_source_directory(./src/ DIR_SRC)
# 5,设定头文件路径(还可以增加其他第三方库的头文件路径)
include_directories(./include/)
# 6,查找并添加OpenCV的头文件目录
find_package(OpenCV REQUIRED)
# message( STATUS " version: ${OpenCV_VERSION}" ) # 我电脑上装的是opencv3.3.1
# message( STATUS " include path: ${OpenCV_INCLUDE_DIRS}" )
include_directories(${OpenCV_INCLUDE_DIRS})
# 7,把源码编译成一个可执行文件,文件名为test04(可以随便取名),会保存在当前目录下
add_executable( test04 ${DIR_SRC} )
target_link_libraries( test04 ${OpenCV_LIBS} ) # 可执行文件名 链接 OpenCV库
|
1
2
3
4
5
6
|
├── template4
│ ├── CMakeLists.txt
│ ├── include
│ │ └── test04.h
│ └── src
│ └── test04.cpp
|
Proceso en ejecución
1
2
3
4
5
6
7
|
## 要先安装 opencv ubuntu下
sudo apt install libopencv-dev
cd template4
mkdir ./build
cd build
cmake ..
make
|
El archivo ejecutable test04 se generará en el directorio de compilación.
La plantilla 5 está dividida en varios submódulos. Cada submódulo tiene su propio CMakeLists.txt, que genera archivos ejecutables, bibliotecas estáticas y bibliotecas dinámicas.
Utilice cmake para construir un proyecto. Cada submódulo tiene sus propias cmakelists. El proyecto crea dos bibliotecas estáticas y una biblioteca dinámica, y también genera un archivo ejecutable que llama a estas bibliotecas.
CMakeLists.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
cmake_minimum_required(VERSION 3.5) # cmake版本最低要求
project(test5) # 设置工程名称
set(CMAKE_CXX_STANDARD 11) # 指定 C++ 版本
set(CMAKE_BUILD_TYPE Release) # 调试使用Debug,可以查看中间变量;发布使用Release,运行速度快
message("${PROJECT_SOURCE_DIR}=" ${PROJECT_SOURCE_DIR})
# 这里设置好路径后,进入子模块的cmake时不用再次设置
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) # 设置可执行文件的输出目录
SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib) # 设置库文件的输出目录
ADD_SUBDIRECTORY(${PROJECT_SOURCE_DIR}/source/add) # 会调用该目录中的CMakeLists.txt进行编译生成静态库
ADD_SUBDIRECTORY(${PROJECT_SOURCE_DIR}/source/sub) # 会调用该目录中的CMakeLists.txt进行编译生成静态库
ADD_SUBDIRECTORY(${PROJECT_SOURCE_DIR}/source/mul) # 会调用该目录中的CMakeLists.txt进行编译生成动态库
ADD_SUBDIRECTORY(${PROJECT_SOURCE_DIR}/source/main) # 会调用该目录中的CMakeLists.txt进行编译生成可执行文件
|
agregar/CMakeLists.txt
1
2
3
4
5
6
7
8
9
10
11
|
# 编译成静态库, libadd.a
# 方法一:逐个添加cpp源文件,适用于文件数量少的情况
# add_library(add ${CMAKE_CURRENT_SOURCE_DIR}/add.cpp ${CMAKE_CURRENT_SOURCE_DIR}/add3.cpp)
# 方法二:搜索有的cpp源文件,并将列表存储在一个变量中,适用于文件多的情况
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SRC_LIST)
add_library(add ${SRC_LIST})
# 方法三:递归遍历目录,获取所有的CPP文件,适用于多级目录的情况
# file(GLOB_RECURSE cpp_files ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) # GLOB是不递归
# add_library(add ${cpp_files})
|
mul/CMakeLists.txt
1
2
|
# 编译成动态库libmul.so
add_library(mul SHARED ${CMAKE_CURRENT_SOURCE_DIR}/mul.cpp)
|
sub/CMakeLists.txt
1
2
|
# 编译成静态库, libsub.a
add_library(sub ${CMAKE_CURRENT_SOURCE_DIR}/sub.cpp)
|
principal/CMakeLists.txt
1
2
3
4
5
6
7
8
9
10
|
# 添加头文件路径,会检索目录中的所有头文件
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../add
${CMAKE_CURRENT_SOURCE_DIR}/../sub
${CMAKE_CURRENT_SOURCE_DIR}/../mul
${CMAKE_CURRENT_SOURCE_DIR}/../main)
# 把源码编译成一个可执行文件
add_executable(main ./main.cpp)
# 添加链接库,动态和静态都行
target_link_libraries(main add sub mul)
|
Estructura del directorio
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
├── template5
│ ├── CMakeLists.txt
│ ├── readme.md
│ ├── run.sh
│ └── source
│ ├── add
│ │ ├── add3.cpp
│ │ ├── add3.h
│ │ ├── add.cpp
│ │ ├── add.h
│ │ └── CMakeLists.txt
│ ├── main
│ │ ├── CMakeLists.txt
│ │ └── main.cpp
│ ├── mul
│ │ ├── CMakeLists.txt
│ │ ├── mul.cpp
│ │ └── mul.h
│ └── sub
│ ├── CMakeLists.txt
│ ├── sub.cpp
│ └── sub.h
|
Proceso en ejecución
MANTENER_19
La plantilla 6 se divide en varios submódulos, con un solo CMakeLists.txt, que genera archivos ejecutables, bibliotecas estáticas y bibliotecas dinámicas.
Utilice cmake para crear un proyecto que contenga varios submódulos, pero los submódulos no tienen sus propias cmakelists. Todo el proyecto tiene un solo CMakeLists.txt. El proyecto crea dos bibliotecas estáticas y una biblioteca dinámica, y también genera un archivo ejecutable que llama a estas bibliotecas.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
cmake_minimum_required(VERSION 3.5) # cmake版本最低要求
project(test6) # 设置工程名称
set(CMAKE_CXX_STANDARD 11) # 指定 C++ 版本
set(CMAKE_BUILD_TYPE Release) # 调试使用Debug,可以查看中间变量;发布使用Release,运行速度快
message("${PROJECT_SOURCE_DIR}=" ${PROJECT_SOURCE_DIR})
# 这里设置好路径后,进入子模块的cmake时不用再次设置
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) # 设置可执行文件的输出目录
SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib) # 设置库文件的输出目录
# 编译add,生成静态库
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/source/add ADD_SRC_LIST)
add_library(add ${ADD_SRC_LIST})
# 编译sub,生成静态库
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/source/sub SUB_SRC_LIST)
add_library(sub ${SUB_SRC_LIST})
# 编译mul,生成动态库
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/source/mul MUL_SRC_LIST)
add_library(mul SHARED ${MUL_SRC_LIST})
# 添加头文件路径,用于编译可执行文件
include_directories(./source/add
./source/sub
./source/mul)
# 编译main,生成可执行文件
add_executable(main ${CMAKE_CURRENT_SOURCE_DIR}/source/main/main.cpp)
target_link_libraries(main add sub mul) # 链接所有库
|
Estructura del directorio
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
├── template6
│ ├── CMakeLists.txt
│ ├── readme.md
│ ├── run.sh
│ └── source
│ ├── add
│ │ ├── add3.cpp
│ │ ├── add3.h
│ │ ├── add.cpp
│ │ └── add.h
│ ├── main
│ │ └── main.cpp
│ ├── mul
│ │ ├── mul.cpp
│ │ └── mul.h
│ └── sub
│ ├── sub.cpp
│ └── sub.h
|
Proceso en ejecución
1
2
3
4
5
6
7
8
|
cd template6
rm -rf build
mkdir build
cd build/
cmake ..
make
cd ../bin
./main
|
Comandos comunes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
# Note:cmake中不区分大小写。
# 设置变量,方便后面自动配置,进入子模块的cmake时不用再次设置
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) # 设置可执行文件的输出目录
SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib) # 设置库文件的输出目录
# 设定源码列表,查找指定目录下的所有源文件,并将名称保存到 DIR_SRCS 变量中
aux_source_directory(./src/ DIR_SRC)
# 设定头文件查找路径,可以将所有头文件路径都添加到这里面
include_directories(./include/
./source/sub)
# 查找并添加OpenCV的头文件目录,在target_link_libraries()中需要进行动态链接
find_package(OpenCV REQUIRED)
# 编译子模块,会自动调用子模块中的Cmakelists.txt进行编译
add_subdirectory(sub)
# 将${ADD_SRC_LIST}中的所有源码编译生成静态库add
add_library(add ${ADD_SRC_LIST})
# 将${SRCS}中的所有源码编译成一个可执行文件,文件名为main
add_executable( main ${SRCS} )
# 编译可执行文件之后,添加动态链接库,会链接静态库subadd和opencv库
target_link_libraries(main subadd ${OpenCV_LIBS})
# 打印一些日志信息
message("PROJECT_SOURCE_DIR=" ${PROJECT_SOURCE_DIR})
|
Variables de uso común
MANTENER_24
1 compilación cruzada adicional CH32V203
MANTENER_25
Archivo enlace.ld
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
|
ENTRY( _start )
__stack_size = 2048;
PROVIDE( _stack_size = __stack_size );
MEMORY
{
/* CH32V20x_D6 - CH32V203F6-CH32V203G6-CH32V203K6-CH32V203C6 */
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 32K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 10K
/* CH32V20x_D6 - CH32V203K8-CH32V203C8-CH32V203G8-CH32V203F8 */
/* FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 64K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K */
/* CH32V20x_D8 - CH32V203RB
CH32V20x_D8W - CH32V208x
FLASH + RAM supports the following configuration
FLASH-128K + RAM-64K
FLASH-144K + RAM-48K
FLASH-160K + RAM-32K
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 160K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 32K
*/
}
SECTIONS
{
.init :
{
_sinit = .;
. = ALIGN(4);
KEEP(*(SORT_NONE(.init)))
. = ALIGN(4);
_einit = .;
} >FLASH AT>FLASH
.vector :
{
*(.vector);
. = ALIGN(64);
} >FLASH AT>FLASH
.text :
{
. = ALIGN(4);
*(.text)
*(.text.*)
*(.rodata)
*(.rodata*)
*(.glue_7)
*(.glue_7t)
*(.gnu.linkonce.t.*)
. = ALIGN(4);
} >FLASH AT>FLASH
.fini :
{
KEEP(*(SORT_NONE(.fini)))
. = ALIGN(4);
} >FLASH AT>FLASH
PROVIDE( _etext = . );
PROVIDE( _eitcm = . );
.preinit_array :
{
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
} >FLASH AT>FLASH
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
PROVIDE_HIDDEN (__init_array_end = .);
} >FLASH AT>FLASH
.fini_array :
{
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
PROVIDE_HIDDEN (__fini_array_end = .);
} >FLASH AT>FLASH
.ctors :
{
/* gcc uses crtbegin.o to find the start of
the constructors, so we make sure it is
first. Because this is a wildcard, it
doesn't matter if the user does not
actually link against crtbegin.o; the
linker won't look for a file to match a
wildcard. The wildcard also means that it
doesn't matter which directory crtbegin.o
is in. */
KEEP (*crtbegin.o(.ctors))
KEEP (*crtbegin?.o(.ctors))
/* We don't want to include the .ctor section from
the crtend.o file until after the sorted ctors.
The .ctor section from the crtend file contains the
end of ctors marker and it must be last */
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
} >FLASH AT>FLASH
.dtors :
{
KEEP (*crtbegin.o(.dtors))
KEEP (*crtbegin?.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
} >FLASH AT>FLASH
.dalign :
{
. = ALIGN(4);
PROVIDE(_data_vma = .);
} >RAM AT>FLASH
.dlalign :
{
. = ALIGN(4);
PROVIDE(_data_lma = .);
} >FLASH AT>FLASH
.data :
{
*(.gnu.linkonce.r.*)
*(.data .data.*)
*(.gnu.linkonce.d.*)
. = ALIGN(8);
PROVIDE( __global_pointer$ = . + 0x800 );
*(.sdata .sdata.*)
*(.sdata2.*)
*(.gnu.linkonce.s.*)
. = ALIGN(8);
*(.srodata.cst16)
*(.srodata.cst8)
*(.srodata.cst4)
*(.srodata.cst2)
*(.srodata .srodata.*)
. = ALIGN(4);
PROVIDE( _edata = .);
} >RAM AT>FLASH
.bss :
{
. = ALIGN(4);
PROVIDE( _sbss = .);
*(.sbss*)
*(.gnu.linkonce.sb.*)
*(.bss*)
*(.gnu.linkonce.b.*)
*(COMMON*)
. = ALIGN(4);
PROVIDE( _ebss = .);
} >RAM AT>FLASH
PROVIDE( _end = _ebss);
PROVIDE( end = . );
.stack ORIGIN(RAM) + LENGTH(RAM) - __stack_size :
{
PROVIDE( _heap_end = . );
. = ALIGN(4);
PROVIDE(_susrstack = . );
. = . + __stack_size;
PROVIDE( _eusrstack = .);
} >RAM
}
|