Rich Dove
Rich Dove

Reputation: 41

aar support in Android.mk. Dependencies are not picked up by AOSP build

Application (developed in Android Studio) uses AAR as a library. I need to build this app in AOSP tree. So I created Android.mk:

LOCAL_STATIC_JAVA_AAR_LIBRARIES:= <aar alias>

include $(BUILD_PACKAGE)

include $(CLEAR_VARS)

LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := <aar alias>:libs/<lib file>.aar

include $(BUILD_MULTI_PREBUILT)

Build completes successfully but the issue is that AAR contains number of JAR files in its 'libs' directory. And it seems that these JARs are not included into build so application crashes with NoClassDefFoundError.

Is AOSP missing full support of the AAR? Or do I miss something? Thanks. I use Android 6.0.1.

Upvotes: 3

Views: 4237

Answers (4)

Brent K.
Brent K.

Reputation: 1076

I found a way to do this fully automatically. I'll walk through it, then the completed Android.mk is at the bottom of this post.

First, let's create some variables with the parts that will change in your instance so you can copy/paste the remainder. You can manually unzip the .aar file once to check the file structure and contents. The relative path is from my current target directory back to the root of the Android build tree (where 'out' lives).

LOCAL_PATH := $(call my-dir)
#####################################
# Definitions for extracting .so files from .aar
AAR_WITH_JNI=lib/whatever-your.aar
AAR_ALIAS=MakeThisMeaningfullToYou
AAR_JNI_FLAVOR=arm64-v8a
AAR_ROOT_EXTRACT_PATH=out/target/common/obj/JAVA_LIBRARIES/$(AAR_ALIAS)_intermediates/aar/jni/$(AAR_JNI_FLAVOR)
AAR_RELATIVE_EXTRACT_PATH=../../../../$(AAR_ROOT_EXTRACT_PATH)

Next we want to use 'unzip' to query the .aar file for the jni contents using a shell command with the stdout piped through grep and sed to clean it up. This works since the .aar file is checked-in and present at the start of a clean 'make'. So add this to the top of your Android.mk after your variable definitions.

AAR_JNI_SO_FILE_LIST := $(shell \
      unzip -l $(LOCAL_PATH)/$(AAR_WITH_JNI) | \
      grep -e jni/$(AAR_JNI_FLAVOR)/lib\.*.so | \
      sed 's?^.*jni/$(AAR_JNI_FLAVOR)/??g')

Now we follow the pattern in aar support in Android.mk and add the AAR library to your package by adding LOCAL_STATIC_JAVA_AAR_LIBRARIES to your BUILD_PACKAGE and defining an alias to the extracted AAR with a 3 line BUILD_MULTI_PREBUILT after.

include $(CLEAR_VARS)
# your LOCAL_SRC_FILES and whatever else you need here
LOCAL_STATIC_JAVA_AAR_LIBRARIES:= $(AAR_ALIAS)
LOCAL_AAPT_FLAGS := --auto-add-overlay
include $(BUILD_PACKAGE)

include $(CLEAR_VARS)
LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := $(AAR_ALIAS):$(AAR_WITH_JNI)
include $(BUILD_MULTI_PREBUILT)

Now we augment that solution by adding the references to the .so files to the BUILD_PACKAGE. I did this using the relative paths using the addprefix.

LOCAL_PREBUILT_JNI_LIBS := \
 $(addprefix $(AAR_RELATIVE_EXTRACT_PATH)/,$(AAR_JNI_SO_FILE_LIST))

At this point, if you do this on a clean workspace there is an error for each the .so files since they are not present yet. The solution to that is to create an Empty Recipe for each of the .so files with a prerequisite of our alias. We can do that using a foreach loop of an eval and call function combined as described in Generate dynamically Makefile rules Because of these rules, 'make' will know that the .so files aren't expected to be on the filesystem until the conclusion of the BUILD_MULTIPLE_PREBUILT step.

define fake-so-rule-from-aar
$(1): $(AAR_ALIAS) ;
endef
$(foreach soFile,$(addprefix $(AAR_ROOT_EXTRACT_PATH)/,$(AAR_JNI_SO_FILE_LIST)),$(eval $(call fake-so-rule-from-aar,$(soFile))))

So when you put this all together, you don't need to pre-extract the .so files from the .aar file. You can simply 'rm -rf out' and the build system will extract everything it needs and properly copy the .so files to your library.

Finished Android.mk file:

LOCAL_PATH := $(call my-dir)

#####################################
# Definitions for extracting .so files from .aar
AAR_WITH_JNI=lib/my.aar
AAR_ALIAS=MyAarFile
AAR_JNI_FLAVOR=arm64-v8a
AAR_ROOT_EXTRACT_PATH=out/target/common/obj/JAVA_LIBRARIES/$(AAR_ALIAS)_intermediates/aar/jni/$(AAR_JNI_FLAVOR)
AAR_RELATIVE_EXTRACT_PATH=../../../../$(AAR_ROOT_EXTRACT_PATH)
# We cannot look in the driectory at the extracted files on a clean build, so we peek at the .aar contents to know what filenames we'll end up extracting
AAR_JNI_SO_FILE_LIST := $(shell unzip -l $(LOCAL_PATH)/$(AAR_WITH_JNI) | grep -e jni/$(AAR_JNI_FLAVOR)/lib\.*.so | sed 's?^.*jni/$(AAR_JNI_FLAVOR)/??g')
#$(info AAR_JNI_SO_FILE_LIST = $(AAR_JNI_SO_FILE_LIST))

#####################################
# java package
include $(CLEAR_VARS)
LOCAL_MULTILIB := 64
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := AlsoMine    
LOCAL_STATIC_JAVA_AAR_LIBRARIES := $(AAR_ALIAS)
LOCAL_PREBUILT_JNI_LIBS := $(addprefix $(AAR_RELATIVE_EXTRACT_PATH)/,$(AAR_JNI_SO_FILE_LIST))
#$(info LOCAL_PREBUILT_JNI_LIBS = $(LOCAL_PREBUILT_JNI_LIBS))
LOCAL_AAPT_FLAGS := --auto-add-overlay
# Build an APK
include $(BUILD_PACKAGE)

#####################################
# Create an alias to the extracted library from the .aar
include $(CLEAR_VARS)
LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := $(AAR_ALIAS):$(AAR_WITH_JNI)
include $(BUILD_MULTI_PREBUILT)

#####################################
# Create empty recipes so Make thinks it knows the so files are created
# by the BUILD_MULTI_PREBUILT above
define fake-so-rule-from-aar
$(1): $(AAR_ALIAS) ;
#$(info Created empty recipe for $(1): $(AAR_ALIAS))
endef
#$(foreach soFile,$(addprefix $(AAR_ROOT_EXTRACT_PATH)/,$(AAR_JNI_SO_FILE_LIST)), $(info $(soFile)))
$(foreach soFile,$(addprefix $(AAR_ROOT_EXTRACT_PATH)/,$(AAR_JNI_SO_FILE_LIST)),$(eval $(call fake-so-rule-from-aar,$(soFile))))

I left lots of comments in my file with $info calls to print various states for the next person who needs to debug it. Enjoy!

Upvotes: 0

Rich Dove
Rich Dove

Reputation: 41

I ended up with the following:

-in application's Android.mk:

LOCAL_STATIC_JAVA_AAR_LIBRARIES += coollibrary

include $(LOCAL_PATH)/java-static-lib-from-aar.mk

-java-static-lib-from-aar.mk:

define list-jar-libs-from-aar
  $(foreach f, $(2), $(shell if [ -f $(LOCAL_PATH)/$(f)/$(1).aar ]; then unzip -Z1 $(LOCAL_PATH)/$(f)/$(1).aar libs/*.jar | sed 's/libs\///g; s/\.jar//g'; fi))
endef

define build-jar-lib-from-aar
$(2): $(1)
    -mkdir -p $(dir $2);\
    cp -fp $1 $2
endef

MY_STATIC_JAVA_LIBS := $(foreach aar,$(LOCAL_STATIC_JAVA_AAR_LIBRARIES),\
    $(foreach jar, $(call list-jar-libs-from-aar,$(aar), libs aosplibs),\
        $(aar)_$(jar)))

    $(info Adding following java libraries from AAR(s) into LOCAL_STATIC_JAVA_LIBRARIES: $(MY_STATIC_JAVA_LIBS))
LOCAL_STATIC_JAVA_LIBRARIES += $(MY_STATIC_JAVA_LIBS)

$(foreach aar,$(LOCAL_STATIC_JAVA_AAR_LIBRARIES),\
    $(foreach jar, $(call list-jar-libs-from-aar,$(aar), libs aosplibs),\
        $(eval $(call build-jar-lib-from-aar,$(call intermediates-dir-for,JAVA_LIBRARIES,$(aar),,COMMON)/aar/libs/$(jar).jar,$(call intermediates-dir-for,JAVA_LIBRARIES,$(aar)_$(jar),,COMMON)/javalib.jar))))   

java-static-lib-from-aar.mk assumes that AARs reside in ./libs and aosplibs directories.

Upvotes: 1

Ysch
Ysch

Reputation: 772

Raising the dead here, I've experienced the same beahviour. Same goes to *.so files under ./jni/. and aar assets. Looking into $(BUILD_MULTI_PREBUILT), I see it looks only for classes jar and res files.

I ended up extracting al those components (*.so, *.jar, assets), and added them explicitly in my app's Android.mk.

Upvotes: 1

prateekj
prateekj

Reputation: 11

The way you are doing is correct. And jar files included in aar will also be included by this. I think you have missed to include aapt flags in that.

LOCAL_AAPT_FLAGS += --extra-packages your.package.name 

Upvotes: 1

Related Questions