cmake 使用


CMmake

简介

简单来讲,cmake是一个跨平台的makefile生成器。大型项目的额makefile实在是太繁琐了。学习cmake就是在学习其语法。一般来讲,只有在使用c/c++做一个大项目的时候才用得到cmake

基本命令简介

project

1
PROJECT(<projectname> [CXX] [C] [Java])

指定生成项目的名字,可选项目语言(默认位全选)
同时 cmake 系统也帮助我们预定义了
PROJECT_BINARY_DIR 指向生成的项目的路径(build) 和
PROJECT_SOURCE_DIR 指向项目源文件的路径(source)

MESSAGE

1
MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR] "message to display")

这个指令用于向终端输出用户定义的信息,包含了三种类型:

  1. SEND_ERROR,产生错误,生成过程被跳过。
  2. SATUS,输出前缀为—的信息。
  3. FATAL_ERROR,立即终止所有 cmake 过程.
1
MESSAGE(STATUS "build " ${PROJECT_BINARY_DIR} " source " ${PROJECT_SOURCE_DIR})

SET

显式地定义变量

1
SET(SRC_LIST main.c t1.c t2.c)

将 main.c t1.c t2.c 定义为源文件列表

ADD_EXECUTABLE

生成可执行文件

1
ADD_EXECUTABLE(hello ${SRC_LIST})

ADD_SUBDIRECTORY

位当前目录添加一个子目录

1
ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])

INSTALL

CMAKE_INSTALL_PREFIX

DESTINATION 定义了安装的路径,如果路径以/开头,那么指的是绝对路径,这时候
CMAKE_INSTALL_PREFIX 其实就无效了。如果你希望使用 CMAKE_INSTALL_PREFIX 来定义安装路径,就要写成相对路径,即不要以/开头,那么安装后的路径就是
${CMAKE_INSTALL_PREFIX}/<DESTINATION 定义的路径>

1
2
3
4
5
6
7
8
9
INSTALL(TARGETS myrun mylib mystaticlib
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION libstatic )

上面的例子会将:
可执行二进制 myrun 安装到${CMAKE_INSTALL_PREFIX}/bin 目录
动态库 libmylib 安装到${CMAKE_INSTALL_PREFIX}/lib 目录
静态库 libmystaticlib 安装到${CMAKE_INSTALL_PREFIX}/libstatic 目录

普通文件的安装

1
2
3
4
5
INSTALL(FILES files... DESTINATION <dir>
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[RENAME <name>] [OPTIONAL])

ADD_LIBRARY

1
2
3
ADD_LIBRARY(libname [SHARED|STATIC|MODULE]
[EXCLUDE_FROM_ALL]
source1 source2 ... sourceN)

SET_TARGET_PROPERTIES

1
2
3
SET_TARGET_PROPERTIES(target1 target2 ...
PROPERTIES prop1 value1
prop2 value2 ...)

GET_TARGET_PROPERTY

1
2
3
4
5
6
7
与他对应的指令是:
GET_TARGET_PROPERTY(VAR target property)
具体用法如下例,我们向 lib/CMakeListst.txt 中添加:
GET_TARGET_PROPERTY(OUTPUT_VALUE hello_static OUTPUT_NAME)
MESSAGE(STATUS “This is the hello_static
OUTPUT_NAME:”${OUTPUT_VALUE})
如果没有这个属性定义,则返回 NOTFOUND.

INCLUDE_DIRECTORIES

1
2
现在我们在 src/CMakeLists.txt 中添加一个头文件搜索路径,方式很简单,加入:
INCLUDE_DIRECTORIES(/usr/include/hello)
1
LINK_DIRECTORIES(directory1 directory2 ...)

这个指令非常简单,添加非标准的共享库搜索路径,比如,在工程内部同时存在共享库和可 执行二进制,在编译时就需要指定一下这些共享库的路径。这个例子中我们没有用到这个指 令。

1
2
3
4
5
TARGET_LINK_LIBRARIES(target library1
<debug | optimized> library2
...)
这个指令可以用来为 target 添加需要链接的共享库,本例中是一个可执行文件,但是同样
可以用于为自己编写的共享库添加共享库链接。

cmake 学习 assimp的经验

项目文件目录

1
2
3
4
5
6
7
8
9
10
11
12
13
code
--项目的各种代码文件夹
--CMakeLists.txt
contrib
--各种依赖的文件夹
doc
include
--项目的头文件,对外的接口
samples
tests
tools
readme.md
CMakeLists.txt

特点:

  1. 对别的项目的依赖不使用自己编译好的lib或者dll,而是直接使用源文件。估计编译assimp的时候把这些源文件都编译了一遍
  2. code文件夹就放自己的代码。
  3. include文件夹就负责对外提供接口。
  4. 只使用两个CMakeLists.txt,根目录下的负责项目的整体信息描述,code目录下的负责对文件的编译。

cmake 编写

太复杂了,我现在并看不懂。但并不像我这样每一个模块编写一个CMakeLists.txt进行cmake,而是根目录下的负责项目的整体信息描述,code目录下的负责对文件的编译。

项目编译

  1. 首先按照上面的文件目录进行编排。
  2. 编写CMakeLists.txt 文件
    1. 最好编译成动态链接库,不然没有办法将项目的静态依赖库lib文件包含在内。
    2. 若项目的依赖中存在动态库,比如assimp 时,就没办法将其包含在内了,只能让用户显式地使用assimp.
  3. 将项目的头文件列举出来保存到include文件夹中,这里面的就是提供给用户的接口,将不想提供给用户的接口删除即可。
  4. 记得cmake中进行install,将所有需要提供给用户的文件全部放在最显眼的地方。

CMakeLists.txt 编写

我现在对编译有关的知识了解甚少,所以现在只能使用一些最基础的东西,日后会对编译有更深得了解。

  1. cmake的最下版本要求

  2. 确定项目的名称

  3. 确定项目的目录安排

  4. 确定项目的包含目录和库目录

  5. 确定项目生成目标位置

  6. 确定项目的额依赖

  7. 确定目标使用的文件并安排成组

  8. 确定目标工程的筛选器

  9. 编译项目

  10. 安装项目

Demo list

XYY_Game_Engine 的构建示例

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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# CMake 最低版本号要求
cmake_minimum_required (VERSION 3.20)

# 项目信息
project(XYY_Game_Engine)


#变量设置 主要是目录变量
#CMAKE_INSTALL_PREFIX
set(CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_LIST_DIR})
#code目录
set(CODE_ROOT_PATH ${CMAKE_CURRENT_LIST_DIR}/code/)
#build目录
set(BUILD_PATH ${CMAKE_CURRENT_LIST_DIR}/build/)
#存放生成的lib的目录
set(LIB_PATH ${BUILD_PATH}/../lib/)
#存放生成的dll的目录
set(DLL_PATH ${BUILD_PATH}/../dll/)
#编译所需包含目录
set(INCLUDE_PATH_CODE ${CMAKE_CURRENT_LIST_DIR}/include/)
set(INCLUDE_PATH_CONTRIB ${CMAKE_CURRENT_LIST_DIR}/contrib/include/)
#编译所需库目录
set(LIBRARY_PATH_CONTRIB ${CMAKE_CURRENT_LIST_DIR}/contrib/lib/)
#设置test路径
set(TEST_PATH ${CMAKE_CURRENT_LIST_DIR}/build/test/)
#指定程序的包含目录 库目录
#引用目录
#INCLUDE_DIRECTORIES(${INCLUDE_PATH_CODE})
INCLUDE_DIRECTORIES(${INCLUDE_PATH_CONTRIB})
INCLUDE_DIRECTORIES(${TEST_PATH}/include)
#库目录 将生成的lib 目录也包含在内,为了给test使用
LINK_DIRECTORIES(${LIBRARY_PATH_CONTRIB})
LINK_DIRECTORIES(${LIB_PATH})

#设置目标的属性
function(SetDefaultTargetProperties target)
#程序生成文件的目录
set_target_properties(${target} PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY ${LIB_PATH}
LIBRARY_OUTPUT_DIRECTORY ${LIB_PATH}
RUNTIME_OUTPUT_DIRECTORY ${DLL_PATH}
)
endfunction()

#MESSAGE(STATUS ${LIB_PATH})

#code的编译 得到 XYY_Game_Engine.lib XYY_Game_Engine.dll
add_subdirectory(${CODE_ROOT_PATH})


#放入 lib 文件夹
INSTALL(TARGETS
XYY_Game_Engine
LIBRARY DESTINATION
${CMAKE_CURRENT_LIST_DIR}/lib/
)

#放入 bin 中

if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/dll/Debug/XYY_Game_Engine.dll)
INSTALL(FILES
${CMAKE_CURRENT_LIST_DIR}/dll/Debug/XYY_Game_Engine.dll
DESTINATION
${CMAKE_CURRENT_LIST_DIR}/bin/
)
endif()

if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/dll/assimp-vc142-mt.dll)
INSTALL(FILES
${CMAKE_CURRENT_LIST_DIR}/dll/assimp-vc142-mt.dll
DESTINATION
${CMAKE_CURRENT_LIST_DIR}/bin
)
endif()


#对 test 增加支持 dll release test 所有的引用都来原来自include中的
INSTALL(FILES
${CMAKE_CURRENT_LIST_DIR}/bin/assimp-vc142-mt.dll
DESTINATION
${CMAKE_CURRENT_LIST_DIR}/build/test/Release
)

INSTALL(FILES
${CMAKE_CURRENT_LIST_DIR}/bin/XYY_Game_Engine.dll
DESTINATION
${CMAKE_CURRENT_LIST_DIR}/build/test/Release
)
INSTALL(FILES
${CMAKE_CURRENT_LIST_DIR}/bin/assimp-vc142-mt.dll
DESTINATION
${CMAKE_CURRENT_LIST_DIR}/build/test/Debug
)
INSTALL(FILES
${CMAKE_CURRENT_LIST_DIR}/bin/XYY_Game_Engine.dll
DESTINATION
${CMAKE_CURRENT_LIST_DIR}/build/test/Debug
)

FILE(
GLOB_RECURSE Header_Driver
${CODE_ROOT_PATH}/Driver/*.hpp
)
FILE(
GLOB_RECURSE Header_Driver_LocalDriver
${CODE_ROOT_PATH}/Driver/LocalDriver/*.h
)
FILE(
GLOB_RECURSE Header_Driver_Global
${CODE_ROOT_PATH}/Driver/GlobalDriver/*.h
)
#SRC_Element
FILE(
GLOB_RECURSE Header_Element
${CODE_ROOT_PATH}/Element/*.h
)
#SRC_Resource
FILE(
GLOB_RECURSE Header_Resource
${CODE_ROOT_PATH}/Resource/*.h
)
#SRC_Resource
FILE(
GLOB_RECURSE Header_Scene
${CODE_ROOT_PATH}/Scene/*.h
)
#SRC_Sync
FILE(
GLOB_RECURSE Header_Sync
${CODE_ROOT_PATH}/Sync/*.h
)
INSTALL(
FILES
${Header_Driver}
DESTINATION
${INCLUDE_PATH_CODE}/Driver/
)
INSTALL(
FILES
${Header_Driver_LocalDriver}
DESTINATION
${INCLUDE_PATH_CODE}/Driver/LocalDriver/
)
INSTALL(
FILES
${Header_Driver_Global}
DESTINATION
${INCLUDE_PATH_CODE}/Driver/GlobalDriver/
)
INSTALL(
FILES
${Header_Element}
DESTINATION
${INCLUDE_PATH_CODE}/Element/
)
INSTALL(
FILES
${Header_Resource}
DESTINATION
${INCLUDE_PATH_CODE}/Resource/
)
INSTALL(
FILES
${Header_Scene}
DESTINATION
${INCLUDE_PATH_CODE}/Scene/
)
INSTALL(
FILES
${Header_Sync}
DESTINATION
${INCLUDE_PATH_CODE}/Sync/
)
INSTALL(
DIRECTORY
${CMAKE_CURRENT_LIST_DIR}/code/Scxmlexample
DESTINATION
${CMAKE_CURRENT_LIST_DIR}/include/
)
INSTALL(
DIRECTORY
${CMAKE_CURRENT_LIST_DIR}/code/resources
DESTINATION
${CMAKE_CURRENT_LIST_DIR}/include/
)
INSTALL(
DIRECTORY
${CMAKE_CURRENT_LIST_DIR}/code/GLSL
DESTINATION
${CMAKE_CURRENT_LIST_DIR}/include/
)
# example
INSTALL(
DIRECTORY
${CMAKE_CURRENT_LIST_DIR}/include/Scxmlexample
DESTINATION
${TEST_PATH}/
)
# resources
INSTALL(
DIRECTORY
${CMAKE_CURRENT_LIST_DIR}/include/resources
DESTINATION
${TEST_PATH}/
)
# header
INSTALL(
DIRECTORY
${CMAKE_CURRENT_LIST_DIR}/include
DESTINATION
${TEST_PATH}/
)


#target 测试
add_subdirectory(test)
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
#code 

#code 中的所有文件
FILE(GLOB_RECURSE SOURCE_CODE
Driver/*
Driver/LocalDriver/*
Driver/GlobalDriver/*
Element/*
Resource/*
Scene/*
Sync/*
${INCLUDE_PATH_CONTRIB}/*.c
)

#组织 file
#Driver
FILE(
GLOB_RECURSE SRC_Driver
Driver/*.hpp
)
source_group(Driver FILES ${SRC_Driver})
FILE(
GLOB_RECURSE SRC_Driver_LocalDriver
Driver/LocalDriver/*
)
source_group(Driver//LocalDriver FILES ${SRC_Driver_LocalDriver})
FILE(
GLOB_RECURSE SRC_Driver_Global
Driver/GlobalDriver/*
)
source_group(Driver//GlobalDriver FILES ${SRC_Driver_Global})
#SRC_Element
FILE(
GLOB_RECURSE SRC_Element
Element/*
)
source_group(Element FILES ${SRC_Element})
#SRC_Resource
FILE(
GLOB_RECURSE SRC_Resource
Resource/*
)
source_group(Resource FILES ${SRC_Resource})
#SRC_Resource
FILE(
GLOB_RECURSE SRC_Scene
Scene/*
)
source_group(Scene FILES ${SRC_Scene})
#SRC_Sync
FILE(
GLOB_RECURSE SRC_Sync
Sync/*
)
source_group(Sync FILES ${SRC_Sync})
#xml
FILE(
GLOB_RECURSE SRC_Scxmlexample
${CODE_ROOT_PATH}/Scxmlexample/*.xml
)
source_group(Scxmlexample FILES ${SRC_Scxmlexample})




FILE(
GLOB DEPENDENCE_LIB
${LIBRARY_PATH_CONTRIB}/*.lib
)

#生成链接库
add_library(XYY_Game_Engine SHARED ${SOURCE_CODE} ${SRC_Scxmlexample})
SetDefaultTargetProperties(XYY_Game_Engine)
# 应该还要加上 opengl32.lib
target_link_libraries(XYY_Game_Engine
opengl32
glfw3.lib
assimp-vc142-mt.lib
tinyxml.lib
kernel32.lib
user32.lib
gdi32.lib
winspool.lib
comdlg32.lib
advapi32.lib
shell32.lib
ole32.lib
oleaut32.lib
uuid.lib
odbc32.lib
odbccp32.lib
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#对于每一个项目,其相对路径的起始位置是 vs 那一对文件的位置

#xml
FILE(
GLOB_RECURSE SRC_Scxmlexample_test
${CODE_ROOT_PATH}/Scxmlexample/*.xml
)
source_group(Scxmlexample FILES ${SRC_Scxmlexample_test})

#test1
add_executable(test1 test1.cpp ${SRC_Scxmlexample_test})
ADD_DEPENDENCIES(test1 XYY_Game_Engine)
target_link_libraries(test1 XYY_Game_Engine)

#test1
add_executable(test2 test2.cpp ${SRC_Scxmlexample_test})
ADD_DEPENDENCIES(test1 XYY_Game_Engine)
target_link_libraries(test2 XYY_Game_Engine)

文章作者: 崔文耀
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 崔文耀 !
  目录