3

I've got a shared library file, faceblaster-engine.so, compiled for arm-linux-androideabi, placed in the jniLibs folder for Android Studio. I've also got a simple cpp file in the jni folder.

My library is written in Rust, so I have no header files, and I'd like to call functions inside of it through the cpp file, but can't seem to get the library to link correctly. To test, I've made a simple function:

Rust

#[no_mangle]
pub extern fn rust_test() -> c_int {
    82 as c_int
}

C++

extern "C" {

// Test for calling rust function
int rust_test();

jint
Java_com_fureality_faceblaster_MainActivity_testRustLaunch(JNIEnv* env, jobject thiz)
{
    return rust_test();
}

} // End extern

Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := faceblaster-engine
LOCAL_SRC_FILES := ../jniLibs/$(TARGET_ARCH_ABI)/libfaceblaster-engine.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE    := faceblaster
LOCAL_SRC_FILES := gl-tests.cpp
LOCAL_SHARED_LIBRARIES := faceblaster-engine
include $(BUILD_SHARED_LIBRARY)

Error

/home/nathan/Development/projects/faceblaster-android/app/src/main/jni/gl-tests.cpp

Error:(23) undefined reference to `rust_test'
Error:error: ld returned 1 exit status
make: *** [/home/nathan/Development/projects/faceblaster-android/app/build/intermediates/ndk/debug/obj/local/arm64-v8a/libfaceblaster.so] Error 1
Error:Execution failed for task ':app:compileDebugNdk'.
> com.android.ide.common.internal.LoggedErrorException: Failed to run command:
    /home/nathan/Development/bin/android-ndk-r10d/ndk-build NDK_PROJECT_PATH=null APP_BUILD_SCRIPT=/home/nathan/Development/projects/faceblaster-android/app/build/intermediates/ndk/debug/Android.mk APP_PLATFORM=android-21 NDK_OUT=/home/nathan/Development/projects/faceblaster-android/app/build/intermediates/ndk/debug/obj NDK_LIBS_OUT=/home/nathan/Development/projects/faceblaster-android/app/build/intermediates/ndk/debug/lib APP_ABI=all
  Error Code:
    2
  Output:
    /home/nathan/Development/projects/faceblaster-android/app/build/intermediates/ndk/debug/obj/local/arm64-v8a/objs/faceblaster//home/nathan/Development/projects/faceblaster-android/app/src/main/jni/gl-tests.o: In function `Java_com_fureality_faceblaster_MainActivity_testRustLaunch':
    /home/nathan/Development/projects/faceblaster-android/app/src/main/jni/gl-tests.cpp:23: undefined reference to `rust_test'
    collect2: error: ld returned 1 exit status
    make: *** [/home/nathan/Development/projects/faceblaster-android/app/build/intermediates/ndk/debug/obj/local/arm64-v8a/libfaceblaster.so] Error 1

I dunno if my makefile isn't being picked up, or if it is correct? Anyone have an idea on how to properly link against this .so file and register functions I'd like to use?

Thanks in advance for any help!

Edit - Added Java source and rust code compilation method


Java

public class MainActivity extends Activity {

    // External libraries to load
    static {
        System.loadLibrary("faceblaster-engine");
        System.loadLibrary("faceblaster");
    }

    // External functions to register
    public native int testRustLaunch();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // Other stuff omitted for brevity
        Log.d(TAG, "Testing call...");
        int test = testRustLaunch();
        Log.d(TAG, "Received: " + test);
    }
}

Rust Compilation

cargo build --target=arm-linux-androideabi

# /project/.cargo/config file
[target.arm-linux-androideabi]
linker = "/opt/ndk_standalone/bin/arm-linux-androideabi-gcc"

# Cargo.toml
[lib]
name = "faceblaster-engine"
crate_type = ["dylib"]

Edit 2


I've edited by build.gradle script, and my I know my Android.mk is being read and used now, but I am still getting the same compilation error :(

Edit 3


Turns out both of the answers below helped in solving the issue. It was mainly in part of Android Studio not picking up my makefile, the rust code not being declared properly as #[no_mangle] pub extern and my makefile being all jacked up.

nathansizemore
  • 3,028
  • 7
  • 39
  • 63
  • It would probably be good to indicate how you are compiling the Rust code to a (shared?) library. – Shepmaster Jan 27 '15 at 01:45
  • @Shepmaster Thanks for pointing that out, compilation notes have been added :) – nathansizemore Jan 28 '15 at 01:48
  • At which stage does your error occur? Can you tell if it's when compiling the C++ shim? Is it at runtime? Some other point? Is there a way to get verbose output during the compilation / linking phase? – Shepmaster Jan 28 '15 at 02:02
  • @Shepmaster When it's compiling the C++ shim. I can't get it to launch. I'm starting to lean towards Android Studio not picking up my makefile, because I typed a bunch of garbage, and it produced the same result... I'll make an edit and post the entire compilation errors – nathansizemore Jan 28 '15 at 02:09
  • That's great you got it to work! You may want to give [the Rust wiki](https://github.com/rust-lang/rust/wiki/Doc-building-for-android) a once-over to see if there's anything you should update for future searchers. There's also [another SO question](http://stackoverflow.com/q/22200621/155423) that could be useful to others. – Shepmaster Jan 28 '15 at 15:23

2 Answers2

4

Give this a shot:

#[no_mangle]
pub extern fn rust_test() -> i32 {
    82 // Note simplified implementation
}

The specific thing to try is #[no_mangle] and pub. pub will mark the function as being callable from outside the compiled library. #[no_mangle] instructs the compiler to not change the function name, so that the exported symbol will be the literal rust_test.

I also took the liberty of making the actual method body more idiomatic.

Another note is that you should match your Rust and C types more closely. If you want to use an int in C, you should use the Rust type c_int. C's int is allowed to change size depending on your platform! You could also use a int32 in Rust, but then you should use something like int32_t in C.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
2

Your Android.mk doesn't specify that the JNI wrapper actually should try to link to faceblaster-engine - the LOCAL_SHARED_LIBRARIES line instead says it should link to itself. Change it to LOCAL_SHARED_LIBRARIES := faceblaster-engine and it should hopefully work better.

Then to actually load it at runtime, you need to load the libraries in reverse dependency order, i.e.:

System.loadLibrary("faceblaster-engine");
System.loadLibrary("faceblaster");
mstorsjo
  • 12,983
  • 2
  • 39
  • 62
  • Thanks for pointing that out, but changing that did not solve it either. I am beginning to think Android Studio is not even reading the makefile, because I just typed a bunch of garbage into it, and the result was the same... :( – nathansizemore Jan 28 '15 at 01:53
  • I see you did another edit saying that it actually uses `Android.mk` now. You shouldn't have `LOCAL_SHARED_LIBRARIES := $(TARGET_ARCH_ABI)/libfaceblaster-engine.so`, you should have literally `LOCAL_SHARED_LIBRARIES := faceblaster-engine`, as I wrote in the answer. You should have kept the rest of the declaration of the `faceblaster-engine` prebuilt module (`include $(CLEAR_VARS) LOCAL_MODULE := faceblaster-engine LOCAL_SRC_FILES := $(TARGET_ARCH_ABI)/libfaceblaster-engine.so include $(PREBUILT_SHARED_LIBRARY)`) as you had earlier. – mstorsjo Jan 28 '15 at 06:50