Add C# iOS support
This support is experimental and requires .NET 8 Known issues: - Requires macOS due to use of lipo and xcodebuild - arm64 simulator templates are not currently included in the official packaging
This commit is contained in:
parent
9215b03429
commit
ee9a735c26
@ -1,6 +1,6 @@
|
|||||||
# Prior to .NET Core, we supported these: ["windows", "macos", "linuxbsd", "android", "web", "ios"]
|
# Prior to .NET Core, we supported these: ["windows", "macos", "linuxbsd", "android", "web", "ios"]
|
||||||
# Eventually support for each them should be added back.
|
# Eventually support for each them should be added back.
|
||||||
supported_platforms = ["windows", "macos", "linuxbsd", "android"]
|
supported_platforms = ["windows", "macos", "linuxbsd", "android", "ios"]
|
||||||
|
|
||||||
|
|
||||||
def can_build(env, platform):
|
def can_build(env, platform):
|
||||||
|
@ -29,5 +29,7 @@
|
|||||||
<None Include="$(GodotSdkPackageVersionsFilePath)" Pack="true" PackagePath="Sdk">
|
<None Include="$(GodotSdkPackageVersionsFilePath)" Pack="true" PackagePath="Sdk">
|
||||||
<Link>Sdk\SdkPackageVersions.props</Link>
|
<Link>Sdk\SdkPackageVersions.props</Link>
|
||||||
</None>
|
</None>
|
||||||
|
<None Include="Sdk\iOSNativeAOT.props" Pack="true" PackagePath="Sdk" />
|
||||||
|
<None Include="Sdk\iOSNativeAOT.targets" Pack="true" PackagePath="Sdk" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -59,6 +59,18 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<!-- Auto-detect the target Godot platform if it was not specified. -->
|
<!-- Auto-detect the target Godot platform if it was not specified. -->
|
||||||
|
<PropertyGroup Condition=" '$(GodotTargetPlatform)' == '' ">
|
||||||
|
<GodotTargetPlatform Condition=" $(RuntimeIdentifier.StartsWith('ios')) ">ios</GodotTargetPlatform>
|
||||||
|
<GodotTargetPlatform Condition=" '$(GodotTargetPlatform)' == '' and $(RuntimeIdentifier.StartsWith('android')) ">android</GodotTargetPlatform>
|
||||||
|
<GodotTargetPlatform Condition=" '$(GodotTargetPlatform)' == '' and $(RuntimeIdentifier.StartsWith('browser')) ">web</GodotTargetPlatform>
|
||||||
|
|
||||||
|
<GodotTargetPlatform Condition=" '$(GodotTargetPlatform)' == '' and $(RuntimeIdentifier.StartsWith('linux')) ">linuxbsd</GodotTargetPlatform>
|
||||||
|
<GodotTargetPlatform Condition=" '$(GodotTargetPlatform)' == '' and $(RuntimeIdentifier.StartsWith('freebsd')) ">linuxbsd</GodotTargetPlatform>
|
||||||
|
<GodotTargetPlatform Condition=" '$(GodotTargetPlatform)' == '' and $(RuntimeIdentifier.StartsWith('osx')) ">macos</GodotTargetPlatform>
|
||||||
|
<GodotTargetPlatform Condition=" '$(GodotTargetPlatform)' == '' and $(RuntimeIdentifier.StartsWith('win')) ">windows</GodotTargetPlatform>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<!-- Auto-detect the target Godot platform if it was not specified and there's no runtime identifier information. -->
|
||||||
<PropertyGroup Condition=" '$(GodotTargetPlatform)' == '' ">
|
<PropertyGroup Condition=" '$(GodotTargetPlatform)' == '' ">
|
||||||
<GodotTargetPlatform Condition=" '$([MSBuild]::IsOsPlatform(Linux))' ">linuxbsd</GodotTargetPlatform>
|
<GodotTargetPlatform Condition=" '$([MSBuild]::IsOsPlatform(Linux))' ">linuxbsd</GodotTargetPlatform>
|
||||||
<GodotTargetPlatform Condition=" '$([MSBuild]::IsOsPlatform(FreeBSD))' ">linuxbsd</GodotTargetPlatform>
|
<GodotTargetPlatform Condition=" '$([MSBuild]::IsOsPlatform(FreeBSD))' ">linuxbsd</GodotTargetPlatform>
|
||||||
@ -97,4 +109,6 @@
|
|||||||
|
|
||||||
<DefineConstants>$(GodotDefineConstants);$(DefineConstants)</DefineConstants>
|
<DefineConstants>$(GodotDefineConstants);$(DefineConstants)</DefineConstants>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<Import Project="$(MSBuildThisFileDirectory)\iOSNativeAOT.props" Condition=" '$(GodotTargetPlatform)' == 'ios' " />
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -20,4 +20,8 @@
|
|||||||
<PackageReference Include="GodotSharp" Version="$(PackageVersion_GodotSharp)" />
|
<PackageReference Include="GodotSharp" Version="$(PackageVersion_GodotSharp)" />
|
||||||
<PackageReference Include="GodotSharpEditor" Version="$(PackageVersion_GodotSharp)" Condition=" '$(Configuration)' == 'Debug' " />
|
<PackageReference Include="GodotSharpEditor" Version="$(PackageVersion_GodotSharp)" Condition=" '$(Configuration)' == 'Debug' " />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- iOS-specific build targets -->
|
||||||
|
<Import Project="$(MSBuildThisFileDirectory)\iOSNativeAOT.targets" Condition=" '$(GodotTargetPlatform)' == 'ios' " />
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
<Project>
|
||||||
|
<PropertyGroup>
|
||||||
|
<PublishAot>true</PublishAot>
|
||||||
|
<PublishAotUsingRuntimePack>true</PublishAotUsingRuntimePack>
|
||||||
|
<UseNativeAOTRuntime>true</UseNativeAOTRuntime>
|
||||||
|
<TrimmerSingleWarn>false</TrimmerSingleWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
@ -0,0 +1,58 @@
|
|||||||
|
<Project>
|
||||||
|
<ItemGroup>
|
||||||
|
<TrimmerRootAssembly Include="GodotSharp" />
|
||||||
|
<TrimmerRootAssembly Include="$(TargetName)" />
|
||||||
|
<LinkerArg Include="-install_name '@rpath/$(TargetName)$(NativeBinaryExt)'" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<LinkStandardCPlusPlusLibrary>true</LinkStandardCPlusPlusLibrary>
|
||||||
|
<FindXCode Condition=" '$(XCodePath)' == '' and '$([MSBuild]::IsOsPlatform(OSX))' ">true</FindXCode>
|
||||||
|
<XCodePath Condition=" '$(XCodePath)' == '' ">/Applications/Xcode.app/Contents/Developer</XCodePath>
|
||||||
|
<XCodePath>$([MSBuild]::EnsureTrailingSlash('$(XCodePath)'))</XCodePath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<Target Name="PrepareBeforeIlcCompile"
|
||||||
|
BeforeTargets="IlcCompile">
|
||||||
|
|
||||||
|
<Copy SourceFiles="%(ResolvedRuntimePack.PackageDirectory)/runtimes/$(RuntimeIdentifier)/native/icudt.dat" DestinationFolder="$(PublishDir)"/>
|
||||||
|
|
||||||
|
<!-- We need to find the path to Xcode so we can set manual linker args to the correct SDKs
|
||||||
|
Once https://github.com/dotnet/runtime/issues/88737 is released, we can take this out
|
||||||
|
-->
|
||||||
|
|
||||||
|
<Exec Command="xcrun xcode-select -p" ConsoleToMSBuild="true" Condition=" '$(FindXCode)' == 'true' ">
|
||||||
|
<Output TaskParameter="ConsoleOutput" PropertyName="XcodeSelect" />
|
||||||
|
</Exec>
|
||||||
|
|
||||||
|
<PropertyGroup Condition=" '$(FindXCode)' == 'true' ">
|
||||||
|
<XCodePath>$(XcodeSelect)</XCodePath>
|
||||||
|
<XCodePath>$([MSBuild]::EnsureTrailingSlash('$(XCodePath)'))</XCodePath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<Message Importance="normal" Text="Found XCode at $(XcodeSelect)" Condition=" '$(FindXCode)' == 'true' "/>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<LinkerArg Include="-isysroot %22$(XCodePath)Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk%22"
|
||||||
|
Condition=" $(RuntimeIdentifier.Contains('simulator')) "/>
|
||||||
|
<LinkerArg Include="-isysroot %22$(XCodePath)Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk%22"
|
||||||
|
Condition=" !$(RuntimeIdentifier.Contains('simulator')) "/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
<Target Name="FixSymbols"
|
||||||
|
AfterTargets="Publish">
|
||||||
|
|
||||||
|
<RemoveDir Directories="$(PublishDir)$(TargetName).framework.dSYM"/>
|
||||||
|
|
||||||
|
<!-- create-xcframework (called from the export plugin wants the symbol files in a directory
|
||||||
|
with a slightly different name from the one created by dotnet publish, so we copy them over
|
||||||
|
to the correctly-named directory -->
|
||||||
|
<ItemGroup>
|
||||||
|
<SymbolFiles Include="$(NativeBinary).dsym\**\*.*"/>
|
||||||
|
</ItemGroup>
|
||||||
|
<Copy SourceFiles="@(SymbolFiles)" DestinationFolder="$(PublishDir)$(TargetName).framework.dSYM"/>
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
</Project>
|
@ -25,6 +25,9 @@ namespace GodotTools.ProjectEditor
|
|||||||
mainGroup.AddProperty("TargetFramework", "net6.0");
|
mainGroup.AddProperty("TargetFramework", "net6.0");
|
||||||
mainGroup.AddProperty("EnableDynamicLoading", "true");
|
mainGroup.AddProperty("EnableDynamicLoading", "true");
|
||||||
|
|
||||||
|
var net8 = mainGroup.AddProperty("TargetFramework", "net8.0");
|
||||||
|
net8.Condition = " '$(GodotTargetPlatform)' == 'ios' ";
|
||||||
|
|
||||||
string sanitizedName = IdentifierUtils.SanitizeQualifiedIdentifier(name, allowEmptyIdentifiers: true);
|
string sanitizedName = IdentifierUtils.SanitizeQualifiedIdentifier(name, allowEmptyIdentifiers: true);
|
||||||
|
|
||||||
// If the name is not a valid namespace, manually set RootNamespace to a sanitized one.
|
// If the name is not a valid namespace, manually set RootNamespace to a sanitized one.
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -67,7 +68,7 @@ namespace GodotTools.Build
|
|||||||
{
|
{
|
||||||
BuildStarted?.Invoke(buildInfo);
|
BuildStarted?.Invoke(buildInfo);
|
||||||
|
|
||||||
// Required in order to update the build tasks list
|
// Required in order to update the build tasks list.
|
||||||
Internal.GodotMainIteration();
|
Internal.GodotMainIteration();
|
||||||
|
|
||||||
try
|
try
|
||||||
@ -162,7 +163,7 @@ namespace GodotTools.Build
|
|||||||
{
|
{
|
||||||
BuildStarted?.Invoke(buildInfo);
|
BuildStarted?.Invoke(buildInfo);
|
||||||
|
|
||||||
// Required in order to update the build tasks list
|
// Required in order to update the build tasks list.
|
||||||
Internal.GodotMainIteration();
|
Internal.GodotMainIteration();
|
||||||
|
|
||||||
try
|
try
|
||||||
@ -317,6 +318,45 @@ namespace GodotTools.Build
|
|||||||
) => PublishProjectBlocking(CreatePublishBuildInfo(configuration,
|
) => PublishProjectBlocking(CreatePublishBuildInfo(configuration,
|
||||||
platform, runtimeIdentifier, publishOutputDir, includeDebugSymbols));
|
platform, runtimeIdentifier, publishOutputDir, includeDebugSymbols));
|
||||||
|
|
||||||
|
public static bool GenerateXCFrameworkBlocking(
|
||||||
|
List<string> outputPaths,
|
||||||
|
string xcFrameworkPath)
|
||||||
|
{
|
||||||
|
using var pr = new EditorProgress("generate_xcframework", "Generating XCFramework...", 1);
|
||||||
|
|
||||||
|
pr.Step("Running xcodebuild -create-xcframework", 0);
|
||||||
|
|
||||||
|
if (!GenerateXCFramework(outputPaths, xcFrameworkPath))
|
||||||
|
{
|
||||||
|
ShowBuildErrorDialog("Failed to generate XCFramework");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool GenerateXCFramework(List<string> outputPaths, string xcFrameworkPath)
|
||||||
|
{
|
||||||
|
// Required in order to update the build tasks list.
|
||||||
|
Internal.GodotMainIteration();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int exitCode = BuildSystem.GenerateXCFramework(outputPaths, xcFrameworkPath, StdOutputReceived, StdErrorReceived);
|
||||||
|
|
||||||
|
if (exitCode != 0)
|
||||||
|
PrintVerbose(
|
||||||
|
$"xcodebuild create-xcframework exited with code: {exitCode}.");
|
||||||
|
|
||||||
|
return exitCode == 0;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine(e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static bool EditorBuildCallback()
|
public static bool EditorBuildCallback()
|
||||||
{
|
{
|
||||||
if (!File.Exists(GodotSharpDirs.ProjectCsProjPath))
|
if (!File.Exists(GodotSharpDirs.ProjectCsProjPath))
|
||||||
|
@ -9,7 +9,9 @@ using System.Text;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Godot;
|
using Godot;
|
||||||
using GodotTools.BuildLogger;
|
using GodotTools.BuildLogger;
|
||||||
|
using GodotTools.Internals;
|
||||||
using GodotTools.Utils;
|
using GodotTools.Utils;
|
||||||
|
using Directory = GodotTools.Utils.Directory;
|
||||||
|
|
||||||
namespace GodotTools.Build
|
namespace GodotTools.Build
|
||||||
{
|
{
|
||||||
@ -293,5 +295,81 @@ namespace GodotTools.Build
|
|||||||
foreach (string env in platformEnvironmentVariables)
|
foreach (string env in platformEnvironmentVariables)
|
||||||
environmentVariables.Remove(env);
|
environmentVariables.Remove(env);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Process DoGenerateXCFramework(List<string> outputPaths, string xcFrameworkPath,
|
||||||
|
Action<string> stdOutHandler, Action<string> stdErrHandler)
|
||||||
|
{
|
||||||
|
if (Directory.Exists(xcFrameworkPath))
|
||||||
|
{
|
||||||
|
Directory.Delete(xcFrameworkPath, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
var startInfo = new ProcessStartInfo("xcrun");
|
||||||
|
|
||||||
|
BuildXCFrameworkArguments(outputPaths, xcFrameworkPath, startInfo.ArgumentList);
|
||||||
|
|
||||||
|
string launchMessage = startInfo.GetCommandLineDisplay(new StringBuilder("Packaging: ")).ToString();
|
||||||
|
stdOutHandler?.Invoke(launchMessage);
|
||||||
|
if (Godot.OS.IsStdOutVerbose())
|
||||||
|
Console.WriteLine(launchMessage);
|
||||||
|
|
||||||
|
startInfo.RedirectStandardOutput = true;
|
||||||
|
startInfo.RedirectStandardError = true;
|
||||||
|
startInfo.UseShellExecute = false;
|
||||||
|
|
||||||
|
if (OperatingSystem.IsWindows())
|
||||||
|
{
|
||||||
|
startInfo.StandardOutputEncoding = Encoding.UTF8;
|
||||||
|
startInfo.StandardErrorEncoding = Encoding.UTF8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Needed when running from Developer Command Prompt for VS.
|
||||||
|
RemovePlatformVariable(startInfo.EnvironmentVariables);
|
||||||
|
|
||||||
|
var process = new Process { StartInfo = startInfo };
|
||||||
|
|
||||||
|
if (stdOutHandler != null)
|
||||||
|
process.OutputDataReceived += (_, e) => stdOutHandler.Invoke(e.Data);
|
||||||
|
if (stdErrHandler != null)
|
||||||
|
process.ErrorDataReceived += (_, e) => stdErrHandler.Invoke(e.Data);
|
||||||
|
|
||||||
|
process.Start();
|
||||||
|
|
||||||
|
process.BeginOutputReadLine();
|
||||||
|
process.BeginErrorReadLine();
|
||||||
|
|
||||||
|
return process;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GenerateXCFramework(List<string> outputPaths, string xcFrameworkPath, Action<string> stdOutHandler, Action<string> stdErrHandler)
|
||||||
|
{
|
||||||
|
using (var process = DoGenerateXCFramework(outputPaths, xcFrameworkPath, stdOutHandler, stdErrHandler))
|
||||||
|
{
|
||||||
|
process.WaitForExit();
|
||||||
|
|
||||||
|
return process.ExitCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void BuildXCFrameworkArguments(List<string> outputPaths,
|
||||||
|
string xcFrameworkPath, Collection<string> arguments)
|
||||||
|
{
|
||||||
|
var baseDylib = $"{GodotSharpDirs.ProjectAssemblyName}.dylib";
|
||||||
|
var baseSym = $"{GodotSharpDirs.ProjectAssemblyName}.framework.dSYM";
|
||||||
|
|
||||||
|
arguments.Add("xcodebuild");
|
||||||
|
arguments.Add("-create-xcframework");
|
||||||
|
|
||||||
|
foreach (var outputPath in outputPaths)
|
||||||
|
{
|
||||||
|
arguments.Add("-library");
|
||||||
|
arguments.Add(Path.Combine(outputPath, baseDylib));
|
||||||
|
arguments.Add("-debug-symbols");
|
||||||
|
arguments.Add(Path.Combine(outputPath, baseSym));
|
||||||
|
}
|
||||||
|
|
||||||
|
arguments.Add("-output");
|
||||||
|
arguments.Add(xcFrameworkPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,7 @@ using System.Linq;
|
|||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using GodotTools.Build;
|
using GodotTools.Build;
|
||||||
using GodotTools.Core;
|
|
||||||
using GodotTools.Internals;
|
using GodotTools.Internals;
|
||||||
using static GodotTools.Internals.Globals;
|
|
||||||
using Directory = GodotTools.Utils.Directory;
|
using Directory = GodotTools.Utils.Directory;
|
||||||
using File = GodotTools.Utils.File;
|
using File = GodotTools.Utils.File;
|
||||||
using OS = GodotTools.Utils.OS;
|
using OS = GodotTools.Utils.OS;
|
||||||
@ -77,7 +75,7 @@ namespace GodotTools.Export
|
|||||||
$"Resource of type {Internal.CSharpLanguageType} has an invalid file extension: {path}",
|
$"Resource of type {Internal.CSharpLanguageType} has an invalid file extension: {path}",
|
||||||
nameof(path));
|
nameof(path));
|
||||||
|
|
||||||
// TODO What if the source file is not part of the game's C# project
|
// TODO: What if the source file is not part of the game's C# project?
|
||||||
|
|
||||||
bool includeScriptsContent = (bool)GetOption("dotnet/include_scripts_content");
|
bool includeScriptsContent = (bool)GetOption("dotnet/include_scripts_content");
|
||||||
|
|
||||||
@ -89,7 +87,7 @@ namespace GodotTools.Export
|
|||||||
// Because of this, we add a file which contains a line break.
|
// Because of this, we add a file which contains a line break.
|
||||||
AddFile(path, System.Text.Encoding.UTF8.GetBytes("\n"), remap: false);
|
AddFile(path, System.Text.Encoding.UTF8.GetBytes("\n"), remap: false);
|
||||||
|
|
||||||
// Tell the Godot exporter that we already took care of the file
|
// Tell the Godot exporter that we already took care of the file.
|
||||||
Skip();
|
Skip();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -119,7 +117,7 @@ namespace GodotTools.Export
|
|||||||
|
|
||||||
private void _ExportBeginImpl(string[] features, bool isDebug, string path, long flags)
|
private void _ExportBeginImpl(string[] features, bool isDebug, string path, long flags)
|
||||||
{
|
{
|
||||||
_ = flags; // Unused
|
_ = flags; // Unused.
|
||||||
|
|
||||||
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
|
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
|
||||||
return;
|
return;
|
||||||
@ -127,115 +125,261 @@ namespace GodotTools.Export
|
|||||||
if (!DeterminePlatformFromFeatures(features, out string platform))
|
if (!DeterminePlatformFromFeatures(features, out string platform))
|
||||||
throw new NotSupportedException("Target platform not supported.");
|
throw new NotSupportedException("Target platform not supported.");
|
||||||
|
|
||||||
if (!new[] { OS.Platforms.Windows, OS.Platforms.LinuxBSD, OS.Platforms.MacOS, OS.Platforms.Android }
|
if (!new[] { OS.Platforms.Windows, OS.Platforms.LinuxBSD, OS.Platforms.MacOS, OS.Platforms.Android, OS.Platforms.iOS }
|
||||||
.Contains(platform))
|
.Contains(platform))
|
||||||
{
|
{
|
||||||
throw new NotImplementedException("Target platform not yet implemented.");
|
throw new NotImplementedException("Target platform not yet implemented.");
|
||||||
}
|
}
|
||||||
|
|
||||||
string buildConfig = isDebug ? "ExportDebug" : "ExportRelease";
|
PublishConfig publishConfig = new()
|
||||||
|
{
|
||||||
|
BuildConfig = isDebug ? "ExportDebug" : "ExportRelease",
|
||||||
|
IncludeDebugSymbols = (bool)GetOption("dotnet/include_debug_symbols"),
|
||||||
|
RidOS = DetermineRuntimeIdentifierOS(platform),
|
||||||
|
Archs = new List<string>(),
|
||||||
|
UseTempDir = platform != OS.Platforms.iOS, // xcode project links directly to files in the publish dir, so use one that sticks around.
|
||||||
|
BundleOutputs = true,
|
||||||
|
};
|
||||||
|
|
||||||
bool includeDebugSymbols = (bool)GetOption("dotnet/include_debug_symbols");
|
|
||||||
|
|
||||||
var archs = new List<string>();
|
|
||||||
if (features.Contains("x86_64"))
|
if (features.Contains("x86_64"))
|
||||||
{
|
{
|
||||||
archs.Add("x86_64");
|
publishConfig.Archs.Add("x86_64");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (features.Contains("x86_32"))
|
if (features.Contains("x86_32"))
|
||||||
{
|
{
|
||||||
archs.Add("x86_32");
|
publishConfig.Archs.Add("x86_32");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (features.Contains("arm64"))
|
if (features.Contains("arm64"))
|
||||||
{
|
{
|
||||||
archs.Add("arm64");
|
publishConfig.Archs.Add("arm64");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (features.Contains("arm32"))
|
if (features.Contains("arm32"))
|
||||||
{
|
{
|
||||||
archs.Add("arm32");
|
publishConfig.Archs.Add("arm32");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (features.Contains("universal"))
|
if (features.Contains("universal"))
|
||||||
{
|
{
|
||||||
if (platform == OS.Platforms.MacOS)
|
if (platform == OS.Platforms.MacOS)
|
||||||
{
|
{
|
||||||
archs.Add("x86_64");
|
publishConfig.Archs.Add("x86_64");
|
||||||
archs.Add("arm64");
|
publishConfig.Archs.Add("arm64");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var targets = new List<PublishConfig> { publishConfig };
|
||||||
|
|
||||||
|
if (platform == OS.Platforms.iOS)
|
||||||
|
{
|
||||||
|
targets.Add(new PublishConfig
|
||||||
|
{
|
||||||
|
BuildConfig = publishConfig.BuildConfig,
|
||||||
|
Archs = new List<string> { "arm64", "x86_64" },
|
||||||
|
BundleOutputs = false,
|
||||||
|
IncludeDebugSymbols = publishConfig.IncludeDebugSymbols,
|
||||||
|
RidOS = OS.DotNetOS.iOSSimulator,
|
||||||
|
UseTempDir = true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
List<string> outputPaths = new();
|
||||||
|
|
||||||
bool embedBuildResults = (bool)GetOption("dotnet/embed_build_outputs") || features.Contains("android");
|
bool embedBuildResults = (bool)GetOption("dotnet/embed_build_outputs") || features.Contains("android");
|
||||||
|
|
||||||
foreach (var arch in archs)
|
foreach (PublishConfig config in targets)
|
||||||
{
|
{
|
||||||
string ridOS = DetermineRuntimeIdentifierOS(platform);
|
string ridOS = config.RidOS;
|
||||||
string ridArch = DetermineRuntimeIdentifierArch(arch);
|
string buildConfig = config.BuildConfig;
|
||||||
string runtimeIdentifier = $"{ridOS}-{ridArch}";
|
bool includeDebugSymbols = config.IncludeDebugSymbols;
|
||||||
string projectDataDirName = $"data_{GodotSharpDirs.CSharpProjectName}_{platform}_{arch}";
|
|
||||||
if (platform == OS.Platforms.MacOS)
|
foreach (string arch in config.Archs)
|
||||||
{
|
{
|
||||||
projectDataDirName = Path.Combine("Contents", "Resources", projectDataDirName);
|
string ridArch = DetermineRuntimeIdentifierArch(arch);
|
||||||
}
|
string runtimeIdentifier = $"{ridOS}-{ridArch}";
|
||||||
|
string projectDataDirName = $"data_{GodotSharpDirs.CSharpProjectName}_{platform}_{arch}";
|
||||||
// Create temporary publish output directory
|
if (platform == OS.Platforms.MacOS)
|
||||||
|
|
||||||
string publishOutputTempDir = Path.Combine(Path.GetTempPath(), "godot-publish-dotnet",
|
|
||||||
$"{System.Environment.ProcessId}-{buildConfig}-{runtimeIdentifier}");
|
|
||||||
|
|
||||||
_tempFolders.Add(publishOutputTempDir);
|
|
||||||
|
|
||||||
if (!Directory.Exists(publishOutputTempDir))
|
|
||||||
Directory.CreateDirectory(publishOutputTempDir);
|
|
||||||
|
|
||||||
// Execute dotnet publish
|
|
||||||
|
|
||||||
if (!BuildManager.PublishProjectBlocking(buildConfig, platform,
|
|
||||||
runtimeIdentifier, publishOutputTempDir, includeDebugSymbols))
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Failed to build project.");
|
|
||||||
}
|
|
||||||
|
|
||||||
string soExt = ridOS switch
|
|
||||||
{
|
|
||||||
OS.DotNetOS.Win or OS.DotNetOS.Win10 => "dll",
|
|
||||||
OS.DotNetOS.OSX or OS.DotNetOS.iOS => "dylib",
|
|
||||||
_ => "so"
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!File.Exists(Path.Combine(publishOutputTempDir, $"{GodotSharpDirs.ProjectAssemblyName}.dll"))
|
|
||||||
// NativeAOT shared library output
|
|
||||||
&& !File.Exists(Path.Combine(publishOutputTempDir, $"{GodotSharpDirs.ProjectAssemblyName}.{soExt}")))
|
|
||||||
{
|
|
||||||
throw new NotSupportedException(
|
|
||||||
"Publish succeeded but project assembly not found in the output directory");
|
|
||||||
}
|
|
||||||
|
|
||||||
var manifest = new StringBuilder();
|
|
||||||
|
|
||||||
// Add to the exported project shared object list or packed resources.
|
|
||||||
foreach (string file in Directory.GetFiles(publishOutputTempDir, "*", SearchOption.AllDirectories))
|
|
||||||
{
|
|
||||||
if (embedBuildResults)
|
|
||||||
{
|
{
|
||||||
var filePath = SanitizeSlashes(Path.GetRelativePath(publishOutputTempDir, file));
|
projectDataDirName = Path.Combine("Contents", "Resources", projectDataDirName);
|
||||||
var fileData = File.ReadAllBytes(file);
|
}
|
||||||
var hash = Convert.ToBase64String(SHA512.HashData(fileData));
|
|
||||||
|
|
||||||
manifest.Append($"{filePath}\t{hash}\n");
|
// Create temporary publish output directory.
|
||||||
|
string publishOutputDir;
|
||||||
|
|
||||||
AddFile($"res://.godot/mono/publish/{arch}/{filePath}", fileData, false);
|
if (config.UseTempDir)
|
||||||
|
{
|
||||||
|
publishOutputDir = Path.Combine(Path.GetTempPath(), "godot-publish-dotnet",
|
||||||
|
$"{System.Environment.ProcessId}-{buildConfig}-{runtimeIdentifier}");
|
||||||
|
_tempFolders.Add(publishOutputDir);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AddSharedObject(file, tags: null,
|
publishOutputDir = Path.Combine(GodotSharpDirs.ProjectBaseOutputPath, "godot-publish-dotnet",
|
||||||
Path.Join(projectDataDirName,
|
$"{buildConfig}-{runtimeIdentifier}");
|
||||||
Path.GetRelativePath(publishOutputTempDir, Path.GetDirectoryName(file))));
|
|
||||||
|
}
|
||||||
|
|
||||||
|
outputPaths.Add(publishOutputDir);
|
||||||
|
|
||||||
|
if (!Directory.Exists(publishOutputDir))
|
||||||
|
Directory.CreateDirectory(publishOutputDir);
|
||||||
|
|
||||||
|
// Execute dotnet publish.
|
||||||
|
if (!BuildManager.PublishProjectBlocking(buildConfig, platform,
|
||||||
|
runtimeIdentifier, publishOutputDir, includeDebugSymbols))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Failed to build project.");
|
||||||
|
}
|
||||||
|
|
||||||
|
string soExt = ridOS switch
|
||||||
|
{
|
||||||
|
OS.DotNetOS.Win or OS.DotNetOS.Win10 => "dll",
|
||||||
|
OS.DotNetOS.OSX or OS.DotNetOS.iOS or OS.DotNetOS.iOSSimulator => "dylib",
|
||||||
|
_ => "so"
|
||||||
|
};
|
||||||
|
|
||||||
|
string assemblyPath = Path.Combine(publishOutputDir, $"{GodotSharpDirs.ProjectAssemblyName}.dll");
|
||||||
|
string nativeAotPath = Path.Combine(publishOutputDir,
|
||||||
|
$"{GodotSharpDirs.ProjectAssemblyName}.{soExt}");
|
||||||
|
|
||||||
|
if (!File.Exists(assemblyPath) && !File.Exists(nativeAotPath))
|
||||||
|
{
|
||||||
|
throw new NotSupportedException(
|
||||||
|
$"Publish succeeded but project assembly not found at '{assemblyPath}' or '{nativeAotPath}'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// For ios simulator builds, skip packaging the build outputs.
|
||||||
|
if (!config.BundleOutputs)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var manifest = new StringBuilder();
|
||||||
|
|
||||||
|
// Add to the exported project shared object list or packed resources.
|
||||||
|
RecursePublishContents(publishOutputDir,
|
||||||
|
filterDir: dir =>
|
||||||
|
{
|
||||||
|
if (platform == OS.Platforms.iOS)
|
||||||
|
{
|
||||||
|
// Exclude dsym folders.
|
||||||
|
return !dir.EndsWith(".dsym", StringComparison.InvariantCultureIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
filterFile: file =>
|
||||||
|
{
|
||||||
|
if (platform == OS.Platforms.iOS)
|
||||||
|
{
|
||||||
|
// Exclude the dylib artifact, since it's included separately as an xcframework.
|
||||||
|
return Path.GetFileName(file) != $"{GodotSharpDirs.ProjectAssemblyName}.dylib";
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
recurseDir: dir =>
|
||||||
|
{
|
||||||
|
if (platform == OS.Platforms.iOS)
|
||||||
|
{
|
||||||
|
// Don't recurse into dsym folders.
|
||||||
|
return !dir.EndsWith(".dsym", StringComparison.InvariantCultureIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
addEntry: (path, isFile) =>
|
||||||
|
{
|
||||||
|
// We get called back for both directories and files, but we only package files for now.
|
||||||
|
if (isFile)
|
||||||
|
{
|
||||||
|
if (embedBuildResults)
|
||||||
|
{
|
||||||
|
string filePath = SanitizeSlashes(Path.GetRelativePath(publishOutputDir, path));
|
||||||
|
byte[] fileData = File.ReadAllBytes(path);
|
||||||
|
string hash = Convert.ToBase64String(SHA512.HashData(fileData));
|
||||||
|
|
||||||
|
manifest.Append($"{filePath}\t{hash}\n");
|
||||||
|
|
||||||
|
AddFile($"res://.godot/mono/publish/{arch}/{filePath}", fileData, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (platform == OS.Platforms.iOS && path.EndsWith(".dat"))
|
||||||
|
{
|
||||||
|
AddIosBundleFile(path);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddSharedObject(path, tags: null,
|
||||||
|
Path.Join(projectDataDirName,
|
||||||
|
Path.GetRelativePath(publishOutputDir,
|
||||||
|
Path.GetDirectoryName(path))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (embedBuildResults)
|
||||||
|
{
|
||||||
|
byte[] fileData = Encoding.Default.GetBytes(manifest.ToString());
|
||||||
|
AddFile($"res://.godot/mono/publish/{arch}/.dotnet-publish-manifest", fileData, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (embedBuildResults)
|
if (platform == OS.Platforms.iOS)
|
||||||
|
{
|
||||||
|
if (outputPaths.Count > 2)
|
||||||
{
|
{
|
||||||
var fileData = Encoding.Default.GetBytes(manifest.ToString());
|
// lipo the simulator binaries together
|
||||||
AddFile($"res://.godot/mono/publish/{arch}/.dotnet-publish-manifest", fileData, false);
|
// TODO: Move this to the native lipo implementation we have in the macos export plugin.
|
||||||
|
var lipoArgs = new List<string>();
|
||||||
|
lipoArgs.Add("-create");
|
||||||
|
lipoArgs.AddRange(outputPaths.Skip(1).Select(x => Path.Combine(x, $"{GodotSharpDirs.ProjectAssemblyName}.dylib")));
|
||||||
|
lipoArgs.Add("-output");
|
||||||
|
lipoArgs.Add(Path.Combine(outputPaths[1], $"{GodotSharpDirs.ProjectAssemblyName}.dylib"));
|
||||||
|
|
||||||
|
int lipoExitCode = OS.ExecuteCommand(XcodeHelper.FindXcodeTool("lipo"), lipoArgs);
|
||||||
|
if (lipoExitCode != 0)
|
||||||
|
throw new InvalidOperationException($"Command 'lipo' exited with code: {lipoExitCode}.");
|
||||||
|
|
||||||
|
outputPaths.RemoveRange(2, outputPaths.Count - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
var xcFrameworkPath = Path.Combine(GodotSharpDirs.ProjectBaseOutputPath, publishConfig.BuildConfig,
|
||||||
|
$"{GodotSharpDirs.ProjectAssemblyName}.xcframework");
|
||||||
|
if (!BuildManager.GenerateXCFrameworkBlocking(outputPaths,
|
||||||
|
Path.Combine(GodotSharpDirs.ProjectBaseOutputPath, publishConfig.BuildConfig, xcFrameworkPath)))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Failed to generate xcframework.");
|
||||||
|
}
|
||||||
|
|
||||||
|
AddIosEmbeddedFramework(xcFrameworkPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RecursePublishContents(string path, Func<string, bool> filterDir,
|
||||||
|
Func<string, bool> filterFile, Func<string, bool> recurseDir,
|
||||||
|
Action<string, bool> addEntry)
|
||||||
|
{
|
||||||
|
foreach (string file in Directory.GetFiles(path, "*", SearchOption.TopDirectoryOnly))
|
||||||
|
{
|
||||||
|
if (filterFile(file))
|
||||||
|
{
|
||||||
|
addEntry(file, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (string dir in Directory.GetDirectories(path, "*", SearchOption.TopDirectoryOnly))
|
||||||
|
{
|
||||||
|
if (filterDir(dir))
|
||||||
|
{
|
||||||
|
addEntry(dir, false);
|
||||||
|
}
|
||||||
|
else if (recurseDir(dir))
|
||||||
|
{
|
||||||
|
RecursePublishContents(dir, filterDir, filterFile, recurseDir, addEntry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -304,5 +448,15 @@ namespace GodotTools.Export
|
|||||||
platform = null;
|
platform = null;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private struct PublishConfig
|
||||||
|
{
|
||||||
|
public bool UseTempDir;
|
||||||
|
public bool BundleOutputs;
|
||||||
|
public string RidOS;
|
||||||
|
public List<string> Archs;
|
||||||
|
public string BuildConfig;
|
||||||
|
public bool IncludeDebugSymbols;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,6 +118,16 @@ namespace GodotTools.Internals
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string ProjectBaseOutputPath
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_projectCsProjPath == null)
|
||||||
|
DetermineProjectLocation();
|
||||||
|
return Path.Combine(Path.GetDirectoryName(_projectCsProjPath)!, ".godot", "mono", "temp", "bin");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static string LogsDirPathFor(string solution, string configuration)
|
public static string LogsDirPathFor(string solution, string configuration)
|
||||||
=> Path.Combine(BuildLogsDirs, $"{solution.Md5Text()}_{configuration}");
|
=> Path.Combine(BuildLogsDirs, $"{solution.Md5Text()}_{configuration}");
|
||||||
|
|
||||||
|
@ -56,6 +56,7 @@ namespace GodotTools.Utils
|
|||||||
public const string Win10 = "win10";
|
public const string Win10 = "win10";
|
||||||
public const string Android = "android";
|
public const string Android = "android";
|
||||||
public const string iOS = "ios";
|
public const string iOS = "ios";
|
||||||
|
public const string iOSSimulator = "iossimulator";
|
||||||
public const string Browser = "browser";
|
public const string Browser = "browser";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -322,7 +322,7 @@ godot_plugins_initialize_fn try_load_native_aot_library(void *&r_aot_dll_handle)
|
|||||||
|
|
||||||
#if defined(WINDOWS_ENABLED)
|
#if defined(WINDOWS_ENABLED)
|
||||||
String native_aot_so_path = GodotSharpDirs::get_api_assemblies_dir().path_join(assembly_name + ".dll");
|
String native_aot_so_path = GodotSharpDirs::get_api_assemblies_dir().path_join(assembly_name + ".dll");
|
||||||
#elif defined(MACOS_ENABLED)
|
#elif defined(MACOS_ENABLED) || defined(IOS_ENABLED)
|
||||||
String native_aot_so_path = GodotSharpDirs::get_api_assemblies_dir().path_join(assembly_name + ".dylib");
|
String native_aot_so_path = GodotSharpDirs::get_api_assemblies_dir().path_join(assembly_name + ".dylib");
|
||||||
#elif defined(UNIX_ENABLED)
|
#elif defined(UNIX_ENABLED)
|
||||||
String native_aot_so_path = GodotSharpDirs::get_api_assemblies_dir().path_join(assembly_name + ".so");
|
String native_aot_so_path = GodotSharpDirs::get_api_assemblies_dir().path_join(assembly_name + ".so");
|
||||||
@ -330,23 +330,19 @@ godot_plugins_initialize_fn try_load_native_aot_library(void *&r_aot_dll_handle)
|
|||||||
#error "Platform not supported (yet?)"
|
#error "Platform not supported (yet?)"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (FileAccess::exists(native_aot_so_path)) {
|
Error err = OS::get_singleton()->open_dynamic_library(native_aot_so_path, r_aot_dll_handle);
|
||||||
Error err = OS::get_singleton()->open_dynamic_library(native_aot_so_path, r_aot_dll_handle);
|
|
||||||
|
|
||||||
if (err != OK) {
|
if (err != OK) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
|
||||||
|
|
||||||
void *lib = r_aot_dll_handle;
|
|
||||||
|
|
||||||
void *symbol = nullptr;
|
|
||||||
|
|
||||||
err = OS::get_singleton()->get_dynamic_library_symbol_handle(lib, "godotsharp_game_main_init", symbol);
|
|
||||||
ERR_FAIL_COND_V(err != OK, nullptr);
|
|
||||||
return (godot_plugins_initialize_fn)symbol;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
void *lib = r_aot_dll_handle;
|
||||||
|
|
||||||
|
void *symbol = nullptr;
|
||||||
|
|
||||||
|
err = OS::get_singleton()->get_dynamic_library_symbol_handle(lib, "godotsharp_game_main_init", symbol);
|
||||||
|
ERR_FAIL_COND_V(err != OK, nullptr);
|
||||||
|
return (godot_plugins_initialize_fn)symbol;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -376,11 +372,13 @@ void GDMono::initialize() {
|
|||||||
|
|
||||||
godot_plugins_initialize_fn godot_plugins_initialize = nullptr;
|
godot_plugins_initialize_fn godot_plugins_initialize = nullptr;
|
||||||
|
|
||||||
|
#if !defined(IOS_ENABLED)
|
||||||
// Check that the .NET assemblies directory exists before trying to use it.
|
// Check that the .NET assemblies directory exists before trying to use it.
|
||||||
if (!DirAccess::exists(GodotSharpDirs::get_api_assemblies_dir())) {
|
if (!DirAccess::exists(GodotSharpDirs::get_api_assemblies_dir())) {
|
||||||
OS::get_singleton()->alert(vformat(RTR("Unable to find the .NET assemblies directory.\nMake sure the '%s' directory exists and contains the .NET assemblies."), GodotSharpDirs::get_api_assemblies_dir()), RTR(".NET assemblies not found"));
|
OS::get_singleton()->alert(vformat(RTR("Unable to find the .NET assemblies directory.\nMake sure the '%s' directory exists and contains the .NET assemblies."), GodotSharpDirs::get_api_assemblies_dir()), RTR(".NET assemblies not found"));
|
||||||
ERR_FAIL_MSG(".NET: Assemblies not found");
|
ERR_FAIL_MSG(".NET: Assemblies not found");
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!load_hostfxr(hostfxr_dll_handle)) {
|
if (!load_hostfxr(hostfxr_dll_handle)) {
|
||||||
#if !defined(TOOLS_ENABLED)
|
#if !defined(TOOLS_ENABLED)
|
||||||
|
@ -1,50 +0,0 @@
|
|||||||
/**************************************************************************/
|
|
||||||
/* ios_support.h */
|
|
||||||
/**************************************************************************/
|
|
||||||
/* This file is part of: */
|
|
||||||
/* GODOT ENGINE */
|
|
||||||
/* https://godotengine.org */
|
|
||||||
/**************************************************************************/
|
|
||||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
|
||||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
|
||||||
/* */
|
|
||||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
|
||||||
/* a copy of this software and associated documentation files (the */
|
|
||||||
/* "Software"), to deal in the Software without restriction, including */
|
|
||||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
|
||||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
|
||||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
|
||||||
/* the following conditions: */
|
|
||||||
/* */
|
|
||||||
/* The above copyright notice and this permission notice shall be */
|
|
||||||
/* included in all copies or substantial portions of the Software. */
|
|
||||||
/* */
|
|
||||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
|
||||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
|
||||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
|
||||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
|
||||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
|
||||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
|
||||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
|
||||||
/**************************************************************************/
|
|
||||||
|
|
||||||
#ifndef IOS_SUPPORT_H
|
|
||||||
#define IOS_SUPPORT_H
|
|
||||||
|
|
||||||
#if defined(IOS_ENABLED)
|
|
||||||
|
|
||||||
#include "core/string/ustring.h"
|
|
||||||
|
|
||||||
namespace gdmono {
|
|
||||||
namespace ios {
|
|
||||||
namespace support {
|
|
||||||
|
|
||||||
void initialize();
|
|
||||||
void cleanup();
|
|
||||||
} // namespace support
|
|
||||||
} // namespace ios
|
|
||||||
} // namespace gdmono
|
|
||||||
|
|
||||||
#endif // IOS_ENABLED
|
|
||||||
|
|
||||||
#endif // IOS_SUPPORT_H
|
|
@ -1,150 +0,0 @@
|
|||||||
/**************************************************************************/
|
|
||||||
/* ios_support.mm */
|
|
||||||
/**************************************************************************/
|
|
||||||
/* This file is part of: */
|
|
||||||
/* GODOT ENGINE */
|
|
||||||
/* https://godotengine.org */
|
|
||||||
/**************************************************************************/
|
|
||||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
|
||||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
|
||||||
/* */
|
|
||||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
|
||||||
/* a copy of this software and associated documentation files (the */
|
|
||||||
/* "Software"), to deal in the Software without restriction, including */
|
|
||||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
|
||||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
|
||||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
|
||||||
/* the following conditions: */
|
|
||||||
/* */
|
|
||||||
/* The above copyright notice and this permission notice shall be */
|
|
||||||
/* included in all copies or substantial portions of the Software. */
|
|
||||||
/* */
|
|
||||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
|
||||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
|
||||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
|
||||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
|
||||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
|
||||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
|
||||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
|
||||||
/**************************************************************************/
|
|
||||||
|
|
||||||
#include "ios_support.h"
|
|
||||||
|
|
||||||
#if defined(IOS_ENABLED)
|
|
||||||
|
|
||||||
#include "../gd_mono_marshal.h"
|
|
||||||
|
|
||||||
#include "core/ustring.h"
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
#include <os/log.h>
|
|
||||||
|
|
||||||
// Implemented mostly following: https://github.com/mono/mono/blob/master/sdks/ios/app/runtime.m
|
|
||||||
|
|
||||||
// Definition generated by the Godot exporter
|
|
||||||
extern "C" void gd_mono_setup_aot();
|
|
||||||
|
|
||||||
namespace gdmono {
|
|
||||||
namespace ios {
|
|
||||||
namespace support {
|
|
||||||
|
|
||||||
void ios_mono_log_callback(const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *user_data) {
|
|
||||||
os_log_info(OS_LOG_DEFAULT, "(%s %s) %s", log_domain, log_level, message);
|
|
||||||
if (fatal) {
|
|
||||||
os_log_info(OS_LOG_DEFAULT, "Exit code: %d.", 1);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void initialize() {
|
|
||||||
mono_dllmap_insert(nullptr, "System.Native", nullptr, "__Internal", nullptr);
|
|
||||||
mono_dllmap_insert(nullptr, "System.IO.Compression.Native", nullptr, "__Internal", nullptr);
|
|
||||||
mono_dllmap_insert(nullptr, "System.Security.Cryptography.Native.Apple", nullptr, "__Internal", nullptr);
|
|
||||||
|
|
||||||
#ifdef IOS_DEVICE
|
|
||||||
// This function is defined in an auto-generated source file
|
|
||||||
gd_mono_setup_aot();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
mono_set_signal_chaining(true);
|
|
||||||
mono_set_crash_chaining(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void cleanup() {
|
|
||||||
}
|
|
||||||
} // namespace support
|
|
||||||
} // namespace ios
|
|
||||||
} // namespace gdmono
|
|
||||||
|
|
||||||
// The following are P/Invoke functions required by the monotouch profile of the BCL.
|
|
||||||
// These are P/Invoke functions and not internal calls, hence why they use
|
|
||||||
// 'mono_bool' and 'const char*' instead of 'MonoBoolean' and 'MonoString*'.
|
|
||||||
|
|
||||||
#define GD_PINVOKE_EXPORT extern "C" __attribute__((visibility("default")))
|
|
||||||
|
|
||||||
GD_PINVOKE_EXPORT const char *xamarin_get_locale_country_code() {
|
|
||||||
NSLocale *locale = [NSLocale currentLocale];
|
|
||||||
NSString *countryCode = [locale objectForKey:NSLocaleCountryCode];
|
|
||||||
if (countryCode == nullptr) {
|
|
||||||
return strdup("US");
|
|
||||||
}
|
|
||||||
return strdup([countryCode UTF8String]);
|
|
||||||
}
|
|
||||||
|
|
||||||
GD_PINVOKE_EXPORT void xamarin_log(const uint16_t *p_unicode_message) {
|
|
||||||
int length = 0;
|
|
||||||
const uint16_t *ptr = p_unicode_message;
|
|
||||||
while (*ptr++) {
|
|
||||||
length += sizeof(uint16_t);
|
|
||||||
}
|
|
||||||
NSString *msg = [[NSString alloc] initWithBytes:p_unicode_message length:length encoding:NSUTF16LittleEndianStringEncoding];
|
|
||||||
|
|
||||||
os_log_info(OS_LOG_DEFAULT, "%{public}@", msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
GD_PINVOKE_EXPORT const char *xamarin_GetFolderPath(int p_folder) {
|
|
||||||
NSSearchPathDirectory dd = (NSSearchPathDirectory)p_folder;
|
|
||||||
NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:dd inDomains:NSUserDomainMask] lastObject];
|
|
||||||
NSString *path = [url path];
|
|
||||||
return strdup([path UTF8String]);
|
|
||||||
}
|
|
||||||
|
|
||||||
GD_PINVOKE_EXPORT char *xamarin_timezone_get_local_name() {
|
|
||||||
NSTimeZone *tz = nil;
|
|
||||||
tz = [NSTimeZone localTimeZone];
|
|
||||||
NSString *name = [tz name];
|
|
||||||
return (name != nil) ? strdup([name UTF8String]) : strdup("Local");
|
|
||||||
}
|
|
||||||
|
|
||||||
GD_PINVOKE_EXPORT char **xamarin_timezone_get_names(uint32_t *p_count) {
|
|
||||||
NSArray *array = [NSTimeZone knownTimeZoneNames];
|
|
||||||
*p_count = array.count;
|
|
||||||
char **result = (char **)malloc(sizeof(char *) * (*p_count));
|
|
||||||
for (uint32_t i = 0; i < *p_count; i++) {
|
|
||||||
NSString *s = [array objectAtIndex:i];
|
|
||||||
result[i] = strdup(s.UTF8String);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
GD_PINVOKE_EXPORT void *xamarin_timezone_get_data(const char *p_name, uint32_t *p_size) { // FIXME: uint32_t since Dec 2019, unsigned long before
|
|
||||||
NSTimeZone *tz = nil;
|
|
||||||
if (p_name) {
|
|
||||||
NSString *n = [[NSString alloc] initWithUTF8String:p_name];
|
|
||||||
tz = [[NSTimeZone alloc] initWithName:n];
|
|
||||||
} else {
|
|
||||||
tz = [NSTimeZone localTimeZone];
|
|
||||||
}
|
|
||||||
NSData *data = [tz data];
|
|
||||||
*p_size = [data length];
|
|
||||||
void *result = malloc(*p_size);
|
|
||||||
memcpy(result, data.bytes, *p_size);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
GD_PINVOKE_EXPORT void xamarin_start_wwan(const char *p_uri) {
|
|
||||||
// FIXME: What's this for? No idea how to implement.
|
|
||||||
os_log_error(OS_LOG_DEFAULT, "Not implemented: 'xamarin_start_wwan'");
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // IOS_ENABLED
|
|
@ -1928,11 +1928,15 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
|
|||||||
|
|
||||||
bool EditorExportPlatformIOS::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug) const {
|
bool EditorExportPlatformIOS::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug) const {
|
||||||
#ifdef MODULE_MONO_ENABLED
|
#ifdef MODULE_MONO_ENABLED
|
||||||
// Don't check for additional errors, as this particular error cannot be resolved.
|
#ifdef MACOS_ENABLED
|
||||||
r_error += TTR("Exporting to iOS is currently not supported in Godot 4 when using C#/.NET. Use Godot 3 to target iOS with C#/Mono instead.") + "\n";
|
// iOS export is still a work in progress, keep a message as a warning.
|
||||||
r_error += TTR("If this project does not use C#, use a non-C# editor build to export the project.") + "\n";
|
r_error += TTR("Exporting to iOS when using C#/.NET is experimental.") + "\n";
|
||||||
return false;
|
|
||||||
#else
|
#else
|
||||||
|
// TODO: Remove this restriction when we don't rely on macOS tools to package up the native libraries anymore.
|
||||||
|
r_error += TTR("Exporting to iOS when using C#/.NET is experimental and requires macOS.") + "\n";
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
String err;
|
String err;
|
||||||
bool valid = false;
|
bool valid = false;
|
||||||
@ -1963,7 +1967,6 @@ bool EditorExportPlatformIOS::has_valid_export_configuration(const Ref<EditorExp
|
|||||||
}
|
}
|
||||||
|
|
||||||
return valid;
|
return valid;
|
||||||
#endif // !MODULE_MONO_ENABLED
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EditorExportPlatformIOS::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const {
|
bool EditorExportPlatformIOS::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user