CMake 指南

CMake 是一个开源的跨平台构建工具,Cocos2d-x 是一个开源的跨平台游戏引擎,两者十分契合。

Cocos2d-x 决定自 3.17 版本开始,支持 CMake 的全平台构建。支持的平台包括 Android (NDK)、iOS、macOS、Linux、Windows(VC++ compiler),同时支持通过 CMake 将引擎部分进行预编译,并在新的构建过程中重用预编译的引擎库。

基本概念

在使用 CMake 构建工程之前,最好能对软件构建中一些基本的概念有初步的了解,比如什么是编译,链接,打包。了解这些对后续使用 CMake 有很大的帮助。

此处只解释一下:外部构建(Out-of-source Build),在从源码生成最终的二进制可执行文件的过程中,会生成大量的中间文件,中间文件和源码在同一个目录内时会使源码目录混乱不堪,使用外部构建,即将所有生成的中间文件都放在一个非源码目录中,这样无论构建多少次,源码目录始终干净如新。

常用构建选项

CMake 通用

  • -G,通过 CMake 生成特定 IDE 的项目配置文件。这个操作依赖 IDE,即无法在一个没安装 Xcode 的 macOS 上通过 CMake 生成对应的工程文件。

    • -GXcode 生成 Xcode 工程文件
    • -GVisual Studio 15 2017 生成 Visual Studio 2017 工程文件
  • CMAKE_BUILD_TYPE, 指定构建模式,比如 Debug 还是 Release,默认值 Debug。

    • -DCMAKE_BUILD_TYPE=Release 指定以 Release 模式生成工程。
  • -H -B-H 用来指定一个源码目录,指定的目录必须含有一个 CMakeLists.txt 文件,-B 用来指定 CMake 生成的中间文件的存放目录。

    • -H..\cocos2d-x -Bmsvc_build 指定要构建工程的工程路径是上一级目录的 cocos2d-x 子目录,指定 CMake 生成的文件存放在 msvc_build 目录。
  • —build 执行构建过程,同时指定曾使用 CMake 命令生成的构建文件所在目录。
  • cmake —build ./msvcbuild 在执行这个构建过程时,CMake 会自动选择对应的构建工具。<!—### Cocos2d-x 特有以下 Cocos2d-x 特有的选项和使用预编译库有关。1. GEN_COCOS_PREBUILT 控制构建过程是否生成预编译库,默认行为是不生成,开启选项将会增加一个构建目标:prebuilt,执行这个构建目标时,将会编译引擎部分的代码,并将生成的库文件,拷贝至存放预编译库的目录,默认将库拷贝至引擎目录的 prebuilt 子目录。 -DGEN_COCOS_PREBUILT=ON 将会增加一个构建目录:prebuilt,用于执行库的生成,拷贝。1. USE_COCOS_PREBUILT 控制生成的构建工程是否使用预编译库,默认行为是不使用,开启选项将会自动查找使用预编译库,关闭库相关的构建目标,默认在引擎目录的 prebuilt 子目录查找预编译库。 -DUSE_COCOS_PREBUILT=ON 将会自动使用预编译库,关于库相关的 target。1. _COCOS_PREBUILT_ROOT 指定预编译库存放的目录,是一个可选选项,对于在 Android 平台中使用预编译库是一个必选选项,因为正常情况下,Android Studio 在进行 CMake 构建过程时,无法获取到环境变量,导致找不到引擎根目录。 * -DCOCOS_PREBUILT_ROOT=/Users/laptop/cocos-prebuilt 设置存放预编译库的目录—>

各平台构建示例

Linux

  1. cd cocos2d-x
  2. mkdir linux-build && cd linux-build
  3. cmake .. -G"Unix Makefiles"
  4. make -j 4

在执行 make -j 4 命令之前,可以执行 make help 查看所有的构建目标,使用 make <target> 构建一个特定的目标。

macOS

  1. cd cocos2d-x
  2. mkdir mac-build && cd mac-build
  3. cmake .. -GXcode
  4. open Cocos2d-x.xcodeproj

在 macOS 上使用 cmake .. -GXcode 将会默认生成 macOS 的工程。iOS 工程和 macOS 工程并不能同时生成到一个 Xcode 工程中。

iOS

  1. cd cocos2d-x
  2. mkdir ios-build && cd ios-build
  3. cmake .. -GXcode -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_SYSROOT=iphoneos
  4. open Cocos2d-x.xcodeproj

参数-DCMAKE_OSX_SYSROOT=iphoneos是可选的,默认构建的是为运行在 iOS 设备的工程。如果想构建运行在模拟器的工程,请加参数 -DCMAKE_OSX_SYSROOT=iphonesimulator。 需要注意的是,只有在 MacOS 10.15 和 Xcode 11 之后的模拟器才能支持运行 Apple Metal 应用。

Android

在当前版本的工程中, 默认使用 CMake 构建, ndk-build 相关的支持已经移除. 如果需要在 Android Studio 中使用预编译库,需特别设置预编译库存放的目录,请参考关于预编译库的介绍,以及 build.gradle 中的注释。

Windows

  1. cd cocos2d-x
  2. mkdir win32-build && cd win32-build
  3. cmake .. -G"Visual Studio 15 2017"
  4. cmake --build .

以上命令使用 CMake 生成 Cocos2d-x 测试项目的 Visual Studio 2017 工程。生成后,可以在文件浏览器中找到 cocos2d-x/win32-build 目录,双击打开 Cocos2d-x.sln。设置 cpp-tests 为启动项目,即可正常编译运行。

另一种方式,由于 Visual Studio 2017 已经直接支持 CMake 工程,可以直接使用。详细请参考 CMake 支持

常见问题

如何添加 C/C++ 源码?

一般我们会把源码放在 Classes/ 目录下. 但如果只做这一步, 我们会遇到类似的编译报错.

  1. __/**.cpp:__: undefined reference to `******'

我们还需要在 CMakeLists.txt 中添加新增源码的路径

  1. @@ -52,10 +52,12 @@ endif()
  2. list(APPEND GAME_SOURCE
  3. Classes/AppDelegate.cpp
  4. Classes/HelloWorldScene.cpp
  5. + Classes/**/*.cpp
  6. )
  7. list(APPEND GAME_HEADER
  8. Classes/AppDelegate.h
  9. Classes/HelloWorldScene.h
  10. + Classes/**/*.h
  11. )

然后更新 本地工程文件

  1. ~/Projects/MyCppGame/build$ cmake ..

如何添加图片,字体等资源文件?

在不同的平台上, 不同的构建系统 管理资源的方式会有差异. 好在 CMake 已经帮我们做了大部分工作. 我们需要做的就是把准备好的资源放到 Resources/ 目录下.

但在实际操作中, 我们可能还是在 Windows 和 Linux 上遇到类似

  1. Error while loading: 'HelloWorld2.png'

找不到资源的报错.

即使我们已经更新了资源, 报错还是出现了. 这很让人感到困惑.

如果在工程目录中执行下面命令

  1. ~/Projects/MyCppGame$ find . -name Resources

会发现 有多个 Resources 目录

  1. ./Resources
  2. ./build/bin/MyCppGame/Resources

这里的第二个目录是前者的一个副本, 如果出现变更不一致的情形就会存在同步问题.

在 Mac, iOS 和 Android 平台上, 构建工具(gradle,Xcode)生成的目标是一个程序包. 程序包中除了可执行文件,还包括了所需要的其他资源文件. 资源文件会和可执行程序一起发布, 所以目标不再依赖原本的资源文件(Resources/)目录中的内容. 但是 在Windows和Linux上, 生成的目标对象 只有一个可执行文件, 并不包含资源文件. 比如在Windows上就只有一个MyCppGame.exe文件. 为了让可执行文件能够找到资源, 我们需要把整个 Resources/ 拷贝到 和它同一个目录中.

当前我们并没有提供给开发者 拷贝资源文件的 接口, 而是通过CMake 提供的机制把这个过程作为一个钩子交给了 本地构建工具.

Cocos2d-x 提供了类似与下面代码片段的过程

  1. add_custom_command(TARGET MyCppGame POST_BUILD
  2. COMMAND cmake -E copy_directory ./Resources ./build/bin/MyCppGame/Resources
  3. )

这里的重点在 POST_BUILD. 只有触发了Build 才会执行 POST_BUILD. 如果源文件发生了变化, 在编译完成后就会触发 拷贝资源的动作. 这里有一个问题, 如果资源文件发生了变化,但是源码没有变化, 拷贝动作会被触发吗? 答案是:不会. 这就是运行时找不到新增资源的原因.

了解了导致错误的原因, 解决这个问题就不会很难. 一个简单的解决办法就是修改 Classes/ 中的任意源码(比如添加空行,然后删除,保存), 从而触发 BuildPOST_BUILD. 或者通过其他方式手动同步两个Resource文件夹.

在 Mac/iOS 平台还会有一个类似的问题. Xcode 工程需要把 Resources/ 中的文件标记为 "资源文件". 但这一步是在执行 cmake ../ -GXcode 时进行的. 后续 资源文件的变更不会同步到 Xcode 工程文件. 解决这个问题的方法也很简单, 需要重新执行 cmake 更新 Xcode 工程文件.

  1. ~/Projects/MyCppGame/build$ cmake ..

也就是说, 在资源发生变更时需要重新执行 cmake ...

添加字体资源

添加字体资源和添加图片资源的操作是类似的. 和处理图片的过程相似, 就是把 字体 添加到 Resources/fonts 目录.

iOS 上添加字体, 需要一个额外的步骤.

proj.ios_mac/ios/Info.plist 中添加 UIAppFonts.

  1. @@ -29,7 +29,9 @@
  2. <key>LSRequiresIPhoneOS</key>
  3. <true/>
  4. <key>UIAppFonts</key>
  5. - <array/>
  6. + <array>
  7. + <string>fonts/Scissor Cuts.ttf</string>
  8. + </array>
  9. <key>UILaunchStoryboardName</key>

然后更新工程文件

  1. ~/Projects/MyCppGame/build$ cmake ..

如何添加 Lua 源码?

可以参考 图片资源的更新方法.

如何使用第三方代码库?

有的时候我们需要复用已有的代码库 或者导入开源的 第三方代码. 我们可以使用 CMake 提供的指令 add_subdirectory 导入其他的 CMake 工程.

如果这个代码库不包含可用的CMakeLists.txt, 我们需要先把这个工程改造为一个 CMake 工程.

以开源项目nanomsg为例, 在下载好源码之后, 拷贝到工程目录中

  1. mkdir deps
  2. cp -r ~/Downloads/nanomsg-1.1.5 deps/

在 CMakeLists.txt 中添加下面的内容

  1. @@ -129,6 +131,16 @@ target_include_directories(${APP_NAME}
  2. PRIVATE ${COCOS2DX_ROOT_PATH}/cocos/audio/include/
  3. )
  4. +set(NN_STATIC_LIB ON)
  5. +set(NN_ENABLE_DOC OFF)
  6. +set(NN_TESTS OFF)
  7. +set(NN_TOOLS OFF)
  8. +add_subdirectory(deps/nanomsg-1.1.5)
  9. +target_link_libraries(${APP_NAME} nanomsg)
  10. +
  11. +target_include_directories(${APP_NAME} PRIVATE deps/nanomsg-1.1.5/src)
  12. +
  13. +
  14. # mark app resources
  15. setup_cocos_app_config(${APP_NAME})

这里的前4行的set的作用是设置nanomsg的编译选项.

更详细的介绍可以参考 target_include_directoriestarget_link_libraries.

之后我们就可以在代码中使用这个库了

  1. @@ -24,6 +24,10 @@
  2. #include "HelloWorldScene.h"
  3. +#include "nn.h"

如何编辑 iOS 工程的 Info.plist?

和修改字体的过程一样, 我们需要修改 proj.ios_mac/ios/Info.plist. 这个文件并没有被 Xcode 所直接使用, 它是项目中 CMakeFiles/MyCppTest.dir/Info.plist 的模板文件. 我们需要更新目标来更新Info.plist,

在修改模板后, 需要执行 cmake .. 使之生效.

需要注意的是, 通过 Xcode 进行的修改是不会保存到 proj.ios_mac/ios/Info.plist 中的, 如果构建目录被删除或者新建后 都需要重新编辑.

Info.plist 的内容可以参考官方文档

常用的 CMake 命令

CMake 帮助