Brief:
Does anyone know if it possible to configure Cmake to add sub_directory base on Android ABIs (x86,arm64-v8a)?
Something likes:
if (android_x64)
add_subdirectory(${distribution_DIR}/android_x64)
elseif(android_arm64)
add_subdirectory(${distribution_DIR}/android_arm64)
endif()
More Detail:
My scenario is that for a directory structure such as:
AndroidStudioProject
-CMakeLists.txt
-third_party
-android_x64
CMakeListsA.txt
-android_arm64
CMakeListsB.txt
The CMakeLists.txt (Root Project) looks like this:
cmake_minimum_required(VERSION 3.4.1)
# configure import libs
set(distribution_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/out)
add_subdirectory(${distribution_DIR}/android_x64})
add_subdirectory(${distribution_DIR}/android_arm64)
CMakeListA.txt:
# Generated by gn_to_cmake.py.
cmake_minimum_required(VERSION 3.7 FATAL_ERROR)
cmake_policy(VERSION 3.7)
file(WRITE "/third_party//out/android_x64/empty.cpp")
#//:default
set("target" "default")
set("${target}__cxx_srcs" "/third_party/out/android_x64/empty.cpp")
add_custom_target("${target}" SOURCES ${${target}__cxx_srcs})
add_dependencies("${target}"
"build__chip__java__tests_java_build_test"
"src__app"
"src__app__server")
CMakeListB.txt:
# Generated by gn_to_cmake.py.
cmake_minimum_required(VERSION 3.7 FATAL_ERROR)
cmake_policy(VERSION 3.7)
file(WRITE "/third_party//out/android_arm64/empty.cpp")
#//:default
set("target" "default")
set("${target}__cxx_srcs" "/third_party/out/android_arm64/empty.cpp")
add_custom_target("${target}" SOURCES ${${target}__cxx_srcs})
add_dependencies("${target}"
"build__chip__java__tests_java_build_test"
"src__app"
"src__app__server")
externalNativeBuild in Gradle:
android {
compileSdkVersion 29
ndkVersion '22.1.7171670'
defaultConfig {
// ...
externalNativeBuild {
cmake {
targets "default"
}
}
}
// ...
externalNativeBuild {
cmake {
version '3.10.2'
path '../CMakeLists.txt'
}
}
}
I'm hoping that someone with more experience with native development on Android could help me out. Thanks!
Related
I recently updated Android Gradle Plugin to version 7.0.0 (Gradle version 7.0.2).
Since I did this update, my native library continues to be compiled regularly, but no .so files are generated in my final apk.
In fact, running the app the exception is thrown:
java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/it.Ettore.raspcontroller-2/base.apk"],nativeLibraryDirectories=[/data/app/it.Ettore.raspcontroller-2/lib/x86, /data/app/it.Ettore.raspcontroller-2/base.apk!/lib/x86, /system/lib, /vendor/lib]]] couldn't find "libf-native-lib.so"
By downgrading to Android Gradle Plugin version 4.2.2 (Gradle version 6.7.1), everything works fine.
Could it be an Android Gradle Plugin bug or is it my mistake?
build.gradle :
android {
defaultConfig {
externalNativeBuild {
cmake {
cFlags "-fvisibility=hidden"
cppFlags "-fvisibility=hidden"
}
}
ndk {
moduleName "f-native-lib"
}
sourceSets.main {
jni.srcDirs = ['src/main/c']
}
}
buildTypes {
release {
shrinkResources true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
ndk {
debugSymbolLevel 'SYMBOL_TABLE'
}
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
CMakeList.txt:
cmake_minimum_required(VERSION 3.4.1)
add_library( # Sets the name of the library.
f-native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/c/mydir/myfile.c
)
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that you want CMake to locate.
log
)
find_library( # Sets the name of the path variable.
z-lib
# Specifies the name of the NDK library that you want CMake to locate.
z
)
target_link_libraries( # Specifies the target library.
f-native-lib
# Links the target library to the log library included in the NDK.
${log-lib}
${z-lib}
)
Activity:
static {
System.loadLibrary("f-native-lib");
}
Had the same issue, gradle plugin version 7.0.2 fixed this
Are you using tasks.whenTaskAdded in your gradle files? There is an issue with gradle plugin 7.0.0. Refer this. Use 7.0.2 or greater for the resolution.
I am trying to include my .so library from another directory. Compiling my project works fine. But when I run it, it gives me
java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.company.gimikapp-2/base.apk"],nativeLibraryDirectories=[/data/app/com.company.gimikapp-2/lib/arm, /vendor/lib, /system/lib]]] couldn't find "libtheprebuiltlib.so"
Common solutions I see in SO is this:
sourceSets {
main {
jniLibs.srcDirs = ['src/main/jniLibs']
}
}
Tried both
jniLibs.srcDirs = ['C:\\svn\\sys_libs']
and
jniLibs.srcDirs = ['C:/svn/sys_libs']
How do you actually point it to another directory outside your Android project?
This is my CMakeList.txt:
cmake_minimum_required(VERSION 3.4.1)
add_library( native-lib
SHARED
src/main/cpp/source/native-lib.cpp )
add_library(theprebuiltlib SHARED IMPORTED)
set_target_properties(theprebuiltlib PROPERTIES IMPORTED_LOCATION
C:/svn/sys_libs/libtheprebuiltlib.so)
target_include_directories(
native-lib PRIVATE
src/main/cpp/source/
C:/svn/sys_includes/)
find_library( log-lib
log)
target_link_libraries( native-lib
theprebuiltlib
${log-lib})
And here is my gradle setup for my JNI:
android {
...
defaultConfig {
...
externalNativeBuild {
cmake {
cppFlags "-frtti -fexceptions"
}
ndk {
abiFilters 'armeabi'
}
}
...
}
...
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
sourceSets {
main {
jniLibs.srcDirs = ['C:/svn/sys_libs']
}
}
}
Apparently, you have NDK r17 installed and Android plugin v.3.1.0 or higher (we don't see this in the published fragment of build.gradle).
But you set abiFilters to armeabi, which has been dropped. You should set it to armeabi-v7a, and make sure that libtheprebuiltlib.so is also built for this ABI, or you can download an older version of NDK and in build.gradle dependencies set
classpath 'com.android.tools.build:gradle:3.0.1'
You can force the latest plugin handle armeabi if you set it explicitly:
android {
defaultConfig {
ndk {
abiFilters 'armeabi'
}
}
}
(in your script, it is under android.defaultConfig.externalNativeBuild.ndk, so has no effect).
One mistake in your build.gradle, it should read
android {
sourceSets {
main {
jniLibs.srcDir 'C:/svn/sys_libs'
}
}
}
when you have the file C:\svn\sys_libs\armeabi\libtheprebuiltlib.so. But this does not explain why cmake does not work as expected.
For NDK r16 and Gradle plugin v3.1.2, here is the step by step solution (thanks to #Alex Cohn).
Set android.sourceSets.main.jniLibs.srcDir to point to the location of external .so
android {
sourceSets {
main {
jniLibs.srcDir 'C:/svn/sys_libs'
}
}
}
Make sure that your external .so is located in the above path (step1), appended by correct architecture. In my case, it is armeabi, thus my .so is in
C:/svn/sys_libs/armeabi/libtheprebuiltlib.so
Move ndk from android.defaultConfig.externalNativeBuild.ndk to android.defaultConfig.ndk like so
android {
defaultConfig {
ndk {
abiFilters 'armeabi'
}
}
}
Make sure that CMakeLists.txt points to the complete path with proper architecture.
set_target_properties(theprebuiltlib PROPERTIES IMPORTED_LOCATION
C:/svn/sys_libs/armeabi/libtheprebuiltlib.so)
I'm trying to build ffmpeg static libraries and link them to an android project in order to implement a video player. Goal is to make a player capable of receiving video file from various sources similar to p2p file sharing networks. (target api is 21 level which is 1 level short from supposed official solution with MediaSource)
I managed to compile ffmpeg from this repo (all the code used as-is) but later i got stuck on a linking problem. Whenever I try to compile I get list of ffmpeg methods called in my code and a short eloquent message:
Linker command failed with exit code 1
I have no clue how to pass -v flag to the linker in android studio. Would be great if somebody hinted me that.
I use android studio, build with gradle and cmake.
There's my files:
build.gradle (Project)
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
build.gradle (Module)
apply plugin: 'com.android.application'
android {
compileSdkVersion 25
buildToolsVersion "25.0.3"
productFlavors {
x86 {
ndk {
abiFilter "x86"
}
}
arm {
ndk {
abiFilters "armeabi-v7a"
}
}
armv7 {
ndk {
abiFilters "armeabi-v7a"
}
}
}
defaultConfig {
applicationId "com.example.ledo.ndkapplication"
minSdkVersion 22
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags "-std=c++11 -frtti -fexceptions"
arguments '-DANDROID_PLATFORM=android-16'
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
splits {
abi {
enable true
reset()
include 'x86', 'armeabi-v7a'
universalApk true
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
compile 'com.android.support:design:25.3.1'
compile 'com.writingminds:FFmpegAndroid:0.3.2'
testCompile 'junit:junit:4.12'
}
CMakeLists.txt
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 2.8)
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/native-lib.cpp
src/main/cpp/NativePlayer.h
src/main/cpp/NativePlayer.cpp)
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
find_library(png-lib png)
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
#avcodec
#avfilter
#avformat
#avutil
#swresample
#swscale
#${ANDROID_ABI}
message(${ANDROID_ABI})
set(FFMPEG_ROOT_DIR src/main/libs/ffmpeg/${ANDROID_ABI})
add_library(avcodec STATIC IMPORTED)
add_library(avformat STATIC IMPORTED)
add_library(avfilter STATIC IMPORTED)
add_library(avutil STATIC IMPORTED)
add_library(swresample STATIC IMPORTED)
add_library(swscale STATIC IMPORTED)
#SET_TARGET_PROPERTIES(avcodec PROPERTIES LINKER_LANGUAGE C)
set_target_properties(avcodec PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/${FFMPEG_ROOT_DIR}/lib/libavcodec.a)
#SET_TARGET_PROPERTIES(avfilter PROPERTIES LINKER_LANGUAGE C)
set_target_properties(avfilter PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/${FFMPEG_ROOT_DIR}/lib/libavfilter.a)
#SET_TARGET_PROPERTIES(avformat PROPERTIES LINKER_LANGUAGE C)
set_target_properties(avformat PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/${FFMPEG_ROOT_DIR}/lib/libavformat.a)
#SET_TARGET_PROPERTIES(avutil PROPERTIES LINKER_LANGUAGE C)
set_target_properties(avutil PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/${FFMPEG_ROOT_DIR}/lib/libavutil.a)
#SET_TARGET_PROPERTIES(swresample PROPERTIES LINKER_LANGUAGE C)
set_target_properties(swresample PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/${FFMPEG_ROOT_DIR}/lib/libswresample.a)
#SET_TARGET_PROPERTIES(swscale PROPERTIES LINKER_LANGUAGE C)
set_target_properties(swscale PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/${FFMPEG_ROOT_DIR}/lib/libswscale.a)
include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/${FFMPEG_ROOT_DIR}/include )
include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/${FFMPEG_ROOT_DIR}/lib )
target_link_libraries( # Specifies the target library.
native-lib
# Links the target library to the log library
# included in the NDK.
${log-lib}
avcodec
avformat
#avfilter
#swresample
#swscale
#avutil
GLESv2)
I have .a files in following locations:
%PROJECT_DIR%/app/src/libs/ffmpeg/armeabi-v7a
%PROJECT_DIR%/app/src/libs/ffmpeg/armeabi-v7a-neon
%PROJECT_DIR%/app/src/libs/ffmpeg/x86
It doesn't look to me that linker misses files themselves. (I get different out put if I misplace them.)
The error 'Linker command failed with exit code 1' is usually followed by the more detailed error. To find more details, in Xcode click on the error under Buildtime and choose Reveal in log. This should give you extra hint. Without any specific error, it's not possible to know what's the problem.
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.
When you have
android {
defaultConfig {
ndk {
moduleName "yourlib"
stl "stlport_static"
ldLibs "log", "z", "m"
cFlags "-I/some/include/dir/"
}
...
}
...
}
in your build.gradle then Gradle will compile the files in src/main/jni/ and it will generate an Android.mk in build/ndk/debug/Android.mk.
However, in my case, I'm trying to compile some C++ files compiled against OpenCV.
I have this working when I manually create the Android.mk file and run the ndk-build command. But I want to do it via Gradle / Android Studio automatically.
When doing this manually, I include the libraries to link against. I do this, in the manually created Android.mk, with the line:
include /path/to/the/opencv/directory/sdk/native/jni/OpenCV.mk
However, in Android's Gradle plugin, I am unsure of how to add this 'include' directive in the generated Android.mk file.
Can anyone point me in the right Gradle-directive direction to add this line to the generate file? Thanks.
I've found that that the build process will pull everything in from below the ./src/main/jni folder. So, I've placed symlinks there to include and src folders elsewhere - the src files will be enumerated into the .mk file by the build process and the inc files will be scooped up by the compiler. Perhaps its a bit hacky:
android {
defaultConfig {
ndk {
moduleName "yourlib"
cFlags "-std=c99 -I${project.buildDir}/../src/main/jni/inc"
}
...
}
...
}
I also have different cFlags depending upon debug build. This seems to be valid gradle, but doesn't want to build with android-studio. It will build with the gradlew command tho:
android {
defaultConfig {
ndk {
moduleName "yourlib"
cFlags "-std=c99 -I${project.buildDir}/../src/main/jni/inc"
}
...
}
...
buildTypes {
release {
debuggable false
jniDebugBuild false
ndk {
moduleName "yourlib"
}
}
debug {
debuggable true
jniDebugBuild true
ndk {
moduleName "yourlib"
ldLibs "log"
cFlags "-g -D_DEBUG"
}
}
}
}
I hope it can help you (android-studio 0.8.6).