I'm running gradlew to compile an Android app that has a static lib dependency. Somehow, I have an undefined reference to bsd_signal.
I was able to compile this app with gradle 1.X, but I've been obliged to switch to gradle 2.10 and to drop my Android.mk file in favour of putting more build instruction into my gradle.build file, and this is where the trouble arises.
Can anyone tell me whether there is a library that defines bsd_signal, which I should link to my project?
Compiler output
Starting process 'command '/home/myself/Android/Sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-g++''. Working directory: /home/myself/projects/DroidEar/app Command: /home/myself/Android/Sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-g++ #/home/myself/projects/DroidEar/app/build/tmp/linkNativeArmeabi-v7aDebugSharedLibrary/options.txt
Successfully started process 'command '/home/myself/Android/Sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-g++''
/android/ndk/platforms/android-9/arch-arm/usr/include/signal.h:113: error: undefined reference to 'bsd_signal'
/android/ndk/platforms/android-9/arch-arm/usr/include/signal.h:113: error: undefined reference to 'bsd_signal'
collect2: error: ld returned 1 exit status
TMI: Here is my gradle.build file
apply plugin: 'com.android.model.application'
model {
repositories {
libs(PrebuiltLibraries) {
Superpowered {
binaries.withType(StaticLibraryBinary) {
def prefix = "src/main/jniLibs/Superpowered"
headers.srcDir "${prefix}"
if (targetPlatform.getName() == "armeabi-v7a")
staticLibraryFile = file("${prefix}/libSuperpoweredAndroidARM.a")
else if (targetPlatform.getName() == "arm64-v8a")
staticLibraryFile = file("${prefix}/libSuperpoweredAndroidARM64.a")
else if (targetPlatform.getName() == "x86_64")
staticLibraryFile = file("${prefix}/libSuperpoweredAndroidX86_64.a")
else if (targetPlatform.getName() == "X86")
staticLibraryFile = file("${prefix}/libSuperpoweredAndroidX86.a")
}
}
}
}
android {
compileSdkVersion = 23
buildToolsVersion = "23.0.3"
sources {
main {
jni {
dependencies {
library "Superpowered" linkage "static"
}
}
}
}
ndk {
ldLibs.addAll(['log', 'android', 'c'])
}
defaultConfig {
applicationId = "edu.ucdavis.auditoryenhancer"
minSdkVersion.apiLevel = 22
targetSdkVersion.apiLevel = 23
versionCode = 1
versionName = "1.0"
}
}
android.ndk {
moduleName = "native"
}
android.buildTypes {
release {
minifyEnabled = false
proguardFiles.add(file("proguard-rules.pro"))
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
}
It looks to me like bsd_signal is defined in the signal.o component of platforms/android-9/arch-x86/usr/lib/libc.a, but even with my ldLibs call above including c, I get the error.
Till android-19 inclusive NDK-s signal.h declared bsd_signal extern and signal was an inline calling bsd_signal.
Starting with android-21 signal is an extern and bsd_signal is not declared at all.
What's interesting, bsd_signal was still available as a symbol in NDK r10e android-21 libc.so (so there were no linking errors if using r10e), but is not available in NDK r11 and up.
Removing of bsd_signal from NDK-s android-21+ libc.so results in linking errors if code built with android-21+ is linked with static libs built with lower NDK levels that call signal or bsd_signal. Most popular library which calls signal is OpenSSL.
WARNING: Building those static libs with android-21+ (which would put signal symbol directly) would link fine, but would result in *.so failing to load on older Android OS devices due to signal symbol not found in theirs libc.so.
Therefore it's better to stick with <=android-19 for any code that calls signal or bsd_signal.
To link a library built with <android-21 I ended up declaring a bsd_signal wrapper which would call bsd_signal from libc.so (it's still available in device's libc.so, even up to Android 7.0).
#if (__ANDROID_API__ > 19)
#include <android/api-level.h>
#include <android/log.h>
#include <signal.h>
#include <dlfcn.h>
extern "C" {
typedef __sighandler_t (*bsd_signal_func_t)(int, __sighandler_t);
bsd_signal_func_t bsd_signal_func = NULL;
__sighandler_t bsd_signal(int s, __sighandler_t f) {
if (bsd_signal_func == NULL) {
// For now (up to Android 7.0) this is always available
bsd_signal_func = (bsd_signal_func_t) dlsym(RTLD_DEFAULT, "bsd_signal");
if (bsd_signal_func == NULL) {
// You may try dlsym(RTLD_DEFAULT, "signal") or dlsym(RTLD_NEXT, "signal") here
// Make sure you add a comment here in StackOverflow
// if you find a device that doesn't have "bsd_signal" in its libc.so!!!
__android_log_assert("", "bsd_signal_wrapper", "bsd_signal symbol not found!");
}
}
return bsd_signal_func(s, f);
}
}
#endif
PS. Looks like the bsd_signal symbol will be brought back to libc.so in NDK r13:
https://github.com/android-ndk/ndk/issues/160#issuecomment-236295994
This is ABI breakage in the Android NDK. Libraries/binaries built with a pre-android-21 NDK using signal() won't work with android-21 or newer. And the other way around. This Rust ticket has a more detailed explanation: https://github.com/rust-lang/libc/issues/236#issuecomment-198774409
There is also this ticket for the Android NDK: https://github.com/android-ndk/ndk/issues/48
Related
I have an Android Library project which has a part in C/C++ via Android NDK.
The project started half of a year ago so we chose to use Experimental Plugin because of better NDK support.
I'm using gradle-experimental:0.8.2right now. I have a com.android.model.native module and i would like to migrate it to gradle:2.2.0. The only option i see in Gradle Android Plugin DSL is:
AppExtension: android extension for com.android.application projects.
LibraryExtension: android extension for com.android.library projects.
TestExtension: android extension for com.android.test projects.
So the question is how to make a pure native module in gradle with stable gradle plugin?
Here is my current native module:
apply plugin: 'com.android.model.native'
apply from: "../config.gradle"
def config = ext.configuration
model {
android {
compileSdkVersion = config.compileSdkVersion
buildToolsVersion = config.buildToolsVersion
defaultConfig {
minSdkVersion.apiLevel = config.minimumSdkVersion
targetSdkVersion.apiLevel = config.targetSdkVersion
versionCode = 1
versionName = '1.0'
}
ndk {
moduleName = 'apicore'
platformVersion = config.minimumSdkVersion
cppFlags.add("-std=c++11")
cppFlags.add("-pthread")
cppFlags.add("-fexceptions")
cppFlags.add("-frtti")
stl = "gnustl_static"
abiFilters.addAll(config.targetPlatforms)
ldLibs.addAll(['android', 'log'])
}
sources {
main {
jni {
source {
//include "someFile.txt"
// This is ignored.
exclude "main.cpp"
exclude "misc/APITest.cpp"
exclude "misc/APITest.h"
}
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles.add(file('proguard-android.txt'))
}
}
}
}
You will need to create CMakeLists.txt or Android.mk to build your "libapicore.so", if you want to move to stable gradle plugin.
I think you should do next steps:
For easy migration move your .h, .c, .cpp to the
root_folder_of_project\app\src\main\cpp
Also add there CMakeLists.txt. It should look like:
cmake_minimum_required(VERSION 3.4.1)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -pthread")
add_library(apicore SHARED
#here add your cpp sources
mysource1.cpp
mysource2.cpp
#do not include main.cpp misc/APITest.cpp misc/APITest.h
)
#include libraries needed for apicore lib
target_link_libraries(apicore
android
log
)
Now rewrite your app's build.gradle and point it to CMakeLists.txt:
apply plugin: 'com.android.application'
android {
compileSdkVersion = 25
buildToolsVersion = '25.0.2'
defaultConfig {
applicationId = 'com.your.app'
minSdkVersion 16
targetSdkVersion 25
ndk {
abifilters 'armeabi-v7a' /*,'armeabi', etc.*/
}
externalNativeBuild {
cmake {
arguments '-DANDROID_PLATFORM=android-19',
'-DANDROID_TOOLCHAIN=clang', /*or gcc*/
'-DANDROID_CPP_FEATURES=rtti',
'-DANDROID_CPP_FEATURES=exceptions',
'-DANDROID_STL=gnustl_static' /*CMake uses by default*/
}
}
}
buildTypes {...}
externalNativeBuild {
cmake {
path 'src/main/cpp/CMakeLists.txt'
}
}
}
dependencies {...}
With this you will build your android application with your native "libapicore.so" inside.
I just migrated my project from Gradle Experimental Plugin to Gradle plugin for Android. The current Gradle plugin for Android still not provide something what com.android.model.native extension provided from the experimental plugin which is an ability to create a pure native module. I have to realise that i don't even need that. What i did to replace the com.android.model.native module is i made a library module where i handle the native code and building of my native libraries and i just copy the native libraries where i need them. Of course the module generate the .aar but thats not a problem i just don't use it.
I have several c++ projects which I currently build using Android.mk files and ndk-build. Since this is not really great to debug I want to include this c++ projects as modules inside my android studio project. This android studio project is currently an android java library which uses the .so from the ndk-build.
I got debugging to work by creating a new android library module and importing the c++ code, setting header and library paths. This module looks like this:
apply plugin: 'com.android.model.library'
model {
android {
compileSdkVersion = 23
buildToolsVersion = '23.0.2'
defaultConfig {
minSdkVersion.apiLevel = 4
targetSdkVersion.apiLevel = 23
}
}
android.ndk {
moduleName = 'module_name'
toolchain = 'gcc'
stl "gnustl_static"
ldFlags.addAll(["-L/LIB_PATH"])
ldLibs.addAll(["android", "log", "whatever_lib"])
cppFlags.addAll(['-std=c++11',
"-I" + "/SEARCH_PATHS"])
}
android.productFlavors {
create("arm7") {
ndk.abiFilters.add("armeabi-v7a")
}
}
android.sources {
main {
java.source.srcDirs += []
res.source.srcDirs += []
assets.source.srcDirs += []
jniLibs.source.srcDirs += []
jni.source{
srcDirs += [
NATIVE_PROJECT_PATH + "include/",
NATIVE_PROJECT_PATH + "src/",
]
}
}
}
}
This works fine but i get the .so in module/build/output/aar/libname_release.aar/lib/abi/lib.so which is not a good spot since i would need the .so directly without unzipping the aar.
Is there any possible way to let this module only create the .so at a given path and have another module build and use it?
Of course I find a way shortly after asking.
http://tools.android.com/tech-docs/new-build-system/gradle-experimental#TOC-NDK-Dependencies
Instead of "apply plugin: 'com.android.model.library'" you can use "apply plugin: 'com.android.model.native'" and in the module having the dependency you can use:
sources {
main {
jni {
dependencies {
project ":dependency_module"
}
}
}
}
I was trying to create a simple project to test the NDK in which a jnilib uses a prebuilt .so library but I keep getting an UnsatisfiedLink error:
With emulator:
java.lang.UnsatisfiedLinkError: dlopen failed: library "~/AndroidStudioProjects/HelloAndroidJni/app/src/main/jni/libs/dynamic/x86/libadd.so" not found
With actual device:
Caused by: java.lang.UnsatisfiedLinkError: Cannot load library: link_image[1891]: 1675 could not load needed library '~/AndroidStudioProjects/HelloAndroidJni/app/src/main/jni/libs/dynamic/armeabi/libadd.so' for 'libhello-android-jni.so' (load_library[1093]: Library '~/AndroidStudioProjects/HelloAndroidJni/app/src/main/jni/libs/dynamic/armeabi/libadd.so' not found)
The .so library has a single function "add" which adds 2 numbers. I compiled it with the NDK standalone toolchain for different ABI (doc):
add.c
#include "add.h"
int add(int x, int y){
return x + y;
}
My jni (.c file):
#include <jni.h>
#include "add.h"
JNIEXPORT jint JNICALL
Java_com_example_tomas_helloandroidjni_MainActivity_addNumbersJni(JNIEnv *env, jobject instance,
jint n1, jint n2) {
return add(n1, n2);
}
My files structure looks like this:
And my gradle file (as defined in the experimental gradle guide):
apply plugin: 'com.android.model.application'
model {
repositories {
libs(PrebuiltLibraries) {
libadd{
headers.srcDir "src/main/jni/prebuilts/include"
binaries.withType(SharedLibraryBinary) {
sharedLibraryFile = file("src/main/jni/libs/dynamic/${targetPlatform.getName()}/libadd.so")
}
}
}
}
android {
compileSdkVersion = 23
buildToolsVersion = "23.0.2"
sources {
main {
jni {
dependencies {
library "libadd" linkage "shared"
}
}
}
}
ndk {
moduleName = "hello-android-jni"
debuggable = true
}
defaultConfig.with {
applicationId = "com.example.tomas.helloandroidjni"
minSdkVersion.apiLevel = 15
targetSdkVersion.apiLevel = 23
versionCode = 1
versionName = "1.0"
}
buildTypes {
release {
minifyEnabled = false
proguardFiles.add(file("proguard-android.txt"))
}
}
productFlavors {
create ("x86"){
ndk.abiFilters.add("x86")
}
create("arm"){
ndk.abiFilters.add("armeabi")
}
create("arm7"){
ndk.abiFilters.add("armeabi-v7a")
}
create ("fat"){
}
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.2.0'
}
I'm not sure where to look at, as I keep getting this error... Maybe I need to add a flag or something?
Thanks in advance for any advice!
EDIT 1.
I tried with static libraries and they work fine. As a consequence (of adding the static libs) I changed the folders' structure slightly (modifying the gradle file accordingly).
I'm working on both:
Actual device: Samsung GT-I8190L Android 4.1.2, API 16 - ARMv7 Processor rev 1 (v7l)
Emulator: Nexus 4 API 23 - x86
EDIT 2.
To compile the libraries I'm doing it with the NDK Standalone Toolchain:
Actual device (ARMv7): SYSROOT=$NDK/platforms/android-21/arch-arm and CC="$NDK/toolchains/arm-linux-androideabi-4.8/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-gcc-4.8 --sysroot=$SYSROOT"
Emulator (x86): SYSROOT=$NDK/platforms/android-21/arch-x86 and CC="/Users/Tomas/Library/Android/android-ndk-r10e/toolchains/x86-4.8/prebuilt/darwin-x86_64/bin/i686-linux-android-gcc-4.8 --sysroot=$SYSROOT"
This may not be the way these native dependencies were expected to work, but (update: this is implicitly written in the doc!) even with plugin 0.7.0, gradle does not copy libadd.so to the APK. There is a workaround, though; add to build.gradle the enchantment:
model {
android.sources.main {
jniLibs.source.srcDir 'src/main/jni/libs/dynamic'
}
}
Still, I recommend to move libs/dynamic folder out of the jni sources folder.
Supposedly we can now define dependencies on native-only libraries, but I'm having an issue adding multiple dependencies.
apply plugin: 'com.android.model.library'
model {
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
defaultConfig {
minSdkVersion.apiLevel 11
targetSdkVersion.apiLevel 22
versionCode = 1
versionName = "1.0"
}
sources {
//noinspection GroovyAssignabilityCheck
main {
jni {
dependencies {
project ":libraw"
project ":libjpeg"
}
}
}
}
ndk {
moduleName "rawprocessor"
cppFlags.add("-fexceptions")
ldLibs.add("log")
stl "gnustl_shared"
}
}
}
The two libraries are com.android.model.native and they generate an .so properly.
Headers are loaded from foo, but not bar. If I swap the lines they'll load from bar but not foo. Is there a way to load multiple dependencies?
Update (Actual error messages, complete build.gradle added)
undefined reference to 'jpeg_std_error'
undefined reference to 'jpeg_CreateDecompress'
undefined reference to 'jpeg_mem_src'
These continue for any reference to libjpeg in the aggregate library. If I swap libjpeg to be first I get the same undefined reference to any call in the other library.
undefined reference to `open_file(char const*, long long)'
undefined reference to `recycle()'
Update 2
I just noticed that when I add the aggregate library module (the above build.gradle), the build process does not build the second library. If I comment out that library it builds both native-only libraries. I'm now starting to wonder if there's some issue due to the fact that libraw also depends on libjpeg.
I built some test c and c++ programs using gradle following these examples
I was also able to setup android studio with the experimental features to build a android project calling native functions with the help of this answer
I know that I can build this project using the android.useDeprecatedNdk=true
and provide my android.mk file for android studio to compile and build the app.
Is it possible with the experimental feature to build this same program? This project is built with CMAKE and there are some compilation steps that output files that needs to link. ndk-build creates these files but I can't find a way to do it with with android experimental features.
I am attempting for testing purposes and to build a non trivial example of building a more complex project. I decided to try to build the zlib library.
Here is my build.gradle
apply plugin: 'com.android.model.application'
model {
android {
compileSdkVersion = 23
buildToolsVersion = "23.0.1"
defaultConfig.with {
applicationId = "me.test.testnative_exp"
minSdkVersion.apiLevel = 10
targetSdkVersion.apiLevel = 23
versionCode = 1
versionName = "1.0"
buildConfigFields.with {
create() {
type = "int"
name = "VALUE"
value = "1"
}
}
}
}
compileOptions.with {
sourceCompatibility=JavaVersion.VERSION_1_7
targetCompatibility=JavaVersion.VERSION_1_7
}
android.ndk {
moduleName = "hello-jni"
cppFlags += "-I${file("src/main/jni/zlib")}".toString()
stl = "stlport_static"
}
android.buildTypes {
release {
minifyEnabled = false
proguardFiles += file('proguard-rules.pro')
}
}
android.sources {
main {
java {
source {
srcDir 'src'
}
}
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:23.0.1'
compile 'com.android.support:design:23.0.1'
}
my project structure is
app/
build/
libs/
src/
src/main/testActivity.java
jni/zlib <- the zlib folder is the one that I downloaded from the zlib website.
jni/hello-jni.c
After syncing the build.gradle I get a link error on the header in file:
infbak9.c cannot find #include "zutil.h"
but both files are in the zlib directory under jni.
Is it possible to use the android studio experimental ndk build to compile this project?
there is good project in github that i use its structure in mine, it is nice example for start on "how use experimental(v_0.4.0) plugin in complex project?" :
https://github.com/frogermcs/FlatBuffs
*there are zlib in android NDK, just add appropriate cflag and ldLibs.add("z") in gradle. and in your native code include <zlib.h>
and a good experimental gradle config from google :
https://github.com/googlesamples/android-ndk/blob/229cbe86238d401bb06166b8dfadec8198532589/native-codec/app/build.gradle
you might check this repo for a group of samples:
https://github.com/googlesamples/android-ndk
your issue might be able to fixed by adding something as[from Teapot sample there]:
cppFlags.addAll(['-I' + "${ndkDir}/sources/android/cpufeatures",'-I' + file('src/main/jni/ndk_helper')])
just replace the directory with your own path "zutil.h" etc. you may also use the latest gradle-experimental version to simplify your script -- like removing ".with" for defaultConfig.with, delete java 7 version section( the latest android studio could handle java 8 ), those ".toString()" thing is also wordy so the samples have changed style to use "+" [those is not problem for your build error though]