NDK Debugging with gradle-experimental plugin - android-ndk

I'm trying to add native debugging to a project that is an Android Studio NDK project. In the past I just used gradle to kick off a shell script, which built the NDK lib. Now I'm trying to move to use the gradle-experimental plugin.
I've scoured the net for what little info there is, (mostly here, Android Tools Site - Gradle Experimental), about using gradle-experimental with the NDK and I've put together this build.gradle file which is using the preview NDK support for doing the NDK build inline with the Java build.
After finally getting this together from bits-and-pieces of info, I managed to get the NDK portion building, but now it fails to include the httpmime-4.4-beta1.jar file that is clearly included in the dependencies, and I've tried many different permutations of it such as in:
compile files("libs/httpmime-4.4.jar")
But regardless, the errors for the missing symbols from the Jar file still appear.
build.gradle source
apply plugin: 'com.android.model.application'
String APP_PACKAGE_NAME = 'com.obfuscated.app',
VERSION_NAME = '3.0',
TOOLS_VERSION = '23.0.2'
int VERSION_CODE = 15,
MIN_SDK_VERSION = 13,
TARGET_SDK_VERSION = 19,
COMPILE_SDK_VERSION = 23
model {
repositories {
libs(PrebuiltLibraries) {
// prebuilt binaries mirroring Android.mk
libstuff {
headers.srcDirs.add(file("jni/stuff/include/stuff"))
binaries.withType(SharedLibraryBinary) {
sharedLibraryFile = file("jni/stuff/lib/libstuff.so")
}
}
// ...several more of these actually exist in build.gradle and are working
cares {
headers.srcDirs.add(file("jni/c-ares/include"))
binaries.withType(SharedLibraryBinary) {
// StaticLibraryBinary and staticLibraryFile doesnt work despite sample code, at least not for com.android.tools.build:gradle-experimental:0.6.0-alpha5, this builds even though its a static-lib
sharedLibraryFile = file("jni/c-ares/lib/libcaresARM.a")
}
}
}
}
android {
compileSdkVersion = COMPILE_SDK_VERSION
buildToolsVersion = TOOLS_VERSION
defaultConfig.with {
applicationId = APP_PACKAGE_NAME
minSdkVersion.apiLevel = MIN_SDK_VERSION
targetSdkVersion.apiLevel = TARGET_SDK_VERSION
versionCode = VERSION_CODE
versionName = VERSION_NAME
buildConfigFields {
create() {
type "int"
name "VALUE"
value "1"
}
}
compileOptions.with {
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
}
}
signingConfigs {
create("appRelease") {
storeFile file('sign.jks')
storePassword '...'
keyAlias '...'
keyPassword '...'
storeType "jks"
}
}
} // end android
android.lintOptions {
abortOnError = false
}
android.packagingOptions {
exclude 'META-INF/NOTICE'
exclude 'META-INF/LICENSE'
exclude 'META-INF/services/javax.annotation.processing.Processor'
}
android.ndk {
moduleName = "native"
toolchain "clang"
toolchainVersion "3.5"
platformVersion = MIN_SDK_VERSION
ldLibs.addAll('atomic', 'android', 'log', 'OpenSLES')
abiFilters.addAll(["armeabi", "armeabi-v7a"])
CFlags.addAll(["-mfloat-abi=softfp", "-mfpu=neon", "-O3", "-DCARES_STATICLIB", "-Wno-c++11-long-long"])
cppFlags.addAll(["-I${file("jni")}".toString(),
"-I${file("jni/c-ares/include")}".toString(),
"-I${file("jni/coffeecatch")}".toString()])
stl = "stlport_shared"
}
android.sources {
main {
jniLibs {
dependencies {
}
}
jni {
dependencies {
library "libstuff"
library "cares"
// ...
}
source {
srcDir "jni"
}
}
// java {
// dependencies {
// compile files("libs/httpmime-4.4-beta1.jar")
// compile files("libs/FlurryAnalytics-5.1.0.jar")
// }
// }
}
}
android.buildTypes {
debug {
ndk.with {
debuggable = true
}
}
release {
minifyEnabled = false
ndk.with {
debuggable = true
}
}
}
android.productFlavors {
create("arm") {
ndk.with {
abiFilters.add("armeabi-v7a")
ldLibs.addAll([file("jni/stuff/lib/libstuff.so").toString(),
file("jni/c-ares/lib/libcaresARM.a").toString()])
}
}
create("fat") {
// compile and package all supported ABI
}
}
} // end model
repositories {
mavenCentral()
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:support-v4:23.+'
compile 'com.android.support:appcompat-v7:23.+'
compile 'com.android.support:support-v13:23.+'
compile 'com.android.support:support-annotations:23.+'
compile 'com.squareup:otto:1.3.8'
compile 'com.github.machinarius:preferencefragment:0.1.1'
compile 'org.apache.httpcomponents:httpclient-android:4.3.5.1'
compile 'com.fasterxml.jackson.core:jackson-core:2.5.+'
compile 'com.fasterxml.jackson.core:jackson-annotations:2.5.+'
compile 'com.fasterxml.jackson.core:jackson-databind:2.5.+'
compile 'com.jakewharton:butterknife:7.0.1'
compile 'com.google.guava:guava:19.0'
}
allprojects {
tasks.withType(JavaCompile) {
options.compilerArgs << "-Xlint:deprecation" << "-Xlint:unchecked"
}
}
Out of frustration, I switched back to the non-experimental branch, and even with the old build.gradle file it is now failing to find that same jar file. So is it a problem with Android Studio 2.0 Preview 6?
Has anyone else experienced this, or have a solution for it? It would be so convenient to finally have NDK debugging work right in Android Studio, and if it weren't for this last hurdle, I think I would be there.
Short of re-writing the code that depends on that jar file, I am at a loss for what else to try. I am also open to suggestions for the format of my build.gradle file above, since the documentation for these new features is very sparse still, and some of the samples seem to be already out-of-date with regard to the proper syntax.
WHAT AM I MISSING?
You can see that the C and Cpp (mobile:compileNativeArmeabiDebugArmSharedLibraryNativeMainCpp), steps are happening just fine, but then the Javac fails. This jar file approach has worked fine for the past 2 years or so for the http-mime lib from apache, so I don't understand why suddenly this is a problem.
:mobile:mergeArmDebugAndroidTestAssets
:mobile:generateArmDebugAndroidTestResValues UP-TO-DATE
:mobile:generateArmDebugAndroidTestResources
:mobile:mergeArmDebugAndroidTestResources
:mobile:processArmDebugAndroidTestResources
:mobile:generateArmDebugAndroidTestSources
:mobile:copyArmeabi-v7aDebugArmSharedLibraryStlSo
:mobile:compileNativeArmeabi-v7aDebugArmSharedLibraryNativeMainC
:mobile:compileNativeArmeabi-v7aDebugArmSharedLibraryNativeMainCpp
:mobile:linkNativeArmeabi-v7aDebugArmSharedLibrary
:mobile:nativeArmeabi-v7aDebugArmSharedLibrary
:mobile:stripSymbolsArmeabi-v7aDebugArmSharedLibrary
:mobile:ndkBuildArmeabi-v7aDebugArmSharedLibrary
:mobile:ndkBuildArmeabi-v7aDebugArmStaticLibrary UP-TO-DATE
:mobile:copyArmeabiDebugArmSharedLibraryStlSo
:mobile:compileNativeArmeabiDebugArmSharedLibraryNativeMainC
:mobile:compileNativeArmeabiDebugArmSharedLibraryNativeMainCpp
:mobile:linkNativeArmeabiDebugArmSharedLibrary
:mobile:nativeArmeabiDebugArmSharedLibrary
:mobile:stripSymbolsArmeabiDebugArmSharedLibrary
:mobile:ndkBuildArmeabiDebugArmSharedLibrary
:mobile:ndkBuildArmeabiDebugArmStaticLibrary UP-TO-DATE
:mobile:processAndroidArmDebugMainJniLibs UP-TO-DATE
:mobile:androidArmDebug
:mobile:compileArmDebugJavaWithJavac
:mobile:compileArmDebugJavaWithJavac - is not incremental (e.g. outputs have changed, no previous execution, etc.).
Yes, I know the apache libs are deprecated, but this is legacy code that should work despite that fact, and will be updated in the future.

A general way to do the include you're looking for is this in the dependencies.
compile fileTree(dir: 'libs', include: ['*.jar'])
However, I'm not certain that will solve this particular problem. I've always had success with putting the jar in the libs directory at the top of the directory structure.
If you need to have the jar in a different location, then this works for me:
repositories {
flatDir {
dirs '<relativePathToJar>'
}
}
model { ... }
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
}

Related

Android Studio 2.3.3 using gradle component model was creating .aar automagically; Android Studio 3.4.1 now using CMake or NDK-Build does not

My wavvoxlibrary/build.gradle is not only serving the main program (app/build.gradle) but also creating the shared library .aar.
And that is working nicely with Android Studio 2.3.3 and -- for the library -- gradle-experimental 0.9.3. The gradle component model, however, does not support abifilters, say, to build the JNI / c/c++ programs in 64-bit.
So I upgraded to Android Studio 3.4.1, and my wavvoxlibrary/build.gradle moved away form said component model and got the appropriate CMakeList.txt file to list the c/c++ programs and any compiler options that used to be part of the older build.gradle. And I also added the NDK-Build option with the Application.mk and Android.mk make files.
So far so good. My app is built and running correctly -- including the shared libraries for both, armabi-v7a (32-bit) and arm64-v8a (64-bit).
What's missing: to create the .aar file, I have to explicitly call the gradle task wavvoxlibrary .. assemble.
Is there some way to generate the .aar that during build of my, say, signed apk?
I have removed the gradle component model to move from gradle:2.3.3 and gradle-experimental:0.9.3 with "apply plugin: 'com.android.model.library' "
to gradle:3.4.1 with "apply plugin: 'com.android.library' " and also called the cmake (or, optionally ndkBuild instead) to list the c/c++ files and compiler options there.
See my blog with the details: http://jurgenmenge.com/blog/computer/migrate-a-library-in-android-studio/
import java.text.SimpleDateFormat
// migrate from Android Studio with gradle:2.3.3 + gradle-experimental:0.9.3
// to Android Studio with gradle:3.4.1 + Cmake + ndkBuild
// jm*20190614
apply plugin: 'com.android.library'
//apply plugin: 'com.android.model.library'
/**
* Each subversion code should be exactly 2 numeric digits or we will get wrong versions published
* in the Google Play Store; buildType to switch between "cmake" and "ndkBuild"
*/
ext.majorVersion = "03"
ext.minorVersion = "08"
ext.patchVersion = "03"
static String buildKind() { // neat trick I created :-) // jm*20190614
//return "gradle" // model - experimental
//return "cmake"
return "ndkBuild"
}
/**
* The name of the app version, using the Semantic Versioning format (major.minor.patch)
* #return
*/
String appVersionName() {
String appVersion = removeLeadingZeros(ext.majorVersion) + "." \
+ removeLeadingZeros(ext.minorVersion) + "." \
+ ext.patchVersion
System.out.println(" ************************************************")
System.out.println(" *** WavvoxLibrary version: " + appVersion + " ")
System.out.println(" *** buildKind " + buildKind() )
System.out.println(" ************************************************")
return appVersion
}
//model {
android {
compileSdkVersion 28
buildToolsVersion "28.0.3" // "25.0.3"
defaultConfig {
minSdkVersion 16 /*** was: 8 ***/
targetSdkVersion 28
//
versionName appVersionName() + "_" + buildKind()
setProperty("archivesBaseName", "wavvoxlibrary-$versionName")
buildConfigField "String", "VERSION_NAME_WAVVOX_FORMAT", versionNameWavvoxFormat()
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
//} // defaultConfig
ndk {
moduleName "wavvox-decoder"
ldLibs "log", "android"
// these platforms cover 99% percent of all Android devices
abiFilters "arm64-v8a", "armeabi-v7a"
//, "x86", "x86_64"
} // ndk
externalNativeBuild {
switch (buildKind()) {
case "ndkBuild":
ndkBuild {
cFlags "-std=c99", "-O3", "-Ofast", "-mfpu=neon" // jm*20190616
// "-Wno-error=format-security", "-mfloat-abi=softfp:, "-g"
abiFilters "armeabi-v7a", "arm64-v8a"
}
break
case "cmake":
cmake {
cFlags "-std=c99", "-O3", "-Ofast", "-mfpu=neon" // jm*20190616
// "-Wno-error=format-security", "-mfloat-abi=softfp:, "-g"
abiFilters 'armeabi-v7a', 'arm64-v8a'
}
break
default:
println "**** unknown buildType value: " + buildType() + " ****"
}
} // externalNativeBuild
} // defaultConfig
buildTypes {
release {
minifyEnabled true
proguardFiles "proguard-rules.pro"
}
} // buildTypes
// } // android // jm*20190405
// android.ndk { // jm*20190405
// C source files to include in the build script
// android.sources.main.jni { // jm*20190405
/* if (buildKind() == "gradle") // using "model" in gradle_experimental
sourceSets {
main {
jniLibs.srcDirs = ['src/main/jni'] // for pre-built .so files
jni {
//source {
include "wavvoxNativeApp.c"
// ... and all the other c/c++ programs
srcDir "jni"
//}
} // jni
} // main
} // source
*/
externalNativeBuild {
switch (buildKind()) {
case "ndkBuild":
ndkBuild {
path 'src/main/jni/Android.mk'
}
break
case "cmake":
cmake {
version '3.10.2'
path 'src/main/jni/CMakeLists.txt'
}
break
default:
println "**** unknown buildType value: " + buildType() + " ****"
}
} // externalNativeBuild
} // android // jm*20190405
//} // model
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation 'com.android.support:support-annotations:28.0.0'
testImplementation 'junit:junit:4.12'
testImplementation 'org.hamcrest:hamcrest-junit:2.0.0.0'
}
/**
* Workaround for using ProGuard with the experimental Gradle plugin.
*
* Trouble seems to be that unless process(Debug|Release)Resources task is needed, it is not created
* and VariantOutputScope.setProcessResourcesTask in TaskManager.createProcessResTask() is not
* called so when TaskManager.applyProguardConfig() calls
* BaseVariantOutputData.processResouresTask.getProguardOutputFile() it does so on a null object.
* So, by making transformClassesAndResourcesWithProguardFor(Debug|Release) depend on
* process(Debug|Release)Resources I force the task to be created
*
* More discussions here: https://issuetracker.google.com/issues/37079003
*/
tasks.all { task ->
def match = task.name =~ /^transformClassesAndResourcesWithProguardFor(.*)$/
if (match) {
task.dependsOn "process${match.group(1)}Resources"
return
}
}
// ToDo: get automatic build of .aar libraries working;
// currently, double-click on Gradle "wavvoxlibrary / Tasks / build / assemble"
}
I wanted to get the .aar file created automagically while building the signed app (.apk) without the extra step running gradle task wavvoxlibrary assemble.
What do I miss?
Thanks, jm.
I think you might have tried this, but still adding this as answer, if it does not help, will delete this answer.
Add your library module to your settings.gradle file.
include ':app', ':my-library-module'
https://developer.android.com/studio/projects/android-library#AddDependency

Import swagger-codegen project into existing Android project

Im trying to integrate a "module"-project generated by swagger-codegen, into my Android project.
Haven't worked that much with gradle before and the swagger-codegen creates a quite messy build.gradle from my point of view.
I have a hard time finding documentation on how to do this. And I feel a bit lost.
I used this method described in the FAQ
mvn clean package
java -jar modules/swagger-codegen-cli/target/swagger-codegen-cli.jar generate \
-i http://petstore.swagger.io/v2/swagger.json \
-l java --library=okhttp-gson \
-o /var/tmp/java/okhttp-gson/
So fare I tried to copy the source from the project that was generated by swagger-codegen and merge the two gradle build files. I removed the Junit tests because I couldn't get the Junit dependency working (Implementing Swagger-codegen project - Error:(23, 17) Failed to resolve: junit:junit:4.12). But then I got stuck with some conflict between the plugins?
The 'java' plugin has been applied, but it is not compatible with the Android plugins.
Here's the build.gradle:
import static jdk.nashorn.internal.runtime.regexp.joni.ApplyCaseFold.apply
apply plugin: 'idea'
apply plugin: 'eclipse'
group = 'io.swagger'
version = '1.0.0'
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.2'
// classpath 'com.android.tools.build:gradle:1.5.+'
classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3'
}
}
repositories {
jcenter()
maven { url 'http://repo1.maven.org/maven2' }
}
if(hasProperty('target') && target == 'android') {
apply plugin: 'com.android.library'
apply plugin: 'com.github.dcendents.android-maven'
android {
compileSdkVersion 23
buildToolsVersion '23.0.2'
defaultConfig {
minSdkVersion 14
targetSdkVersion 23
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
// Rename the aar correctly
libraryVariants.all { variant ->
variant.outputs.each { output ->
def outputFile = output.outputFile
if (outputFile != null && outputFile.name.endsWith('.aar')) {
def fileName = "\u0024{project.name}- \u0024{variant.baseName}-\u0024{version}.aar"
output.outputFile = new File(outputFile.parent, fileName)
}
}
}
dependencies {
provided 'javax.annotation:jsr250-api:1.0'
}
}
afterEvaluate {
android.libraryVariants.all { variant ->
def task = project.tasks.create "jar${variant.name.capitalize()}", Jar
task.description = "Create jar artifact for ${variant.name}"
task.dependsOn variant.javaCompile
task.from variant.javaCompile.destinationDir
task.destinationDir = project.file("${project.buildDir}/outputs/jar")
task.archiveName = "${project.name}-${variant.baseName}-${version}.jar"
artifacts.add('archives', task);
}
}
task sourcesJar(type: Jar) {
from android.sourceSets.main.java.srcDirs
classifier = 'sources'
}
artifacts {
archives sourcesJar
}
} else {
apply plugin: 'java'
apply plugin: 'maven'
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
install {
repositories.mavenInstaller {
pom.artifactId = 'XxxxXxxx'
}
}
task execute(type:JavaExec) {
main = System.getProperty('mainClass')
classpath = sourceSets.main.runtimeClasspath
}
}
dependencies {
compile 'io.swagger:swagger-annotations:1.5.8'
compile 'com.squareup.okhttp:okhttp:2.7.5'
compile 'com.squareup.okhttp:logging-interceptor:2.7.5'
compile 'com.google.code.gson:gson:2.6.2'
compile 'joda-time:joda-time:2.9.3'
// testCompile 'junit:junit:4.12'
}
Am I doing something complete wrong here? What is the correct way to implement swagger-codegen code into my project?
The swift way to import it was to compile the swagger generated project then copy the .jar file to my android project and add its as a library.
I have a hard time finding documentation on how to do this. And I feel a bit lost.
You could clone the Android swagger-codegen example.
(which does use Junit, so I'm not sure what error you got)
Unless that's what you mean by
So far I tried to copy the source and merge the two gradle build files
To which, I ask, what two Gradle files? It looks like you merged an Android Gradle file with a Java Gradle file, which seems to causing more issues because you are getting...
The 'java' plugin has been applied, but it is not compatible with the Android plugins.
Which seems pretty self explanatory when you have this line
apply plugin: 'java'
It's not too clear what you are trying to do here other than check the build target
if(hasProperty('target') && target == 'android')

Android Studio 2.0 Preview 5, linking ndk app fails to locate module .so and .a

I have an ndk project with two modules:
abwrenderer - native library module
app - native and java hybrid, glues java to the abwrenderer
I just updated to AS 2.0 Preview 5 this morning, and encountered some gradle related issues.
I upgraded to gradle-2.10 and switched to gradle-experimental:0.6.0-alpha5. When attempting to debug, an ndk build is triggered and I run into the following problem:
Error:error: C:\android\projects\foo\abwrenderer\build\intermediates\binaries\debug\obj\armeabi-v7a\libabwrenderer.so: No such file or directory
Now when I was on gradle-2.9 & gradle-experimental:0.6.0-alpha3, the libraries were built in this directory. After this morning's upgrades, the libraries are now located in:
C:\android\projects\foo\abwrenderer\build\libs\abwrenderer\shared\armeabi-v7a\debug
Is there a way to update the search location for project dependencies that build libraries?
For reference, I define the dependency on abwrenderer project as follows (build.gradle (app)):
android.sources {
main {
jni {
source {
srcDirs 'src/main/jni'
}
dependencies {
project ":abwrenderer" buildType "debug" linkage "shared"
}
}
jniLibs {
source {
srcDirs 'src/main/libs'
}
}
}
}
And build.gradle for abwrenderer project is as follows:
apply plugin: "com.android.model.native"
model {
android {
compileSdkVersion = 23
}
android.ndk {
moduleName = "abwrenderer"
cppFlags.addAll(["--std=c++11",
"-fexceptions",
"-frtti"])
ldLibs.addAll(["android", "EGL", "GLESv3", "log", "dl"])
stl = "c++_static"
debuggable = true
}
android.sources {
main {
jni {
exportedHeaders {
srcDir "src/main/jni"
}
}
}
}
}
I have invalidated caches and restarted, done a clean build, etc. Any help would be greatly appreciated!
Your defaultConfig and ndk blocks were missing some info. They should look similar to this:
defaultConfig {
applicationId = 'com.myapp.abwrenderer'
minSdkVersion.apiLevel = 13
targetSdkVersion.apiLevel = 23
versionCode = 1
versionName = '1.0'
}
ndk {
platformVersion = 21
moduleName = 'abwrenderer'
toolchain = 'clang'
stl = 'gnustl_static'
cppFlags.addAll(['-std=c++11'])
ldLibs.addAll(['android', 'EGL', 'GLESv3', 'log', 'dl'])
}
You should take a look at the following NDK sample from Google to see how they did it: hello-libs

Using clang from NDK gradle build system

Using the old Makefile-based Android build system it is possible to using clang to compile sources by adding
NDK_TOOLCHAIN_VERSION=clang
Is there some way to achieve the same thing using the new gradle build system?
It's not directly possible for now, but you can still use the regular Makefiles from gradle:
import org.apache.tools.ant.taskdefs.condition.Os
apply plugin: 'android'
android {
...
sourceSets.main {
jniLibs.srcDir 'src/main/libs' //set jniLibs directory to ./libs
jni.srcDirs = [] //disable automatic ndk-build call
}
// call regular ndk-build(.cmd) script from main src directory
task ndkBuild(type: Exec) {
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
commandLine 'ndk-build.cmd', '-C', file('src/main').absolutePath
} else {
commandLine 'ndk-build', '-C', file('src/main').absolutePath
}
}
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn ndkBuild
}
}
Newer NDK revisions default to Clang. However, you can explicitly request a toolchain using the -DANDROID_TOOLCHAIN switch.
As of the Android Gradle Plugin 2.2.0 things have become much better. You can also adopt cmake. You should look over the new documentation.
android {
...
defaultConfig {
...
// This block is different from the one you use to link Gradle
// to your CMake or ndk-build script.
externalNativeBuild {
// For ndk-build, instead use ndkBuild {}
cmake {
// Passes optional arguments to CMake.
arguments "-DANDROID_TOOLCHAIN=clang"
// Sets optional flags for the C compiler.
cFlags "-D_EXAMPLE_C_FLAG1", "-D_EXAMPLE_C_FLAG2"
// Sets a flag to enable format macro constants for the C++ compiler.
cppFlags "-D__STDC_FORMAT_MACROS"
}
}
}
buildTypes {...}
productFlavors {
...
demo {
...
externalNativeBuild {
cmake {
...
// Specifies which native libraries to build and package for this
// product flavor. If you don't configure this property, Gradle
// builds and packages all shared object libraries that you define
// in your CMake or ndk-build project.
targets "native-lib-demo"
}
}
}
paid {
...
externalNativeBuild {
cmake {
...
targets "native-lib-paid"
}
}
}
}
// Use this block to link Gradle to your CMake or ndk-build script.
externalNativeBuild {
cmake {...}
// or ndkBuild {...}
}
}

Build and link multiple NDK libraries using gradle

I have an Android project that links together a bunch of sources into a single monolithic JNI library. I would like to split this single library out into multiple smaller libraries with some dependencies between them. How can I achieve this with the new gradle build system?
You can achieve this with the standalone android native plugin from the experimental gradle plugin family. The new plugins are based on the gradle component approach towards modeling builds. There are many advantages to using the new system.
For example:
root
+ lib -> 'com.android.model.native'
+ lub -> 'com.android.model.native'
+ aar -> 'com.android.model.library'
+ app -> 'com.android.model.application'
build.gradle
configure([project(':lib'), project(':lub'), project(':aar'), project(':app')]) {
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle-experimental:0.6.0-alpha5'
}
}
}
lib/build.gradle
apply plugin: "com.android.model.native"
model {
android {
compileSdkVersion 23
ndk {
moduleName "foo"
}
sources {
main {
jni {
exportedHeaders {
srcDir "src/main/headers"
}
}
}
}
}
lub/build.gradle
apply plugin: "com.android.model.native"
model {
android {
compileSdkVersion 23
ndk {
moduleName "bar"
}
sources {
main {
jni {
exportedHeaders {
srcDir "include"
}
}
}
}
}
aar/build.gradle
apply plugin: "com.android.model.library"
model {
android {
buildToolsVersion '23.0.2'
compileSdkVersion 23
ndk {
moduleName "aggregate-jni"
stl "stlport_shared" // using cpp?
}
sources {
main {
jni {
dependencies {
project ":lib"
project ":lub"
}
}
}
}
}
app/build.gradle
apply plugin: 'com.android.model.application'
model {
android {
buildToolsVersion '23.0.2'
compileSdkVersion 23
defaultConfig {
applicationId "com.example.app"
minSdkVersion.apiLevel 21
targetSdkVersion.apiLevel 23
versionCode 1
versionName "1.0"
}
}
}
dependencies {
compile project(':aar')
}
If linkage is dynamic (default for ndk), the aar will contain:
libfoo.so libbar.so libaggregate-jni.so libstlport.so
and your mirror java classes. You can simply
System.load("aggregate-jni");
in your java classes and referenced libraries will load too. If linkage is static, you'll end up with a single libaggregate-jni.so anyway. Note that it's bad to link a bunch of things statically against the stl as you will end up with multiple copies of the stl in your binary. This can really mess things up.
Note that the default gcc toolchain is now deprecated. It's probably best to use:
model {
android {
ndk {
toolchain 'clang'
stl 'c++_shared'
}
}
}
Finally, you absolutely don't need an aar, I simply included it for completeness. The app project can depend directly on standalone native lib projects as the application plugin fully support the ndk, too.

Resources