Closed mehlkelm closed 4 years ago
Thanks for your feedback! One step that’s missing is setting the NDK path in the app settings > SDK Location. If you’re on a Mac you should set it to the custom NDK that is installed by the toolchain in:
~/Library/Android/android-ndk-r20-clang-r353983c1
Can you check if that helps with the linker error?
I can't find "App settings". Do you mean "Android NDK location" in File > Project Structure… > SDK Location? Setting it there doesn't help.
Yeah that’s the right setting.
I found that for me, if I understand it correctly _ZNSt6__ndk111__call_onceERVmPvPFvS2_E
is defined in (not referenced by) libgnustep-base.so. Can you run the following and see if you get the same result?
$ nm ~/Library/Android/GNUstep/x86/lib/libgnustep-base.so | grep _ZNSt6__ndk111__call_onceERVmPvPFvS2_E
00758380 T _ZNSt6__ndk111__call_onceERVmPvPFvS2_E
Also, does the hello-objectivec example work for you?
The hello-objectivec example works perfectly, and that without any "Android NDK location" setting!
My result:
$ nm ~/Library/Android/GNUstep/x86/lib/libgnustep-base.so | grep _ZNSt6__ndk111__call_onceERVmPvPFvS2_E
U _ZNSt6__ndk111__call_onceERVmPvPFvS2_E
Hmm interesting. Looks like your libgnustep-base.so (unlike mine) doesn’t define the function. Could you attach your (zipped) ~/Library/Android/GNUstep/build.log
?
That being said, question is why the example still works... I noticed in step 6 above you wrote that you call System.loadLibrary()
in an init method. Can you try putting it in a static initializer as in the example?
static {
System.loadLibrary("native-lib");
}
I'm on Kotlin, so static won't work here. I tried to do it according to the "Native C++" Android Studio template (like below) which also doesn't work:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
initializeGNUstep(this);
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navView: BottomNavigationView = findViewById(R.id.nav_view)
val navController = findNavController(R.id.nav_host_fragment)
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
val appBarConfiguration = AppBarConfiguration(
setOf(
R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications, R.id.navigation_temperatures
)
)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
}
external fun initializeGNUstep(context: Context?)
companion object {
// Used to load the 'native-lib' library on application startup.
init {
System.loadLibrary("native-lib")
}
}
}
I’ve never used Kotlin – could you try it with a Java project just to narrow it down further?
Kotlin can't be the issue: a) I get the same problem with a brand new Java Project (no Kotlin, no AndroidX) b) I can convert your hello-objectivec and its MainActivity to Kotlin while still being able to load native-lib
I see. I have no idea why the example project works and a new project doesn’t, and I cannot reproduce the issue. But the core issue seems to be that your GNUstep library file doesn’t define some symbols that mine does. If you could send me your build.log I can see if I can spot some differences in your build process that might be specific to your machine/setup.
The good news: I got it working! The bad news: I still have to find out how… Will analyze a bit more and then post step by step how it works reproducibly for me.
This pull request would document what worked for me: https://github.com/gnustep/tools-android/pull/8.
Thanks for the PR! Can you tell what the missing bit was to get it working in the end?
In the end I tried so much stuff that I'm not sure what exactly worked but I suspect step 6: "Link to C++" was missing.
I fixed the underlying issue causing the linker error with _ZNSt6__ndk111__call_onceERVmPvPFvS2_E
: the toolchain libraries were not correctly linking against the shared C++ runtime library.
Please try rebuilding the toolchain with the latest changes, and also update your Gradle setup to use the shared C++ runtime library like this: https://github.com/gnustep/android-examples/commit/d3f5aba353718cd8c6a50ce56c1083b79993f8c7
Regarding instructions to integrate this in your own project I hope to be able to create a standalone CMake toolchain file that can simply be included in Android Studio (that’s why I haven’t merged your PR yet).
As an iOS developer with little Android experience (which makes GNUstep especially tempting) I had no problem going from the toolchain setup instructions to the sample project. However, configuring your own new or existing Android project for GNUstep is not yet documented.
Looking at the linked android-examples I tried the following (maybe this can be used as a draft for a new section at the end of the existing README, once it works):
New project in Android Studio a. Basic Fragment with View Model b. Enable Kotlin
Create folder app/src/main/cpp
Add CMakeLists.txt () to cpp folder
Edit CMakeLists.txt to include your Objective-C implementation (.m) files in OBJECTIVEC_SRCS
Edit app/build.gradle. Add:
externalNativeBuild { cmake { cppFlags "" } } ndk { // ABIs supported by GNUstep toolchain abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64" }
Edit MainActivity class: Add
init { System.loadLibrary(„native-lib“) }
So far this fails at System.loadLibrary("native-lib") with following error:
2020-02-12 12:32:19.933 985-985/zoziapps.ch.sousvidecelsius E/AndroidRuntime: FATAL EXCEPTION: main Process: zoziapps.ch.sousvidecelsius, PID: 985 java.lang.UnsatisfiedLinkError: dlopen failed: cannot locate symbol "_ZNSt6ndk111call_onceERVmPvPFvS2_E" referenced by "/data/app/zoziapps.ch.sousvidecelsius-qgnwdnfRmeXSFCUffVd7bw==/lib/x86/libgnustep-base.so"... at java.lang.Runtime.loadLibrary0(Runtime.java:1016) at java.lang.System.loadLibrary(System.java:1669) at zoziapps.ch.sousvidecelsius.MainActivity.(MainActivity.kt:15)
at java.lang.Class.newInstance(Native Method)
at android.app.AppComponentFactory.instantiateActivity(AppComponentFactory.java:69)
at androidx.core.app.CoreComponentFactory.instantiateActivity(CoreComponentFactory.java:43)
at android.app.Instrumentation.newActivity(Instrumentation.java:1215)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2831)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)