first commit

This commit is contained in:
aOK 2024-08-19 13:52:05 +03:00
commit e2c095af41
34 changed files with 667 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

49
Cargo.lock generated Normal file
View file

@ -0,0 +1,49 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "cc"
version = "1.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72db2f7947ecee9b03b510377e8bb9077afa27176fdbff55c51027e976fdcc48"
dependencies = [
"shlex",
]
[[package]]
name = "libc"
version = "0.2.157"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "374af5f94e54fa97cf75e945cce8a6b201e88a1a07e688b47dfd2a59c66dbd86"
[[package]]
name = "makepad-android-state"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd004cda8be459fd76954218b76a1249a079fb9360bbca4e724cb7ddb2962857"
dependencies = [
"makepad-jni-sys",
]
[[package]]
name = "makepad-jni-sys"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9775cbec5fa0647500c3e5de7c850280a88335d1d2d770e5aa2332b801ba7064"
[[package]]
name = "rust-interop"
version = "0.1.0"
dependencies = [
"cc",
"libc",
"makepad-android-state",
"makepad-jni-sys",
]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"

30
Cargo.toml Normal file
View file

@ -0,0 +1,30 @@
[package]
name = "rust-interop"
version = "0.1.0"
edition = "2021"
[lib]
name = "rust_interop"
path = "src/lib.rs"
crate-type = ["cdylib"]
# [bin]
# name = "_rust_interop"
# path = "examples/interop.rs"
[dependencies]
libc = "0.2.157"
makepad-jni-sys = "0.4.0"
makepad-android-state = "0.1.0"
# makepad-android-state = { path = "../makepad/libs/android_state", version = "0.1.0" }
# makepad-jni-sys = { path = "../makepad/libs/jni-sys", version = "0.4.0" }
[build-dependencies]
cc = "1.1.13"
[build]
build = "build.rs"
[features]
default = ["android-result"]
android-result = []

0
README.md Normal file
View file

99
build.rs Normal file
View file

@ -0,0 +1,99 @@
use std::{path::Path, process::Command};
fn main() {
compile_java();
compile_c();
compile_objc();
compile_swift();
link_libraries();
// Run the Java command after the build
if let Err(e) = run_java_command() {
panic!("Failed to run Java command: {:?}", e);
}
}
fn compile_java() {
// Compile Java code using `javac`
println!("cargo:rerun-if-changed=java/HelloWorld.java");
let java_compile_status = Command::new("javac")
.arg("-d")
.arg("java") // Output directory for .class files
.arg("java/HelloWorld.java")
.status()
.expect("Failed to compile Java code");
if !java_compile_status.success() {
panic!("Java compilation failed");
}
}
fn run_java_command() -> std::io::Result<()> {
let java_class_path = "./java";
let java_library_path = "./target/release";
Command::new("java")
.arg("-cp")
.arg(java_class_path)
.arg("-Djava.library.path")
.arg(java_library_path)
.arg("HelloWorld")
.status()
.map(|_| ())
}
fn compile_c() {
// Compile C code
cc::Build::new()
.file("c/example.c")
.include("c") // Include directory for header files
.compile("example");
}
fn compile_objc() {
// Compile Objective-C code
cc::Build::new()
.file("objc/MyClass.m") // Path to your Objective-C source file
.includes(&["objc"]) // Include necessary directories
.flag("-ObjC") // Objective-C flag for linking all categories and classes
.compile("MyClass"); // Name of the compiled library (without extension)
// Link to the Objective-C runtime
println!("cargo:rustc-link-lib=objc"); // Link against the Objective-C runtime library
// Specify the path to the Objective-C header files
println!("cargo:include=objc"); // Include directory for headers
}
fn compile_swift() {
// Compile Swift code using `swiftc`
println!("cargo:rerun-if-changed=swift/MySwiftClass.swift");
let swift_output_dir = "swift";
let swift_file = "swift/MySwiftClass.swift";
let swift_lib = "libSwiftModule.dylib";
// Ensure swift directory exists
if !Path::new(swift_output_dir).exists() {
std::fs::create_dir_all(swift_output_dir).expect("Failed to create Swift output directory");
}
Command::new("swiftc")
.args(&[
"-emit-library",
"-emit-module",
"-module-name",
"SwiftModule",
"-o",
swift_lib,
swift_file,
])
.current_dir(swift_output_dir)
.status()
.expect("Failed to compile Swift code");
}
fn link_libraries() {
// Link to the compiled C, Objective-C, and Swift libraries
println!("cargo:rustc-link-lib=static=example");
println!("cargo:rustc-link-lib=objc");
println!("cargo:rustc-link-lib=dylib=SwiftModule"); // Link the Swift library
println!("cargo:rustc-link-search=native=swift"); // Search path for Swift library
}

9
c/example.c Normal file
View file

@ -0,0 +1,9 @@
#include "example.h"
int add(int a, int b) {
return a + b;
}
void print_message() {
printf("Hello from C!\n");
}

9
c/example.h Normal file
View file

@ -0,0 +1,9 @@
#ifndef EXAMPLE_H
#define EXAMPLE_H
#include <stdio.h>
int add(int a, int b);
void print_message();
#endif // EXAMPLE_H

8
examples/c.rs Normal file
View file

@ -0,0 +1,8 @@
// examples/c.rs
// sudo cargo run --example c --release
fn main() {
unsafe {
rust_interop::add_and_print(2, 4); // Call the function from rust_interop
}
}

25
examples/java.rs Normal file
View file

@ -0,0 +1,25 @@
use std::process::Command;
fn main() {
// Run the Java command after the build
if let Err(e) = run_java_command() {
panic!("Failed to run Java command: {:?}", e);
}
}
fn run_java_command() -> std::io::Result<()> {
let java_class_path = "./java";
let java_library_path = "./target/release";
let status = Command::new("java")
.arg("-cp")
.arg(java_class_path)
.arg(format!("-Djava.library.path={}", java_library_path))
.arg("HelloWorld")
.status()?;
if !status.success() {
panic!("Java execution failed");
}
Ok(())
}

9
examples/swift.rs Normal file
View file

@ -0,0 +1,9 @@
use rust_interop::swift::swiftFunction;
fn main() {
unsafe {
// let result = rust_interop::swift::swiftFunction();
let result = swiftFunction();
println!("Result from Swift function: {}", result);
}
}

24
extra.txt Normal file
View file

@ -0,0 +1,24 @@
javac -version
sudo cargo build --release
sudo cargo run --example c --release
sudo cargo run --example java --release
sudo cargo run --example swift --release
This will generate a com/example/MyJavaClass.class file.
javac -d . MyJavaClass.java
javac HelloWorld.java
javac -h . java/HelloWorld.java
java -Djava.library.path=./target/release -cp java HelloWorld
javac -h ./java java/HelloWorld.java
cargo build --release
java -cp ./java -Djava.library.path=./target/release HelloWorld
java -verbose:jni -cp ./java -Djava.library.path=./target/release HelloWorld
chmod +x ./target/release/librust_jni_example.dylib
swiftc -emit-library -emit-module -module-name SwiftModule -o libSwiftModule.dylib swift/MySwiftClass.swift

BIN
java/HelloWorld.class Normal file

Binary file not shown.

21
java/HelloWorld.h Normal file
View file

@ -0,0 +1,21 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorld */
#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: HelloWorld
* Method: callRustFunction
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_HelloWorld_callRustFunction
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif

17
java/HelloWorld.java Normal file
View file

@ -0,0 +1,17 @@
// java/HelloWorld.java
public class HelloWorld {
static {
System.loadLibrary("rust_interop");
}
public native void callRustFunction();
public String getGreeting() {
return "Hello from Java!";
}
public static void main(String[] args) {
HelloWorld hw = new HelloWorld();
hw.callRustFunction();
}
}

14
java/Main.java Normal file
View file

@ -0,0 +1,14 @@
// java/Main.java
public class Main {
static {
System.loadLibrary("rust_jni_example");
}
private static native void call_java_from_rust();
public static void main(String[] args) {
System.out.println("Calling Rust function...");
call_java_from_rust();
System.out.println("Rust function call completed.");
}
}

0
javascript/index.js Normal file
View file

20
objc/MyClass.h Normal file
View file

@ -0,0 +1,20 @@
// swift/MyClass.h
#import <Foundation/Foundation.h>
#ifdef __cplusplus
extern "C" {
#endif
// Declare functions to be used by Rust
void create_object();
void call_method();
const char* get_message();
#ifdef __cplusplus
}
#endif
@interface MyClass : NSObject
- (void)printMessage;
- (NSString *)getMessage;
@end

35
objc/MyClass.m Normal file
View file

@ -0,0 +1,35 @@
// swift/MyClass.m
#import "MyClass.h"
@implementation MyClass
- (void)printMessage {
NSLog(@"Hello from Objective-C!");
}
- (NSString *)getMessage {
return @"Hello from Objective-C!";
}
@end
// C-compatible interface functions
static MyClass *myClassInstance = nil;
void create_object() {
myClassInstance = [[MyClass alloc] init];
}
void call_method() {
if (myClassInstance) {
[myClassInstance printMessage];
}
}
const char* get_message() {
if (myClassInstance) {
NSString *message = [myClassInstance getMessage];
return [message UTF8String];
}
return "";
}

6
objc/objc_wrapper.h Normal file
View file

@ -0,0 +1,6 @@
#ifndef OBJC_WRAPPER_H
#define OBJC_WRAPPER_H
void call_objc_function();
#endif // OBJC_WRAPPER_H

7
objc/objc_wrapper.m Normal file
View file

@ -0,0 +1,7 @@
#import "objc_wrapper.h"
#import "MyClass.h"
void call_objc_function() {
MyClass *obj = [[MyClass alloc] init];
[obj helloFromObjectiveC];
}

5
src/c.rs Normal file
View file

@ -0,0 +1,5 @@
// Declare external functions from C
extern "C" {
pub fn add(a: i32, b: i32) -> i32;
pub fn print_message();
}

188
src/java.rs Normal file
View file

@ -0,0 +1,188 @@
use makepad_android_state::get_java_vm;
use makepad_jni_sys::{jint, jobject, JavaVM, _jmethodID, _jobject};
use std::{
ffi::{CStr, CString},
ptr,
};
use makepad_jni_sys as jni_sys;
static mut _ACTIVITY: jobject = ptr::null_mut();
static mut VM: *mut JavaVM = ptr::null_mut();
#[no_mangle]
pub extern "C" fn call_java_from_rust() {
unsafe {
// Get the JavaVM instance
if get_java_vm().is_null() {
eprintln!("Failed to get JavaVM");
return;
}
let env = attach_jni_env();
let class_name = CString::new("HelloWorld").unwrap();
let method_name = CString::new("getGreeting").unwrap();
let method_sig = CString::new("()Ljava/lang/String;").unwrap();
let (obj, method_id) = new_object(env, class_name, method_name, method_sig);
// let method_name = CString::new("getGreeting").unwrap();
// let method_sig = CString::new("()Ljava/lang/String;").unwrap();
// let method_id =
// (**env).GetMethodID.unwrap()(env, class, method_name.as_ptr(), method_sig.as_ptr());
// if method_id.is_null() {
// eprintln!("Failed to find getGreeting method.");
// detach_current_thread();
// return;
// }
// println!("Found getGreeting method.");
let result = (**env).CallObjectMethod.unwrap()(env, obj, method_id);
if result.is_null() {
eprintln!("Failed to call getGreeting method.");
detach_current_thread();
return;
}
println!("Called getGreeting method.");
let result_str_ptr = (**env).GetStringUTFChars.unwrap()(env, result, ptr::null_mut());
if result_str_ptr.is_null() {
eprintln!("Failed to convert Java string to Rust string.");
detach_current_thread();
return;
}
let result_str = CStr::from_ptr(result_str_ptr)
.to_string_lossy()
.into_owned();
println!("Result from Java: {}", result_str);
(**env).ReleaseStringUTFChars.unwrap()(env, result, result_str_ptr);
// Detach the current thread from the JVM
detach_current_thread();
// Note: We're not destroying the JVM here because it's generally not recommended
// to destroy the JVM unless you're absolutely sure you want to terminate the entire Java environment.
// If you do need to destroy the JVM, you can uncomment the following code:
/*
// Destroy the JVM
let destroy_res = (**vm).DestroyJavaVM.unwrap()(vm);
if destroy_res != 0 {
eprintln!("Failed to destroy JVM.");
} else {
println!("Destroyed JVM.");
}
*/
}
}
pub unsafe fn new_object(
env: *mut jni_sys::JNIEnv,
class_name: CString,
method_name: CString,
method_sig: CString,
) -> (*mut _jobject, *mut _jmethodID) {
let find_class = (**env).FindClass.unwrap();
let get_method_id = (**env).GetMethodID.unwrap();
let new_object_here = (**env).NewObject.unwrap();
let class = find_class(env, class_name.as_ptr() as _);
if class.is_null() {
eprintln!("Failed to find {:?} class.", class_name);
detach_current_thread();
// return;
}
println!("Found {:?} class.", class_name);
let method_id = get_method_id(
env,
class,
method_name.as_ptr() as _,
method_sig.as_ptr() as _,
);
if method_id.is_null() {
eprintln!("Failed to find constructor method.");
detach_current_thread();
// return;
}
println!("Found constructor method.");
let obj = new_object_here(env, class, method_id);
if obj.is_null() {
eprintln!("Failed to create {:?} object.", class_name);
detach_current_thread();
// return;
}
println!("Created {:?} object.", class_name);
(obj, method_id)
}
pub unsafe fn detach_current_thread() {
// eprintln!("Failed to find constructor method.");
let detach_res = (**get_java_vm()).DetachCurrentThread.unwrap()(get_java_vm());
if detach_res != 0 {
eprintln!(
"Failed to detach current thread. Error code: {}. Ensure the thread is properly attached and the JVM state is correct.",
detach_res
);
} else {
println!("Detached current thread from JVM.");
}
}
pub unsafe fn attach_jni_env() -> *mut jni_sys::JNIEnv {
let mut env: *mut jni_sys::JNIEnv = std::ptr::null_mut();
let attach_current_thread = (**get_java_vm()).AttachCurrentThread.unwrap();
let res = attach_current_thread(get_java_vm(), &mut env, std::ptr::null_mut());
// assert!(res == 0);
if res != 0 || env.is_null() {
eprintln!(
"Failed to attach current thread or env is null. Error code: {}",
res
);
return env;
}
println!("Attached current thread to JVM.");
env
}
#[no_mangle]
pub extern "C" fn test_attach_detach() {
unsafe {
if get_java_vm().is_null() {
eprintln!("Failed to get JavaVM");
return;
}
let _ = attach_jni_env();
detach_current_thread();
}
}
#[no_mangle]
pub extern "C" fn Java_HelloWorld_callRustFunction(_env: *mut jni_sys::JNIEnv, _obj: jobject) {
// Implementation of your native method
// println!("Rust function callRustFunction is called from Java!");
// Optionally, you can call other Rust functions like call_java_from_rust() here if needed
call_java_from_rust();
// test_attach_detach();
}
#[no_mangle]
pub extern "system" fn JNI_OnLoad(vm: *mut JavaVM, _reserved: *const std::ffi::c_void) -> jint {
unsafe {
VM = vm;
}
jni_sys::JNI_VERSION_1_6
}
#[no_mangle]
pub extern "C" fn JNI_OnUnload(_vm: *mut JavaVM, _reserved: *const std::ffi::c_void) {
// unsafe {
// // Optionally, you can add cleanup code here
// }
}

30
src/lib.rs Normal file
View file

@ -0,0 +1,30 @@
//File lib.rs
// src/lib.rs
// src/java.rs
// src/rust_interop.rs
// src/c.rs
// examples/c.rs
// examples/java.rs
// examples/swift.rs
// src/main.rs
// src/objc.rs
// src/swift
// c/example.h
// c/example.c
// java/HelloWorld.java
// java/HelloWorld.class
// objc/MyClass.h
// objc/MyClass.m
// swift/MySwiftClass.swift
// swift/SwiftModule.h
// swift/module.modulemap
// Cargo.toml
// build.rs
pub mod c;
pub mod java;
pub mod objc;
pub mod rust_interop;
pub mod swift;
pub use rust_interop::add_and_print; // Re-export the function for easier access

7
src/objc.rs Normal file
View file

@ -0,0 +1,7 @@
use std::ffi::c_char;
extern "C" {
pub fn create_object();
pub fn call_method();
pub fn get_message() -> *const c_char;
}

10
src/rust_interop.rs Normal file
View file

@ -0,0 +1,10 @@
// src/rust_interop.rs
pub use crate::c::{add, print_message};
pub fn add_and_print(a: i32, b: i32) {
let result = unsafe { add(a, b) };
println!("The result of adding {} and {} is: {}", a, b, result);
unsafe {
print_message();
}
}

8
src/swift.rs Normal file
View file

@ -0,0 +1,8 @@
use libc::c_int;
// use std::ffi::CString;
// use std::os::raw::c_void;
// use std::ptr;
extern "C" {
pub fn swiftFunction() -> c_int;
}

8
swift/MySwiftClass.swift Normal file
View file

@ -0,0 +1,8 @@
// swift/MySwiftClass.swift
import Foundation
@_silgen_name("swift_function")
func swiftFunction() -> Int32 {
print("Swift function called")
return 42
}

View file

@ -0,0 +1,9 @@
{
"ABIRoot": {
"kind": "Root",
"name": "TopLevel",
"printedName": "TopLevel",
"json_format_version": 8
},
"ConstValues": []
}

15
swift/SwiftModule.h Normal file
View file

@ -0,0 +1,15 @@
// swift/SwiftModule.h
#ifndef SWIFT_MODULE_H
#define SWIFT_MODULE_H
#ifdef __cplusplus
extern "C" {
#endif
int32_t swiftFunction();
#ifdef __cplusplus
}
#endif
#endif // SWIFT_MODULE_H

BIN
swift/SwiftModule.swiftdoc Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
swift/libSwiftModule.dylib Executable file

Binary file not shown.

4
swift/module.modulemap Normal file
View file

@ -0,0 +1,4 @@
module SwiftModule {
header "SwiftModule.h"
export *
}