godot icon indicating copy to clipboard operation
godot copied to clipboard

Add linux-bionic RID Export Option

Open atlasapplications opened this issue 1 year ago • 1 comments

Supersedes #97901

Resolves #97775

@raulsntos hopefully I'm homing in to your design goals!

This time around I just replace the RID, as you mentioned, with the linux-bionic one when it's enabled for Android, which should be the only platform that the option shows up for.

atlasapplications avatar Oct 07 '24 00:10 atlasapplications

@raulsntos understood and done!

With .NET 9 in release candidate I'm excited to add this feature for Android as it will bring a big improvement in startup time for my game :)

atlasapplications avatar Oct 07 '24 13:10 atlasapplications

I tried this PR and enabled the linux bionic option, however it still failed to build when exporting for Android NativeAOT.

The PrivateSdkAssemblies ItemGroup is required for _ComputeAssembliesToCompileToNative 
/home/luo/.nuget/packages/microsoft.dotnet.ilcompiler/8.0.10/build/Microsoft.NETCore.Native.Publish.targets(76,5)

beicause avatar Nov 18 '24 13:11 beicause

I tried this PR and enabled the linux bionic option, however it still failed to build when exporting for Android NativeAOT.

The PrivateSdkAssemblies ItemGroup is required for _ComputeAssembliesToCompileToNative 
/home/luo/.nuget/packages/microsoft.dotnet.ilcompiler/8.0.10/build/Microsoft.NETCore.Native.Publish.targets(76,5)

What does your .csproj look like?

Some documentation may be necessary to note this since it may not be obvious, but extra options are added to the .csproj file to make NativeAOT work on Android. These are those options:

<PropertyGroup>
    <PublishAOT Condition=" '$(GodotTargetPlatform)' == 'android' ">true</PublishAOT>
    <DisableUnsupportedError Condition=" '$(GodotTargetPlatform)' == 'android' ">true</DisableUnsupportedError
    <PublishAotUsingRuntimePack Condition=" '$(GodotTargetPlatform)' == 'android' ">true</PublishAotUsingRuntimePack>
</PropertyGroup>
<ItemGroup>
    <TrimmerRootAssembly Include="GodotSharp" />
    <TrimmerRootAssembly Include="$(TargetName)" />
</ItemGroup>

atlasapplications avatar Nov 18 '24 13:11 atlasapplications

After adding <PublishAotUsingRuntimePack >true</PublishAotUsingRuntimePack>, still failed to build but the error message is diffrent:

MSB3073: The command ""clang" "/home/luo/new-game-project/.godot/mono/temp/obj/ExportDebug/linux-bionic-arm64/native/New Game Project.o" -o "/home/luo/new-game-project/.godot/mono/temp/bin/ExportDebug/linux-bionic-arm64/native/New Game Project.so" -Wl,--version-script=/home/luo/new-game-project/.godot/mono/temp/obj/ExportDebug/linux-bionic-arm64/native/New Game Project.exports -Wl,--export-dynamic -gz=zlib -fuse-ld=lld /home/luo/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.linux-bionic-arm64/8.0.10/runtimes/linux-bionic-arm64/native/libbootstrapperdll.o /home/luo/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.linux-bionic-arm64/8.0.10/runtimes/linux-bionic-arm64/native/libRuntime.WorkstationGC.a /home/luo/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.linux-bionic-arm64/8.0.10/runtimes/linux-bionic-arm64/native/libeventpipe-disabled.a /home/luo/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.linux-bionic-arm64/8.0.10/runtimes/linux-bionic-arm64/native/libstdc++compat.a /home/luo/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.linux-bionic-arm64/8.0.10/runtimes/linux-bionic-arm64/native/libSystem.Native.a /home/luo/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.linux-bionic-arm64/8.0.10/runtimes/linux-bionic-arm64/native/libSystem.Globalization.Native.a /home/luo/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.linux-bionic-arm64/8.0.10/runtimes/linux-bionic-arm64/native/libSystem.IO.Compression.Native.a /home/luo/.nuget/packages/microsoft.netcore.app.runtime.nativeaot.linux-bionic-arm64/8.0.10/runtimes/linux-bionic-arm64/native/libSystem.Security.Cryptography.Native.OpenSsl.a --target=aarch64-linux-android21 -g -Wl,-rpath,'$ORIGIN' -Wl,--build-id=sha1 -Wl,--as-needed -Wl,-e0x0 -pthread -ldl -lz -llog -lm -shared -Wl,-z,relro -Wl,-z,now -Wl,--eh-frame-hdr -Wl,--discard-all -Wl,--gc-sections -Wl,-T,"/home/luo/new-game-project/.godot/mono/temp/obj/ExportDebug/linux-bionic-arm64/native/sections.ld"" exited with code 1. /home/luo/.nuget/packages/microsoft.dotnet.ilcompiler/8.0.10/build/Microsoft.NETCore.Native.targets(366,5)

beicause avatar Nov 18 '24 14:11 beicause

What that error says to me is that your build tools may not be set up properly.

As a reminder NativeAOT compiles to platform specific machine code as opposed to the less platform specific IL code that the dotnet runtime interprets on non NativeAOT platforms. What that means for when we're building our project is that you need native compilers set up. I would double check everything is installed correctly which is outlined in the Godot docs here and here.

A few things that the Godot docs may not specify is that you need to set the ANDROID_NDK_HOME in addition to the ANDROID_HOME environment variables so dotnet knows where to look for clang which is probably that error right there. If you're on windows that path would look something like C:\Users\[luo]\AppData\Local\Android\Sdk\ndk\23.2.8568313.

atlasapplications avatar Nov 18 '24 14:11 atlasapplications

@atlasapplications Thank you for your guidance. I encountered two errors again ( tested in arch linux)

  1. (Resolved) The project name cannot contain spaces. Otherwise, there will be the following errors:
...
--version-script=/home/luo/new-game-project/.godot/mono/temp/obj/ExportDebug/linux-bionic-arm64/native/New Game Project.exports
clang: error: no such file or directory: 'Game'
clang: error: no such file or directory: 'Project.exports'
  1. It seems dotnet is still using my linux clang toolchain, but I have alreay set ANDROID_HOME and ANDROID_NDK_HOME
  1>ld.lld : error : cannot open crtbegin_so.o: No such file or directory [/home/luo/newgameproject/NewGameProject.csproj]
     1>ld.lld : error : unable to find library -llog [/home/luo/newgameproject/NewGameProject.csproj]
     1>ld.lld : error : cannot open /usr/lib/clang/18/lib/linux/libclang_rt.builtins-aarch64-android.a: No such file or directory [/home/luo/newgameproject/NewGameProject.csproj]
     1>ld.lld : error : unable to find library -l:libunwind.a [/home/luo/newgameproject/NewGameProject.csproj]
     1>ld.lld : error : cannot open /usr/lib/clang/18/lib/linux/libclang_rt.builtins-aarch64-android.a: No such file or directory [/home/luo/newgameproject/NewGameProject.csproj]
     1>ld.lld : error : unable to find library -l:libunwind.a [/home/luo/newgameproject/NewGameProject.csproj]
     1>ld.lld : error : cannot open crtend_so.o: No such file or directory [/home/luo/newgameproject/NewGameProject.csproj]
     1>clang : error : linker command failed with exit code 1 (use -v to see invocation) [/home/luo/newgameproject/NewGameProject.csproj]

beicause avatar Nov 18 '24 14:11 beicause

Unfortunately I'm not as familiar with building this on Linux but what I would do is start by checking that the environment variables are returning what is expected.

I think on Linux you can put echo "$ANDROID_NDK_HOME" which should be equivalent to echo $ENV:ANDROID_NDK_HOME on windows. Then I would check clang --version and see if it's the right one, and so on. That error seems to indicate the NDK is still not being found (or wrong one) so it might still be related to how you set the environment variable.

atlasapplications avatar Nov 18 '24 16:11 atlasapplications

I can run the follow command successfully by setting CppCompilerAndLinker ( or add <CppCompilerAndLinker>$(ANDROID_NDK_HOME)/toolchains/llvm/prebuilt/linux-x86_64/bin/clang</CppCompilerAndLinker> to csproj):

dotnet publish -r linux-bionic-arm64 -c ExportDebug -p:CppCompilerAndLinker=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/clang -p:PublishAOT=true -p:DisableUnsupportedError=true -p:PublishAotUsingRuntimePack=true

By setting CppCompilerAndLinker in csproj it still can't be successfully built in Godot. The error message:

  ./platform/android/export/export_plugin.cpp:760 - Android .so file names must start with "lib", but got: /tmp/godot-publish-dotnet/269823-ExportDebug-linux-bionic-arm64/NewGameProject.so
  ./editor/export/editor_export_platform.h:239 - Export: Could not export project files.
  Cannot remove non-existent file or directory: '/home/luo/.cache/godot/tmpexport.1731953638.apk'.

Then I tried to specify the TargetName in csproj to add prefix, the problem is that the export plugin still searches .so based on ProjectAssemblyName.

<Project Sdk="Godot.NET.Sdk/4.4.0-dev">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <EnableDynamicLoading>true</EnableDynamicLoading>
    <RootNamespace>NewGameProject</RootNamespace>
    <!--Enable NativeAOT-->
    <PublishAOT >true</PublishAOT>
    <DisableUnsupportedError Condition=" '$(GodotTargetPlatform)' == 'android' ">true</DisableUnsupportedError>
    <PublishAotUsingRuntimePack Condition=" '$(GodotTargetPlatform)' == 'android' ">true</PublishAotUsingRuntimePack>
    <CppCompilerAndLinker Condition=" '$(GodotTargetPlatform)' == 'android' ">$(ANDROID_NDK_HOME)/toolchains/llvm/prebuilt/linux-x86_64/bin/clang</CppCompilerAndLinker>
    <TargetName Condition=" '$(GodotTargetPlatform)' == 'android' ">lib$(MSBuildProjectName)</TargetName>
  </PropertyGroup>

  <ItemGroup>
    <!-- Root the assemblies to avoid trimming. -->
    <TrimmerRootAssembly Include="GodotSharp" />
    <TrimmerRootAssembly Include="$(TargetName)" />
  </ItemGroup>

</Project>

屏幕截图_20241119_124022

beicause avatar Nov 18 '24 17:11 beicause

Adding the lib prefix and copying the .so into the apk will result runtime errors:

2024-11-19 13:18:15.390 31066-31174 godot                   com.example.game                     E  ERROR: Can't open dynamic library: libmonosgen-2.0.so. Error: dlopen failed: library "libmonosgen-2.0.so" not found.
2024-11-19 13:18:15.390 31066-31174 godot                   com.example.game                     E     at: open_dynamic_library (platform/android/os_android.cpp:240)
2024-11-19 13:18:15.391 31066-31174 godot                   com.example.game                     E  ERROR: Can't open dynamic library: /data/data/com.example.game/cache/data_NewGameProject_android_arm64/NewGameProject.so. Error: dlopen failed: library "NewGameProject.so" not found.
2024-11-19 13:18:15.391 31066-31174 godot                   com.example.game                     E     at: open_dynamic_library (platform/android/os_android.cpp:240)
2024-11-19 13:18:15.391 31066-31174 godot                   com.example.game                     E  ERROR: .NET: Failed to load hostfxr
2024-11-19 13:18:15.391 31066-31174 godot                   com.example.game                     E     at: initialize (modules/mono/mono_gd/gd_mono.cpp:549)

Instead, after removing the check, I successfully build and run on Android:

diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index 9545d74730..c5282bdef6 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -755,11 +755,6 @@ Error EditorExportPlatformAndroid::store_in_apk(APKExportData *ed, const String
 }
 
 Error EditorExportPlatformAndroid::save_apk_so(void *p_userdata, const SharedObject &p_so) {
-	if (!p_so.path.get_file().begins_with("lib")) {
-		String err = "Android .so file names must start with \"lib\", but got: " + p_so.path;
-		ERR_PRINT(err);
-		return FAILED;
-	}
 	APKExportData *ed = static_cast<APKExportData *>(p_userdata);
 	Vector<ABI> abis = get_abis();
 	bool exported = false;

I think removing the check is a simple solution If it's not necessary.

beicause avatar Nov 19 '24 06:11 beicause

@beicause I don't know if we can remove that check, the error message seems to imply this is a restriction of the Android platform. The @godotengine/android team can confirm if this check is safe to remove.

Alternatively, if we need the native library to start with a lib prefix we can probably rename it before copying it and add a special case to the try_load_native_aot_library function for Android like so:

diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index c81aa91fa5..9de762a408 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -416,6 +416,8 @@ godot_plugins_initialize_fn try_load_native_aot_library(void *&r_aot_dll_handle)
 	String native_aot_so_path = GodotSharpDirs::get_api_assemblies_dir().path_join(assembly_name + ".dll");
 #elif defined(MACOS_ENABLED) || defined(IOS_ENABLED)
 	String native_aot_so_path = GodotSharpDirs::get_api_assemblies_dir().path_join(assembly_name + ".dylib");
+#elif defined(ANDROID_ENABLED)
+	String native_aot_so_path = "lib" + assembly_name + ".so";
 #elif defined(UNIX_ENABLED)
 	String native_aot_so_path = GodotSharpDirs::get_api_assemblies_dir().path_join(assembly_name + ".so");
 #else

This is because since https://github.com/godotengine/godot/pull/88803 we are including every native library .so into the lib directory of the APK. So we should use only the filename to open the native library and Android will know where to find it.

raulsntos avatar Dec 19 '24 06:12 raulsntos

@beicause I don't know if we can remove that check, the error message seems to imply this is a restriction of the Android platform. The @godotengine/android team can confirm if this check is safe to remove.

From a quick search, that indeed seems to be an Android specific restriction, so I don't think we can remove the check.

@raulsntos I'd echo your proposal of renaming the library instead and updating the loading logic.

m4gr3d avatar Dec 19 '24 19:12 m4gr3d

@beicause This is my previous project, I used #86791 to export Android aot, maybe you can refer to it? https://github.com/scgm0/Book-of-Genesis

scgm0 avatar Dec 20 '24 00:12 scgm0

@atlasapplications @raulsntos Any updates on this PR? The current Android changes look good but they don't include the updates mentioned in https://github.com/godotengine/godot/pull/97908#issuecomment-2552927695 to update the library name.

m4gr3d avatar Jan 21 '25 05:01 m4gr3d

@beicause I don't know if we can remove that check, the error message seems to imply this is a restriction of the Android platform. The @godotengine/android team can confirm if this check is safe to remove.

Alternatively, if we need the native library to start with a lib prefix we can probably rename it before copying it and add a special case to the try_load_native_aot_library function for Android like so:

diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index c81aa91fa5..9de762a408 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -416,6 +416,8 @@ godot_plugins_initialize_fn try_load_native_aot_library(void *&r_aot_dll_handle)
 	String native_aot_so_path = GodotSharpDirs::get_api_assemblies_dir().path_join(assembly_name + ".dll");
 #elif defined(MACOS_ENABLED) || defined(IOS_ENABLED)
 	String native_aot_so_path = GodotSharpDirs::get_api_assemblies_dir().path_join(assembly_name + ".dylib");
+#elif defined(ANDROID_ENABLED)
+	String native_aot_so_path = "lib" + assembly_name + ".so";
 #elif defined(UNIX_ENABLED)
 	String native_aot_so_path = GodotSharpDirs::get_api_assemblies_dir().path_join(assembly_name + ".so");
 #else

This is because since #88803 we are including every native library .so into the lib directory of the APK. So we should use only the filename to open the native library and Android will know where to find it.

Should those changes be done in a new PR or should I go ahead and do them here?

atlasapplications avatar Jan 25 '25 15:01 atlasapplications

Yes, go ahead and include those changes in this PR; otherwise, the export won't work.

raulsntos avatar Jan 26 '25 02:01 raulsntos

@atlasapplications It's still missing the rename. I didn't include the code for that in my previous comment, sorry about that.

Something like this should work:

diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
index 591e558268..50cdab13b0 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
@@ -363,6 +363,14 @@ namespace GodotTools.Export
 
                                         if (IsSharedObject(fileName))
                                         {
+                                            if (fileName.EndsWith(".so") && !fileName.StartsWith("lib"))
+                                            {
+                                                // Add 'lib' prefix required for all native libraries in Android.
+                                                string newPath = string.Concat(path.AsSpan(0, path.Length - fileName.Length), "lib", fileName);
+                                                Godot.DirAccess.RenameAbsolute(path, newPath);
+                                                path = newPath;
+                                            }
+
                                             AddSharedObject(path, tags: new string[] { arch },
                                                 Path.Join(projectDataDirName,
                                                     Path.GetRelativePath(publishOutputDir,

raulsntos avatar Jan 27 '25 20:01 raulsntos

I'm queueing this for 4.5-dev1 as we're currently in feature freeze for 4.4, but this should otherwise be ready to merge.

akien-mga avatar Feb 05 '25 10:02 akien-mga

Thanks! Congratulations on your first merged contribution! 🎉

Repiteo avatar Mar 07 '25 21:03 Repiteo

Hi, sorry to bother you.

I'm on Godot 4.5 dev5

dotnet publish -c ExportRelease -r linux-bionic-arm64 works and generates a .so file.

When exporting from the editor (gradle builds are enabled, but I see no error there): "Export .NET Project: Failed to build project"

<PropertyGroup>
    <DisableUnsupportedError>true</DisableUnsupportedError>
    <PublishAotUsingRuntimePack>true</PublishAotUsingRuntimePack>
  	<CppCompilerAndLinker>$(ANDROID_NDK_HOME)/toolchains/llvm/prebuilt/current-os/bin/clang</CppCompilerAndLinker>
    <TargetName>lib$(MSBuildProjectName)</TargetName>

    <PublishAot>true</PublishAot>

    <IlcGenerateMapFile>false</IlcGenerateMapFile>
    <IlcDisableUnsupportedError>true</IlcDisableUnsupportedError>
  </PropertyGroup>
  
  <ItemGroup>
    <LinkerArg Include="-Wl,--undefined-version" />
  </ItemGroup>
  
  <ItemGroup>
    <TrimmerRootAssembly Include="GodotSharp" />
    <TrimmerRootAssembly Include="$(TargetName)" />
  </ItemGroup>

"Android Use Linux Bionic" is true

Can the editor be configured to give more details?

oxygen avatar Jun 09 '25 18:06 oxygen

Take a look at the MSBuild panel for the output of the dotnet publish command.

raulsntos avatar Jun 09 '25 22:06 raulsntos

requestd linker (path to clang) not found in PATH. (on Windows the error contains the full path starting with C:, on MacOS the error contains a partial path in an incorrect absolute form)

The same error on both Windows and MacOS.

It is in path, checked with which and where.

Did you ever encounter this?

.net 8

Edit:

Providing a full path to clang without $ variables in csproj switched to the next error: Symbol stripping tool ('llvm-objcopy' or 'objcopy') not found in PATH. Try installing appropriate package for llvm-objcopy or objcopy to resolve the problem or set the StripSymbols property to false to disable symbol stripping.

So I added <StripSymbols>false</StripSymbols> which Microsoft says it started defaulting to true in .net 7

Now I have Publish succeeded but project assembly not found /random/temp/path/to/NameNnotStartingWithLib.dll or /random/temp/path/to/NameNotStartingWithLib.so

So it appears the editor is looking for the ProjectName.dll or ProjectName.so instead of libProjectName.dll or libProjectName.so

Removing the lib prefixing in csproj solved it.

I'll leave this here in case anyone goes through the same motions and stumbles upon this thread.

Thanks for the help :)

BTW: FPS doubled on low end Android device for an animation which I thought was capped by 3D complexity. It was actually the tween on the CPU. NICE!!!!

oxygen avatar Jun 10 '25 08:06 oxygen