Linking to the C++ runtime with a Flutter app on Android - android

I'm trying to make use of a prebuilt dynamic library in a Flutter (v2.2.1) Android application.
I've placed the compiled libs into the android/app/src/main/jniLibs folder under target specific sub-folders, and they're picked up correctly by the build system and I can load the library with DynamicLibrary.open().
The library is written with Rust, but it makes use of a C++ library and I'm getting the error dlopen failed: cannot locate symbol "__cxa_pure_virtual" referenced by...
So my question is, how exactly do I tell the build system to link against the C++ runtime?
The Android 'C++ library support' page explains that the default runtime for ndk-build is none, and that I should add APP_STL := c++_shared to my Application.mk file.
Up until this point I didn't have an Application.mk file, but I could see that adding one might make sense, so I added the APP_STL definition to android/app/jni/Application.mk, and after some reading I decided that I should also add an Android.mk file (I don't know if it's actually required), with the following contents:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := foo-prebuilt
LOCAL_SRC_FILES := $(TARGET_ARCH_ABI)/libfoo.so
include $(PREBUILT_SHARED_LIBRARY)
From what I understand these then need to be picked up by Gradle, so in android/app/build.gradle I've added:
...
android {
...
externalNativeBuild {
ndkBuild {
path 'jni/Android.mk'
path 'jni/Application.mk'
}
}
}
Unfortunately this doesn't have any effect (I get the same error, and inspecting the APK shows that libc++_shared.so hasn't been included). I'm at the point where I'm thinking there's surely a simpler way to tell the build system to link against libc++_shared.so?
If I'm going down the right path, any ideas for why isn't my APP_STL definition being applied?

I guess you should put libc++_shared.so into dir:android/app/src/main/jniLibs.

Related

skip prebuilt-library.mk on gradle sync android studio

My project app depends on a library module alib. both app and alib build a c++ library with ndk. the app c++ lib, called app.so, depends on alib.so, which is the c++ lib for the library module. Inside the android.mk of app, i have:
LOCAL_PATH := $(the_right_alib_path)
include $(CLEAR_VARS)
LOCAL_MODULE := alibsdk
LOCAL_SRC_FILES := libalib.so
include $(PREBUILT_SHARED_LIBRARY)
Gradle sync fails on prebuilt-library.mk, with message Android NDK: Check that /the_right_alib_path/libalib.so exists or that its path is correct.
the file would be there if i build the module with gradle, it's naturally not there when I do gradle sync.
How can I skip the execution of prebuilt-library.mk?
Alternatively there is a way to tell ndk that the alib.so will be built by another gradle module?
Ps. This is more annoying cause in reality it checks all the libraries for different versions/flavor/dimensions, and i don't need to build all those libraries to work on the dev version on the app.
You can avoid the error by small modification of the Android.mk:
include $(CLEAR_VARS)
LOCAL_MODULE := alibsdk
LOCAL_SRC_FILES := libalib.so
ifeq ($(findstring n,$(MAKEFLAGS)),n)
include $(BUILD_SHARED_LIBRARY)
else
include $(PREBUILT_SHARED_LIBRARY)
endif
This takes advantage of the -n flag that is passed to ndk-build during sync. You can create a custom define for that, if you wish. build-shared-library.mk will generate a warning about strange SRC, but won't fail.
Same can be achieved without touching the Android.mk files: in your build.gradle, use
if (project.gradle.startParameter.taskNames.isEmpty()
|| project.gradle.startParameter.taskNames[0].contains(":generate")) {
android.defaultConfig.externalNativeBuild.ndkBuild.arguments += 'PREBUILT_SHARED_LIBRARY=$(BUILD_SHARED_LIBRARY)'
}
Android Studio runs ndk-build many times: with no tasks (the Sync step), within a :generate[flavor][Debug|Release]Sources, and within :externalNativeBuild[flavor][Debug|Release].
To retrieve the current task, I followed https://stackoverflow.com/a/21603707/192373.
Maybe the easiest fix is to rely on an obscure implementation detail of prebuilt-library.mk*). Early in your main Android.mk, add the line
override prebuilt=$(if $(strip $(wildcard $(prebuilt_path))),$(prebuilt_path),.)
This can be done in gradle, if necessary:
android.defaultConfig.externalNativeBuild.ndkBuild {
arguments 'prebuilt=$(if $(strip $(wildcard $(prebuilt_path))),$(prebuilt_path),.)'
}
*) verified for NDK r19b and earlier.

Include prebuilt static library on existing aosp module

I am trying to include my prebuilt library on the existing Android Module art/runtime/. I have followed the official documentation, found here. In detail, these are the changes I 've made:
Added lines in art/Android.mk:
# This makes sure my library's .mk is found
include $(art_path)/mylib/Android.mk
Added lines in art/runtime/Android.mk:
# Added this, after the LOCAL_C_INCLUDES
LOCAL_STATIC_LIBRARIES := mylib-prebuilt
# Also, on the next mention of LOCAL_STATIC_LIBRARIES, I changed the
# assignment operator to '+=', so mylib won't overriden
Include in art/runtime.cc source mylib's header:
#include "mylib.h"
// then at some point use it
I have put mylib sources in art/mylib. I manually build, using a regular Makefile, the archive libmylib.a, which I want to be statically linked to libart.so.
In the Android.mk found at art/mylib I have added the following:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := mylib-prebuilt
LOCAL_SRC_FILES := $(LOCAL_PATH)/libmylib.a
LOCAL_EXPORT_C_INCLUDES := \
$(LOCAL_PATH)/mylib.h \
$(LOCAL_PATH)/another_header.h
include $(PREBUILT_STATIC_LIBRARY)
By exporting the C includes, the dependent modules should have automatically appended them to their local list. However, this is not the case, as I get the error:
fatal error: 'mylib.h' file not found
If I workaround this issue, with a stupid way that I really shouldn't, I stumble upon a link error, as my libmylib.a is never used during linking.
Other information:
I have placed mylib in art/mylib as it will be part of the art namespace and its methods will be calling/being called by art/runtime sources, e.g. runtime.cc. I have chosen to go with prebuilt-libraries, instead of directly including my sources, like Garbage Collector (found at art/runtime/gc`), as I want to pass particular compilation flags to individual source files.
I have already tried, and failed, solutions to other questions, found here and here.
Any thoughts?
EDIT:
When I try to make a full build, I get the following error:
make: *** No rule to make target
'out/host//obj/STATIC_LIBRARIES/mylib-prebuilt_intermediates/export_includes',
needed by
'out/host//obj/EXECUTABLES/dex2oat_intermediates/import_includes'.
Stop.
Thanks!
I finally managed to get this to work.
My library is now placed at art/runtime/mylib.
In art/runtime/Android.mk, I include the sources with:
LOCAL_C_INCLUDES += art/runtime/mylib
This makes mylib.h visible to the runtime source files.
The art/runtime/mylib/Android.mk has changed almost completely to:
LOCAL_MODULE := libmylib
LOCAL_SRC_FILES := libmylib.a
LOCAL_MODULE_SUFFIX := .a
LOCAL_MODULE_CLASS := STATIC_LIBRARIES
include $(BUILD_PREBUILT)
In my previous attempts, LOCAL_EXPORT_C_INCLUDES, in contrary to documentation, it didn't copy files to the dependent modules. Also PREBUILT_STATIC_LIBRARY didn't seem to be working.
Now, I can build my library in an archive of objects, using regular makefiles, and the build system copies it each time it gets changed to the appropriate locations, so at the link stage can be blended with libart.so.

Failure to build a project using NDK

I'm trying out NDK for the first time, and I'm stuck with building this project. I'm attempting to use the libraries used in Android's screencap recording program to read data from my screen. I understand that I need to use LibGUI, which I've pulled from my phone. Whenever I compile, however, I get this as an error message:
C:/Users/Kevin/Desktop/ScreenCapture//jni/main.cpp:2:34: fatal error: gui/ISurfaceComposer.h: No such file or directory
Which is odd, considering that I have libgui.so included.
What my main file looks like:
#include <stdio.h>
#include <gui/ISurfaceComposer.h>
int main() {
printf("Started!");
return 0;
}
What my Android.mk looks like:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libgui-prebuilt
LOCAL_SRC_FILES := libgui.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := screencapture
LOCAL_SRC_FILES := main.cpp
LOCAL_SHARED_LIBRARIES := libgui-prebuilt
include $(BUILD_EXECUTABLE)
And I have both the main file and libgui.so in the same folder.
main.cpp:2:34: fatal error: gui/ISurfaceComposer.h: No such file or directory
Your error message indicates that the compiler is unable to find a header file, but your attempted solution is to provide a shared object (.so) library from the phone.
Libraries might help at link time, but before you can link you must successfully compile, and to do that you are going to have to obtain the missing header (perhaps from AOSP sources, as source code it will not be found on the phone) or else recreate it by deducing its contents from clues you collect. Unfortunately, it often isn't possible to take just one internal header out of AOSP, as it will likely depend on many others.
Linking against private system internals also introduces a risk of your program breaking if the private interface between internal components changes between Android versions.
(Normally I would also mention that you are unlikely to be able to record a screen with the NDK in it's normal usage of making a shared library to link into an application process, however your Android.mk seems to indicate you are building an executable - that might work if you can succesfully build it and you run it as a user with sufficient permission such as adb's shell userid or using a root hack)
Add one line for module screencapture:
LOCAL_C_INCLUDES := $(LOCAL_PATH)/
If you haven't folder gui, you need to copy&paste includes headers *.h into folder jni/gui. Always such a requirement about using like that for ndk...
Or second case - using other location of this source for ldlibs:
LOCAL_LDLIBS := -L/full_path_to_source_gui/gui/

How to build multiple projects in the correct dependency order with Android NDK?

I have a series of existing libraries which I need to re-use in an Android application. The layout is similar to:
\Libraries\libOne
\Libraries\libTwo [Static library]
\Libraries\libThree
\Applications\MyApplication\ [Application]
libTwo depends on libOne, and libThree depends on libTwo. How can I get the build system to build all of the libraries in the correct order? I'm trying to use Eclipse, but if necessary I can use the command line.
All of these libraries will eventually be referenced by a Java application (and use JNI to interact with them). Any clues on how I setup the Android.mk / Application.mk files?
I've tried using BUILD_STATIC_LIBRARY for libTwo, but it doesn't actually output any files! I was expecting a libTwo.a file, but nothing gets compiled or built.
Do I write one Android.mk in the application? Or an Android.mk for each project?
OK, now I see your edit, and this makes it possible to answer the specific question.
You must have at least one Android.mk file for your application if you want to use Android NDK to build your native library/ies. This is not a requirement, though. It is OK to build it though Cmake, or a "standalone toolchain" with "traditional" makefiles, or with a MS Visual Studio plugin, or any other way. It is the result that matters. The result is a shared object built with a compatible compiler for a bionic runtime.
It makes goode sense to put the library in ${project_root}/libs/armeabi/ directory (for ARM v6 compatible devices, other subdirectories for x86, MIPS, arm v7a) to allow the APK builder pack it correctly, to allow app installer to unpack the correct version (compatible with the device processor) into /data/data/${package_name}/lib directory on the device, and finally to be able to use System.loadLibrary(short_name) to use it from Java. But it is also quite possible to pack the so file differently, unpack it manually, and load it from any place on the device file system (provided your app has permission to write and read this file).
But if we filter out exotic cases, it is much more comfortable to have an Android.mk in the ${project_root}/jni directory. In terms of ndk-build command, each library is a separate MODULE, but all three may be defined in one Android.mk file. On the other hand, if your libraries are isolated (e.g. come from separate 3rd parties), you will probably prefer to create three Android.mk files. Luckily, ndk-build is nothing but a wrapper around gnu make, and the simple include statement in Android.mk works as in any other makefiles.
In summary, your case is probably covered by a simple Applications/MyApplication/ [Application]/jni/Android.mk file:
include ../../Libraries/libOne/Android.mk
include ../../Libraries/libTwo/Android.mk
include ../../Libraries/libThree/Android.mk
I don't know what dependency you have between libOne and libTwo, but for libOne the file Libraries/libOne/Android.mk will look like
LOCAL_PATH = $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libOne
LOCAL_SRC_FILES := first.c
include $(BUILD_STATIC_LIBRARY)
and Libraries/libThree/Android.mk
LOCAL_PATH = $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libThree
LOCAL_SRC_FILES := third.c
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../libOne $(LOCAL_PATH)/../libTwo
LOCAL_STATIC_LIBRARIES := libOne libTwo
include $(BUILD_SHARED_LIBRARY)
You should run ndk-build from Applications/MyApplication/ [Application] directory - either from command prompt, or through Eclipse ADT plugin.
update the same may be expressed by one Android.mk file in jni directory:
LOCAL_PATH = ../../Libraries/libOne
include $(CLEAR_VARS)
LOCAL_MODULE := libOne
LOCAL_SRC_FILES := first.c
include $(BUILD_STATIC_LIBRARY)
LOCAL_PATH = ../../Libraries/libThree
include $(CLEAR_VARS)
LOCAL_MODULE := libThree
LOCAL_SRC_FILES := third.c
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../libOne $(LOCAL_PATH)/../libTwo
LOCAL_STATIC_LIBRARIES := libOne libTwo
include $(BUILD_SHARED_LIBRARY)
There is an android section in the projects' properties, where you can edit the library dependencies. It can only be used, if libOne libTwo and libThree are marked as libraries, in their properties panel.

Accessing headers for NDK from Android Library Project

I'm currently implementing a custom Logging mechanism that I need to be accessible from both native and Java code. The fundamentals of the logging are implemented in C/C++ with a Java wrapper, and the two together are being built as an android Library Project.
The issue at hand is that while my Java code can access the Library project output, there doesn't seem to be a way for my native code to access the native .so or headers from the Library project. Is there an additional step I'm missing or is this just a limitation of the current ADT? More specifically, is there a makefile/eclipse configuration that will address the things I'm used to getting out of Library projects in general? (Build .so as needed, import rebuilt .so, import relevant headers for c/c++ compilation, etc.)
I don't think it's a limitation. We are supposed to declare native code dependencies in Android.mk and Application.mk
Worked out a way to get what I wanted - most of the information is (of course) in the NDK documentation, but what I was trying to do isn't 100% supported within the ADT. It should also be noted that I'm currently stuck developing in a windows environment, so much of this might be easier or unnecessary in Linux. The first key is the $(call import-module ...) macro. Within your library project, move the source files and the Android.mk folder into a named directory you can locate later. My Library project directory looked like this:
MyProject
> src
> res
v jni
- Application.mk
v MyLib
- source.cpp
- source.h
- Android.mk
I also had to edit my Application.mk to point to the project:
APP_PROJECT_PATH := <path-to-my-project>
APP_BUILD_SCRIPT := $(APP_PROJECT_PATH)/jni/MyProject/Android.mk
Annoyingly, this broke my Android.mk in ways unforseen until I added a ./ to my source files. Also you need to export your includes for linking:
LOCAL_SRC_FILES := ./source.cpp
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
Ensure that the system path variable NDK_MODULE_PATH is set to include your library's JNI directory, e.g. <path-to-my-project>/jni (Note: I had to restart eclipse after I did this).
In the receiving application's Android.mk file (the one you'd like to link natively to your app), import the module:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := MyNativeProject
LOCAL_SRC_FILES := source.cpp
LOCAL_CFLAGS := -DANDROID_NDK -g -std=c99
LOCAL_SHARED_LIBRARIES := MyLib
include $(BUILD_SHARED_LIBRARY)
$(call import-module,android/native_app_glue)
$(call import-module, IntelLog)
At this point everything built perfectly, but the APK packager didn't like the generated .so binary being included twice (once from the Library project's natural import process, and again from the import-module call). To solve this, clean the library and don't build it again! the import-module call will build the .so and import it into your project. (Obviously, if your project only requires the Java API, you would need that .so file to be built). Congratulations! you have a functional (if not straightforward) build process with a hybrid native/Java Library

Categories

Resources