Skip to content

Commit dd4dc1b

Browse files
committed
Reorganized projects + use relative path for rust logger
1 parent dce535a commit dd4dc1b

23 files changed

Lines changed: 449 additions & 208 deletions

File tree

build-all.py

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
#!/usr/bin/env python3
2+
import os
3+
import subprocess
4+
import shutil
5+
import sys
6+
from pathlib import Path
7+
8+
print("=== Building Rust FFI Libraries for All Platforms ===")
9+
10+
# Get the directory where this script is located
11+
SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
12+
os.chdir(SCRIPT_DIR)
13+
14+
# Projects to build: (relative path, crate name, base lib name without prefixes)
15+
CRATES = [
16+
("optimizationsandtweaks_pathfinding", "optimizationsandtweaks_pathfinding"),
17+
("optimizationsandtweaks_blocksupdates", "optimizationsandtweaks_blocksupdates"),
18+
("optimizationsandtweaks_shared", "optimizationsandtweaks_shared"),
19+
]
20+
21+
# Destination base directory (relative to repo root, where this script sits)
22+
DEST_BASE = os.path.abspath("src/main/resources/assets/optimizationsandtweaks/natives")
23+
24+
# Setup osxcross PATH if available
25+
OSXCROSS_PATH = os.path.expanduser("~/osxcross/target/bin")
26+
if os.path.isdir(OSXCROSS_PATH):
27+
os.environ["PATH"] = f"{OSXCROSS_PATH}:{os.environ.get('PATH', '')}"
28+
print(f"\u2713 osxcross found at {OSXCROSS_PATH}")
29+
30+
31+
def run(cmd, check=True, cwd=None):
32+
print(f"$ {' '.join(cmd)}")
33+
subprocess.run(cmd, check=check, cwd=cwd)
34+
35+
36+
def check_target(target):
37+
result = subprocess.run(["rustup", "target", "list"], capture_output=True, text=True)
38+
if f"{target} (installed)" not in result.stdout:
39+
print(f"Installing target {target}...")
40+
run(["rustup", "target", "add", target])
41+
42+
43+
def ensure_dirs():
44+
for os_dir in ("linux", "windows", "macos"):
45+
d = os.path.join(DEST_BASE, os_dir)
46+
os.makedirs(d, exist_ok=True)
47+
48+
49+
def build_crate_for_target(crate_dir, crate_name, target, os_dir, lib_base):
50+
print(f"\n=== Building {crate_name} for {target} ===")
51+
run(["cargo", "build", "--release", "--target", target], cwd=crate_dir)
52+
53+
dest_dir = os.path.join(DEST_BASE, os_dir)
54+
os.makedirs(dest_dir, exist_ok=True)
55+
56+
# Expected filenames per platform
57+
if os_dir == "windows":
58+
lib_name = f"{lib_base}.dll"
59+
src_path = os.path.join(crate_dir, "target", target, "release", lib_name)
60+
elif os_dir == "macos":
61+
lib_name = f"lib{lib_base}.dylib"
62+
src_path = os.path.join(crate_dir, "target", target, "release", lib_name)
63+
else: # linux
64+
lib_name = f"lib{lib_base}.so"
65+
src_path = os.path.join(crate_dir, "target", target, "release", lib_name)
66+
67+
if not os.path.isfile(src_path):
68+
print(f"\u2717 Warning: Library not found at {src_path}")
69+
return None
70+
71+
dest_path = os.path.join(dest_dir, lib_name)
72+
shutil.copy2(src_path, dest_path)
73+
print(f"\u2713 Copied {lib_name} to {dest_path}")
74+
return dest_path
75+
76+
77+
# Prepare
78+
ensure_dirs()
79+
80+
# Linux x86_64
81+
check_target("x86_64-unknown-linux-gnu")
82+
for crate_rel, crate_name in CRATES:
83+
crate_dir = os.path.join(SCRIPT_DIR, crate_rel)
84+
build_crate_for_target(crate_dir, crate_name, "x86_64-unknown-linux-gnu", "linux", crate_name)
85+
86+
# Windows x86_64
87+
check_target("x86_64-pc-windows-gnu")
88+
if shutil.which("x86_64-w64-mingw32-gcc"):
89+
for crate_rel, crate_name in CRATES:
90+
crate_dir = os.path.join(SCRIPT_DIR, crate_rel)
91+
build_crate_for_target(crate_dir, crate_name, "x86_64-pc-windows-gnu", "windows", crate_name)
92+
else:
93+
print("\u26a0 Warning: MinGW-w64 not found. Skipping Windows build.\n Install with: sudo apt-get install mingw-w64")
94+
95+
# macOS - Build both architectures and create universal binaries per crate
96+
macos_x86_ok = shutil.which("x86_64-apple-darwin23.5-clang") is not None or sys.platform == "darwin"
97+
macos_arm_ok = shutil.which("aarch64-apple-darwin23.5-clang") is not None or sys.platform == "darwin"
98+
99+
if macos_x86_ok:
100+
check_target("x86_64-apple-darwin")
101+
if macos_arm_ok:
102+
check_target("aarch64-apple-darwin")
103+
104+
for crate_rel, crate_name in CRATES:
105+
crate_dir = os.path.join(SCRIPT_DIR, crate_rel)
106+
x86_lib = None
107+
arm_lib = None
108+
109+
if macos_x86_ok:
110+
run(["cargo", "build", "--release", "--target", "x86_64-apple-darwin"], cwd=crate_dir)
111+
x86_lib = os.path.join(crate_dir, "target", "x86_64-apple-darwin", "release", f"lib{crate_name}.dylib")
112+
if not os.path.isfile(x86_lib):
113+
print(f"\u26a0 Warning: macOS x86_64 lib missing for {crate_name}: {x86_lib}")
114+
x86_lib = None
115+
116+
if macos_arm_ok:
117+
run(["cargo", "build", "--release", "--target", "aarch64-apple-darwin"], cwd=crate_dir)
118+
arm_lib = os.path.join(crate_dir, "target", "aarch64-apple-darwin", "release", f"lib{crate_name}.dylib")
119+
if not os.path.isfile(arm_lib):
120+
print(f"\u26a0 Warning: macOS ARM64 lib missing for {crate_name}: {arm_lib}")
121+
arm_lib = None
122+
123+
dest_dir = os.path.join(DEST_BASE, "macos")
124+
os.makedirs(dest_dir, exist_ok=True)
125+
universal_lib = os.path.join(dest_dir, f"lib{crate_name}.dylib")
126+
127+
if x86_lib and arm_lib:
128+
print(f"\n=== Creating macOS Universal Binary for {crate_name} ===")
129+
if shutil.which("lipo"):
130+
run(["lipo", "-create", "-output", universal_lib, x86_lib, arm_lib])
131+
print(f"\u2713 Created universal binary at {universal_lib}")
132+
else:
133+
print("\u26a0 Warning: 'lipo' not found. Copying ARM64 version only.")
134+
shutil.copy2(arm_lib, universal_lib)
135+
print(f"\u2713 Copied ARM64 library to {universal_lib}")
136+
elif x86_lib:
137+
shutil.copy2(x86_lib, universal_lib)
138+
print(f"\u2713 Copied x86_64 library to {universal_lib}")
139+
elif arm_lib:
140+
shutil.copy2(arm_lib, universal_lib)
141+
print(f"\u2713 Copied ARM64 library to {universal_lib}")
142+
143+
print("\n=== Build Complete ===")
144+
print(f"Native libraries have been copied to:\n {DEST_BASE}/linux/\n {DEST_BASE}/windows/\n {DEST_BASE}/macos/")
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[target.x86_64-apple-darwin]
2+
linker = "x86_64-apple-darwin23.5-clang"
3+
ar = "x86_64-apple-darwin23.5-ar"
4+
5+
[target.aarch64-apple-darwin]
6+
linker = "aarch64-apple-darwin23.5-clang"
7+
ar = "aarch64-apple-darwin23.5-ar"
8+
9+
[target.x86_64-pc-windows-gnu]
10+
linker = "x86_64-w64-mingw32-gcc"
11+
ar = "x86_64-w64-mingw32-ar"
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Rust
2+
target/
3+
Cargo.lock
4+
5+
# Java
6+
*.class
7+
*.jar
8+
9+
# IDE
10+
.idea/
11+
.vscode/
12+
*.iml
13+
14+
# OS
15+
.DS_Store
16+
Thumbs.db
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[package]
2+
name = "optimizationsandtweaks_blocksupdates"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[lib]
7+
name = "optimizationsandtweaks_blocksupdates"
8+
crate-type = ["cdylib"]
9+
10+
[dependencies]
11+
jni = "0.21"
12+
lazy_static = "1.5.0"
13+
leaktracer = "0.1"
14+
rayon = "1.10"
15+
crossbeam = "0.8"
16+
lru = "0.12"
17+
optimizationsandtweaks_shared = { path = "../optimizationsandtweaks_shared" }
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
use std::env;
2+
3+
fn main() {
4+
println!("cargo:rerun-if-changed=src/lib.rs");
5+
6+
// Get the target information from environment variables set by cargo
7+
let target = env::var("TARGET").unwrap();
8+
let profile = env::var("PROFILE").unwrap();
9+
10+
// Only copy the library after the build is complete (not during build.rs execution)
11+
// This prevents the infinite loop issue
12+
println!("cargo:warning=Building for target: {}", target);
13+
println!("cargo:warning=Build profile: {}", profile);
14+
15+
// Determine OS-specific subdirectory and library name based on target triple
16+
let (os_dir, lib_name) = if target.contains("windows") {
17+
("windows", "optimizationsandtweaks_blocksupdates.dll")
18+
} else if target.contains("darwin") || target.contains("apple") {
19+
("macos", "optimizationsandtweaks_blocksupdates.dylib")
20+
} else if target.contains("linux") {
21+
("linux", "optimizationsandtweaks_blocksupdates.so")
22+
} else {
23+
panic!("Unsupported target OS: {}", target);
24+
};
25+
26+
// Store the OS directory and library name for post-build script
27+
println!("cargo:rustc-env=TARGET_OS_DIR={}", os_dir);
28+
println!("cargo:rustc-env=TARGET_LIB_NAME={}", lib_name);
29+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// TODO
2+
use optimizationsandtweaks_shared::{init_panic_logging, log_native_line};
3+
use jni::JNIEnv;
4+
use jni::objects::{JClass, JString};
5+
use jni::sys::{jint, jstring, JNI_VERSION_1_6};
6+
use std::os::raw::c_void;
7+
#[no_mangle]
8+
pub unsafe extern "system" fn JNI_OnLoad(_vm: *mut jni::sys::JavaVM, _reserved: *mut c_void) -> jint {
9+
// Init panic hook and any global state here.
10+
init_panic_logging();
11+
log_native_line("JNI_OnLoad: library loaded");
12+
13+
// Return the JNI version we support.
14+
JNI_VERSION_1_6
15+
}
16+
17+
#[no_mangle]
18+
pub unsafe extern "system" fn JNI_OnUnload(_vm: *mut jni::sys::JavaVM, _reserved: *mut c_void) {
19+
log_native_line("JNI_OnUnload: library unloading");
20+
}
21+
22+
/// Simple void function that logs a message. JNI signature: ()V
23+
#[no_mangle]
24+
pub extern "system" fn Java_com_example_native_NativeLib_rustHelloWorld(
25+
_env: JNIEnv,
26+
_class: JClass,
27+
) {
28+
log_native_line("Hello from Rust (hello_world)");
29+
}
30+
31+
/// Returns a Java string. JNI signature: ()Ljava/lang/String;
32+
#[no_mangle]
33+
pub extern "system" fn Java_com_example_native_NativeLib_getHelloString(
34+
mut env: JNIEnv,
35+
_class: JClass,
36+
) -> jstring {
37+
let s = env
38+
.new_string("Hello from Rust!")
39+
.expect("Couldn't create java string");
40+
s.into_raw()
41+
}
42+
43+
/// Receives a Java string and logs it. JNI signature: (Ljava/lang/String;)V
44+
#[no_mangle]
45+
pub extern "system" fn Java_com_example_native_NativeLib_printMessage(
46+
mut env: JNIEnv,
47+
_class: JClass,
48+
message: JString,
49+
) {
50+
if let Ok(msg) = env.get_string(&message) {
51+
let msg_str: String = msg.into();
52+
log_native_line(&format!("Java->Rust message: {}", msg_str));
53+
} else {
54+
log_native_line("Java->Rust message: <invalid string>");
55+
}
56+
}

optimizationsandtweaks_pathfinding/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ lazy_static = "1.5.0"
1313
leaktracer = "0.1"
1414
rayon = "1.10"
1515
crossbeam = "0.8"
16-
lru = "0.12"
16+
lru = "0.12"
17+
optimizationsandtweaks_shared = { path = "../optimizationsandtweaks_shared" }

0 commit comments

Comments
 (0)