Merge pull request #51191 from neikeq/mono-ios-aot-cache
C#+iOS: Cache AOT compilater output
This commit is contained in:
commit
7a7de3ce12
|
@ -249,55 +249,72 @@ namespace GodotTools.Export
|
|||
|
||||
var aotObjFilePaths = new List<string>(assemblies.Count);
|
||||
|
||||
foreach (var assembly in assemblies)
|
||||
string compilerDirPath = Path.Combine(GodotSharpDirs.DataEditorToolsDir, "aot-compilers",
|
||||
$"{OS.Platforms.iOS}-arm64");
|
||||
string crossCompiler = FindCrossCompiler(compilerDirPath);
|
||||
|
||||
string aotCacheDir = Path.Combine(ProjectSettings.GlobalizePath(GodotSharpDirs.ResTempDir),
|
||||
"obj", isDebug ? "ExportDebug" : "ExportRelease", "godot-aot-cache");
|
||||
|
||||
if (!Directory.Exists(aotCacheDir))
|
||||
Directory.CreateDirectory(aotCacheDir);
|
||||
|
||||
var aotCache = new AotCache(Path.Combine(aotCacheDir, "cache.json"));
|
||||
|
||||
try
|
||||
{
|
||||
string assemblyName = assembly.Key;
|
||||
string assemblyPath = assembly.Value;
|
||||
|
||||
string asmFileName = assemblyName + ".dll.S";
|
||||
string objFileName = assemblyName + ".dll.o";
|
||||
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
string asmFilePath = Path.Combine(aotTempDir, asmFileName);
|
||||
string assemblyName = assembly.Key;
|
||||
string assemblyPath = assembly.Value;
|
||||
|
||||
var compilerArgs = GetAotCompilerArgs(OS.Platforms.iOS, isDebug, "arm64", aotOpts, assemblyPath, asmFilePath);
|
||||
string asmFilePath = Path.Combine(aotCacheDir, assemblyName + ".dll.S");
|
||||
string objFilePath = Path.Combine(aotCacheDir, assemblyName + ".dll.o");
|
||||
|
||||
string compilerDirPath = Path.Combine(GodotSharpDirs.DataEditorToolsDir, "aot-compilers", $"{OS.Platforms.iOS}-arm64");
|
||||
|
||||
ExecuteCompiler(FindCrossCompiler(compilerDirPath), compilerArgs, bclDir);
|
||||
|
||||
// Assembling
|
||||
const string iOSPlatformName = "iPhoneOS";
|
||||
const string versionMin = "10.0"; // TODO: Turn this hard-coded version into an exporter setting
|
||||
string iOSSdkPath = Path.Combine(XcodeHelper.XcodePath,
|
||||
$"Contents/Developer/Platforms/{iOSPlatformName}.platform/Developer/SDKs/{iOSPlatformName}.sdk");
|
||||
|
||||
string objFilePath = Path.Combine(aotTempDir, objFileName);
|
||||
|
||||
var clangArgs = new List<string>()
|
||||
aotCache.RunCached(name: assemblyName, input: assemblyPath, output: objFilePath, () =>
|
||||
{
|
||||
"-isysroot", iOSSdkPath,
|
||||
"-Qunused-arguments",
|
||||
$"-miphoneos-version-min={versionMin}",
|
||||
"-arch", "arm64",
|
||||
"-c",
|
||||
"-o", objFilePath,
|
||||
"-x", "assembler"
|
||||
};
|
||||
Console.WriteLine($"AOT compiler: Compiling '{assemblyName}'...");
|
||||
|
||||
if (isDebug)
|
||||
clangArgs.Add("-DDEBUG");
|
||||
var compilerArgs = GetAotCompilerArgs(OS.Platforms.iOS, isDebug,
|
||||
"arm64", aotOpts, assemblyPath, asmFilePath);
|
||||
|
||||
clangArgs.Add(asmFilePath);
|
||||
ExecuteCompiler(crossCompiler, compilerArgs, bclDir);
|
||||
|
||||
int clangExitCode = OS.ExecuteCommand(XcodeHelper.FindXcodeTool("clang"), clangArgs);
|
||||
if (clangExitCode != 0)
|
||||
throw new Exception($"Command 'clang' exited with code: {clangExitCode}");
|
||||
// Assembling
|
||||
const string iOSPlatformName = "iPhoneOS";
|
||||
const string versionMin = "10.0"; // TODO: Turn this hard-coded version into an exporter setting
|
||||
string iOSSdkPath = Path.Combine(XcodeHelper.XcodePath,
|
||||
$"Contents/Developer/Platforms/{iOSPlatformName}.platform/Developer/SDKs/{iOSPlatformName}.sdk");
|
||||
|
||||
var clangArgs = new List<string>()
|
||||
{
|
||||
"-isysroot", iOSSdkPath,
|
||||
"-Qunused-arguments",
|
||||
$"-miphoneos-version-min={versionMin}",
|
||||
"-arch", "arm64",
|
||||
"-c",
|
||||
"-o", objFilePath,
|
||||
"-x", "assembler"
|
||||
};
|
||||
|
||||
if (isDebug)
|
||||
clangArgs.Add("-DDEBUG");
|
||||
|
||||
clangArgs.Add(asmFilePath);
|
||||
|
||||
int clangExitCode = OS.ExecuteCommand(XcodeHelper.FindXcodeTool("clang"), clangArgs);
|
||||
if (clangExitCode != 0)
|
||||
throw new Exception($"Command 'clang' exited with code: {clangExitCode}");
|
||||
});
|
||||
|
||||
aotObjFilePaths.Add(objFilePath);
|
||||
}
|
||||
|
||||
aotModuleInfoSymbols.Add($"mono_aot_module_{AssemblyNameToAotSymbol(assemblyName)}_info");
|
||||
aotModuleInfoSymbols.Add($"mono_aot_module_{AssemblyNameToAotSymbol(assemblyName)}_info");
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
aotCache.SaveCache();
|
||||
}
|
||||
|
||||
RunAr(aotObjFilePaths, libAotFilePath);
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace GodotTools.Export
|
||||
{
|
||||
public class AotCache
|
||||
{
|
||||
private readonly string _cacheFilePath;
|
||||
private readonly Cache _cache = new Cache();
|
||||
private bool _hasUnsavedChanges = false;
|
||||
|
||||
public AotCache(string cacheFilePath)
|
||||
{
|
||||
_cacheFilePath = cacheFilePath;
|
||||
|
||||
if (File.Exists(_cacheFilePath))
|
||||
LoadCache(_cacheFilePath, out _cache);
|
||||
}
|
||||
|
||||
private static byte[] ComputeSha256Checksum(string filePath)
|
||||
{
|
||||
using (var sha256 = SHA256.Create())
|
||||
{
|
||||
using (var streamReader = File.OpenRead(filePath))
|
||||
return sha256.ComputeHash(streamReader);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool CompareHashes(byte[] a, byte[] b)
|
||||
{
|
||||
if (a.Length != b.Length)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < a.Length; i++)
|
||||
{
|
||||
if (a[i] != b[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private class Cache
|
||||
{
|
||||
[JsonProperty("assemblies")]
|
||||
public Dictionary<string, CachedChecksums> Assemblies { get; set; } =
|
||||
new Dictionary<string, CachedChecksums>();
|
||||
}
|
||||
|
||||
private struct CachedChecksums
|
||||
{
|
||||
[JsonProperty("input_checksum")] public string InputChecksumBase64 { get; set; }
|
||||
[JsonProperty("output_checksum")] public string OutputChecksumBase64 { get; set; }
|
||||
}
|
||||
|
||||
private static void LoadCache(string cacheFilePath, out Cache cache)
|
||||
{
|
||||
using (var streamReader = new StreamReader(cacheFilePath, Encoding.UTF8))
|
||||
using (var jsonReader = new JsonTextReader(streamReader))
|
||||
{
|
||||
cache = new JsonSerializer().Deserialize<Cache>(jsonReader);
|
||||
}
|
||||
}
|
||||
|
||||
private static void SaveCache(string cacheFilePath, Cache cache)
|
||||
{
|
||||
using (var streamWriter = new StreamWriter(cacheFilePath, append: false, Encoding.UTF8))
|
||||
using (var jsonWriter = new JsonTextWriter(streamWriter))
|
||||
{
|
||||
new JsonSerializer().Serialize(jsonWriter, cache);
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryGetCachedChecksums(string name, out CachedChecksums cachedChecksums)
|
||||
=> _cache.Assemblies.TryGetValue(name, out cachedChecksums);
|
||||
|
||||
private void ChangeCache(string name, byte[] inputChecksum, byte[] outputChecksum)
|
||||
{
|
||||
_cache.Assemblies[name] = new CachedChecksums()
|
||||
{
|
||||
InputChecksumBase64 = Convert.ToBase64String(inputChecksum),
|
||||
OutputChecksumBase64 = Convert.ToBase64String(outputChecksum)
|
||||
};
|
||||
_hasUnsavedChanges = true;
|
||||
}
|
||||
|
||||
public void SaveCache()
|
||||
{
|
||||
if (!_hasUnsavedChanges)
|
||||
return;
|
||||
SaveCache(_cacheFilePath, _cache);
|
||||
_hasUnsavedChanges = false;
|
||||
}
|
||||
|
||||
private bool IsCached(string name, byte[] inputChecksum, string output)
|
||||
{
|
||||
if (!File.Exists(output))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryGetCachedChecksums(name, out var cachedChecksums))
|
||||
return false;
|
||||
|
||||
if (string.IsNullOrEmpty(cachedChecksums.InputChecksumBase64) ||
|
||||
string.IsNullOrEmpty(cachedChecksums.OutputChecksumBase64))
|
||||
return false;
|
||||
|
||||
var cachedInputChecksum = Convert.FromBase64String(cachedChecksums.InputChecksumBase64);
|
||||
|
||||
if (!CompareHashes(inputChecksum, cachedInputChecksum))
|
||||
return false;
|
||||
|
||||
var outputChecksum = ComputeSha256Checksum(output);
|
||||
var cachedOutputChecksum = Convert.FromBase64String(cachedChecksums.OutputChecksumBase64);
|
||||
|
||||
if (!CompareHashes(outputChecksum, cachedOutputChecksum))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void RunCached(string name, string input, string output, Action action)
|
||||
{
|
||||
var inputChecksum = ComputeSha256Checksum(input);
|
||||
|
||||
if (IsCached(name, inputChecksum, output))
|
||||
{
|
||||
Console.WriteLine($"AOT compiler cache: '{name}' already compiled.");
|
||||
return;
|
||||
}
|
||||
|
||||
action();
|
||||
|
||||
var outputChecksum = ComputeSha256Checksum(output);
|
||||
|
||||
ChangeCache(name, inputChecksum, outputChecksum);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue