Froola

https://github.com/ayumax/Froola

Froola is a tool I created for testing Unreal Engine code plugins on multiple platforms simultaneously and generating plugin packages.

Originally, I had developed several UE plugins that supported Windows and Mac platforms, which made package creation a tedious process each time.

This is because in a Windows environment, you can create packages for Windows, Android, and Linux, but not for Mac and iOS.

Therefore, I had to build the plugin in both Windows and Mac environments, and then combine them to create a single plugin package.

It would have been nice if Unreal Editor had such functionality, but I couldn’t find it within my research, so I was manually combining them each time.

However, I thought others might also benefit from having these capabilities in a single tool, so I created Froola.

Comparison with CI/CD features like GitHub Actions

Normally, when wanting to accomplish such tasks, the first thing that comes to mind is using CI/CD features like GitHub Actions.

However, running Unreal Engine in the cloud is challenging, primarily from a financial perspective.

A single version of Unreal Engine is approximately 50GB, and preparing multiple versions requires even more storage.

Additionally, a fairly powerful CPU is necessary, making it difficult to set up an execution environment.

Although you can rent Mac EC2 instances on AWS, they are quite expensive with a minimum one-day rental. Other Mac cloud services I found were also costly.

Therefore, I believe many people are likely using on-premises servers as self-hosted runners.

However, when dealing with public repositories as an individual, I wanted to avoid self-hosted runners for security reasons, which led me to conclude that building directly on my development PC was the best option.

Froola’s Structure

Froola generates multi-platform compatible plugin packages, but what it does is straightforward: it builds the plugin in both Windows and Mac environments and then combines them to create a single plugin package.

After consideration, I decided to use Linux UE through Docker instead of using Windows UE for Linux builds. I’ll explain the reason for this later.

Therefore, it operates with the following workflow:

  1. Clone the plugin project from a remote repository to the Windows environment
  2. Create directories for Windows and Linux builds in the Windows environment and copy the repository files there
  3. Copy the repository files cloned in (1) to Mac via SCP
  4. Launch UE on Windows and UE in Docker on Windows simultaneously to build the plugin
  5. Build the plugin using Mac’s UE via SSH
  6. Download the package built on Mac to Windows via SCP
  7. Combine the packages built for Windows, Mac, and Linux to create a single plugin package
    sequenceDiagram
    participant Windows
    participant Windows(Docker)
    participant Mac

    Windows->>Windows: Clone from remote repository
    Windows->>Windows: Create Windows, Linux build directories & copy files
    Windows->>Mac: Copy repository files via SCP
    Windows->>Windows: Build plugin with UE
    Windows->>Windows(Docker): Build plugin with UE in Docker
    Mac->>Mac: Build plugin with Mac's UE via SSH
    Mac->>Windows: Download Mac-built package to Windows via SCP
    Windows->>Windows: Combine build packages from each OS into a single plugin package

As you can see, there’s nothing particularly complex about this process.

Simplicity is best.

In fact, I believe there are no other viable methods currently available. At most, you could build for Linux alongside Windows using Windows UE.

Automated Testing

When creating Froola, I made it possible to run tests on each platform in addition to creating packages.

Unreal Engine’s C++ is designed for multiple platforms, so the same code should work on different platforms, but whether it actually performs as expected varies case by case.

One of my plugins, ObjectDeliverer, uses network functionality, and since network features differ by OS, code that works fine on Windows might fail on Mac.

Therefore, running tests on each platform is essential to maintain plugin quality.

Froola uses Unreal Editor on Windows, Mac, and Linux to run tests, allowing you to verify test results across all three platforms.

Test results and Editor execution logs are saved on the Windows PC, which helps in identifying the cause when tests fail.

Incidentally, this testing capability is the reason why I don’t use Windows UE for Linux builds.

How to Run Froola

Froola is provided as a Windows executable application.

For operation instructions, please refer to this link.

You can run it using a command like the following:

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]

After execution, results are saved in a directory structure like this:

All results are centrally stored in one location, making it easy to troubleshoot when problems occur.

Additionally, the final plugin package is saved in the releases directory. This contains merged files generated for the target platforms, so you can submit this directory to Fab to publish it as a multi-platform compatible plugin.

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’s Design

Froola is built as a C# console application, partly because I like C#.

I hadn’t created many full-fledged console applications in C# before, so initially I was concerned about how to handle numerous command-line arguments.

However, I discovered an excellent library called ConsoleAppFramework, which made it easy to handle a large number of command-line arguments.

This library apparently uses source generators and creates command-line argument information from function comments. It’s impressive.

By using this library, the implementation doesn’t get messy even when the application has multiple commands, so I think I can implement additional commands in the future without much difficulty.

    /// <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)
    {
    }

Conclusion

Froola includes features that I personally wanted, so it may not have all the functions that other developers are looking for.

However, I believe there must be someone in the world facing similar challenges, so I hope this tool will be helpful to those people.

At the moment, Froola has only been tested in my own environment. If you encounter any issues or if Froola does not work well in your environment, please let me know by creating an issue on GitHub.

I will continue to use Froola myself to make plugin development more efficient!