diff --git a/misc/dist/iphone-mono-libs/libmono-ee-interp.xcframework/Info.plist b/misc/dist/iphone-mono-libs/libmono-ee-interp.xcframework/Info.plist new file mode 100644 index 00000000000..85b0b982098 --- /dev/null +++ b/misc/dist/iphone-mono-libs/libmono-ee-interp.xcframework/Info.plist @@ -0,0 +1,40 @@ + + + + + AvailableLibraries + + + LibraryIdentifier + ios-arm64 + LibraryPath + libmono-ee-interp.a + SupportedArchitectures + + arm64 + + SupportedPlatform + ios + + + LibraryIdentifier + ios-arm64_x86_64-simulator + LibraryPath + libmono-ee-interp.a + SupportedArchitectures + + arm64 + x86_64 + + SupportedPlatform + ios + SupportedPlatformVariant + simulator + + + CFBundlePackageType + XFWK + XCFrameworkFormatVersion + 1.0 + + diff --git a/misc/dist/iphone-mono-libs/libmono-ee-interp.xcframework/ios-arm64/empty b/misc/dist/iphone-mono-libs/libmono-ee-interp.xcframework/ios-arm64/empty new file mode 100644 index 00000000000..bd3e8943336 --- /dev/null +++ b/misc/dist/iphone-mono-libs/libmono-ee-interp.xcframework/ios-arm64/empty @@ -0,0 +1 @@ +Dummy file to make dylibs folder exported diff --git a/misc/dist/iphone-mono-libs/libmono-ee-interp.xcframework/ios-arm64_x86_64-simulator/empty b/misc/dist/iphone-mono-libs/libmono-ee-interp.xcframework/ios-arm64_x86_64-simulator/empty new file mode 100644 index 00000000000..bd3e8943336 --- /dev/null +++ b/misc/dist/iphone-mono-libs/libmono-ee-interp.xcframework/ios-arm64_x86_64-simulator/empty @@ -0,0 +1 @@ +Dummy file to make dylibs folder exported diff --git a/misc/dist/iphone-mono-libs/libmono-icall-table.xcframework/Info.plist b/misc/dist/iphone-mono-libs/libmono-icall-table.xcframework/Info.plist new file mode 100644 index 00000000000..feadce5e97d --- /dev/null +++ b/misc/dist/iphone-mono-libs/libmono-icall-table.xcframework/Info.plist @@ -0,0 +1,40 @@ + + + + + AvailableLibraries + + + LibraryIdentifier + ios-arm64 + LibraryPath + libmono-icall-table.a + SupportedArchitectures + + arm64 + + SupportedPlatform + ios + + + LibraryIdentifier + ios-arm64_x86_64-simulator + LibraryPath + libmono-icall-table.a + SupportedArchitectures + + arm64 + x86_64 + + SupportedPlatform + ios + SupportedPlatformVariant + simulator + + + CFBundlePackageType + XFWK + XCFrameworkFormatVersion + 1.0 + + diff --git a/misc/dist/iphone-mono-libs/libmono-icall-table.xcframework/ios-arm64/empty b/misc/dist/iphone-mono-libs/libmono-icall-table.xcframework/ios-arm64/empty new file mode 100644 index 00000000000..bd3e8943336 --- /dev/null +++ b/misc/dist/iphone-mono-libs/libmono-icall-table.xcframework/ios-arm64/empty @@ -0,0 +1 @@ +Dummy file to make dylibs folder exported diff --git a/misc/dist/iphone-mono-libs/libmono-icall-table.xcframework/ios-arm64_x86_64-simulator/empty b/misc/dist/iphone-mono-libs/libmono-icall-table.xcframework/ios-arm64_x86_64-simulator/empty new file mode 100644 index 00000000000..bd3e8943336 --- /dev/null +++ b/misc/dist/iphone-mono-libs/libmono-icall-table.xcframework/ios-arm64_x86_64-simulator/empty @@ -0,0 +1 @@ +Dummy file to make dylibs folder exported diff --git a/misc/dist/iphone-mono-libs/libmono-ilgen.xcframework/Info.plist b/misc/dist/iphone-mono-libs/libmono-ilgen.xcframework/Info.plist new file mode 100644 index 00000000000..95abbe7a94f --- /dev/null +++ b/misc/dist/iphone-mono-libs/libmono-ilgen.xcframework/Info.plist @@ -0,0 +1,40 @@ + + + + + AvailableLibraries + + + LibraryIdentifier + ios-arm64 + LibraryPath + libmono-ilgen.a + SupportedArchitectures + + arm64 + + SupportedPlatform + ios + + + LibraryIdentifier + ios-arm64_x86_64-simulator + LibraryPath + libmono-ilgen.a + SupportedArchitectures + + arm64 + x86_64 + + SupportedPlatform + ios + SupportedPlatformVariant + simulator + + + CFBundlePackageType + XFWK + XCFrameworkFormatVersion + 1.0 + + diff --git a/misc/dist/iphone-mono-libs/libmono-ilgen.xcframework/ios-arm64/empty b/misc/dist/iphone-mono-libs/libmono-ilgen.xcframework/ios-arm64/empty new file mode 100644 index 00000000000..bd3e8943336 --- /dev/null +++ b/misc/dist/iphone-mono-libs/libmono-ilgen.xcframework/ios-arm64/empty @@ -0,0 +1 @@ +Dummy file to make dylibs folder exported diff --git a/misc/dist/iphone-mono-libs/libmono-ilgen.xcframework/ios-arm64_x86_64-simulator/empty b/misc/dist/iphone-mono-libs/libmono-ilgen.xcframework/ios-arm64_x86_64-simulator/empty new file mode 100644 index 00000000000..bd3e8943336 --- /dev/null +++ b/misc/dist/iphone-mono-libs/libmono-ilgen.xcframework/ios-arm64_x86_64-simulator/empty @@ -0,0 +1 @@ +Dummy file to make dylibs folder exported diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs b/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs index 372cccc46ec..92aeb25e7ac 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs @@ -22,7 +22,12 @@ namespace GodotTools.Export public bool FullAot; private bool _useInterpreter; - public bool UseInterpreter { get => _useInterpreter && !LLVMOnly; set => _useInterpreter = value; } + + public bool UseInterpreter + { + get => _useInterpreter && !LLVMOnly; + set => _useInterpreter = value; + } public string[] ExtraAotOptions; public string[] ExtraOptimizerOptions; @@ -82,7 +87,6 @@ namespace GodotTools.Export public static void CompileAssembliesForAndroid(ExportPlugin exporter, bool isDebug, string[] abis, AotOptions aotOpts, string aotTempDir, IDictionary assemblies, string bclDir) { - foreach (var assembly in assemblies) { string assemblyName = assembly.Key; @@ -107,7 +111,7 @@ namespace GodotTools.Export ExecuteCompiler(FindCrossCompiler(compilerDirPath), compilerArgs, bclDir); // The Godot exporter expects us to pass the abi in the tags parameter - exporter.AddSharedObject(soFilePath, tags: new[] { abi }); + exporter.AddSharedObject(soFilePath, tags: new[] {abi}); } } } @@ -146,11 +150,101 @@ namespace GodotTools.Export public static void CompileAssembliesForiOS(ExportPlugin exporter, bool isDebug, string[] architectures, AotOptions aotOpts, string aotTempDir, IDictionary assemblies, string bclDir) { + void RunAr(IEnumerable objFilePaths, string outputFilePath) + { + var arArgs = new List() + { + "cr", + outputFilePath + }; + + foreach (string objFilePath in objFilePaths) + arArgs.Add(objFilePath); + + int arExitCode = OS.ExecuteCommand(XcodeHelper.FindXcodeTool("ar"), arArgs); + if (arExitCode != 0) + throw new Exception($"Command 'ar' exited with code: {arExitCode}"); + } + + void RunLipo(IEnumerable libFilePaths, string outputFilePath) + { + var lipoArgs = new List(); + lipoArgs.Add("-create"); + lipoArgs.AddRange(libFilePaths); + lipoArgs.Add("-output"); + lipoArgs.Add(outputFilePath); + + int lipoExitCode = OS.ExecuteCommand(XcodeHelper.FindXcodeTool("lipo"), lipoArgs); + if (lipoExitCode != 0) + throw new Exception($"Command 'lipo' exited with code: {lipoExitCode}"); + } + + void CreateDummyLibForSimulator(string name, string xcFrameworkPath = null) + { + xcFrameworkPath = xcFrameworkPath ?? MonoFrameworkFromTemplate(name); + string simulatorSubDir = Path.Combine(xcFrameworkPath, "ios-arm64_x86_64-simulator"); + + string libFilePath = Path.Combine(simulatorSubDir, name + ".a"); + + if (File.Exists(libFilePath)) + return; + + string CompileForArch(string arch) + { + string baseFilePath = Path.Combine(aotTempDir, $"{name}.{arch}"); + string sourceFilePath = baseFilePath + ".c"; + + string source = $"int _{AssemblyNameToAotSymbol(name)}() {{ return 0; }}\n"; + File.WriteAllText(sourceFilePath, source); + + const string iOSPlatformName = "iPhoneSimulator"; + const string versionMin = "10.0"; + string iOSSdkPath = Path.Combine(XcodeHelper.XcodePath, + $"Contents/Developer/Platforms/{iOSPlatformName}.platform/Developer/SDKs/{iOSPlatformName}.sdk"); + + string objFilePath = baseFilePath + ".o"; + + var clangArgs = new[] + { + "-isysroot", iOSSdkPath, + $"-miphonesimulator-version-min={versionMin}", + "-arch", arch, + "-c", + "-o", objFilePath, + sourceFilePath + }; + + int clangExitCode = OS.ExecuteCommand(XcodeHelper.FindXcodeTool("clang"), clangArgs); + if (clangExitCode != 0) + throw new Exception($"Command 'clang' exited with code: {clangExitCode}"); + + string arOutputFilePath = Path.Combine(aotTempDir, baseFilePath + ".a"); + RunAr(new[] {objFilePath}, arOutputFilePath); + + return arOutputFilePath; + } + + RunLipo(new[] {CompileForArch("arm64"), CompileForArch("x86_64")}, libFilePath); + } + + string projectAssemblyName = GodotSharpEditor.ProjectAssemblyName; + string libAotName = $"lib-aot-{projectAssemblyName}"; + + string libAotXcFrameworkPath = Path.Combine(aotTempDir, $"{libAotName}.xcframework"); + string libAotXcFrameworkDevicePath = Path.Combine(libAotXcFrameworkPath, "ios-arm64"); + string libAotXcFrameworkSimPath = Path.Combine(libAotXcFrameworkPath, "ios-arm64_x86_64-simulator"); + + Directory.CreateDirectory(libAotXcFrameworkPath); + Directory.CreateDirectory(libAotXcFrameworkDevicePath); + Directory.CreateDirectory(libAotXcFrameworkSimPath); + + string libAotFileName = $"{libAotName}.a"; + string libAotFilePath = Path.Combine(libAotXcFrameworkDevicePath, libAotFileName); + var cppCode = new StringBuilder(); var aotModuleInfoSymbols = new List(assemblies.Count); - // {arch: paths} - var objFilePathsForiOSArch = architectures.ToDictionary(arch => arch, arch => new List(assemblies.Count)); + var aotObjFilePaths = new List(assemblies.Count); foreach (var assembly in assemblies) { @@ -160,36 +254,29 @@ namespace GodotTools.Export string asmFileName = assemblyName + ".dll.S"; string objFileName = assemblyName + ".dll.o"; - foreach (string arch in architectures) { - string aotArchTempDir = Path.Combine(aotTempDir, arch); - string asmFilePath = Path.Combine(aotArchTempDir, asmFileName); + string asmFilePath = Path.Combine(aotTempDir, asmFileName); - var compilerArgs = GetAotCompilerArgs(OS.Platforms.iOS, isDebug, arch, aotOpts, assemblyPath, asmFilePath); + var compilerArgs = GetAotCompilerArgs(OS.Platforms.iOS, isDebug, "arm64", aotOpts, assemblyPath, asmFilePath); - // Make sure the output directory exists - Directory.CreateDirectory(aotArchTempDir); - - string compilerDirPath = Path.Combine(GodotSharpDirs.DataEditorToolsDir, "aot-compilers", $"{OS.Platforms.iOS}-{arch}"); + string compilerDirPath = Path.Combine(GodotSharpDirs.DataEditorToolsDir, "aot-compilers", $"{OS.Platforms.iOS}-arm64"); ExecuteCompiler(FindCrossCompiler(compilerDirPath), compilerArgs, bclDir); // Assembling - bool isSim = arch == "i386" || arch == "x86_64"; // Shouldn't really happen as we don't do AOT for the simulator - string versionMinName = isSim ? "iphonesimulator" : "iphoneos"; - string iOSPlatformName = isSim ? "iPhoneSimulator" : "iPhoneOS"; + 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"); + $"Contents/Developer/Platforms/{iOSPlatformName}.platform/Developer/SDKs/{iOSPlatformName}.sdk"); - string objFilePath = Path.Combine(aotArchTempDir, objFileName); + string objFilePath = Path.Combine(aotTempDir, objFileName); var clangArgs = new List() { "-isysroot", iOSSdkPath, "-Qunused-arguments", - $"-m{versionMinName}-version-min={versionMin}", - "-arch", arch, + $"-miphoneos-version-min={versionMin}", + "-arch", "arm64", "-c", "-o", objFilePath, "-x", "assembler" @@ -204,18 +291,67 @@ namespace GodotTools.Export if (clangExitCode != 0) throw new Exception($"Command 'clang' exited with code: {clangExitCode}"); - objFilePathsForiOSArch[arch].Add(objFilePath); + aotObjFilePaths.Add(objFilePath); } aotModuleInfoSymbols.Add($"mono_aot_module_{AssemblyNameToAotSymbol(assemblyName)}_info"); } - // Generate driver code - cppCode.AppendLine("#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__)"); - cppCode.AppendLine("#define IOS_DEVICE"); - cppCode.AppendLine("#endif"); + RunAr(aotObjFilePaths, libAotFilePath); - cppCode.AppendLine("#ifdef IOS_DEVICE"); + // Archive the AOT object files into a static library + + File.WriteAllText(Path.Combine(libAotXcFrameworkPath, "Info.plist"), + $@" + + + + AvailableLibraries + + + LibraryIdentifier + ios-arm64 + LibraryPath + {libAotFileName} + SupportedArchitectures + + arm64 + + SupportedPlatform + ios + + + LibraryIdentifier + ios-arm64_x86_64-simulator + LibraryPath + {libAotFileName} + SupportedArchitectures + + arm64 + x86_64 + + SupportedPlatform + ios + SupportedPlatformVariant + simulator + + + CFBundlePackageType + XFWK + XCFrameworkFormatVersion + 1.0 + + +"); + + // Add the fat AOT static library to the Xcode project + CreateDummyLibForSimulator(libAotName, libAotXcFrameworkPath); + exporter.AddIosProjectStaticLib(libAotXcFrameworkPath); + + // Generate driver code + cppCode.AppendLine("#include "); + + cppCode.AppendLine("#if !TARGET_OS_SIMULATOR"); cppCode.AppendLine("extern \"C\" {"); cppCode.AppendLine("// Mono API"); cppCode.AppendLine(@" @@ -280,59 +416,11 @@ MONO_AOT_MODE_LAST = 1000, cppCode.AppendLine("} // gd_mono_setup_aot"); cppCode.AppendLine("} // extern \"C\""); - cppCode.AppendLine("#endif // IOS_DEVICE"); + cppCode.AppendLine("#endif // !TARGET_OS_SIMULATOR"); // Add the driver code to the Xcode project exporter.AddIosCppCode(cppCode.ToString()); - // Archive the AOT object files into a static library - - var arFilePathsForAllArchs = new List(); - string projectAssemblyName = GodotSharpEditor.ProjectAssemblyName; - - foreach (var archPathsPair in objFilePathsForiOSArch) - { - string arch = archPathsPair.Key; - var objFilePaths = archPathsPair.Value; - - string arOutputFilePath = Path.Combine(aotTempDir, $"lib-aot-{projectAssemblyName}.{arch}.a"); - - var arArgs = new List() - { - "cr", - arOutputFilePath - }; - - foreach (string objFilePath in objFilePaths) - arArgs.Add(objFilePath); - - int arExitCode = OS.ExecuteCommand(XcodeHelper.FindXcodeTool("ar"), arArgs); - if (arExitCode != 0) - throw new Exception($"Command 'ar' exited with code: {arExitCode}"); - - arFilePathsForAllArchs.Add(arOutputFilePath); - } - - // It's lipo time - - string fatOutputFileName = $"lib-aot-{projectAssemblyName}.fat.a"; - string fatOutputFilePath = Path.Combine(aotTempDir, fatOutputFileName); - - var lipoArgs = new List(); - lipoArgs.Add("-create"); - lipoArgs.AddRange(arFilePathsForAllArchs); - lipoArgs.Add("-output"); - lipoArgs.Add(fatOutputFilePath); - - int lipoExitCode = OS.ExecuteCommand(XcodeHelper.FindXcodeTool("lipo"), lipoArgs); - if (lipoExitCode != 0) - throw new Exception($"Command 'lipo' exited with code: {lipoExitCode}"); - - // TODO: Add the AOT lib and interpreter libs as device only to suppress warnings when targeting the simulator - - // Add the fat AOT static library to the Xcode project - exporter.AddIosProjectStaticLib(fatOutputFilePath); - // Add the required Mono libraries to the Xcode project string MonoLibFile(string libFileName) => libFileName + ".iphone.fat.a"; @@ -351,9 +439,12 @@ MONO_AOT_MODE_LAST = 1000, if (aotOpts.UseInterpreter) { - exporter.AddIosProjectStaticLib(MonoLibFromTemplate("libmono-ee-interp")); - exporter.AddIosProjectStaticLib(MonoLibFromTemplate("libmono-icall-table")); - exporter.AddIosProjectStaticLib(MonoLibFromTemplate("libmono-ilgen")); + CreateDummyLibForSimulator("libmono-ee-interp"); + exporter.AddIosProjectStaticLib(MonoFrameworkFromTemplate("libmono-ee-interp")); + CreateDummyLibForSimulator("libmono-icall-table"); + exporter.AddIosProjectStaticLib(MonoFrameworkFromTemplate("libmono-icall-table")); + CreateDummyLibForSimulator("libmono-ilgen"); + exporter.AddIosProjectStaticLib(MonoFrameworkFromTemplate("libmono-ilgen")); } // TODO: Turn into an exporter option @@ -379,11 +470,8 @@ MONO_AOT_MODE_LAST = 1000, { var builder = new StringBuilder(); - foreach (var charByte in Encoding.UTF8.GetBytes(assemblyName)) - { - char @char = (char)charByte; - builder.Append(Char.IsLetterOrDigit(@char) || @char == '_' ? @char : '_'); - } + foreach (char @char in assemblyName) + builder.Append(char.IsLetterOrDigit(@char) || @char == '_' ? @char : '_'); return builder.ToString(); } @@ -582,27 +670,27 @@ MONO_AOT_MODE_LAST = 1000, { case OS.Platforms.Windows: case OS.Platforms.UWP: - { - string arch = bits == "64" ? "x86_64" : "i686"; - return $"windows-{arch}"; - } + { + string arch = bits == "64" ? "x86_64" : "i686"; + return $"windows-{arch}"; + } case OS.Platforms.OSX: - { - Debug.Assert(bits == null || bits == "64"); - string arch = "x86_64"; - return $"{platform}-{arch}"; - } + { + Debug.Assert(bits == null || bits == "64"); + string arch = "x86_64"; + return $"{platform}-{arch}"; + } case OS.Platforms.X11: case OS.Platforms.Server: - { - string arch = bits == "64" ? "x86_64" : "i686"; - return $"linux-{arch}"; - } + { + string arch = bits == "64" ? "x86_64" : "i686"; + return $"linux-{arch}"; + } case OS.Platforms.Haiku: - { - string arch = bits == "64" ? "x86_64" : "i686"; - return $"{platform}-{arch}"; - } + { + string arch = bits == "64" ? "x86_64" : "i686"; + return $"{platform}-{arch}"; + } default: throw new NotSupportedException($"Platform not supported: {platform}"); }