I'm developing an Android app using FFmpeg. I have built FFmpeg into *.so files and put them into jniLibs as follows:
src
--main
----jniLibs
------armeabi
--------libavcodec-57.so
--------libavformat-57.so
--------xx.so
While in grade script, abifilter of ndk is armeabi.
In java files, I have succeeded load these .so files and the built apk also contains them. However, when I use any API (e.g. av_register_all()) of them in a .c file under src/jni folder, build error comes:
Error:(14) undefined reference to 'av_register_all'
Error:Execution failed for task ':app:compileDebugNdk'.
com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Process 'command '/Users/zhouyf/Library/Android/sdk/ndk-bundle/ndk-build'' finished with non-zero exit value 2
It seems that problem exists in linker. But I found answer that just putting .so files to jniLibs/armeabi will be OK.
Do I need modify build.gradle file to link those .so files or else?
P.S.
If I don't call the API, the app will run successfully, only with warning: W/linker: libavformat-57.so: unused DT entry: type 0x6ffffffe arg 0x60e0
W/linker: libavformat-57.so: unused DT entry: type 0x6fffffff arg 0x2
Environment:
Android Studio 2.1.1
Mac OS X 10.11.5
You should add libraries as dependencies when you build by using ndk-build.
Disable default ndk-build, in your build.gradle
sourceSets.main {
jniLibs.srcDir 'src/main/libs'
jni.srcDirs = []
}
Add your own ndk-build task, in your build.gradle
def ndkDir = properties.getProperty("ndk.dir")
task ndkBuild(type: Exec) {
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
commandLine ndkDir + File.separator + 'ndk-build.cmd', '-C', file('src/main/jni').absolutePath
} else {
commandLine ndkDir + File.separator + 'ndk-build', '-C', file('src/main/jni').absolutePath
}
}
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn ndkBuild
}
Finally, in your Android.mk, add the libraries like this.
LOCAL_MODULE := # ...
# ...
LOCAL_LDFLAGS += -L/path/of/the/libraries -lavcodec-57 -lavformat-57 ...
Related
I have updated my project's Gradle version from 5.6.4 to 6.8.1, with Gradle-Build-Tool updated from 3.4.3 to 4.1.1.
And I recived this error in externalNativeBuild phase when I rebuilt my project in Android Studio, there is nothing more.
I copy the command and run it in Terminal, and I get the same errors.
This is the command line I get in Gradle 6`s build:
ndk-build NDK_PROJECT_PATH=null APP_BUILD_SCRIPT={MyPath}/jni/Android.mk NDK_APPLICATION_MK={MyPath}/jni/Application.mk APP_ABI=armeabi-v7a NDK_ALL_ABIS=armeabi-v7a NDK_DEBUG=1 APP_PLATFORM=android-21 NDK_OUT={MyPath}/build/intermediates/ndkBuild/debug/obj NDK_LIBS_OUT={MyPath}/build/intermediates/ndkBuild/debug/lib APP_CPPFLAGS+=-frtti APP_CPPFLAGS+=-DCC_ENABLE_CHIPMUNK_INTEGRATION=1 APP_CPPFLAGS+=-DL2D_TARGET_ANDROID_ES2=1 APP_CPPFLAGS+=-std=c++14 APP_CPPFLAGS+=-pthread APP_CPPFLAGS+=-DCSM_TARGET_ANDROID_ES2=1 APP_CPPFLAGS+=-fexceptions APP_CPPFLAGS+=-fpermissive APP_CPPFLAGS+=-DCHANNEL_CN APP_CPPFLAGS+=-DCOCOS2D_DEBUG=1 APP_STL=gnustl_static NDK_TOOLCHAIN_VERSION=4.9 APP_LDFLAGS=-latomic APP_PLATFORM=android-18 NDK_MODULE_PATH={SomeOtherPath} -j8 NDK_DEBUG=1 nnchannelcn
So I check it in Gradle 5, and I find out there is an extra argurment in Gradle 6, its the last one nnchannelcn, which is my target name.
so I remove it and rebuild in Terminal, and It fixed this error.
I had set target to nnchannelcn in my build.gadle, and it wonnot add this argurment in Gradle 5. This is my build.gradle:
...
externalNativeBuild {
ndkBuild {
targets 'nnchannelcn'
arguments 'APP_STL=gnustl_static'
arguments 'NDK_TOOLCHAIN_VERSION=4.9'
arguments 'APP_LDFLAGS=-latomic'
arguments 'APP_PLATFORM=android-18'
arguments 'NDK_MODULE_PATH='{SomeOtherPath}'
arguments '-j' + Runtime.runtime.availableProcessors()
cppFlags = ["-frtti", "-DCC_ENABLE_CHIPMUNK_INTEGRATION=1", "-DL2D_TARGET_ANDROID_ES2=1",
"-std=c++14", "-pthread","-DCSM_TARGET_ANDROID_ES2=1",
"-fexceptions", "-fpermissive", "-DCHANNEL_CN"]
}
arguments 'NDK_DEBUG=1'
}
}
ndk {
abiFilters = []
abiFilters.addAll(PROP_APP_ABI.split(':').collect{it as String})
}
...
externalNativeBuild {
ndkBuild {
path "jni/Android.mk"
}
}
...
externalNativeBuild {
ndkBuild {
arguments 'NDK_DEBUG=1'
}
}
...
I change my ndk-build file to refuse last argurment to solve it, and Its work well in Android Studio, but its only a temporary measures.
I want to know how this argurment cause ndk-build fail? And is there a way to remove this in build.gradle? Or it`s my fault to miss some settings?
My NDK version is 'r16b', but I had change it to 'r21' in my attempt, its doesn't work, The only thing I changed is gradle version, so I don`t think is NDK error or some ".mk' errors.
Thanks.
I'm currently working on a nbk project for android. I have the current commands in my build.gradle, so that I can build from my gradle wrapper:
def ndkDir = "/Development/android-sdk-macosx/ndk-bundle"
task buildNative(type: Exec, description: 'Compile JNI source via NDK') {
commandLine "$ndkDir/ndk-build",
'-C', file('src/main/jni').absolutePath,
'-j', Runtime.runtime.availableProcessors(),
'all',
'NDK_DEBUG=1'
}
task cleanNative(type: Exec, description: 'Clean JNI object files') {
commandLine "$ndkDir/ndk-build",
'-C', file('src/main/jni').absolutePath,
'clean'
}
I am able to build using ./gradlew buildNative, but when I try gradle assembleDebug I get errors:
compiling TextRenderer.cpp failed.
/Users/user/android-ndk/san-angeles/app/src/main/jni/src/Renderers/TextRenderer.cpp:5:10: fatal error: 'ft2build.h' file not found
#include <ft2build.h>
^
1 error generated.
compiling BoxRenderer.cpp successful.
compiling triangle.cpp successful.
Finished compileSanangelesArmeabi-v7aDebugSharedLibrarySanangelesMainCpp, see full log file:///Users/user/android-ndk/san-angeles/app/build/tmp/compileSanangelesArmeabi-v7aDebugSharedLibrarySanangelesMainCpp/output.txt.
I am using a freetype library that I cross compiled with issues, but I got it passed that point (I think):
Was able to cross compile Freetype2, now what?
Edit:
I have changed my gradle build to this:
apply plugin: 'com.android.model.application'
model {
android {
...
sourceSets.main {
jniLibs.srcDir 'src/main/jni/freetype/lib'
jni.srcDirs = []
}
...
}
}
task buildNative (...){
...
}
task cleanNative (...){
...
}
But now I get this error:
Gradle sync failed: Cause: com.android.build.gradle.managed.AndroidConfig$Impl
If you don't want to struggle with the ever-changing C++ support of the experimental gradle plugin, you can simply use build.gradle generated by Android Studio. To disable automatic compilation of C++ files in the jni folder, you can override jni.srcDirs:
jni.srcDirs = []
jniLibs.srcDir 'src/main/jni/freetype/lib'
Here I override jniLibs.srcDir so that the library compiled with ndk-build will be included in the APK file.
Actually, I prefer to keep jni.srcDirs pointing at my C++ files (this way, I don't need another IDE running to work with them), and I disable the gradle tasks (somewhere in build.gradle file):
tasks.all {
task -> if (task.name.contains('compileDebugNdk') ||
task.name.contains('compileReleaseNdk'))
task.enabled = false
}
I can also teach the system to run buildNative and cleanNative when necessary:
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn buildNative
}
clean.dependsOn cleanNative
Note that the next version of Android Studio, 2.2 (now in public beta), makes ndk-build a first class citizen.
I am using Almalence: Open Camera repository
Checkout the Screenshot
Gives Compile Errors:
Error:(24, 34) ImageConversionUtils.h: No such file or directory
compilation terminated.
make.exe: *** [C:\Users\sagar_000\Documents\OpenCamera-master\app\build\intermediates\ndk\debug\obj/local/arm64-v8a/objs/OpenCamera-master/C_\Users\sagar_000\Documents\OpenCamera-master\app\src\main\jni\bestshot\bestshot.o] Error 1
Error:Execution failed for task ':app:compileDebugNdk'.
com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Process 'command 'C:\Users\sagar_000\AppData\Local\Android\sdk\ndk-bundle\ndk-build.cmd'' finished with non-zero exit value 2
You just need to add these ndk properties into your build.gradle. Must be same level with defaultConfig
sourceSets.main {
jni.srcDirs = [] // This prevents the auto generation of Android.mk
jniLibs.srcDir 'src/main/libs' // This is not necessary unless you have precompiled libraries in your project.
}
task buildNative(type: Exec, description: 'Compile JNI source via NDK') {
def ndkDir = android.ndkDirectory
commandLine "$ndkDir/ndk-build.cmd",
'-C', file('src/main/jni').absolutePath, // Change src/main/jni the relative path to your jni source
'-j', Runtime.runtime.availableProcessors(),
'all',
'NDK_DEBUG=1'
}
task cleanNative(type: Exec, description: 'Clean JNI object files') {
def ndkDir = android.ndkDirectory
commandLine "$ndkDir/ndk-build.cmd",
'-C', file('src/main/jni').absolutePath, // Change src/main/jni the relative path to your jni source
'clean'
}
In old traditional android ndk we will specify the static library to be linked in the Android.mk file.
Android.mk
PLATFORM_PREFIX := /opt/android-ext/
LOCAL_PATH := $(PLATFORM_PREFIX)/lib
include $(CLEAR_VARS)
LOCAL_MODULE := library
LOCAL_SRC_FILES := library.a
include $(PREBUILT_STATIC_LIBRARY)
LOCAL_STATIC_LIBRARIES := android_native_app_glue library
Here is my Question
I am little bit Confused when switching to Gradle experimental plugin for NDK. Share your ideas on how to link Static library in App build.gradle file.
I had followed the latest gradle experimental plug-in documentation given here.
Have a look at this sample.
Tell the compiler where the headers are (in android.ndk{}):
CFlags += "-I${file("path/to/headers")}".toString()
cppFlags += CFlags
Tell the linker where the .a file is (in android.ndk{} or where
defining the flavors - make sure to add abiFilter - for example
abiFilters += "armeabi-v7")
ldFlags += "-L${file(path/to/library.a)}".toString()
ldLibs += ["nameOfLibrary"]
Note that the name of the library by convention is the string after
"lib" in the .a file name. For example, for a file named libNative.a
you should add ldLibs += ["native"] to gradle.
Create a new module and use apply plugin: 'java' to apply java plugin. In the build.gradle write the necessary code to get and place the .a file in the appropriate directory (where you will get it from your module which is using it). Don't forget to add a dependency in the module using the library (compile project(':libraryModule') in dependencies{}) and to include it in the project in settings.gradle file with include ':libraryModule'. If you want to place the module in a specified by you folder (for example where currently your Android.mk file is), just add project(':libraryModule').projectDir = new File(settingsDir, 'path/to/module').
That should do it.
The above answers work around gradle's prior insufficient NDK integration. This answer illustrates new gradle integration with the NDK.
Take a look at this proposed sample written against gradle 2.9 and the android plugin 0.6.0-alpha1. In constrast to how the question is posed, this answer contains a separate project for the library. This functionality can be explored to allow gradle to build that library before it is used by the app project. The other answers rely on the presumption that the library had already been built.
The :secondlib com.android.model.application builds libsecondlib.so (loaded in Java code with System.loadLibrary("secondlib"). The name 'secondlib' is poorly named. I like to think of it as a .so "wrapper" for all other native libraries linked for use by the app.
That shared library is statically linked against firstlib.a as built by the :firstlib com.android.model.native.
The headers are exported from the :firstlib to any dependent projects (:secondlib in this example) as per the exportedHeaders clause. This way dependent projects know how to link against the .so/.a. This replaces the CFlags+="-I/path/to/headers" syntax in a prior answer.
:secondlib links against :firstlib statically as per the following clause:
android.sources {
main {
jni {
dependencies {
project ":firstlib" buildType "debug" linkage "static"
}
}
// TODO(proppy): show jniLibs dependencies on .so
}
}
As shown by the comment, the example is incomplete. The finishing syntax is shown in the 'NDK Dependencies' section of the experimental android plugin documentation. Within that document the syntax for how to statically link instead of dynamically link should be clear.
There are currently some shortcomings (e.g. the buildType of the dependency shown above is by default 'debug' and not the current buildType being built).
EDIT: Here is a work-in-progress sample of the new dependencies syntax in app/build.gradle pulled out of one of my projects:
android.sources {
main {
jni {
//for exportedHeaders
dependencies { project ":libfoo" linkage "shared" }
}
jniLibs {
//Where the swig wrapped library .so is. I use swig to create code to interface with libfoo.so within :app
source { srcDirs 'libs' }
//for file in $(model.repositories.libs.libfoo)
dependencies { library "libfoo" }
}
}
}
repositories {
libs(PrebuiltLibraries) {
libevdev {
//headers already available from our libfoo project via exportedHeaders
//headers.srcDir "../libfoo/src/main/jni/"
binaries.withType(SharedLibraryBinary) {
sharedLibraryFile = file("../libfoo/build/intermediates/binaries/debug/lib/${targetPlatform.getName()}/libfoo.so")
}
}
}
}
Conceptually, apk is zip of manifest,native files and class library.
So if you copy the static library to output it will work.
So in gradle you should use the copy task and make these library as part of output.
I just used below for my so files and it worked.
task copyNativeLibs2h(type: Copy) {
from(new File(getProjectDir(), 'libs')) { include '**/*.so' }
into new File(buildDir, 'native-libs')
}
tasks.withType(JavaCompile) { compileTask -> compileTask.dependsOn copyNativeLibs2h }
You cannot build a static library with gradle, even with the experimental plugin. You can use a prebuilt library, or build it with ndk-build and link it into the shared object with the gradle plugin.
Here is an example:
Assume that we have this directory structure:
project (build.gradle, gradle.properties, etc.)
jni_static (Application.mk, Android.mk, cpp files for the static lib)
app (build.gradle, src/main, etc.)
jni_shared (cpp files for the shared lib)
And here are the relevant pieces of project/app/build.gradle:
// look for NDK directory
import org.apache.tools.ant.taskdefs.condition.Os
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
def ndkBuild = properties.getProperty('ndk.dir') + '/ndk-build'
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
ndkBuild += '.cmd'
}
apply plugin: 'com.android.model.application'
… dependencies, model { compileOptions, etc.
// static lib is built with ndk-build
def LOCAL_MODULE = "static"
def appAbi = "armeabi-v7a"
def ndkOut = "build/intermediates/$LOCAL_MODULE"
def staticLibPath = "$ndkOut/local/$appAbi/lib${LOCAL_MODULE}.a"
// To guarantee that the intermediates shared library is always refreshed,
// we delete it in gradle task rmSO.
task rmSO(type: Delete) {
delete 'build/intermediates/binaries/debug/lib/armeabi-v7a', 'libshared.so'
delete 'build/intermediates/binaries/debug/obj/armeabi-v7a', 'libshared.so'
}
// in file jni/Android.mk there is a section for LOCAL_MODULE=static
// which builds the static library
task buildStaticLib(type: Exec, description: 'Compile Static lib via NDK') {
commandLine "$ndkBuild", "$staticLibPath", "NDK_APPLICATION_MK=../jni_static/Application.mk",
"NDK_PROJECT_PATH=../jni_static", "NDK_OUT=$ndkOut"
dependsOn rmSO
}
task cleanNative(type: Exec, description: 'Clean JNI object files') {
commandLine "$ndkBuild", "clean", "NDK_APPLICATION_MK=../jni_static/Application.mk",
"NDK_PROJECT_PATH=../jni_static", "NDK_OUT=$ndkOut"
}
clean.dependsOn cleanNative
tasks.all {
task ->
// link of the shared library depends on build of the static lib
if (task.name.startsWith('link')) {
task.dependsOn buildStaticLib
}
// before build, make sure the intermediate so is not stuck there
if (task.name.startsWith('package')) {
task.dependsOn rmSO
}
}
// build the wrapper shared lib around the static lib using the experimental plugin
model {
android.ndk {
moduleName = "shared"
cppFlags += "-std=c++11"
ldFlags += "$staticLibPath".toString()
ldLibs += "log"
stl = "gnustl_static"
abiFilters += "$appAbi".toString()
}
android.sources {
main.jni.source {
srcDirs = ["jni_shared"]
}
}
}
Important: the sources for the shared lib should be in a separate directory, so that the sources of the static library are not in it or under it. This because the experimental plugin cannot exclude some files under srcDirs.
I am trying to build android project with imported c-libraries and get next error.
Error:Execution failed for task ':app:compileDebugNdk'.
com.android.ide.common.internal.LoggedErrorException: Failed to run command:
/Applications/Android Studio.app/ndk/ndk-build NDK_PROJECT_PATH=null APP_BUILD_SCRIPT=/Users/eugene/KREF14001/app/build/ndk/debug/Android.mk APP_PLATFORM=android-19 NDK_OUT=/Users/eugene/KREF14001/app/build/ndk/debug/obj NDK_LIBS_OUT=/Users/eugene/KREF14001/app/build/ndk/debug/lib APP_ABI=all
Error Code:
2
Output:
usage: dirname path
/Users/eugene/build/core/init.mk:471: * Android NDK: Aborting . Stop.
What does it mean? How can i fix this problem?
I've read that the NDK_HOME shouldn't contain any space in its path (even if it's \ escaped).
This is the code i use to build using android-ndk from gradle. For this add ndk directory path in gradle.properties ie . add ndkdir=/home/user/android-ndk-r9d and put all jni files in a folder native in src/main/ as you can see from code posted below. It will create jar with native libs which you can use normally as in System.loadLibrary("library");
dependencies {
compile fileTree(dir: "$buildDir/native-libs", include: '*.jar')
}
task ndkBuild(type: Exec) {
commandLine "$ndkdir/ndk-build", "--directory", "$projectDir/src/main/native", '-j', Runtime.runtime.availableProcessors(),
"APP_PLATFORM=android-8",
"APP_BUILD_SCRIPT=$projectDir/src/main/native/Android.mk",
"NDK_OUT=$buildDir/native/obj",
"NDK_APP_DST_DIR=$buildDir/native/libs/\$(TARGET_ARCH_ABI)"
}
task nativeLibsToJar(type: Zip, description: 'create a jar with native libs') {
destinationDir file("$buildDir/native-libs")
baseName 'native-libs'
extension 'jar'
from fileTree(dir: "$buildDir/native/libs", include: '**/*.so')
into 'lib/'
}
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn nativeLibsToJar
}
nativeLibsToJar.dependsOn 'ndkBuild'