NDK support deprecated for Android Studio 1.1.0 - android-studio

Currently running Android Studio 1.1.0. Installed NDK and added the link to the build.gradle file. Building the project gives a trace with the following text.
WARNING [Project: :app] Current NDK support is deprecated. Alternative will be provided in the future.
android-ndk-r10d\ndk-build.cmd'' finished with non-zero exit value 1
Is NDK r10d unsupported by Android Studio?

The current NDK support is still working for simple projects (ie. C/C++ sources with no dependency on other NDK prebuilt libraries), including when using the latest r10d NDK.
But it's really limited, and as the warning says, it's deprecated, yes.
What I recommend to do is to simply deactivate it, and make gradle call ndk-build directly. This way you can keep your classic Android.mk/Application.mk configuration files, and calling ndk-build from your project will still work the same as with an eclipse project:
import org.apache.tools.ant.taskdefs.condition.Os
...
android {
...
sourceSets.main {
jniLibs.srcDir 'src/main/libs' //set .so files location to libs instead of jniLibs
jni.srcDirs = [] //disable automatic ndk-build call
}
// add a task that calls regular ndk-build(.cmd) script from app directory
task ndkBuild(type: Exec) {
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
commandLine 'ndk-build.cmd', '-C', file('src/main').absolutePath
} else {
commandLine 'ndk-build', '-C', file('src/main').absolutePath
}
}
// add this task as a dependency of Java compilation
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn ndkBuild
}
}

I use the method below to construct the ndk-build absolute path:
def getNdkBuildExecutablePath() {
File ndkDir = android.ndkDirectory
if (ndkDir == null) {
throw new Exception('NDK directory is not configured.')
}
def isWindows = System.properties['os.name'].toLowerCase().contains('windows')
def ndkBuildFile = new File(ndkDir, isWindows ? 'ndk-build.cmd' : 'ndk-build')
if (!ndkBuildFile.exists()) {
throw new Exception(
"ndk-build executable not found: $ndkBuildFile.absolutePath")
}
ndkBuildFile.absolutePath
}
Used as:
commandLine getNdkBuildExecutablePath(), '-C', ...

Now Android Studio 1.3 at Canary channel fully supports NDK. Try it. Reference: http://tools.android.com/download/studio/canary/latest

Related

How to remove Kotlin Support from Flutter App?

Created a new Flutter project with Kotlin support on Android Studio and it is giving the following error on running the app:-
What went wrong: Execution failed for task ':app:compileDebugKotlin'.
Now I just want to remove the Kotlin support from my project, please suggest the steps?
According to your question, there might be many reasons behind...
Check out this Stack Overflow Solution if this can work.
Apart from this, To remove Kotlin support you can check your Gradle for some lines below..
buildscript {
ext.kotlin_version = '1.2.71'
...
dependencies {
...
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
...
apply plugin: 'kotlin-android'
...
android{
...
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
...
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
...
}
Delete all of these above to remove kotlin support in your project.

How to reduce build variants of native library in Android Studio 3.3?

In Android Studio 3.2.1 I had two build variants visible for my native library; Release and Debug. In 3.3 I get a combination of these and all ABIs. I don't want to build multiple APKs for all the ABIs.
The relevant parts of the native library project:
apply plugin: 'com.android.library'
android {
defaultConfig {
ndk {
// Specifies the ABI configurations of your native
// libraries Gradle should build and package with your APK.
abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
}
}
buildTypes {
release {
}
debug {
}
}
externalNativeBuild {
ndkBuild {
path 'jni/Android.mk'
}
}
sourceSets {
main {
java.srcDir generatedSrcDir
}
}
sourceSets {
main {
jni.srcDirs = []
}
}
}
Build warnings
Maybe unrelated, but I observed this warning in the Build log (actually twice in a row):
WARNING: ABIs [arm64-v8a,armeabi-v7a,armeabi] set by
'android.injected.build.abi' gradle flag contained 'ARMEABI' not
targeted by this project.
How do I get back to the old behavior?
Apparently this is an intentional change: see this issue.
According to the linked ticket, this is only "cosmetic"(ie a UI change); a full/fat .apk is still built.

Android studio 2.1 editor(CLint) unable to find headers of modules that use native(ndk) plugin

It looks like the AndroidStudio 2.1 editor highlights the header file from a native module red as though it is missing. It seems to build and run with no issues though. Anyone facing the same issue? (See attached images below)
Also the editor suggests that I include Addition.h using the long relative path. Adding the header file location with cppFlags does not seem to fix this issue.
app build.gradle
apply plugin: 'com.android.model.application'
model {
android {
.......
ndk{
moduleName = 'native'
}
sources {
main {
jni {
dependencies {
project ':Addition' linkage 'static'
}
}
}
}
}
}
Addition module build.gradle
apply plugin: 'com.android.model.native'
model {
android {
........
ndk {
moduleName = 'addition'
}
sources {
main {
jni {
exportedHeaders{
srcDir "src"
}
source {
srcDir "src"
}
}
}
}
}
}
Steps to Reproduce: https://github.com/noridge/StudioClintBug
Studio Build: 2.1.1
Version of Gradle Plugin: experimental 0.7.0 and 2.1.0
Version of Gradle: 2.10
OS: MacOSX El Capitan 10.11.4
Filed a bug with Google. They seem to have fixed it in 2.2
https://code.google.com/p/android/issues/detail?id=209865

Automatically copy .so files from NDK library project?

Using Android Studio 1.2.1.1 with Gradle 1.2.3. I have an NDK library project A that produces a number of .so files in its own libs folder. Though, I cannot see the reference to this folder anywhere in the build.gradle file.
When I reference library A from my app MyApp I would expect the .so files to be copied to MyApp/src/main/jniLibs, but they aren't. Do I have to manually copy these files every time I make a change in the A library?
How can I automate this step?
I edited mine to look like below, which picks it up from where ndk-build.cmd places them.
sourceSets {
main.jniLibs.srcDirs += 'libs/'
}
I does not know if ther is a way to have you .so files copied as you want whiout wirtting anything.
But you could have gradle doing it for you.
In your gradle file, add a task that copy those .so files where you need them to be.
android {
...
sourceSets.main {
jni.srcDirs = []
jniLibs.srcDir 'src/main/libs'
}
//copying files
task copyNativeLib(type: Exec, description: 'Copy native file') {
commandLine 'cp <path_of_so_files> src/main/libs'
}
//add dependencies
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn copyNativeLib
}
}
Or maybe just
android {
...
sourceSets.main {
jni.srcDirs = []
jniLibs.srcDir '<path_to_your_so_files>'
}
}

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

Resources