在构建的时候会执行cmake命令
3.6.3/resources/tools/cmake/bin/cmake.exe
-S"E:/proj/cocos/NewProject/native/engine/win64"
-B"E:/proj/cocos/NewProject/build/windows/proj"
-DRES_DIR="E:/proj/cocos/NewProject/build/windows"
-DAPP_NAME="NewProject"
如果你对cmake比较熟悉,就会看明白命令行的选项都什么意思,可以参考这篇文章CMake命令行使用姿势
一切的起点
- native\engine\win64\CMakeLists.txt
cmake_minimum_required(VERSION 3.8)
set(APP_NAME "NewProject" CACHE STRING "Project Name")
project(${APP_NAME} CXX)
set(CC_PROJECT_DIR ${CMAKE_CURRENT_LIST_DIR})
set(CC_UI_RESOURCES)
set(CC_PROJ_SOURCES)
set(CC_COMMON_SOURCES)
set(CC_ALL_SOURCES)
include(${CC_PROJECT_DIR}/../common/CMakeLists.txt) # 引入外部的CMake
set(EXECUTABLE_NAME ${APP_NAME})
# 重点关注的逻辑在这里
cc_windows_before_target(${EXECUTABLE_NAME})
add_executable(${EXECUTABLE_NAME}
${CC_ALL_SOURCES}
)
cc_windows_after_target(${EXECUTABLE_NAME})
- native\engine\common\CMakeLists.txt
# .. 省略了很多set变量逻辑
if(NOT RES_DIR) # 对应外部设置的RES_DIR变量
message(FATAL_ERROR "RES_DIR is not set!")
endif()
# 这个文件包含了大量的set,没有任何的逻辑
include(${RES_DIR}/proj/cfg.cmake)
# localCfg.cmake 是空逻辑
if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/localCfg.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/localCfg.cmake)
endif()
# 这个变量定义在cfg.cmake,指向的是3.6.3/resources/resources/3d/engine/native
if(NOT COCOS_X_PATH)
message(FATAL_ERROR "COCOS_X_PATH is not set!")
endif()
# engine相关的逻辑
include(${COCOS_X_PATH}/CMakeLists.txt)
# 入口源代码
list(APPEND CC_COMMON_SOURCES
${CMAKE_CURRENT_LIST_DIR}/Classes/Game.h
${CMAKE_CURRENT_LIST_DIR}/Classes/Game.cpp
)
从log可以看到,中间有执行:
node.exe plugin_parser.js
那剩下的问题就是cc_windows_before_target在哪里?在engine/common里面有加载engine相关的逻辑,我们看下engine大概处理了哪些
- ${COCOS_X_PATH}/CMakeLists.txt
第一行就有
include(${CMAKE_CURRENT_LIST_DIR}/cmake/predefine.cmake)
在predefine.cmake中有
## predefined configurations for game applications
include(${CMAKE_CURRENT_LIST_DIR}/../../templates/cmake/common.cmake)
if(APPLE)
include(${CMAKE_CURRENT_LIST_DIR}/../../templates/cmake/apple.cmake)
elseif(WINDOWS)
include(${CMAKE_CURRENT_LIST_DIR}/../../templates/cmake/windows.cmake)
elseif(LINUX)
include(${CMAKE_CURRENT_LIST_DIR}/../../templates/cmake/linux.cmake)
elseif(ANDROID)
include(${CMAKE_CURRENT_LIST_DIR}/../../templates/cmake/android.cmake)
elseif(OHOS)
include(${CMAKE_CURRENT_LIST_DIR}/../../templates/cmake/ohos.cmake)
elseif(QNX)
elseif(EMSCRIPTEN)
else()
message(FATAL_ERROR "Unhandled platform specified cmake utils!")
endif()
所以逻辑就又分发到了templates里面,其实里面的逻辑也就刚好只有这2个marco,而这2个marco也就刚好就是我们要找的marco
macro(cc_windows_before_target target_name)
if(${CMAKE_SIZEOF_VOID_P} STREQUAL "4")
message(FATAL_ERROR "Win32 architecture is no more supported!!!")
endif()
list(APPEND CC_UI_RESOURCES
${CC_PROJECT_DIR}/game.rc
)
list(APPEND CC_PROJ_SOURCES
${CC_PROJECT_DIR}/main.cpp
${CC_PROJECT_DIR}/resource.h
${CC_UI_RESOURCES}
)
# 收集资源,实现逻辑在cmake/common.cmake
cc_include_resources(${RES_DIR}/data CC_ASSET_FILES)
list(APPEND CC_ALL_SOURCES ${CC_PROJ_SOURCES} ${CC_COMMON_SOURCES} ${CC_ASSET_FILES})
# 很重要的一个函数,实现逻辑也在cmake/common.cmake
cc_common_before_target(${target_name})
endmacro()
macro(cc_windows_after_target target_name)
source_group(TREE ${RES_DIR}/data PREFIX "Resources" FILES ${CC_ASSET_FILES})
source_group(TREE ${CC_PROJECT_DIR} PREFIX "Source Files" FILES ${CC_PROJ_SOURCES})
source_group(TREE ${CC_PROJECT_DIR}/../common PREFIX "Source Files" FILES ${CC_COMMON_SOURCES})
target_link_libraries(${target_name} ${ENGINE_NAME})
target_include_directories(${target_name} PRIVATE
${CC_PROJECT_DIR}/../common/Classes
)
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${target_name})
cc_common_after_target(${target_name})
if(EXISTS ${RES_DIR}/data/jsb-adapter)
set(bin_dir ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR})
add_custom_target(copy_resource ALL
COMMAND ${CMAKE_COMMAND} -E echo "Copying resources to ${bin_dir}"
COMMAND ${CMAKE_COMMAND} -E make_directory ${bin_dir}/Resources
COMMAND robocopy "${RES_DIR}/data/" "${bin_dir}/Resources/" /MIR || (exit 0)
COMMAND ${CMAKE_COMMAND} -E echo "Copying resources done!"
)
add_dependencies(${target_name} copy_resource)
set_target_properties(copy_resource PROPERTIES FOLDER Utils)
endif()
if(MSVC)
foreach(item ${WINDOWS_DLLS})
get_filename_component(filename ${item} NAME)
get_filename_component(abs ${item} ABSOLUTE)
add_custom_command(TARGET ${target_name} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${abs} $<TARGET_FILE_DIR:${target_name}>/${filename}
)
endforeach()
foreach(item ${V8_DLLS})
get_filename_component(filename ${item} NAME)
add_custom_command(TARGET ${target_name} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${V8_DIR}/$<IF:$<BOOL:$<CONFIG:RELEASE>>,Release,Debug>/${filename} $<TARGET_FILE_DIR:${target_name}>/${filename}
)
endforeach()
target_link_options(${target_name} PRIVATE /SUBSYSTEM:WINDOWS)
endif()
endmacro()
- cmake/common.cmake
macro(cc_common_before_target target_name)
set(CC_TARGET_NAME ${target_name})
if(NOT CC_TARGET_NAME)
message(FATAL_ERROR "CC_TARGET_NAME is not set!")
endif()
if(NOT SKIP_SCAN_PLUGINS AND USE_PLUGINS)
# 里面有execute_process,调用nodejs脚本plugins_parser.js
cc_gen_plugin_cmake_hook()
else()
message(STATUS " Skip search plugins")
endif()
cc_load_hooks("Pre")
if(USE_PLUGINS)
# 加载ProjDir/Pre*.cmake、ProjDir/*Pre.cmake脚本
cc_plugin_entry()
endif()
endmacro()
总结
总体来说,如果对cmake比较熟悉的话,还是非常容易看明白大概的实现思路,cmake的学习曲线本身也没有那么高。