linking against .so files in a .aar file using android gradle experimental
Asked Answered
A

1

9

I am using the android gradle experimental plugin to build an app module with some native code. This native code uses a library with pre-built .so files, which I am bundling into a .aar file via an android library module. The .aar file builds fine, but how do I link the native code module in the app module to the pre-built .so files in the .aar module? The gradle experimental documentation doesn't mention this scenario.

Also, I'd like to package up include files in the .aar file if possible (although they shouldn't be packaged with the final application).

In /prebuiltlib:

build.gradle
-src/
--main/
---jniLibs/
----libfoo.so

Here are the gradle files:

/prebuiltlib/build.gradle

apply plugin: "com.android.model.library"
model {
    android {
        compileSdkVersion 25
        buildToolsVersion "25.0.3"

        defaultConfig {
            minSdkVersion.apiLevel = 21
            targetSdkVersion.apiLevel = 21
            versionCode 1
            versionName "1.0"

        }

        buildTypes {
            release {
                minifyEnabled false
                proguardFiles.add(file("proguard-rules.pro"))
            }
        }
    }
}

dependencies {
    compile fileTree(dir: "libs", include: ["*.jar"])
    compile "com.android.support:appcompat-v7:25.3.1"
}

Here is /app/build.gradle, note the ??? where I'm not sure what to put:

import org.apache.tools.ant.taskdefs.condition.Os
apply plugin: 'com.android.model.application'

model {
    repositories {
        libs(PrebuiltLibraries) {
            // ??? is this right, and does this go into app/build.gradle or mylib/build.gradle?
            foo {
                binaries.withType(SharedLibraryBinary) {
                    sharedLibraryFile = file('???/libfoo.so')
                }
            }
        }
    }
    android {
        compileSdkVersion = 25
        buildToolsVersion = '25.0.3'

        defaultConfig {
            applicationId = 'com.jeremy.stackoverflow.sample'
            minSdkVersion.apiLevel = 21
            targetSdkVersion.apiLevel = 21
            versionCode = 1
            versionName = '1.0'
        }

        ndk {
            platformVersion = 21
            moduleName = 'native-activity'
            toolchain = 'gcc'
            toolchainVersion = '4.9'
            stl = 'gnustl_shared'
            abiFilters.add('armeabi-v7a')
            ldLibs.addAll(['log',
                           'android',
                           'EGL',
                           'GLESv2'
            ])
            // ??? How do I add include paths from the .aar module?
            cppFlags.add('-I' + file('???/include'))
            cppFlags.addAll(['-std=c++11', '-fexceptions'])
        }

        sources {
            main {
                jni {
                    dependencies {
                        // ??? Is this right?
                        library 'foo' linkage 'shared'
                    }
                }
                jniLibs {
                    source {
                        // ??? Do I need one of these for the libs in the .aar?
                        srcDir file('???/jniLibs')
                    }
                    dependencies {
                        // ??? Is this right?
                        library 'foo'
                    }
                }
            }
        }
        buildTypes {
            release {
                minifyEnabled = false
                proguardFiles.add(file('proguard-rules.pro'))
            }
        }
    }
}

dependencies {
    println rootProject.getName()
    compile fileTree(include: ['*.jar'], dir: 'libs')
    compile 'com.android.support:appcompat-v7:25.3.1'
    compile project(':prebuiltlib')
}
Angel answered 15/5, 2017 at 21:49 Comment(1)
I know google did this in their "ArCore C Sdk", but I couldn't implement it in my project. Did you manage to solve this?Anciently
S
0

Starting with Android Gradle Plugin 4.0, C/C++ dependencies can be imported from AARs linked in your build.gradle file. Gradle will automatically make these available to the native build system, but your build system must be configured to make use of the imported libraries and headers.

On the build.gradle of the app that imports the module add:

In gradle 4.0: add the following to your project's gradle.properties file:

android.enablePrefab=true

In gradle 4.1: add the following to the android block of your module's build.gradle file:

buildFeatures {
  prefab true
}

Then add the dependency on the module that has to be imported with:

dependencies {
    implementation files('libs/nativelib-release.aar')
}

where in this case the lib aar in release version was placed on the app/libs directory.

On the build.gradle of the module that will be imported add:

buildFeatures {
    prefabPublishing = true
}

prefab {
    create("nativelib") {
      headers = "src/main/cpp/include"
    }

    // if you have other libs to expose...
    create("myotherlibrary") {
        headers = "src/main/cpp/myotherlibrary/include"
    } 
}

note that currently only one include path is allowed (where the .h that contain the public API must be placed) and a bug was raised here: https://issuetracker.google.com/issues/168775349

, if your application defines libapp.so and it uses cURL, your CMakeLists.txt should include the following:

add_library(app SHARED app.cpp)

# Add these two lines.
find_package(nativelib REQUIRED CONFIG)
target_link_libraries(app nativelib::nativelib)

app.cpp is now able to #include "curl/curl.h", libapp.so will be automatically linked against libcurl.so when building, and libcurl.so will be included in the APK.

Source: https://developer.android.com/studio/build/native-dependencies

Extra bits:

It may be needed to build the aar module with a shared stl, you can do that adding on the module gradle file the following on the android defaultConfig:

externalNativeBuild {
    cmake {
        cppFlags ""
        arguments "-DANDROID_STL=c++_shared"
    }
}

For Debugging:

the module library will be exposed to the cmake of the app through the variable CMAKE_FIND_ROOT_PATH. use message(${CMAKE_FIND_ROOT_PATH}) to show that path, and inside there will be a file called nativelibConfig.cmake (or another lib/module name). the file contains the following:

if(NOT TARGET nativelib::nativelib)
add_library(nativelib::nativelib SHARED IMPORTED)
set_target_properties(nativelib::nativelib PROPERTIES
    IMPORTED_LOCATION "/Users/bloom/.gradle/caches/transforms-3/44ca3477885179ff4cb5073527c9d262/transformed/nativelib-debug/prefab/modules/nativelib/libs/android.arm64-v8a/libnativelib.so"
    INTERFACE_INCLUDE_DIRECTORIES "/Users/bloom/.gradle/caches/transforms-3/44ca3477885179ff4cb5073527c9d262/transformed/nativelib-debug/prefab/modules/nativelib/include"
    INTERFACE_LINK_LIBRARIES ""
)
endif()

it's visible the library name with the :: format (pretty weird) in case it's difficult to guess the name to use on CMakelists.txt, and the paths exported.

To refresh the editor etc. try build->Refresh linked C++ projects.

Sue answered 3/11, 2020 at 15:45 Comment(2)
can you explain a bit more on this? It's not clear yet on which directory goes what.Odyl
this is to use .so libraries inside an aar file. The aar file can come from a repository or be local (usually then copied on a libs folder). To import the aar file check this docs developer.android.com/studio/build/dependenciesSue

© 2022 - 2024 — McMap. All rights reserved.