diff --git a/Cargo.lock b/Cargo.lock index 1806e8b67..8074b12b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5321,7 +5321,6 @@ dependencies = [ "ndk-sys 0.6.0+11769913", "objc", "objc_id", - "openssl", "percent-encoding", "rand 0.9.1", "reqwest 0.12.22", @@ -5411,7 +5410,6 @@ dependencies = [ "futures-util", "getrandom 0.3.3", "http-range", - "openssl", "ouroboros", "rand 0.9.1", "reqwest 0.12.22", @@ -5668,7 +5666,6 @@ dependencies = [ "dioxus-native-dom", "futures-util", "keyboard-types", - "openssl", "rustc-hash 2.1.1", "tokio", "tracing", @@ -11614,15 +11611,6 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" -[[package]] -name = "openssl-src" -version = "300.5.1+3.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "735230c832b28c000e3bc117119e6466a663ec73506bc0a9907ea4187508e42a" -dependencies = [ - "cc", -] - [[package]] name = "openssl-sys" version = "0.9.109" @@ -11631,7 +11619,6 @@ checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" dependencies = [ "cc", "libc", - "openssl-src", "pkg-config", "vcpkg", ] diff --git a/Cargo.toml b/Cargo.toml index 88a124e70..06c12b87c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -366,7 +366,7 @@ winit = { version = "0.30.11", features = ["rwh_06"] } incremental = true # crank up the opt level for wasm-split-cli in dev mode -# important here that lto is on and the debug symbols are present (since they're used by wasm-opt)a +# important here that lto is on and the debug symbols are present (since they're used by wasm-opt) [profile.wasm-split-release] inherits = "release" opt-level = 'z' @@ -449,10 +449,6 @@ tokio = { version = "1.46", default-features = false, features = ["sync", "macro [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] tokio = { version = "1.46", features = ["full"] } -# force vendored openssl on android -[target.'cfg(target_os = "android")'.dev-dependencies] -openssl = { version = "0.10", features = ["vendored"] } - # To make most examples faster to compile, we split out assets and http-related stuff # This trims off like 270 dependencies, leading to a significant speedup in compilation time [features] diff --git a/packages/cli/assets/android/prebuilt/README.md b/packages/cli/assets/android/prebuilt/README.md new file mode 100644 index 000000000..e1d7c4855 --- /dev/null +++ b/packages/cli/assets/android/prebuilt/README.md @@ -0,0 +1,13 @@ +This folder contains prebuilt versions of android crates to make cross-compiling easier. + +We use the official prebuilds distributed by google. + +You can find the full set of prebuilt libraries that google distributes here: + +https://maven.google.com/web/index.html?q=com.android.ndk.thirdparty#com.android.ndk.thirdparty + +The version included in `dx` is downloaded from here: + +https://maven.google.com/web/index.html?q=com.android.ndk.thirdparty#com.android.ndk.thirdparty:openssl:1.1.1q-beta-1 + +The SHA of the `.aar` file from google and `dx` are different. The Rust openssl-sys crate expects libcrypto and libssl to be in the same folder, but the `.aar` that google distributes splits the two libraries into two different folders. I (jon) have simply merged the folders together and then re-packed the folder as a `.tar.gz`. diff --git a/packages/cli/assets/android/prebuilt/openssl-1.1.1q-beta-1.tar.gz b/packages/cli/assets/android/prebuilt/openssl-1.1.1q-beta-1.tar.gz new file mode 100644 index 000000000..5cfbde20d Binary files /dev/null and b/packages/cli/assets/android/prebuilt/openssl-1.1.1q-beta-1.tar.gz differ diff --git a/packages/cli/src/build/request.rs b/packages/cli/src/build/request.rs index ed1a9cfe7..53b3a96c9 100644 --- a/packages/cli/src/build/request.rs +++ b/packages/cli/src/build/request.rs @@ -1147,6 +1147,10 @@ impl BuildRequest { async fn write_frameworks(&self, _ctx: &BuildContext, direct_rustc: &RustcArgs) -> Result<()> { let framework_dir = self.frameworks_folder(); + // We have some prebuilt stuff that needs to be copied into the framework dir + let openssl_dir = AndroidTools::openssl_lib_dir(&self.triple); + let openssl_dir_disp = openssl_dir.display().to_string(); + for arg in &direct_rustc.link_args { // todo - how do we handle windows dlls? we don't want to bundle the system dlls // for now, we don't do anything with dlls, and only use .dylibs and .so files @@ -1180,13 +1184,25 @@ impl BuildRequest { // On android, the c++_shared flag means we need to copy the libc++_shared.so precompiled // library to the jniLibs folder - if arg.contains("-lc++_shared") && self.bundle == BundleFormat::Android { + if self.bundle == BundleFormat::Android && arg.contains("-lc++_shared") { std::fs::copy( self.workspace.android_tools()?.libcpp_shared(&self.triple), framework_dir.join("libc++_shared.so"), ) .with_context(|| "Failed to copy libc++_shared.so into bundle")?; } + + // Copy over libssl and libcrypto if they are present in the link args + if self.bundle == BundleFormat::Android && arg.contains(openssl_dir_disp.as_str()) { + let libssl = openssl_dir.join("libssl.so"); + let libcrypto = openssl_dir.join("libcrypto.so"); + std::fs::copy(&libssl, framework_dir.join("libssl.so")).with_context(|| { + format!("Failed to copy libssl.so into bundle from {libssl:?}") + })?; + std::fs::copy(&libcrypto, framework_dir.join("libcrypto.so")).with_context( + || format!("Failed to copy libcrypto.so into bundle from {libcrypto:?}"), + )?; + } } Ok(()) @@ -2590,17 +2606,21 @@ impl BuildRequest { // choose the clang target with the highest version // Should we filter for only numbers? - let clang_builtins_target = std::fs::read_dir(clang_folder) - .expect("Unable to get clang target directory") - .filter_map(|a| a.ok()) - .max_by(|a, b| a.file_name().cmp(&b.file_name())) - .expect("Unable to get clang target") - .path(); - let clang_rt = format!( - "-L{} -lstatic=clang_rt.builtins-{}-android", - clang_builtins_target.join("lib").join("linux").display(), - rt_builtins(&triple) - ); + let clang_rt = std::fs::read_dir(&clang_folder) + .map(|dir| { + let clang_builtins_target = dir + .filter_map(|a| a.ok()) + .max_by(|a, b| a.file_name().cmp(&b.file_name())) + .map(|s| s.path()) + .unwrap_or_else(|| clang_folder.join("clang")); + + format!( + "-L{} -lstatic=clang_rt.builtins-{}-android", + clang_builtins_target.join("lib").join("linux").display(), + rt_builtins(&triple) + ) + }) + .unwrap_or_default(); let extra_include: String = format!( "{}/usr/include/{}", @@ -2614,6 +2634,17 @@ impl BuildRequest { extra_include ); + // Load up the OpenSSL environment variables, using our defaults if not set. + // if the user specifies `/vendor`, then they get vendored, unless OPENSSL_NO_VENDOR is passed (implicitly...) + let openssl_lib_dir = std::env::var("OPENSSL_LIB_DIR") + .map(PathBuf::from) + .unwrap_or_else(|_| AndroidTools::openssl_lib_dir(&self.triple)); + let openssl_include_dir = std::env::var("OPENSSL_INCLUDE_DIR") + .map(PathBuf::from) + .unwrap_or_else(|_| AndroidTools::openssl_include_dir()); + let openssl_libs = + std::env::var("OPENSSL_LIBS").unwrap_or_else(|_| "ssl:crypto".to_string()); + for env in [ (cc_key, target_cc.clone().into_os_string()), (cflags_key, target_cflags.into()), @@ -2651,6 +2682,15 @@ impl BuildRequest { linker.into_os_string(), ), ("ANDROID_NDK_ROOT".to_string(), ndk_home.into_os_string()), + ( + "OPENSSL_LIB_DIR".to_string(), + openssl_lib_dir.into_os_string(), + ), + ( + "OPENSSL_INCLUDE_DIR".to_string(), + openssl_include_dir.into_os_string(), + ), + ("OPENSSL_LIBS".to_string(), openssl_libs.into()), // Set the wry env vars - this is where wry will dump its kotlin files. // Their setup is really annyoing and requires us to hardcode `dx` to specific versions of tao/wry. ( @@ -4684,6 +4724,11 @@ __wbg_init({{module_or_path: "/{}/{wasm_path}"}}).then((wasm) => {{ ) .await?; + // We want to copy over the prebuilt OpenSSL binaries to ~/.dx/prebuilt/openssl- + if self.bundle == BundleFormat::Android { + AndroidTools::unpack_prebuilt_openssl()?; + } + Ok(()) } } diff --git a/packages/cli/src/build/tools.rs b/packages/cli/src/build/tools.rs index c63cedc9b..a38351cde 100644 --- a/packages/cli/src/build/tools.rs +++ b/packages/cli/src/build/tools.rs @@ -1,3 +1,5 @@ +use crate::Result; +use anyhow::Context; use itertools::Itertools; use std::{path::PathBuf, sync::Arc}; use target_lexicon::{ @@ -301,6 +303,56 @@ impl AndroidTools { _ => rust_target, }) as _ } + + pub(crate) fn openssl_prebuilt_aar() -> &'static [u8] { + include_bytes!("../../assets/android/prebuilt/openssl-1.1.1q-beta-1.tar.gz") + } + + pub(crate) fn openssl_prebuilt_dest() -> PathBuf { + crate::Workspace::dioxus_data_dir() + .join("prebuilt") + .join("openssl-1.1.1q-beta-1") + } + + pub(crate) fn openssl_lib_dir(arch: &Triple) -> PathBuf { + let libs_dir = Self::openssl_prebuilt_dest().join("ssl").join("libs"); + + match arch.architecture { + Architecture::Arm(_) => libs_dir.join("android.armeabi-v7a"), + Architecture::Aarch64(_) => libs_dir.join("android.arm64-v8a"), + Architecture::X86_32(_) => libs_dir.join("android.x86"), + Architecture::X86_64 => libs_dir.join("android.x86_64"), + _ => libs_dir.join("android.arm64-v8a"), // Default to arm64-v8a + } + } + + pub(crate) fn openssl_include_dir() -> PathBuf { + Self::openssl_prebuilt_dest().join("ssl").join("include") + } + + /// Unzip the prebuilt OpenSSL AAR file into the `.dx/prebuilt/openssl-` directory + pub(crate) fn unpack_prebuilt_openssl() -> Result<()> { + let raw_aar = AndroidTools::openssl_prebuilt_aar(); + let aar_dest = AndroidTools::openssl_prebuilt_dest(); + + if aar_dest.exists() { + tracing::trace!("Prebuilt OpenSSL already exists at {:?}", aar_dest); + return Ok(()); + } + + std::fs::create_dir_all(aar_dest.parent().context("no parent for aar")?) + .context("failed to create prebuilt OpenSSL directory")?; + + // Unpack the entire tar.gz file into the destination directory + let mut archive = tar::Archive::new(flate2::read::GzDecoder::new(raw_aar as &[u8])); + archive + .unpack(aar_dest.parent().context("no parent for aar dest")?) + .context("failed to unpack prebuilt OpenSSL archive")?; + + tracing::debug!("Unpacked prebuilt OpenSSL to {:?}", aar_dest); + + Ok(()) + } } fn var_or_debug(name: &str) -> Option { diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index 3fc9b6763..b77f52b73 100644 --- a/packages/desktop/Cargo.toml +++ b/packages/desktop/Cargo.toml @@ -78,11 +78,6 @@ ndk = { version = "0.9.0" } ndk-sys = { version = "0.6.0" } ndk-context = { version = "0.1.1" } -# The `openssl` dependency generally does not know how to cross-compile for Android. To make the lives -# of our users easier, we automatically enable the `openssl` feature when building for Android. -# Feature are additive, so users will need to "subtract" this feature if they do not want it. -openssl = { version = "0.10", features = ["vendored"], optional = true } - # use native tls on other platforms [target.'cfg(not(target_os = "android"))'.dependencies] tungstenite = { workspace = true, features = ["native-tls"] } @@ -96,12 +91,11 @@ objc = "0.2.7" lazy-js-bundle = { workspace = true } [features] -default = ["tokio_runtime", "transparent", "devtools", "openssl-vendored-android"] +default = ["tokio_runtime", "transparent", "devtools"] tokio_runtime = ["dep:tokio"] fullscreen = ["wry/fullscreen"] devtools = ["wry/devtools", "dep:dioxus-devtools", "dioxus-signals"] transparent = ["wry/transparent"] -openssl-vendored-android = ["openssl"] gnu = [] [package.metadata.docs.rs] diff --git a/packages/native/Cargo.toml b/packages/native/Cargo.toml index 95fb2ed7a..a02b0936e 100644 --- a/packages/native/Cargo.toml +++ b/packages/native/Cargo.toml @@ -10,14 +10,13 @@ homepage = "https://dioxuslabs.com/learn/0.6/getting_started" keywords = ["dom", "ui", "gui", "react"] [features] -default = ["accessibility", "hot-reload", "tracing", "net", "svg", "system-fonts", "openssl-vendored-android"] +default = ["accessibility", "hot-reload", "tracing", "net", "svg", "system-fonts"] svg = ["blitz-dom/svg", "blitz-paint/svg"] net = ["dep:tokio", "dep:blitz-net"] accessibility = ["blitz-shell/accessibility", "blitz-dom/accessibility"] tracing = ["dep:tracing", "blitz-shell/tracing", "blitz-dom/tracing"] hot-reload = ["dep:dioxus-cli-config", "dep:dioxus-devtools"] system-fonts = ["blitz-dom/system_fonts"] -openssl-vendored-android = ["openssl"] autofocus = [] [dependencies] @@ -52,9 +51,6 @@ tracing = { workspace = true, optional = true } rustc-hash = { workspace = true } futures-util = { workspace = true } -[target.'cfg(target_os = "android")'.dependencies] -openssl = { version = "0.10", features = ["vendored"], optional = true } - [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"]