From ed0abb661c21a57dad81ec2f317ee46eaebfd347 Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Tue, 4 Feb 2025 17:02:59 -0700 Subject: [PATCH] musikr: implement taglib build step for rust module --- gradle.properties | 4 + musikr/build.gradle | 2 + musikr/src/main/jni/Cargo.lock | 166 ++++++++++++++++++++ musikr/src/main/jni/Cargo.toml | 4 + musikr/src/main/jni/android.toolchain.cmake | 15 ++ musikr/src/main/jni/build.rs | 101 ++++++++++++ musikr/src/main/jni/build_taglib.sh | 1 + musikr/src/main/jni/src/lib.rs | 8 + 8 files changed, 301 insertions(+) create mode 100644 musikr/src/main/jni/android.toolchain.cmake create mode 100644 musikr/src/main/jni/build.rs diff --git a/gradle.properties b/gradle.properties index f28e39824..64d55ae52 100644 --- a/gradle.properties +++ b/gradle.properties @@ -25,3 +25,7 @@ android.nonFinalResIds=true org.gradle.parallel=true org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled org.jetbrains.dokka.experimental.gradle.pluginMode.noWarn=true +project.RUST_ANDROID_GRADLE_ARMV7_LINUX_ANDROIDEABI_NDK_PATH=android.ndkDirectory +project.RUST_ANDROID_GRADLE_I686_LINUX_ANDROID_NDK_PATH=android.ndkDirectory +project.RUST_ANDROID_GRADLE_AARCH64_LINUX_ANDROID_NDK_PATH=android.ndkDirectory +project.RUST_ANDROID_GRADLE_X64_64_LINUX_ANDROID_NDK_PATH=android.ndkDirectory diff --git a/musikr/build.gradle b/musikr/build.gradle index 7e011d7a7..81c6697c7 100644 --- a/musikr/build.gradle +++ b/musikr/build.gradle @@ -46,6 +46,7 @@ android { } } + cargo { module = "./src/main/jni" libname = "metadatajni" @@ -78,6 +79,7 @@ dependencies { androidTestImplementation 'androidx.test.ext:junit:1.2.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1' } + // //task assembleTaglib(type: Exec) { // def jniDir = "$projectDir/src/main/cpp" diff --git a/musikr/src/main/jni/Cargo.lock b/musikr/src/main/jni/Cargo.lock index c9cce0594..a899bbbfc 100644 --- a/musikr/src/main/jni/Cargo.lock +++ b/musikr/src/main/jni/Cargo.lock @@ -2,12 +2,27 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + [[package]] name = "bytes" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" +[[package]] +name = "cc" +version = "1.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "755717a7de9ec452bf7f3f1a3099085deabd7f2962b861dae91ecd7a365903d2" +dependencies = [ + "shlex", +] + [[package]] name = "cesu8" version = "1.1.0" @@ -20,6 +35,42 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clap" +version = "4.5.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e77c3243bd94243c03672cb5154667347c457ca271254724f9f393aee1c05ff" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7" +dependencies = [ + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + [[package]] name = "combine" version = "4.6.7" @@ -30,6 +81,71 @@ dependencies = [ "memchr", ] +[[package]] +name = "cxx" +version = "1.0.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc894913dccfed0f84106062c284fa021c3ba70cb1d78797d6f5165d4492e45" +dependencies = [ + "cc", + "cxxbridge-cmd", + "cxxbridge-flags", + "cxxbridge-macro", + "foldhash", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "503b2bfb6b3e8ce7f95d865a67419451832083d3186958290cee6c53e39dfcfe" +dependencies = [ + "cc", + "codespan-reporting", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-cmd" +version = "1.0.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0d2cb64a95b4b5a381971482235c4db2e0208302a962acdbe314db03cbbe2fb" +dependencies = [ + "clap", + "codespan-reporting", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f797b0206463c9c2a68ed605ab28892cca784f1ef066050f4942e3de26ad885" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79010a2093848e65a3e0f7062d3f02fb2ef27f866416dfe436fccfa73d3bb59" +dependencies = [ + "proc-macro2", + "quote", + "rustversion", + "syn", +] + +[[package]] +name = "foldhash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + [[package]] name = "jni" version = "0.21.1" @@ -52,6 +168,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +[[package]] +name = "link-cplusplus" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d240c6f7e1ba3a28b0249f774e6a9dd0175054b52dfbb61b16eb8505c3785c9" +dependencies = [ + "cc", +] + [[package]] name = "log" version = "0.4.25" @@ -68,6 +193,8 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" name = "metadatajni" version = "1.0.0" dependencies = [ + "cxx", + "cxx-build", "jni", ] @@ -89,6 +216,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rustversion" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" + [[package]] name = "same-file" version = "1.0.6" @@ -98,6 +231,24 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "scratch" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3cf7c11c38cb994f3d40e8a8cde3bbd1f72a435e4c49e85d6553d8312306152" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "syn" version = "2.0.98" @@ -109,6 +260,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -135,6 +295,12 @@ version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + [[package]] name = "walkdir" version = "2.5.0" diff --git a/musikr/src/main/jni/Cargo.toml b/musikr/src/main/jni/Cargo.toml index 2b6ab756c..2b3deda83 100644 --- a/musikr/src/main/jni/Cargo.toml +++ b/musikr/src/main/jni/Cargo.toml @@ -8,4 +8,8 @@ crate-type = ["cdylib"] name = "metadatajni" [dependencies] +cxx = "1.0.137" jni = "0.21.1" + +[build-dependencies] +cxx-build = "1.0.137" \ No newline at end of file diff --git a/musikr/src/main/jni/android.toolchain.cmake b/musikr/src/main/jni/android.toolchain.cmake new file mode 100644 index 000000000..53123fef9 --- /dev/null +++ b/musikr/src/main/jni/android.toolchain.cmake @@ -0,0 +1,15 @@ +# Define the minimum CMake version and project name +cmake_minimum_required(VERSION 3.22.1) + +# Set the Android NDK path +option(ANDROID_NDK_PATH "Path to Android NDK Install. Should be same version specified in gradle." REQUIRED) + +# Specify the target Android API level +set(ANDROID_PLATFORM android-24) + +# Define the toolchain +set(CMAKE_SYSTEM_NAME Android) +set(CMAKE_ANDROID_ARCH_ABI ${ANDROID_ABI}) +set(CMAKE_ANDROID_NDK ${ANDROID_NDK_PATH}) +set(CMAKE_ANDROID_STL_TYPE c++_static) +set(CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION clang) \ No newline at end of file diff --git a/musikr/src/main/jni/build.rs b/musikr/src/main/jni/build.rs new file mode 100644 index 000000000..2ae4500b9 --- /dev/null +++ b/musikr/src/main/jni/build.rs @@ -0,0 +1,101 @@ +use std::env; +use std::path::Path; +use std::process::Command; +use cxx_build; + +fn main() { + let working_dir = env::current_dir().expect("Failed to get current working directory"); + + // New: Extract ndk path from CLANG_PATH. + let clang_path = env::var("CLANG_PATH").expect("CLANG_PATH env var not set"); + let toolchains_marker = "/toolchains"; + let ndk_path = if let Some(pos) = clang_path.find(toolchains_marker) { + &clang_path[..pos] + } else { + panic!("CLANG_PATH does not contain '{}'", toolchains_marker); + }; + + let working_dir = Path::new(&working_dir); + + // Define directories. + let taglib_src_dir = working_dir.join("taglib"); + let taglib_build_dir = taglib_src_dir.join("build"); + let taglib_pkg_dir = taglib_src_dir.join("pkg"); + let ndk_toolchain = working_dir.join("android.toolchain.cmake"); + + // Define architectures. + let target = env::var("TARGET").expect("TARGET env var not set"); + let arch = if target.contains("x86_64") { + "x86_64" + } else if target.contains("i686") { + "x86" + } else if target.contains("aarch64") { + "arm64-v8a" + } else if target.contains("arm") { + "armeabi-v7a" + } else { + "unknown" + }; + + let arch_build_dir = taglib_build_dir.join(arch); + let arch_pkg_dir = taglib_pkg_dir.join(arch); + + // Configure step. + let status = Command::new("cmake") + .arg("-B") + .arg(arch_build_dir.to_str().unwrap()) + .arg(format!("-DANDROID_NDK_PATH={}", ndk_path)) + .arg(format!( + "-DCMAKE_TOOLCHAIN_FILE={}", + ndk_toolchain.to_str().unwrap() + )) + .arg(format!("-DANDROID_ABI={}", arch)) + .arg("-DBUILD_SHARED_LIBS=OFF") + .arg("-DVISIBILITY_HIDDEN=ON") + .arg("-DBUILD_TESTING=OFF") + .arg("-DBUILD_EXAMPLES=OFF") + .arg("-DBUILD_BINDINGS=OFF") + .arg("-DWITH_ZLIB=OFF") + .arg("-DCMAKE_BUILD_TYPE=Release") + .arg("-DCMAKE_CXX_FLAGS=-fPIC") + .current_dir(&taglib_src_dir) + .status() + .expect("Failed to run cmake configure"); + if !status.success() { + panic!("cmake configure failed for arch {}", arch); + } + + // Build step. + let status = Command::new("cmake") + .arg("--build") + .arg(arch_build_dir.to_str().unwrap()) + .arg("--config") + .arg("Release") + .arg(format!("-j{}", std::thread::available_parallelism().unwrap().get())) + .status() + .expect("Failed to run cmake build"); + if !status.success() { + panic!("cmake build failed for arch {}", arch); + } + + // Install step. + let status = Command::new("cmake") + .arg("--install") + .arg(arch_build_dir.to_str().unwrap()) + .arg("--config") + .arg("Release") + .arg("--prefix") + .arg(arch_pkg_dir.to_str().unwrap()) + .arg("--strip") + .status() + .expect("Failed to run cmake install"); + if !status.success() { + panic!("cmake install failed for arch {}", arch); + } + + cxx_build::bridge("src/lib.rs") + .include(format!["taglib/pkg/{}/include", arch]) // path to Taglib includes + .flag_if_supported("-std=c++14") + .compile("taglib_cxx_bindings"); + // println!("cargo:rerun-if-changed=src/lib.rs"); +} diff --git a/musikr/src/main/jni/build_taglib.sh b/musikr/src/main/jni/build_taglib.sh index a934819d8..5ba18802a 100755 --- a/musikr/src/main/jni/build_taglib.sh +++ b/musikr/src/main/jni/build_taglib.sh @@ -1,3 +1,4 @@ +# Deprecated: Please use the new Rust buildscript in build.rs set -e WORKING_DIR=$1 echo "Working directory is at $WORKING_DIR" diff --git a/musikr/src/main/jni/src/lib.rs b/musikr/src/main/jni/src/lib.rs index b2e55839b..67b6a6e90 100644 --- a/musikr/src/main/jni/src/lib.rs +++ b/musikr/src/main/jni/src/lib.rs @@ -2,6 +2,14 @@ use jni::objects::{JClass, JString}; use jni::sys::jstring; use jni::JNIEnv; +#[cxx::bridge] +mod ffi { + unsafe extern "C++" { + include!("taglib/taglib.h"); + // Add the C++ APIs you need to call. + } +} + #[no_mangle] pub extern "C" fn Java_org_oxycblt_musikr_metadata_MetadataJNI_rust( mut env: JNIEnv,