Add shared libraries support for Web to load game module (C++)
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user