350 lines
12 KiB
C#
350 lines
12 KiB
C#
// Copyright (c) 2012-2024 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>();
|
|
private const int CacheVersion = 23;
|
|
|
|
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, Dictionary<string, string> map)
|
|
{
|
|
if (map != null)
|
|
{
|
|
writer.Write(map.Count);
|
|
foreach (var e in map)
|
|
{
|
|
writer.Write(e.Key);
|
|
writer.Write(e.Value);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
writer.Write(0);
|
|
}
|
|
}
|
|
|
|
internal static void Write(BinaryWriter writer, HashSet<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(BinaryWriter writer, List<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(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, List<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 void Write<T>(BinaryWriter writer, T[] list) where T : IBindingsCache
|
|
{
|
|
if (list != null)
|
|
{
|
|
var count = list.Length;
|
|
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 Dictionary<string, string> Read(BinaryReader reader, Dictionary<string, string> map)
|
|
{
|
|
var count = reader.ReadInt32();
|
|
if (count != 0)
|
|
{
|
|
map = new Dictionary<string, string>();
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
var key = reader.ReadString();
|
|
var value = reader.ReadString();
|
|
map.Add(key, value);
|
|
}
|
|
}
|
|
return map;
|
|
}
|
|
|
|
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)
|
|
{
|
|
if (!Directory.Exists(moduleOptions.IntermediateFolder))
|
|
return;
|
|
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(CacheVersion);
|
|
writer.Write(FileCache.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(FileCache.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 (!FileCache.Exists(path))
|
|
return false;
|
|
try
|
|
{
|
|
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 != CacheVersion)
|
|
return false;
|
|
if (FileCache.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 (FileCache.GetLastWriteTime(headerFile).Ticks > reader.ReadInt64())
|
|
return false;
|
|
}
|
|
|
|
// Info
|
|
var newModuleInfo = new ModuleInfo
|
|
{
|
|
Module = moduleInfo.Module,
|
|
Name = moduleInfo.Name,
|
|
Namespace = moduleInfo.Namespace,
|
|
IsFromCache = true,
|
|
};
|
|
newModuleInfo.Read(reader);
|
|
|
|
// Skip parsing and use data loaded from cache
|
|
moduleInfo = newModuleInfo;
|
|
return true;
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// Skip loading cache
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|