title: cmake 使用
tags: [cmake,项目构建]
categories:
- tools
- cmake
date: 2021-07-13 13:47:11
CMmake
简介
简单来讲,cmake是一个跨平台的makefile生成器。大型项目的额makefile实在是太繁琐了。学习cmake就是在学习其语法。一般来讲,只有在使用c/c++做一个大项目的时候才用得到cmake
基本命令简介
project
PROJECT(<projectname> [CXX] [C] [Java])
指定生成项目的名字,可选项目语言(默认位全选)
同时 cmake 系统也帮助我们预定义了
PROJECT_BINARY_DIR 指向生成的项目的路径(build) 和
PROJECT_SOURCE_DIR 指向项目源文件的路径(source)
MESSAGE
MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR] "message to display")
这个指令用于向终端输出用户定义的信息,包含了三种类型:
- SEND_ERROR,产生错误,生成过程被跳过。
- SATUS,输出前缀为—的信息。
- FATAL_ERROR,立即终止所有 cmake 过程.
MESSAGE(STATUS "build " ${PROJECT_BINARY_DIR} " source " ${PROJECT_SOURCE_DIR})
SET
显式地定义变量
SET(SRC_LIST main.c t1.c t2.c)
将 main.c t1.c t2.c 定义为源文件列表
ADD_EXECUTABLE
生成可执行文件
ADD_EXECUTABLE(hello ${SRC_LIST})
ADD_SUBDIRECTORY
位当前目录添加一个子目录
ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
INSTALL
CMAKE_INSTALL_PREFIX
DESTINATION 定义了安装的路径,如果路径以/开头,那么指的是绝对路径,这时候
CMAKE_INSTALL_PREFIX 其实就无效了。如果你希望使用 CMAKE_INSTALL_PREFIX 来定义安装路径,就要写成相对路径,即不要以/开头,那么安装后的路径就是
${CMAKE_INSTALL_PREFIX}/<DESTINATION 定义的路径>
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 目录
普通文件的安装
INSTALL(FILES files... DESTINATION <dir>
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[RENAME <name>] [OPTIONAL])
ADD_LIBRARY
ADD_LIBRARY(libname [SHARED|STATIC|MODULE]
[EXCLUDE_FROM_ALL]
source1 source2 ... sourceN)
SET_TARGET_PROPERTIES
SET_TARGET_PROPERTIES(target1 target2 ...
PROPERTIES prop1 value1
prop2 value2 ...)
GET_TARGET_PROPERTY
与他对应的指令是:
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
现在我们在 src/CMakeLists.txt 中添加一个头文件搜索路径,方式很简单,加入:
INCLUDE_DIRECTORIES(/usr/include/hello)
LINK_DIRECTORIES
LINK_DIRECTORIES(directory1 directory2 ...)
这个指令非常简单,添加非标准的共享库搜索路径,比如,在工程内部同时存在共享库和可 执行二进制,在编译时就需要指定一下这些共享库的路径。这个例子中我们没有用到这个指 令。
TARGET_LINK_LIBRARIES
TARGET_LINK_LIBRARIES(target library1
<debug | optimized> library2
...)
这个指令可以用来为 target 添加需要链接的共享库,本例中是一个可执行文件,但是同样
可以用于为自己编写的共享库添加共享库链接。
cmake 学习 assimp的经验
项目文件目录
code
--项目的各种代码文件夹
--CMakeLists.txt
contrib
--各种依赖的文件夹
doc
include
--项目的头文件,对外的接口
samples
tests
tools
readme.md
CMakeLists.txt
特点:
- 对别的项目的依赖不使用自己编译好的lib或者dll,而是直接使用源文件。估计编译assimp的时候把这些源文件都编译了一遍
- code文件夹就放自己的代码。
- include文件夹就负责对外提供接口。
- 只使用两个CMakeLists.txt,根目录下的负责项目的整体信息描述,code目录下的负责对文件的编译。
cmake 编写
太复杂了,我现在并看不懂。但并不像我这样每一个模块编写一个CMakeLists.txt进行cmake,而是根目录下的负责项目的整体信息描述,code目录下的负责对文件的编译。
项目编译
- 首先按照上面的文件目录进行编排。
- 编写CMakeLists.txt 文件
- 最好编译成动态链接库,不然没有办法将项目的静态依赖库lib文件包含在内。
- 若项目的依赖中存在动态库,比如assimp 时,就没办法将其包含在内了,只能让用户显式地使用assimp.
- 将项目的头文件列举出来保存到include文件夹中,这里面的就是提供给用户的接口,将不想提供给用户的接口删除即可。
- 记得cmake中进行install,将所有需要提供给用户的文件全部放在最显眼的地方。
CMakeLists.txt 编写
我现在对编译有关的知识了解甚少,所以现在只能使用一些最基础的东西,日后会对编译有更深得了解。
-
cmake的最下版本要求
-
确定项目的名称
-
确定项目的目录安排
-
确定项目的包含目录和库目录
-
确定项目生成目标位置
-
确定项目的额依赖
-
确定目标使用的文件并安排成组
-
确定目标工程的筛选器
-
编译项目
-
安装项目
Demo list
XYY_Game_Engine 的构建示例
# 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)
#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
)
#对于每一个项目,其相对路径的起始位置是 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)