在构建的时候会执行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的学习曲线本身也没有那么高。