Android Studio 1.4, experimental plugin, path to JNI folder? - android-ndk

I'm using Android Studio 1.4 and the experimental plugin (com.android.tools.build:gradle-experimental:0.2.0) with NDK support.
The project has one C file, and links the rest of the native code via a static library, like this:
android.ndk {
moduleName = "native"
ldFlags += ["-g"]
ldFlags += ["-L/Users/me/Desktop/MyApp/app/src/main/jni", "-lnative"]
}
This works for me, linking libnative.a from the jni folder.
But I'd like to specify the location of the native library relative to the project.
How exactly do I do that?
Thanks.

android.ndk {
moduleName = "native"
ldFlags += ["-g"]
ldLibs += ["-L", file("src/main/jni").absolutePath, "-lnative"]
}
Note that I use ldLibs, not ldFlags - these are used a bit differently to forge the linker command line.
Actually, if you want to add just one library, the preferred syntax would be
ldflags += file("src/main/jni/libnative.a").absolutePath
(there is no need for the linker to look for other libraries in this "src/main/jni" directory).
file() is relative to the current Module; use File() if you want to specify paths relative to current Project.

Related

msvc & cmake fully link static library with other static libraries (bundle) [duplicate]

I want to bundle five static libraries into one library in CMake. How can I proceed for this?
Like library a, b, c, d, and e should bundle into alpha_lib.
If you are using Visual Studio, you can take advantage of the Microsoft Library Manager (LIB.exe) to combine your static libraries into one. Your CMake could follow these steps:
Use find_program() to have CMake locate the MSVC lib.exe tool on your system. If you run cmake from the Visual Studio Command Prompt, find_program can locate lib.exe automatically, without using the optional PATHS argument to tell it where to look.
Use CMake's add_custom_target() command to call lib.exe using the syntax for merging libraries:
lib.exe /OUT:alpha_lib.lib a.lib b.lib c.lib d.lib e.lib
You can use target-dependent generator expressions in the custom target command to have CMake resolve the locations of your built libraries. The custom target will create a Project in your Visual Studio solution that can be run separately to merge all of the built static libraries into one library.
Your CMake could look something like this:
# Create the static libraries (a, b, c, d, and e)
add_library(a STATIC ${a_SOURCES})
...
add_library(e STATIC ${e_SOURCES})
# Tell CMake to locate the lib.exe tool.
find_program(MSVC_LIB_TOOL lib.exe)
# If the tool was found, create the custom target.
if(MSVC_LIB_TOOL)
add_custom_target(CombineStaticLibraries
COMMAND ${MSVC_LIB_TOOL} /OUT:$<TARGET_FILE_DIR:a>/alpha_lib.lib
$<TARGET_FILE:a>
$<TARGET_FILE:b>
$<TARGET_FILE:c>
$<TARGET_FILE:d>
$<TARGET_FILE:e>
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)
endif()

Android NDK, CMake with other libraries

So I am trying to build and test out a CMake with the Android NDK on Android Studio. I can get my library to compile, but it doesn't seem to want to pull any third-party dependencies over. I've been reading through the toolchain and looking for better documentations, with no luck. Can someone tell me if I am missing?
cmake_minimum_required(VERSION 3.4.1)
set(SFML_PATH ${ANDROID_NDK}/sources/sfml)
set(SFML_LIB_PATH ${SFML_PATH}/lib/${ANDROID_NDK_ABI_NAME})
set(SFML_LIB_SYSTEM ${SFML_LIB_PATH}/libsfml-system.so)
set(SFML_LIB_AUDIO ${SFML_LIB_PATH}/libsfml-audio.so)
set(SFML_LIB_GRAPHICS ${SFML_LIB_PATH}/libsfml-graphics.so)
set(SFML_LIB_NETWORK ${SFML_LIB_PATH}/libsfml-network.so)
set(SFML_LIB_WINDOW ${SFML_LIB_PATH}/libsfml-window.so)
set(SFML_LIB_ACTIVITY ${SFML_LIB_PATH}/libsfml-activity.so)
set(SFML_LIB_MAIN ${SFML_LIB_PATH}/libsfml-main.a)
set(SFML_LIBS ${SFML_LIB_SYSTEM} ${SFML_LIB_GRAPHICS} ${SFML_LIB_AUDIO} ${SFML_LIB_WINDOW} ${SFML_LIB_ACTIVITY})
include_directories(${SFML_PATH}/include)
link_directories(${SFML_LIB_PATH})
add_library(native-lib SHARED
src/main/cpp/native-lib.cpp)
target_link_libraries(native-lib log ${SFML_LIBS})
#file(COPY ${SFML_LIBS} DESTINATION ${__android_install_path})
FOREACH(SFML_LIB ${SFML_LIB})
execute_process( COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${SFML_LIB}" "${LIBRARY_OUTPUT_PATH}/${SFML_LIB}" RESULT_VARIABLE __fileCopyProcess )
MESSAGE("Lib: ${SFML_LIB}")
ENDFOREACH(SFML_LIB)
Above is my CMakeLists.txt. I have done a little hacking to get it to compile with SFML with the paths, as I have not found good documentation with CMake and Android yet.
May you add more info for:
"but it doesn't seem to want to pull any third-party dependencies over."?
this one:
https://github.com/googlesamples/android-ndk/tree/master/hello-libs
has static and shared 3rd party libs, you may try it.
For the shared dependent lib, you will need to pack them into APK, that is done inside gradle, cmake will not do it.
The above example shows that, basically they need to be copied into your app/src/main/jniLibs too so they will be packed into apk, and pushed to your android phone/tablet. At runtime they could be loaded.
I have tried to put a group of libraries into one directory, and use
link_directories(...)
then just put the lib names directly into
target_link_libraries(...)
also works. Make sure you have the right libs for the ABIs you intend to support for your app [looks like you are just building for one ABI].
The process could be little long it will depend on your android skills.
An example could be similar to this process:
Crosscompile sfml.
Create your jni bridge
Generate with cmake the project and compile
Copy your files to android studio. create java loading library code.
I guess that you have crosscompiled sfml and you know how works crosscompiling process, if I am wrong check these link below:
Tutorial:
https://github.com/SFML/SFML/wiki/Tutorial:-Building-SFML-for-Android
Source code:
https://github.com/SFML/SFML
Toolchain:
https://github.com/SFML/SFML/blob/master/cmake/toolchains/android.toolchain.cmake
Changes on your cmake:
add this file
FIND_PACKAGE(SFML required)
In cmake put your SFML build directory and cmake will fills your VARIABLES
automatically for instance this variables:
set(SFML_PATH ${ANDROID_NDK}/sources/sfml)
set(SFML_LIB_PATH ${SFML_PATH}/lib/${ANDROID_NDK_ABI_NAME})
set(SFML_LIB_SYSTEM ${SFML_LIB_PATH}/libsfml-system.so)
set(SFML_LIB_AUDIO ${SFML_LIB_PATH}/libsfml-audio.so)
set(SFML_LIB_GRAPHICS ${SFML_LIB_PATH}/libsfml-graphics.so)
set(SFML_LIB_NETWORK ${SFML_LIB_PATH}/libsfml-network.so)
set(SFML_LIB_WINDOW ${SFML_LIB_PATH}/libsfml-window.so)
set(SFML_LIB_ACTIVITY ${SFML_LIB_PATH}/libsfml-activity.so)
set(SFML_LIB_MAIN ${SFML_LIB_PATH}/libsfml-main.a)
There are two ways to make android studio native apps:
Easy way:
Create JNI bridge:
Crosscompile your cmake script and copy your lib to app/src/main/jniLibs
add library in execution time
code:
try
{
Log.v(LOG_TAG, "adding your library");
System.loadLibrary(your_library);
}
catch(UnsatisfiedLinkError e)
{
Log.e(LOG_TAG,e.getMessage());
}
More complete way (it allows to debug library)
Create your ndk module in gradle
example
android.ndk {
moduleName = "your_library"
cppFlags.add("-fexceptions")
//cppFlags.add("-std=c++11")
//cFlags.add("-fopenmp")
cppFlags.add("-I" + file("src/main/jni").absolutePath)
stl = "gnustl_shared" // Which STL library to use: gnustl or stlport
ldLibs.addAll(["android", "EGL", "GLESv2", "dl", "log", "z"])
String libsDir = curDir.absolutePath + "/src/main/jniLibs/armeabi/"
ldLibs.add(libsDir + "your_native_lib.so")
}

How does one set a C/C++ include path with Gradle Experimental Plugin?

Android Studio cannot find my header files when they are in a location different to the main source folder of my module. This is noticeable by #include "SDL_config.h" statements being highlighted in red (other #include statements are fine).
I have tried modifying the cppFlags values below but I am starting to doubt that these cppFlags are even being passed to the compiler at all.
Has anyone managed to include files from folders other than their main source folder in this way?
Is there a problem with the way I have specified the cppFlags, or perhaps the moduleName or even sources?
I could go through the SDL2 source and modify all of the #include statements to relative #include paths but I don't really want to modify the SDL2 source. I should simply be able to specify a list of include paths somehow.
apply plugin: 'com.android.model.application'
model {
...
android.ndk {
moduleName = 'main'
//W:\hello-sdl-android\android-project\app\src\main\jni\SDL2\include
cppFlags += "-I${file("src/main/jni/SDL2/include")}".toString()
cppFlags += "-I${file("src/main/jni/SDL2/src")}".toString()
}
android.sources {
main.jni {
source {
srcDirs += ['src/main/jni/src']
srcDirs += ['src/main/jni/SDL2/src']
}
}
}
...
}
UPDATE: Here's more information on my current project layout:
app/src/main/jni
app/src/main/jni/src/Main.cpp <- This is compiling
app/src/main/jni/SDL2 <- All SDL2 source is under here
app/src/main/jni/GLM <- All GLM source is under here
This layout is a direct result of using the example project which I was provided here: https://gitlab.com/scannerdarkly/hello-sdl-android
That project will build using ndk-build from the command line - I wanted to take things a step further by building within Android Studio. My project will attempt to draw a triangle on a GLES2 device.
Here is a link to my current project so far:
http://www.mediafire.com/download/92577p7vf123l72/hello-sdl-android.zip
I use a slightly different approach:
cFlags += "-I" + file("src/main/jni/SDL2/include").absolutePath
.. and this works. The reason is probably that the compiler is launched with a different working directory, and absolutePath resolves any ambiguity here.
Yes! I figured it out - SDL2 source files are .C files, so include paths need to be declared using CFlags, not cppFlags.
Here is another style for more header paths using the gradle experimental plugin, example with openssl and some "abc" library:
// compile parameters
// include openssl headers C (if you have C files)
CFlags.add("-isystem${project.rootDir}/external-libraries/openssl/openssl-1.0.2g/include".toString())
// include openssl headers C++ (if you have cpp files)
cppFlags.add("-isystem${project.rootDir}/external-libraries/openssl/openssl-1.0.2g/include".toString())
// include abc headers C (if you have C files)
CFlags.add("-I${project.rootDir}/external-libraries/abc/abc-5.5/include".toString())
// include abc headers C++ (if you have cpp files)
cppFlags.add("-I${project.rootDir}/external-libraries/abc/abc-5.5/include".toString())
// linking parameters
// link libcrypto.so
ldFlags.add("-L${project.rootDir}/external-libraries/openssl/openssl-1.0.2g/lib/armeabi-v7a".toString())
ldLibs.add('crypto')
// link libabc.so
ldFlags.add("-L${project.rootDir}/external-libraries/abc/abc-5.5/lib".toString())
ldLibs.add('abc')
for many header paths :
cFlags = "-I" + file("src/main/jni/path1").absolutePath +
" -I" + file("src/main/jni/path2").absolutePath +
" -I" + file("src/main/jni/path3").absolutePath

How to configure NDK with Android Gradle plugin 0.7

The new Android gradle plugin (0.7) seems to include new support for the NDK, but in the documentation there is little to no mention of it (the only reference I found is a test called ndkSanAngeles).
It looks like gradle is looking for the NDK, which I have included in my PATH. However, building the project fails with
What went wrong:
Execution failed for task ':OGLTests:compileDefaultFlavorDebugNdk'.
NDK not configured
How can I configure the NDK in gradle?
My current build.gradle looks like this:
task nativeLibsToJar(type: Zip, description: 'create a jar with native libs') {
destinationDir file("$buildDir/native-libs")
baseName 'native-libs'
extension 'jar'
from fileTree(dir: 'src/main/libs', include: '**/*.so')
from fileTree(dir: 'src/main/libs', include: '**/gdb*')
into 'lib/'
}
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn nativeLibsToJar
}
dependencies {
compile fileTree(dir: "$buildDir/native-libs", include: '*.jar')
}
android {
compileSdkVersion 19
buildToolsVersion '19.0.0'
defaultConfig {
minSdkVersion 14
targetSdkVersion 19
versionCode 1
versionName "0.1"
}
buildTypes {
release {
runProguard false
}
debug {
// jniDebugBuild true
runProguard false
debuggable true
}
}
productFlavors {
defaultFlavor {
proguardFile 'proguard-rules.txt'
}
}
}
Thanks.
Looking through the gradle plugin code, I found the following that helped me use both NDK and prebuilt native libraries:
To simply Link in Prebuilt Native Libraries, just add an ndk section to your task. For instance, I added it below in productFlavors. The abiFilter is the folder name the libs are stored in. abiFilters means both libs from the comma separated list will be added to your final APK (so you could theoretically have "armeabi", "armeabi-v7a", "x86", and "mips" all in one APK, and the O/S would choose the supported architecture lib on install):
productFlavors {
arm {
ndk {
abiFilters "armeabi", "armeabi-v7a"
}
}
x86 {
ndk {
abiFilter "x86"
}
}
}
In this example, the arm build will create an APK with the V5 and V7A arm libs in it, and the x86 build will create an APK with just x86 libs in it. This will search for the native libraries in your project jniLibs directory. The jniLibs directory should be structures as the old jni directory, i.e.:
[project]/[app]/src/main/jniLibs/armeabi/libmyNative.so
[project]/[app]/src/main/jniLibs/armeabi-v7a/libmyNative.so
[project]/[app]/src/main/jniLibs/x86/libmyNative.so
Then you can load it in Java as follows:
static
{
loadLibrary("myNative");
}
Now, let's say one native library depends on another one. You MUST (if setting your min API to API 17 or lower) load the dependent libraries first:
static
{
loadLibrary("myDependency");
loadLibrary("myNative");
}
You can also place the ndk {} section in your defaultConfig or a buildType (such as debug or release or whatever else you may use). For example:
buildTypes {
debug {
ndk {
abiFilters "armeabi", "armeabi-v7a"
}
}
}
By prebuilt, I mean 3rd party libs you downloaded or a library you built using the NDK toolchain or your own ARM toolchain (not the ndk-build script itself).
In API 18, they fixed a long standing architectural issue that prevented the native lib loader from "automatically" loading dependencies because it didn't know about the application's lib directory (security reasons, etc). In API 18 and above, if myNative depends on myDependency above, you can just call loadLibrary("myNative") and the OS will handle loading myDependency. Don't RELY on this though, until the market penetration of devices running API 17 and below is at a low number acceptable to you.
To explicitly Build NDK Libraries From Source in the current version of Android Studio, you may do the following:
Set the ndk.dir value in your local.properties to point to NDK home as mentioned previously. Does anyone know if you can use env vars directly in local.properties? :)
In your build.gradle file, add something like this to your task (again, can be defaultConfig, debug, release, a productFlavor, etc):
ndk {
moduleName "myNDKModule"
stl "stlport_shared"
ldLibs "log", "z", "m"
cFlags "-I/some/include/path"
}
This is the basic structure with currently supported types (moduleName, stl, ldLibs, and cFlags). I looked and did not find more than this. There is an issue I believe with ldLibs because it automatically adds "-l" to the front of each field above. You can trick it though (I had to) by saying:
ldLibs "log -lz -lm -Wl,-whole-archive -l/path/to/someOtherLib -Wl,-no-whole-archive"
In this line, you're just tagging on to the end of the first parameter to add parameters that don't start with -l, so you can get by for now. In the case above, I'm linking a whole static library into my NDK module for use from within Java. I've asked the google developer to add additional features to allow this or even ability to merge your own Android.mk file into the NDK build process, but as this is all new, it may be a while.
Currently, whatever you put in build.gradle deletes the temp build directory and recreates it every time, so unless you want to download and modify the gradle android plugin source code (which would be fun), there are some "make due"'s like this required to get your stuff copied into the build. The android gradle script that provides this ndk support in essence generates an Android.mk file and builds using the NDK system in a temporary directory.
Sidetracked for a sec. The moduleName should match a c or cpp file in your project under the jni directory like:
[project]/[app]/src/main/jni/myNDKModule.cpp
The stl value should be set to a value of "stlport_shared" or "stlport_static" if you want to use the stlport libraries for C++. You can leave stl out if you don't need extended C++ support. Remember Android provides very basic C++ support by default. For other supported C++ libraries, view your NDK documentation guidelines in the NDK you downloaded. Note that by setting it to stlport_shared here, gradle copies the libstlport_shared.so lib from your NDK's sources/cxx-stl/stlport/libs directory to your APK's lib directories. It also handles the include path in the compiler (technically the gradle doesn't do all of this, but the Android NDK build system). So don't put your own copy of stlport into your jniLibs directory.
Lastly, I think cFlags is pretty obvious.
You can not set ANDROID_NDK_HOME on the Mac OSX (see below), but from some research I've done appears maybe this still works on other OSs. It will be removed though.
I wanted to comment but don't have the reputation yet. Dennis, the environment variables are ignored completely, not just overridden. In fact, you don't get any of your environment variables. From what I can tell, the Android Studio IDE creates its own environment with just a few specific environment variables (check System.getenv() and print it out from a gradle script).
I wrote this up as a bug here because using env vars builds fine from cmd line:
https://code.google.com/p/android/issues/detail?id=65213
but as you can see, Google decided they didn't want environment variables being used by the IDE at all; I'm still on the fence on that decision. It makes my life painful to have to update local.properties to point to absolute paths that can be loaded in my gradle scripts, which I still haven't figured out how to do yet (but haven't looked that hard). That means I either force my team members to use the same path as me, play with links, make them all type them in every time they pull the repo, or add an automation script. I believe it's a bad decision that will cost time for any developers that rely on env vars which may be small at the micro level but huge at the macro level.
groundloop, I believe the IDE will be updated soon with the ability to add the NDK folder path to your project, and it will auto generate the local.properties file from that (at least it wouldn't make sense if they had not thought of this).
For more detailed examples from Google, here are the latest examples (search for jni or ndk):
https://docs.google.com/viewer?a=v&pid=sites&srcid=YW5kcm9pZC5jb218dG9vbHN8Z3g6NDYzNTVjMjNmM2YwMjhhNA
cross-platform fat APKs using NDK:
Lastly, there is a drawback to using gradle and not being able to provide your own Android.mk file so that you could only link in 3rd party native libraries from a single architecture to your NDK. Note I said "link in". You can build the NDK Modules (moduleName above) in several architectures with the "abiFilters" command, and they will be placed in your app such that the same APK could be used on multiple architectures. If you need to link in your own 3rd party libs or even have different values for cFlags depending on your architecture, there's not a simple way.
I tried the following and it appeared to work at first, but then I found out it was simply building the NDK by appending everything together from the two ndk sections (or something like that, it did somehow build multiple architecture libraries though):
android {
compileSdkVersion 23
buildToolsVersion '23.0.1'
defaultConfig {
minSdkVersion 14
targetSdkVersion 23
versionCode 28
versionName "3.0"
}
buildTypes {
def commonLibs = " -lfoo -lbar -lwhatever"
def armV7LibsDir = "/whatever/armv7a/libs"
def armX86LibsDir = "/whatever/x86/libs"
def armV7IncDir = "/whatever/armv7a/include"
def x86IncDir = "/whatever/x86/include"
debug {
ndk {
cFlags = "-I" + armV7IncDir
moduleName "myNativeCPPModule"
stl "stlport_shared"
abiFilter "armeabi-v7a"
ldLibs "log -L" + armV7LibsDir + commonLibs
}
ndk {
cFlags = "-I" + armX86IncDir
moduleName "myNativeCPPModule"
stl "stlport_shared"
abiFilter "x86"
ldLibs "log -L" + armX86LibsDir + commonLibs
}
}
}
}
After much grief trying to create a fat binary in a clean manor with gradle and native 3rd party libs, I finally came to the conclusion that Google Play's built-in multi-architecture support for APKs is really the best route to go anyway, so create individual APKs for each architecture.
So I created multiple buildTypes, no product flavors, and added the following code to generate the version code for each type.
// This is somewhat nasty, but we need to put a "2" in front of all ARMEABI-V7A builds, a "3" in front of 64-bit ARM, etc.
// Google Play chooses the best APK based on version code, so if a device supports both X86 and
// ARM, it will choose the X86 APK (preferred because Inky ARM running on an X86 with Houdini ARM Emulator crashes in our case)
android.applicationVariants.all { variant ->
if (variant.buildType.name.equals('release')) {
variant.mergedFlavor.versionCode = 2000 + defaultConfig.versionCode
} else if (variant.buildType.name.equals('debug')) {
variant.mergedFlavor.versionCode = 2000 + defaultConfig.versionCode
} else if (variant.buildType.name.equals('debugArmV8a')) {
variant.mergedFlavor.versionCode = 3000 + defaultConfig.versionCode
} else if (variant.buildType.name.equals('releaseArmV8a')) {
variant.mergedFlavor.versionCode = 3000 + defaultConfig.versionCode
} else if (variant.buildType.name.equals('debugMips')) {
variant.mergedFlavor.versionCode = 5000 + defaultConfig.versionCode
} else if (variant.buildType.name.equals('releaseMips')) {
variant.mergedFlavor.versionCode = 5000 + defaultConfig.versionCode
} else if (variant.buildType.name.equals('debugMips64')) {
variant.mergedFlavor.versionCode = 6000 + defaultConfig.versionCode
} else if (variant.buildType.name.equals('releaseMips64')) {
variant.mergedFlavor.versionCode = 6000 + defaultConfig.versionCode
} else if (variant.buildType.name.equals('debugX86')) {
variant.mergedFlavor.versionCode = 8000 + defaultConfig.versionCode
} else if (variant.buildType.name.equals('releaseX86')) {
variant.mergedFlavor.versionCode = 8000 + defaultConfig.versionCode
} else if (variant.buildType.name.equals('debugX86_64')) {
variant.mergedFlavor.versionCode = 9000 + defaultConfig.versionCode
} else if (variant.buildType.name.equals('releaseX86_64')) {
variant.mergedFlavor.versionCode = 9000 + defaultConfig.versionCode
}
}
Now all you have to do is set the value for versionCode in your defaultConfig object, as you would do normally, and this appends it to the end of the architecture-specific version string, based on build type. The version string remains the same for all builds but varies the code to provide an order of precedence from ARM all the way up to X86_64. It's a bit hackish or hard-coded, but it gets the job done. Note that this provides you with up to 999 versions, so if you need more, multiply the numbers above by 10, not sure what the max value is that you can put in for the version code.
In my case, we have a fairly complex build system. We build CPython for 9 architectures, 3 of which are Android, then build a bunch of our own libraries, and link them all together into a single library for each architecture. We use the ndk command line build tools, automake, and python to build everything, instead of Android.mk files. The final libraries are then linked into a single JNI interface cpp file (called myNativeCPPModule above). One click of the button, and everything is built all at once, very nice Android Studio.
Found the answer. Including ndk.dir=path/to/ndk in the local.properties file did the trick.
Update:
On the latest versions of Android Studio, you can set the value directly in the Project Structure > SDK location.
you can also set ANDROID_NDK_HOME environment variable
As commented before, adding ndk.dir= in local.properties helps. Interestingly I found that local.properties overrides any value set for environment variable ANDROID_NDK_HOME, even if you don't have ndk.dir configured in local.properties. (at least with the gradle android plugin v 0.7.3).
This is confounding as Android Studio can overwrite local.properties, and it doesn't seem to provide a way to configure ndk.dir :(
Android studio suggests to include the path to ndk in local.properties

How to link dynamical library in Qt

i'm developing app using QT Creator and external library - librachive. But i cant link it with my project. I have a library file libarchive.so.13.1.2 in my project folder, and this string in my .pro file:
LIBS += -L$$_PRO_FILE_PWD_ -llibarchive.so.13.1.2
And i have an error :-1: error: cannot find -llibarchive.so.13.1.2. I'd tried different filenames and symlinks. What i'm doing wrong?
Try this
LIBS += -L$$_PRO_FILE_PWD_ -larchive.so.13.1.2

Resources