🔎

Godot iOS アプリケーションの main 関数のありかを探る

2023/10/19に公開

エントリポイントを見つけることは、アプリケーションの挙動を知る上で重要なヒントになります。今回は Godot 製の iOS アプリケーションの挙動を知るために、main 関数の場所を探ります。

1. 最小限の Godot アプリケーションを用意する

iOS 端末上で動作する Godot アプリケーションの動きを確認するためには、Godot アプリケーション側で少なくともメインシーンを作成しておく必要があります。

ひとまず、光があたった球を置いてメインシーンとして保存します。

メインシーンに球、カメラ、光源を置く
メインシーンに球、カメラ、光源を置く

Godot のドキュメント Exporting for iOS を参考にして Xcode プロジェクトをエクスポートします。エクスポートされた Xcode プロジェクトをビルドすると、光があたった球が表示されるアプリケーションができました。

作成したアプリケーションが起動するようす
作成したアプリケーションが起動するようす

2. Godot からエクスポートした Xcode プロジェクトの構成を眺める

エクスポートされた Xcode プロジェクトの構成は以下のようになっています。

Godot エディタからエクスポートした Xcode プロジェクトの構成
Godot エディタからエクスポートした Xcode プロジェクトの構成

このプロジェクトでビルド対象になっているファイルは、dummy.cpp のみです。

ビルド対象(Compile Sources)に含まれるファイルの一覧
ビルド対象(Compile Sources)に含まれるファイルの一覧

dummy.cpp の内容を確認すると、アプリケーションの実行に直接関わるような処理は一切記述されていないことが分かります。

dummy.cpp の内容
dummy.cpp の内容

にも関わらず、このプロジェクトからアプリケーションをビルドすると、Godot アプリケーションとして動作するようになっています。アプリケーションのエントリポイントである main 関数に相当する記述はどこに書かれているのでしょうか。

3. libgodot.a の中身を覗いて、main 関数が書かれているファイルを見つける

上のプロジェクト構成にあった、godot フレームワークの中身を調べます。Xcode 上で表示されている godot フレームワークの実態は、{Xcodeプロジェクト名}.xcframework として配置されているディレクトリで、中にはアーキテクチャごとの libgodot.a が置かれています。

godot フレームワークのファイル構成
godot フレームワークのファイル構成

nm コマンドを使用して libgodot.a を眺めて main 関数らしきものがないか探ってみると、それっぽい箇所が見つかります。

% nm template.xcframework/ios-arm64/libgodot.a | less

それっぽい箇所
それっぽい箇所

main.ios.template_debug.arm64.o に含まれているワード(例えば GodotApplicalitionDelegate)で Godot のソースコードを検索すると、main.ios.template_debug.arm64.o の元になっていると思われるファイル main.m を見つけることができます。

platform/ios/main.m
#import "godot_app_delegate.h"

#import <UIKit/UIKit.h>
#include <stdio.h>

int gargc;
char **gargv;

int main(int argc, char *argv[]) {
#if defined(VULKAN_ENABLED)
  //MoltenVK - enable full component swizzling support
  setenv("MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE", "1", 1);
#endif

  gargc = argc;
  gargv = argv;

  @autoreleasepool {
    NSString *className = NSStringFromClass([GodotApplicalitionDelegate class]);
    UIApplicationMain(argc, argv, nil, className);
  }
  return 0;
}

4. 見つけた main 関数がエントリポイントになっているのかを確認する

上で見つけた main 関数の全体をコメントアウトし、そこからカスタムテンプレートを作成して iOS アプリケーションをビルドすることで、上に記載した main 関数がエントリポイントとしての役割を果たしているかどうかを確認します。

main.m の main 関数をコメントアウトします。

platform/ios/main.m(コメントアウトした箇所のみ)
// int main(int argc, char *argv[]) {
// #if defined(VULKAN_ENABLED)
//   //MoltenVK - enable full component swizzling support
//   setenv("MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE", "1", 1);
// #endif

//   gargc = argc;
//   gargv = argv;

//   @autoreleasepool {
//     NSString *className = NSStringFromClass([GodotApplicalitionDelegate class]);
//     UIApplicationMain(argc, argv, nil, className);
//   }
//   return 0;
// }

上の変更がある状態でターゲット template_debug, template_release をビルドします。今回は実機上で動作確認をしているので、ios_simulator=yes などのオプションによるビルドはしていません。

# debug 構成向けのビルド
% scons platform=ios target=template_debug
...
[  8%] Compiling platform/ios/main.m
...
[100%] scons: done building targets.
[Time elapsed: 00:00:07.221]
# release 構成向けのビルド
% scons platform=ios target=template_release
...
[  8%] Compiling platform/ios/main.m
...
[100%] scons: done building targets.
[Time elapsed: 00:00:07.034]

できた成果物を再度 nm コマンドで眺めると、main.ios.template_debug.arm64.o の内容が変化していることが確認できます。

main 関数をコメントアウトした後の  の内容
main 関数をコメントアウトした後の main.ios.template_debug.arm64.o の内容

これらの成果物をカスタムテンプレートを作成するためのディレクトリに配置して、カスタムテンプレートを変更し、Godot エディタから Xcode プロジェクトを再度エクスポートします。

godot % cp bin/libgodot.ios.template_debug.arm64.a ios_xcode/libgodot.ios.debug.xcframework/ios-arm64/libgodot.a    
godot % cp bin/libgodot.ios.template_release.arm64.a ios_xcode/libgodot.ios.release.xcframework/ios-arm64/libgodot.a
godot % (cd ios_xcode && zip -r ../ios.zip ./*)

作成したカスタムテンプレートを使用して Xcode プロジェクトをエクスポートすると、エラーが発生します。

上記で作成したカスタムテンプレートを採用した場合のエクスポート時のエラー
上記で作成したカスタムテンプレートを採用した場合のエクスポート時のエラー

エラーには「Xcodeプロジェクトの作成に失敗」と書かれていますが、実際には Xcode プロジェクトはエクスポートされています。Xcode プロジェクトを開き、ビルドを実行すると、エントリポイントに相当する処理がないという理由でビルドエラーが発生することが分かります。

Xcode のビルドエラーから、エントリポイントがないことによるエラーだったことが分かる
Xcode のビルドエラーから、エントリポイントがないことによるエラーだったことが分かる

5. まとめ

いくつかの確認を経て、platform/ios/main.m に定義されている関数 int main(int argc, char *argv[]) が Godot の iOS 向けアプリケーションのエントリポイントであることが分かりました。

そして、このエントリポイントとなる関数はエクスポートされた Xcode プロジェクトにコードとして置かれている訳ではなく、ライブラリ libgodot.a の内部に定義されていることも分かりました。

さらに、libgodot.a は内部にアプリケーションの起動から動作に必要な処理をすべて含んでいるため、外から参照されることを全く想定していません。Xcode プロジェクトに配置したコードから libgodot.a の内部にある定義を参照して操作をするためには、libgodot.a に含まれる定義を外部に公開する必要があり、その実現は次の課題になりそうです。

Discussion