Nigel McFarlane
2018-03-26 16:52:07 UTC
I'm trying to execute a method from a native C++ library but can't get the
link step to work. Please could someone point me to whatever vital step
I'm missing in the process?
My OS is Windows 10. I'm using Android Studio 3.0.1, Gradle 4.1 and NDK
16.1. I should also note that I currently have very little experience with
these tools.
The ultimate task is to demonstrate to a client that a C++ library we wrote
on top of OpenCV will indeed compile on Android. For now, however, I'm
using a simple standalone C++ library, nmcfMisc, written by myself, as an
example. There are no macro annotations or externs in the code. The
method in question, nmcfMiscUseful::Round(), is trivial and looks this this:
// nmcfMiscUseful.h:
#ifndef __nmcfMiscUseful_H__
#define __nmcfMiscUseful_H__
#include <string>
#include <vector>
#include <ostream>
#include <cmath>
//------------------------------------------------------------------------------
/// Useful misc maths and other methods.
//------------------------------------------------------------------------------
namespace nmcfMiscUseful {
/// Round function which works with negative and positive numbers
int Round(double x);
// nmcfMiscUseful.cpp:
namespace nmcfMiscUseful {
//------------------------------------------------------------------------------
// Round function which works with negative and positive numbers
//------------------------------------------------------------------------------
int Round(double x) {
if (x >= 0.0)
return (int) (x + 0.5);
else
return (int) (x - 0.5);
}
I would normally compile this for Visual Studio using the following
CMakeLists.txt:
PROJECT (nmcfMisc)
# makefile to create standalone library.
# set minimum cmake version
cmake_minimum_required(VERSION 3.4)
# Options
Option(BUILD_SHARED "Build shared library" OFF)
# set top level paths
# find all .cpp and .h files and add to project
file (GLOB CPPFILES *.cpp)
file (GLOB CXXFILES *.cxx)
file (GLOB HFILES *.h)
set (SOURCEFILES ${CPPFILES} ${CXXFILES} ${HFILES})
# create library project
if (${BUILD_SHARED})
set (CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
add_library (${PROJECT_NAME} SHARED ${SOURCEFILES})
else()
add_library (${PROJECT_NAME} STATIC ${SOURCEFILES})
endif()
To create the library I compiled this in Android Studio using the default
"C++ support" project. The only changes I made to the project were in the
default gradle file, replacing the default CMake file with the one above
and adding a -D argument to switch to the shared library build.
build.gradle looks like this:
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
defaultConfig {
applicationId "uk.ac.beds.nmcfmisc"
minSdkVersion 21
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner
"android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags "-frtti -fexceptions"
arguments "-DBUILD_SHARED=ON"
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
}
externalNativeBuild {
cmake {
path
'C:/Users/nmcfarlane/SoftwareProjects/VisualStudio/MyLibraries/nmcfMisc/CMakeLists.txt'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:26.1.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation
'com.android.support.test.espresso:espresso-core:3.0.1'
}
The library compiles with no errors and produces libnmcfMisc.so. Actually
it produces 4 flavours of the library and repeats them in different build
locations. It might be significant that the build/intermediates/jniLibs
directory is empty.
To use the library I created another default C++ support project called
MyCppApp. I created the folder app/src/main/jniLibs and copied the four
flavour directories into it from app/build/intermeidates/cmake/debug/obj.
I added the following to MainActivity:
static {
try {
System.loadLibrary("native-lib");
System.loadLibrary("nmcfMisc");
}
catch (UnsatisfiedLinkError ule) {
android.util.Log.e("LoadJniLib", "ERROR: Could not load native
library: " + ule.getMessage());
}
}
The program runs and appears to find the library.
I added the include path to the default CMakeLists and added the method to
native-lib.cpp:
#include <jni.h>
#include <string>
#include "nmcfMiscUseful.h"
extern "C"
JNIEXPORT jstring JNICALL
Java_uk_ac_beds_mycppapp_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
int x = nmcfMiscUseful::Round(17.9);
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
The #include works, but now the compiler returns a link error:
Error:(12) undefined reference to 'nmcfMiscUseful::Round(double)'
I've tried adding the windows nmcfMisc.lib to the CMake
target_link_libraries() (which is what I would do if I were compiling
native-lib for Visual Studio) but it makes no difference. I also tried
adding the .so library to target_link_libraries() but this crashes the
build.
How do I complete the link step? Do I need to prepare the C++ code for the
native build? Did I build the .so library correctly, and did I choose the
correct .so outputs to copy to jniLibs? Do I need to write an NDK makefile
to complete the linkage? Or is there something else I've missed?
link step to work. Please could someone point me to whatever vital step
I'm missing in the process?
My OS is Windows 10. I'm using Android Studio 3.0.1, Gradle 4.1 and NDK
16.1. I should also note that I currently have very little experience with
these tools.
The ultimate task is to demonstrate to a client that a C++ library we wrote
on top of OpenCV will indeed compile on Android. For now, however, I'm
using a simple standalone C++ library, nmcfMisc, written by myself, as an
example. There are no macro annotations or externs in the code. The
method in question, nmcfMiscUseful::Round(), is trivial and looks this this:
// nmcfMiscUseful.h:
#ifndef __nmcfMiscUseful_H__
#define __nmcfMiscUseful_H__
#include <string>
#include <vector>
#include <ostream>
#include <cmath>
//------------------------------------------------------------------------------
/// Useful misc maths and other methods.
//------------------------------------------------------------------------------
namespace nmcfMiscUseful {
/// Round function which works with negative and positive numbers
int Round(double x);
// nmcfMiscUseful.cpp:
namespace nmcfMiscUseful {
//------------------------------------------------------------------------------
// Round function which works with negative and positive numbers
//------------------------------------------------------------------------------
int Round(double x) {
if (x >= 0.0)
return (int) (x + 0.5);
else
return (int) (x - 0.5);
}
I would normally compile this for Visual Studio using the following
CMakeLists.txt:
PROJECT (nmcfMisc)
# makefile to create standalone library.
# set minimum cmake version
cmake_minimum_required(VERSION 3.4)
# Options
Option(BUILD_SHARED "Build shared library" OFF)
# set top level paths
# find all .cpp and .h files and add to project
file (GLOB CPPFILES *.cpp)
file (GLOB CXXFILES *.cxx)
file (GLOB HFILES *.h)
set (SOURCEFILES ${CPPFILES} ${CXXFILES} ${HFILES})
# create library project
if (${BUILD_SHARED})
set (CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
add_library (${PROJECT_NAME} SHARED ${SOURCEFILES})
else()
add_library (${PROJECT_NAME} STATIC ${SOURCEFILES})
endif()
To create the library I compiled this in Android Studio using the default
"C++ support" project. The only changes I made to the project were in the
default gradle file, replacing the default CMake file with the one above
and adding a -D argument to switch to the shared library build.
build.gradle looks like this:
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
defaultConfig {
applicationId "uk.ac.beds.nmcfmisc"
minSdkVersion 21
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner
"android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags "-frtti -fexceptions"
arguments "-DBUILD_SHARED=ON"
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
}
externalNativeBuild {
cmake {
path
'C:/Users/nmcfarlane/SoftwareProjects/VisualStudio/MyLibraries/nmcfMisc/CMakeLists.txt'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:26.1.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation
'com.android.support.test.espresso:espresso-core:3.0.1'
}
The library compiles with no errors and produces libnmcfMisc.so. Actually
it produces 4 flavours of the library and repeats them in different build
locations. It might be significant that the build/intermediates/jniLibs
directory is empty.
To use the library I created another default C++ support project called
MyCppApp. I created the folder app/src/main/jniLibs and copied the four
flavour directories into it from app/build/intermeidates/cmake/debug/obj.
I added the following to MainActivity:
static {
try {
System.loadLibrary("native-lib");
System.loadLibrary("nmcfMisc");
}
catch (UnsatisfiedLinkError ule) {
android.util.Log.e("LoadJniLib", "ERROR: Could not load native
library: " + ule.getMessage());
}
}
The program runs and appears to find the library.
I added the include path to the default CMakeLists and added the method to
native-lib.cpp:
#include <jni.h>
#include <string>
#include "nmcfMiscUseful.h"
extern "C"
JNIEXPORT jstring JNICALL
Java_uk_ac_beds_mycppapp_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
int x = nmcfMiscUseful::Round(17.9);
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
The #include works, but now the compiler returns a link error:
Error:(12) undefined reference to 'nmcfMiscUseful::Round(double)'
I've tried adding the windows nmcfMisc.lib to the CMake
target_link_libraries() (which is what I would do if I were compiling
native-lib for Visual Studio) but it makes no difference. I also tried
adding the .so library to target_link_libraries() but this crashes the
build.
How do I complete the link step? Do I need to prepare the C++ code for the
native build? Did I build the .so library correctly, and did I choose the
correct .so outputs to copy to jniLibs? Do I need to write an NDK makefile
to complete the linkage? Or is there something else I've missed?
--
You received this message because you are subscribed to the Google Groups "android-ndk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to android-ndk+***@googlegroups.com.
To post to this group, send email to android-***@googlegroups.com.
Visit this group at https://groups.google.com/group/android-ndk.
To view this discussion on the web visit https://groups.google.com/d/msgid/android-ndk/4fbcf72d-a58f-4ffe-9524-1ecfd9c058bc%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
You received this message because you are subscribed to the Google Groups "android-ndk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to android-ndk+***@googlegroups.com.
To post to this group, send email to android-***@googlegroups.com.
Visit this group at https://groups.google.com/group/android-ndk.
To view this discussion on the web visit https://groups.google.com/d/msgid/android-ndk/4fbcf72d-a58f-4ffe-9524-1ecfd9c058bc%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.