Pass a variable at build time into externalNativeBuild in Android - android

Is there a way to pass a constant at compile time into the externalNativeBuild gradle enclosure, and to be able to use it in code?
For example:
externalNativeBuild {
cmake {
arguments "-DTEST=" + rootProject.ext.value
}
}
And the to be able to use it somehow like next:
void foo() {
int bla = TEST;
....
}
Note that the above code is not working, as TEST is not recognized by the c++ compiler, which is what I am trying to solve.

The easiest solution will be to use add_definitions:
add_definitions(-DTEST=${TEST})
to your CMakeLists.txt file.
Another one would be to use configuration file
Basing on Native sample app from Android Studio:
Create file: config.h.in in the same folder as default native-lib.cpp with following contents:
#ifndef NATIVEAPP1_CONFIG_H_IN
#define NATIVEAPP1_CONFIG_H_IN
#cmakedefine TEST ${TEST}
#endif //NATIVEAPP1_CONFIG_H_IN
In CMakeLists.txt add:
# This sets cmake variable to the one passed from gradle.
# This TEST variable is available only in cmake but can be read in
# config.h.in which is used by cmake to create config.h
set(TEST ${TEST})
configure_file( config.h.in ${CMAKE_BINARY_DIR}/generated/config.h )
include_directories( ${CMAKE_BINARY_DIR}/generated/ )
(add it for example, above add_library call)
Finally, add #include "config.h" in you cod (ex. in native-lib.cpp) to use TEST macro.
Rebuild

Related

Dart FFI can't load dynamic library on Android

I have a simple C++ function compiled into a dylib file that I'm trying to run on an Android phone. The function is super simple, it just adds to numbers and returns the result. However, I keep getting this error:
Another exception was thrown: Invalid argument(s): Failed to load dynamic library 'libadd.dylib': dlopen failed: library "libadd.dylib" not found .
I'm really not sure what I'm doing wrong. I've done the following steps:
My Dart implementation:
import 'dart:ffi' as ffi;
import 'dart:io' show Platform, Directory;
import 'package:path/path.dart' as path;
typedef C_ADD = ffi.Int Function(
ffi.Int a, ffi.Int b); // FFI signature of C function
typedef ADD = int Function(int a, int b);
void linkAndCallFunction() {
var libraryPath = path.join(Directory.current.path, "libadd.dylib");
final dylib = ffi.DynamicLibrary.open(libraryPath);
final ADD add = dylib.lookup<ffi.NativeFunction<C_ADD>>("add").asFunction();
final result = add(40, 2);
print(result);
}
I've added these to the build.gradle files:
build.gradle:
buildscript{
ext{
ndkVersion = "25.1.8937393"
}
...
and app/build.gradle:
android {
ndkVersion rootProject.ext.ndkVersion
externalNativeBuild {
cmake {
path "../../lib/CMakeLists.txt"
}
}
This is my CMakeLists.txt file:
cmake_minimum_required(VERSION 3.10.2)
project(add LANGUAGES CXX C)
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
add_library(add SHARED ./add.cpp)
and my file structure of the project looks like this:
lib/
- add.cpp
- add.o
- CMakeLists.txt
- libadd.dylib
- main.dart
it also may be worth mentioning that in order to compile add.cpp into a dylib I ran the following commands:
g++ -c add.cpp
ar rvs libadd.dylib add.o
and if you're wondering, add.cpp looks like this:
#define EXPORT extern "C" __attribute__((visibility("default")))
__attribute__((used))
EXPORT
int add(int a, int b){
return a + b;
}
Where is this error coming from? am I compiling to a dylib incorrectly?
The answer to this problem was relatively simple, it just stemmed from my lack of knowledge on how Android actually compiles to a static library. Hopefully it helps someone else who is trying to understand how to setup external C++ code in a flutter program.
The static library is generated automatically and is set up in CMakeLists.txt.
Firstly, I moved all the C++ files into the android folder. Then, I set up CMakeLists.txt like this:
cmake_minimum_required(VERSION 3.10.2)
add_library( add // library name, will create libadd.so
SHARED
add.cpp
)
The problem before was that I was trying to manually compile the file myself, when I should have been letting CMakeLists do it instead. According to the android documentation:
"The convention CMake uses to name the file of your library is as follows:
liblibrary-name.so
For example, if you specify "native-lib" as the name of your shared library in the build script, CMake creates a file named libnative-lib.so. "
https://developer.android.com/studio/projects/configure-cmake
So when I run the program, the static library is created automatically and placed in the correct place. Then, the Dart FFI can find it with the DynamicLibrary.open() function. In this case, CMakeLists will generate a file called libadd.so, and I can callDynamicLibrary.open('libadd.so') and the program works.

How do I Print to Console using dart:ffi in Flutter?

Trying to print to console when running my Flutter app (on Android) from the following C++ code I am running through dart:ffi:
#include <iostream>
std::cout << "Hello, World!";
Does not give me any output in the terminal. How would I print to the Flutter terminal from C++?
I know my functions otherwise work correctly because I am getting correct actions on pointers / return values.
Edit: When only running on android, there is a simpler way. I have updated the answer below.
You'll have to pass a wrapper to the print function into the native code
void wrappedPrint(Pointer<Utf8> arg){
print(Utf8.fromUtf8(arg));
}
typedef _wrappedPrint_C = Void Function(Pointer<Utf8> a);
final wrappedPrintPointer = Pointer.fromFunction<_wrappedPrint_C>(_wrappedPrint_C);
final void Function(Pointer) initialize =
_nativeLibrary
.lookup<NativeFunction<Void Function(Pointer)>>("initialize")
.asFunction<void Function(Pointer)>();
initialize(wrappedPrintPointer);
and then use that in your C library:
void (*print)(char *);
void initialize(void (*printCallback)(char *)) {
print = printCallback;
print("C library initialized");
}
void someOtherFunction() {
print("Hello World");
}
When only running on android, things get simpler. Instead of all of the above, do:
Simply use the android logging mechanism, it will show up on the console, at least when using flutter run. I'm assuming flutter attaches to logcat using the app's PID.
To do so, update the CMakeLists.txt with:
find_library( # Defines the name of the path variable that stores the
# location of the NDK library.
log-lib
# Specifies the name of the NDK library that
# CMake needs to locate.
log )
# Links your native library against one or more other native libraries.
target_link_libraries( # Specifies the target library.
<your-libs-name-here>
# Links the log library to the target library.
${log-lib} )
and in your c lib do:
#include <android/log.h>
void someOtherFunction() {
__android_log_print(ANDROID_LOG_DEBUG, "flutter", "Hello world! You can use %s", "formatting");
}

Configure CMake to add_library base on current Android build type

I store my credential keys inside a cpp native file and use CMake to build then link it to my app:
Current code:
My src/main/cpp/credentials-provider-dev.cpp file:
JNIEXPORT jobject JNICALL
Java_com_{package}_CredentialsProvider_extractApiCredentials(JNIEnv *env, jobject instance) {
jclass cls = env -> FindClass("com/{path}/models/ApiCredentials");
jmethodID methodId = env -> GetMethodID(cls, "<init>",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
return env -> NewObject(cls, methodId,
env->NewStringUTF("key"),
env->NewStringUTF("other key"),
env->NewStringUTF("another key"),
env->NewStringUTF("key again"),
NULL, NULL
);
}
My build.gradle defines the link to CMakeLists.txt file:
externalNativeBuild {
cmake {
path 'src/main/cpp/CMakeLists.txt'
}
}
My src/main/cpp/CMakeLists.txt file:
cmake_minimum_required(VERSION 3.4.1)
add_library(
credentials-provider-dev
SHARED
credentials-provider-dev.cpp)
The credentials-provider-dev file only define my dev environment credentials and this code works fine when I build in Debug Type.
Problem:
I also have staging and release build and I want to use different credentials-provider-{dev/staging/production}.cpp file for each build type:
debug {
ext.alwaysUpdateBuildId = false
applicationIdSuffix ".debug"
}
staging {
initWith debug
debuggable true
}
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules-app.pro'
signingConfig signingConfigs.release
}
Attempts:
I use native code to store these keys because of better key protection. And I don't want to merge all the build keys into 1 file because of repo security. Only the person has the responsibility to access production build have the credentials-provider-production.cpp file and be able to build release. So my teammates can only have the dev file and build debug mode only.
I tried with find_library to check for staging cpp file existence before calling add_library like this but it didn't work, the lib is still not added:
find_file(
STAGING_KEY_LIB
PATHS main/cpp/credentials-provider-staging.cpp)
if (STAGING_KEY_LIB)
add_library(
credentials-provider-staging
SHARED
main/cpp/credentials-provider-staging.cpp)
endif()
I also tried the CMAKE_BUILD_TYPE parameter sent to the CMakeLists.txt like below. But base on the document: The valid values are Release and Debug. I want to have Staging build too so this method didn't work
add_library(
credentials-provider-${CMAKE_BUILD_TYPE}
SHARED
credentials-provider-${CMAKE_BUILD_TYPE}.cpp)
In summary:
Using native code: How can I separate my secret-key files into different build type-based files? Anyone has experience with this please help.
Thanks
This is my current solution:
Project structure:
src
| - CMakeLists.txt
|
| - debug/cpp
| - CMakeLists.txt
| - credentials-debug.cpp
|
| - main/cpp
| - CMakeLists.txt
| - credentials-release.cpp
|
| - staging/cpp
| - CMakeLists.txt
| - credentials-staging.cpp
src/CMakeLists.txt:
cmake_minimum_required(VERSION 3.4.1)
add_subdirectory(debug/cpp)
add_subdirectory(staging/cpp)
add_subdirectory(main/cpp)
src/debug/cpp/CMakeLists.txt:
add_library(
credentials-debug
SHARED
credentials-debug.cpp)
src/main/cpp/CMakeLists.txt: (ignored in .gitignore & will be empty on other machine)
add_library(
credentials-release
SHARED
credentials-release.cpp)
src/staging/cpp/CMakeLists.txt: (ignored in .gitignore & will be empty on other machine)
add_library(
credentials-staging
SHARED
credentials-staging.cpp)
So each child CMakeLists.txt file loads a different cpp file.
On my local machine, I'll have all 3 (CMakeLists + cpp) files.
On other teammates that don't have permission to build staging or release. They don't have the staging + release cpp files and just a placeholder empty CMakeList files
In the Kotlin code: I can extract the debug/staging/release build type to decide to load the right library
Pros:
Change the build type is a 1 step process, just change the build variant from Android Studio and the code automatically pick the right native lib to import
Important credentials files are ignored from repo
Cons:
On my machine, all available cpp files for 3 build types are built from the beginning but only 1 of them is used
When someone pulls the project, they have to manually create empty CMakeLists in main/cpp and staging/cpp folder so the add_subdirectory won't throw an error
I'm using the CMAKE_BUILD_TYPE which is passed by Android to CMake. In my case I wanted to use a specific library version, but this approach can also be used with an CMake IF to define a different flow.
string(TOLOWER ${CMAKE_BUILD_TYPE} BUILD_TYPE)
target_link_libraries(Bar ${PROJECT_SOURCE_DIR}/../../Foo/lib/build/intermediates/library_and_local_jars_jni/${BUILD_TYPE}/jni/arm64-v8a/libFoo.so)

Android NDK CMake uses C++17

The Problem
I'd like to use decomposition declarations in my Android NDK project. Therefore clang needs to be called with -std=c++17. Currently my code compiles without errors, but Clang prints the following warning:
warning: decomposition declarations are a C++17 extension [-Wc++17-extensions]
What I know and what I want
In the build log I spotted that -std=... is appended to the build flags four times:
[...]/bin/clang++ [...] -Wformat -Werror=format-security -std=c++11 -std=c++1z\
-fexceptions -std=c++1z -Wall -O0 -fno-limit-debug-info -fPIC\
-std=gnu++11 -MD -MT [...]
I know where the second and the third flag comes from (see below). I tried to change them to -std=c++17 and -std=c++1z but with no success.
I guess that later flags override earlier ones. So I don't really care about the first one. But I can't figure out where the last one (-std=gnu++11) comes from and how I could deactivate or modify it. Also I guess the fact that it's gnu++11 instead of c++11 activates some GNU extension that leads to the situation that I'm only getting warnings and no errors.
But I can't tell for sure. Nevertheless I want "real" C++17 support and not just some GNU extensions. Also I want to get rid of the warnings.
Files involved in the building process
My gradle.build
The switch cppFlags in here is the origin of the second flag from the above excerpt from the build log. I know that some of the versions here are outdated. But I got that from the NDK sample repository and I'm new to Android programming. I'm still figuring out how things work. And so I don't care about that part yet.
apply plugin: 'com.android.application'
android {
compileSdkVersion = 25
defaultConfig {
applicationId = 'com.example.stackoverflow'
minSdkVersion 14
targetSdkVersion 25
externalNativeBuild {
cmake {
arguments '-DANDROID_STL=c++_static'
cppFlags "-std=c++1z -fexceptions"
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
}
externalNativeBuild {
cmake {
path 'src/main/CMakeLists.txt'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:25.4.0'
implementation 'com.android.support.constraint:constraint-layout:1.0.1'
}
My CMakeLists.txt
Origin of the third flag. cmrc_add_resource_library from assets/CMakeRC.cmake compiles resources. I searched the code and there is nothing related to std=* or CMAKE_*_FLAGS. If you don't believe me have a look at the source yourself.
cmake_minimum_required(VERSION 3.4.1)
include(assets/CMakeRC.cmake)
# Build native_app_glue as a static library
set(${CMAKE_C_FLAGS}, "${CMAKE_C_FLAGS}")
add_library(native_app_glue STATIC
${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c)
# Now build app's shared library
cmrc_add_resource_library(shaderFiles assets/shader/standard.fs assets/shader/standard.vs)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++1z -Wall")
# Export ANativeActivity_onCreate(),
set(CMAKE_SHARED_LINKER_FLAGS
"${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate")
add_library(myappname SHARED
cpp/main.cpp
#cpp files...
target_include_directories(myappname PRIVATE
${ANDROID_NDK}/sources/android/native_app_glue)
# Add library dependencies
target_link_libraries(myappname
shaderFiles
android
native_app_glue
EGL
GLESv2
log)
None of the other files in my project contains code that remotely has to do with build flags. So I guess that's all.
Main Question
Where could that last -std= flag originate from? If the above problem description is not enough to deduce a solution, what further steps could I take in finding out the origin? And maybe I got it all wrong and clang++ already compiles with C++17. But why am I getting these warnings then? And how do I get rid of them?
Update
I searched my local Android SDK folder for std=gnu++11 and tried changing all occurrences consecutively to find out which one is the one used in my build process. It turns out that in cmake/3.6.4111459/share/cmake-3.6/Modules/Compiler/Clang-CXX.cmake the variable CMAKE_CXX11_EXTENSION_COMPILE_OPTION is responsible to the aforementioned compiler flag. My current workaround is setting it to the desired standard in my CMakeLists.txt:
set(CMAKE_CXX11_EXTENSION_COMPILE_OPTION "-std=c++17")
This works. I got rid of all the warnings. However it seems a bit hacky and I still don't have a clue what actually appends this variable to the build command. So I'm not posting this as an answer as I'm still searching for an actual solution. But if anyone has the same problem and is just searching for a quick fix, well, here you go!
The NDK's toolchain file obeys -std settings as it should. Both flags will appear, but they do so in the order that has the user's setting override the toolchains.
It sounds like you have something like target_compile_features(foobar PRIVATE cxx_range_for) or set_property(TARGET foobar PROPERTY CXX_STANDARD 11) somewhere in your build.
Passing arguments to cmake should be done as a list, not as a simple string. I use this in one of my projects to build a native lib:
cmake {
// Passes optional arguments to CMake. Make sure it is a list, not just a string :-) .
arguments "-DANDROID_NATIVE_API_LEVEL=21", "-DANDROID_STL=c++_shared", "-DANDROID=true"
cppFlags "-fexceptions", "-frtti"
...
}
Your build script uses simple strings, not lists.

How to load native library into native android code? (AndroidStudio)

I have a library in C called mylib in the folder
jniLibs/your_architecture/mylib.so
In Java, to load the library, you just have to type that code in your source:
static {
System.loadLibrary("mylib");
}
But how can you load the library in a native C code (in Android Studio) ?
I add that the library don't use the JNI conventions, it's a normal shared library.
If this can help someone, here how to load a library in native code c/c++ :
1 - to avoid java.lang.UnsatisfiedLinkError: dlopen failed: add this to the build.gradle into android block :
sourceSets {
main {
jniLibs.srcDirs = ['src/main/jniLibs']
}
}
Assuming that your lib files are in
src/main/jniLibs/{Architecture}/
( depending where is your jniLibs folder, in my case it is located at app/src/main/ but the most time it is in app/)
2 - In your CMakeList, add you library as SHARED and IMPORTED by adding the following block :
add_library(mylib SHARED
IMPORTED
)
3 - Add target properties to locate you lib mylib.so by adding the following block :
set_target_properties( mylib PROPERTIES
IMPORTED_LOCATION
${PROJECT_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/mylib.so
)
We back forward (/../) here because we concidere that CMakeLists.txt is in
src/main/cpp/
Otherwise, the most time it is in app/ folder, in this case, we don't need to back forward.
So we have to back to main/ before going into jniLibs/
4 - Add you lib mylib to your target link libraries :
target_link_libraries(native-library-jni
..
..
mylib
..
..
)
5 - Finally, to call methods of your lib mylib.so, you have to create/copy-past the header containing these methods signatures and include it in your source file : #include "mylib.h"
You can now call your methods [namespace::]method(args...)
Extra links :
PROJECT_SOURCE_DIR
CMAKE_SOURCE_DIR
Q : Are CMAKE_SOURCE_DIR and PROJECT_SOURCE_DIR the same in CMake?

Categories

Resources