Skip to content
This repository has been archived by the owner on Apr 16, 2020. It is now read-only.

Basic Android Toodle app + Rust JNA bindings #30

Open
wants to merge 26 commits into
base: master
Choose a base branch
from

Conversation

grigoryk
Copy link
Collaborator

@grigoryk grigoryk commented Nov 24, 2017

PR state as of 29/12/17 PST:

  • skeleton android app with "list" and "new item" activities
  • android logging support from Rust
  • JNA interface into Rust which allows creating items from Java and registers a callback that listens for new items
  • item sets are passed back to Java and are displayed as a list
  • an attempt to avoid leaking memory at the interface layer

TODO:

  • rebase on top of mentat store branch - no more SQL!
  • expand "ItemJNA" struct to also have due_date and completion_date
  • UI support for modifying these dates
  • label support

@grigoryk grigoryk requested a review from fluffyemily November 24, 2017 11:39
@grigoryk grigoryk changed the title Android app scaffolding + logging from rust Basic Android Toodle app + Rust jni bindings Nov 27, 2017
Copy link
Owner

@fluffyemily fluffyemily left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been trying to compile this but when I come to building the android lib I get the following error:

cargo build --target i686-linux-android --release
   Compiling libsqlite3-sys v0.8.1
error: failed to run custom build command for `libsqlite3-sys v0.8.1`
process didn't exit successfully: `/Users/emilytoop/Development/cross-platform-rust/complex/cargo/target/release/build/libsqlite3-sys-af5f505c0fc46472/build-script-build` (exit code: 101)
--- stdout
TARGET = Some("i686-linux-android")
OPT_LEVEL = Some("3")
TARGET = Some("i686-linux-android")
HOST = Some("x86_64-apple-darwin")
TARGET = Some("i686-linux-android")
TARGET = Some("i686-linux-android")
HOST = Some("x86_64-apple-darwin")
CC_i686-linux-android = None
CC_i686_linux_android = None
TARGET_CC = None
CC = None
TARGET = Some("i686-linux-android")
HOST = Some("x86_64-apple-darwin")
CFLAGS_i686-linux-android = None
CFLAGS_i686_linux_android = None
TARGET_CFLAGS = None
CFLAGS = None
DEBUG = Some("false")
running: "i686-linux-android-gcc" "-O3" "-ffunction-sections" "-fdata-sections" "-fPIC" "-m32" "-DSQLITE_CORE" "-DSQLITE_DEFAULT_FOREIGN_KEYS=1" "-DSQLITE_ENABLE_API_ARMOR" "-DSQLITE_ENABLE_COLUMN_METADATA" "-DSQLITE_ENABLE_DBSTAT_VTAB" "-DSQLITE_ENABLE_FTS3" "-DSQLITE_ENABLE_FTS3_PARENTHESIS" "-DSQLITE_ENABLE_FTS5" "-DSQLITE_ENABLE_JSON1" "-DSQLITE_ENABLE_LOAD_EXTENSION=1" "-DSQLITE_ENABLE_MEMORY_MANAGEMENT" "-DSQLITE_ENABLE_RTREE" "-DSQLITE_ENABLE_STAT2" "-DSQLITE_ENABLE_STAT4" "-DSQLITE_HAVE_ISNAN" "-DSQLITE_SOUNDEX" "-DSQLITE_THREADSAFE=1" "-DSQLITE_USE_URI" "-DHAVE_USLEEP=1" "-Wall" "-Wextra" "-o" "/Users/emilytoop/Development/cross-platform-rust/complex/cargo/target/i686-linux-android/release/build/libsqlite3-sys-9c6be82b1e4f932e/out/sqlite3/sqlite3.o" "-c" "sqlite3/sqlite3.c"

--- stderr
thread 'main' panicked at '
Internal error occurred: Failed to find tool. Is `i686-linux-android-gcc` installed?

I've updated my Android Studio, my NDK and my build tools to try and resolve this, but it looks as if it's a problem with one of our dependencies. Did you encounter this? And if you did, how did you solve it?


#[no_mangle]
pub unsafe extern fn Java_com_mozilla_toodle_RustToodle_newToodle(env: JNIEnv, _: JClass, db_path: JString) -> jlong {
log("newToodle!");
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just pass the args into the existing new_toodle function to save having to replicate code? I think the fewer places we have to make changes the better.


#[no_mangle]
pub unsafe extern fn Java_com_mozilla_toodle_RustToodle_itemSetDueDate(env: JNIEnv, _: JClass, item: *mut Item, due_date: JValue) {
let item = &mut*item;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again, just call item_set_due_date rather than having to do it all again here.

@grigoryk
Copy link
Collaborator Author

@fluffyemily for the build issue, I had to add the NDK/bin directories into my PATH.

I'm not sure why, this isn't what I'd expect to have to do (why doesn't .cargo/config work?). Also, a side-effect is that doing so breaks a regular Firefox build, for me anyway (but that's easily remedied by adjusting your PATH again).

@grigoryk
Copy link
Collaborator Author

@fluffyemily check out a basic JNA integration.

@grigoryk grigoryk dismissed fluffyemily’s stale review November 30, 2017 02:37

It all changed, need another review.

@grigoryk
Copy link
Collaborator Author

This really needs a rebase on top of @fluffyemily's mentat branch ASAP before things diverge too far.

Copy link
Owner

@fluffyemily fluffyemily left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a few concerns about calling unsafe code from inside ListManager.

@@ -24,6 +25,11 @@ pub mod strings {
r_str.to_string()
}

pub fn to_string(pointer: *const c_char) -> String {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is exactly the same as c_char_to_string. Either overwrite the old one or update it to this. No point having 2 functions doing exactly the same thing,

@@ -61,6 +62,16 @@ pub unsafe extern "C" fn item_destroy(item: *mut Item) {
let _ = Box::from_raw(item);
}

// TODO Can these simpler android methods also work for swift?
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, they can. Let's all use the same FFI if possible.

}

pub extern "C" fn a_item_destroy(_: Box<Item>) {
// Rust will clean up for us automatically, since we own the Item.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not the case for iOS so we need to leave a destroy function around for that.

}

#[no_mangle]
pub unsafe extern "C" fn a_item_set_due_date(item: &mut Item, due_date: *const size_t) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is essentially the same code. How about we call a_item_set_due_date after casting the *mut Item pointer to a reference in item_set_due_date.

@@ -34,6 +34,11 @@ pub unsafe extern "C" fn label_destroy(label: *mut Label) {
let _ = Box::from_raw(label);
}

#[no_mangle]
pub unsafe extern "C" fn a_label_destroy(_: Box<Label>) {
// magic!
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are compiler flags for different architectures. #[cfg(target_os="android")] will compile a function just for Android, for example. For functions that have to be different between iOS and Android we should use these flags to ensure not everything is compiled every time and move as much shared code as possible into functions that these architecture specific ones can call into.

use time::Timespec;

#[no_mangle]
pub unsafe extern "C" fn a_list_manager_create_item(manager: *const Arc<ListManager>, name: *const c_char, due_date: *const time_t) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At this point the iOS app is broken already so I don't mind if you make a breaking change, as long as you check with Kit first to make sure he's not relying on it.

@@ -204,6 +207,23 @@ impl ListManager {
}
}

pub fn fetch_item_uuids(&self) -> Vec<*const c_char> {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not a huge fan of c types being used inside ListManager. string_to_c_char calls unsafe code and unsafe code should be kept to the FFI boundary as much as possible which allows this function to be called from elsewhere in Rust without safety worries.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, good call! I'll revise this.

@@ -32,6 +33,15 @@ pub struct Item {
pub labels: Vec<Label>,
}

#[repr(C)]
#[derive(Debug)]
pub struct ItemJNA {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is something I have been considering doing for all objects we pass over the boundary - converting them from complex Rust types to C structs. This would also make handling the objects in Swift much easier.

@@ -224,6 +225,44 @@ impl ListManager {
uuids
}

pub fn fetch_all_items(&self) -> Vec<ItemJNA> {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Take a look at the Into trait. https://doc.rust-lang.org/std/convert/trait.Into.html

This is a trait you can implement on structs to convert one thing into another. Think about implementing Into for Item to ItemJNA and Vec<Item> to Vec<ItemJNA>. That way the ListManager function can deal entirely with safe types and code and you can call list.into() from inside your FFI function when Boxing it for return.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's nice about Into is that implementing Into automatically implements From which means when we pass an ItemJNA back into we can call item.from() to turn it back into an Item.

@grigoryk grigoryk changed the title Basic Android Toodle app + Rust jni bindings Basic Android Toodle app + Rust JNA bindings Nov 30, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants