NDK: Android.mk / GNU Make - android

The Android.mk for my NDK project contains several hundred CPP files from which I build about a dozen static libs and finally a single shared lib. Most of my developing experience so far is on Windows, so I'm not very familiar with Linux, GNU Make, and the like.
When I add a new CPP file to Android.mk (i.e. to LOCAL_SRC_FILES), issuing ndk-build rebuilds all of my files, apparently because Android.mk changed, and it refers to all of the CPP files. Is there a way to avoid this? Let's assume I make a separate file for each static lib, and each such file would add that lib's CPP files to LOCAL_SRC_FILES and be included by Android.mk. Should that result in rebuilding only the lib whose included file is modified? This was actually my first attempt, but I went seriously wrong somewhere, and had to start over as the build would no longer succeed (which is why I'm asking before trying again).
Assuming the above approach is reasonable, how does including a file relate to the scope/lifetime of variables set in the including/included files?
Still hoping someone can answer this...

Firstly try to split your project into multiple android.mk files, instead of just one. It would limit the recompilation step each time you change one of them.
Then try this ndk-build -o <your_android.mk>. For instance:
ndk-build -o jni/Android.mk
Ref: How to prevent Android native project full rebuild after changing Android.mk?

Related

Libogg compiled with standalone toolchain (generated by NDK script) tries to load itself from incorrect file - libogg.so.0 instead of libogg.so

I'm trying to add libogg to my android NDK project.
I'm using project template that comes with SDL library.
I'm on Windows, so all following scripts were executed under MSYS, a linux terminal emulator.
First, I generated a standalone toolchain.
android-ndk/build/tools/make-standalone-toolchain.sh --platform=android-12 --system=windows-x86_64 --toolchain=arm-linux-androideabi-4.9 --install-dir=Y:/gcc_android_androideabi-4.9_api12/
Then I built the library.
make clean
configure CC="/y/gcc_android_androideabi-4.9_api12/bin/arm-linux-androideabi-gcc" LIBS="-lgnustl_shared" CFLAGS="-mthumb" --host=arm-linux-androideabi
make
Then I copied resulting libogg.so to my project folder and added following to my jni/src/Android.mk:
...
include $(CLEAR_VARS)
LOCAL_MODULE := ogg-prebuilt
LOCAL_SRC_FILES := ../../prebuilt-libs/$(TARGET_ARCH_ABI)/libogg.so
include $(PREBUILT_SHARED_LIBRARY)
...
LOCAL_SHARED_LIBRARIES := ... ogg-prebuilt ...
...
Similar code works fine for another libraries that I use.
ndk-build successfully finds my libraries and adds them to the .apk.
The problem arises when I call System.loadLibrary("ogg"); at runtime. Dynamic linker gives me error message saying something like
Can't find library "libogg.so.0".
I'm sure that my library loading code is ok because it works for other libraries that I have.
Error from the dynamic linker is interesting because make not just generated single libogg.so. It generated 3 completely same files with different names: libogg.so, libogg.so.0 and libogg.so.0.8.2.
I tried to replace libogg.so with libogg.so.0 and adjusted Android.mk properly, but NDK build script yelled at me, saying that prebuilt shared libraries must have .so extension. I tried to just copy libogg.so.0 to libs/ folder, but NDK build script ignored it when building .apk.
Then I opened libogg.so in a hex editor and searched for libogg. I found only one occurence: libogg.so.0[NUL]. I replaced .0 with 2 [NUL]s, so it became libogg.so[NUL][NUL][NUL] and now library loads perfectly.
I can reproduce this error with all toolchains I use: arm-linux-androideabi-gcc-4.9, i686-linux-android-gcc-4.9 and mipsel-linux-android-gcc-4.9. They all are generated using similar scripts.
The error persists if I rename libogg.so.0 or libogg.so.0.8.2 to libogg.so and use it instead of original one. As I said, all three files have same content.
While I can make a sed script to automatically fix library name in .so files when necessary, I wonder if there is a better solution to this problem.
Maybe I have to add some flags to configure?
My slightly less hacky fix for this is to remove the version information from the build system of libogg.
In the directory where you have the libogg source code, open the file src/Makefile.am and change this line:
libogg_la_LDFLAGS = -no-undefined -version-info #LIB_CURRENT#:#LIB_REVISION#:#LIB_AGE#
to
libogg_la_LDFLAGS = -avoid-version
If you don't have automake installed then you can change the generated Makefile after running ./configure. Find the libogg_la_LDFLAGS and make a similar change to the one detailed above. On Linux a sed one-liner like this does the job:
sed -i 's/^libogg_la_LDFLAGS.*/libogg_la_LDFLAGS = -avoid-version/g' src/Makefile

Android NDK mangles relative path on windows

I am trying to create an Android Studio wrapper around C code and am running into a problem with the NDK. Because the C code is from a 3rd party project, I am trying to not move the code location and have the project in a subdirectory of the repository and as such have to not use the build in call to the NDK and its autogenerated make file. The NDK call works correctly, but I get the following error:
make.exe: *** No rule to make target `C:/some_relative_path/jni/../../../../core.c', needed by `C:/some_relative_path/obj/local/armeabi/objs/my_module/C_/some_relative_path/jni/__/__/__/__/core.o'. Stop.
As you can see, the object path has been mangled such that : and .. have been turned into underscores.
I had to add a jni folder to my project and place the Android.mk and Application.mk files in it to satisfy the path appending of the NDK Gradle plugin. As a result the jni folder had no source files in it. Since I found several links on google talking about needing more than one source file, I added two dummy source files to the jni directory.
Among other things, my Android.mk file contains the following:
LOCAL_SRC_FILES := \
$(LOCAL_PATH)/NDKBug1.c \
$(LOCAL_PATH)/NDKBug2.c \
$(LOCAL_PATH)/../../../../core.c \
I'm looking to see if anyone can help me with either this path issue directly, or perhaps suggest an alternative way of setting things up.
The file names in LOCAL_SRC_FILES are supposed to be relative to the local directory, so you need to remove the $(LOCAL_PATH)/ prefix - then it should work.
I haven't heard about a bug requiring to have source files in the local dir though, so I think you can get rid of them.

Android Eclipse: How to reference library project's NDK library from another project?

I have an Android Eclipse library project "Lib" that contains both Java and C code built with the NDK. I have another project "App", which is dependant on Lib. App also contains both Java and C. My goal is to have multiple Eclipse App-like projects which each use the Lib project.
A Java routine in App calls a Java routine in Lib which calls C code in Lib. This compiles, links, and runs perfectly on the device.
Now I want to add a call from the C code in App to the C code in Lib. My First problem is header files. I need to include a header file, jni/lib-jni,h, from the Lib project:
#include "lib-jni.h"
in a .c file in App. For this #include to work I believe I need to add Lib's jni/ directory to LOCAL_C_INCLUDES in App's jni/Android.mk. I do not want to hard-code this path, I would like to get it from Eclipse if possible. After all, I've told Eclipse that the App project depends on the Lib project and Eclipse knows how to connect Java calls between the two.
I temporarily kludged around the first problem by copying lib-jni.h from Lib's jni/ directory to App's jni/ directory. Now App's C code compiles, but it won't link; it gets an undefined. In App's Android.mk I need to tell the C linker to link against libLib.so. I tried putting -lLib on LOCAL_LDLIBS, but that didn't work. libLib.so is in the Lib project in Lib/libs/armeabi/libLib.so. Eclipse knows to incorporate this .so into the .apk file it builds for App.
Is there a way to cleanly solve these two problems?
Add Lib's jni/ directory to App's LOCAL_C_INCLUDES
Add a reference to libLIb.so to App's LOCAL_LDLIBS
I phrased these problems in terms of possible solutions. I'm open to any solution. For example, if using LOCAL_LDLIBS the wrong way to go, what is the right way?
Note: Because App and Lib are two separate Eclipse projects built at separate times I am pretty sure I can not use LOCAL_EXPORT_C_INCLUDES in Lib's Android.mk to make Lib's jni/ directory available to App's Anroid.mk - I tried it didn't work. But I'm prepared to be educated.
I always set the LOCAL_C_INCLUDES to the original lib, so I think that is not that bad idea. An other solution is to include the external library as a static library in your libs folder, but I'm not sure if it will work.

How to add a new .cpp file to, and then build, a c++ library for Android?

I'm making a Unity3d plugin for this c++ library for Android. Ive gotten it to work in windows by opening the visual studio project the library makers provided, adding a new .cpp file to it with my API code and building to a dll.
I'm wondering now how I can add this new .cpp file to the library and build it for Android.
So far I have installed the Android sdk and ndk on a machine with ubuntu and successfully built the original library using ndk-build (as per these instructions)
I'm guessing it is not as simple as copying my .cpp file into the folder and building as there are AndroidManifest.xml files and so on.
The AndroidManifest.xml file is not pertinent for an NDK build.
You will need to find the appropriate Android.mk file. You may need to add the file name into the LOCAL_SRC_FILES in that make file, although they may have it set up to compile all the .cpp files in the directory or something more intelligent along those lines. It is, after all, a regular make file.
See here for info on the Android.mk file specifics.
Edit:
I suppose I should add that it would be the LOCAL_SRC_FILES preceding the relevant include $(BUILD_SHARED_LIBRARY) for the library you intend to build. Each make file may contain an arbitrary number of targets, although in the simplest case it's usually one make file to build a library.

Compiling a C/C++ project that uses external libraries using the Android NDK

I'm completely new to Java and Android dev but I'm trying to port a C/C++ project to an android phone just to do some testing. Here are some details. The project depends on the Openssl, GMP, and NTL libraries. I downloaded copies and put everything inside a the jni/ folder. So now the folder structure looks like this:
jni/
Android.mk
gmp/
Android.mk
...
ntl/
Android.mk
...
openssl/
Android.mk
...
myproject/
Android.mk
...
Every one of the subfolders that has .cpp, c. files has an Android.mk file. Now, my question is: very broadly, how should I go about structuring the contents of the Android.mk files? I was able to follow instructions online and compile Openssl (I produced all the .so files inside of it). How will the top-level Android.mk use these? Or does it now?
When we run ndk-build in the top directory of your project (the parent of the jni directory in your picture), we essentially run gnu make and include jni/Android.mk.
In your case, this top jni/Android.mk should only include all child .mk files:
include $(call all-subdir-makefiles)
The main file is jni/myproject/Android.mk, which refers to the binaries produced by others, like
LOCAL_SHARED_LIBRARIES += openssl
and so on. Note that the names are not the names of library files, but of the LOCAL_MODULE's that build them.
Often, it is easier to use static libraries. For example, you need to load all shared libraries from Java yourself, in the correct dependencies order.
main is an informal word here. I just want to say that this is the make file that causes its siblings to do their work, and do it in expected order. It does that because it refers to the binaries produced by the other make files, by specifying LOCAL_SHARD_LIBRARIES and LOCAL_STATIC_LIBRARIES.
It is important to understand that it is not enough to include all the make files in jni/Android.mk to cause them all do their work, and do it in expected order. I want to emphasize that the jni/Android.mk file is a service file, not the main, even though it sits on top of the directory hierarchy.

Categories

Resources