Merge remote-tracking branch 'origin/1.1' into linux-editor
# Conflicts: # Source/FlaxEngine.Gen.cs # Source/Tools/Flax.Build/Utilities/Utilities.cs
This commit is contained in:
@@ -1,16 +1,17 @@
|
||||
// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved.
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Flax.Build.Bindings
|
||||
{
|
||||
/// <summary>
|
||||
/// The native type information for bindings generator.
|
||||
/// </summary>
|
||||
public class ApiTypeInfo
|
||||
public class ApiTypeInfo : IBindingsCache
|
||||
{
|
||||
public ApiTypeInfo Parent;
|
||||
public List<ApiTypeInfo> Children;
|
||||
public List<ApiTypeInfo> Children = new List<ApiTypeInfo>();
|
||||
public string NativeName;
|
||||
public string Name;
|
||||
public string Namespace;
|
||||
@@ -22,6 +23,7 @@ namespace Flax.Build.Bindings
|
||||
public virtual bool IsClass => false;
|
||||
public virtual bool IsStruct => false;
|
||||
public virtual bool IsEnum => false;
|
||||
public virtual bool IsInterface => false;
|
||||
public virtual bool IsValueType => false;
|
||||
public virtual bool IsScriptingObject => false;
|
||||
public virtual bool IsPod => false;
|
||||
@@ -90,6 +92,35 @@ namespace Flax.Build.Bindings
|
||||
Children.Add(apiTypeInfo);
|
||||
}
|
||||
|
||||
public virtual void Write(BinaryWriter writer)
|
||||
{
|
||||
BindingsGenerator.Write(writer, NativeName);
|
||||
BindingsGenerator.Write(writer, Name);
|
||||
BindingsGenerator.Write(writer, Namespace);
|
||||
BindingsGenerator.Write(writer, Attributes);
|
||||
BindingsGenerator.Write(writer, Comment);
|
||||
writer.Write(IsInBuild);
|
||||
BindingsGenerator.Write(writer, Children);
|
||||
}
|
||||
|
||||
public virtual void Read(BinaryReader reader)
|
||||
{
|
||||
NativeName = BindingsGenerator.Read(reader, NativeName);
|
||||
Name = BindingsGenerator.Read(reader, Name);
|
||||
Namespace = BindingsGenerator.Read(reader, Namespace);
|
||||
Attributes = BindingsGenerator.Read(reader, Attributes);
|
||||
Comment = BindingsGenerator.Read(reader, Comment);
|
||||
IsInBuild = reader.ReadBoolean();
|
||||
Children = BindingsGenerator.Read(reader, Children);
|
||||
|
||||
// Fix hierarchy
|
||||
for (int i = 0; i < Children.Count; i++)
|
||||
{
|
||||
var child = Children[i];
|
||||
child.Parent = this;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace Flax.Build.Bindings
|
||||
public const string Enum = "API_ENUM";
|
||||
public const string Class = "API_CLASS";
|
||||
public const string Struct = "API_STRUCT";
|
||||
public const string Interface = "API_INTERFACE";
|
||||
public const string Function = "API_FUNCTION";
|
||||
public const string Property = "API_PROPERTY";
|
||||
public const string Field = "API_FIELD";
|
||||
@@ -27,6 +28,7 @@ namespace Flax.Build.Bindings
|
||||
Enum,
|
||||
Class,
|
||||
Struct,
|
||||
Interface,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -66,6 +66,14 @@ namespace Flax.Build.Bindings
|
||||
return null;
|
||||
}
|
||||
|
||||
// Special case for Engine TEXT macro
|
||||
if (value.Contains("TEXT(\""))
|
||||
return value.Replace("TEXT(\"", "(\"");
|
||||
|
||||
// Special case for value constructors
|
||||
if (value.Contains('(') && value.Contains(')'))
|
||||
return "new " + value;
|
||||
|
||||
// Convert from C++ to C#
|
||||
switch (value)
|
||||
{
|
||||
@@ -124,8 +132,8 @@ namespace Flax.Build.Bindings
|
||||
return result;
|
||||
}
|
||||
|
||||
// ScriptingObjectReference or AssetReference or WeakAssetReference
|
||||
if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference") && typeInfo.GenericArgs != null)
|
||||
// ScriptingObjectReference or AssetReference or WeakAssetReference or SoftObjectReference
|
||||
if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null)
|
||||
return typeInfo.GenericArgs[0].Type.Replace("::", ".");
|
||||
|
||||
// Array or Span
|
||||
@@ -180,8 +188,8 @@ namespace Flax.Build.Bindings
|
||||
return "IntPtr";
|
||||
}
|
||||
|
||||
// ScriptingObjectReference or AssetReference or WeakAssetReference
|
||||
if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference") && typeInfo.GenericArgs != null)
|
||||
// ScriptingObjectReference or AssetReference or WeakAssetReference or SoftObjectReference
|
||||
if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null)
|
||||
return "IntPtr";
|
||||
|
||||
return GenerateCSharpNativeToManaged(buildData, typeInfo, caller);
|
||||
@@ -223,8 +231,8 @@ namespace Flax.Build.Bindings
|
||||
return "FlaxEngine.Object.GetUnmanagedPtr";
|
||||
}
|
||||
|
||||
// ScriptingObjectReference or AssetReference or WeakAssetReference
|
||||
if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference") && typeInfo.GenericArgs != null)
|
||||
// ScriptingObjectReference or AssetReference or WeakAssetReference or SoftObjectReference
|
||||
if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null)
|
||||
return "FlaxEngine.Object.GetUnmanagedPtr";
|
||||
|
||||
// Default
|
||||
@@ -386,14 +394,36 @@ namespace Flax.Build.Bindings
|
||||
}
|
||||
}
|
||||
|
||||
private static void GenerateCSharpAttributes(BuildData buildData, StringBuilder contents, string indent, string attributes, string[] comment, bool canUseTooltip, bool useUnmanaged)
|
||||
private static bool IsDefaultValueSupported(string value)
|
||||
{
|
||||
// TEXT macro (eg. TEXT("text"))
|
||||
// TODO: support string for default value attribute
|
||||
if (value.Contains("TEXT(\""))
|
||||
return false;
|
||||
|
||||
// Value constructors (eg. Vector2(1, 2))
|
||||
// TODO: support value constructors for default value attribute
|
||||
if (value.Contains('(') && value.Contains(')'))
|
||||
return false;
|
||||
|
||||
// Constants (eg. Vector2::Zero)
|
||||
// TODO: support constants for default value attribute
|
||||
if (value.Contains("::"))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void GenerateCSharpAttributes(BuildData buildData, StringBuilder contents, string indent, ApiTypeInfo apiTypeInfo, string attributes, string[] comment, bool canUseTooltip, bool useUnmanaged, string defaultValue = null)
|
||||
{
|
||||
var writeTooltip = true;
|
||||
var writeDefaultValue = true;
|
||||
if (!string.IsNullOrEmpty(attributes))
|
||||
{
|
||||
// Write attributes
|
||||
contents.Append(indent).Append('[').Append(attributes).Append(']').AppendLine();
|
||||
writeTooltip = !attributes.Contains("Tooltip(");
|
||||
writeTooltip = !attributes.Contains("Tooltip(") && !attributes.Contains("HideInEditor");
|
||||
writeDefaultValue = !attributes.Contains("DefaultValue(");
|
||||
}
|
||||
|
||||
if (useUnmanaged)
|
||||
@@ -417,20 +447,27 @@ namespace Flax.Build.Bindings
|
||||
contents.Append(indent).Append("[Tooltip(\"").Append(tooltip).Append("\")]").AppendLine();
|
||||
}
|
||||
}
|
||||
if (!string.IsNullOrEmpty(defaultValue) && writeDefaultValue && IsDefaultValueSupported(defaultValue))
|
||||
{
|
||||
// Write default value attribute
|
||||
defaultValue = GenerateCSharpDefaultValueNativeToManaged(buildData, defaultValue, apiTypeInfo);
|
||||
contents.Append(indent).Append("[DefaultValue(").Append(defaultValue).Append(")]").AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
private static void GenerateCSharpAttributes(BuildData buildData, StringBuilder contents, string indent, ApiTypeInfo apiTypeInfo, bool useUnmanaged)
|
||||
private static void GenerateCSharpAttributes(BuildData buildData, StringBuilder contents, string indent, ApiTypeInfo apiTypeInfo, bool useUnmanaged, string defaultValue = null)
|
||||
{
|
||||
GenerateCSharpAttributes(buildData, contents, indent, apiTypeInfo.Attributes, apiTypeInfo.Comment, true, useUnmanaged);
|
||||
GenerateCSharpAttributes(buildData, contents, indent, apiTypeInfo, apiTypeInfo.Attributes, apiTypeInfo.Comment, true, useUnmanaged, defaultValue);
|
||||
}
|
||||
|
||||
private static void GenerateCSharpAttributes(BuildData buildData, StringBuilder contents, string indent, MemberInfo memberInfo, bool useUnmanaged)
|
||||
private static void GenerateCSharpAttributes(BuildData buildData, StringBuilder contents, string indent, ApiTypeInfo apiTypeInfo, MemberInfo memberInfo, bool useUnmanaged, string defaultValue = null)
|
||||
{
|
||||
GenerateCSharpAttributes(buildData, contents, indent, memberInfo.Attributes, memberInfo.Comment, true, useUnmanaged);
|
||||
GenerateCSharpAttributes(buildData, contents, indent, apiTypeInfo, memberInfo.Attributes, memberInfo.Comment, true, useUnmanaged, defaultValue);
|
||||
}
|
||||
|
||||
private static void GenerateCSharpClass(BuildData buildData, StringBuilder contents, string indent, ClassInfo classInfo)
|
||||
{
|
||||
var useUnmanaged = classInfo.IsStatic || classInfo.IsScriptingObject;
|
||||
contents.AppendLine();
|
||||
|
||||
// Namespace begin
|
||||
@@ -446,7 +483,7 @@ namespace Flax.Build.Bindings
|
||||
GenerateCSharpComment(contents, indent, classInfo.Comment);
|
||||
|
||||
// Class begin
|
||||
GenerateCSharpAttributes(buildData, contents, indent, classInfo, true);
|
||||
GenerateCSharpAttributes(buildData, contents, indent, classInfo, useUnmanaged);
|
||||
contents.Append(indent);
|
||||
if (classInfo.Access == AccessLevel.Public)
|
||||
contents.Append("public ");
|
||||
@@ -461,7 +498,7 @@ namespace Flax.Build.Bindings
|
||||
else if (classInfo.IsAbstract)
|
||||
contents.Append("abstract ");
|
||||
contents.Append("unsafe partial class ").Append(classInfo.Name);
|
||||
if (classInfo.BaseType != null && classInfo.BaseTypeInheritance != AccessLevel.Private)
|
||||
if (classInfo.BaseType != null && !classInfo.IsBaseTypeHidden)
|
||||
contents.Append(" : ").Append(GenerateCSharpNativeToManaged(buildData, classInfo.BaseType, classInfo));
|
||||
contents.AppendLine();
|
||||
contents.Append(indent + "{");
|
||||
@@ -482,19 +519,20 @@ namespace Flax.Build.Bindings
|
||||
// Events
|
||||
foreach (var eventInfo in classInfo.Events)
|
||||
{
|
||||
if (!useUnmanaged)
|
||||
throw new NotImplementedException("TODO: support events inside non-static and non-scripting API class types.");
|
||||
var paramsCount = eventInfo.Type.GenericArgs?.Count ?? 0;
|
||||
|
||||
contents.AppendLine();
|
||||
|
||||
foreach (var comment in eventInfo.Comment)
|
||||
{
|
||||
if (comment.Contains("/// <returns>"))
|
||||
continue;
|
||||
var c = comment.Replace("::", ".");
|
||||
contents.Append(indent);
|
||||
contents.Append(c);
|
||||
contents.AppendLine();
|
||||
contents.Append(indent).Append(comment.Replace("::", ".")).AppendLine();
|
||||
}
|
||||
|
||||
GenerateCSharpAttributes(buildData, contents, indent, eventInfo, true);
|
||||
GenerateCSharpAttributes(buildData, contents, indent, classInfo, eventInfo, useUnmanaged);
|
||||
contents.Append(indent);
|
||||
if (eventInfo.Access == AccessLevel.Public)
|
||||
contents.Append("public ");
|
||||
@@ -505,10 +543,10 @@ namespace Flax.Build.Bindings
|
||||
if (eventInfo.IsStatic)
|
||||
contents.Append("static ");
|
||||
string eventSignature = "event Action";
|
||||
if (eventInfo.Type.GenericArgs.Count != 0)
|
||||
if (paramsCount != 0)
|
||||
{
|
||||
eventSignature += '<';
|
||||
for (var i = 0; i < eventInfo.Type.GenericArgs.Count; i++)
|
||||
for (var i = 0; i < paramsCount; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
eventSignature += ", ";
|
||||
@@ -546,7 +584,7 @@ namespace Flax.Build.Bindings
|
||||
if (eventInfo.IsStatic)
|
||||
contents.Append("static ");
|
||||
contents.Append($"void Internal_{eventInfo.Name}_Invoke(");
|
||||
for (var i = 0; i < eventInfo.Type.GenericArgs.Count; i++)
|
||||
for (var i = 0; i < paramsCount; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
contents.Append(", ");
|
||||
@@ -557,7 +595,7 @@ namespace Flax.Build.Bindings
|
||||
contents.Append(indent).Append('{').AppendLine();
|
||||
contents.Append(indent).Append(" Internal_").Append(eventInfo.Name);
|
||||
contents.Append('(');
|
||||
for (var i = 0; i < eventInfo.Type.GenericArgs.Count; i++)
|
||||
for (var i = 0; i < paramsCount; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
contents.Append(", ");
|
||||
@@ -586,14 +624,10 @@ namespace Flax.Build.Bindings
|
||||
{
|
||||
if (comment.Contains("/// <returns>"))
|
||||
continue;
|
||||
|
||||
var c = comment.Replace("::", ".");
|
||||
contents.Append(indent);
|
||||
contents.Append(c);
|
||||
contents.AppendLine();
|
||||
contents.Append(indent).Append(comment.Replace("::", ".")).AppendLine();
|
||||
}
|
||||
|
||||
GenerateCSharpAttributes(buildData, contents, indent, fieldInfo, true);
|
||||
GenerateCSharpAttributes(buildData, contents, indent, classInfo, fieldInfo, useUnmanaged, fieldInfo.DefaultValue);
|
||||
contents.Append(indent);
|
||||
if (fieldInfo.Access == AccessLevel.Public)
|
||||
contents.Append("public ");
|
||||
@@ -604,8 +638,19 @@ namespace Flax.Build.Bindings
|
||||
if (fieldInfo.IsStatic)
|
||||
contents.Append("static ");
|
||||
var returnValueType = GenerateCSharpNativeToManaged(buildData, fieldInfo.Type, classInfo);
|
||||
contents.Append(returnValueType).Append(' ').AppendLine(fieldInfo.Name);
|
||||
contents.AppendLine(indent + "{");
|
||||
contents.Append(returnValueType).Append(' ').Append(fieldInfo.Name);
|
||||
if (!useUnmanaged)
|
||||
{
|
||||
if (fieldInfo.DefaultValue != null)
|
||||
{
|
||||
var defaultValue = GenerateCSharpDefaultValueNativeToManaged(buildData, fieldInfo.DefaultValue, classInfo);
|
||||
if (!string.IsNullOrEmpty(defaultValue))
|
||||
contents.Append(" = ").Append(defaultValue);
|
||||
}
|
||||
contents.AppendLine(";");
|
||||
continue;
|
||||
}
|
||||
contents.AppendLine().AppendLine(indent + "{");
|
||||
indent += " ";
|
||||
|
||||
contents.Append(indent).Append("get { ");
|
||||
@@ -615,7 +660,7 @@ namespace Flax.Build.Bindings
|
||||
if (!fieldInfo.IsReadOnly)
|
||||
{
|
||||
contents.Append(indent).Append("set { ");
|
||||
GenerateCSharpWrapperFunctionCall(buildData, contents, classInfo, fieldInfo.Setter, true);
|
||||
GenerateCSharpWrapperFunctionCall(buildData, contents, classInfo, fieldInfo.Setter, useUnmanaged);
|
||||
contents.Append(" }").AppendLine();
|
||||
}
|
||||
|
||||
@@ -630,23 +675,19 @@ namespace Flax.Build.Bindings
|
||||
// Properties
|
||||
foreach (var propertyInfo in classInfo.Properties)
|
||||
{
|
||||
if (!useUnmanaged)
|
||||
throw new NotImplementedException("TODO: support properties inside non-static and non-scripting API class types.");
|
||||
|
||||
contents.AppendLine();
|
||||
|
||||
foreach (var comment in propertyInfo.Comment)
|
||||
{
|
||||
if (comment.Contains("/// <returns>") || comment.Contains("<param name="))
|
||||
continue;
|
||||
|
||||
var c = comment.Replace("::", ".");
|
||||
contents.Append(indent);
|
||||
if (propertyInfo.Getter != null && propertyInfo.Setter != null)
|
||||
contents.Append(c.Replace("/// Gets ", "/// Gets or sets "));
|
||||
else
|
||||
contents.Append(c);
|
||||
contents.AppendLine();
|
||||
contents.Append(indent).Append(comment.Replace("::", ".")).AppendLine();
|
||||
}
|
||||
|
||||
GenerateCSharpAttributes(buildData, contents, indent, propertyInfo, true);
|
||||
GenerateCSharpAttributes(buildData, contents, indent, classInfo, propertyInfo, useUnmanaged);
|
||||
contents.Append(indent);
|
||||
if (propertyInfo.Access == AccessLevel.Public)
|
||||
contents.Append("public ");
|
||||
@@ -692,11 +733,14 @@ namespace Flax.Build.Bindings
|
||||
// Functions
|
||||
foreach (var functionInfo in classInfo.Functions)
|
||||
{
|
||||
if (!useUnmanaged)
|
||||
throw new Exception($"Not supported function {functionInfo.Name} inside non-static and non-scripting class type {classInfo.Name}.");
|
||||
|
||||
if (!functionInfo.NoProxy)
|
||||
{
|
||||
contents.AppendLine();
|
||||
GenerateCSharpComment(contents, indent, functionInfo.Comment);
|
||||
GenerateCSharpAttributes(buildData, contents, indent, functionInfo, true);
|
||||
GenerateCSharpAttributes(buildData, contents, indent, classInfo, functionInfo, true);
|
||||
contents.Append(indent);
|
||||
if (functionInfo.Access == AccessLevel.Public)
|
||||
contents.Append("public ");
|
||||
@@ -808,7 +852,7 @@ namespace Flax.Build.Bindings
|
||||
foreach (var comment in fieldInfo.Comment)
|
||||
contents.Append(indent).Append(comment).AppendLine();
|
||||
|
||||
GenerateCSharpAttributes(buildData, contents, indent, fieldInfo, fieldInfo.IsStatic);
|
||||
GenerateCSharpAttributes(buildData, contents, indent, structureInfo, fieldInfo, fieldInfo.IsStatic, fieldInfo.DefaultValue);
|
||||
contents.Append(indent);
|
||||
if (fieldInfo.Access == AccessLevel.Public)
|
||||
contents.Append("public ");
|
||||
@@ -832,7 +876,7 @@ namespace Flax.Build.Bindings
|
||||
contents.AppendLine();
|
||||
foreach (var comment in fieldInfo.Comment)
|
||||
contents.Append(indent).Append(comment).AppendLine();
|
||||
GenerateCSharpAttributes(buildData, contents, indent, fieldInfo, fieldInfo.IsStatic);
|
||||
GenerateCSharpAttributes(buildData, contents, indent, structureInfo, fieldInfo, fieldInfo.IsStatic);
|
||||
contents.Append(indent);
|
||||
if (fieldInfo.Access == AccessLevel.Public)
|
||||
contents.Append("public ");
|
||||
@@ -938,7 +982,7 @@ namespace Flax.Build.Bindings
|
||||
foreach (var comment in entryInfo.Comment)
|
||||
contents.Append(indent).Append(comment).AppendLine();
|
||||
|
||||
GenerateCSharpAttributes(buildData, contents, indent, entryInfo.Attributes, entryInfo.Comment, true, false);
|
||||
GenerateCSharpAttributes(buildData, contents, indent, enumInfo, entryInfo.Attributes, entryInfo.Comment, true, false);
|
||||
contents.Append(indent).Append(entryInfo.Name);
|
||||
if (!string.IsNullOrEmpty(entryInfo.Value))
|
||||
contents.Append(" = ").Append(entryInfo.Value);
|
||||
@@ -984,9 +1028,6 @@ namespace Flax.Build.Bindings
|
||||
|
||||
private static void GenerateCSharp(BuildData buildData, ModuleInfo moduleInfo, ref BindingsResult bindings)
|
||||
{
|
||||
if (!bindings.UseBindings)
|
||||
return;
|
||||
|
||||
var contents = new StringBuilder();
|
||||
buildData.Modules.TryGetValue(moduleInfo.Module, out var moduleBuildInfo);
|
||||
|
||||
@@ -1037,12 +1078,6 @@ namespace Flax.Build.Bindings
|
||||
// Save generated file
|
||||
contents.AppendLine("#endif");
|
||||
Utilities.WriteFileIfChanged(bindings.GeneratedCSharpFilePath, contents.ToString());
|
||||
|
||||
// Ensure that generated file is included into build
|
||||
if (useBindings && moduleBuildInfo != null && !moduleBuildInfo.SourceFiles.Contains(bindings.GeneratedCSharpFilePath))
|
||||
{
|
||||
moduleBuildInfo.SourceFiles.Add(bindings.GeneratedCSharpFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
internal struct GuidInterop
|
||||
|
||||
266
Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cache.cs
Normal file
266
Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cache.cs
Normal file
@@ -0,0 +1,266 @@
|
||||
// (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Flax.Build.NativeCpp;
|
||||
|
||||
namespace Flax.Build.Bindings
|
||||
{
|
||||
internal interface IBindingsCache
|
||||
{
|
||||
void Write(BinaryWriter writer);
|
||||
void Read(BinaryReader reader);
|
||||
}
|
||||
|
||||
partial class BindingsGenerator
|
||||
{
|
||||
private static readonly Dictionary<string, Type> _typeCache = new Dictionary<string, Type>();
|
||||
|
||||
internal static void Write(BinaryWriter writer, string e)
|
||||
{
|
||||
var valid = e != null;
|
||||
writer.Write(valid);
|
||||
if (valid)
|
||||
writer.Write(e);
|
||||
}
|
||||
|
||||
internal static void Write(BinaryWriter writer, string[] list)
|
||||
{
|
||||
if (list != null)
|
||||
{
|
||||
writer.Write(list.Length);
|
||||
for (int i = 0; i < list.Length; i++)
|
||||
writer.Write(list[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write(0);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Write(BinaryWriter writer, IEnumerable<string> list)
|
||||
{
|
||||
if (list != null)
|
||||
{
|
||||
writer.Write(list.Count());
|
||||
foreach (var e in list)
|
||||
writer.Write(e);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write(0);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Write<T>(BinaryWriter writer, T e) where T : IBindingsCache
|
||||
{
|
||||
if (e != null)
|
||||
{
|
||||
writer.Write(e.GetType().FullName);
|
||||
e.Write(writer);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write(string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Write<T>(BinaryWriter writer, IList<T> list) where T : IBindingsCache
|
||||
{
|
||||
if (list != null)
|
||||
{
|
||||
var count = list.Count;
|
||||
writer.Write(count);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var e = list[i];
|
||||
writer.Write(e.GetType().FullName);
|
||||
e.Write(writer);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write(0);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string Read(BinaryReader reader, string e)
|
||||
{
|
||||
var valid = reader.ReadBoolean();
|
||||
if (valid)
|
||||
e = reader.ReadString();
|
||||
return e;
|
||||
}
|
||||
|
||||
internal static string[] Read(BinaryReader reader, string[] list)
|
||||
{
|
||||
var count = reader.ReadInt32();
|
||||
if (count != 0)
|
||||
{
|
||||
list = new string[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
list[i] = reader.ReadString();
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
internal static T Read<T>(BinaryReader reader, T e) where T : IBindingsCache
|
||||
{
|
||||
var typename = reader.ReadString();
|
||||
if (string.IsNullOrEmpty(typename))
|
||||
return e;
|
||||
if (!_typeCache.TryGetValue(typename, out var type))
|
||||
{
|
||||
type = Builder.BuildTypes.FirstOrDefault(x => x.FullName == typename);
|
||||
if (type == null)
|
||||
{
|
||||
var msg = $"Missing type {typename}.";
|
||||
Log.Error(msg);
|
||||
throw new Exception(msg);
|
||||
}
|
||||
_typeCache.Add(typename, type);
|
||||
}
|
||||
e = (T)Activator.CreateInstance(type);
|
||||
e.Read(reader);
|
||||
return e;
|
||||
}
|
||||
|
||||
internal static List<T> Read<T>(BinaryReader reader, List<T> list) where T : IBindingsCache
|
||||
{
|
||||
var count = reader.ReadInt32();
|
||||
if (count != 0)
|
||||
{
|
||||
list = new List<T>(count);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var typename = reader.ReadString();
|
||||
if (!_typeCache.TryGetValue(typename, out var type))
|
||||
{
|
||||
type = Builder.BuildTypes.FirstOrDefault(x => x.FullName == typename);
|
||||
if (type == null)
|
||||
{
|
||||
var msg = $"Missing type {typename}.";
|
||||
Log.Error(msg);
|
||||
throw new Exception(msg);
|
||||
}
|
||||
_typeCache.Add(typename, type);
|
||||
}
|
||||
var e = (T)Activator.CreateInstance(type);
|
||||
e.Read(reader);
|
||||
list.Add(e);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private static string GetCachePath(Module module, BuildOptions moduleOptions)
|
||||
{
|
||||
return Path.Combine(moduleOptions.IntermediateFolder, module.Name + ".Bindings.cache");
|
||||
}
|
||||
|
||||
private static void SaveCache(ModuleInfo moduleInfo, BuildOptions moduleOptions, List<string> headerFiles)
|
||||
{
|
||||
var path = GetCachePath(moduleInfo.Module, moduleOptions);
|
||||
using (var stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read))
|
||||
using (var writer = new BinaryWriter(stream, Encoding.UTF8))
|
||||
{
|
||||
// Version
|
||||
writer.Write(4);
|
||||
writer.Write(File.GetLastWriteTime(Assembly.GetExecutingAssembly().Location).Ticks);
|
||||
|
||||
// Build options
|
||||
writer.Write(moduleOptions.IntermediateFolder);
|
||||
writer.Write((int)moduleOptions.Platform.Target);
|
||||
writer.Write((int)moduleOptions.Architecture);
|
||||
writer.Write((int)moduleOptions.Configuration);
|
||||
Write(writer, moduleOptions.PublicDefinitions);
|
||||
Write(writer, moduleOptions.PrivateDefinitions);
|
||||
Write(writer, moduleOptions.CompileEnv.PreprocessorDefinitions);
|
||||
|
||||
// Header files
|
||||
writer.Write(headerFiles.Count);
|
||||
for (int i = 0; i < headerFiles.Count; i++)
|
||||
{
|
||||
var headerFile = headerFiles[i];
|
||||
writer.Write(headerFile);
|
||||
writer.Write(File.GetLastWriteTime(headerFile).Ticks);
|
||||
}
|
||||
|
||||
// Info
|
||||
moduleInfo.Write(writer);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool LoadCache(ref ModuleInfo moduleInfo, BuildOptions moduleOptions, List<string> headerFiles)
|
||||
{
|
||||
var path = GetCachePath(moduleInfo.Module, moduleOptions);
|
||||
if (!File.Exists(path))
|
||||
return false;
|
||||
using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
using (var reader = new BinaryReader(stream, Encoding.UTF8))
|
||||
{
|
||||
// Version
|
||||
var version = reader.ReadInt32();
|
||||
if (version != 4)
|
||||
return false;
|
||||
if (File.GetLastWriteTime(Assembly.GetExecutingAssembly().Location).Ticks != reader.ReadInt64())
|
||||
return false;
|
||||
|
||||
// Build options
|
||||
if (reader.ReadString() != moduleOptions.IntermediateFolder ||
|
||||
reader.ReadInt32() != (int)moduleOptions.Platform.Target ||
|
||||
reader.ReadInt32() != (int)moduleOptions.Architecture ||
|
||||
reader.ReadInt32() != (int)moduleOptions.Configuration)
|
||||
return false;
|
||||
var publicDefinitions = Read(reader, Utilities.GetEmptyArray<string>());
|
||||
if (publicDefinitions.Length != moduleOptions.PublicDefinitions.Count || publicDefinitions.Any(x => !moduleOptions.PublicDefinitions.Contains(x)))
|
||||
return false;
|
||||
var privateDefinitions = Read(reader, Utilities.GetEmptyArray<string>());
|
||||
if (privateDefinitions.Length != moduleOptions.PrivateDefinitions.Count || privateDefinitions.Any(x => !moduleOptions.PrivateDefinitions.Contains(x)))
|
||||
return false;
|
||||
var preprocessorDefinitions = Read(reader, Utilities.GetEmptyArray<string>());
|
||||
if (preprocessorDefinitions.Length != moduleOptions.CompileEnv.PreprocessorDefinitions.Count || preprocessorDefinitions.Any(x => !moduleOptions.CompileEnv.PreprocessorDefinitions.Contains(x)))
|
||||
return false;
|
||||
|
||||
// Header files
|
||||
var headerFilesCount = reader.ReadInt32();
|
||||
if (headerFilesCount != headerFiles.Count)
|
||||
return false;
|
||||
for (int i = 0; i < headerFilesCount; i++)
|
||||
{
|
||||
var headerFile = headerFiles[i];
|
||||
if (headerFile != reader.ReadString())
|
||||
return false;
|
||||
if (File.GetLastWriteTime(headerFile).Ticks > reader.ReadInt64())
|
||||
return false;
|
||||
}
|
||||
|
||||
// Info
|
||||
var newModuleInfo = new ModuleInfo
|
||||
{
|
||||
Module = moduleInfo.Module,
|
||||
Name = moduleInfo.Name,
|
||||
Namespace = moduleInfo.Namespace,
|
||||
IsFromCache = true,
|
||||
};
|
||||
try
|
||||
{
|
||||
newModuleInfo.Read(reader);
|
||||
|
||||
// Skip parsing and use data loaded from cache
|
||||
moduleInfo = newModuleInfo;
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Skip loading cache
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -100,7 +100,7 @@ namespace Flax.Build.Bindings
|
||||
return value;
|
||||
if (typeInfo.Type == "String")
|
||||
return $"Variant(StringView({value}))";
|
||||
if (typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "ScriptingObjectReference")
|
||||
if (typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "SoftObjectReference")
|
||||
return $"Variant({value}.Get())";
|
||||
if (typeInfo.IsArray)
|
||||
{
|
||||
@@ -149,7 +149,7 @@ namespace Flax.Build.Bindings
|
||||
return $"((StringView){value}).GetText()";
|
||||
if (typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference")
|
||||
return $"ScriptingObject::Cast<{typeInfo.GenericArgs[0].Type}>((Asset*){value})";
|
||||
if (typeInfo.Type == "ScriptingObjectReference")
|
||||
if (typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "SoftObjectReference")
|
||||
return $"ScriptingObject::Cast<{typeInfo.GenericArgs[0].Type}>((ScriptingObject*){value})";
|
||||
if (typeInfo.IsArray)
|
||||
throw new Exception($"Not supported type to convert from the Variant to fixed-size array '{typeInfo}[{typeInfo.ArraySize}]'.");
|
||||
@@ -340,8 +340,8 @@ namespace Flax.Build.Bindings
|
||||
}
|
||||
}
|
||||
|
||||
// ScriptingObjectReference or AssetReference or WeakAssetReference
|
||||
if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference") && typeInfo.GenericArgs != null)
|
||||
// ScriptingObjectReference or AssetReference or WeakAssetReference or SoftObjectReference
|
||||
if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null)
|
||||
{
|
||||
type = "MonoObject*";
|
||||
return "{0}.GetManagedInstance()";
|
||||
@@ -448,8 +448,8 @@ namespace Flax.Build.Bindings
|
||||
type = "MonoReflectionType*";
|
||||
return "MUtils::UnboxVariantType({0})";
|
||||
default:
|
||||
// ScriptingObjectReference or AssetReference or WeakAssetReference
|
||||
if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference") && typeInfo.GenericArgs != null)
|
||||
// ScriptingObjectReference or AssetReference or WeakAssetReference or SoftObjectReference
|
||||
if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null)
|
||||
{
|
||||
// For non-pod types converting only, other API converts managed to unmanaged object in C# wrapper code)
|
||||
if (CppNonPodTypesConvertingGeneration)
|
||||
@@ -832,7 +832,7 @@ namespace Flax.Build.Bindings
|
||||
|
||||
contents.AppendLine(" ScriptingTypeHandle managedTypeHandle = object->GetTypeHandle();");
|
||||
contents.AppendLine(" const ScriptingType* managedTypePtr = &managedTypeHandle.GetType();");
|
||||
contents.AppendLine(" while (managedTypePtr->Class.Spawn != &ManagedBinaryModule::ManagedObjectSpawn)");
|
||||
contents.AppendLine(" while (managedTypePtr->Script.Spawn != &ManagedBinaryModule::ManagedObjectSpawn)");
|
||||
contents.AppendLine(" {");
|
||||
contents.AppendLine(" managedTypeHandle = managedTypePtr->GetBaseType();");
|
||||
contents.AppendLine(" managedTypePtr = &managedTypeHandle.GetType();");
|
||||
@@ -841,7 +841,7 @@ namespace Flax.Build.Bindings
|
||||
contents.AppendLine(" if (IsDuringWrapperCall)");
|
||||
contents.AppendLine(" {");
|
||||
contents.AppendLine(" // Prevent stack overflow by calling native base method");
|
||||
contents.AppendLine(" const auto scriptVTableBase = managedTypePtr->Class.ScriptVTableBase;");
|
||||
contents.AppendLine(" const auto scriptVTableBase = managedTypePtr->Script.ScriptVTableBase;");
|
||||
contents.Append($" return (object->**({functionInfo.UniqueName}_Signature*)&scriptVTableBase[{scriptVTableIndex} + 2])(");
|
||||
separator = false;
|
||||
for (var i = 0; i < functionInfo.Parameters.Count; i++)
|
||||
@@ -854,7 +854,7 @@ namespace Flax.Build.Bindings
|
||||
}
|
||||
contents.AppendLine(");");
|
||||
contents.AppendLine(" }");
|
||||
contents.AppendLine(" auto scriptVTable = (MMethod**)managedTypePtr->Class.ScriptVTable;");
|
||||
contents.AppendLine(" auto scriptVTable = (MMethod**)managedTypePtr->Script.ScriptVTable;");
|
||||
contents.AppendLine($" ASSERT(scriptVTable && scriptVTable[{scriptVTableIndex}]);");
|
||||
contents.AppendLine($" auto method = scriptVTable[{scriptVTableIndex}];");
|
||||
contents.AppendLine(" MonoObject* exception = nullptr;");
|
||||
@@ -961,7 +961,7 @@ namespace Flax.Build.Bindings
|
||||
var classInfo = typeInfo as ClassInfo;
|
||||
var structureInfo = typeInfo as StructureInfo;
|
||||
var baseType = classInfo?.BaseType ?? structureInfo?.BaseType;
|
||||
if (baseType != null && baseType.Type == "ISerializable")
|
||||
if (classInfo != null && classInfo.IsBaseTypeHidden)
|
||||
baseType = null;
|
||||
CppAutoSerializeFields.Clear();
|
||||
CppAutoSerializeProperties.Clear();
|
||||
@@ -1050,6 +1050,25 @@ namespace Flax.Build.Bindings
|
||||
contents.Append('}').AppendLine();
|
||||
}
|
||||
|
||||
private static string GenerateCppInterfaceInheritanceTable(BuildData buildData, StringBuilder contents, ModuleInfo moduleInfo, ClassStructInfo typeInfo, string typeNameNative)
|
||||
{
|
||||
var interfacesPtr = "nullptr";
|
||||
var interfaces = typeInfo.Interfaces;
|
||||
if (interfaces != null)
|
||||
{
|
||||
interfacesPtr = typeNameNative + "_Interfaces";
|
||||
contents.Append("static const ScriptingType::InterfaceImplementation ").Append(interfacesPtr).AppendLine("[] = {");
|
||||
for (int i = 0; i < interfaces.Count; i++)
|
||||
{
|
||||
var interfaceInfo = interfaces[i];
|
||||
contents.Append(" { &").Append(interfaceInfo.NativeName).Append("::TypeInitializer, (int16)VTABLE_OFFSET(").Append(typeInfo.NativeName).Append(", ").Append(interfaceInfo.NativeName).AppendLine(") },");
|
||||
}
|
||||
contents.AppendLine(" { nullptr, 0 },");
|
||||
contents.AppendLine("};");
|
||||
}
|
||||
return interfacesPtr;
|
||||
}
|
||||
|
||||
private static void GenerateCppClass(BuildData buildData, StringBuilder contents, ModuleInfo moduleInfo, ClassInfo classInfo)
|
||||
{
|
||||
var classTypeNameNative = classInfo.FullNameNative;
|
||||
@@ -1058,6 +1077,7 @@ namespace Flax.Build.Bindings
|
||||
var classTypeNameInternal = classInfo.NativeName;
|
||||
if (classInfo.Parent != null && !(classInfo.Parent is FileInfo))
|
||||
classTypeNameInternal = classInfo.Parent.FullNameNative + '_' + classTypeNameInternal;
|
||||
var useScripting = classInfo.IsStatic || classInfo.IsScriptingObject;
|
||||
|
||||
if (classInfo.IsAutoSerialization)
|
||||
GenerateCppAutoSerialization(buildData, contents, moduleInfo, classInfo, classTypeNameNative);
|
||||
@@ -1070,32 +1090,34 @@ namespace Flax.Build.Bindings
|
||||
// Events
|
||||
foreach (var eventInfo in classInfo.Events)
|
||||
{
|
||||
CppIncludeFiles.Add("Engine/Scripting/ManagedCLR/MEvent.h");
|
||||
if (!useScripting)
|
||||
continue;
|
||||
var paramsCount = eventInfo.Type.GenericArgs?.Count ?? 0;
|
||||
|
||||
// C# event invoking wrapper (calls C# event from C++ delegate)
|
||||
CppIncludeFiles.Add("Engine/Scripting/ManagedCLR/MEvent.h");
|
||||
contents.Append(" ");
|
||||
if (eventInfo.IsStatic)
|
||||
contents.Append("static ");
|
||||
contents.AppendFormat("void {0}_ManagedWrapper(", eventInfo.Name);
|
||||
for (var i = 0; i < eventInfo.Type.GenericArgs.Count; i++)
|
||||
for (var i = 0; i < paramsCount; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
contents.Append(", ");
|
||||
contents.Append(eventInfo.Type.GenericArgs[i]);
|
||||
contents.Append(" arg" + i);
|
||||
contents.Append(eventInfo.Type.GenericArgs[i]).Append(" arg" + i);
|
||||
}
|
||||
contents.Append(')').AppendLine();
|
||||
contents.Append(" {").AppendLine();
|
||||
contents.Append(" static MMethod* mmethod = nullptr;").AppendLine();
|
||||
contents.Append(" if (!mmethod)").AppendLine();
|
||||
contents.AppendFormat(" mmethod = {1}::GetStaticClass()->GetMethod(\"Internal_{0}_Invoke\", {2});", eventInfo.Name, classTypeNameNative, eventInfo.Type.GenericArgs.Count).AppendLine();
|
||||
contents.AppendFormat(" mmethod = {1}::GetStaticClass()->GetMethod(\"Internal_{0}_Invoke\", {2});", eventInfo.Name, classTypeNameNative, paramsCount).AppendLine();
|
||||
contents.Append(" CHECK(mmethod);").AppendLine();
|
||||
contents.Append(" MonoObject* exception = nullptr;").AppendLine();
|
||||
if (eventInfo.Type.GenericArgs.Count == 0)
|
||||
if (paramsCount == 0)
|
||||
contents.AppendLine(" void** params = nullptr;");
|
||||
else
|
||||
contents.AppendLine($" void* params[{eventInfo.Type.GenericArgs.Count}];");
|
||||
for (var i = 0; i < eventInfo.Type.GenericArgs.Count; i++)
|
||||
contents.AppendLine($" void* params[{paramsCount}];");
|
||||
for (var i = 0; i < paramsCount; i++)
|
||||
{
|
||||
var paramType = eventInfo.Type.GenericArgs[i];
|
||||
var paramName = "arg" + i;
|
||||
@@ -1118,7 +1140,7 @@ namespace Flax.Build.Bindings
|
||||
contents.Append("bool bind)").AppendLine();
|
||||
contents.Append(" {").AppendLine();
|
||||
contents.Append(" Function<void(");
|
||||
for (var i = 0; i < eventInfo.Type.GenericArgs.Count; i++)
|
||||
for (var i = 0; i < paramsCount; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
contents.Append(", ");
|
||||
@@ -1135,11 +1157,63 @@ namespace Flax.Build.Bindings
|
||||
contents.Append(" else").AppendLine();
|
||||
contents.AppendFormat(" {0}{1}.Unbind(f);", bindPrefix, eventInfo.Name).AppendLine();
|
||||
contents.Append(" }").AppendLine().AppendLine();
|
||||
|
||||
// Generic scripting event invoking wrapper (calls scripting code from C++ delegate)
|
||||
CppIncludeFiles.Add("Engine/Scripting/Events.h");
|
||||
contents.Append(" ");
|
||||
if (eventInfo.IsStatic)
|
||||
contents.Append("static ");
|
||||
contents.AppendFormat("void {0}_Wrapper(", eventInfo.Name);
|
||||
for (var i = 0; i < paramsCount; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
contents.Append(", ");
|
||||
contents.Append(eventInfo.Type.GenericArgs[i]).Append(" arg" + i);
|
||||
}
|
||||
contents.Append(')').AppendLine();
|
||||
contents.Append(" {").AppendLine();
|
||||
if (paramsCount == 0)
|
||||
contents.AppendLine(" Variant* params = nullptr;");
|
||||
else
|
||||
contents.AppendLine($" Variant params[{paramsCount}];");
|
||||
for (var i = 0; i < paramsCount; i++)
|
||||
{
|
||||
var paramType = eventInfo.Type.GenericArgs[i];
|
||||
var paramName = "arg" + i;
|
||||
var paramValue = GenerateCppWrapperNativeToVariant(buildData, paramType, classInfo, paramName);
|
||||
contents.Append($" params[{i}] = {paramValue};").AppendLine();
|
||||
}
|
||||
contents.AppendLine($" ScriptingEvents::Event({(eventInfo.IsStatic ? "nullptr" : "(ScriptingObject*)this")}, Span<Variant>(params, {paramsCount}), {classTypeNameNative}::TypeInitializer, StringView(TEXT(\"{eventInfo.Name}\"), {eventInfo.Name.Length}));");
|
||||
contents.Append(" }").AppendLine().AppendLine();
|
||||
|
||||
// Scripting event wrapper binding method (binds/unbinds generic wrapper to C++ delegate)
|
||||
contents.AppendFormat(" static void {0}_Bind(", eventInfo.Name);
|
||||
contents.AppendFormat("{0}* obj, void* instance, bool bind)", classTypeNameNative).AppendLine();
|
||||
contents.Append(" {").AppendLine();
|
||||
contents.Append(" Function<void(");
|
||||
for (var i = 0; i < paramsCount; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
contents.Append(", ");
|
||||
contents.Append(eventInfo.Type.GenericArgs[i]);
|
||||
}
|
||||
contents.Append(")> f;").AppendLine();
|
||||
if (eventInfo.IsStatic)
|
||||
contents.AppendFormat(" f.Bind<{0}_Wrapper>();", eventInfo.Name).AppendLine();
|
||||
else
|
||||
contents.AppendFormat(" f.Bind<{1}Internal, &{1}Internal::{0}_Wrapper>(({1}Internal*)instance);", eventInfo.Name, classTypeNameInternal).AppendLine();
|
||||
contents.Append(" if (bind)").AppendLine();
|
||||
contents.AppendFormat(" {0}{1}.Bind(f);", bindPrefix, eventInfo.Name).AppendLine();
|
||||
contents.Append(" else").AppendLine();
|
||||
contents.AppendFormat(" {0}{1}.Unbind(f);", bindPrefix, eventInfo.Name).AppendLine();
|
||||
contents.Append(" }").AppendLine().AppendLine();
|
||||
}
|
||||
|
||||
// Fields
|
||||
foreach (var fieldInfo in classInfo.Fields)
|
||||
{
|
||||
if (!useScripting)
|
||||
continue;
|
||||
if (fieldInfo.Getter != null)
|
||||
GenerateCppWrapperFunction(buildData, contents, classInfo, fieldInfo.Getter, "{0}");
|
||||
if (fieldInfo.Setter != null)
|
||||
@@ -1149,6 +1223,8 @@ namespace Flax.Build.Bindings
|
||||
// Properties
|
||||
foreach (var propertyInfo in classInfo.Properties)
|
||||
{
|
||||
if (!useScripting)
|
||||
continue;
|
||||
if (propertyInfo.Getter != null)
|
||||
GenerateCppWrapperFunction(buildData, contents, classInfo, propertyInfo.Getter);
|
||||
if (propertyInfo.Setter != null)
|
||||
@@ -1158,6 +1234,8 @@ namespace Flax.Build.Bindings
|
||||
// Functions
|
||||
foreach (var functionInfo in classInfo.Functions)
|
||||
{
|
||||
if (!useScripting)
|
||||
throw new Exception($"Not supported function {functionInfo.Name} inside non-static and non-scripting class type {classInfo.Name}.");
|
||||
GenerateCppWrapperFunction(buildData, contents, classInfo, functionInfo);
|
||||
}
|
||||
|
||||
@@ -1267,50 +1345,88 @@ namespace Flax.Build.Bindings
|
||||
// Runtime initialization (internal methods binding)
|
||||
contents.AppendLine(" static void InitRuntime()");
|
||||
contents.AppendLine(" {");
|
||||
foreach (var eventInfo in classInfo.Events)
|
||||
if (useScripting)
|
||||
{
|
||||
contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{eventInfo.Name}_Bind\", &{eventInfo.Name}_ManagedBind);");
|
||||
}
|
||||
foreach (var fieldInfo in classInfo.Fields)
|
||||
{
|
||||
if (fieldInfo.Getter != null)
|
||||
contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{fieldInfo.Getter.UniqueName}\", &{fieldInfo.Getter.UniqueName});");
|
||||
if (fieldInfo.Setter != null)
|
||||
contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{fieldInfo.Setter.UniqueName}\", &{fieldInfo.Setter.UniqueName});");
|
||||
}
|
||||
foreach (var propertyInfo in classInfo.Properties)
|
||||
{
|
||||
if (propertyInfo.Getter != null)
|
||||
contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{propertyInfo.Getter.UniqueName}\", &{propertyInfo.Getter.UniqueName});");
|
||||
if (propertyInfo.Setter != null)
|
||||
contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{propertyInfo.Setter.UniqueName}\", &{propertyInfo.Setter.UniqueName});");
|
||||
}
|
||||
foreach (var functionInfo in classInfo.Functions)
|
||||
{
|
||||
contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{functionInfo.UniqueName}\", &{functionInfo.UniqueName});");
|
||||
foreach (var eventInfo in classInfo.Events)
|
||||
{
|
||||
contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{eventInfo.Name}_Bind\", &{eventInfo.Name}_ManagedBind);");
|
||||
|
||||
// Register scripting event binder
|
||||
contents.AppendLine($" ScriptingEvents::EventsTable[Pair<ScriptingTypeHandle, StringView>({classTypeNameNative}::TypeInitializer, StringView(TEXT(\"{eventInfo.Name}\"), {eventInfo.Name.Length}))] = (void(*)(ScriptingObject*, void*, bool)){classTypeNameInternal}Internal::{eventInfo.Name}_Bind;");
|
||||
}
|
||||
foreach (var fieldInfo in classInfo.Fields)
|
||||
{
|
||||
if (fieldInfo.Getter != null)
|
||||
contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{fieldInfo.Getter.UniqueName}\", &{fieldInfo.Getter.UniqueName});");
|
||||
if (fieldInfo.Setter != null)
|
||||
contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{fieldInfo.Setter.UniqueName}\", &{fieldInfo.Setter.UniqueName});");
|
||||
}
|
||||
foreach (var propertyInfo in classInfo.Properties)
|
||||
{
|
||||
if (propertyInfo.Getter != null)
|
||||
contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{propertyInfo.Getter.UniqueName}\", &{propertyInfo.Getter.UniqueName});");
|
||||
if (propertyInfo.Setter != null)
|
||||
contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{propertyInfo.Setter.UniqueName}\", &{propertyInfo.Setter.UniqueName});");
|
||||
}
|
||||
foreach (var functionInfo in classInfo.Functions)
|
||||
{
|
||||
contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{functionInfo.UniqueName}\", &{functionInfo.UniqueName});");
|
||||
}
|
||||
}
|
||||
GenerateCppClassInitRuntime?.Invoke(buildData, classInfo, contents);
|
||||
|
||||
contents.AppendLine(" }");
|
||||
contents.Append('}');
|
||||
contents.Append(';');
|
||||
contents.AppendLine();
|
||||
contents.AppendLine(" }").AppendLine();
|
||||
|
||||
if (!useScripting && !classInfo.IsAbstract)
|
||||
{
|
||||
// Constructor
|
||||
contents.AppendLine(" static void Ctor(void* ptr)");
|
||||
contents.AppendLine(" {");
|
||||
contents.AppendLine($" new(ptr){classTypeNameNative}();");
|
||||
contents.AppendLine(" }").AppendLine();
|
||||
|
||||
// Destructor
|
||||
contents.AppendLine(" static void Dtor(void* ptr)");
|
||||
contents.AppendLine(" {");
|
||||
contents.AppendLine($" (({classTypeNameNative}*)ptr)->~{classInfo.NativeName}();");
|
||||
contents.AppendLine(" }").AppendLine();
|
||||
}
|
||||
|
||||
contents.Append('}').Append(';').AppendLine();
|
||||
contents.AppendLine();
|
||||
|
||||
// Interfaces
|
||||
var interfacesTable = GenerateCppInterfaceInheritanceTable(buildData, contents, moduleInfo, classInfo, classTypeNameNative);
|
||||
|
||||
// Type initializer
|
||||
contents.Append($"ScriptingTypeInitializer {classTypeNameNative}::TypeInitializer((BinaryModule*)GetBinaryModule{moduleInfo.Name}(), ");
|
||||
contents.Append($"StringAnsiView(\"{classTypeNameManaged}\", {classTypeNameManaged.Length}), ");
|
||||
contents.Append($"sizeof({classTypeNameNative}), ");
|
||||
contents.Append($"&{classTypeNameInternal}Internal::InitRuntime, ");
|
||||
if (!classInfo.IsStatic && !classInfo.NoSpawn)
|
||||
contents.Append($"(ScriptingType::SpawnHandler)&{classTypeNameNative}::Spawn, ");
|
||||
if (useScripting)
|
||||
{
|
||||
if (classInfo.IsStatic || classInfo.NoSpawn)
|
||||
contents.Append("&ScriptingType::DefaultSpawn, ");
|
||||
else
|
||||
contents.Append($"(ScriptingType::SpawnHandler)&{classTypeNameNative}::Spawn, ");
|
||||
if (classInfo.BaseType != null && useScripting)
|
||||
contents.Append($"&{classInfo.BaseType}::TypeInitializer, ");
|
||||
else
|
||||
contents.Append("nullptr, ");
|
||||
contents.Append(setupScriptVTable);
|
||||
}
|
||||
else
|
||||
contents.Append("&ScriptingType::DefaultSpawn, ");
|
||||
if (classInfo.BaseType != null)
|
||||
contents.Append($"&{classInfo.BaseType}::TypeInitializer, ");
|
||||
else
|
||||
contents.Append("nullptr, ");
|
||||
contents.Append(setupScriptVTable);
|
||||
{
|
||||
if (classInfo.IsAbstract)
|
||||
contents.Append("nullptr, nullptr, ");
|
||||
else
|
||||
contents.Append($"&{classTypeNameInternal}Internal::Ctor, &{classTypeNameInternal}Internal::Dtor, ");
|
||||
if (classInfo.BaseType != null)
|
||||
contents.Append($"&{classInfo.BaseType}::TypeInitializer");
|
||||
else
|
||||
contents.Append("nullptr");
|
||||
}
|
||||
contents.Append(", ").Append(interfacesTable);
|
||||
contents.Append(");");
|
||||
contents.AppendLine();
|
||||
|
||||
@@ -1505,10 +1621,46 @@ namespace Flax.Build.Bindings
|
||||
}
|
||||
}
|
||||
|
||||
private static bool GenerateCppType(BuildData buildData, StringBuilder contents, ModuleInfo moduleInfo, object type)
|
||||
private static void GenerateCppInterface(BuildData buildData, StringBuilder contents, ModuleInfo moduleInfo, InterfaceInfo interfaceInfo)
|
||||
{
|
||||
var interfaceTypeNameNative = interfaceInfo.FullNameNative;
|
||||
var interfaceTypeNameManaged = interfaceInfo.FullNameManaged;
|
||||
var interfaceTypeNameManagedInternalCall = interfaceTypeNameManaged.Replace('+', '/');
|
||||
var interfaceTypeNameInternal = interfaceInfo.NativeName;
|
||||
if (interfaceInfo.Parent != null && !(interfaceInfo.Parent is FileInfo))
|
||||
interfaceTypeNameInternal = interfaceInfo.Parent.FullNameNative + '_' + interfaceTypeNameInternal;
|
||||
|
||||
contents.AppendLine();
|
||||
contents.AppendFormat("class {0}Internal", interfaceTypeNameInternal).AppendLine();
|
||||
contents.Append('{').AppendLine();
|
||||
contents.AppendLine("public:");
|
||||
|
||||
// Runtime initialization (internal methods binding)
|
||||
contents.AppendLine(" static void InitRuntime()");
|
||||
contents.AppendLine(" {");
|
||||
contents.AppendLine(" }").AppendLine();
|
||||
|
||||
contents.Append('}').Append(';').AppendLine();
|
||||
contents.AppendLine();
|
||||
|
||||
// Type initializer
|
||||
contents.Append($"ScriptingTypeInitializer {interfaceTypeNameNative}::TypeInitializer((BinaryModule*)GetBinaryModule{moduleInfo.Name}(), ");
|
||||
contents.Append($"StringAnsiView(\"{interfaceTypeNameManaged}\", {interfaceTypeNameManaged.Length}), ");
|
||||
contents.Append($"&{interfaceTypeNameInternal}Internal::InitRuntime");
|
||||
contents.Append(");");
|
||||
contents.AppendLine();
|
||||
|
||||
// Nested types
|
||||
foreach (var apiTypeInfo in interfaceInfo.Children)
|
||||
{
|
||||
GenerateCppType(buildData, contents, moduleInfo, apiTypeInfo);
|
||||
}
|
||||
}
|
||||
|
||||
private static void GenerateCppType(BuildData buildData, StringBuilder contents, ModuleInfo moduleInfo, object type)
|
||||
{
|
||||
if (type is ApiTypeInfo apiTypeInfo && apiTypeInfo.IsInBuild)
|
||||
return false;
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
@@ -1516,18 +1668,16 @@ namespace Flax.Build.Bindings
|
||||
GenerateCppClass(buildData, contents, moduleInfo, classInfo);
|
||||
else if (type is StructureInfo structureInfo)
|
||||
GenerateCppStruct(buildData, contents, moduleInfo, structureInfo);
|
||||
else if (type is InterfaceInfo interfaceInfo)
|
||||
GenerateCppInterface(buildData, contents, moduleInfo, interfaceInfo);
|
||||
else if (type is InjectCppCodeInfo injectCppCodeInfo)
|
||||
contents.AppendLine(injectCppCodeInfo.Code);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
catch
|
||||
{
|
||||
Log.Error($"Failed to generate C++ bindings for {type}.");
|
||||
throw;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void GenerateCppCppUsedNonPodTypes(BuildData buildData, ApiTypeInfo apiType)
|
||||
@@ -1575,21 +1725,16 @@ namespace Flax.Build.Bindings
|
||||
CppReferencesFiles.Add(fileInfo);
|
||||
}
|
||||
}
|
||||
|
||||
var headerPos = contents.Length;
|
||||
|
||||
foreach (var child in moduleInfo.Children)
|
||||
{
|
||||
foreach (var apiTypeInfo in child.Children)
|
||||
{
|
||||
if (GenerateCppType(buildData, contents, moduleInfo, apiTypeInfo))
|
||||
bindings.UseBindings = true;
|
||||
GenerateCppType(buildData, contents, moduleInfo, apiTypeInfo);
|
||||
}
|
||||
}
|
||||
|
||||
if (!bindings.UseBindings)
|
||||
return;
|
||||
|
||||
GenerateCppModuleSource?.Invoke(buildData, moduleInfo, contents);
|
||||
|
||||
{
|
||||
@@ -1883,7 +2028,7 @@ namespace Flax.Build.Bindings
|
||||
var binaryModuleSourcePath = Path.Combine(project.ProjectFolderPath, "Source", binaryModuleName + ".Gen.cpp");
|
||||
contents.AppendLine("// This code was auto-generated. Do not modify it.");
|
||||
contents.AppendLine();
|
||||
contents.AppendLine($"#include \"Engine/Scripting/BinaryModule.h\"");
|
||||
contents.AppendLine("#include \"Engine/Scripting/BinaryModule.h\"");
|
||||
contents.AppendLine($"#include \"{binaryModuleName}.Gen.h\"");
|
||||
contents.AppendLine();
|
||||
contents.AppendLine($"StaticallyLinkedBinaryModuleInitializer StaticallyLinkedBinaryModule{binaryModuleName}(GetBinaryModule{binaryModuleName});");
|
||||
|
||||
@@ -17,6 +17,7 @@ namespace Flax.Build.Bindings
|
||||
public Stack<ApiTypeInfo> ScopeTypeStack;
|
||||
public Stack<AccessLevel> ScopeAccessStack;
|
||||
public Dictionary<string, string> PreprocessorDefines;
|
||||
public List<string> StringCache;
|
||||
|
||||
public ApiTypeInfo ValidScopeInfoFromStack
|
||||
{
|
||||
@@ -46,14 +47,12 @@ namespace Flax.Build.Bindings
|
||||
}
|
||||
}
|
||||
|
||||
private static List<string> _commentCache;
|
||||
|
||||
private static string[] ParseComment(ref ParsingContext context)
|
||||
{
|
||||
if (_commentCache == null)
|
||||
_commentCache = new List<string>();
|
||||
if (context.StringCache == null)
|
||||
context.StringCache = new List<string>();
|
||||
else
|
||||
_commentCache.Clear();
|
||||
context.StringCache.Clear();
|
||||
|
||||
int tokensCount = 0;
|
||||
bool isValid = true;
|
||||
@@ -77,7 +76,7 @@ namespace Flax.Build.Bindings
|
||||
if (commentLine.StartsWith("// "))
|
||||
commentLine = "/// " + commentLine.Substring(3);
|
||||
|
||||
_commentCache.Insert(0, commentLine);
|
||||
context.StringCache.Insert(0, commentLine);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -90,14 +89,14 @@ namespace Flax.Build.Bindings
|
||||
for (var i = 0; i < tokensCount; i++)
|
||||
context.Tokenizer.NextToken(true, true);
|
||||
|
||||
if (_commentCache.Count == 1)
|
||||
if (context.StringCache.Count == 1)
|
||||
{
|
||||
// Ensure to have summary begin/end pair
|
||||
_commentCache.Insert(0, "/// <summary>");
|
||||
_commentCache.Add("/// </summary>");
|
||||
context.StringCache.Insert(0, "/// <summary>");
|
||||
context.StringCache.Add("/// </summary>");
|
||||
}
|
||||
|
||||
return _commentCache.ToArray();
|
||||
return context.StringCache.ToArray();
|
||||
}
|
||||
|
||||
private struct TagParameter
|
||||
@@ -383,17 +382,101 @@ namespace Flax.Build.Bindings
|
||||
return name;
|
||||
}
|
||||
|
||||
private static void ParseInheritance(ref ParsingContext context, ClassStructInfo desc, out bool isFinal)
|
||||
{
|
||||
desc.BaseType = null;
|
||||
desc.BaseTypeInheritance = AccessLevel.Private;
|
||||
|
||||
var token = context.Tokenizer.NextToken();
|
||||
isFinal = token.Value == "final";
|
||||
if (isFinal)
|
||||
token = context.Tokenizer.NextToken();
|
||||
|
||||
if (token.Type == TokenType.Colon)
|
||||
{
|
||||
while (token.Type != TokenType.LeftCurlyBrace)
|
||||
{
|
||||
var accessToken = context.Tokenizer.ExpectToken(TokenType.Identifier);
|
||||
switch (accessToken.Value)
|
||||
{
|
||||
case "public":
|
||||
desc.BaseTypeInheritance = AccessLevel.Public;
|
||||
token = context.Tokenizer.ExpectToken(TokenType.Identifier);
|
||||
break;
|
||||
case "protected":
|
||||
desc.BaseTypeInheritance = AccessLevel.Protected;
|
||||
token = context.Tokenizer.ExpectToken(TokenType.Identifier);
|
||||
break;
|
||||
case "private":
|
||||
token = context.Tokenizer.ExpectToken(TokenType.Identifier);
|
||||
break;
|
||||
default:
|
||||
token = accessToken;
|
||||
break;
|
||||
}
|
||||
|
||||
var baseTypeInfo = new TypeInfo
|
||||
{
|
||||
Type = token.Value,
|
||||
};
|
||||
if (token.Value.Length > 2 && token.Value[0] == 'I' && char.IsUpper(token.Value[1]))
|
||||
{
|
||||
// Interface
|
||||
if (desc.InterfaceNames == null)
|
||||
desc.InterfaceNames = new List<TypeInfo>();
|
||||
desc.InterfaceNames.Add(baseTypeInfo);
|
||||
token = context.Tokenizer.NextToken();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (desc.BaseType != null)
|
||||
{
|
||||
// Allow for multiple base classes, just the first one needs to be a valid base type
|
||||
break;
|
||||
throw new Exception($"Invalid '{desc.Name}' inheritance (only single base class is allowed for scripting types, excluding interfaces).");
|
||||
}
|
||||
desc.BaseType = baseTypeInfo;
|
||||
token = context.Tokenizer.NextToken();
|
||||
if (token.Type == TokenType.LeftCurlyBrace)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (token.Type == TokenType.LeftAngleBracket)
|
||||
{
|
||||
var genericType = context.Tokenizer.ExpectToken(TokenType.Identifier);
|
||||
token = context.Tokenizer.ExpectToken(TokenType.RightAngleBracket);
|
||||
desc.BaseType.GenericArgs = new List<TypeInfo>
|
||||
{
|
||||
new TypeInfo
|
||||
{
|
||||
Type = genericType.Value,
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: find better way to resolve this (custom base type attribute?)
|
||||
if (desc.BaseType.Type == "ShaderAssetTypeBase")
|
||||
{
|
||||
desc.BaseType = desc.BaseType.GenericArgs[0];
|
||||
}
|
||||
|
||||
token = context.Tokenizer.NextToken();
|
||||
}
|
||||
}
|
||||
token = context.Tokenizer.PreviousToken();
|
||||
}
|
||||
else
|
||||
{
|
||||
// No base type
|
||||
token = context.Tokenizer.PreviousToken();
|
||||
}
|
||||
}
|
||||
|
||||
private static ClassInfo ParseClass(ref ParsingContext context)
|
||||
{
|
||||
var desc = new ClassInfo
|
||||
{
|
||||
Children = new List<ApiTypeInfo>(),
|
||||
Access = context.CurrentAccessLevel,
|
||||
BaseTypeInheritance = AccessLevel.Private,
|
||||
Functions = new List<FunctionInfo>(),
|
||||
Properties = new List<PropertyInfo>(),
|
||||
Fields = new List<FieldInfo>(),
|
||||
Events = new List<EventInfo>(),
|
||||
};
|
||||
|
||||
// Read the documentation comment
|
||||
@@ -410,64 +493,8 @@ namespace Flax.Build.Bindings
|
||||
// Read name
|
||||
desc.Name = desc.NativeName = ParseName(ref context);
|
||||
|
||||
// Read class inheritance
|
||||
token = context.Tokenizer.NextToken();
|
||||
var isFinal = token.Value == "final";
|
||||
if (isFinal)
|
||||
token = context.Tokenizer.NextToken();
|
||||
if (token.Type == TokenType.Colon)
|
||||
{
|
||||
// Current class does have inheritance defined
|
||||
var accessToken = context.Tokenizer.ExpectToken(TokenType.Identifier);
|
||||
switch (accessToken.Value)
|
||||
{
|
||||
case "public":
|
||||
desc.BaseTypeInheritance = AccessLevel.Public;
|
||||
token = context.Tokenizer.ExpectToken(TokenType.Identifier);
|
||||
break;
|
||||
case "protected":
|
||||
desc.BaseTypeInheritance = AccessLevel.Protected;
|
||||
token = context.Tokenizer.ExpectToken(TokenType.Identifier);
|
||||
break;
|
||||
case "private":
|
||||
token = context.Tokenizer.ExpectToken(TokenType.Identifier);
|
||||
break;
|
||||
}
|
||||
|
||||
desc.BaseType = new TypeInfo
|
||||
{
|
||||
Type = token.Value,
|
||||
};
|
||||
token = context.Tokenizer.NextToken();
|
||||
if (token.Type == TokenType.LeftAngleBracket)
|
||||
{
|
||||
var genericType = context.Tokenizer.ExpectToken(TokenType.Identifier);
|
||||
context.Tokenizer.ExpectToken(TokenType.RightAngleBracket);
|
||||
desc.BaseType.GenericArgs = new List<TypeInfo>
|
||||
{
|
||||
new TypeInfo
|
||||
{
|
||||
Type = genericType.Value,
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: find better way to resolve this (custom base type attribute?)
|
||||
if (desc.BaseType.Type == "ShaderAssetTypeBase")
|
||||
{
|
||||
desc.BaseType = desc.BaseType.GenericArgs[0];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
token = context.Tokenizer.PreviousToken();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No base type
|
||||
token = context.Tokenizer.PreviousToken();
|
||||
desc.BaseType = null;
|
||||
}
|
||||
// Read inheritance
|
||||
ParseInheritance(ref context, desc, out var isFinal);
|
||||
|
||||
// Process tag parameters
|
||||
foreach (var tag in tagParams)
|
||||
@@ -523,12 +550,72 @@ namespace Flax.Build.Bindings
|
||||
return desc;
|
||||
}
|
||||
|
||||
private static InterfaceInfo ParseInterface(ref ParsingContext context)
|
||||
{
|
||||
var desc = new InterfaceInfo
|
||||
{
|
||||
Access = context.CurrentAccessLevel,
|
||||
};
|
||||
|
||||
// Read the documentation comment
|
||||
desc.Comment = ParseComment(ref context);
|
||||
|
||||
// Read parameters from the tag
|
||||
var tagParams = ParseTagParameters(ref context);
|
||||
|
||||
// Read 'class' keyword
|
||||
var token = context.Tokenizer.NextToken();
|
||||
if (token.Value != "class")
|
||||
throw new Exception($"Invalid API_INTERFACE usage (expected 'class' keyword but got '{token.Value} {context.Tokenizer.NextToken().Value}').");
|
||||
|
||||
// Read name
|
||||
desc.Name = desc.NativeName = ParseName(ref context);
|
||||
if (desc.Name.Length < 2 || desc.Name[0] != 'I' || !char.IsUpper(desc.Name[1]))
|
||||
throw new Exception($"Invalid API_INTERFACE name '{desc.Name}' (it must start with 'I' character followed by the uppercase character).");
|
||||
|
||||
// Read inheritance
|
||||
ParseInheritance(ref context, desc, out _);
|
||||
|
||||
// Process tag parameters
|
||||
foreach (var tag in tagParams)
|
||||
{
|
||||
switch (tag.Tag.ToLower())
|
||||
{
|
||||
case "public":
|
||||
desc.Access = AccessLevel.Public;
|
||||
break;
|
||||
case "protected":
|
||||
desc.Access = AccessLevel.Protected;
|
||||
break;
|
||||
case "private":
|
||||
desc.Access = AccessLevel.Private;
|
||||
break;
|
||||
case "inbuild":
|
||||
desc.IsInBuild = true;
|
||||
break;
|
||||
case "attributes":
|
||||
desc.Attributes = tag.Value;
|
||||
break;
|
||||
case "name":
|
||||
desc.Name = tag.Value;
|
||||
break;
|
||||
case "namespace":
|
||||
desc.Namespace = tag.Value;
|
||||
break;
|
||||
default:
|
||||
Log.Warning($"Unknown or not supported tag parameter {tag} used on interface {desc.Name} at line {context.Tokenizer.CurrentLine}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
private static FunctionInfo ParseFunction(ref ParsingContext context)
|
||||
{
|
||||
var desc = new FunctionInfo
|
||||
{
|
||||
Access = context.CurrentAccessLevel,
|
||||
Parameters = new List<FunctionInfo.ParameterInfo>(),
|
||||
};
|
||||
|
||||
// Read the documentation comment
|
||||
@@ -691,6 +778,35 @@ namespace Flax.Build.Bindings
|
||||
else
|
||||
propertyInfo.Setter = functionInfo;
|
||||
|
||||
if (propertyInfo.Getter != null && propertyInfo.Setter != null)
|
||||
{
|
||||
// Check if getter and setter types are matching (const and ref specifiers are skipped)
|
||||
var getterType = propertyInfo.Getter.ReturnType;
|
||||
var setterType = propertyInfo.Setter.Parameters[0].Type;
|
||||
if (!string.Equals(getterType.Type, setterType.Type) ||
|
||||
getterType.IsPtr != setterType.IsPtr ||
|
||||
getterType.IsArray != setterType.IsArray ||
|
||||
getterType.IsBitField != setterType.IsBitField ||
|
||||
getterType.ArraySize != setterType.ArraySize ||
|
||||
getterType.BitSize != setterType.BitSize ||
|
||||
!TypeInfo.Equals(getterType.GenericArgs, setterType.GenericArgs))
|
||||
{
|
||||
// Skip compatible types
|
||||
if (getterType.Type == "String" && setterType.Type == "StringView")
|
||||
return propertyInfo;
|
||||
if (getterType.Type == "Array" && setterType.Type == "Span" && getterType.GenericArgs?.Count == 1 && setterType.GenericArgs?.Count == 1 && getterType.GenericArgs[0].Equals(setterType.GenericArgs[0]))
|
||||
return propertyInfo;
|
||||
throw new Exception($"Property {propertyName} in class {classInfo.Name} (line {context.Tokenizer.CurrentLine}) has mismatching getter return type ({getterType}) and setter parameter type ({setterType}). Both getter and setter methods must use the same value type used for property.");
|
||||
}
|
||||
|
||||
// Fix documentation comment to reflect both getter and setters available
|
||||
for (var i = 0; i < propertyInfo.Comment.Length; i++)
|
||||
{
|
||||
ref var comment = ref propertyInfo.Comment[i];
|
||||
comment = comment.Replace("/// Gets ", "/// Gets or sets ");
|
||||
}
|
||||
}
|
||||
|
||||
return propertyInfo;
|
||||
}
|
||||
|
||||
@@ -698,9 +814,7 @@ namespace Flax.Build.Bindings
|
||||
{
|
||||
var desc = new EnumInfo
|
||||
{
|
||||
Children = new List<ApiTypeInfo>(),
|
||||
Access = context.CurrentAccessLevel,
|
||||
Entries = new List<EnumInfo.EntryInfo>(),
|
||||
};
|
||||
|
||||
// Read the documentation comment
|
||||
@@ -855,10 +969,7 @@ namespace Flax.Build.Bindings
|
||||
{
|
||||
var desc = new StructureInfo
|
||||
{
|
||||
Children = new List<ApiTypeInfo>(),
|
||||
Access = context.CurrentAccessLevel,
|
||||
Fields = new List<FieldInfo>(),
|
||||
Functions = new List<FunctionInfo>(),
|
||||
};
|
||||
|
||||
// Read the documentation comment
|
||||
@@ -875,55 +986,8 @@ namespace Flax.Build.Bindings
|
||||
// Read name
|
||||
desc.Name = desc.NativeName = ParseName(ref context);
|
||||
|
||||
// Read structure inheritance
|
||||
token = context.Tokenizer.NextToken();
|
||||
if (token.Type == TokenType.Colon)
|
||||
{
|
||||
// Current class does have inheritance defined
|
||||
var accessToken = context.Tokenizer.ExpectToken(TokenType.Identifier);
|
||||
switch (accessToken.Value)
|
||||
{
|
||||
case "public":
|
||||
token = context.Tokenizer.ExpectToken(TokenType.Identifier);
|
||||
break;
|
||||
case "protected":
|
||||
token = context.Tokenizer.ExpectToken(TokenType.Identifier);
|
||||
break;
|
||||
case "private":
|
||||
token = context.Tokenizer.ExpectToken(TokenType.Identifier);
|
||||
break;
|
||||
default:
|
||||
token = accessToken;
|
||||
break;
|
||||
}
|
||||
|
||||
desc.BaseType = new TypeInfo
|
||||
{
|
||||
Type = token.Value,
|
||||
};
|
||||
token = context.Tokenizer.NextToken();
|
||||
if (token.Type == TokenType.LeftAngleBracket)
|
||||
{
|
||||
var genericType = context.Tokenizer.ExpectToken(TokenType.Identifier);
|
||||
context.Tokenizer.ExpectToken(TokenType.RightAngleBracket);
|
||||
desc.BaseType.GenericArgs = new List<TypeInfo>
|
||||
{
|
||||
new TypeInfo
|
||||
{
|
||||
Type = genericType.Value,
|
||||
}
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
token = context.Tokenizer.PreviousToken();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No base type
|
||||
token = context.Tokenizer.PreviousToken();
|
||||
}
|
||||
// Read inheritance
|
||||
ParseInheritance(ref context, desc, out _);
|
||||
|
||||
// Process tag parameters
|
||||
foreach (var tag in tagParams)
|
||||
@@ -974,11 +1038,30 @@ namespace Flax.Build.Bindings
|
||||
|
||||
// Read parameters from the tag
|
||||
var tagParams = ParseTagParameters(ref context);
|
||||
context.Tokenizer.SkipUntil(TokenType.Identifier);
|
||||
|
||||
// Read 'static' keyword
|
||||
desc.IsStatic = context.Tokenizer.NextToken().Value == "static";
|
||||
if (!desc.IsStatic)
|
||||
context.Tokenizer.PreviousToken();
|
||||
// Read 'static' or 'mutable'
|
||||
Token token;
|
||||
var isMutable = false;
|
||||
while (true)
|
||||
{
|
||||
token = context.Tokenizer.CurrentToken;
|
||||
if (!desc.IsStatic && token.Value == "static")
|
||||
{
|
||||
desc.IsStatic = true;
|
||||
context.Tokenizer.NextToken();
|
||||
}
|
||||
else if (!isMutable && token.Value == "mutable")
|
||||
{
|
||||
isMutable = true;
|
||||
context.Tokenizer.NextToken();
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Tokenizer.PreviousToken();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Read type
|
||||
desc.Type = ParseType(ref context);
|
||||
@@ -987,11 +1070,10 @@ namespace Flax.Build.Bindings
|
||||
desc.Name = ParseName(ref context);
|
||||
|
||||
// Read ';' or default value or array size or bit-field size
|
||||
var token = context.Tokenizer.ExpectAnyTokens(new[] { TokenType.SemiColon, TokenType.Equal, TokenType.LeftBracket, TokenType.Colon });
|
||||
token = context.Tokenizer.ExpectAnyTokens(new[] { TokenType.SemiColon, TokenType.Equal, TokenType.LeftBracket, TokenType.Colon });
|
||||
if (token.Type == TokenType.Equal)
|
||||
{
|
||||
//context.Tokenizer.SkipUntil(TokenType.SemiColon, out var defaultValue);
|
||||
context.Tokenizer.SkipUntil(TokenType.SemiColon);
|
||||
context.Tokenizer.SkipUntil(TokenType.SemiColon, out desc.DefaultValue, false);
|
||||
}
|
||||
else if (token.Type == TokenType.LeftBracket)
|
||||
{
|
||||
@@ -1106,7 +1188,6 @@ namespace Flax.Build.Bindings
|
||||
context.Tokenizer.ExpectToken(TokenType.LeftParent);
|
||||
var desc = new InjectCppCodeInfo
|
||||
{
|
||||
Children = new List<ApiTypeInfo>(),
|
||||
Code = context.Tokenizer.ExpectToken(TokenType.String).Value.Replace("\\\"", "\""),
|
||||
};
|
||||
desc.Code = desc.Code.Substring(1, desc.Code.Length - 2);
|
||||
|
||||
@@ -4,6 +4,8 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Flax.Build.NativeCpp;
|
||||
using BuildData = Flax.Build.Builder.BuildData;
|
||||
|
||||
@@ -58,16 +60,12 @@ namespace Flax.Build.Bindings
|
||||
|
||||
private static ModuleInfo ParseModuleInner(BuildData buildData, Module module, BuildOptions moduleOptions = null)
|
||||
{
|
||||
if (buildData.ModulesInfo.TryGetValue(module, out var moduleInfo))
|
||||
return moduleInfo;
|
||||
|
||||
// Setup bindings module info descriptor
|
||||
moduleInfo = new ModuleInfo
|
||||
var moduleInfo = new ModuleInfo
|
||||
{
|
||||
Module = module,
|
||||
Name = module.BinaryModuleName,
|
||||
Namespace = string.Empty,
|
||||
Children = new List<ApiTypeInfo>(),
|
||||
};
|
||||
if (string.IsNullOrEmpty(moduleInfo.Name))
|
||||
throw new Exception("Module name cannot be empty.");
|
||||
@@ -81,320 +79,375 @@ namespace Flax.Build.Bindings
|
||||
throw new Exception($"Cannot parse module {module.Name} without options.");
|
||||
|
||||
// Collect all header files that can have public symbols for API
|
||||
var headerFiles = moduleOptions.SourceFiles.Where(x => x.EndsWith(".h")).ToList();
|
||||
|
||||
// Skip if no header files to process
|
||||
var headerFiles = new List<string>(moduleOptions.SourceFiles.Count / 2);
|
||||
for (int i = 0; i < moduleOptions.SourceFiles.Count; i++)
|
||||
{
|
||||
if (moduleOptions.SourceFiles[i].EndsWith(".h", StringComparison.OrdinalIgnoreCase))
|
||||
headerFiles.Add(moduleOptions.SourceFiles[i]);
|
||||
}
|
||||
if (headerFiles.Count == 0)
|
||||
return moduleInfo;
|
||||
|
||||
// Find and load files with API tags
|
||||
string[] headerFilesContents = null;
|
||||
//using (new ProfileEventScope("LoadHeaderFiles"))
|
||||
if (module.Name == "Core")
|
||||
{
|
||||
var anyApi = false;
|
||||
// Special case for Core module to ignore API tags defines
|
||||
for (int i = 0; i < headerFiles.Count; i++)
|
||||
{
|
||||
// Skip scripting types definitions file
|
||||
if (headerFiles[i].Replace('\\', '/').EndsWith("Engine/Core/Config.h", StringComparison.Ordinal) ||
|
||||
headerFiles[i].EndsWith("EditorContextAPI.h", StringComparison.Ordinal))
|
||||
continue;
|
||||
|
||||
// Check if file contains any valid API tag
|
||||
var contents = File.ReadAllText(headerFiles[i]);
|
||||
for (int j = 0; j < ApiTokens.SearchTags.Length; j++)
|
||||
if (headerFiles[i].EndsWith("Config.h", StringComparison.Ordinal))
|
||||
{
|
||||
if (contents.Contains(ApiTokens.SearchTags[j]))
|
||||
{
|
||||
if (headerFilesContents == null)
|
||||
headerFilesContents = new string[headerFiles.Count];
|
||||
headerFilesContents[i] = contents;
|
||||
anyApi = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!anyApi)
|
||||
return moduleInfo;
|
||||
}
|
||||
|
||||
// Skip if none of the headers was modified after last time generated C++ file was edited
|
||||
// TODO: skip parsing if module has API cache file -> then load it from disk
|
||||
/*if (!forceGenerate)
|
||||
{
|
||||
var lastGenerateTime = File.GetLastWriteTime(bindings.GeneratedCppFilePath);
|
||||
var anyModified = false;
|
||||
for (int i = 0; i < headerFiles.Count; i++)
|
||||
{
|
||||
if (File.GetLastWriteTime(headerFiles[i]) > lastGenerateTime)
|
||||
{
|
||||
anyModified = true;
|
||||
headerFiles.RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!anyModified)
|
||||
return;
|
||||
}*/
|
||||
// Sort file paths to have stable results
|
||||
headerFiles.Sort();
|
||||
|
||||
Log.Verbose($"Parsing API bindings for {module.Name} ({moduleInfo.Name})");
|
||||
|
||||
// Process all header files to generate the module API code reflection
|
||||
var context = new ParsingContext
|
||||
// Load cache
|
||||
using (new ProfileEventScope("LoadCache"))
|
||||
{
|
||||
CurrentAccessLevel = AccessLevel.Public,
|
||||
ScopeTypeStack = new Stack<ApiTypeInfo>(),
|
||||
ScopeAccessStack = new Stack<AccessLevel>(),
|
||||
PreprocessorDefines = new Dictionary<string, string>(),
|
||||
};
|
||||
for (int i = 0; i < headerFiles.Count; i++)
|
||||
{
|
||||
if (headerFilesContents[i] == null)
|
||||
continue;
|
||||
var fileInfo = new FileInfo
|
||||
if (LoadCache(ref moduleInfo, moduleOptions, headerFiles))
|
||||
{
|
||||
Parent = null,
|
||||
Children = new List<ApiTypeInfo>(),
|
||||
Name = headerFiles[i],
|
||||
Namespace = moduleInfo.Name,
|
||||
};
|
||||
moduleInfo.AddChild(fileInfo);
|
||||
buildData.ModulesInfo[module] = moduleInfo;
|
||||
|
||||
try
|
||||
{
|
||||
// Tokenize the source
|
||||
var tokenizer = new Tokenizer();
|
||||
tokenizer.Tokenize(headerFilesContents[i]);
|
||||
|
||||
// Init the context
|
||||
context.Tokenizer = tokenizer;
|
||||
context.File = fileInfo;
|
||||
context.ScopeInfo = null;
|
||||
context.ScopeTypeStack.Clear();
|
||||
context.ScopeAccessStack.Clear();
|
||||
context.PreprocessorDefines.Clear();
|
||||
context.EnterScope(fileInfo);
|
||||
|
||||
// Process the source code
|
||||
ApiTypeInfo scopeType = null;
|
||||
Token prevToken = null;
|
||||
while (true)
|
||||
// Initialize API
|
||||
using (new ProfileEventScope("Init"))
|
||||
{
|
||||
// Move to the next token
|
||||
var token = tokenizer.NextToken();
|
||||
if (token == null)
|
||||
continue;
|
||||
if (token.Type == TokenType.EndOfFile)
|
||||
break;
|
||||
|
||||
// Parse API_.. tags in source code
|
||||
if (token.Type == TokenType.Identifier && token.Value.StartsWith("API_", StringComparison.Ordinal))
|
||||
{
|
||||
if (string.Equals(token.Value, ApiTokens.Class, StringComparison.Ordinal))
|
||||
{
|
||||
if (!(context.ScopeInfo is FileInfo))
|
||||
throw new NotImplementedException("TODO: add support for nested classes in scripting API");
|
||||
|
||||
var classInfo = ParseClass(ref context);
|
||||
scopeType = classInfo;
|
||||
context.ScopeInfo.AddChild(scopeType);
|
||||
context.CurrentAccessLevel = AccessLevel.Public;
|
||||
}
|
||||
else if (string.Equals(token.Value, ApiTokens.Property, StringComparison.Ordinal))
|
||||
{
|
||||
var propertyInfo = ParseProperty(ref context);
|
||||
}
|
||||
else if (string.Equals(token.Value, ApiTokens.Function, StringComparison.Ordinal))
|
||||
{
|
||||
var functionInfo = ParseFunction(ref context);
|
||||
|
||||
if (context.ScopeInfo is ClassInfo classInfo)
|
||||
classInfo.Functions.Add(functionInfo);
|
||||
else if (context.ScopeInfo is StructureInfo structureInfo)
|
||||
structureInfo.Functions.Add(functionInfo);
|
||||
else
|
||||
throw new Exception($"Not supported free-function {functionInfo.Name} at line {tokenizer.CurrentLine}. Place it in the class to use API bindings for it.");
|
||||
}
|
||||
else if (string.Equals(token.Value, ApiTokens.Enum, StringComparison.Ordinal))
|
||||
{
|
||||
var enumInfo = ParseEnum(ref context);
|
||||
context.ScopeInfo.AddChild(enumInfo);
|
||||
}
|
||||
else if (string.Equals(token.Value, ApiTokens.Struct, StringComparison.Ordinal))
|
||||
{
|
||||
var structureInfo = ParseStructure(ref context);
|
||||
scopeType = structureInfo;
|
||||
context.ScopeInfo.AddChild(scopeType);
|
||||
context.CurrentAccessLevel = AccessLevel.Public;
|
||||
}
|
||||
else if (string.Equals(token.Value, ApiTokens.Field, StringComparison.Ordinal))
|
||||
{
|
||||
var fieldInfo = ParseField(ref context);
|
||||
var scopeInfo = context.ValidScopeInfoFromStack;
|
||||
|
||||
if (scopeInfo is ClassInfo classInfo)
|
||||
classInfo.Fields.Add(fieldInfo);
|
||||
else if (scopeInfo is StructureInfo structureInfo)
|
||||
structureInfo.Fields.Add(fieldInfo);
|
||||
else
|
||||
throw new Exception($"Not supported location for field {fieldInfo.Name} at line {tokenizer.CurrentLine}. Place it in the class or structure to use API bindings for it.");
|
||||
}
|
||||
else if (string.Equals(token.Value, ApiTokens.Event, StringComparison.Ordinal))
|
||||
{
|
||||
var eventInfo = ParseEvent(ref context);
|
||||
var scopeInfo = context.ValidScopeInfoFromStack;
|
||||
|
||||
if (scopeInfo is ClassInfo classInfo)
|
||||
classInfo.Events.Add(eventInfo);
|
||||
else
|
||||
throw new Exception($"Not supported location for event {eventInfo.Name} at line {tokenizer.CurrentLine}. Place it in the class to use API bindings for it.");
|
||||
}
|
||||
else if (string.Equals(token.Value, ApiTokens.InjectCppCode, StringComparison.Ordinal))
|
||||
{
|
||||
var injectCppCodeInfo = ParseInjectCppCode(ref context);
|
||||
fileInfo.AddChild(injectCppCodeInfo);
|
||||
}
|
||||
else if (string.Equals(token.Value, ApiTokens.AutoSerialization, StringComparison.Ordinal))
|
||||
{
|
||||
if (context.ScopeInfo is ClassInfo classInfo)
|
||||
classInfo.IsAutoSerialization = true;
|
||||
else if (context.ScopeInfo is StructureInfo structureInfo)
|
||||
structureInfo.IsAutoSerialization = true;
|
||||
else
|
||||
throw new Exception($"Not supported location for {ApiTokens.AutoSerialization} at line {tokenizer.CurrentLine}. Place it in the class or structure that uses API bindings.");
|
||||
}
|
||||
}
|
||||
|
||||
// Track access level inside class
|
||||
if (context.ScopeInfo != null && token.Type == TokenType.Colon && prevToken != null && prevToken.Type == TokenType.Identifier)
|
||||
{
|
||||
if (string.Equals(prevToken.Value, "public", StringComparison.Ordinal))
|
||||
{
|
||||
context.CurrentAccessLevel = AccessLevel.Public;
|
||||
}
|
||||
else if (string.Equals(prevToken.Value, "protected", StringComparison.Ordinal))
|
||||
{
|
||||
context.CurrentAccessLevel = AccessLevel.Protected;
|
||||
}
|
||||
else if (string.Equals(prevToken.Value, "private", StringComparison.Ordinal))
|
||||
{
|
||||
context.CurrentAccessLevel = AccessLevel.Private;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle preprocessor blocks
|
||||
if (token.Type == TokenType.Preprocessor)
|
||||
{
|
||||
token = tokenizer.NextToken();
|
||||
switch (token.Value)
|
||||
{
|
||||
case "define":
|
||||
{
|
||||
token = tokenizer.NextToken();
|
||||
var name = token.Value;
|
||||
var value = string.Empty;
|
||||
token = tokenizer.NextToken(true);
|
||||
while (token.Type != TokenType.Newline)
|
||||
{
|
||||
value += token.Value;
|
||||
token = tokenizer.NextToken(true);
|
||||
}
|
||||
value = value.Trim();
|
||||
context.PreprocessorDefines[name] = value;
|
||||
break;
|
||||
}
|
||||
case "if":
|
||||
{
|
||||
// Parse condition
|
||||
var condition = string.Empty;
|
||||
token = tokenizer.NextToken(true);
|
||||
while (token.Type != TokenType.Newline)
|
||||
{
|
||||
condition += token.Value;
|
||||
token = tokenizer.NextToken(true);
|
||||
}
|
||||
|
||||
// Replace contents with defines
|
||||
condition = condition.Trim();
|
||||
condition = ReplacePreProcessorDefines(condition, context.PreprocessorDefines.Keys);
|
||||
condition = ReplacePreProcessorDefines(condition, moduleOptions.PublicDefinitions);
|
||||
condition = ReplacePreProcessorDefines(condition, moduleOptions.PrivateDefinitions);
|
||||
condition = ReplacePreProcessorDefines(condition, moduleOptions.CompileEnv.PreprocessorDefinitions);
|
||||
condition = condition.Replace("false", "0");
|
||||
condition = condition.Replace("true", "1");
|
||||
|
||||
// Check condition
|
||||
// TODO: support expressions in preprocessor defines in API headers?
|
||||
if (condition != "1")
|
||||
{
|
||||
// Skip chunk of code
|
||||
ParsePreprocessorIf(fileInfo, tokenizer, ref token);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case "ifdef":
|
||||
{
|
||||
// Parse condition
|
||||
var define = string.Empty;
|
||||
token = tokenizer.NextToken(true);
|
||||
while (token.Type != TokenType.Newline)
|
||||
{
|
||||
define += token.Value;
|
||||
token = tokenizer.NextToken(true);
|
||||
}
|
||||
|
||||
// Check condition
|
||||
define = define.Trim();
|
||||
if (!context.PreprocessorDefines.ContainsKey(define) && !moduleOptions.CompileEnv.PreprocessorDefinitions.Contains(define))
|
||||
{
|
||||
ParsePreprocessorIf(fileInfo, tokenizer, ref token);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Scope tracking
|
||||
if (token.Type == TokenType.LeftCurlyBrace)
|
||||
{
|
||||
context.ScopeTypeStack.Push(scopeType);
|
||||
context.ScopeInfo = context.ScopeTypeStack.Peek();
|
||||
scopeType = null;
|
||||
}
|
||||
else if (token.Type == TokenType.RightCurlyBrace)
|
||||
{
|
||||
context.ScopeTypeStack.Pop();
|
||||
if (context.ScopeTypeStack.Count == 0)
|
||||
throw new Exception($"Mismatch of the {{}} braces pair in file '{fileInfo.Name}' at line {tokenizer.CurrentLine}.");
|
||||
context.ScopeInfo = context.ScopeTypeStack.Peek();
|
||||
if (context.ScopeInfo is FileInfo)
|
||||
context.CurrentAccessLevel = AccessLevel.Public;
|
||||
}
|
||||
|
||||
prevToken = token;
|
||||
moduleInfo.Init(buildData);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error($"Failed to parse '{fileInfo.Name}' file to generate bindings.");
|
||||
Log.Exception(ex);
|
||||
throw;
|
||||
|
||||
return moduleInfo;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse bindings
|
||||
Log.Verbose($"Parsing API bindings for {module.Name} ({moduleInfo.Name})");
|
||||
int concurrency = Math.Min(Math.Max(1, (int)(Environment.ProcessorCount * Configuration.ConcurrencyProcessorScale)), Configuration.MaxConcurrency);
|
||||
concurrency = 1; // Disable concurrency for parsing (the gain is unnoticeable or even worse in some cases)
|
||||
if (concurrency == 1 || headerFiles.Count < 2 * concurrency)
|
||||
{
|
||||
// Single-threaded
|
||||
for (int i = 0; i < headerFiles.Count; i++)
|
||||
{
|
||||
using (new ProfileEventScope(Path.GetFileName(headerFiles[i])))
|
||||
{
|
||||
ParseModuleInnerAsync(moduleInfo, moduleOptions, headerFiles, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Multi-threaded
|
||||
ThreadPool.GetMinThreads(out var workerThreads, out var completionPortThreads);
|
||||
if (workerThreads != concurrency)
|
||||
ThreadPool.SetMaxThreads(concurrency, completionPortThreads);
|
||||
Parallel.For(0, headerFiles.Count, (i, state) =>
|
||||
{
|
||||
using (new ProfileEventScope(Path.GetFileName(headerFiles[i])))
|
||||
{
|
||||
ParseModuleInnerAsync(moduleInfo, moduleOptions, headerFiles, i);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Save cache
|
||||
using (new ProfileEventScope("SaveCache"))
|
||||
{
|
||||
SaveCache(moduleInfo, moduleOptions, headerFiles);
|
||||
}
|
||||
|
||||
// Initialize API
|
||||
moduleInfo.Init(buildData);
|
||||
using (new ProfileEventScope("Init"))
|
||||
{
|
||||
moduleInfo.Init(buildData);
|
||||
}
|
||||
|
||||
return moduleInfo;
|
||||
}
|
||||
|
||||
private static void ParseModuleInnerAsync(ModuleInfo moduleInfo, BuildOptions moduleOptions, List<string> headerFiles, int workIndex)
|
||||
{
|
||||
// Find and load files with API tags
|
||||
bool hasApi = false;
|
||||
string headerFileContents = File.ReadAllText(headerFiles[workIndex]);
|
||||
for (int j = 0; j < ApiTokens.SearchTags.Length; j++)
|
||||
{
|
||||
if (headerFileContents.Contains(ApiTokens.SearchTags[j]))
|
||||
{
|
||||
hasApi = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasApi)
|
||||
return;
|
||||
|
||||
// Process header file to generate the module API code reflection
|
||||
var fileInfo = new FileInfo
|
||||
{
|
||||
Parent = null,
|
||||
Name = headerFiles[workIndex],
|
||||
Namespace = moduleInfo.Name,
|
||||
};
|
||||
lock (moduleInfo)
|
||||
{
|
||||
moduleInfo.AddChild(fileInfo);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Tokenize the source
|
||||
var tokenizer = new Tokenizer();
|
||||
tokenizer.Tokenize(headerFileContents);
|
||||
|
||||
// Init the context
|
||||
var context = new ParsingContext
|
||||
{
|
||||
File = fileInfo,
|
||||
Tokenizer = tokenizer,
|
||||
ScopeInfo = null,
|
||||
CurrentAccessLevel = AccessLevel.Public,
|
||||
ScopeTypeStack = new Stack<ApiTypeInfo>(),
|
||||
ScopeAccessStack = new Stack<AccessLevel>(),
|
||||
PreprocessorDefines = new Dictionary<string, string>(),
|
||||
};
|
||||
context.EnterScope(fileInfo);
|
||||
|
||||
// Process the source code
|
||||
ApiTypeInfo scopeType = null;
|
||||
Token prevToken = null;
|
||||
while (true)
|
||||
{
|
||||
// Move to the next token
|
||||
var token = tokenizer.NextToken();
|
||||
if (token == null)
|
||||
continue;
|
||||
if (token.Type == TokenType.EndOfFile)
|
||||
break;
|
||||
|
||||
// Parse API_.. tags in source code
|
||||
if (token.Type == TokenType.Identifier && token.Value.StartsWith("API_", StringComparison.Ordinal))
|
||||
{
|
||||
if (string.Equals(token.Value, ApiTokens.Class, StringComparison.Ordinal))
|
||||
{
|
||||
if (!(context.ScopeInfo is FileInfo))
|
||||
throw new NotImplementedException("TODO: add support for nested classes in scripting API");
|
||||
|
||||
var classInfo = ParseClass(ref context);
|
||||
scopeType = classInfo;
|
||||
context.ScopeInfo.AddChild(scopeType);
|
||||
context.CurrentAccessLevel = AccessLevel.Public;
|
||||
}
|
||||
else if (string.Equals(token.Value, ApiTokens.Property, StringComparison.Ordinal))
|
||||
{
|
||||
var propertyInfo = ParseProperty(ref context);
|
||||
}
|
||||
else if (string.Equals(token.Value, ApiTokens.Function, StringComparison.Ordinal))
|
||||
{
|
||||
var functionInfo = ParseFunction(ref context);
|
||||
|
||||
if (context.ScopeInfo is ClassInfo classInfo)
|
||||
classInfo.Functions.Add(functionInfo);
|
||||
else if (context.ScopeInfo is StructureInfo structureInfo)
|
||||
structureInfo.Functions.Add(functionInfo);
|
||||
else
|
||||
throw new Exception($"Not supported free-function {functionInfo.Name} at line {tokenizer.CurrentLine}. Place it in the class to use API bindings for it.");
|
||||
}
|
||||
else if (string.Equals(token.Value, ApiTokens.Enum, StringComparison.Ordinal))
|
||||
{
|
||||
var enumInfo = ParseEnum(ref context);
|
||||
context.ScopeInfo.AddChild(enumInfo);
|
||||
}
|
||||
else if (string.Equals(token.Value, ApiTokens.Struct, StringComparison.Ordinal))
|
||||
{
|
||||
var structureInfo = ParseStructure(ref context);
|
||||
scopeType = structureInfo;
|
||||
context.ScopeInfo.AddChild(scopeType);
|
||||
context.CurrentAccessLevel = AccessLevel.Public;
|
||||
}
|
||||
else if (string.Equals(token.Value, ApiTokens.Field, StringComparison.Ordinal))
|
||||
{
|
||||
var fieldInfo = ParseField(ref context);
|
||||
var scopeInfo = context.ValidScopeInfoFromStack;
|
||||
|
||||
if (scopeInfo is ClassInfo classInfo)
|
||||
classInfo.Fields.Add(fieldInfo);
|
||||
else if (scopeInfo is StructureInfo structureInfo)
|
||||
structureInfo.Fields.Add(fieldInfo);
|
||||
else
|
||||
throw new Exception($"Not supported location for field {fieldInfo.Name} at line {tokenizer.CurrentLine}. Place it in the class or structure to use API bindings for it.");
|
||||
}
|
||||
else if (string.Equals(token.Value, ApiTokens.Event, StringComparison.Ordinal))
|
||||
{
|
||||
var eventInfo = ParseEvent(ref context);
|
||||
var scopeInfo = context.ValidScopeInfoFromStack;
|
||||
|
||||
if (scopeInfo is ClassInfo classInfo)
|
||||
classInfo.Events.Add(eventInfo);
|
||||
else
|
||||
throw new Exception($"Not supported location for event {eventInfo.Name} at line {tokenizer.CurrentLine}. Place it in the class to use API bindings for it.");
|
||||
}
|
||||
else if (string.Equals(token.Value, ApiTokens.InjectCppCode, StringComparison.Ordinal))
|
||||
{
|
||||
var injectCppCodeInfo = ParseInjectCppCode(ref context);
|
||||
fileInfo.AddChild(injectCppCodeInfo);
|
||||
}
|
||||
else if (string.Equals(token.Value, ApiTokens.Interface, StringComparison.Ordinal))
|
||||
{
|
||||
if (!(context.ScopeInfo is FileInfo))
|
||||
throw new NotImplementedException("TODO: add support for nested interfaces in scripting API");
|
||||
|
||||
var interfaceInfo = ParseInterface(ref context);
|
||||
scopeType = interfaceInfo;
|
||||
context.ScopeInfo.AddChild(scopeType);
|
||||
context.CurrentAccessLevel = AccessLevel.Public;
|
||||
}
|
||||
else if (string.Equals(token.Value, ApiTokens.AutoSerialization, StringComparison.Ordinal))
|
||||
{
|
||||
if (context.ScopeInfo is ClassInfo classInfo)
|
||||
classInfo.IsAutoSerialization = true;
|
||||
else if (context.ScopeInfo is StructureInfo structureInfo)
|
||||
structureInfo.IsAutoSerialization = true;
|
||||
else
|
||||
throw new Exception($"Not supported location for {ApiTokens.AutoSerialization} at line {tokenizer.CurrentLine}. Place it in the class or structure that uses API bindings.");
|
||||
}
|
||||
}
|
||||
|
||||
// Track access level inside class
|
||||
if (context.ScopeInfo != null && token.Type == TokenType.Colon && prevToken != null && prevToken.Type == TokenType.Identifier)
|
||||
{
|
||||
if (string.Equals(prevToken.Value, "public", StringComparison.Ordinal))
|
||||
{
|
||||
context.CurrentAccessLevel = AccessLevel.Public;
|
||||
}
|
||||
else if (string.Equals(prevToken.Value, "protected", StringComparison.Ordinal))
|
||||
{
|
||||
context.CurrentAccessLevel = AccessLevel.Protected;
|
||||
}
|
||||
else if (string.Equals(prevToken.Value, "private", StringComparison.Ordinal))
|
||||
{
|
||||
context.CurrentAccessLevel = AccessLevel.Private;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle preprocessor blocks
|
||||
if (token.Type == TokenType.Preprocessor)
|
||||
{
|
||||
token = tokenizer.NextToken();
|
||||
switch (token.Value)
|
||||
{
|
||||
case "define":
|
||||
{
|
||||
token = tokenizer.NextToken();
|
||||
var name = token.Value;
|
||||
var value = string.Empty;
|
||||
token = tokenizer.NextToken(true);
|
||||
while (token.Type != TokenType.Newline)
|
||||
{
|
||||
value += token.Value;
|
||||
token = tokenizer.NextToken(true);
|
||||
}
|
||||
value = value.Trim();
|
||||
context.PreprocessorDefines[name] = value;
|
||||
break;
|
||||
}
|
||||
case "if":
|
||||
{
|
||||
// Parse condition
|
||||
var condition = string.Empty;
|
||||
token = tokenizer.NextToken(true);
|
||||
while (token.Type != TokenType.Newline)
|
||||
{
|
||||
var tokenValue = token.Value.Trim();
|
||||
if (tokenValue.Length == 0)
|
||||
{
|
||||
token = tokenizer.NextToken(true);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Very simple defines processing
|
||||
tokenValue = ReplacePreProcessorDefines(tokenValue, context.PreprocessorDefines.Keys);
|
||||
tokenValue = ReplacePreProcessorDefines(tokenValue, moduleOptions.PublicDefinitions);
|
||||
tokenValue = ReplacePreProcessorDefines(tokenValue, moduleOptions.PrivateDefinitions);
|
||||
tokenValue = ReplacePreProcessorDefines(tokenValue, moduleOptions.CompileEnv.PreprocessorDefinitions);
|
||||
tokenValue = tokenValue.Replace("false", "0");
|
||||
tokenValue = tokenValue.Replace("true", "1");
|
||||
tokenValue = tokenValue.Replace("||", "|");
|
||||
if (tokenValue.Length != 0 && tokenValue != "1" && tokenValue != "0" && tokenValue != "|")
|
||||
tokenValue = "0";
|
||||
|
||||
condition += tokenValue;
|
||||
token = tokenizer.NextToken(true);
|
||||
}
|
||||
|
||||
// Filter condition
|
||||
condition = condition.Replace("1|1", "1");
|
||||
condition = condition.Replace("1|0", "1");
|
||||
condition = condition.Replace("0|1", "1");
|
||||
|
||||
// Skip chunk of code of condition fails
|
||||
if (condition != "1")
|
||||
{
|
||||
ParsePreprocessorIf(fileInfo, tokenizer, ref token);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case "ifdef":
|
||||
{
|
||||
// Parse condition
|
||||
var define = string.Empty;
|
||||
token = tokenizer.NextToken(true);
|
||||
while (token.Type != TokenType.Newline)
|
||||
{
|
||||
define += token.Value;
|
||||
token = tokenizer.NextToken(true);
|
||||
}
|
||||
|
||||
// Check condition
|
||||
define = define.Trim();
|
||||
if (!context.PreprocessorDefines.ContainsKey(define) && !moduleOptions.CompileEnv.PreprocessorDefinitions.Contains(define))
|
||||
{
|
||||
ParsePreprocessorIf(fileInfo, tokenizer, ref token);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Scope tracking
|
||||
if (token.Type == TokenType.LeftCurlyBrace)
|
||||
{
|
||||
context.ScopeTypeStack.Push(scopeType);
|
||||
context.ScopeInfo = context.ScopeTypeStack.Peek();
|
||||
scopeType = null;
|
||||
}
|
||||
else if (token.Type == TokenType.RightCurlyBrace)
|
||||
{
|
||||
context.ScopeTypeStack.Pop();
|
||||
if (context.ScopeTypeStack.Count == 0)
|
||||
throw new Exception($"Mismatch of the {{}} braces pair in file '{fileInfo.Name}' at line {tokenizer.CurrentLine}.");
|
||||
context.ScopeInfo = context.ScopeTypeStack.Peek();
|
||||
if (context.ScopeInfo is FileInfo)
|
||||
context.CurrentAccessLevel = AccessLevel.Public;
|
||||
}
|
||||
|
||||
prevToken = token;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error($"Failed to parse '{fileInfo.Name}' file to generate bindings.");
|
||||
Log.Exception(ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private static string ReplacePreProcessorDefines(string text, IEnumerable<string> defines)
|
||||
{
|
||||
foreach (var define in defines)
|
||||
{
|
||||
if (text.Contains(define))
|
||||
if (string.Equals(text, define, StringComparison.Ordinal))
|
||||
text = text.Replace(define, "1");
|
||||
}
|
||||
return text;
|
||||
@@ -426,41 +479,82 @@ namespace Flax.Build.Bindings
|
||||
throw new Exception($"Invalid #if/endif pairing in file '{fileInfo.Name}'. Failed to generate API bindings for it.");
|
||||
}
|
||||
|
||||
private static bool UseBindings(object type)
|
||||
{
|
||||
var apiTypeInfo = type as ApiTypeInfo;
|
||||
if (apiTypeInfo != null && apiTypeInfo.IsInBuild)
|
||||
return false;
|
||||
if ((type is ModuleInfo || type is FileInfo) && apiTypeInfo != null)
|
||||
{
|
||||
foreach (var child in apiTypeInfo.Children)
|
||||
{
|
||||
if (UseBindings(child))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return type is ClassInfo ||
|
||||
type is StructureInfo ||
|
||||
type is InterfaceInfo ||
|
||||
type is InjectCppCodeInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The API bindings generation utility that can produce scripting bindings for another languages to the native code.
|
||||
/// </summary>
|
||||
public static void GenerateBindings(BuildData buildData, Module module, ref BuildOptions moduleOptions, out BindingsResult bindings)
|
||||
{
|
||||
// Parse module (or load from cache)
|
||||
var moduleInfo = ParseModule(buildData, module, moduleOptions);
|
||||
bindings = new BindingsResult
|
||||
{
|
||||
UseBindings = UseBindings(moduleInfo),
|
||||
GeneratedCppFilePath = Path.Combine(moduleOptions.IntermediateFolder, module.Name + ".Bindings.Gen.cpp"),
|
||||
GeneratedCSharpFilePath = Path.Combine(moduleOptions.IntermediateFolder, module.Name + ".Bindings.Gen.cs"),
|
||||
};
|
||||
var moduleInfo = ParseModule(buildData, module, moduleOptions);
|
||||
|
||||
if (bindings.UseBindings)
|
||||
{
|
||||
buildData.Modules.TryGetValue(moduleInfo.Module, out var moduleBuildInfo);
|
||||
|
||||
// Ensure that generated files are included into build
|
||||
if (!moduleBuildInfo.SourceFiles.Contains(bindings.GeneratedCSharpFilePath))
|
||||
moduleBuildInfo.SourceFiles.Add(bindings.GeneratedCSharpFilePath);
|
||||
}
|
||||
|
||||
// Skip if module is cached (no scripting API changed)
|
||||
if (moduleInfo.IsFromCache)
|
||||
return;
|
||||
|
||||
// Process parsed API
|
||||
foreach (var child in moduleInfo.Children)
|
||||
using (new ProfileEventScope("Process"))
|
||||
{
|
||||
try
|
||||
foreach (var child in moduleInfo.Children)
|
||||
{
|
||||
foreach (var apiTypeInfo in child.Children)
|
||||
ProcessAndValidate(buildData, apiTypeInfo);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
if (child is FileInfo fileInfo)
|
||||
Log.Error($"Failed to validate '{fileInfo.Name}' file to generate bindings.");
|
||||
throw;
|
||||
try
|
||||
{
|
||||
foreach (var apiTypeInfo in child.Children)
|
||||
ProcessAndValidate(buildData, apiTypeInfo);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
if (child is FileInfo fileInfo)
|
||||
Log.Error($"Failed to validate '{fileInfo.Name}' file to generate bindings.");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generate bindings for scripting
|
||||
Log.Verbose($"Generating API bindings for {module.Name} ({moduleInfo.Name})");
|
||||
GenerateCpp(buildData, moduleInfo, ref bindings);
|
||||
GenerateCSharp(buildData, moduleInfo, ref bindings);
|
||||
if (bindings.UseBindings)
|
||||
{
|
||||
Log.Verbose($"Generating API bindings for {module.Name} ({moduleInfo.Name})");
|
||||
using (new ProfileEventScope("Cpp"))
|
||||
GenerateCpp(buildData, moduleInfo, ref bindings);
|
||||
using (new ProfileEventScope("CSharp"))
|
||||
GenerateCSharp(buildData, moduleInfo, ref bindings);
|
||||
|
||||
// TODO: add support for extending this code and support generating bindings for other scripting languages
|
||||
// TODO: add support for extending this code and support generating bindings for other scripting languages
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -568,7 +662,6 @@ namespace Flax.Build.Bindings
|
||||
{
|
||||
foreach (var fieldInfo in structureInfo.Fields)
|
||||
{
|
||||
// TODO: support bit-fields in structure fields
|
||||
if (fieldInfo.Type.IsBitField)
|
||||
throw new NotImplementedException($"TODO: support bit-fields in structure fields (found field {fieldInfo} in structure {structureInfo.Name})");
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved.
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Flax.Build.Bindings
|
||||
@@ -8,7 +9,7 @@ namespace Flax.Build.Bindings
|
||||
/// <summary>
|
||||
/// The native class information for bindings generator.
|
||||
/// </summary>
|
||||
public class ClassInfo : ApiTypeInfo
|
||||
public class ClassInfo : ClassStructInfo
|
||||
{
|
||||
private static readonly HashSet<string> InBuildScriptingObjectTypes = new HashSet<string>
|
||||
{
|
||||
@@ -22,19 +23,17 @@ namespace Flax.Build.Bindings
|
||||
"Actor",
|
||||
};
|
||||
|
||||
public AccessLevel Access;
|
||||
public TypeInfo BaseType;
|
||||
public AccessLevel BaseTypeInheritance;
|
||||
public bool IsBaseTypeHidden;
|
||||
public bool IsStatic;
|
||||
public bool IsSealed;
|
||||
public bool IsAbstract;
|
||||
public bool IsAutoSerialization;
|
||||
public bool NoSpawn;
|
||||
public bool NoConstructor;
|
||||
public List<FunctionInfo> Functions;
|
||||
public List<PropertyInfo> Properties;
|
||||
public List<FieldInfo> Fields;
|
||||
public List<EventInfo> Events;
|
||||
public List<FunctionInfo> Functions = new List<FunctionInfo>();
|
||||
public List<PropertyInfo> Properties = new List<PropertyInfo>();
|
||||
public List<FieldInfo> Fields = new List<FieldInfo>();
|
||||
public List<EventInfo> Events = new List<EventInfo>();
|
||||
|
||||
internal HashSet<string> UniqueFunctionNames;
|
||||
|
||||
@@ -50,6 +49,9 @@ namespace Flax.Build.Bindings
|
||||
{
|
||||
base.Init(buildData);
|
||||
|
||||
// Internal base types are usually hidden from bindings (used in core-only internally)
|
||||
IsBaseTypeHidden = BaseTypeInheritance == AccessLevel.Private || BaseType == null;
|
||||
|
||||
// Cache if it it Scripting Object type
|
||||
if (InBuildScriptingObjectTypes.Contains(Name))
|
||||
_isScriptingObject = true;
|
||||
@@ -73,6 +75,40 @@ namespace Flax.Build.Bindings
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write(BinaryWriter writer)
|
||||
{
|
||||
// TODO: convert into flags
|
||||
writer.Write(IsStatic);
|
||||
writer.Write(IsSealed);
|
||||
writer.Write(IsAbstract);
|
||||
writer.Write(IsAutoSerialization);
|
||||
writer.Write(NoSpawn);
|
||||
writer.Write(NoConstructor);
|
||||
BindingsGenerator.Write(writer, Functions);
|
||||
BindingsGenerator.Write(writer, Properties);
|
||||
BindingsGenerator.Write(writer, Fields);
|
||||
BindingsGenerator.Write(writer, Events);
|
||||
|
||||
base.Write(writer);
|
||||
}
|
||||
|
||||
public override void Read(BinaryReader reader)
|
||||
{
|
||||
// TODO: convert into flags
|
||||
IsStatic = reader.ReadBoolean();
|
||||
IsSealed = reader.ReadBoolean();
|
||||
IsAbstract = reader.ReadBoolean();
|
||||
IsAutoSerialization = reader.ReadBoolean();
|
||||
NoSpawn = reader.ReadBoolean();
|
||||
NoConstructor = reader.ReadBoolean();
|
||||
Functions = BindingsGenerator.Read(reader, Functions);
|
||||
Properties = BindingsGenerator.Read(reader, Properties);
|
||||
Fields = BindingsGenerator.Read(reader, Fields);
|
||||
Events = BindingsGenerator.Read(reader, Events);
|
||||
|
||||
base.Read(reader);
|
||||
}
|
||||
|
||||
public int GetScriptVTableSize(Builder.BuildData buildData, out int offset)
|
||||
{
|
||||
if (_scriptVTableSize == -1)
|
||||
|
||||
60
Source/Tools/Flax.Build/Bindings/ClassStructInfo.cs
Normal file
60
Source/Tools/Flax.Build/Bindings/ClassStructInfo.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Flax.Build.Bindings
|
||||
{
|
||||
/// <summary>
|
||||
/// The native class/structure information for bindings generator.
|
||||
/// </summary>
|
||||
public abstract class ClassStructInfo : ApiTypeInfo
|
||||
{
|
||||
public AccessLevel Access;
|
||||
public AccessLevel BaseTypeInheritance;
|
||||
public TypeInfo BaseType;
|
||||
public List<InterfaceInfo> Interfaces; // Optional
|
||||
public List<TypeInfo> InterfaceNames; // Optional
|
||||
|
||||
public override void Init(Builder.BuildData buildData)
|
||||
{
|
||||
base.Init(buildData);
|
||||
|
||||
if (Interfaces == null && InterfaceNames != null && InterfaceNames.Count != 0)
|
||||
{
|
||||
Interfaces = new List<InterfaceInfo>();
|
||||
for (var i = 0; i < InterfaceNames.Count; i++)
|
||||
{
|
||||
var interfaceName = InterfaceNames[i];
|
||||
var apiTypeInfo = BindingsGenerator.FindApiTypeInfo(buildData, interfaceName, this);
|
||||
if (apiTypeInfo is InterfaceInfo interfaceInfo)
|
||||
{
|
||||
Interfaces.Add(interfaceInfo);
|
||||
}
|
||||
}
|
||||
if (Interfaces.Count == 0)
|
||||
Interfaces = null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write(BinaryWriter writer)
|
||||
{
|
||||
writer.Write((byte)Access);
|
||||
writer.Write((byte)BaseTypeInheritance);
|
||||
BindingsGenerator.Write(writer, BaseType);
|
||||
BindingsGenerator.Write(writer, InterfaceNames);
|
||||
|
||||
base.Write(writer);
|
||||
}
|
||||
|
||||
public override void Read(BinaryReader reader)
|
||||
{
|
||||
Access = (AccessLevel)reader.ReadByte();
|
||||
BaseTypeInheritance = (AccessLevel)reader.ReadByte();
|
||||
BaseType = BindingsGenerator.Read(reader, BaseType);
|
||||
InterfaceNames = BindingsGenerator.Read(reader, InterfaceNames);
|
||||
|
||||
base.Read(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved.
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Flax.Build.Bindings
|
||||
{
|
||||
@@ -10,7 +11,7 @@ namespace Flax.Build.Bindings
|
||||
/// </summary>
|
||||
public class EnumInfo : ApiTypeInfo
|
||||
{
|
||||
public struct EntryInfo
|
||||
public struct EntryInfo : IBindingsCache
|
||||
{
|
||||
public string Name;
|
||||
public string[] Comment;
|
||||
@@ -21,11 +22,27 @@ namespace Flax.Build.Bindings
|
||||
{
|
||||
return Name + (string.IsNullOrEmpty(Value) ? string.Empty : " = " + Value);
|
||||
}
|
||||
|
||||
public void Write(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(Name);
|
||||
BindingsGenerator.Write(writer, Comment);
|
||||
BindingsGenerator.Write(writer, Value);
|
||||
BindingsGenerator.Write(writer, Attributes);
|
||||
}
|
||||
|
||||
public void Read(BinaryReader reader)
|
||||
{
|
||||
Name = reader.ReadString();
|
||||
Comment = BindingsGenerator.Read(reader, Comment);
|
||||
Value = BindingsGenerator.Read(reader, Value);
|
||||
Attributes = BindingsGenerator.Read(reader, Attributes);
|
||||
}
|
||||
}
|
||||
|
||||
public AccessLevel Access;
|
||||
public TypeInfo UnderlyingType;
|
||||
public List<EntryInfo> Entries;
|
||||
public List<EntryInfo> Entries = new List<EntryInfo>();
|
||||
|
||||
public override bool IsValueType => true;
|
||||
public override bool IsEnum => true;
|
||||
@@ -36,6 +53,24 @@ namespace Flax.Build.Bindings
|
||||
throw new NotSupportedException("API enums cannot have sub-types.");
|
||||
}
|
||||
|
||||
public override void Write(BinaryWriter writer)
|
||||
{
|
||||
writer.Write((byte)Access);
|
||||
BindingsGenerator.Write(writer, UnderlyingType);
|
||||
BindingsGenerator.Write(writer, Entries);
|
||||
|
||||
base.Write(writer);
|
||||
}
|
||||
|
||||
public override void Read(BinaryReader reader)
|
||||
{
|
||||
Access = (AccessLevel)reader.ReadByte();
|
||||
UnderlyingType = BindingsGenerator.Read(reader, UnderlyingType);
|
||||
Entries = BindingsGenerator.Read(reader, Entries);
|
||||
|
||||
base.Read(reader);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "enum " + Name;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved.
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.IO;
|
||||
|
||||
namespace Flax.Build.Bindings
|
||||
{
|
||||
@@ -9,6 +11,20 @@ namespace Flax.Build.Bindings
|
||||
{
|
||||
public TypeInfo Type;
|
||||
|
||||
public override void Write(BinaryWriter writer)
|
||||
{
|
||||
BindingsGenerator.Write(writer, Type);
|
||||
|
||||
base.Write(writer);
|
||||
}
|
||||
|
||||
public override void Read(BinaryReader reader)
|
||||
{
|
||||
Type = BindingsGenerator.Read(reader, Type);
|
||||
|
||||
base.Read(reader);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var result = string.Empty;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved.
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.IO;
|
||||
|
||||
namespace Flax.Build.Bindings
|
||||
{
|
||||
@@ -12,6 +14,31 @@ namespace Flax.Build.Bindings
|
||||
public bool NoArray;
|
||||
public FunctionInfo Getter;
|
||||
public FunctionInfo Setter;
|
||||
public string DefaultValue;
|
||||
|
||||
public bool HasDefaultValue => !string.IsNullOrEmpty(DefaultValue);
|
||||
|
||||
public override void Write(BinaryWriter writer)
|
||||
{
|
||||
BindingsGenerator.Write(writer, Type);
|
||||
// TODO: convert into flags
|
||||
writer.Write(IsReadOnly);
|
||||
writer.Write(NoArray);
|
||||
BindingsGenerator.Write(writer, DefaultValue);
|
||||
|
||||
base.Write(writer);
|
||||
}
|
||||
|
||||
public override void Read(BinaryReader reader)
|
||||
{
|
||||
Type = BindingsGenerator.Read(reader, Type);
|
||||
// TODO: convert into flags
|
||||
IsReadOnly = reader.ReadBoolean();
|
||||
NoArray = reader.ReadBoolean();
|
||||
DefaultValue = BindingsGenerator.Read(reader, DefaultValue);
|
||||
|
||||
base.Read(reader);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
@@ -19,6 +46,8 @@ namespace Flax.Build.Bindings
|
||||
if (IsStatic)
|
||||
result += "static ";
|
||||
result += Type + " " + Name;
|
||||
if (HasDefaultValue)
|
||||
result += " = " + DefaultValue;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved.
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Flax.Build.Bindings
|
||||
/// <summary>
|
||||
/// The native file information for bindings generator.
|
||||
/// </summary>
|
||||
public class FileInfo : ApiTypeInfo, IComparable<FileInfo>
|
||||
public class FileInfo : ApiTypeInfo, IComparable, IComparable<FileInfo>
|
||||
{
|
||||
public override void AddChild(ApiTypeInfo apiTypeInfo)
|
||||
{
|
||||
@@ -26,5 +26,12 @@ namespace Flax.Build.Bindings
|
||||
{
|
||||
return System.IO.Path.GetFileName(Name);
|
||||
}
|
||||
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
if (obj is ApiTypeInfo apiTypeInfo)
|
||||
return Name.CompareTo(apiTypeInfo.Name);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved.
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Flax.Build.Bindings
|
||||
{
|
||||
@@ -9,7 +10,7 @@ namespace Flax.Build.Bindings
|
||||
/// </summary>
|
||||
public class FunctionInfo : MemberInfo
|
||||
{
|
||||
public struct ParameterInfo
|
||||
public struct ParameterInfo : IBindingsCache
|
||||
{
|
||||
public string Name;
|
||||
public TypeInfo Type;
|
||||
@@ -25,6 +26,28 @@ namespace Flax.Build.Bindings
|
||||
return Attributes != null && Attributes.Contains(name);
|
||||
}
|
||||
|
||||
public void Write(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(Name);
|
||||
BindingsGenerator.Write(writer, Type);
|
||||
BindingsGenerator.Write(writer, DefaultValue);
|
||||
BindingsGenerator.Write(writer, Attributes);
|
||||
// TODO: convert into flags
|
||||
writer.Write(IsRef);
|
||||
writer.Write(IsOut);
|
||||
}
|
||||
|
||||
public void Read(BinaryReader reader)
|
||||
{
|
||||
Name = reader.ReadString();
|
||||
Type = BindingsGenerator.Read(reader, Type);
|
||||
DefaultValue = BindingsGenerator.Read(reader, DefaultValue);
|
||||
Attributes = BindingsGenerator.Read(reader, Attributes);
|
||||
// TODO: convert into flags
|
||||
IsRef = reader.ReadBoolean();
|
||||
IsOut = reader.ReadBoolean();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var result = Type + " " + Name;
|
||||
@@ -42,12 +65,36 @@ namespace Flax.Build.Bindings
|
||||
|
||||
public string UniqueName;
|
||||
public TypeInfo ReturnType;
|
||||
public List<ParameterInfo> Parameters;
|
||||
public List<ParameterInfo> Parameters = new List<ParameterInfo>();
|
||||
public bool IsVirtual;
|
||||
public bool IsConst;
|
||||
public bool NoProxy;
|
||||
public GlueInfo Glue;
|
||||
|
||||
public override void Write(BinaryWriter writer)
|
||||
{
|
||||
BindingsGenerator.Write(writer, ReturnType);
|
||||
BindingsGenerator.Write(writer, Parameters);
|
||||
// TODO: convert into flags
|
||||
writer.Write(IsVirtual);
|
||||
writer.Write(IsConst);
|
||||
writer.Write(NoProxy);
|
||||
|
||||
base.Write(writer);
|
||||
}
|
||||
|
||||
public override void Read(BinaryReader reader)
|
||||
{
|
||||
ReturnType = BindingsGenerator.Read(reader, ReturnType);
|
||||
Parameters = BindingsGenerator.Read(reader, Parameters);
|
||||
// TODO: convert into flags
|
||||
IsVirtual = reader.ReadBoolean();
|
||||
IsConst = reader.ReadBoolean();
|
||||
NoProxy = reader.ReadBoolean();
|
||||
|
||||
base.Read(reader);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var result = string.Empty;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved.
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
namespace Flax.Build.Bindings
|
||||
{
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved.
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.IO;
|
||||
|
||||
namespace Flax.Build.Bindings
|
||||
{
|
||||
@@ -9,6 +11,20 @@ namespace Flax.Build.Bindings
|
||||
{
|
||||
public string Code;
|
||||
|
||||
public override void Write(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(Code);
|
||||
|
||||
base.Write(writer);
|
||||
}
|
||||
|
||||
public override void Read(BinaryReader reader)
|
||||
{
|
||||
Code = reader.ReadString();
|
||||
|
||||
base.Read(reader);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
|
||||
24
Source/Tools/Flax.Build/Bindings/InterfaceInfo.cs
Normal file
24
Source/Tools/Flax.Build/Bindings/InterfaceInfo.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
namespace Flax.Build.Bindings
|
||||
{
|
||||
/// <summary>
|
||||
/// The native class/structure interface information for bindings generator.
|
||||
/// </summary>
|
||||
public class InterfaceInfo : ClassStructInfo
|
||||
{
|
||||
public override bool IsInterface => true;
|
||||
|
||||
public override void AddChild(ApiTypeInfo apiTypeInfo)
|
||||
{
|
||||
apiTypeInfo.Namespace = null;
|
||||
|
||||
base.AddChild(apiTypeInfo);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "interface " + Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved.
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved.
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.IO;
|
||||
|
||||
namespace Flax.Build.Bindings
|
||||
{
|
||||
/// <summary>
|
||||
/// The native member information for bindings generator.
|
||||
/// </summary>
|
||||
public class MemberInfo
|
||||
public class MemberInfo : IBindingsCache
|
||||
{
|
||||
public string Name;
|
||||
public string[] Comment;
|
||||
@@ -17,5 +19,23 @@ namespace Flax.Build.Bindings
|
||||
{
|
||||
return Attributes != null && Attributes.Contains(name);
|
||||
}
|
||||
|
||||
public virtual void Write(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(Name);
|
||||
BindingsGenerator.Write(writer, Comment);
|
||||
writer.Write(IsStatic);
|
||||
writer.Write((byte)Access);
|
||||
BindingsGenerator.Write(writer, Attributes);
|
||||
}
|
||||
|
||||
public virtual void Read(BinaryReader reader)
|
||||
{
|
||||
Name = reader.ReadString();
|
||||
Comment = BindingsGenerator.Read(reader, Comment);
|
||||
IsStatic = reader.ReadBoolean();
|
||||
Access = (AccessLevel)reader.ReadByte();
|
||||
Attributes = BindingsGenerator.Read(reader, Attributes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved.
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Flax.Build.Bindings
|
||||
{
|
||||
@@ -8,10 +11,41 @@ namespace Flax.Build.Bindings
|
||||
public class ModuleInfo : ApiTypeInfo
|
||||
{
|
||||
public Module Module;
|
||||
public bool IsFromCache;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "module " + Name;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Init(Builder.BuildData buildData)
|
||||
{
|
||||
base.Init(buildData);
|
||||
|
||||
// Sort module files to prevent bindings rebuild due to order changes (list might be created in async)
|
||||
Children.Sort();
|
||||
}
|
||||
|
||||
public override void Write(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(Module.Name);
|
||||
writer.Write(Module.FilePath);
|
||||
BindingsGenerator.Write(writer, Module.BinaryModuleName);
|
||||
writer.Write(Module.BuildNativeCode);
|
||||
|
||||
base.Write(writer);
|
||||
}
|
||||
|
||||
public override void Read(BinaryReader reader)
|
||||
{
|
||||
if (reader.ReadString() != Module.Name ||
|
||||
reader.ReadString() != Module.FilePath ||
|
||||
BindingsGenerator.Read(reader, Module.BinaryModuleName) != Module.BinaryModuleName ||
|
||||
reader.ReadBoolean() != Module.BuildNativeCode)
|
||||
throw new Exception();
|
||||
|
||||
base.Read(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved.
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.IO;
|
||||
|
||||
namespace Flax.Build.Bindings
|
||||
{
|
||||
@@ -11,6 +13,24 @@ namespace Flax.Build.Bindings
|
||||
public FunctionInfo Getter;
|
||||
public FunctionInfo Setter;
|
||||
|
||||
public override void Write(BinaryWriter writer)
|
||||
{
|
||||
BindingsGenerator.Write(writer, Type);
|
||||
BindingsGenerator.Write(writer, Getter);
|
||||
BindingsGenerator.Write(writer, Setter);
|
||||
|
||||
base.Write(writer);
|
||||
}
|
||||
|
||||
public override void Read(BinaryReader reader)
|
||||
{
|
||||
Type = BindingsGenerator.Read(reader, Type);
|
||||
Getter = BindingsGenerator.Read(reader, Getter);
|
||||
Setter = BindingsGenerator.Read(reader, Setter);
|
||||
|
||||
base.Read(reader);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Type + " " + Name;
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved.
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Flax.Build.Bindings
|
||||
{
|
||||
/// <summary>
|
||||
/// The native structure information for bindings generator.
|
||||
/// </summary>
|
||||
public class StructureInfo : ApiTypeInfo
|
||||
public class StructureInfo : ClassStructInfo
|
||||
{
|
||||
public AccessLevel Access;
|
||||
public TypeInfo BaseType;
|
||||
public List<FieldInfo> Fields;
|
||||
public List<FunctionInfo> Functions;
|
||||
public List<FieldInfo> Fields = new List<FieldInfo>();
|
||||
public List<FunctionInfo> Functions = new List<FunctionInfo>();
|
||||
public bool IsAutoSerialization;
|
||||
public bool ForceNoPod;
|
||||
|
||||
@@ -27,7 +26,7 @@ namespace Flax.Build.Bindings
|
||||
{
|
||||
base.Init(buildData);
|
||||
|
||||
if (ForceNoPod)
|
||||
if (ForceNoPod || (InterfaceNames != null && InterfaceNames.Count != 0))
|
||||
{
|
||||
_isPod = false;
|
||||
return;
|
||||
@@ -45,6 +44,26 @@ namespace Flax.Build.Bindings
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write(BinaryWriter writer)
|
||||
{
|
||||
BindingsGenerator.Write(writer, Fields);
|
||||
BindingsGenerator.Write(writer, Functions);
|
||||
writer.Write(IsAutoSerialization);
|
||||
writer.Write(ForceNoPod);
|
||||
|
||||
base.Write(writer);
|
||||
}
|
||||
|
||||
public override void Read(BinaryReader reader)
|
||||
{
|
||||
Fields = BindingsGenerator.Read(reader, Fields);
|
||||
Functions = BindingsGenerator.Read(reader, Functions);
|
||||
IsAutoSerialization = reader.ReadBoolean();
|
||||
ForceNoPod = reader.ReadBoolean();
|
||||
|
||||
base.Read(reader);
|
||||
}
|
||||
|
||||
public override void AddChild(ApiTypeInfo apiTypeInfo)
|
||||
{
|
||||
if (apiTypeInfo is EnumInfo)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved.
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
@@ -10,7 +11,7 @@ namespace Flax.Build.Bindings
|
||||
/// <summary>
|
||||
/// The native type information for bindings generator.
|
||||
/// </summary>
|
||||
public class TypeInfo : IEquatable<TypeInfo>
|
||||
public class TypeInfo : IEquatable<TypeInfo>, IBindingsCache
|
||||
{
|
||||
public string Type;
|
||||
public bool IsConst;
|
||||
@@ -51,6 +52,34 @@ namespace Flax.Build.Bindings
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Write(BinaryWriter writer)
|
||||
{
|
||||
BindingsGenerator.Write(writer, Type);
|
||||
// TODO: pack as flags
|
||||
writer.Write(IsConst);
|
||||
writer.Write(IsRef);
|
||||
writer.Write(IsPtr);
|
||||
writer.Write(IsArray);
|
||||
writer.Write(IsBitField);
|
||||
writer.Write(ArraySize);
|
||||
writer.Write(BitSize);
|
||||
BindingsGenerator.Write(writer, GenericArgs);
|
||||
}
|
||||
|
||||
public void Read(BinaryReader reader)
|
||||
{
|
||||
Type = BindingsGenerator.Read(reader, Type);
|
||||
// TODO: convert into flags
|
||||
IsConst = reader.ReadBoolean();
|
||||
IsRef = reader.ReadBoolean();
|
||||
IsPtr = reader.ReadBoolean();
|
||||
IsArray = reader.ReadBoolean();
|
||||
IsBitField = reader.ReadBoolean();
|
||||
ArraySize = reader.ReadInt32();
|
||||
BitSize = reader.ReadInt32();
|
||||
GenericArgs = BindingsGenerator.Read(reader, GenericArgs);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var sb = new StringBuilder(64);
|
||||
@@ -83,7 +112,7 @@ namespace Flax.Build.Bindings
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static bool Equals(List<TypeInfo> a, List<TypeInfo> b)
|
||||
public static bool Equals(List<TypeInfo> a, List<TypeInfo> b)
|
||||
{
|
||||
if (a == null && b == null)
|
||||
return true;
|
||||
|
||||
@@ -12,12 +12,31 @@ namespace Flax.Build
|
||||
partial class Builder
|
||||
{
|
||||
private static RulesAssembly _rules;
|
||||
private static Type[] _buildTypes;
|
||||
|
||||
/// <summary>
|
||||
/// The build configuration files postfix.
|
||||
/// </summary>
|
||||
public static string BuildFilesPostfix = ".Build.cs";
|
||||
|
||||
/// <summary>
|
||||
/// The cached list of types from Flax.Build assembly. Reused by other build tool utilities to improve performance.
|
||||
/// </summary>
|
||||
internal static Type[] BuildTypes
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_buildTypes == null)
|
||||
{
|
||||
using (new ProfileEventScope("CacheBuildTypes"))
|
||||
{
|
||||
_buildTypes = typeof(Program).Assembly.GetTypes();
|
||||
}
|
||||
}
|
||||
return _buildTypes;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The rules assembly data.
|
||||
/// </summary>
|
||||
@@ -128,7 +147,7 @@ namespace Flax.Build
|
||||
|
||||
using (new ProfileEventScope("InitInBuildPlugins"))
|
||||
{
|
||||
foreach (var type in typeof(Program).Assembly.GetTypes().Where(x => !x.IsAbstract && x.IsSubclassOf(typeof(Plugin))))
|
||||
foreach (var type in BuildTypes.Where(x => x.IsClass && !x.IsAbstract && x.IsSubclassOf(typeof(Plugin))))
|
||||
{
|
||||
var plugin = (Plugin)Activator.CreateInstance(type);
|
||||
plugin.Init();
|
||||
@@ -176,67 +195,74 @@ namespace Flax.Build
|
||||
|
||||
// Prepare targets and modules objects
|
||||
Type[] types;
|
||||
Target[] targetObjects;
|
||||
Module[] moduleObjects;
|
||||
Plugin[] pluginObjects;
|
||||
var targetObjects = new List<Target>(16);
|
||||
var moduleObjects = new List<Module>(256);
|
||||
var pluginObjects = new List<Plugin>();
|
||||
using (new ProfileEventScope("GetTypes"))
|
||||
{
|
||||
types = assembly.GetTypes();
|
||||
targetObjects = types.Where(x => !x.IsAbstract && x.IsSubclassOf(typeof(Target))).Select(type =>
|
||||
for (var i = 0; i < types.Length; i++)
|
||||
{
|
||||
var target = (Target)Activator.CreateInstance(type);
|
||||
|
||||
var targetFilename = target.Name + BuildFilesPostfix;
|
||||
target.FilePath = files.FirstOrDefault(path => string.Equals(Path.GetFileName(path), targetFilename, StringComparison.OrdinalIgnoreCase));
|
||||
if (target.FilePath == null)
|
||||
var type = types[i];
|
||||
if (!type.IsClass || type.IsAbstract)
|
||||
continue;
|
||||
if (type.IsSubclassOf(typeof(Target)))
|
||||
{
|
||||
targetFilename = target.Name + "Target" + BuildFilesPostfix;
|
||||
var target = (Target)Activator.CreateInstance(type);
|
||||
|
||||
var targetFilename = target.Name + BuildFilesPostfix;
|
||||
target.FilePath = files.FirstOrDefault(path => string.Equals(Path.GetFileName(path), targetFilename, StringComparison.OrdinalIgnoreCase));
|
||||
if (target.FilePath == null)
|
||||
{
|
||||
if (target.Name.EndsWith("Target"))
|
||||
{
|
||||
targetFilename = target.Name.Substring(0, target.Name.Length - "Target".Length) + BuildFilesPostfix;
|
||||
target.FilePath = files.FirstOrDefault(path => string.Equals(Path.GetFileName(path), targetFilename, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
targetFilename = target.Name + "Target" + BuildFilesPostfix;
|
||||
target.FilePath = files.FirstOrDefault(path => string.Equals(Path.GetFileName(path), targetFilename, StringComparison.OrdinalIgnoreCase));
|
||||
if (target.FilePath == null)
|
||||
{
|
||||
throw new Exception(string.Format("Failed to find source file path for {0}", target));
|
||||
if (target.Name.EndsWith("Target"))
|
||||
{
|
||||
targetFilename = target.Name.Substring(0, target.Name.Length - "Target".Length) + BuildFilesPostfix;
|
||||
target.FilePath = files.FirstOrDefault(path => string.Equals(Path.GetFileName(path), targetFilename, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
if (target.FilePath == null)
|
||||
{
|
||||
throw new Exception(string.Format("Failed to find source file path for {0}", target));
|
||||
}
|
||||
}
|
||||
}
|
||||
target.FolderPath = Path.GetDirectoryName(target.FilePath);
|
||||
target.Init();
|
||||
targetObjects.Add(target);
|
||||
}
|
||||
target.FolderPath = Path.GetDirectoryName(target.FilePath);
|
||||
target.Init();
|
||||
return target;
|
||||
}).ToArray();
|
||||
moduleObjects = types.Where(x => !x.IsAbstract && x.IsSubclassOf(typeof(Module))).Select(type =>
|
||||
{
|
||||
var module = (Module)Activator.CreateInstance(type);
|
||||
|
||||
var moduleFilename = module.Name + BuildFilesPostfix;
|
||||
module.FilePath = files.FirstOrDefault(path => string.Equals(Path.GetFileName(path), moduleFilename, StringComparison.OrdinalIgnoreCase));
|
||||
if (module.FilePath == null)
|
||||
else if (type.IsSubclassOf(typeof(Module)))
|
||||
{
|
||||
moduleFilename = module.Name + "Module" + BuildFilesPostfix;
|
||||
var module = (Module)Activator.CreateInstance(type);
|
||||
|
||||
var moduleFilename = module.Name + BuildFilesPostfix;
|
||||
module.FilePath = files.FirstOrDefault(path => string.Equals(Path.GetFileName(path), moduleFilename, StringComparison.OrdinalIgnoreCase));
|
||||
if (module.FilePath == null)
|
||||
{
|
||||
throw new Exception(string.Format("Failed to find source file path for {0}", module));
|
||||
moduleFilename = module.Name + "Module" + BuildFilesPostfix;
|
||||
module.FilePath = files.FirstOrDefault(path => string.Equals(Path.GetFileName(path), moduleFilename, StringComparison.OrdinalIgnoreCase));
|
||||
if (module.FilePath == null)
|
||||
{
|
||||
throw new Exception(string.Format("Failed to find source file path for {0}", module));
|
||||
}
|
||||
}
|
||||
module.FolderPath = Path.GetDirectoryName(module.FilePath);
|
||||
module.Init();
|
||||
moduleObjects.Add(module);
|
||||
}
|
||||
module.FolderPath = Path.GetDirectoryName(module.FilePath);
|
||||
module.Init();
|
||||
return module;
|
||||
}).ToArray();
|
||||
pluginObjects = types.Where(x => !x.IsAbstract && x.IsSubclassOf(typeof(Plugin))).Select(type =>
|
||||
{
|
||||
var plugin = (Plugin)Activator.CreateInstance(type);
|
||||
plugin.Init();
|
||||
return plugin;
|
||||
}).ToArray();
|
||||
else if (type.IsSubclassOf(typeof(Plugin)))
|
||||
{
|
||||
var plugin = (Plugin)Activator.CreateInstance(type);
|
||||
|
||||
plugin.Init();
|
||||
pluginObjects.Add(plugin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_rules = new RulesAssembly(assembly, targetObjects, moduleObjects, pluginObjects);
|
||||
_rules = new RulesAssembly(assembly, targetObjects.ToArray(), moduleObjects.ToArray(), pluginObjects.ToArray());
|
||||
}
|
||||
|
||||
return _rules;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using Flax.Build.Graph;
|
||||
|
||||
@@ -27,12 +28,12 @@ namespace Flax.Build.BuildSystem.Graph
|
||||
/// <summary>
|
||||
/// The maximum amount of threads to be used for the parallel execution.
|
||||
/// </summary>
|
||||
public int ThreadCountMax = 1410;
|
||||
public int ThreadCountMax = Configuration.MaxConcurrency;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of threads to allocate per processor. Use it to allocate more threads for faster execution or use less to keep reduce CPU usage during build.
|
||||
/// </summary>
|
||||
public float ProcessorCountScale = 1.0f;
|
||||
public float ProcessorCountScale = Configuration.ConcurrencyProcessorScale;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int Execute(List<Task> tasks)
|
||||
@@ -178,7 +179,12 @@ namespace Flax.Build.BuildSystem.Graph
|
||||
|
||||
private int ExecuteTask(Task task)
|
||||
{
|
||||
ProcessStartInfo startInfo = new ProcessStartInfo
|
||||
string name = "Task";
|
||||
if (task.ProducedFiles != null && task.ProducedFiles.Count != 0)
|
||||
name = Path.GetFileName(task.ProducedFiles[0]);
|
||||
var profilerEvent = Profiling.Begin(name);
|
||||
|
||||
var startInfo = new ProcessStartInfo
|
||||
{
|
||||
WorkingDirectory = task.WorkingDirectory,
|
||||
FileName = task.CommandPath,
|
||||
@@ -230,10 +236,13 @@ namespace Flax.Build.BuildSystem.Graph
|
||||
// Hang until process end
|
||||
process.WaitForExit();
|
||||
|
||||
Profiling.End(profilerEvent);
|
||||
return process.ExitCode;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Profiling.End(profilerEvent);
|
||||
|
||||
// Ensure to cleanup data
|
||||
process?.Close();
|
||||
}
|
||||
|
||||
@@ -111,5 +111,16 @@ namespace Flax.Build
|
||||
// By default deploy all C++ header files
|
||||
files.AddRange(Directory.GetFiles(FolderPath, "*.h", SearchOption.AllDirectories));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the file to the build sources if exists.
|
||||
/// </summary>
|
||||
/// <param name="options">The options.</param>
|
||||
/// <param name="path">The source file path.</param>
|
||||
protected void AddSourceFileIfExists(BuildOptions options, string path)
|
||||
{
|
||||
if (File.Exists(path))
|
||||
options.SourceFiles.Add(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,7 +330,12 @@ namespace Flax.Build
|
||||
}
|
||||
|
||||
// Collect all files to compile
|
||||
var cppFiles = moduleOptions.SourceFiles.Where(x => x.EndsWith(".cpp")).ToList();
|
||||
var cppFiles = new List<string>(moduleOptions.SourceFiles.Count / 2);
|
||||
for (int i = 0; i < moduleOptions.SourceFiles.Count; i++)
|
||||
{
|
||||
if (moduleOptions.SourceFiles[i].EndsWith(".cpp", StringComparison.OrdinalIgnoreCase))
|
||||
cppFiles.Add(moduleOptions.SourceFiles[i]);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(module.BinaryModuleName))
|
||||
{
|
||||
|
||||
@@ -56,6 +56,11 @@ namespace Flax.Build.NativeCpp
|
||||
/// </summary>
|
||||
public bool Optimization = false;
|
||||
|
||||
/// <summary>
|
||||
/// Enables the whole program optimization.
|
||||
/// </summary>
|
||||
public bool WholeProgramOptimization = false;
|
||||
|
||||
/// <summary>
|
||||
/// Enables functions level linking support.
|
||||
/// </summary>
|
||||
@@ -131,6 +136,7 @@ namespace Flax.Build.NativeCpp
|
||||
RuntimeTypeInfo = RuntimeTypeInfo,
|
||||
Inlining = Inlining,
|
||||
Optimization = Optimization,
|
||||
WholeProgramOptimization = WholeProgramOptimization,
|
||||
FunctionLevelLinking = FunctionLevelLinking,
|
||||
DebugInformation = DebugInformation,
|
||||
UseDebugCRT = UseDebugCRT,
|
||||
|
||||
@@ -157,7 +157,7 @@ namespace Flax.Build
|
||||
if (_platforms == null)
|
||||
{
|
||||
using (new ProfileEventScope("GetPlatforms"))
|
||||
_platforms = typeof(Platform).Assembly.GetTypes().Where(x => !x.IsAbstract && x.IsSubclassOf(typeof(Platform))).Select(Activator.CreateInstance).Cast<Platform>().ToArray();
|
||||
_platforms = Builder.BuildTypes.Where(x => x.IsClass && !x.IsAbstract && x.IsSubclassOf(typeof(Platform))).Select(Activator.CreateInstance).Cast<Platform>().ToArray();
|
||||
}
|
||||
|
||||
foreach (var platform in _platforms)
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace Flax.Build.Plugins
|
||||
|
||||
private void OnGenerateCppScriptWrapperFunction(Builder.BuildData buildData, ClassInfo classInfo, FunctionInfo functionInfo, int scriptVTableSize, int scriptVTableIndex, StringBuilder contents)
|
||||
{
|
||||
// Generate C++ wrapper function to invoke Visual Script instead of overriden native function (with support for base method callback)
|
||||
// Generate C++ wrapper function to invoke Visual Script instead of overridden native function (with support for base method callback)
|
||||
|
||||
BindingsGenerator.CppIncludeFiles.Add("Engine/Content/Assets/VisualScript.h");
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace Flax.Build.Plugins
|
||||
contents.AppendLine(" if (IsDuringWrapperCall)");
|
||||
contents.AppendLine(" {");
|
||||
contents.AppendLine(" // Prevent stack overflow by calling base method");
|
||||
contents.AppendLine(" const auto scriptVTableBase = object->GetType().Class.ScriptVTableBase;");
|
||||
contents.AppendLine(" const auto scriptVTableBase = object->GetType().Script.ScriptVTableBase;");
|
||||
contents.Append($" return (object->**({functionInfo.UniqueName}_Signature*)&scriptVTableBase[{scriptVTableIndex} + 2])(");
|
||||
separator = false;
|
||||
for (var i = 0; i < functionInfo.Parameters.Count; i++)
|
||||
@@ -61,7 +61,7 @@ namespace Flax.Build.Plugins
|
||||
}
|
||||
contents.AppendLine(");");
|
||||
contents.AppendLine(" }");
|
||||
contents.AppendLine(" auto scriptVTable = (VisualScript::Method**)object->GetType().Class.ScriptVTable;");
|
||||
contents.AppendLine(" auto scriptVTable = (VisualScript::Method**)object->GetType().Script.ScriptVTable;");
|
||||
contents.AppendLine($" ASSERT(scriptVTable && scriptVTable[{scriptVTableIndex}]);");
|
||||
|
||||
if (functionInfo.Parameters.Count != 0)
|
||||
|
||||
@@ -4,6 +4,7 @@ using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace Flax.Build
|
||||
{
|
||||
@@ -12,7 +13,7 @@ namespace Flax.Build
|
||||
/// </summary>
|
||||
public class ProfileEventScope : IDisposable
|
||||
{
|
||||
private int _id;
|
||||
private readonly int _id;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ProfileEventScope"/> class.
|
||||
@@ -61,6 +62,11 @@ namespace Flax.Build
|
||||
/// The event call depth (for parent-children events evaluation).
|
||||
/// </summary>
|
||||
public int Depth;
|
||||
|
||||
/// <summary>
|
||||
/// The calling thread id.
|
||||
/// </summary>
|
||||
public int ThreadId;
|
||||
}
|
||||
|
||||
private static int _depth;
|
||||
@@ -78,6 +84,7 @@ namespace Flax.Build
|
||||
e.StartTime = DateTime.Now;
|
||||
e.Duration = TimeSpan.Zero;
|
||||
e.Depth = _depth++;
|
||||
e.ThreadId = Thread.CurrentThread.ManagedThreadId;
|
||||
_events.Add(e);
|
||||
return _events.Count - 1;
|
||||
}
|
||||
@@ -147,8 +154,8 @@ namespace Flax.Build
|
||||
for (int i = 0; i < _events.Count; i++)
|
||||
{
|
||||
var e = _events[i];
|
||||
|
||||
contents.Append($"{{ \"pid\":1, \"tid\":1, \"ts\":{(int)((e.StartTime - startTime).TotalMilliseconds * 1000.0)}, \"dur\":{(int)(e.Duration.TotalMilliseconds * 1000.0)}, \"ph\":\"X\", \"name\":\"{e.Name}\", \"args\":{{ \"startTime\":\"{e.StartTime.ToShortTimeString()}\" }} }}\n");
|
||||
|
||||
contents.Append($"{{ \"pid\":{e.ThreadId}, \"tid\":1, \"ts\":{(int)((e.StartTime - startTime).TotalMilliseconds * 1000.0)}, \"dur\":{(int)(e.Duration.TotalMilliseconds * 1000.0)}, \"ph\":\"X\", \"name\":\"{e.Name}\", \"args\":{{ \"startTime\":\"{e.StartTime.ToShortTimeString()}\" }} }}\n");
|
||||
|
||||
if (i + 1 != _events.Count)
|
||||
contents.Append(",");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved.
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -68,7 +68,7 @@ namespace Flax.Build
|
||||
using (new ProfileEventScope("GetSdks"))
|
||||
{
|
||||
_sdks = new Dictionary<string, Sdk>();
|
||||
var types = typeof(Sdk).Assembly.GetTypes().Where(x => !x.IsAbstract && x.IsSubclassOf(typeof(Sdk)));
|
||||
var types = Builder.BuildTypes.Where(x => !x.IsAbstract && x.IsSubclassOf(typeof(Sdk)));
|
||||
foreach (var type in types)
|
||||
{
|
||||
object instance = null;
|
||||
|
||||
@@ -225,7 +225,7 @@ namespace Flax.Build
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Setups the target building environment (native C++). Allows to modify compiler and linker options. Options applied here are used by all modules included into this target (can be overriden per module).
|
||||
/// Setups the target building environment (native C++). Allows to modify compiler and linker options. Options applied here are used by all modules included into this target (can be overridden per module).
|
||||
/// </summary>
|
||||
/// <param name="options">The build options.</param>
|
||||
public virtual void SetupTargetEnvironment(BuildOptions options)
|
||||
@@ -256,6 +256,7 @@ namespace Flax.Build
|
||||
options.CompileEnv.IntrinsicFunctions = false;
|
||||
options.CompileEnv.BufferSecurityCheck = true;
|
||||
options.CompileEnv.Inlining = false;
|
||||
options.CompileEnv.WholeProgramOptimization = false;
|
||||
|
||||
options.LinkEnv.DebugInformation = true;
|
||||
options.LinkEnv.LinkTimeCodeGeneration = false;
|
||||
@@ -273,11 +274,11 @@ namespace Flax.Build
|
||||
options.CompileEnv.IntrinsicFunctions = true;
|
||||
options.CompileEnv.BufferSecurityCheck = true;
|
||||
options.CompileEnv.Inlining = true;
|
||||
//options.CompileEnv.WholeProgramOptimization = true;
|
||||
options.CompileEnv.WholeProgramOptimization = false;
|
||||
|
||||
options.LinkEnv.DebugInformation = true;
|
||||
options.LinkEnv.LinkTimeCodeGeneration = true;
|
||||
options.LinkEnv.UseIncrementalLinking = false;
|
||||
options.LinkEnv.LinkTimeCodeGeneration = false;
|
||||
options.LinkEnv.UseIncrementalLinking = true;
|
||||
options.LinkEnv.Optimization = true;
|
||||
break;
|
||||
case TargetConfiguration.Release:
|
||||
@@ -291,7 +292,7 @@ namespace Flax.Build
|
||||
options.CompileEnv.IntrinsicFunctions = true;
|
||||
options.CompileEnv.BufferSecurityCheck = false;
|
||||
options.CompileEnv.Inlining = true;
|
||||
//options.CompileEnv.WholeProgramOptimization = true;
|
||||
options.CompileEnv.WholeProgramOptimization = true;
|
||||
|
||||
options.LinkEnv.DebugInformation = false;
|
||||
options.LinkEnv.LinkTimeCodeGeneration = true;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Flax.Build
|
||||
{
|
||||
/// <summary>
|
||||
@@ -133,6 +135,18 @@ namespace Flax.Build
|
||||
[CommandLine("logfile", "<path>", "The log file path relative to the working directory. Set to empty to disable it/")]
|
||||
public static string LogFile = "Cache/Intermediate/Log.txt";
|
||||
|
||||
/// <summary>
|
||||
/// The maximum allowed concurrency for a build system (maximum active worker threads count).
|
||||
/// </summary>
|
||||
[CommandLine("maxConcurrency", "<threads>", "The maximum allowed concurrency for a build system (maximum active worker threads count).")]
|
||||
public static int MaxConcurrency = 1410;
|
||||
|
||||
/// <summary>
|
||||
/// The concurrency scale for a build system that specifies how many worker threads allocate per-logical processor.
|
||||
/// </summary>
|
||||
[CommandLine("concurrencyProcessorScale", "<scale>", "The concurrency scale for a build system that specifies how many worker threads allocate per-logical processor.")]
|
||||
public static float ConcurrencyProcessorScale = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The output binaries folder path relative to the working directory.
|
||||
/// </summary>
|
||||
@@ -186,5 +200,10 @@ namespace Flax.Build
|
||||
/// </summary>
|
||||
[CommandLine("customProjectFormat", "<type>", "Generates code project files for a custom project format type. Valid only with -genproject option.")]
|
||||
public static string ProjectFormatCustom = null;
|
||||
|
||||
/// <summary>
|
||||
/// Custom configuration defines provided via command line for the build tool.
|
||||
/// </summary>
|
||||
public static List<string> CustomDefines = new List<string>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,7 +312,7 @@ namespace Flax.Deps.Dependencies
|
||||
}
|
||||
|
||||
// Get the source
|
||||
//CloneGitRepoSingleBranch(root, "https://github.com/NVIDIAGameWorks/PhysX.git", "4.1");
|
||||
CloneGitRepoSingleBranch(root, "https://github.com/NVIDIAGameWorks/PhysX.git", "4.1");
|
||||
|
||||
foreach (var platform in options.Platforms)
|
||||
{
|
||||
|
||||
@@ -258,9 +258,8 @@ namespace Flax.Deps.Dependencies
|
||||
"mono_type_normalize",
|
||||
};
|
||||
|
||||
private void BuildMsvc(BuildOptions options, TargetPlatform platform, TargetArchitecture architecture)
|
||||
private void BuildMsvc(BuildOptions options, TargetPlatform platform, TargetArchitecture architecture, string configuration = "Release")
|
||||
{
|
||||
var configuration = "Release";
|
||||
string buildPlatform;
|
||||
switch (architecture)
|
||||
{
|
||||
@@ -491,12 +490,13 @@ namespace Flax.Deps.Dependencies
|
||||
{
|
||||
case TargetPlatform.Windows:
|
||||
{
|
||||
BuildMsvc(options, platform, TargetArchitecture.x64);
|
||||
var configuration = "Release";
|
||||
BuildMsvc(options, platform, TargetArchitecture.x64, configuration);
|
||||
//BuildBcl(options, platform);
|
||||
|
||||
// Export header files
|
||||
Deploy.VCEnvironment.BuildSolution(Path.Combine(root, "msvc", "libmono-dynamic.vcxproj"), "Release", "x64");
|
||||
Deploy.VCEnvironment.BuildSolution(Path.Combine(root, "msvc", "build-install.vcxproj"), "Release", "x64");
|
||||
Deploy.VCEnvironment.BuildSolution(Path.Combine(root, "msvc", "libmono-dynamic.vcxproj"), configuration, "x64");
|
||||
Deploy.VCEnvironment.BuildSolution(Path.Combine(root, "msvc", "build-install.vcxproj"), configuration, "x64");
|
||||
|
||||
// Get exported mono methods to forward them in engine module (on Win32 platforms)
|
||||
GetMonoExports(options);
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace Flax.Deps
|
||||
}
|
||||
|
||||
// Get all deps
|
||||
var dependencies = typeof(DepsBuilder).Assembly.GetTypes().Where(x => x.IsSubclassOf(typeof(Dependency))).Select(Activator.CreateInstance).Cast<Dependency>().ToArray();
|
||||
var dependencies = Builder.BuildTypes.Where(x => x.IsSubclassOf(typeof(Dependency))).Select(Activator.CreateInstance).Cast<Dependency>().ToArray();
|
||||
if (dependencies.Length == 0)
|
||||
Log.Warning("No dependencies found!");
|
||||
for (var i = 0; i < dependencies.Length; i++)
|
||||
|
||||
@@ -64,17 +64,20 @@
|
||||
<ItemGroup>
|
||||
<Compile Include="Bindings\ApiTypeInfo.cs" />
|
||||
<Compile Include="Bindings\BindingsGenerator.Api.cs" />
|
||||
<Compile Include="Bindings\BindingsGenerator.Cache.cs" />
|
||||
<Compile Include="Bindings\BindingsGenerator.Cpp.cs" />
|
||||
<Compile Include="Bindings\BindingsGenerator.cs" />
|
||||
<Compile Include="Bindings\BindingsGenerator.CSharp.cs" />
|
||||
<Compile Include="Bindings\BindingsGenerator.Parsing.cs" />
|
||||
<Compile Include="Bindings\ClassInfo.cs" />
|
||||
<Compile Include="Bindings\ClassStructInfo.cs" />
|
||||
<Compile Include="Bindings\EnumInfo.cs" />
|
||||
<Compile Include="Bindings\EventInfo.cs" />
|
||||
<Compile Include="Bindings\FieldInfo.cs" />
|
||||
<Compile Include="Bindings\FileInfo.cs" />
|
||||
<Compile Include="Bindings\InheritanceInfo.cs" />
|
||||
<Compile Include="Bindings\InjectCppCodeInfo.cs" />
|
||||
<Compile Include="Bindings\InterfaceInfo.cs" />
|
||||
<Compile Include="Bindings\LangType.cs" />
|
||||
<Compile Include="Bindings\MemberInfo.cs" />
|
||||
<Compile Include="Bindings\FunctionInfo.cs" />
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace Flax.Build
|
||||
var path = Path.GetDirectoryName(Configuration.LogFile);
|
||||
if (!string.IsNullOrEmpty(path) && !Directory.Exists(path))
|
||||
Directory.CreateDirectory(path);
|
||||
_logFile = new FileStream(Configuration.LogFile, FileMode.Create);
|
||||
_logFile = new FileStream(Configuration.LogFile, FileMode.Create, FileAccess.Write, FileShare.Read);
|
||||
_logFileWriter = new StreamWriter(_logFile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved.
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved.
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
@@ -467,8 +467,11 @@ namespace Flax.Build.Platforms
|
||||
// Frame-Pointer Omission
|
||||
commonArgs.Add("/Oy");
|
||||
|
||||
// Whole Program Optimization
|
||||
commonArgs.Add("/GL");
|
||||
if (compileEnvironment.WholeProgramOptimization)
|
||||
{
|
||||
// Whole Program Optimization
|
||||
commonArgs.Add("/GL");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -721,7 +724,7 @@ namespace Flax.Build.Platforms
|
||||
args.Add("/PDBALTPATH:%_PDB%");
|
||||
|
||||
// Optimize
|
||||
if (linkEnvironment.Optimization)
|
||||
if (linkEnvironment.Optimization && !linkEnvironment.UseIncrementalLinking)
|
||||
{
|
||||
// Generate an EXE checksum
|
||||
args.Add("/RELEASE");
|
||||
|
||||
@@ -32,6 +32,16 @@ namespace Flax.Build
|
||||
{
|
||||
// Setup
|
||||
CommandLine.Configure(typeof(Configuration));
|
||||
foreach (var option in CommandLine.GetOptions())
|
||||
{
|
||||
if (option.Name.Length > 1 && option.Name[0] == 'D')
|
||||
{
|
||||
var define = option.Name.Substring(1);
|
||||
if (!string.IsNullOrEmpty(option.Value))
|
||||
define += "=" + option.Value;
|
||||
Configuration.CustomDefines.Add(define);
|
||||
}
|
||||
}
|
||||
if (Configuration.CurrentDirectory != null)
|
||||
Environment.CurrentDirectory = Configuration.CurrentDirectory;
|
||||
Globals.Root = Directory.GetCurrentDirectory();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved.
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -460,6 +460,24 @@ namespace Flax.Build
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Skips all tokens until the tokenizer steps into token of given type (and it is also skipped, so, NextToken will give the next token).
|
||||
/// </summary>
|
||||
/// <param name="tokenType">The expected token type.</param>
|
||||
/// <param name="context">The output contents of the skipped tokens.</param>
|
||||
/// <param name="includeWhitespaces">When false, all white-space tokens will be ignored.</param>
|
||||
public void SkipUntil(TokenType tokenType, out string context, bool includeWhitespaces)
|
||||
{
|
||||
context = string.Empty;
|
||||
while (NextToken(true).Type != tokenType)
|
||||
{
|
||||
var token = CurrentToken;
|
||||
if (!includeWhitespaces && (token.Type == TokenType.Newline || token.Type == TokenType.Whitespace))
|
||||
continue;
|
||||
context += token.Value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the <see cref="Tokenizer"/>.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved.
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Flax.Build
|
||||
/// </summary>
|
||||
/// <param name="str">The input string.</param>
|
||||
/// <returns>The file size text.</returns>
|
||||
internal static uint GetHashCode(string str)
|
||||
public static uint GetHashCode(string str)
|
||||
{
|
||||
uint hash = 5381;
|
||||
if (str != null)
|
||||
@@ -33,6 +33,16 @@ namespace Flax.Build
|
||||
return hash;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the empty array of the given type (shared one).
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type.</typeparam>
|
||||
/// <returns>The empty array object.</returns>
|
||||
public static T[] GetEmptyArray<T>()
|
||||
{
|
||||
return Enumerable.Empty<T>() as T[];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size of the file as a readable string.
|
||||
/// </summary>
|
||||
|
||||
@@ -75,14 +75,14 @@ namespace Flax.Stats
|
||||
/// <summary>
|
||||
/// Gets total amount of memory used by that node and all child nodes
|
||||
/// </summary>
|
||||
public long TotaSizeOnDisk
|
||||
public long TotalSizeOnDisk
|
||||
{
|
||||
get
|
||||
{
|
||||
long result = SizeOnDisk;
|
||||
for (int i = 0; i < Children.Length; i++)
|
||||
{
|
||||
result += Children[i].TotaSizeOnDisk;
|
||||
result += Children[i].TotalSizeOnDisk;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -153,15 +153,15 @@ namespace Flax.Stats
|
||||
/// <summary>
|
||||
/// Gets total amount of lines of code per language
|
||||
/// </summary>
|
||||
/// <param name="languge">Language</param>
|
||||
/// <param name="language">Language</param>
|
||||
/// <returns>Result amount of lines</returns>
|
||||
public long GetTotalLinesOfCode(Languages languge)
|
||||
public long GetTotalLinesOfCode(Languages language)
|
||||
{
|
||||
long result = 0;
|
||||
result += LinesOfCode[(int)languge];
|
||||
result += LinesOfCode[(int)language];
|
||||
for (int i = 0; i < Children.Length; i++)
|
||||
{
|
||||
result += Children[i].GetTotalLinesOfCode(languge);
|
||||
result += Children[i].GetTotalLinesOfCode(language);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -270,9 +270,9 @@ namespace Flax.Stats
|
||||
|
||||
public void CleanupDirectories()
|
||||
{
|
||||
var chld = Children.ToList();
|
||||
chld.RemoveAll(e => ignoredFolders.Contains(e.ShortName.ToLower()));
|
||||
Children = chld.ToArray();
|
||||
var child = Children.ToList();
|
||||
child.RemoveAll(e => ignoredFolders.Contains(e.ShortName.ToLower()));
|
||||
Children = child.ToArray();
|
||||
|
||||
foreach (var a in Children)
|
||||
{
|
||||
|
||||
@@ -103,7 +103,7 @@ namespace Flax.Stats
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write string in UTF-8 encoding to the stream and ofset data
|
||||
/// Write string in UTF-8 encoding to the stream and offset data
|
||||
/// </summary>
|
||||
/// <param name="fs">File stream</param>
|
||||
/// <param name="data">Data to write</param>
|
||||
@@ -292,7 +292,7 @@ namespace Flax.Stats
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write arry of Guids to the stream
|
||||
/// Write array of Guids to the stream
|
||||
/// </summary>
|
||||
/// <param name="fs">File stream</param>
|
||||
/// <param name="val">Value to write</param>
|
||||
|
||||
@@ -143,7 +143,29 @@ namespace FlaxEngine.Tests
|
||||
Transform ab = a.LocalToWorld(b);
|
||||
Transform ba = a.WorldToLocal(ab);
|
||||
|
||||
Assert.IsTrue(Transform.NearEqual(ref b, ref ba, 0.00001f));
|
||||
Assert.IsTrue(Transform.NearEqual(ref b, ref ba, 0.00001f), $"Got: {b} but expected {ba}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test conversions between transform local/world space
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAddSubtract()
|
||||
{
|
||||
var rand = new Random(10);
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
Transform a = new Transform(rand.NextVector3(), Quaternion.Euler(i * 10, 0, i), rand.NextVector3() * 10.0f);
|
||||
Transform b = new Transform(rand.NextVector3(), Quaternion.Euler(i, 1, 22), rand.NextVector3() * 0.3f);
|
||||
|
||||
Transform ab = a + b;
|
||||
Transform newA = ab - b;
|
||||
Assert.IsTrue(Transform.NearEqual(ref a, ref newA, 0.00001f), $"Got: {newA} but expected {a}");
|
||||
|
||||
Transform ba = b + a;
|
||||
Transform newB = ba - a;
|
||||
Assert.IsTrue(Transform.NearEqual(ref b, ref newB, 0.00001f), $"Got: {newB} but expected {b}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user