Froola

https://github.com/ayumax/Froola

Froolaは私の作成したUnreal Engineのコードプラグインを複数プラットフォーム同時にテストしたり、プラグインパッケージを生成したりするためのツールです。

もともと私はUEのプラグインをいくつか作っていたのですが、それらはWindows, Macのプラットフォームに対応していたため毎回パッケージの作成が面倒でした。

というのも、Windows環境ではWindows, Android, Linux向けにパッケージを作れますがMac, iOS向けには作れません。

そのため、WindowsとMacの両環境でプラグインのビルドをしたあとに、それらを組み合わせて1つのプラグインパッケージを作っていました。

Unreal Editorにそのような機能があればよかったのですが、私の調べた範囲では見つからなかったので毎回手動で組み合わせていました。

でも、どうせならこれらを1つのツールとしたほうがうれしい人もいるかもしれないと思い、Froolaを作りました。

GitHub ActionsなどのCI/CD機能との比較

上記のようなことをしたい場合、通常ならGitHub ActionsなどのCI/CD機能を利用することをまず思いつきます。

しかし、Unreal Engineをクラウドで動作させるのは主に金銭的に大変です。

Unreal Engineの1つのバージョンはだいたい50GBくらいになり、複数バージョン用意するとさらにストレージは必要になります。

またCPUもそこそこパワーがあるものが必要になるため、実行環境を用意するのが大変でした。

またMacはAWSなどでEC2を借りれたりもするのですが、最低1日単位でかなり高いです。ほかにもいくつかMacのクラウドサービスもありますが、それらも高価でした。

そこでおそらく多くの人はオンプレのサーバーをself-hosted runnerとして使っているのではないかと思います。

ただ個人でpublicなリポジトリを扱っている場合はセキュリティの観点からself-hosted runnerは避けたかったので、そうなると自分の開発PCで直接ビルドするのが一番よいなという結論になりました。

Froolaの構成

Froolaはマルチプラットフォーム対応のプラグインパッケージを生成しますが、やっていることは単純でWindowsとMacの両環境でプラグインをビルドしてから、それらを組み合わせて1つのプラグインパッケージを作っています。

またLinuxビルドは考えた結果WindowsのUEを使わずにLinuxのUEをDocker経由で使うようにしています。これについての理由は後述します。

そのため、以下のようなフローで動きます。

  1. Windows環境にプラグインプロジェクトをリモートリポジトリからCloneする
  2. Windows環境にWindows, Linuxビルド用のディレクトリをつくりそこにリポジトリのファイルをコピー
  3. MacにSCPで(1)でCloneしたリポジトリのファイルをコピーする
  4. WindowsのUEと、Windows上のDockerのUEを同時に起動してプラグインをビルドする
  5. MacにSSHでMacのUEを使ってプラグインをビルドする
  6. MacでビルドしたパッケージをSCPでWindowsにダウンロード
  7. Windows, Mac, Linuxのビルドしたパッケージを組み合わせて1つのプラグインパッケージを作成する
    sequenceDiagram
    participant Windows
    participant Windows(Docker)
    participant Mac

    Windows->>Windows: リモートリポジトリからClone
    Windows->>Windows: Windows, Linuxビルド用ディレクトリ作成&ファイルコピー
    Windows->>Mac: SCPでリポジトリファイルをコピー
    Windows->>Windows: UEでプラグインビルド
    Windows->>Windows(Docker): Docker内UEでプラグインビルド
    Mac->>Mac: SSHでMacのUEを使いプラグインビルド
    Mac->>Windows: MacでビルドしたパッケージをSCPでWindowsにダウンロード
    Windows->>Windows: 各OSのビルドパッケージを組み合わせて1つのプラグインパッケージを作成

これを見るとまったく複雑なことはやっていないことがわかると思います。

シンプルイズベストです。

というかこれ以外にやれる方法は現状ないと思います。せいぜいWindwosのUEでLinux向けも一緒にビルドするくらい。

自動テストの存在

今回Froolaを作るにあたって、パッケージをつくるだけでなくテストの実行も各プラットフォームで行えるようにしました。

Unreal EngineのC++はマルチプラットフォームになっているので、同じコードが異なるプラットフォームで動作するようになっているのですが、実際に期待通りに動くかどうかは場合によりけりです。

私の作ったプラグインでObjectDelivererというネットワーク機能を使うプラグインがあるのですが、ネットワークの機能はOSによって異なるのでWindowsだと問題ないコードもMacだと失敗するといったことがありました。

そのため、プラグインの品質を保つためには各プラットフォームでのテストの実行が不可欠です。

FroolaはWindwos, Mac, LinuxのUnreal Editorを使ってテストを実行するので、3つのプラットフォームでテストの結果を確認することができます。

テスト結果やEditor実行時のログはまとめてWindows PCに保存されるのでテストが失敗した場合の原因究明に役立ちます。

ちなみに、このテスト実行の存在がLinux向けビルドをWindowsのUEで行わない理由です。

Froolaの実行方法

FroolaはWindowsのexe形式のアプリとして提供されます。

動作方法はこちらを参照してください。

以下のようなコマンドを実行して動作させることができます。

Froola.exe plugin -n ObjectDeliverer -p ObjectDelivererTest -u git@github.com:ayumax/ObjectDeliverer.git -b master -e [Windows,Mac,Linux]  -v [5.5] -t -c -g [Win64,Mac,Android,IOS,Linux]

実行後、このようなディレクトリに結果が保存されます。

一か所にすべての結果が集中して保存されるので、問題発生時の原因究明が容易です。

また、releasesディレクトリには最終的なプラグインパッケージが保存されます。これは対象プラットフォーム向けに生成されたファイルがマージされた状態で保存されているので、このディレクトリをFabに提出すればマルチプラットフォーム対応のプラグインとして公開することができます。

20250502_205034_ObjectDeliverer/
├── build
│   ├── Windows_UE5.5
│   │   └── Build.log
│   ├── Mac_UE5.5
│   │   └── Build.log
│   └── Linux_UE5.5
│       └── Build.log
├── tests
│   ├── Windows_UE5.5
│   │   ├── AutomationTest.log
│   │   ├── index.html
│   │   └── index.json
│   ├── Mac_UE5.5
│   │   ├── AutomationTest.log
│   │   ├── index.html
│   │   └── index.json
│   └── Linux_UE5.5
│       ├── AutomationTest.log
│       ├── index.html
│       └── index.json
├── packages
│   ├── Windows_UE5.5
│   │   ├── BuildPlugin.log
│   │   └── Plugin
│   │       ├── ObjectDeliverer.uplugin
│   │       ├── Binaries
│   │       ├── Intermediate
│   │       ├── Resources
│   │       └── Source
│   ├── Mac_UE5.5
│   │   ├── BuildPlugin.log
│   │   └── Plugin
│   │       ├── ObjectDeliverer.uplugin
│   │       ├── Binaries
│   │       ├── Intermediate
│   │       ├── Resources
│   │       └── Source
│   └── Linux_UE5.5
│       ├── BuildPlugin.log
│       └── Plugin
│           ├── ObjectDeliverer.uplugin
│           ├── Binaries
│           ├── Intermediate
│           ├── Resources
│           └── Source
├── releases
│   └── ObjectDeliverer_UE5.5
│       ├── ObjectDeliverer.uplugin
│       ├── Binaries
│       ├── Intermediate
│       ├── Resources
│       └── Source
├── froola.log
└── settings.json

Froolaの設計など

Froolaは私がC#が好きということもあり、C#のコンソールアプリとして作られています。

私は本格的なコンソールアプリをC#ではあまり作ったことがなかったので当初は大量のコマンドライン引数をどのように取得するか悩みました。

でもConsoleAppFrameworkという素晴らしいライブラリを発見して、大量のコマンドライン引数を簡単に取得することができました。

このライブラリはソースジェネレーターを使っているらしく、関数のコメントからコマンドライン引数の情報を作っているらしいです。すごい。

このライブラリを使うことで、アプリ内に複数コマンドがある場合でも実装がごちゃごちゃしなくなるので、今後別のコマンドを追加するときでも割と困らずに実装できると思います。

    /// <summary>
    ///     Runs the plugin build, test, and packaging process.
    /// </summary>
    /// <param name="pluginName">-n,Name of the plugin</param>
    /// <param name="projectName">-p,Name of the project</param>
    /// <param name="gitRepositoryUrl">-u,URL of the git repository</param>
    /// <param name="gitBranch">-b,Branch of the git repository</param>
    /// <param name="editorPlatforms">-e,Editor platforms</param>
    /// <param name="engineVersions">-v,Engine versions</param>
    /// <param name="resultPath">-o,Path to save results</param>
    /// <param name="runTest">-t,Run tests</param>
    /// <param name="runPackage">-c,Run packaging</param>
    /// <param name="packagePlatforms">-g,Game platforms</param>
    /// <param name="cancellationToken">token for cancellation</param>
    [Command("plugin")]
    [SuppressMessage("ReSharper", "ForeachCanBePartlyConvertedToQueryUsingAnotherGetEnumerator")]
    public async Task Run(
        [Required] string pluginName,
        [Required] string projectName,
        [Required] string gitRepositoryUrl,
        [MinLength(1)] [Required] string gitBranch,
        [EnumArray(typeof(EditorPlatform))] string[]? editorPlatforms = null,
        [EnumArray(typeof(UEVersion))] string[]? engineVersions = null,
        string? resultPath = null,
        bool? runTest = null,
        bool? runPackage = null,
        [EnumArray(typeof(GamePlatform))] string[]? packagePlatforms = null,
        CancellationToken cancellationToken = default)
    {
    }

まとめ

Froolaには私自身が使いたい機能をいれたので、私以外の開発者の方にとってはもしかしたらほしい機能ではない可能性はあります。

ただ、おそらく世界のどっかには私と同じ困りごとをしている人もいると思うので、そういった方の助けになればうれしいです。

今のところ、私自身の環境でしかテストできていないので、もしFroolaがうまく自分の環境で動かないということがあれば、GitHubのissueで教えてください。

私自身もFroolaを使ってプラグイン開発を効率的に行っていこうと思います!