Android NDK - problem linking an external library (can't found it) - android-ndk

I am working with Android NDK r6b under cygwin (the system is updated correctly). I am modifying the hello-jni sample in order to learn working with NDK. Since i have a library written in C++ that i wish to use in the hello-jni (actually, i have created a prj called helloworld with a single .cpp file called ndkfoo.cpp) sample, i created a new Android project in Eclipse (updated for Android), added a jni directory, added a Android.mk and Application.mk files, edited them in order to compile the .cpp. At the end of the compilation, i obtain a .so file.
Now, in the helloworld Android.mk, i need to make some edits in order to tell the linker to include that library. Suppose the library file is libmylib.so, i have the following android.mk script:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := ndkfoo
LOCAL_SRC_FILES := ndkfoo.cpp
LOCAL_C_INCLUDES += $(LOCAL_PATH)/mylib
LOCAL_LDLIBS += -L$(LOCAL_PATH)/../../mylib/libs/armeabi/ -lmylib
include $(BUILD_SHARED_LIBRARY)
My directories are organized in the following way:
d:/android/android-ndk-r6b => android ndk root
d:/android/workspace/helloworld => helloworld project
d:/android/workspace/mylib => mylib project library
(therefore, the path to libmylib.so is: d:/android/workspace/mylib/libs/armeabi).
Unfortunately, this doesn't seems to work. If i remove every reference to mylib from ndkfoo.cpp, it compiles and run even on my phone. If i do not remove references to mylib, it compiles but doens't link: i obtain the following result:
D:/android/android-ndk-r6b/toolchains/arm-linux-androideabi-4.4.3/prebuilt/windo
ws/bin/../lib/gcc/arm-linux-androideabi/4.4.3/../../../../arm-linux-androideabi/
bin/ld.exe: cannot find -lmylib
Ps.
I forgot to mention that i run ndk-buld under the jni directory of the helloworld project.
Pss.
I have found a lot of similar questions over the internet. I have always worked with Visual C/C++ IDE, so i am really new to GCC, makefiles and so on...

The message
D:/android/android-ndk-r6b/toolchains/arm-linux-androideabi-4.4.3/prebuilt/windo ws/bin/../lib/gcc/arm-linux-androideabi/4.4.3/../../../../arm-linux-androideabi/ bin/ld.exe: cannot find -lmylib
is indicating you that the linker is not finding your library, this should come from a problem in the LD_LIBS' path.
I think that my-dir macro does not include devices' unit identifier so, your LOCAL_PATH variable should miss the D: and, I think, won't work with cygwin. I'm a Linux user and I'm not 100% sure but, if you replace
LOCAL_PATH := $(call my-dir)
by
LOCAL_PATH := D:$(call my-dir)
it should work. On the other hand you can always set the relative path by setting:
LOCAL_LDLIBS += -L$../../mylib/libs/armeabi/ -lmylib

Related

How do I convert a simple jni example from Cmake to ndk-build?

I'm a long time programmer but new to Android Studio. I have inherited some existing JNI library code intended to be run with ndk-build that already has its Android.mk file. It would be nice to reuse that. When I opened a new project with Android Studio and set the C++ support flag, it set itself up to use CMake instead of ndk-build. The documentation for Android Studio says both are available so I decided to try a simple test to see if I had found all the places that needed to change. It was a complete fail with error messages that seem to lead nowhere.
To recreate, create a new project ProjTest2 with Android Studio and check the Include C++ Support box. Accept the default Phone and Tablet form factor with a Minimum SDK of API 16: Android 4.1. Choose the Empty Activity. Accept the default Activity and Layout names. Accept the default Toolchain and do not check the boxes for Exceptions Support or Runtime. Run program, select device Android Emulator Nexus_5_API_24:5554. All is well; get message Hello from C++ on emulator screen.
Attempt to change to ndk-build. Open build.grade file for Module:app. Replace lines:
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
with:
externalNativeBuild {
ndkBuild {
path "jni/Android.mk"
}
}
Create a new folder jni in the directory ProjTest2/app. Create the file Android.mk in that folder with the following content:
# A simple test for the minimal standard C++ library
#
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := native-lib
LOCAL_SRC_FILES := $(LOCAL_PATH)/../src/main/cpp/native-lib.cpp
LOCAL_LDLIBS := -llog -landroid
include $(BUILD_SHARED_LIBRARY)
The build now fails claiming that it cannot find the target of #include <string> and that it cannot resolve the container 'std'. I find that I can clear the IDE error flags by adding LOCAL_C_INCLUDES += C:\Users\JWC\AppData\Local\Android\sdk\ndk-bundle\sources\cxx-stl\gnu-libstdc++\4.9\include to the Android.mk file but the resulting make still fails when it cannot find some of the sub-includes from <string>.
Did I miss something simple, or is this just not as easy as it seems it should be?
Edit:
Also needs file:
# Application.mk
APP_ABI := armeabi-v7a armeabi
APP_PLATFORM := android-21
APP_OPTIM := release
APP_STL := gnustl_static
CMake defaults to using the gnustl_static STL, whereas ndk-build defaults to using none.
Add the following to your project:
jni/Application.mk:
APP_STL := gnustl_static
There are a handful of these available. gnustl_static will match the cmake use, but you can see other choices in the official docs.

ndk-build of static lib - why is it building other files?

I have a project structure as follows. All I want to do is build a static library with SQLite source but for some reason when I run ndk-build, it builds other source too - as if it is including Android.mk files in other locations.
jni/SQLite
jni/SQLite/Android.mk (the only .mk file that I want to build)
jni/TT
jni/Application.mk
jni/Android.mk (for building everything such as SDL2)
TT is a symbolic link to /work/TT which contains a number of cross-platform source files including SQLite, SDL2 and other source.
/work/TT
/work/TT/SQLite
/work/TT/SQLite/sqlite3.c
/work/TT/SDL2
..
Here is jni/SQLite/Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
TT_PATH := /work/TT
SRC_SQLITE := $(TT_PATH)/SQLite/sqlite3.c
LOCAL_C_INCLUDES += $(NDK_APP_PROJECT_PATH)/jni
LOCAL_MODULE := SQLite
LOCAL_SRC_FILES := $(SRC_SQLITE)
include $(BUILD_STATIC_LIBRARY)
Why are other files being built? What do I need to do to build SQLite only?
At the command prompt I am in the following folder:
jni/SQLite
I run ndk-build (same folder as the Android.mk contents above)
I expect to see sqlite3.c being the only file compiled. But no, I see all of the SDL2 project files being built..
Once all files have been compiled, if I run ndk-build again I see this:
MacbookPro:SQLite admin$ ndk-build
[armeabi-v7a] Install : libSDL2.so => libs/armeabi-v7a/libSDL2.so
[armeabi-v7a] Install : libmain.so => libs/armeabi-v7a/libmain.so
This clearly shows modules SDL2 and main being built.. but why?
Perhaps I am supposed to use:
ndk-build -f Android.mk
..to build my one and only desired Android.mk instead of automatically having my jni folder searched/built?
When I do that I get the following error but at least it looks as though it is trying to run the one specific Android.mk:
MacbookPro:SQLite admin$ ndk-build -f Android.mk
Android NDK: Trying to define local module 'SQLite' in Android.mk.
Android NDK: But this module was already defined by /Work/TT/android-TT/app/src/main/jni/SQLite/Android.mk.
/Android/android-ndk-r9d/build/core/build-module.mk:34: *** Android NDK: Aborting. . Stop.
My Application.mk is as follows but this is not present in the SQLite folder where I run ndk-build, this is the jni/Application.mk:
# Uncomment this if you're using STL in your project
# See CPLUSPLUS-SUPPORT.html in the NDK documentation for more information
APP_STL := gnustl_static
APP_PLATFORM := android-14
APP_ABI := armeabi-v7a
UPDATE: I tried:
ndk-build -d -f Android.mk
..and the debug output shows that the SDL2/Android.mk and other .mk files are still being called. How do I stop this recursive behaviour so it only builds the Android.mk in the current folder?
This is a snippet of the debug output that I see:
Reading makefile `/Work/TT/android-TT/app/src/main/jni/SDL/Android.mk' (search path) (no ~ expansion)...
Reading makefile `/Work/TT/android-TT/app/src/main/obj/local/armeabi-v7a/objs/main/SQLite/sqlite3.o.d' (search path) (don't care) (no ~ expansion)...
The answer to my problem is to do this:
ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=./Android.mk
This will build the current folder Android.mk as required.

How to link prebuilt shared/static libraries in ndk build with avoiding the library to get copy in lib/{arch} folder?

I have two native modules to be build as shared library. Both the modules have dependecy on some other common modules.
I am giving the dependecy in Android.mk file using "PREBUILT_SHARED_LIBRARY", but the problem is the libraries specified with "PREBUILT_SHARED_LIBRARY" will get copied to libs/armeabi folder too.. :(
When I include their respective .jars in the main application it throws an eror saying duplicate copy of libraries..
I have to solve this problem by linking the common libraries in ndk build without using "PREBUILT_SHARED_LIBRARY" so that it will not copy the all dependent libraries to libs/areabi.
Can anybody please tell me how can I resolve this problem ? I have googled about this, but everywhere I see the answer use "PREBUILT_SHARED_LIBRARY" to link with already built shared libraries.
Your prebuilt's Android.mk is probably wrong, if you do something like this
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := mygreatlibrary-prebuilt
MY_LIBRARY_NAME := myGreatLibrary
### export include path
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
### path to library
LOCAL_SRC_FILES := libs/$(TARGET_ARCH_ABI)/lib$(MY_LIBRARY_NAME).so
### export dependency on the library
LOCAL_EXPORT_LDLIBS := -L$(LOCAL_PATH)/libs/$(TARGET_ARCH_ABI)/
LOCAL_EXPORT_LDLIBS += -l$(MY_LIBRARY_NAME)
include $(PREBUILT_SHARED_LIBRARY)
Then the correct library will be copied to the correct place

How to call JNIHelp.h via NDK?

I am trying to write an JNI file and it includes JNIHelp.h, however I met some error:
jni/JNIMPEG4Writer.cpp:4:21: fatal error: JNIHelp.h: No such file or directory
compilation terminated.
I guess I should add something to the Android.mk file, but I am not sure what should I add. This is my Android.mk file:
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := JNIMPEG4Writer
LOCAL_STATIC_LIBRARIES := MPEG4Writer
#LOCAL_LDLIBS := -llog
LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib –llog
LOCAL_SRC_FILES := JNIMPEG4Writer.cpp
include $(BUILD_SHARED_LIBRARY)
Does anyone have any ideas? Thanks!
JNIHelp.h is not part of NDK. You inherited some code from the Android platform. You will find other dependencies on non-public modules, most likely libcutils and libutils.
You have three options: build your code as a module of the platform, rewrite the code to only use public headers and libraries, or download parts of the platform, e.g. https://android.googlesource.com/platform/libnativehelper/, and arrange the include paths accordingly.
To satisfy the linker in the latter scenario, you can use adb pull /system/lib to acquire the versions of libnativehelper.so, libcutils.so, and other referenced non-public libraries. Note that ndk-build will complain about linking against these libraries.

Android NDK: how to build and then use a prebuilt library

I have read many posts on how to share prebuilt libraries using the Android.mk system. The solutions boil down to two steps:
A directory with the already-built library uses *include $(PREBUILT_SHARED_LIBRARY)*
The project consuming the library uses *LOCAL_SHARED_LIBRARIES*
(you can substitute "STATIC" for "SHARED" to build and use a .a instead of a .so)
What I am trying to do is add a step 0: build the library from sources. If I change a source file that contributes to the library I want the Android.mk system to execute steps 0, 1, and 2 in order.
I have two projects in Eclipse/ADT:
MyApp - uses MyLibrary
MyLibrary - contains the source files for the library
The question I'm asking here focuses on MyLibrary project. Here is the Android.mk for MyLibrary:
LOCAL_PATH := $(call my-dir)
# step 0: build my library
include $(CLEAR_VARS)
LOCAL_MODULE := mylibrary
LOCAL_LDLIBS := -llog
LOCAL_SRC_FILES := libsrc1.c libsrc2.c
include $(BUILD_SHARED_LIBRARY)
# step 1: export my library (PREBUILT_SHARED_LIBRARY):
include $(CLEAR_VARS)
LOCAL_MODULE := mylibrary-prebuilt
LOCAL_SRC_FILES := ../libs/$(TARGET_ARCH_ABI)/libmylibrary.so
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
include $(PREBUILT_SHARED_LIBRARY)
If I do a project->clean... and then a project->build project in this project I get the error:
ERROR:jni/Android.mk:mylibrary-prebuilt: LOCAL_SRC_FILES points to a missing file.
The clean removes the .so and I'm guessing PREBUILT_SHARED_LIBRARY detects the missing .so before BUILD_SHARED_LIBRARY rebuilds the library and copies it (even though the steps are in the correct order).
If I comment out step 1 and build, libmylibrary.so is correctly built and copied to libs/armeabi/libmylibrary.so. If I then uncomment step 2 and do a project->build project, that is without doing a clean first, I get these warnings and error:
warning: overriding commands for target `obj/local/armeabi/libmylibrary.so'
warning: ignoring old commands for target `obj/local/armeabi/libmylibrary.so'
warning: overriding commands for target `libs/armeabi/libmylibrary.so'
warning: ignoring old commands for target `libs/armeabi/libmylibrary.so'
make: *** No rule to make target `jni/../libs/armeabi/libmylibrary.so', needed by `obj/local/armeabi/libmylibrary.so'. Stop.
I think I understand these errors, but I do not see a way to accomplish what I want.
What am I missing?
You can simply avoid step 1 entirely and just rebuild the library from sources. I'm not sure I understand the benefits of having the three steps your describe. Feel free to clarify.
In all cases, your analysis of ndk-build's behaviour is spot on, and all of this is completely intentional.
The NDK itself provides several prebuilt libraries and allows you to rebuild them from sources if you want. For example, if you look at sources/cxx-stl/stlport/Android.mk, you will see that a variable (STLPORT_FORCE_REBUILD) is used to control whether to use the prebuilt binaries, or rebuild them from sources.
Maybe using a similar method for your project would work.

Resources