I am making an attempt to write an NDK basic app to understand how NDK works. I have a text view and a button in the MainActivity and a library class HelloWorldLib.java which have the static native function helloWorld. I have created the header file copied it and created the ".c" file in the jni folder.
When i am building via ndk-build i am getting the error "no rule to make target" error. I checked lots of posts and answer but nothing worked.
I included a test.c empty file as indicated in the below link and was able to build the project,However, when i ran my app i got the error that " no native implementation found" which is bizzare because i have the implementation.
https://code.google.com/p/android/issues/detail?id=66937
OnClick from where HelloWorldLib is called.
public void onClick(View v) {
// TODO Auto-generated method stub
String inNDK = HelloWorldLib.helloWorld();
tv.setText(inNDK);
}
HelloWorldLib where native is funciton is defined.
public class HelloWorldLib {
public native static String helloWorld();
static{
System.loadLibrary("com.example.androidndk_HelloWorldLib");
}
}
header file created by Javah -jni
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_androidndk_HelloWorldLib */
#ifndef _Included_com_example_androidndk_HelloWorldLib
#define _Included_com_example_androidndk_HelloWorldLib
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_androidndk_HelloWorldLib
* Method: helloWorld
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_androidndk_HelloWorldLib_helloWorld
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif
C file copied from .h and then modified.
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <com_example_androidndk_HelloWorldLib.h>
#include <string.h>
extern "C" {
JNIEXPORT jstring JNICALL Java_com_example_androidndk_HelloWorldLib_helloWorld
(JNIEnv *env, jclass clazz){
return (*env)->NewStringUTF(env, "Hello from JNI ! Compiled with ABI");
}
Andoid.mk file
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := com.example.androidndk_HelloWorldLib.c
LOCAL_MODULE := com.example.androidndk_HelloWorldLib
include $(BUILD_SHARED_LIBRARY)
The error i am getting in the command prompt is shown below:
D:\Users\gabhatia\Desktop\Android SDK\MyWorkspace\AndroidNDK>ndk-build
make.exe: *** No rule to make target `jni/com.example.androidndk_HelloWorldLib.c
', needed by `obj/local/x86/objs/com.example.androidndk_HelloWorldLib/com.exampl
e.androidndk_HelloWorldLib.o'. Stop.
I am not sure where i am getting wrong but any help will be greatly appreciated.
Thanks.
GB.
Here
LOCAL_SRC_FILES := com.example.androidndk_HelloWorldLib.c
LOCAL_MODULE := com.example.androidndk_HelloWorldLib
change to
FILE_LIST := $(wildcard $(LOCAL_PATH)/*.cpp)
LOCAL_SRC_FILES := $(FILE_LIST:$(LOCAL_PATH)/%=%)
Have a try.
Related
What I want to test is the Android NDK JNI called in the following order: C (NDK binary) -> java -> C (shared library). So, I wrote an NDK binary (JNI_src.c) included the shared library (jnitest.so) and ran it on Android. but, at runtime the linker cannot find the shared library jnitest.so used by JNI_src.
08-27 10:29:16.416 1121 1121 F linker : CANNOT LINK EXECUTABLE "/system/bin/JNI_output": library "./obj/local/arm64-v8a/libjnitest.so" not found
The following is the C code and Makefile I wrote (NDK Binary, shared library).
JNI_src.c Code
#include <stdio.h>
#include <android/log.h>
#include <jni.h>
#include <stdlib.h>
extern void shared_func();
int main()
{
printf("hello");
__android_log_print(ANDROID_LOG_INFO, "test", "hello");
// create JNI variable
JNIEnv *env;
JavaVM *jvm;
JavaVMInitArgs vm_args; // env variables to control invocation
JavaVMOption options[4];
jint res;
jstring jstr;
jclass cls;
jmethodID mid;
jobjectArray args;
vm_args.version = JNI_VERSION_1_2;
options[0].optionString = "-Djava.compiler=NONE";
options[1].optionString = "-Djava.class.path=.";
res = JNI_CreateJavaVM(&jvm, (void**) &env, &vm_args);
if(res < 0){
__android_log_print(ANDROID_LOG_INFO, "cnunsrtest", "Can't create Java VM");
fprintf(stderr, "Can't create Java VM\n");
exit(1);
}
cls = (*env)->FindClass(env, "Hello");
if(cls == 0){
__android_log_print(ANDROID_LOG_INFO, "cnunsrtest", "Can't find Hello class");
fprintf(stderr, "Can't find Hello class\n");
exit(1);
}
mid = (*env)->GetStaticMethodID(env, cls, "main", "([Ljava/lang/STring;])V");
if(mid == 0){
__android_log_print(ANDROID_LOG_INFO, "cnunsrtest", "Can't find Hello.main");
fprintf(stderr, "Can't find Hello.main\n");
exit(1);
}
jstr = (*env)->NewStringUTF(env, "Hello, World!");
if(jstr == 0){
__android_log_print(ANDROID_LOG_INFO, "cnunsrtest", "Out of memory1");
fprintf(stderr, "Out of memory1\n");
exit(1);
}
args = (*env)->NewObjectArray(env, 1,
(*env)->FindClass(env, "java/lang/String"), jstr);
if(args == 0){
__android_log_print(ANDROID_LOG_INFO, "cnunsrtest", "Out of memory2");
fprintf(stderr, "Out of memory2\n");
exit(1);
}
(*env)->CallStaticVoidMethod(env, cls, mid, args);
shared_func();
(*jvm)->DestroyJavaVM(jvm);
return 0;
}
jnitest.so (jnitest.c)
#include <jni.h>
#include <stdio.h>
void shared_func()
{
printf("shared func cnunsr");
}
JNIEXPORT void JNICALL java_Hello_jnifunc(JNIEnv *env, jobject obj)
{
printf("Java call cnunsr");
shared_func();
return;
}
Code that built the shared library before make .
aarch64-linux-gnu-gcc -I/home/juno/jdk1.8.0_301/include -I/home/juno/jdk1.8.0_301/include/linux -o libjnitest.so jnitest.c -shared -fPIC
After building the shared library, run the Makefile to get the executable.
Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := jvm
LOCAL_SRC_FILES := $(SYSROOT)/home/juno/jdk1.8.0_301/jre/lib/aarch64/server/libjvm.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := jnitest
LOCAL_SRC_FILES := $(SYSROOT)/home/juno/Desktop/nkd/libjnitest.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := JNI_output
LOCAL_SRC_FILES := JNI_src.c
LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog
LOCAL_SHARED_LIBRARIES := jnitest jvm
include $(BUILD_EXECUTABLE)
I copied the executable file and shared library to Android and ran it. But the linker could not find the shared library. I think the cause is incorrect information about the library in the NDK binary. (./obj/local/arm64-v8a/libjnitest.so)
Therefore, readelf was used. (readelf --all JNI_output)
...
Dynamic section at offset 0xdd0 contains 33 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [./obj/local/arm64-v8a/libjnitest.so]
0x0000000000000001 (NEEDED) Shared library: [libjvm.so]
0x0000000000000001 (NEEDED) Shared library: [liblog.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so]
0x0000000000000001 (NEEDED) Shared library: [libm.so]
0x0000000000000001 (NEEDED) Shared library: [libstdc++.so]
0x0000000000000001 (NEEDED) Shared library: [libdl.so]
0x000000000000001e (FLAGS) BIND_NOW
0x000000006ffffffb (FLAGS_1) Flags: NOW PIE
0x0000000000000015 (DEBUG) 0x0
0x0000000000000007 (RELA) 0x650
...
As expected, the dynamic section of libjvm.so is normal, but the dynamic section of libjnitest.so contains an odd path. What part of the Android.mk needs to be modified? I tried setting LD_LIBRARY_PATH to the path where libjnitest.so exists, but the same error occurs.
Given
void foo() {
int i = 0;
#ifdef MACRO_A
// custom behaviour
#endif
// program code
}
Is it possible to pass #define MACRO_A to clang++ when compiling to allow the 'custom behavour' statements to come into effect? I cannot find documentation which suggests this is possible in clang++, but it IS possible in other compilers (g++).
The command, thanks to pevasquez's help, is
clang++ -D MACRO_A main.cpp -o main
The function
void foo() {
int i = 0;
#ifdef MACRO_A
// custom behaviour
#endif
// program code
}
will compile with the code in the pre-processor directive section being included.
This will be an addition to my makefiles to create a debug friendly make-chain as well and the production version.
I've searched for the error LNK2005 "already defined in .obj" but can't find content related to the specific problem I am facing. Hope someone can help me on this...
I've a header foo.h
// foo.h
#ifndef FOO_H
#define FOO_H
void foo() {
print("foo\n");
}
#endif
and main file... main.cpp
// main.cpp
#include <thread>
#include "foo.h"
int main() {
std::thread t(foo);
t.join();
return 0;
}
Now, it compile without any errors and gives the gives output to the console...
foo
But if I create another file foo.cpp and just include the header foo.h and do nothing else...
// foo.cpp
#include "foo.h"
...I get linker error LNK2005 "void __cdecl foo(void)" (?foo##YAXXZ) already defined in main.obj
Don't know what's going wrong here.?!!
You must place only the prototype of the foo() function in the header file, and the implementation once in the .cpp.
Thus, foo.h must contain:
#pragma once
void foo();
And foo.cpp:
#include "foo.h"
void foo() {
printf("Whatever");
}
I trying to include a shared library through android build system. It named "libmd5b". I already check this library in NDK and it working well. But when I've built android I didn't found my library not in /system/lib and nor in else place. There is my actions step by step:
1) Put sources into ($AndroidSourceFolder)/external/libmd5b/jni
Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_CFLAGS := -fPIC
LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib
LOCAL_MODULE := md5b
LOCAL_SRC_FILES := md5b.cpp md5.cpp # source files
LOCAL_MODULE_TAGS := optional
# C++ inclusions:
LOCAL_STATIC_LIBRARIES += libstlport_static
LOCAL_C_INCLUDES += external/stlport/stlport bionic/ bionic/libstdc++/include
include $(BUILD_SHARED_LIBRARY)
Application.mk
APP_STL:=stlport_static
APP_MODULES := md5b
md5b.cpp
#include <jni.h>
#include <string>
#include <iostream>
#include <fstream>
#include <android/log.h>
#include "md5.h"
using namespace std;
extern "C" {
//Original name changed because we building library system wide visible.
JNIEXPORT jstring JNICALL Md5B
(JNIEnv * env, jobject obj, jstring fpath);
};
JNIEXPORT jstring JNICALL Md5B
(JNIEnv * env, jobject obj, jstring fpath)
{
string strpath = env->GetStringUTFChars(fpath, NULL);
ifstream inFile;
inFile.open(strpath.c_str());
string line;
string strFile;
while (!inFile.eof())
{
getline(inFile, line);
strFile += line;
}
inFile.close();
string md5R = md5(strFile);
char* chmd5R = new char [md5R.length()];
strcpy (chmd5R, md5R.c_str());
return env->NewStringUTF(chmd5R);
}
And there is other library files: md5.cpp and md5.h. This files in pure C++ without any jni preparations. So I think it is no important.
2) Next step is changing ($AndroidSourceFolder)/build/target/product/full.mk to look like this:
PRODUCT_PACKAGES := \
Camera \
libmd5b
$(call inherit-product, $(SRC_TARGET_DIR)/product/full_base_telephony.mk)
$(call inherit-product, $(SRC_TARGET_DIR)/board/generic/device.mk)
# Overrides
PRODUCT_NAME := full
PRODUCT_DEVICE := generic
PRODUCT_BRAND := Android
PRODUCT_MODEL := Full Android on Emulator
3) After all this I launching it to compile:
$source build/envsetup.sh
$lunch full-eng
$make
4) Making libmd5b:
make md5b
<build information>
Install: out/target/product/generic/system/lib/md5b.so
That's it. After end of 'make' I can't found my library. It should be in /system/lib but it is not there. So where is my water library? And why I hasn't got any errors while compile it?
Solved.
There was error in Android.mk file in section named LOCAL_MODULE. Module should be named with lib* preffix:
lib<module name>
I´m trying to add a signal to my qthread, but I get an some errors:
error: undefined reference to `vtable for RFDongleCommunication'
error: undefined reference to `RFDongleCommunication::newLogger(unsigned char, unsigned char)'
This is my header file:
#ifndef RFDONGLECOMMUNICATION_H
#define RFDONGLECOMMUNICATION_H
#include <QThread>
#include "qextserialport.h"
#include <QtGui>
class RFDongleCommunication: public QThread
{
Q_OBJECT
public:
explicit RFDongleCommunication(QextSerialPort * port);
QextSerialPort * rfport;
QByteArray data;
signals:
void newLogger(uchar,uchar);
private:
void run();
};
#endif // RFDONGLECOMMUNICATION_H
And the cpp file
#include "rfdonglecommunication.h"
#include "QDebug"
RFDongleCommunication::RFDongleCommunication(QextSerialPort * port)
{
rfport=port;
}
void RFDongleCommunication::run()
{
while(!(rfport->bytesAvailable()));
data = rfport->readAll();
uchar id = data[1];
uchar type = data[2];
emit newLogger(id,type);
}
Does anybody see what I´m doing wrong?
Make shure your class is in a different .cpp and .h file that are included in the MOC process generation
Click on: File - New File or Project - Files and classes - C++ - New
class
undefined reference to `vtable means that the moc cpp file is not generated.
I see that this is a very old post, but it seems people still keep asking very similar or even exactly the same question. I would elaborate the answer given by Rudolfs Bundulis above a little, and hope it would be helpful.
In case you are using Qt Creator and when you compiled your project for the first time, you did not put "Q_OBJECT" in your header file, then the moc cpp file for your (qthread) cpp file was not generated. In this case, simply running "Clean All" and "Rebuild All" after putting "Q_OBJECT" in your header file will not work. You need to go to your build folder to manually delete the Qt generated "Makefile" and run "Rebuild All" or "Build All" again, your error message will be gone.