Add shared libraries support for Web to load game module (C++)

This commit is contained in:
Wojtek Figat
2026-02-17 15:07:11 +01:00
parent 34bddc7db1
commit 9772227146
5 changed files with 49 additions and 7 deletions

View File

@@ -70,7 +70,7 @@ bool WebPlatformTools::OnPostProcess(CookingData& data)
{
Array<String> files;
FileSystem::DirectoryGetFiles(files, data.OriginalOutputPath, TEXT("*"), DirectorySearchOption::TopDirectoryOnly);
for (String& file : files)
for (const String& file : files)
{
if (file.EndsWith(TEXT(".js")))
{
@@ -89,6 +89,19 @@ bool WebPlatformTools::OnPostProcess(CookingData& data)
return true;
}
// Move .wasm assemblies into the data files in order for dlopen to work (blocking)
{
Array<String> files;
FileSystem::DirectoryGetFiles(files, data.OriginalOutputPath, TEXT("*.wasm"), DirectorySearchOption::AllDirectories);
StringView gameWasm = StringUtils::GetFileNameWithoutExtension(gameJs);
for (const String& file : files)
{
if (StringUtils::GetFileNameWithoutExtension(file) == gameWasm)
continue; // Skip the main game module
FileSystem::MoveFile(data.DataOutputPath / StringUtils::GetFileName(file), file, true);
}
}
// Pack data files into a single file using Emscripten's file_packager tool
{
CreateProcessSettings procSettings;

View File

@@ -11,11 +11,13 @@
#include "Engine/Core/Collections/Dictionary.h"
#include "Engine/Platform/CPUInfo.h"
#include "Engine/Platform/MemoryStats.h"
#include "Engine/Profiler/ProfilerCPU.h"
#if !BUILD_RELEASE
#include "Engine/Core/Types/StringView.h"
#include "Engine/Utilities/StringConverter.h"
#endif
#include <chrono>
#include <dlfcn.h>
#include <unistd.h>
#include <emscripten/emscripten.h>
#include <emscripten/threading.h>
@@ -279,16 +281,25 @@ bool WebPlatform::SetEnvironmentVariable(const String& name, const String& value
void* WebPlatform::LoadLibrary(const Char* filename)
{
return nullptr;
PROFILE_CPU();
ZoneText(filename, StringUtils::Length(filename));
const StringAsANSI<> filenameANSI(filename);
void* result = dlopen(filenameANSI.Get(), RTLD_NOW);
if (!result)
{
LOG(Error, "Failed to load {0} because {1}", filename, String(dlerror()));
}
return result;
}
void WebPlatform::FreeLibrary(void* handle)
{
dlclose(handle);
}
void* WebPlatform::GetProcAddress(void* handle, const char* symbol)
{
return nullptr;
return dlsym(handle, symbol);
}
#endif

View File

@@ -148,8 +148,12 @@ namespace Flax.Build
{
if (OutputType == TargetOutputType.Executable && !Configuration.BuildBindingsOnly)
{
if (buildOptions.Platform.Target == TargetPlatform.Android)
switch (buildOptions.Platform.Target)
{
case TargetPlatform.Android:
case TargetPlatform.Web:
return false;
}
if (!buildOptions.Platform.HasModularBuildSupport)
return false;
return !IsMonolithicExecutable || (!buildOptions.Platform.HasExecutableFileReferenceSupport && UseSymbolsExports);

View File

@@ -17,10 +17,13 @@ namespace Flax.Build.Platforms
public override bool HasRequiredSDKsInstalled { get; }
/// <inheritdoc />
public override bool HasSharedLibrarySupport => false;
public override bool HasSharedLibrarySupport => true;
/// <inheritdoc />
public override bool HasModularBuildSupport => false;
public override bool HasModularBuildSupport => true;
/// <inheritdoc />
public override bool HasExecutableFileReferenceSupport => true;
/// <inheritdoc />
public override bool HasDynamicCodeExecutionSupport => false;

View File

@@ -132,6 +132,9 @@ namespace Flax.Build.Platforms
if (options.LinkEnv.LinkTimeCodeGeneration)
args.Add("-flto");
if (options.LinkEnv.Output == LinkerOutput.SharedLibrary)
args.Add("-fPIC");
var sanitizers = options.CompileEnv.Sanitizers;
if (sanitizers.HasFlag(Sanitizer.Address))
args.Add("-fsanitize=address");
@@ -259,6 +262,13 @@ namespace Flax.Build.Platforms
// Setup file access (Game Cooker packs files with file_packager tool)
args.Add("-sFORCE_FILESYSTEM");
args.Add("-sLZ4");
// https://emscripten.org/docs/compiling/Dynamic-Linking.html#dynamic-linking
// TODO: use -sMAIN_MODULE=2 and -sSIDE_MODULE=2 to strip unused code (mark public APIs with EMSCRIPTEN_KEEPALIVE)
if (options.LinkEnv.Output == LinkerOutput.Executable)
args.Add("-sMAIN_MODULE");
else
args.Add("-sSIDE_MODULE");
}
args.Add("-Wl,--start-group");
@@ -266,6 +276,7 @@ namespace Flax.Build.Platforms
// Input libraries
var libraryPaths = new HashSet<string>();
var dynamicLibExt = Platform.SharedLibraryFileExtension;
var executableExt = Platform.ExecutableFileExtension;
foreach (var library in options.LinkEnv.InputLibraries.Concat(options.Libraries))
{
var dir = Path.GetDirectoryName(library);
@@ -279,7 +290,7 @@ namespace Flax.Build.Platforms
{
args.Add(string.Format("\"-l{0}\"", library));
}
else if (string.IsNullOrEmpty(ext))
else if (ext == executableExt)
{
// Skip executable
}