Add support for binary modules with native-code only

This commit is contained in:
Wojtek Figat
2021-02-25 13:01:15 +01:00
parent 3da6f9186c
commit b193a7abc4
21 changed files with 123 additions and 27 deletions

View File

@@ -456,7 +456,7 @@ int32 GameCookerImpl::ThreadFunction()
bool GameCookerService::Init()
{
auto editorAssembly = GetBinaryModuleFlaxEngine()->Assembly;
auto editorAssembly = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly;
editorAssembly->Unloading.Bind(OnEditorAssemblyUnloading);
return false;

View File

@@ -73,7 +73,7 @@ MonoReflectionType* CustomEditorsUtil::GetCustomEditor(MonoReflectionType* refTy
bool CustomEditorsUtilService::Init()
{
TRACK_ASSEMBLY(GetBinaryModuleFlaxEngine()->Assembly);
TRACK_ASSEMBLY(((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly);
Scripting::BinaryModuleLoaded.Bind(&OnBinaryModuleLoaded);
return false;
@@ -84,7 +84,7 @@ void OnAssemblyLoaded(MAssembly* assembly)
const auto startTime = DateTime::NowUTC();
// Prepare FlaxEngine
auto engineAssembly = GetBinaryModuleFlaxEngine()->Assembly;
auto engineAssembly = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly;
if (!engineAssembly->IsLoaded())
{
LOG(Warning, "Cannot load custom editors meta for assembly {0} because FlaxEngine is not loaded.", assembly->ToString());
@@ -173,7 +173,7 @@ void OnAssemblyLoaded(MAssembly* assembly)
void OnAssemblyUnloading(MAssembly* assembly)
{
// Fast clear for editor unloading
if (assembly == GetBinaryModuleFlaxEngine()->Assembly)
if (assembly == ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly)
{
Cache.Clear();
return;

View File

@@ -900,7 +900,7 @@ public:
if (stack && stack->Scope)
{
const int32 count = stack->Scope->Parameters.Length() + stack->Scope->ReturnedValues.Count();
const auto mclass = GetBinaryModuleFlaxEngine()->Assembly->GetClass("FlaxEditor.Editor+VisualScriptLocal");
const auto mclass = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly->GetClass("FlaxEditor.Editor+VisualScriptLocal");
ASSERT(mclass);
result = mono_array_new(mono_domain_get(), mclass->GetNative(), count);
VisualScriptLocalManaged local;
@@ -954,7 +954,7 @@ public:
s = s->PreviousFrame;
count++;
}
const auto mclass = GetBinaryModuleFlaxEngine()->Assembly->GetClass("FlaxEditor.Editor+VisualScriptStackFrame");
const auto mclass = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly->GetClass("FlaxEditor.Editor+VisualScriptStackFrame");
ASSERT(mclass);
result = mono_array_new(mono_domain_get(), mclass->GetNative(), count);
s = stack;

View File

@@ -175,7 +175,7 @@ ManagedEditor::ManagedEditor()
: PersistentScriptingObject(SpawnParams(ObjectID, ManagedEditor::TypeInitializer))
{
// Link events
auto editor = GetBinaryModuleFlaxEngine()->Assembly;
auto editor = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly;
editor->Loaded.Bind<ManagedEditor, &ManagedEditor::OnEditorAssemblyLoaded>(this);
ProbesRenderer::OnRegisterBake.Bind<OnRegisterBake>();
ProbesRenderer::OnFinishBake.Bind<OnFinishBake>();
@@ -192,7 +192,7 @@ ManagedEditor::ManagedEditor()
ManagedEditor::~ManagedEditor()
{
// Unlink events
auto editor = GetBinaryModuleFlaxEngine()->Assembly;
auto editor = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly;
editor->Loaded.Unbind<ManagedEditor, &ManagedEditor::OnEditorAssemblyLoaded>(this);
ProbesRenderer::OnRegisterBake.Unbind<OnRegisterBake>();
ProbesRenderer::OnFinishBake.Unbind<OnFinishBake>();

View File

@@ -524,7 +524,7 @@ bool ScriptsBuilderService::Init()
_isInited = true;
// Link for Editor assembly unload event to clear cached Internal_OnCompilationEnd to prevent errors
auto editorAssembly = GetBinaryModuleFlaxEngine()->Assembly;
auto editorAssembly = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly;
editorAssembly->Unloading.Bind(onEditorAssemblyUnloading);
// Listen to scripts reloading events and forward them to c#

View File

@@ -104,7 +104,7 @@ bool CacheMethods()
if (Internal_SendLog && Internal_SendLogException && Internal_GetStackTrace)
return false;
auto engine = GetBinaryModuleFlaxEngine()->Assembly;
auto engine = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly;
if (engine == nullptr || !engine->IsLoaded())
return true;
auto debugLogHandlerClass = engine->GetClass("FlaxEngine.DebugLogHandler");

View File

@@ -163,7 +163,7 @@ void ManagedPostProcessEffect::FetchInfo()
static MMethod* FetchInfoManaged = nullptr;
if (FetchInfoManaged == nullptr)
{
auto klass = GetBinaryModuleFlaxEngine()->Assembly->GetClass("FlaxEngine.PostProcessEffect");
auto klass = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly->GetClass("FlaxEngine.PostProcessEffect");
ASSERT(klass);
FetchInfoManaged = klass->GetMethod("FetchInfo", 3);
ASSERT(FetchInfoManaged);

View File

@@ -633,7 +633,7 @@ void ManagedBinaryModule::OnLoaded(MAssembly* assembly)
// Cache types for managed-only types that can be used in the engine
_firstManagedTypeIndex = Types.Count();
NativeBinaryModule* flaxEngine = GetBinaryModuleFlaxEngine();
NativeBinaryModule* flaxEngine = (NativeBinaryModule*)GetBinaryModuleFlaxEngine();
if (flaxEngine->Assembly->IsLoaded())
{
// TODO: check only assemblies that references FlaxEngine.CSharp.dll
@@ -1039,6 +1039,37 @@ void NativeBinaryModule::Destroy(bool isReloading)
}
}
NativeOnlyBinaryModule::NativeOnlyBinaryModule(const StringAnsiView& name)
: BinaryModule()
, _name(name)
, Library(nullptr)
{
}
const StringAnsi& NativeOnlyBinaryModule::GetName() const
{
return _name;
}
bool NativeOnlyBinaryModule::IsLoaded() const
{
return true;
}
void NativeOnlyBinaryModule::Destroy(bool isReloading)
{
BinaryModule::Destroy(isReloading);
// Release native library
const auto library = Library;
if (library)
{
Library = nullptr;
Platform::FreeLibrary(library);
// Don't do anything after FreeLibrary (this pointer might be invalid)
}
}
Array<GetBinaryModuleFunc>& StaticallyLinkedBinaryModuleInitializer::GetStaticallyLinkedBinaryModules()
{
static Array<GetBinaryModuleFunc> modules;

View File

@@ -355,7 +355,39 @@ public:
void Destroy(bool isReloading) override;
};
typedef NativeBinaryModule* (*GetBinaryModuleFunc)();
/// <summary>
/// The C++ scripting assembly container.
/// </summary>
class FLAXENGINE_API NativeOnlyBinaryModule : public BinaryModule
{
private:
StringAnsi _name;
public:
/// <summary>
/// Initializes a new instance of the <see cref="NativeOnlyBinaryModule" /> class.
/// </summary>
/// <param name="name">The module name.</param>
NativeOnlyBinaryModule(const StringAnsiView& name);
public:
/// <summary>
/// The native library (C++ DLL).
/// </summary>
void* Library;
public:
// [BinaryModule]
const StringAnsi& GetName() const override;
bool IsLoaded() const override;
void Destroy(bool isReloading) override;
};
typedef BinaryModule* (*GetBinaryModuleFunc)();
// Helper utility for registering native binary modules that are statically linked.
class FLAXENGINE_API StaticallyLinkedBinaryModuleInitializer

View File

@@ -66,7 +66,7 @@ void PluginManagerImpl::OnAssemblyLoaded(MAssembly* assembly)
PROFILE_CPU_NAMED("Load Assembly Plugins");
// Prepare FlaxEngine
auto engineAssembly = GetBinaryModuleFlaxEngine()->Assembly;
auto engineAssembly = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly;
if (!engineAssembly->IsLoaded())
{
LOG(Warning, "Cannot find plugin class types for assembly {0} because FlaxEngine is not loaded.", assembly->ToString());

View File

@@ -124,7 +124,7 @@ bool ScriptingService::Init()
const auto startTime = DateTime::NowUTC();
// Link for assemblies events
auto engineAssembly = GetBinaryModuleFlaxEngine()->Assembly;
auto engineAssembly = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly;
engineAssembly->Loaded.Bind(onEngineLoaded);
engineAssembly->Unloading.Bind(onEngineUnloading);
@@ -424,7 +424,7 @@ bool Scripting::Load()
// Load FlaxEngine
const String flaxEnginePath = Globals::BinariesFolder / TEXT("FlaxEngine.CSharp.dll");
if (GetBinaryModuleFlaxEngine()->Assembly->Load(flaxEnginePath))
if (((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly->Load(flaxEnginePath))
{
LOG(Error, "Failed to load FlaxEngine C# assembly.");
return true;

View File

@@ -48,7 +48,7 @@ void StdTypesContainer::Clear()
bool StdTypesContainer::Gather()
{
#define GET_CLASS(assembly, type, typeName) \
type = CONCAT_MACROS(GetBinaryModule, assembly)()->Assembly->GetClass(typeName); \
type = ((ManagedBinaryModule*)CONCAT_MACROS(GetBinaryModule, assembly)())->Assembly->GetClass(typeName); \
if (type == nullptr) \
{ \
LOG(Error, "Missing managed type: \'{0}\'", String(typeName)); \

View File

@@ -5,7 +5,7 @@
StaticallyLinkedBinaryModuleInitializer StaticallyLinkedBinaryModuleFlaxEngine(GetBinaryModuleFlaxEngine);
extern "C" NativeBinaryModule* GetBinaryModuleFlaxEngine()
extern "C" BinaryModule* GetBinaryModuleFlaxEngine()
{
static NativeBinaryModule module("FlaxEngine", MAssemblyOptions());
return &module;

View File

@@ -13,5 +13,5 @@
#define FLAXENGINE_COMPANY "Flax"
#define FLAXENGINE_COPYRIGHT "Copyright (c) 2012-2021 Wojciech Figat. All rights reserved."
class NativeBinaryModule;
extern "C" FLAXENGINE_API NativeBinaryModule* GetBinaryModuleFlaxEngine();
class BinaryModule;
extern "C" FLAXENGINE_API BinaryModule* GetBinaryModuleFlaxEngine();

View File

@@ -1091,6 +1091,10 @@ namespace Flax.Build.Bindings
private static unsafe void GenerateCSharp(BuildData buildData, IGrouping<string, Module> binaryModule)
{
// Skip generating C# bindings code for native-only modules
if (binaryModule.Any(x => !x.BuildCSharp))
return;
var contents = new StringBuilder();
var binaryModuleName = binaryModule.Key;
var project = Builder.GetModuleProject(binaryModule.First(), buildData);

View File

@@ -218,7 +218,7 @@ namespace Flax.Build.Bindings
using (var writer = new BinaryWriter(stream, Encoding.UTF8))
{
// Version
writer.Write(4);
writer.Write(5);
writer.Write(File.GetLastWriteTime(Assembly.GetExecutingAssembly().Location).Ticks);
// Build options
@@ -254,7 +254,7 @@ namespace Flax.Build.Bindings
{
// Version
var version = reader.ReadInt32();
if (version != 4)
if (version != 5)
return false;
if (File.GetLastWriteTime(Assembly.GetExecutingAssembly().Location).Ticks != reader.ReadInt64())
return false;

View File

@@ -1991,6 +1991,7 @@ namespace Flax.Build.Bindings
// Skip generating C++ bindings code for C#-only modules
if (binaryModule.Any(x => !x.BuildNativeCode))
return;
var useCSharp = binaryModule.Any(x => x.BuildCSharp);
var contents = new StringBuilder();
var binaryModuleName = binaryModule.Key;
@@ -2018,8 +2019,8 @@ namespace Flax.Build.Bindings
contents.AppendLine($"#define {binaryModuleNameUpper}_COMPANY \"{project.Company}\"");
contents.AppendLine($"#define {binaryModuleNameUpper}_COPYRIGHT \"{project.Copyright}\"");
contents.AppendLine();
contents.AppendLine("class NativeBinaryModule;");
contents.AppendLine($"extern \"C\" {binaryModuleNameUpper}_API NativeBinaryModule* GetBinaryModule{binaryModuleName}();");
contents.AppendLine("class BinaryModule;");
contents.AppendLine($"extern \"C\" {binaryModuleNameUpper}_API BinaryModule* GetBinaryModule{binaryModuleName}();");
GenerateCppBinaryModuleHeader?.Invoke(buildData, binaryModule, contents);
Utilities.WriteFileIfChanged(binaryModuleHeaderPath, contents.ToString());
@@ -2033,9 +2034,16 @@ namespace Flax.Build.Bindings
contents.AppendLine();
contents.AppendLine($"StaticallyLinkedBinaryModuleInitializer StaticallyLinkedBinaryModule{binaryModuleName}(GetBinaryModule{binaryModuleName});");
contents.AppendLine();
contents.AppendLine($"extern \"C\" NativeBinaryModule* GetBinaryModule{binaryModuleName}()");
contents.AppendLine($"extern \"C\" BinaryModule* GetBinaryModule{binaryModuleName}()");
contents.AppendLine("{");
contents.AppendLine($" static NativeBinaryModule module(\"{binaryModuleName}\", MAssemblyOptions());");
if (useCSharp)
{
contents.AppendLine($" static NativeBinaryModule module(\"{binaryModuleName}\", MAssemblyOptions());");
}
else
{
contents.AppendLine($" static NativeOnlyBinaryModule module(\"{binaryModuleName}\");");
}
contents.AppendLine(" return &module;");
contents.AppendLine("}");
GenerateCppBinaryModuleSource?.Invoke(buildData, binaryModule, contents);

View File

@@ -33,6 +33,7 @@ namespace Flax.Build.Bindings
writer.Write(Module.FilePath);
BindingsGenerator.Write(writer, Module.BinaryModuleName);
writer.Write(Module.BuildNativeCode);
writer.Write(Module.BuildCSharp);
base.Write(writer);
}
@@ -42,7 +43,9 @@ namespace Flax.Build.Bindings
if (reader.ReadString() != Module.Name ||
reader.ReadString() != Module.FilePath ||
BindingsGenerator.Read(reader, Module.BinaryModuleName) != Module.BinaryModuleName ||
reader.ReadBoolean() != Module.BuildNativeCode)
reader.ReadBoolean() != Module.BuildNativeCode ||
reader.ReadBoolean() != Module.BuildCSharp
)
throw new Exception();
base.Read(reader);

View File

@@ -23,6 +23,8 @@ namespace Flax.Build
var buildOptions = buildData.TargetOptions;
foreach (var binaryModule in buildData.BinaryModules)
{
if (binaryModule.All(x => !x.BuildCSharp))
continue;
var binaryModuleName = binaryModule.Key;
using (new ProfileEventScope(binaryModuleName))
{
@@ -71,6 +73,7 @@ namespace Flax.Build
var dependencyModule = buildData.Rules.GetModule(dependencyName);
if (dependencyModule != null &&
!string.IsNullOrEmpty(dependencyModule.BinaryModuleName) &&
dependencyModule.BuildCSharp &&
dependencyModule.BinaryModuleName != binaryModuleName &&
buildData.Modules.TryGetValue(dependencyModule, out var dependencyModuleOptions))
{

View File

@@ -37,6 +37,11 @@ namespace Flax.Build
/// </summary>
public bool BuildNativeCode = true;
/// <summary>
/// True if module has C# code to build. Can be used for native modules without C# bindings nor code.
/// </summary>
public bool BuildCSharp = true;
/// <summary>
/// Initializes a new instance of the <see cref="Module"/> class.
/// </summary>

View File

@@ -831,6 +831,11 @@ namespace Flax.Build
// C#-only binary module
binaryModuleInfo.NativePath = string.Empty;
}
if (!binaryModule.Any(x => x.BuildCSharp))
{
// Skip C#
binaryModuleInfo.ManagedPath = string.Empty;
}
break;
}
case TargetLinkType.Modular:
@@ -850,6 +855,11 @@ namespace Flax.Build
// C#-only binary module
binaryModuleInfo.NativePath = string.Empty;
}
if (!module.BuildCSharp)
{
// Skip C#
binaryModuleInfo.ManagedPath = string.Empty;
}
break;
}
default: throw new ArgumentOutOfRangeException();