2

I'm trying to generate Android.mk files automatically for an existing build environment. This environment has many libraries linking to each other and also possibly linking to 3rd party libs (like boost).

Let's say I have libA using boost. My goal being to build both libA and libB by running ndk-build from libB folder.

So I generate this Android.mk for libA:

LOCAL_PATH := $(call my-dir)

# Import boost
include $(CLEAR_VARS)
LOCAL_MODULE    := boost_atomic
LOCAL_SRC_FILES := ../dev/libcpp/boost/1.60.1/lib/libboost_atomic.a
include $(PREBUILT_STATIC_LIBRARY)

# libA:
include $(CLEAR_VARS)
LOCAL_MODULE    := libA
LOCAL_SRC_FILES := libA.cpp
LOCAL_C_INCLUDES := ../dev/libcpp/boost/1.60.1

# boost import:
LOCAL_CPPFLAGS += -DHAVE_CONFIG_H -fexceptions -frtti
LOCAL_STATIC_LIBRARIES += boost_atomic

# build libA:
include $(BUILD_SHARED_LIBRARY)

Now I have libB using both boost and libA. libB's Android.mk is very similar to libA's except I added the import of libA file as below:

# Import libA
include $(CLEAR_VARS)
LOCAL_MODULE := libA
include ../../libA/jni/Android.mk

When I try to make libB, I'm being reported:

Android NDK: Trying to define local module 'boost_atomic' in ../../libA/jni/Android.mk.
Android NDK: But this module was already defined by ../../libA/jni/Android.mk.
B:/Android/android-ndk-r11b/build//../build/core/build-module.mk:34: *** Android
 NDK: Aborting.    .  Stop.

Is there a way for me to check if boost_atomic is already defined (like if (exists boost_atomic)) to make sure it gets defined once only? Or should I suffix all the names (ending up with boost_system_for_libA and boost_system_for_libB) to prevent the conflict? Or any other alternative?

jpo38
  • 20,821
  • 10
  • 70
  • 151
  • It *is* possible to avoid duplicate definitions of modules, but in your case, for boost it would be easier to simply add `LOCAL_LDLIBS += ../dev/libcpp/boost/1.60.1/lib/libboost_atomic.a` instead of adding it as LOCAL_STATIC_LIBRARIES – Alex Cohn Oct 20 '16 at 16:56
  • @AlexCohn: Would be a nice fix. Will try it tomorrow. Can I do the same for shared libraries? Because I guess I'll have the same problem if having `libC` using `libB` and `libA`, `libA` will end up being defined twice (imported both by `libB` and `libC`) when I'll compile `libC`. – jpo38 Oct 20 '16 at 18:59
  • No, this won't work for non-prebuilt libs, and shared libs should not be specified in LDLIBS anyways. But usually it's easy to include your Android.mk files according to hierarchy of subdirectories. This avoids duplication. – Alex Cohn Oct 21 '16 at 04:46
  • @AlexCohn: Thanks, `LOCAL_LDLIBS += ../dev/libcpp/boost/1.60.1/lib/libboost_atomic.a` will give `non-system libraries in linker`warning. How to get rid of this warning? (Tried simply `LOCAL_STATIC_LIBRARIES += ../dev/libcpp/boost/1.60.1/lib/libboost_atomic.a` but it won't work). – jpo38 Oct 21 '16 at 07:35
  • You can ignore this warning. It is only relevant for shared libs. – Alex Cohn Oct 21 '16 at 07:53

3 Answers3

3

You have an NDK function $(modules_get_list). Relying on it, your libA/jni/Android.mk file may look as follows:

LOCAL_PATH := $(call my-dir)

include ../../boost/Android.mk

# libA:
include $(CLEAR_VARS)
LOCAL_MODULE    := libA
LOCAL_SRC_FILES := libA.cpp

# boost import:
LOCAL_STATIC_LIBRARIES += boost_atomic

# build libA:
$(if $(call set_is_member,$(modules-get-list),$(LOCAL_MODULE)),\
    ,$(eval include $(BUILD_SHARED_LIBRARY)))

and boost/Android.mk file:

LOCAL_PATH := $(call my-dir)

# Import boost
include $(CLEAR_VARS)
LOCAL_MODULE    := boost_atomic
LOCAL_SRC_FILES := ../dev/libcpp/boost/1.60.1/lib/libboost_atomic.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../dev/libcpp/boost/1.60.1
LOCAL_EXPORT_CPPFLAGS += -DHAVE_CONFIG_H -fexceptions -frtti
$(if $(call set_is_member,$(modules-get-list),$(LOCAL_MODULE)),\
    ,$(eval include $(PREBUILT_STATIC_LIBRARY))

Finally, libB/jni/Android.mk:

LOCAL_PATH := $(call my-dir)

include ../../boost/Android.mk
include ../../libA/jni/Android.mk

# libB:
include $(CLEAR_VARS)
LOCAL_MODULE    := libB
LOCAL_SRC_FILES := libB.cpp

# boost import:
LOCAL_STATIC_LIBRARIES += boost_atomic

# build libA:
$(if $(call set_is_member,$(modules-get-list),$(LOCAL_MODULE)),\
    ,$(eval include $(BUILD_SHARED_LIBRARY)))

I prefer not to have duplicate definitions for 3rd party prebuilt libs, like boost, but rather to use a separate Android.mk definition for each, and include it whenever necessary. This way, I have one place to change if the external library is updated.

Update: if you don't like $(if …) syntax, you may use

ifeq ($(filter $(modules-get-list),$(LOCAL_MODULE)),)
    include $(BUILD_…_LIBRARY)
endif

instead.

Alex Cohn
  • 56,089
  • 9
  • 113
  • 307
  • 1
    Nice, I did set my own variable to track duplicate definition, but it makes the code complex (see http://stackoverflow.com/questions/40173692/android-makefiles-how-to-early-return). I'll try your solution, but only in a few weeks (when coming back from vacation). – jpo38 Oct 22 '16 at 18:58
  • Did not work. I tried to call `ifndef $(modules_get_list).$(LOCAL_MODULE)` just after I do `include $(PREBUILT_STATIC_LIBRARY)` and this is evaluated to `true`. Did I miss something? – jpo38 Nov 04 '16 at 15:49
  • My apologies, this is `$(modules-get-list)`. Please see the updated answer. – Alex Cohn Nov 06 '16 at 10:38
  • Thanks Alex. How can I put this in a regular if statement? `ifeq ($(call set_is_member,$(modules-get-list),$(LOCAL_MODULE)),true)` fails, as `$(call set_is_member,$(modules-get-list),$(LOCAL_MODULE))` is evaluated to `T` not `true` (sorry, I'm not very familiar with those makefile syntax. – jpo38 Nov 07 '16 at 08:41
1

As Alex Cohn mentioned, one may fix that by replacing:

include $(CLEAR_VARS)
LOCAL_MODULE    := boost_atomic
LOCAL_SRC_FILES := ../dev/libcpp/boost/1.60.1/lib/libboost_atomic.a
include $(PREBUILT_STATIC_LIBRARY)

...

# boost import:
LOCAL_CPPFLAGS += -DHAVE_CONFIG_H -fexceptions -frtti
LOCAL_STATIC_LIBRARIES += boost_atomic

By simply:

LOCAL_LDLIBS += ../dev/libcpp/boost/1.60.1/lib/libboost_atomic.a

This works but:

  • Introduces a compilation warning:

    WARNING:/Users/jwalton/Android-CryptoPP/jni/Android.mk:prng: non-system libraries in linker flags: -lcryptopp -lstlport_shared
    This is likely to result in incorrect builds. Try using LOCAL_STATIC_LIBRARIES
    or LOCAL_SHARED_LIBRARIES instead to list the library dependencies of the current module

  • Won't work for shared 3rd party libraries

So I finally fixed this my adding a new mk file specific to libA, libB:

libA's Android.mk is now:

LOCAL_PATH := $(call my-dir)

# Import boost
include $(CLEAR_VARS)
LOCAL_MODULE    := boost_atomic
LOCAL_SRC_FILES := ../dev/libcpp/boost/1.60.1/lib/libboost_atomic.a
include $(PREBUILT_STATIC_LIBRARY)

include libA.mk

libA's libA.mk is now:

LOCAL_PATH := $(call my-dir)

# libA:
include $(CLEAR_VARS)
LOCAL_MODULE    := libA
LOCAL_SRC_FILES := libA.cpp
LOCAL_C_INCLUDES := ../dev/libcpp/boost/1.60.1

# boost link:
LOCAL_CPPFLAGS += -DHAVE_CONFIG_H -fexceptions -frtti
LOCAL_STATIC_LIBRARIES += boost_atomic

# build libA:
include $(BUILD_SHARED_LIBRARY)

libB's libB.mk now has:

# Import libA
include $(CLEAR_VARS)
LOCAL_MODULE := libA
include ../../libA/jni/libA.mk

So that it imports libA without importing boost (also defined in libB's Android.mk).

Then, no warning and it compiles smoothly.

jpo38
  • 20,821
  • 10
  • 70
  • 151
1

I'm trying to generate Android.mk files automatically for an existing build environment. This environment has many libraries linking to each other and also possibly linking to 3rd party libs (like boost).

Why not to use standalone NDK toolchains instead of painful coping with Android.mk files? Sounds like standalone toolchains are ideally suitable in your situation.

Sergio
  • 8,099
  • 2
  • 26
  • 52
  • @jpo38 Yes, they do. _... for an existing build environment ..._ - seems like you have some makefiles / build scripts already, don't you? – Sergio Oct 21 '16 at 13:18
  • Yes, am using Visual Studio. It also natively supports Android target but only for Professional edition, I'm using Express edition... – jpo38 Oct 21 '16 at 13:42
  • @jpo38 Visual Studio is not my cup of tea, but anyway I believe that even Express edition lets you to use custom toolchain somehow. Maybe [this answer](http://stackoverflow.com/a/14819449/2878070) would give you an idea. – Sergio Oct 22 '16 at 06:52
  • I'll have a look. But, honestly, generating the makefiles manually from CMake is not such a big deal. I got it working with only 100 lines of CMake code being written (already did that some time ago to generate QtCreator project files from CMake, as importing CMake in QtCreator was not working properly for me). And I fully control what's happening... – jpo38 Oct 22 '16 at 06:59