Gradle NDK to specify an 'include' directive in generated Android.mk - android

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).

Related

CMAKE not obeying flags from gradle for NDK building on Android

I have an Android NDK project which I am trying to build through Gradle+CMake.
build.gradle:
apply plugin: 'com.android.library'
allprojects {
repositories {
jcenter()
google()
}
}
android {
compileSdkVersion 27
defaultConfig {
minSdkVersion 16
targetSdkVersion 27
versionCode 1
versionName "1.0"
externalNativeBuild {
cmake {
cppFlags "-std=c++11"
arguments "-DANDROID_ABI=armeabi-v7a",
"-DANDROID_PLATFORM=android-16",
"-DANDROID_STL=gnustl_static",
"-DANDROID_CPP_FEATURES=rtti exceptions",
"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=libs"
}
}
}
buildTypes {
release {
ndk {
abiFilters "armeabi-v7a"
}
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
ndk {
abiFilters "armeabi-v7a"
}
}
}
externalNativeBuild {
cmake {
path 'CMakeLists.txt'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
}
CMAKE Command Output:
Executable : /Users/ssk/Library/Android/sdk/cmake/3.6.3155560/bin/cmake
arguments :
-H/Users/ssk/MyProject
-B/Users/ssk/MyProject/.externalNativeBuild/cmake/debug/armeabi-v7a
-DANDROID_ABI=armeabi-v7a
-DANDROID_PLATFORM=android-16
------> -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=/Users/ssk/MyProject/build/intermediates/cmake/debug/obj/armeabi-v7a
-DCMAKE_BUILD_TYPE=Debug
-DANDROID_NDK=/Users/ssk/Library/Android/sdk/ndk-bundle
-DCMAKE_CXX_FLAGS=-std=c++11
-DCMAKE_TOOLCHAIN_FILE=/Users/ssk/Library/Android/sdk/ndk-bundle/build/cmake/android.toolchain.cmake
-DCMAKE_MAKE_PROGRAM=/Users/ssk/Library/Android/sdk/cmake/3.6.3155560/bin/ninja
-GAndroid Gradle - Ninja
-DANDROID_STL=gnustl_static
-DANDROID_ABI=armeabi-v7a
-DANDROID_PLATFORM=android-16
-DANDROID_CPP_FEATURES=rtti exceptions
------> -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=libs
jvmArgs :
I am trying to configure the output directory using DCMAKE_LIBRARY_OUTPUT_DIRECTORY, but it's not obeying.
Gradle prefixes a default one before my option as highlighted (------> in cmake command output).
Question:
How should I configure the output directory?
Below snippet will configure your output directory:
For static lib output directory, try below:
# copy out the static lib binary
set_target_properties(${STATIC_LIBRARY_NAME}
PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY "libs/${ANDROID_ABI}")
For shared lib output directory, try below:
# copy out the shared lib binary
set_target_properties(${SHARED_LIBRARY_NAME}
PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "libs/${ANDROID_ABI}")
Explanation:
ANDROID_ABI is a variable defining the Android ABI, e.g. armeabi-v7a
Reference about the CMake variables definition:
Android NDK path variable for "strip" command in CMake build tool chain
Probably you need to set the buildStagingDirectory option. In the next link you will find how to manage paths for CMake in gradle:
CMakeOptions
And as a side note unrelated to the question. In your gradle script seems you are still using gnustl_static which is no longer supported and will be removed in the next NDK versions. You should switch to either c++_static or c++_shared. Take into account that gcc has been deprecated and you should use clang. Next an example:
externalNativeBuild {
cmake {
arguments "-DANDROID_PLATFORM=android-16", "-DANDROID_STL=c++_static", "-DANDROID_TOOLCHAIN=clang"
abiFilters "armeabi-v7a", "arm64-v8a", "x86"
}
}

Add .so (prebuilt) library from another directory to APK

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)

Gradle seems to overrwrite ndk build arguments while adding product flavors

I have an android project where the build.gradle looks like this:
android {
compileSdkVersion 25
buildToolsVersion "25.0.0"
defaultConfig {
applicationId "com.example.test"
minSdkVersion 24
targetSdkVersion 25
versionCode 1
versionName "1.0"
ndk {
// Specifies the ABI configurations of your native
// libraries Gradle should build and package with your APK.
abiFilters 'armeabi-v7a'
}
externalNativeBuild {
ndkBuild {
targets "test_app"
}
}
}
buildTypes {
release {
minifyEnabled false
useProguard false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
productFlavors {
general {
externalNativeBuild {
ndkBuild {
arguments "LOCAL_CFLAGS+=-std=c++11"
arguments "LOCAL_CFLAGS+=-DMODE_GENERAL"
}
}
}
full {
externalNativeBuild {
ndkBuild {
arguments "LOCAL_CFLAGS+=-std=c++11"
arguments "LOCAL_CFLAGS+=-DMODE_FULL"
}
}
}
}
externalNativeBuild {
ndkBuild {
path 'src/main/cpp/Android.mk'
}
}
}
My Android.mk in path 'src/main/cpp/Android.mk' already includes LOCAL_CFLAGS += -std=c++11 and it builds fine if I don't have any product flavors. However, if I do have the product flavors then I have to include arguments "LOCAL_CFLAGS+=-std=c++11" to make it build. I am wondering if there is any way to avoid this as I feel I should only need to include extra args which are not mentioned in the Android.mk.
The root cause of the problematic behavior is not gradle, but that you set LOCAL_CFLAGS (a make variable) on make's command line. The rules of gnu make which runs under the hood of ndk-build, state that this "locks" the variable, and whatever you set in makefile has no effect. Make introduces a special override directive, but it cannot be used in ndk-build for LOCAL_CFLAGS.
You are not supposed to override LOCAL_CFLAGS from outside a local module; you should use APP_CFLAGS in Application.mk or as ndk-build command line arguments.
It is not a good idea to set (whatever)_CFLAGS=-std=c++11 because these flags will be passed both to C++ and C compilers. This setting belongs to LOCAL_CPPFLAGS and her kin.
With gradle plugin, you can use cFlags and cppFlags, as shown in the official doc - better than general-purpose arguments, e.g.:
productFlavors {
full {
externalNativeBuild {
ndkBuild {
cFlags "-DMODE_FULL"
cppFlags "-fexceptions"
}
}
}
}

Generate .so file in Android Studio

I'm aware that there are answers related to this, but they're old and outdated. I got Android Studio 1.3 and already downloaded Android NDK. When I run an app, it crashes and gives findLibrary returned null in Logcat. I figured out this was due to no .so library file (Correct me if I'm wrong). My question is - How do I generate a .so file in Android Studio?
What I have -
Android Studio 1.3 Stable version
Gradle 1.5
Android Experimental Plugin 0.2
Notice - If the library .so file has to be built from Cygwin or CMD, please tell me how to do it.
There are a few steps needed to get the NDK hooked up into Android Studio. Currently, support is marked as experimental and AS is starting to bundle the ability to download the NDK within the IDE. By default, AS uses a generic Android.mk and Application.mk when source and/or libs are placed in the jni or jniLibs folder. The instructions below override those defaults in order to provide more customization ability.
In short, you will need to:
Create the default jni and jniLibs directories for your source and libs.
Tell Android Studio where to find your NDK build chain
Tell gradle how to compile and where to place your libs
Create an Android.mk file to specify building and linking order
Create some source
Create directories
Inside /app/src/main create a jni and jniLibs directory.
Update local.properties
Inside your local.properties file, add a line similar to:
ndk.dir=/home/nathan/development/bin/android-ndk-r10e
Update build.gradle
This refers to the module level, not the application level. This ensures that we have defined the build path in the step above and removes the ability for Android Studio to automatically invoke ndk-build. Use the following example as a guide.
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"
/*
* Other ndk flags configurable here are
* cppFlags += "-fno-rtti"
* cppFlags += "-fno-exceptions"
* ldLibs = ["android", "log"]
* stl = "system"
*/
}
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")
}
}
Android.mk
You will need an Android.mk file inside the /app/src/main/jni directory
LOCAL_PATH := $(call my-dir)
# Builds a dylib out of test.cpp
include $(CLEAR_VARS)
LOCAL_MODULE := test
LOCAL_SRC_FILES := test.cpp
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)
test.cpp
Add some awesome C/C++ source code for your lib. These files will start in /app/src/main/jni and will be compiled and linked as specified in your Android.mk
Example file
#include <jni.h>
#include <android/log.h>
static const char *SOME_TAG = "MyAwesomeTag";
extern "C"
{
void
Java_com_something_something_1android_ClassName_some_fn(JNIEnv *env, jobject obj)
{
__android_log_print(ANDROID_LOG_VERBOSE, SOME_TAG, "Hello from NDK :)");
}
} // End extern
Compile and run.
Over a year later with Android Studio 2.2 and above, you can now get all of this done for you for free just by selecting "Include C++ Support" when creating a new project.
For more information check out: https://developer.android.com/studio/projects/add-native-code.html.

how to specify NDK_TOOLCHAIN_VERSION in gradle file for android ndk build

I am moving my Android project that uses ndk-build to use the gradle build system as described in the examples of the new build tools for android. In this link http://tools.android.com/tech-docs/new-build-system. I am looked at the gradle-samples-0.11 at the bottom of the page for inspiration.
So I managed to configure all of the pieces I need by including the following code in my build.gradle default config section.
ndk {
moduleName "MyModuleName"
ldLibs "log"
cFlags "-std=c++11 -fexceptions"
stl "gnustl_static"
}
I have this line in my Application.mk file in the original project:
NDK_TOOLCHAIN_VERSION := 4.9
It is the last piece I can't configure.
I am using NDK Revision 10. I need this NDK_TOOLCHAIN_VERSION:=4.9 because my build is reporting Error:(25, 11) error: expected nested-name-specifier before 'Integer' and the c++ code at that line looks like this.
using Integer = int16_t;
Does anyone have an idea on how I can solve this, please?
I've been trying to solve this too. But in the ended up by writing custom tasks to make Android Studio use Application.mk and Android.mk just like in eclipse.
My build.gradle looks like this
apply plugin: 'com.android.application'
android {
buildTypes {
release {
runProguard false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
compileSdkVersion 20
buildToolsVersion "20.0.0"
defaultConfig {
minSdkVersion 15
targetSdkVersion 20
versionCode 1
}
packagingOptions {
exclude 'META-INF/DEPENDENCIES'
exclude 'META-INF/LICENSE'
exclude 'META-INF/NOTICE'
}
sourceSets.main {
jniLibs.srcDir 'src/main/libs'
jni.srcDirs = [] //disable automatic ndk-build call
}
}
task buildNative(type: Exec) {
def ndkBuild = null;
def ndkBuildingDir = new File("src/main/jni");
def hasNdk = false;
if (System.getenv("NDK_BUILD_CMD") != null) {
hasNdk = true;
ndkBuild = new File(System.getenv("NDK_BUILD_CMD"))
}
commandLine ndkBuild, "--directory", ndkBuildingDir
doFirst {
if (!hasNdk) {
logger.error('##################')
logger.error("NDK build failed!!")
logger.error('Reason: NDK_BUILD_CMD not set.')
logger.error('##################')
}
assert hasNdk: "NDK_BUILD_CMD not set."
}
}
tasks.withType(JavaCompile) { compileTask -> compileTask.dependsOn buildNative }
task cleanNative(type: Exec) {
def ndkBuild = null;
def ndkBuildingDir = new File("src/main/jni");
def hasNdk = false;
if (System.getenv("NDK_BUILD_CMD") != null) {
hasNdk = true;
ndkBuild = new File(System.getenv("NDK_BUILD_CMD"))
}
commandLine ndkBuild, "--directory", ndkBuildingDir, "clean"
doFirst {
if (!hasNdk) {
logger.error('##################')
logger.error("NDK build failed!!")
logger.error('Reason: NDK_BUILD_CMD not set.')
logger.error('##################')
}
assert hasNdk: "NDK_BUILD_CMD not set."
}
}
clean.dependsOn 'cleanNative'
For this to work, you need to set an environment variable NDK_BUILD_CMD to be set the the exact ndk-build executable.
On Windows, you can just set an environment variable NDK_BUILD_CMD point to your ndk-build.exe
On Mac,the path variables you set in your .bash_profile is not accessible on GUI applications(hence Android Studio will not be able to read them).
So edit your .bash_profile to be something like
GRADLE_HOME={path_to_gradle}
ANDROID_SDK_ROOT={path_to_sdk_dir}
ANDROID_HOME=$ANDROID_SDK_ROOT/platform-tools
ANDROID_NDK_HOME={path_to_ndk}
NDK_BUILD_CMD=$ANDROID_NDK_HOME/ndk-build
export PATH=$GRADLE_HOME/bin:$ANDROID_HOME:$ANDROID_SDK_ROOT/tools:$ANDROID_NDK_HOME:/opt/local/bin:/opt/local/sbin:$PATH
launchctl setenv GRADLE_HOME $GRADLE_HOME
launchctl setenv ANDROID_HOME $ANDROID_HOME
launchctl setenv ANDROID_NDK_HOME $ANDROID_NDK_HOME
launchctl setenv NDK_BUILD_CMD $NDK_BUILD_CMD
The launchctl lines will make your environment variables visible inside your Android Studio.
PS: The .bash_profile is run every time you open the terminal. So for this to work properly with Android Studio, you need to launch the terminal once and then run Android Studio. Otherwise the build will fail saying NDK_BUILD_CMD not set. I haven't found any way to set the values when Mac starts. If someone else can find a way, please feel free to suggest.
I've just had this problem, but my solution was actually in your initial question. The latest version of the NDK uses GCC 4.9 by default (https://developer.android.com/tools/sdk/ndk/index.html). This is only default on a 64bit machine it would seem. So you no longer need to set the NDK_TOOLCHAIN_VERSION property in your build.gradle file. So simply setting:
cFlags "-std=c++11 -fexceptions"
stl "gnustl_static"
in ndk{} Should be enough. Works for me.
although this question was asked a long time ago,
the proper solution as described in (https://developer.android.com/studio/projects/install-ndk):
If you install a specific version of the NDK and want to use it in a module, you specify it using the android.ndkVersion property in the module's build.gradle file, as shown in the following code sample.
android {
ndkVersion "major.minor.build"
}
In the latest version of Android Studio 1.5 and Gradle 2.10 with Experimental Plugins 0.7.0-alpha4, you can specify like this example it uses Clang toolchain with version 3.6..
ndk {
moduleName "MyModuleName"
ldLibs "log"
cFlags "-std=c++11 -fexceptions"
stl "gnustl_static"
toolchain "clang"
toolchainVersion = "3.6"
}
You can refer your NDK directory to know which toolchain and which versions are available.
For NDK r10e, GCC (4.8, 4.9) and Clang (3.5, 3.6) options are available. If you don't specify toolchain it will by default use GCC 4.9.
Refer LINK to know more about app gradle configurations.

Categories

Resources