From 8e1e15304e9dc98d7f875fc8dceb7d4ce19adc47 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Mon, 5 Aug 2024 09:46:28 -0300 Subject: [PATCH] feat(cli): check if Rust lib contains required Android and iOS symbols #10094 (#10483) * feat(cli): check if Rust lib contains required Android symbols #10094 * check for ios aswell --- .changes/check-android-lib-symbols.md | 6 ++ core/tauri-macros/src/mobile.rs | 1 + core/tauri/src/lib.rs | 1 + tooling/cli/Cargo.lock | 7 +++ tooling/cli/Cargo.toml | 1 + .../cli/src/migrate/migrations/v1/frontend.rs | 2 +- tooling/cli/src/migrate/migrations/v2_rc.rs | 2 +- .../mobile/android/android_studio_script.rs | 62 ++++++++++++++++--- tooling/cli/src/mobile/ios/xcode_script.rs | 19 +++++- 9 files changed, 88 insertions(+), 13 deletions(-) create mode 100644 .changes/check-android-lib-symbols.md diff --git a/.changes/check-android-lib-symbols.md b/.changes/check-android-lib-symbols.md new file mode 100644 index 000000000..7afbfcf41 --- /dev/null +++ b/.changes/check-android-lib-symbols.md @@ -0,0 +1,6 @@ +--- +"tauri-cli": patch:enhance +"@tauri-apps/cli": patch:enhance +--- + +Check if the Rust library contains the symbols required at runtime for Android and iOS apps. diff --git a/core/tauri-macros/src/mobile.rs b/core/tauri-macros/src/mobile.rs index 99d1e0c77..09e9af1f0 100644 --- a/core/tauri-macros/src/mobile.rs +++ b/core/tauri-macros/src/mobile.rs @@ -76,6 +76,7 @@ pub fn entry_point(_attributes: TokenStream, item: TokenStream) -> TokenStream { stop_unwind(#function_name); } + // be careful when renaming this, the `start_app` symbol is checked by the CLI #[cfg(not(target_os = "android"))] #[no_mangle] #[inline(never)] diff --git a/core/tauri/src/lib.rs b/core/tauri/src/lib.rs index d74f7a6bc..10cc5b1c2 100644 --- a/core/tauri/src/lib.rs +++ b/core/tauri/src/lib.rs @@ -147,6 +147,7 @@ macro_rules! android_binding { ::tauri::tao ); + // be careful when renaming this, the `Java_app_tauri_plugin_PluginManager_handlePluginResponse` symbol is checked by the CLI ::tauri::tao::platform::android::prelude::android_fn!( app_tauri, plugin, diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index 92a43176b..67cbcd3f2 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -1293,6 +1293,12 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +[[package]] +name = "elf" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4445909572dbd556c457c849c4ca58623d84b27c8fff1e74b0b4227d8b90d17b" + [[package]] name = "elliptic-curve" version = "0.13.8" @@ -5132,6 +5138,7 @@ dependencies = [ "dialoguer", "duct", "dunce", + "elf", "env_logger", "glob", "handlebars", diff --git a/tooling/cli/Cargo.toml b/tooling/cli/Cargo.toml index d079348b9..34df3f3c3 100644 --- a/tooling/cli/Cargo.toml +++ b/tooling/cli/Cargo.toml @@ -99,6 +99,7 @@ oxc_ast = "0.16" magic_string = "0.3" phf = { version = "0.11", features = ["macros"] } walkdir = "2" +elf = "0.7" [target."cfg(windows)".dependencies.windows-sys] version = "0.52" diff --git a/tooling/cli/src/migrate/migrations/v1/frontend.rs b/tooling/cli/src/migrate/migrations/v1/frontend.rs index 4e34cd3c4..cd7b9444d 100644 --- a/tooling/cli/src/migrate/migrations/v1/frontend.rs +++ b/tooling/cli/src/migrate/migrations/v1/frontend.rs @@ -81,7 +81,7 @@ pub fn migrate(app_dir: &Path, tauri_dir: &Path) -> Result<()> { .current_package_version(pkg, app_dir) .unwrap_or_default() .unwrap_or_default(); - if version.starts_with("1") { + if version.starts_with('1') { new_npm_packages.push(format!("{pkg}@^{npm_version}")); } } diff --git a/tooling/cli/src/migrate/migrations/v2_rc.rs b/tooling/cli/src/migrate/migrations/v2_rc.rs index 54f9118d6..2bba891e7 100644 --- a/tooling/cli/src/migrate/migrations/v2_rc.rs +++ b/tooling/cli/src/migrate/migrations/v2_rc.rs @@ -74,7 +74,7 @@ fn migrate_npm_dependencies(app_dir: &Path) -> Result<()> { .current_package_version(pkg, app_dir) .unwrap_or_default() .unwrap_or_default(); - if version.starts_with("1") { + if version.starts_with('1') { install_deps.push(format!("{pkg}@^2.0.0-rc.0")); } } diff --git a/tooling/cli/src/mobile/android/android_studio_script.rs b/tooling/cli/src/mobile/android/android_studio_script.rs index 153f132d2..25a52e965 100644 --- a/tooling/cli/src/mobile/android/android_studio_script.rs +++ b/tooling/cli/src/mobile/android/android_studio_script.rs @@ -10,12 +10,15 @@ use crate::{ }; use clap::{ArgAction, Parser}; +use anyhow::Context; use cargo_mobile2::{ android::{adb, target::Target}, opts::Profile, target::{call_for_targets_with_fallback, TargetTrait}, }; +use std::path::Path; + #[derive(Debug, Parser)] pub struct Options { /// Targets to build. @@ -83,22 +86,61 @@ pub fn command(options: Options) -> Result<()> { } } + let mut validated_lib = false; + call_for_targets_with_fallback( options.targets.unwrap_or_default().iter(), &detect_target_ok, &env, |target: &Target| { - target - .build( - &config, - &metadata, - &env, - cli_options.noise_level, - true, - profile, - ) - .map_err(Into::into) + target.build( + &config, + &metadata, + &env, + cli_options.noise_level, + true, + profile, + )?; + + if !validated_lib { + validated_lib = true; + + let lib_path = config + .app() + .target_dir(target.triple, profile) + .join(config.so_name()); + + validate_lib(&lib_path)?; + } + + Ok(()) }, ) .map_err(|e| anyhow::anyhow!(e.to_string()))? } + +fn validate_lib(path: &Path) -> Result<()> { + let so_bytes = std::fs::read(path)?; + let elf = elf::ElfBytes::::minimal_parse(&so_bytes) + .context("failed to parse ELF")?; + let (symbol_table, string_table) = elf + .dynamic_symbol_table() + .context("failed to read dynsym section")? + .context("missing dynsym tables")?; + + let mut symbols = Vec::new(); + for s in symbol_table.iter() { + if let Ok(symbol) = string_table.get(s.st_name as usize) { + symbols.push(symbol); + } + } + + if !symbols.contains(&"Java_app_tauri_plugin_PluginManager_handlePluginResponse") { + anyhow::bail!( + "Library from {} does not include required runtime symbols. This means you are likely missing the tauri::mobile_entry_point macro usage, see the documentation for more information: https://v2.tauri.app/start/migrate/from-tauri-1", + path.display() + ); + } + + Ok(()) +} diff --git a/tooling/cli/src/mobile/ios/xcode_script.rs b/tooling/cli/src/mobile/ios/xcode_script.rs index 17f944867..d16f46611 100644 --- a/tooling/cli/src/mobile/ios/xcode_script.rs +++ b/tooling/cli/src/mobile/ios/xcode_script.rs @@ -16,7 +16,8 @@ use std::{ collections::HashMap, env::{current_dir, set_current_dir, var_os}, ffi::OsStr, - path::PathBuf, + path::{Path, PathBuf}, + process::Command, }; #[derive(Debug, Parser)] @@ -211,6 +212,8 @@ pub fn command(options: Options) -> Result<()> { return Err(anyhow::anyhow!("Library not found at {}. Make sure your Cargo.toml file has a [lib] block with `crate-type = [\"staticlib\", \"cdylib\", \"lib\"]`", lib_path.display())); } + validate_lib(&lib_path)?; + let project_dir = config.project_dir(); let externals_lib_dir = project_dir.join(format!("Externals/{arch}/{}", profile.as_str())); std::fs::create_dir_all(&externals_lib_dir)?; @@ -221,3 +224,17 @@ pub fn command(options: Options) -> Result<()> { } Ok(()) } + +fn validate_lib(path: &Path) -> Result<()> { + // we ignore `nm` errors + if let Ok(output) = Command::new("nm").arg(path).output() { + let symbols = String::from_utf8_lossy(&output.stdout); + if !symbols.contains("start_app") { + anyhow::bail!( + "Library from {} does not include required runtime symbols. This means you are likely missing the tauri::mobile_entry_point macro usage, see the documentation for more information: https://v2.tauri.app/start/migrate/from-tauri-1", + path.display() + ); + } + } + Ok(()) +}