Reading through the README on the SuperPowered SDK GitHub page, I notice that for having it working on Android you need to:
Create the jni folder inside the project's folder: app/src/main/jni
Copy the contents of the following files from one of the example
projects: gradle/wrapper/gradle-wrapper.properties, local.properties,
build.gradle, app/build.gradle, app/src/main/jni/CMakeLists.txt Open
build.gradle (Module: app), and change the applicationId
I've done all of that, still when I'm trying to sync my project with gradle, I get the following error message:
Error:(34, 0) Could not find method arguments() for arguments [-DANDROID_PLATFORM=android-16, -DANDROID_TOOLCHAIN=clang, -DANDROID_ARM_NEON=TRUE, -DANDROID_STL=gnustl_static, -DPATH_TO_SUPERPOWERED:STRING=null] on object of type com.android.build.gradle.internal.dsl.CmakeOptions.
Open File
This is how my app/build.gradle looks like:
apply plugin: 'com.android.application'
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
def superpowered_sdk_path = properties.getProperty('superpowered.dir')
android {
signingConfigs {
dancam_dev_key {
keyAlias ######
keyPassword ######
storeFile file('/Users/daniele/Desktop/Chords/#####')
storePassword ########
}
}
compileSdkVersion 26
buildToolsVersion "26.0.1"
defaultConfig {
applicationId #####
minSdkVersion 21
targetSdkVersion 26
versionCode 17
versionName "2.1"
signingConfig #######
multiDexEnabled true
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' // these platforms cover 99% percent of all Android devices
}
}
externalNativeBuild {
cmake {
arguments '-DANDROID_PLATFORM=android-16', '-DANDROID_TOOLCHAIN=clang', '-DANDROID_ARM_NEON=TRUE', '-DANDROID_STL=gnustl_static', "-DPATH_TO_SUPERPOWERED:STRING=${superpowered_sdk_path}"
cFlags '-O3', '-fsigned-char' // full optimization, char data type is signed
cppFlags '-fsigned-char', "-I${superpowered_sdk_path}"
}
}
buildTypes {
release {
shrinkResources true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
productFlavors {
}
dataBinding {
enabled = true
}
}
sourceSets {
main {
jniLibs.srcDirs = ['src/main/jni']
}
}
externalNativeBuild {
cmake {
path 'src/main/jni/CMakeLists.txt'
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
....
}
EDIT:
Here is the cmake.txt file
cmake_minimum_required(VERSION 3.4.1)
set(
PATH_TO_SUPERPOWERED
CACHE STRING ""
)
message(${ANDROID_ABI})
file(GLOB CPP_FILES "*.cpp")
add_library(
SuperpoweredExample
SHARED
${CPP_FILES}
${PATH_TO_SUPERPOWERED}/AndroidIO/SuperpoweredAndroidAudioIO.cpp
)
include_directories(src/main/jni)
include_directories(${PATH_TO_SUPERPOWERED})
target_link_libraries(
SuperpoweredExample
log
android
OpenSLES
${PATH_TO_SUPERPOWERED}/libSuperpoweredAndroid${ANDROID_ABI}.a
)
I have a very similar config (as it inherits the default suggested config) but found some differences.
You provide the cmake config like this:
externalNativeBuild {
cmake {
path 'src/main/jni/CMakeLists.txt'
}
}
but as I understood you saved it as "cmake.txt" which is never linked or found.
Does renaming cmake.txt to CMakeLists.txt help?
Related
Recently i have tried to attach cmake with my existing android studio project. I am searching SO since last four day not yet succeeded. Please don't mark this as duplicate. I am running android studio 2.2.1 with gradle version 2.14.1 in windows 8.1pro.
My build.gradle (app level):
apply plugin: 'com.android.application'
android {
compileSdkVersion 24
buildToolsVersion "25.0.2"
defaultConfig {
applicationId = "com.example.user.myproject"
minSdkVersion 17
targetSdkVersion 24
multiDexEnabled true
versionCode 1
versionName "1.0"
vectorDrawables.useSupportLibrary true
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
arguments '-DANDROID_TOOLCHAIN=clang'
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
// proguardFiles.add(file("proguard-rules.txt"))
}
}
externalNativeBuild {
cmake {
path 'src/main/cpp/CMakeLists.txt'
}
}
productFlavors {
arm7 {
// in the future, ndk.abiFilter might also work
ndk {
abiFilter 'armeabi-v7a'
}
}
arm8 {
ndk {
abiFilters 'arm64-v8a'
}
}
arm {
ndk {
abiFilter 'armeabi'
}
}
x86 {
ndk {
abiFilter 'x86'
}
}
x86_64 {
ndk {
abiFilter 'x86_64'
}
}
mips {
ndk {
abiFilters 'mips', 'mips64'
}
}
universal {
ndk {
abiFilters 'mips', 'mips64', 'x86', 'x86_64'
}
}
}
}
dependencies {
My CMakeLists.txt (path: 'app/src/main/cpp')
cmake_minimum_required(VERSION 3.4.1)
add_library(native-lib
SHARED
src/main/cpp/native-lib.cpp)
I am getting this error in message view:
Error while executing 'C:\Users\User\AppData\Local\Android\Sdk\cmake\3.6.4111459\bin\cmake.exe' with arguments {-HF:\MyProject\app\src\main\cpp -BF:\MyProject\app\.externalNativeBuild\cmake\x86_64Debug\x86_64 -GAndroid Gradle - Ninja -DANDROID_ABI=x86_64 -DANDROID_NDK=C:\Users\User\AppData\Local\Android\Sdk\ndk-bundle -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=F:\MyProject\app\build\intermediates\cmake\x86_64\debug\obj\x86_64 -DCMAKE_BUILD_TYPE=Debug -DCMAKE_MAKE_PROGRAM=C:\Users\User\AppData\Local\Android\Sdk\cmake\3.6.4111459\bin\ninja.exe -DCMAKE_TOOLCHAIN_FILE=C:\Users\User\AppData\Local\Android\Sdk\ndk-bundle\build\cmake\android.toolchain.cmake -DANDROID_NATIVE_API_LEVEL=21 -DANDROID_TOOLCHAIN=clang}
I don't know what's wrong here.
I'm migrating a project from gradle-experimental:0.8.3 to gradle:2.3.3 .
Along with it I need to also start using the new cmake which is proving to not be so straight forward.
A snippet for the experimental gradle plugin that worked:
repositories {
libs(PrebuiltLibraries) {
// Configure one pre-built lib: shared
crypto {
// Inform Android Studio where header file dir for this lib
headers.srcDir "${lib_distribution_root}/openssl/includes"
// Inform Android Studio where lib is -- each ABI should have a lib file
binaries.withType(SharedLibraryBinary) {
sharedLibraryFile = file("${lib_distribution_root}/openssl/lib/armeabi/libcrypto.so")
}
}
openssl {
// Inform Android Studio where header file dir for this lib
headers.srcDir "${lib_distribution_root}/openssl/includes"
// Inform Android Studio where lib is -- each ABI should have a lib file
binaries.withType(SharedLibraryBinary) {
sharedLibraryFile = file("${lib_distribution_root}/openssl/lib/armeabi/libssl.so")
}
}
pjsip {
// Inform Android Studio where header file dir for this lib
headers.srcDir "${lib_distribution_root}/pjsip/includes"
// Inform Android Studio where lib is -- each ABI should have a lib file
binaries.withType(SharedLibraryBinary) {
sharedLibraryFile = file("${lib_distribution_root}/pjsip/lib/armeabi/libpjsua.so")
}
}
}
}
android {
def globalConfiguration = rootProject.ext
compileSdkVersion = globalConfiguration.androidCompileSdkVersion
buildToolsVersion = globalConfiguration.androidBuildToolsVersion
defaultConfig.with {
minSdkVersion.apiLevel = globalConfiguration.androidMinSdkVersion
targetSdkVersion.apiLevel = globalConfiguration.androidTargetSdkVersion
multiDexEnabled = true
testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner"
}
dexOptions.with {
javaMaxHeapSize = "4g"
}
lintOptions.with {
abortOnError = false
}
sources {
main {
jni {
dependencies {
library 'crypto' linkage 'shared'
library 'openssl' linkage 'shared'
library 'pjsip' linkage 'shared'
}
}
jniLibs {
// for shared lib, lib need to be pushed to the target too
// Once libs are copied into app/src/main/jniLibs directory,
// Android Studio will pack them into APK's lib/ directory
// Here we like to avoid another duplication by pointing
// to the files that containing our libs' distribution location
// so the same file is used by compiler at host, also packed
// into APk to be used at Target (phone/tablet)
source {
srcDir "${lib_distribution_root}/openssl/lib/armeabi"
srcDir "${lib_distribution_root}/pjsip/lib/armeabi"
}
}
}
}
ndk {
moduleName = "datanative"
platformVersion = 19
stl = "gnustl_static"
ldLibs.addAll("android", "stdc++", "log", "GLESv2", "EGL", "OpenSLES", "z", "m")
cppFlags.addAll("-std=c++11", "-frtti", "-fexceptions", "-pthread", "-fpic", "-ffunction-sections", "-funwind-tables")
cppFlags.add("-DPJ_AUTOCONF")
abiFilters.add(abiName)
cppFlags.add("-marm")
}
}
}
The current work-in-progress has the gradle config:
android {
def globalConfiguration = rootProject.ext
compileSdkVersion globalConfiguration.androidCompileSdkVersion
buildToolsVersion globalConfiguration.androidBuildToolsVersion
defaultConfig {
minSdkVersion globalConfiguration.androidMinSdkVersion
targetSdkVersion globalConfiguration.androidTargetSdkVersion
multiDexEnabled true
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags "-std=c++11", "-DPJ_AUTOCONF", "-fpic", "-frtti", "-fexceptions", "-pthread", "-ffunction-sections", "-funwind-tables", "-marm"
arguments '-DANDROID_PLATFORM=android-21', '-DANDROID_STL=gnustl_static'
}
}
ndk {
moduleName "datanative"
abiFilters 'armeabi'
}
}
externalNativeBuild {
cmake {
path 'CMakeLists.txt'
}
}
sourceSets {
main {
jniLibs.srcDirs '../distribution/pjsip/lib/armeabi/', '../distribution/openssl/lib/armeabi/', 'src/main/cpp/'
}
}
dexOptions.with {
javaMaxHeapSize = "4g"
}
lintOptions.with {
abortOnError = false
}
}
And the CMakeLists:
cmake_minimum_required(VERSION 3.4.1)
file(GLOB SOURCES
src/main/cpp/*.h
src/main/cpp/*.cpp
)
set(distribution_DIR ${path})
include_directories(src/main/cpp/)
include_directories(../distribution/openssl/includes/)
include_directories(../distribution/pjsip/includes/)
add_library( crypto SHARED IMPORTED )
set_target_properties(crypto PROPERTIES IMPORTED_LOCATION
# Provides the path to the library you want to import.
absolute_path_cause_gradle_has_error_otherwise/distribution/openssl/lib/armeabi/libcrypto.so )
add_library( ssl SHARED IMPORTED )
set_target_properties(ssl PROPERTIES IMPORTED_LOCATION
# Provides the path to the library you want to import.
absolute_path_cause_gradle_has_error_otherwise/distribution/openssl/lib/armeabi/libssl.so )
add_library( pjsip SHARED IMPORTED )
set_target_properties(pjsip PROPERTIES IMPORTED_LOCATION
# Provides the path to the library you want to import.
absolute_path_cause_gradle_has_error_otherwise/distribution/pjsip/lib/armeabi/libpjsua.so )
add_library( data SHARED ${SOURCES} )
target_link_libraries(data crypto ssl pjsip)
The linking error that I'm facing and maybe not the root cause:
/src/main/cpp/sthread.cpp
Error:(73) undefined reference to '__emutls_v._ZN6STrace12pLocalStreamE'
Error:(73) undefined reference to '__emutls_v._ZN6STrace12pLocalStreamE'
/Library/Android/sdk/ndk-bundle/sources/cxx-stl/gnu-libstdc++/4.9/include/bits/basic_string.h
Error:(547) undefined reference to '__emutls_v._ZN6STrace12pLocalStreamE'
Error:(547) undefined reference to '__emutls_v._ZN6STrace12pLocalStreamE'
Any clue will be apreciated.
Thank you!
I am trying to generate a static library using CMAKE and Android Studio(2.3.2). Below is what my CMakeLists.txt looks like. I am unable to generate .a file, however when I change the library to SHARED, CMakeTestModule.so file gets generated when I do "Build->Rebuild Project". Is it required for me to add/set any flag for building STATIC libraries.
cmake_minimum_required(VERSION 3.4.1)
project (CMakeTestProject)
include_directories(
src/main/cpp/
)
add_library(
CMakeTestModule
STATIC
src/main/cpp/CMakeTestModule.cpp
)
add_executable(
CMakeTestModule_test
src/main/cpp/CMakeTestModule_test.cpp
)
target_link_libraries(CMakeTestModule_test CMakeTestModule)
This is what my build.gradle looks like:
apply plugin: 'com.android.library'
android {
compileSdkVersion 16
buildToolsVersion "25.0.0"
defaultConfig {
minSdkVersion 8
targetSdkVersion 8
externalNativeBuild {
cmake {
abiFilters 'armeabi'
}
}
}
externalNativeBuild {
cmake {
path 'CMakeLists.txt'
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
}
}
I fixed this issue by specifying the target in the build.gradle. Something like below. Since apk only uses .so files, we need to mention the targets for static libraries and executables.
apply plugin: 'com.android.library'
android {
compileSdkVersion 16
buildToolsVersion "25.0.0"
defaultConfig {
minSdkVersion 8
targetSdkVersion 8
externalNativeBuild {
cmake {
abiFilters 'armeabi'
}
targets "CMakeTestModule_test", "CMakeTestModule"
}
}
externalNativeBuild {
cmake {
path 'CMakeLists.txt'
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
}
}
I have 4 gradle build files: when I build in android studio I am constantly having the error:
No signature of method:
org.gradle.model.ModelMap.getDefaultProguardFile() is applicable for
argument types: (java.lang.String) values: [proguard-android.txt]
file 1
// 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-experimental:0.9.3'
// 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
}
file 2
apply plugin: 'com.android.model.native'
model {
android {
compileSdkVersion = 25
buildToolsVersion = '25.0.0'
defaultConfig {
minSdkVersion.apiLevel = 17
targetSdkVersion.apiLevel = 25
versionCode = 1
versionName = '1.0'
}
ndk {
moduleName = 'fpextractor'
platformVersion = 17
toolchain = "clang"
stl = 'gnustl_static' //std::mutex not in gnustl_static
cppFlags.add('-std=c++11')
abiFilters.addAll(['armeabi', 'armeabi-v7a', 'x86'])
//abiFilters.addAll(['armeabi', 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64', 'mips', 'mips64']) //this is default
//abiFilters.addAll(['armeabi'])
ldLibs.addAll(['android', 'log', 'atomic', 'z'])
}
}
}
// This is just copy out the header file and built lib into distribution
// directory for clint application to use; it is a small overhead of this sample:
// both lib and app are put inside one project space [save maintenance time]
task(distributeLib, type : Copy) {
// trigger build library
dependsOn assemble
into '../distribution/fpextractor/'
from('src/main/jni/fp_extractor.h') {
into 'include/'
}
from('build/outputs/native/release/lib') {
into 'lib/'
}
}
file 3
apply plugin: 'com.android.model.native'
model {
android {
compileSdkVersion = 25
buildToolsVersion = '25.0.0'
defaultConfig {
minSdkVersion.apiLevel = 17
targetSdkVersion.apiLevel = 25
versionCode = 1
versionName = '1.0'
}
ndk {
moduleName = 'nativeaudio'
platformVersion = 17
toolchain = "clang"
stl = 'gnustl_static' //std::mutex not in gnustl_static
cppFlags.add('-std=c++11')
abiFilters.addAll(['armeabi', 'armeabi-v7a', 'x86'])
//abiFilters.addAll(['armeabi', 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64', 'mips', 'mips64']) //this is default
//abiFilters.addAll(['armeabi-v7a'])
ldLibs.addAll(['android', 'log', 'OpenSLES', 'atomic'])
}
}
}
// This is just copy out the header file and built lib into distribution
// directory for clint application to use; it is a small overhead of this sample:
// both lib and app are put inside one project space [save maintenance time]
task(distributeLib, type : Copy) {
// trigger build library
dependsOn assemble
into '../distribution/nativeaudio/'
from('src/main/jni/buf_manager.h') {
into 'include/'
}
from('src/main/jni/android_debug.h') {
into 'include/'
}
from('src/main/jni/debug_utils.h') {
into 'include/'
}
from('src/main/jni/audio_common.h') {
into 'include/'
}
from('src/main/jni/audio_recorder.h') {
into 'include/'
}
from('build/outputs/native/release/lib') {
into 'lib/'
}
}
file 4
apply plugin: 'com.android.model.application'
// Root of 3rd party lib(s): location could be anywhere on the host system
def lib_distribution_root = '../distribution'
model {
repositories {
libs(PrebuiltLibraries) {
// Configure one pre-built lib: shared
nativeaudio {
// Inform Android Studio where header file dir for this lib
headers.srcDir "${lib_distribution_root}/nativeaudio/include"
// Inform Android Studio where lib is -- each ABI should have a lib file
binaries.withType(SharedLibraryBinary) {
sharedLibraryFile = file("${lib_distribution_root}/nativeaudio/lib/${targetPlatform.getName()}/libnativeaudio.so")
}
}
fpextractor {
// Inform Android Studio where header file dir for this lib
headers.srcDir "${lib_distribution_root}/fpextractor/include"
// Inform Android Studio where lib is -- each ABI should have a lib file
binaries.withType(SharedLibraryBinary) {
sharedLibraryFile = file("${lib_distribution_root}/fpextractor/lib/${targetPlatform.getName()}/libfpextractor.so")
}
}
// Configure another pre-built lib: shared;[change to static after Studio supports]
// static lib generation. USING static lib is supported NOW, for that case,
// simple change:
// SharedLibaryBinary --> StaticLibraryBinary
// sharedLibraryFile --> staticLibraryFile
// *.so --> *.a
//gperf {
// headers.srcDir "${lib_distribution_root}/gperf/include"
// binaries.withType(SharedLibraryBinary) {
// sharedLibraryFile = file("${lib_distribution_root}/gperf/lib/${targetPlatform.getName()}/libgperf.so")
// }
//}
}
}
android {
compileSdkVersion = 25
buildToolsVersion = '25.0.0'
defaultConfig {
applicationId='com.gfk.mediawatchapp'
minSdkVersion.apiLevel = 17
targetSdkVersion.apiLevel = 25
versionCode = 22
versionName = '255.0.4'
// Enabling multidex support.
//multiDexEnabled true
}
ndk {
platformVersion = 17
moduleName = 'mwlib'
toolchain = "clang"
stl = 'gnustl_static'
cppFlags.add('-std=c++11')
ldLibs.addAll(['android', 'log', 'OpenSLES', 'atomic'])
//build a default combined apk including all ABIs.
//abiFilters.addAll(['armeabi-v7a'])
abiFilters.addAll(['armeabi', 'armeabi-v7a', 'x86']) //this is default
}
sources {
main {
jni {
dependencies {
library 'nativeaudio' linkage 'shared'
library 'fpextractor' linkage 'shared'
// if gperf were *.a, change shared --> static
//library 'gperf' linkage 'shared'
}
}
jniLibs {
// for shared lib, lib need to be pushed to the target too
// Once libs are copied into app/src/main/jniLibs directory,
// Android Studio will pack them into APK's lib/ directory
// Here we like to avoid another duplication by pointing
// to the files that containing our libs' distribution location
// so the same file is used by compiler at host, also packed
// into APk to be used at Target (phone/tablet)
source {
srcDir "${lib_distribution_root}/nativeaudio/lib"
srcDir "${lib_distribution_root}/fpextractor/lib"
//srcDir "${lib_distribution_root}/gperf/lib"
}
}
}
}
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles.add(file('proguard-android.txt'))
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
}
dependencies {
println rootProject.getName()
compile fileTree(include: ['*.jar'], dir: 'libs')
compile 'com.android.support:support-v4:25.3.1'
compile 'commons-net:commons-net:3.5'
//compile 'com.android.support:appcompat-v7:23.3.0'
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support:design:25.3.1'
compile 'com.android.support:cardview-v7:25.3.1'
compile 'com.android.support:recyclerview-v7:25.3.1'
compile 'com.google.android.gms:play-services-appindexing:9.8.0'
compile 'com.amazonaws:aws-android-sdk-core:2.4.2'
compile 'com.amazonaws:aws-android-sdk-s3:2.4.2'
compile 'com.amazonaws:aws-android-sdk-ddb:2.4.2'
compile 'com.amazonaws:aws-android-sdk-cognitoidentityprovider:2.4.2'
}
// Unnecessary dependency management:
// Make sure the libs are available when begin compiling application project
// This could be ignored because in real scenario, the pre-built libs are
// already given to us before creating application.
tasks.whenTaskAdded { task ->
if (task.name.contains('compile')) {
task.dependsOn ':nativeaudio:distributeLib'
task.dependsOn ':fpextractor:distributeLib'
}
}
Please: Can anyone help me to understand why I have always the following error:
No signature of method:
org.gradle.model.ModelMap.getDefaultProguardFile() is applicable for
argument types: (java.lang.String) values: [proguard-android.txt]
Gradle experimental does not include getDefaultProguardFile() since it does not have any version of ProGuard config by default.
You can use move the lines from your proguard-android.txt to proguard-rules.pro and then change this line:
proguardFiles.add(file('proguard-android.txt'))
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
into this:
proguardFiles.add(file('proguard-rules.pro'))
For me adding a manifest with
jar {
manifest {
attributes 'Main-Class': com.package.to.main.Class
}
}
helped.
See here: Creating runnable JAR with Gradle
I'm testing out the new Android Studio C/C++ building via CMake through stable gradle (http://tools.android.com/tech-docs/external-c-builds).
In my app, an already rooted device needs to use an ABI-dependent binary that I compile inside Android Studio.
When I try to compile a standard library with
add_library(mylib SHARED mylib.c)
it gets automatically compiled and copied inside the lib/[ABI] folder of the APK (e.g. /lib/armeabi/mylib.so) but if I compile an executable binary with:
add_executable(mybinary mybinary.cpp)
binaries are corectly generated inside the build folder:
app/build/intermediates/cmake/debug/lib/armeabi/mybinary
app/build/intermediates/cmake/debug/lib/x86_64/mybinary
...
but they do not seem to be copied anywhere inside the apk.
Which is the correct way to handle this need? Is a gradle-task the way to go?
build.gradle:
apply plugin: 'com.android.application'
android {
compileSdkVersion 24
buildToolsVersion "24.0.1"
defaultConfig {
applicationId "com.my.app"
minSdkVersion 10
targetSdkVersion 24
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags ""
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild{
cmake{
path "CMakeLists.txt"
}
}
defaultConfig {
externalNativeBuild {
cmake {
targets "
arguments "-DANDROID_TOOLCHAIN=clang", "-DANDROID_PLATFORM=android-21"
cFlags "-DTEST_C_FLAG1", "-DTEST_C_FLAG2"
cppFlags "-DTEST_CPP_FLAG2", "-DTEST_CPP_FLAG2"
abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a'
}
}
}
}
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'
})
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:24.1.1'
compile 'com.android.support:design:24.1.1'
compile 'com.android.support:recyclerview-v7:24.1.1'
compile 'eu.chainfire:libsuperuser:1.0.0.201607041850'
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1)
set(CMAKE_VERBOSE_MAKEFILE on)
add_executable(mybinary ${CMAKE_CURRENT_SOURCE_DIR}/mybinary.cpp)
target_link_libraries( mybinary libcustom)
target_include_directories (mybinary PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
mybinary.cpp
#include <stdlib.h>
#include <string>
#include <iostream>
using namespace std;
int main(int argc, char *argv[]) {
string hello = "Hello from C++";
cout << "Message from native code: " << hello << "\n";
return EXIT_SUCCESS;
}
How the app should interact with mybinary:
import eu.chainfire.libsuperuser.Shell;
...
Shell.SU.run("/path/to/mybinary");
Ok, I've found a solution that seems to by quite comfortable but probably there are more proper ways out there;
CMakeLists.txt is by default placed inside myAppProject/app so I've added this line to CMakeLists.txt:
set(EXECUTABLE_OUTPUT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/src/main/assets/${ANDROID_ABI}")
complete app/CMakeLists.txt:
cmake_minimum_required(VERSION 3.4.1)
set(CMAKE_VERBOSE_MAKEFILE on)
# set binary output folder to Android assets folder
set(EXECUTABLE_OUTPUT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/src/main/assets/${ANDROID_ABI}")
add_subdirectory (src/main/cpp/mylib)
add_subdirectory (src/main/cpp/mybinary)
complete app/src/main/cpp/mybinary/CMakeLists.txt:
add_executable(mybinary ${CMAKE_CURRENT_SOURCE_DIR}/mybinary.cpp)
# mybinary, in this example, has mylib as dependency
target_link_libraries( mybinary mylib)
target_include_directories (mybinary PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
complete app/src/main/cpp/mylib/CMakeLists.txt:
add_library( # Sets the name of the library.
mylib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
# Associated headers in the same location as their source
# file are automatically included.
${CMAKE_CURRENT_SOURCE_DIR}/mylib.cpp )
target_include_directories (mylib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
Doing so, any executable binary is compiled directly into assets folder, inside a subfolder whose name is the target ABI, eg:
assets/armeabi/mybinary
assets/x86_64/mybinary
...
In order to use the proper binary inside the App, the correct binary should be selected:
String abi;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
abi = Build.SUPPORTED_ABIS[0];
} else {
//noinspection deprecation
abi = Build.CPU_ABI;
}
String folder;
if (abi.contains("armeabi-v7a")) {
folder = "armeabi-v7a";
} else if (abi.contains("x86_64")) {
folder = "x86_64";
} else if (abi.contains("x86")) {
folder = "x86";
} else if (abi.contains("armeabi")) {
folder = "armeabi";
}
...
AssetManager assetManager = getAssets();
InputStream in = assetManager.open(folder+"/" + "mybinary");
Then, the binary should be copied away from assets folder with the correct execute permissions:
OutputStream out = context.openFileOutput("mybinary", MODE_PRIVATE);
long size = 0;
int nRead;
while ((nRead = in.read(buff)) != -1) {
out.write(buff, 0, nRead);
size += nRead;
}
out.flush();
Log.d(TAG, "Copy success: " + " + size + " bytes");
File execFile = new File(context.getFilesDir()+"/mybinary");
execFile.setExecutable(true);
That's all!
UPDATE:
gradle.build file:
apply plugin: 'com.android.application'
android {
compileSdkVersion 25
buildToolsVersion "25"
defaultConfig {
applicationId "com.myapp.example"
minSdkVersion 10
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags ""
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
defaultConfig {
externalNativeBuild {
cmake {
targets "mylib", "mybinary"
arguments "-DANDROID_TOOLCHAIN=clang"
cFlags "-DTEST_C_FLAG1", "-DTEST_C_FLAG2"
cppFlags "-DTEST_CPP_FLAG2", "-DTEST_CPP_FLAG2"
abiFilters 'armeabi', 'armeabi-v7a', 'x86', 'x86_64'
}
}
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:25.0.0'
compile 'com.android.support:design:25.0.0'
compile 'com.android.support:recyclerview-v7:25.0.0'
compile 'com.android.support:cardview-v7:25.0.0'
compile 'eu.chainfire:libsuperuser:1.0.0.201607041850'
}
Make the executable files output to where the Android Gradle plugin expects libraries:set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")Fool the plugin into thinking your executable is a shared object:add_executable(i_am_an_executable.so main.c)Check the APK:$ 7z l build/outputs/apk/app-debug.apk lib/ [2:08:56]
Date Time Attr Size Compressed Name
------------------- ----- ------------ ------------ ------------------------
..... 9684 4889 lib/armeabi/i_am_an_executable.so
..... 6048 1710 lib/arm64-v8a/i_am_an_executable.so
..... 9688 4692 lib/armeabi-v7a/i_am_an_executable.so
..... 5484 1715 lib/x86/i_am_an_executable.so
..... 6160 1694 lib/x86_64/i_am_an_executable.so
Access and run your executable; it is located in context.getApplicationInfo().nativeLibraryDir.
The downside to this is that you cannot set android:extractNativeLibs to false — I don't know of any way to access lib/ in an APK from within the app.