Cocos2d-x 3.17.1のcmakeでスタティックライブラリを作成してビルドを高速化する

はじめに

FirebaseをAndroidに追加するときに思ったのですが、Cocos2d-xのAndroid環境は本当に地獄ですね…

少しGradleやCMakeist.txtを編集するとフルビルドになってCocos2d-x丸ごとリコンパイルになって時間がすごくかかります。

少し前に、3.17でcmakeが推奨だからndk-buildを使用していたlib-genが削除され、3.17.1でエレガントじゃないと割とふんわりした理由で代案が無いのにcmakeのプレビルドまでも削除されてしまいました。(CocosStudioを削除した頃からの伝統なのかCocos開発チームのこういう姿勢は本当にやばいですね)

このIssueです。

github.com

したがってCocos2d-x 3.17.1以降のAndroidでは事前にビルド済みのスタティックライブラリが簡単には利用できなくなっています。Cocos2d-xのフルビルドって1アーキテクチャ当たり約10分弱もかかるので少し編集して動きを見てデバッグという作業方法が非常に取りにくいのでこれを改善したいと思います。

ご注意 その1:

かなりセンシティブな内容のため修正を誤るとビルドができなくなります。変更する前にファイルはあらかじめバックアップしましょう。また、自分の環境(作成中のアプリ + ADV(API29))で動作するところまでは確認しましたが個々人で事情(リンクしてるライブラリ)が大きく異なるため動かないケースがありますがご了承ください。

ご注意 その2:

あくまでデバッグ中の話なのでリリースするapkを作成する場合フルビルドをしてパッケージ化するようにしてください。紹介の内容で動作の完全性は保証できません。

作業環境

確認環境は以下の通り。

3.17.1で公式の動作する最大のバージョンだと思います。たぶん。

  • Windows10
  • Cocos2d-x 3.17.1
  • AndroidStudio3.5
  • Gradel 5.1.1
  • Android Graled Plugin 3.4.1
  • NDK r20

事前準備

まずは、以下手順でスタティックライブラリを作成します。手作業になるので各自準備を行います。

(1) 新しいプロジェクトを作成する

cocosコンソールから新しいプロジェクトを作成します。ビルドに使うだけなので適当でよいです。

cocos new -l cpp -d ${適当なフォルダ}

新規プロジェクトのほうが自分のコードが無いのでビルドが早いので新規ビルドを作成していますが、作成済みのプロジェクトでも問題ありません(自分のコードのビルドが入ってるとビルド時間が少し長くなります)

(2) ビルドするアーキテクチャを選択する

以下の手順でビルドするCPUアーキテクチャの種類を指定します。

まず、作成したプロジェクトの"proj.android"をAndroidStudioで開きます。

次に、gradle.propertiesの"PROP_APP_API"を以下の通り変更します。

# 事前ビルドなので全部指定
PROP_APP_ABI=armeabi-v7a:arm64-v8a:x86

(3) ビルドを実行する

AndroidStudio上から ビルド > [モジュールの作成] を選択

最初はビルドバリアントがdebugになっていると思うので、完了したらreleseもビルドする

ここが最高に時間がかかるのでボタンを押したらお茶でもして完了するのを待ちます。

(4) 作成したライブラリを所定のフォルダに移動する

以下フォルダにある以下のファイルを自分のプロジェクトにコピーします。

以下フォルダの内容物をコピーして、、、

# ここにスタティックライブラリが合計6ファイル生成される
# .aファイルが数種類あるが"libcocos2d.a"しか使用しない
${適当なフォルダ}\proj.android\app\.externalNativeBuild\cmake\[${BUILD_TYPE}]\[ABI]\lib\libcocos2d.a

# 備考:
# ${BUILD_TYPE} = "debug" or "release"
# ${ABI} = "armeabi-v7a" or "arm64-v8a" or "x86"

自分のプロジェクトに以下構成で配置します。

${アプリのプロジェクト}
  + cocos2d
    + prebuilt
      + Debug.android.x86/libcocos2d.a
      + Debug.android.armeabi-v7a/libcocos2d.a
      + Debug.android.arm64-v8a/libcocos2d.a
      + Release.android.x86/libcocos2d.a
      + Release.android.armeabi-v7a/libcocos2d.a
      + Release.android.arm64-v8a/libcocos2d.a

大文字と小文字を区別する可能性があるので打ち間違えないようにしてください。

自分のプロジェクトを変更する

次に自分のアプリのプロジェクトにスタティックライブラリを使用する設定をしていきます。

(a) プレビルドオプションを追加

アプリのgradle.gradleのcmakeに以下引数を追加します。

...(前略)...
} else if (PROP_BUILD_TYPE == 'cmake') {
    cmake {
        targets '...'
        arguments ..."-DANDROID_ARM_NEON=TRUE", "-DUSE_COCOS_PREBUILT=ON" ★最後にこの記述を追加する
        cppFlags "-frtti -fexceptions -fsigned-char"
    }
}

USE_COCOS_PREBUILT変数を追加して"ON"なら先ほど追加したライブラリを使用、"OFF"ならフルビルドという指定ができるようにします。

(b) Makeファイルの修正

プロジェクトのルートにあるCMakeList.txtに以下内容を追記します。以下記述を追加します。

1か所目

先頭にある以下個所を書き換えます。

// CMakeList.txt

### 元の内容
...
set(COCOS2DX_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cocos2d)
set(CMAKE_MODULE_PATH ${COCOS2DX_ROOT_PATH}/cmake/Modules/)

include(CocosBuildSet)
add_subdirectory(${COCOS2DX_ROOT_PATH}/cocos ${ENGINE_BINARY_PATH}/cocos/core)

# ↓↓↓↓↓↓↓↓

### 書き換えた記述
...
set(COCOS2DX_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cocos2d)
set(CMAKE_MODULE_PATH ${COCOS2DX_ROOT_PATH}/cmake/Modules/)

include(CocosBuildSet)
# コメントアウト
#add_subdirectory(${COCOS2DX_ROOT_PATH}/cocos ${ENGINE_BINARY_PATH}/cocos/core)

# ここから追記
# for pre-built >>>
if(NOT USE_COCOS_PREBUILT)
    add_subdirectory(${COCOS2DX_ROOT_PATH}/cocos ${ENGINE_BINARY_PATH}/cocos/core)
else()
    add_subdirectory(${COCOS2DX_ROOT_PATH}/external ${ENGINE_BINARY_PATH}/external)
    add_library(cocos2d STATIC IMPORTED GLOBAL)
    set_target_properties(cocos2d 
        PROPERTIES
            IMPORTED_LOCATION
            ${COCOS2DX_ROOT_PATH}/prebuilt/${CMAKE_BUILD_TYPE}.android.${ANDROID_ABI}/libcocos2d.a)
endif()
# <<< for pre-built

2か所目:

中ほどより下にある以下個所に内容を追加します。

### 元の内容
...
if(NOT ANDROID)
    add_executable(${APP_NAME} ${all_code_files})
else()
    add_library(${APP_NAME} SHARED ${all_code_files})
    add_subdirectory(${COCOS2DX_ROOT_PATH}/cocos/platform/android ${ENGINE_BINARY_PATH}/cocos/platform)
    target_link_libraries(${APP_NAME} -Wl,--whole-archive cpp_android_spec -Wl,--no-whole-archive)
endif()

target_link_libraries(${APP_NAME} cocos2d)

# ↓↓↓↓↓↓↓↓
...
    target_link_libraries(${APP_NAME} -Wl,--whole-archive cpp_android_spec -Wl,--no-whole-archive)
endif()

target_link_libraries(${APP_NAME} cocos2d)

# ここから追記
# for pre-built >>>
if(USE_COCOS_PREBUILT)
    
    target_link_libraries(${APP_NAME} external)
    
    #
    # Basic cocos2d-x includes
    #
    target_include_directories(${APP_NAME}
        PUBLIC ${COCOS2DX_ROOT_PATH}
        PUBLIC ${COCOS2DX_ROOT_PATH}/cocos
        PUBLIC ${COCOS2DX_ROOT_PATH}/extensions
        PUBLIC ${COCOS2DX_ROOT_PATH}/cocos/platform
        PUBLIC ${COCOS2DX_ROOT_PATH}/cocos/base
        PUBLIC ${COCOS2DX_ROOT_PATH}/cocos/editor-support
        PUBLIC ${COCOS2DX_ROOT_PATH}/cocos/audio/include
        PUBLIC ${COCOS2DX_ROOT_PATH}/cocos/platform/${PLATFORM_FOLDER}
    )
    
    #
    # External header includes
    # 補足:external以下をリンクしている場合手で参照を追加する
    #
    target_include_directories(${APP_NAME}
        PUBLIC ${COCOS2DX_ROOT_PATH}/external
        PUBLIC ${COCOS2DX_ROOT_PATH}/external/include/json
        PUBLIC ${COCOS2DX_ROOT_PATH}/external/openssl/include/${platform_name}
    )
    
    # add base macro define and compile options
    use_cocos2dx_compile_define(${APP_NAME})
    use_cocos2dx_compile_options(${APP_NAME})
    
    # use all platform related system libs
    use_cocos2dx_libs_depend(${APP_NAME})
    
endif()
# <<< for pre-built

これで完了です。

(c) 自分のアプリをビルドする

この状態で、最初に追加した変数が「"-DUSE_COCOS_PREBUILT=ON"」となっていることを確認してAndroidStudio上から ビルド > [プロジェクトの作成] を実行します。

そうすると、自分のコードとexternal以下のビルドが走りビルドステップが600程度削減され、ビルド時間が1アーキテクチャ当たり10分から3分に短縮されると思います。

すっきり。

external以下もstatic化できたらいいのですがメンテがコード量が多くる&作るのがかなり面倒そうだったので対象除外しています。だれかどうにかしてください。

一応ここにできるといってる人が居るっぽいです。が、解読するのに結構かかりそうなので力尽きました。。

github.com

discuss.cocos2d-x.org

参照サイト

CMakeの使い方(その1)

CMake: IMPORTEDターゲット

以上です。