Building shared with static library dependency - android-ndk

I'm trying to build a shared library using NDK. My folder structure has two folders, one written in C++ (the core) and one written in Java, called project, which is an Android Studio project. The C++ library is compiled fine and the .a file is generated, but then it's not linking with the shared library. Here's my build.gradle:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.3.0'
classpath 'net.sf.proguard:proguard-gradle:5.2.1'
}
}
apply plugin: 'android-library'
android {
compileSdkVersion 16
buildToolsVersion "23.0.1"
defaultConfig {
minSdkVersion 16
targetSdkVersion 23
versionCode 1
versionName "1.0"
ndk {
moduleName "core"
cFlags "-std=c++11 -fexceptions -I../core/includes"
stl "stlport_shared"
}
}
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
jniLibs.srcDir 'libs'
jni.srcDirs = ['src/main/jni', 'src/main/jni/', 'jni/']
}
}
task buildNative(type: Exec, description: 'Compile JNI source via NDK') {
def ndkDir = plugins.getPlugin('com.android.library').sdkHandler.getNdkFolder()
commandLine "$ndkDir/ndk-build",
'-C', file('jni').absolutePath,
'-j', Runtime.runtime.availableProcessors(),
'all',
'NDK_DEBUG=1'
}
task cleanNative(type: Exec, description: 'Clean JNI object files') {
def ndkDir = plugins.getPlugin('com.android.library').sdkHandler.getNdkFolder()
commandLine "$ndkDir/ndk-build",
'-C', file('jni').absolutePath,
'clean'
}
task cleanBinaryFolders(type: Delete, description: 'Clean binary folders') {
delete 'libs', 'obj'
}
clean.dependsOn 'cleanNative'
clean.dependsOn 'cleanBinaryFolders'
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn buildNative
}
}
dependencies {
compile 'com.android.support:support-v4:20.0.0'
}
Here's my Android.mk:
SHELL = /bin/bash
MY_HOMEDIR = $(realpath $(shell pwd)/../..)
MY_COREDIR = $(MY_HOMEDIR)/core
MY_ANDROIDDIR = $(MY_HOMEDIR)/project
MY_CORESOURCES = $(shell find $(MY_COREDIR)/src -type f -name "*.cpp")
MY_BRIDGESOURCES = $(shell find $(MY_ANDROIDDIR)/jni -type f -name "*.cpp")
LOCAL_PATH = $(MY_HOMEDIR)
# Generate a static library from the core implementation
include $(CLEAR_VARS)
LOCAL_MODULE = core
LOCAL_SRC_FILES = $(MY_CORESOURCES)
TARGET_PLATFORM = android-16
TARGET_ARCH_ABI = all
LOCAL_C_INCLUDES = $(MY_COREDIR)/includes
#LOCAL_LDLIBS = -llog
LOCAL_CFLAGS = -llog -I$(MY_COREDIR)/includes
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE = project
LOCAL_SRC_FILES = $(MY_BRIDGESOURCES)
TARGET_PLATFORM = android-16
TARGET_ARCH_ABI = all
LOCAL_STATIC_LIBRARIES = core
LOCAL_C_INCLUDES = $(MY_COREDIR)/includes
LOCAL_LDLIBS = -lcore # I’m not sure about this
LOCAL_CFLAGS = -llog -I$(MY_COREDIR)/includes
include $(BUILD_SHARED_LIBRARY)
When compiling like this I get a bunch of undefined reference errors, even though the methods are implemented by the core module. What am I missing?

https://stackoverflow.com/a/22401410/755804 reads:
Only use LOCAL_LDLIBS for system library dependencies. If you want to point to another library, it's much better to list them in either LOCAL_STATIC_LIBRARIES and LOCAL_SHARED_LIBRARIES (even if this means defining a PREBUILT_XXX module for them), because this lets the build system work out dependencies and ordering automatically for you.

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

How I can add a cflag in cmake Android Studio?

How I can add -D_FILE_OFFSET_BITS=64 in Cmake config file. I'm trying to add as cflag in build.gradle but it not work.
externalNativeBuild {
cmake {
cppFlags ""
cFlags "-D_FILE_OFFSET_BITS=64"
arguments "-DANDROID_TOOLCHAIN=gcc"
}
}
In the CMakeLists.txt file, I do something like:
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D__ANDROID__ -DANDROID -DCUSTOM_FLAG")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DGL_GLEXT_PROTOTYPES=1 -DIOAPI_NO_64 -DUSE_FILE32API ")
However, you can use gradle too, but externalNativeBuild needs to be under defaultConfig or release or debug (a build flavor) for cmake options to pass the flags.
For Example:
android {
compileSdkVersion 25
buildToolsVersion '25.0.3'
defaultConfig {
applicationId "..."
externalNativeBuild {
cmake {
// here, arguments, cppFlags, cFlags, .. all work
arguments '-DANDROID_PLATFORM=android-15',
'-DANDROID_TOOLCHAIN=clang', '-DANDROID_STL=gnustl_static',
'-DANDROID_CPP_FEATURES=rtti exceptions'
}
}
}
externalNativeBuild {
cmake {
// only 'path' variable is valid here
path '../../gameSource/CMakeLists.txt'
}
}
buildTypes {
release {
// .. release flavor
}
debug {
//... debug flavor
}
}
}

Building libxml2 into NDK with Android Studio and Gradle Experimental

My Android project has been going for a couple of years using Eclipse and ADT. The project makes use of 3 precompiled static libraries (curl, ssl, and crypto), and then compiles and statically links libxml2 in. The relevant lines from Android.mk are:
LOCAL_MODULE := my_shim
LOCAL_SRC_FILES := $(LOCAL_FILE_LIST:$(LOCAL_PATH)/%=%)
LOCAL_CFLAGS := -DCURL_DISABLE_TYPECHECK
LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog -lz
LOCAL_SHARED_LIBRARIES :=
LOCAL_STATIC_LIBRARIES += xml2 curl ssl crypto
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../c_module
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../libxml2/include
include $(BUILD_SHARED_LIBRARY)
include $(APP_LOCAL_PATH)/../../libxml2/Android.mk
I'm now switching to Android Studio 2 using Gradle Experimental, but I'm having problems getting the Gradle configuration right. I think I've managed to sort out the dependencies on log and z, and the statically compiled curl, ssl, and crypto libraries, but I can't work out how to tell it to build and then statically link the libxml2 module.
Any clues? This is what I have so far:
model {
android {
...
}
android.ndk {
moduleName "my_shim"
platformVersion 19
abiFilters.addAll(["armeabi", "x86"])
CFlags.add("-DCURL_DISABLE_TYPECHECK")
ldLibs.addAll(["log", "z"])
stl "stlport_static"
}
android.sources {
main {
jni {
dependencies {
library "crypto" linkage "static"
library "curl" linkage "static"
library "ssl" linkage "static"
library "xml2" linkage "static"
}
}
}
}
repositories {
libs(PrebuiltLibraries) {
crypto {
binaries.withType(StaticLibraryBinary) {
def cryptoLibPath = "src/main/jni/includes/${targetPlatform.getName()}/libcrypto.a"
staticLibraryFile = file("${cryptoLibPath}")
}
}
}
libs(PrebuiltLibraries) {
curl {
binaries.withType(StaticLibraryBinary) {
def curlLibPath = "src/main/jni/includes/${targetPlatform.getName()}/libcurl.a"
staticLibraryFile = file("${curlLibPath}")
}
}
}
libs(PrebuiltLibraries) {
ssl {
binaries.withType(StaticLibraryBinary) {
def sslLibPath = "src/main/jni/includes/${targetPlatform.getName()}/libssl.a"
staticLibraryFile = file("${sslLibPath}")
}
}
}
}
}
There're two ways, I think.
Prebuild libxml2 manually and put it with other prebuilt libraries.
Make a dependency (and separate project for xml2 library) It's described here

How do I wrap an existing C library for use with Android Studio, with SWIG, given I have the headers?

I have an existing C library, built for Android. I now need to interface with it using JNI. SWIG seems to be a smart way to do this. But all of the SWIG examples I can find build c code into a library which is then wrapped by SWIG.
I have all the headers for the library I have, which is necessary for SWIG to do its work, but I can't figure out how to include the library in the build process of Android Studio.
The gradle build file looks like this for one example project, but I don't see how it knows to include the built .so file into the project.
apply plugin: 'com.android.application'
android {
compileSdkVersion 21
buildToolsVersion "21.1.2"
defaultConfig {
applicationId "com.sureshjoshi.android.ndkexample"
minSdkVersion 15
targetSdkVersion 21
versionCode 1
versionName "1.0"
ndk {
moduleName "SeePlusPlus" // Name of C++ module (i.e. libSeePlusPlus)
cFlags "-std=c++11 -fexceptions" // Add provisions to allow C++11 functionality
stl "gnustl_shared" // Which STL library to use: gnustl or stlport
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:21.0.3'
compile 'com.jakewharton:butterknife:6.0.0'
}
// Location of where to store the jni wrapped files
def coreWrapperDir = new File("${projectDir}/src/main/java/com/sureshjoshi/core")
task createCoreWrapperDir {
coreWrapperDir.mkdirs()
}
// For this to work, it's assumed SWIG is installed
// TODO: This only works when called from Command Line (gradlew runSwig)
task runSwig(type:Exec, dependsOn: ['createCoreWrapperDir']) {
String osName = System.getProperty("os.name").toLowerCase();
if (osName.contains("windows")) {
workingDir '/src/main/jni' // This implicitly starts from $(projectDir) evidently
commandLine 'cmd', '/c', 'swig'
args '-c++', '-java', '-package', 'com.sureshjoshi.core', '-outdir', coreWrapperDir.absolutePath, 'SeePlusPlus.i'
}
else {
commandLine 'swig'
args '-c++', '-java', '-package', 'com.sureshjoshi.core', '-outdir', coreWrapperDir.absolutePath, "${projectDir}/src/main/jni/SeePlusPlus.i"
}
}
The example code you're using is mine, so maybe I can help :)
The library itself is statically loaded into the project in the MainActivity (or MainApplication, whatever). Make sure your libraries end up in the correct location
static {
// Use the same name as defined in app.gradle (do not add the 'lib' or the '.so')
System.loadLibrary("SeePlusPlus");
}
In my NDK example, you'll see after building, the .so files end up in android-ndk-swig-example/NDKExample/app/build/intermediates/ndk/debug/lib/
If you have your own .so files, I believe they need to go into the app/src/main/jniLibs/[architecture] folder... You can put them there, then try the loadLibrary, and if the app crashes, it's incorrect.
Similarly, check out this answer - specifically the talk about sourcesets (default should be jniLibs, but you can mod it): https://stackoverflow.com/a/26693354/992509

How to set standard c99 for compile android NDK project

I want to build small library which was written in C99 for Android, but compiler gave log as
note: use option -std=c99 or -std=gnu99 to compile your code
Where can I set it?
In your Android.mk add
LOCAL_CFLAGS += -std=c99
For example:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_CFLAGS += -std=c99
LOCAL_SRC_FILES := com_example_ndktest_TestLib.c
LOCAL_MODULE := com_example_ndktest_TestLib
include $(BUILD_SHARED_LIBRARY)
Make sure you add 'LOCAL_CFLAGS' after adding 'include $(CLEAR_VARS)'
An addendum to auselen's answer:
According to the NDK docs (kandroid.org mirror), LOCAL_CFLAGS will only apply to each module - if you want this behavior across an entire project, set APP_CFLAGS in Application.mk. Also, CFLAGS will cover C and C++ sources, CPPFLAGS covers C++ only.
As people may be arriving here looking to "set standard c99 for compile android NDK project", I think this needs an update.
For Android Studio 1.4 with Gradle 2.5, the c99 can be set in build.gradle
NOTE THAT THE CASE SENSITIVE SYNTAX WITHIN BUILD.GRADLE HAS CHANGED FROM cFlags TO CFlags (many examples online use the old syntax).
here is a build.gradle modified from the sample hello-jni project with C99 support added.
apply plugin: 'com.android.model.application'
model {
android {
compileSdkVersion = 23
buildToolsVersion = "23.0.0"
defaultConfig.with {
applicationId = "com.example.hellojni"
minSdkVersion.apiLevel = 4
targetSdkVersion.apiLevel = 23
}
}
compileOptions.with {
sourceCompatibility=JavaVersion.VERSION_1_7
targetCompatibility=JavaVersion.VERSION_1_7
}
/*
* native build settings
*/
android.ndk {
moduleName = "hello-jni"
CFlags += "-std=c99"
}
android.buildTypes {
release {
minifyEnabled = false
proguardFiles += file('proguard-rules.txt')
}
}
android.productFlavors {
// for detailed abiFilter descriptions, refer to "Supported ABIs" #
// https://developer.android.com/ndk/guides/abis.html#sa
create("arm") {
ndk.abiFilters += "armeabi"
}
create("arm7") {
ndk.abiFilters += "armeabi-v7a"
}
create("arm8") {
ndk.abiFilters += "arm64-v8a"
}
create("x86") {
ndk.abiFilters += "x86"
}
create("x86-64") {
ndk.abiFilters += "x86_64"
}
create("mips") {
ndk.abiFilters += "mips"
}
create("mips-64") {
ndk.abiFilters += "mips64"
}
// To include all cpu architectures, leaves abiFilters empty
create("all")
}
}

Resources