From 8ea9598c1f98a47fd70e24b1453b28f7ae1d182c Mon Sep 17 00:00:00 2001 From: mfwolffe Date: Tue, 26 May 2026 21:28:02 -0400 Subject: [PATCH 1/5] add freebsd to os module cfg gates --- src/os/mod.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/os/mod.rs b/src/os/mod.rs index 0cbfb3fb..73f58731 100644 --- a/src/os/mod.rs +++ b/src/os/mod.rs @@ -12,13 +12,19 @@ mod linux; #[cfg(target_os = "linux")] pub use self::linux::*; +#[cfg(target_os = "freebsd")] +mod freebsd; + +#[cfg(target_os = "freebsd")] +pub use self::freebsd::*; + #[cfg(windows)] mod windows; #[cfg(windows)] pub use self::windows::*; -#[cfg(not(any(target_os = "macos", target_os = "linux", windows)))] +#[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "freebsd", windows)))] compile_error!("Host platform not yet supported by cargo-mobile2! We'd love if you made a PR to add support for this platform ❤️"); // TODO: we should probably expose common functionality throughout `os` in a From 494d924b44deea3554a89baaaa384b254887d625 Mon Sep 17 00:00:00 2001 From: mfwolffe Date: Tue, 26 May 2026 21:28:56 -0400 Subject: [PATCH 2/5] add freebsd os module --- src/os/freebsd/mod.rs | 175 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 src/os/freebsd/mod.rs diff --git a/src/os/freebsd/mod.rs b/src/os/freebsd/mod.rs new file mode 100644 index 00000000..c940f1cc --- /dev/null +++ b/src/os/freebsd/mod.rs @@ -0,0 +1,175 @@ +pub(super) mod info; + +#[path = "../linux/xdg.rs"] +mod xdg; + +use std::{ + ffi::{OsStr, OsString}, + io, + path::{Path, PathBuf}, +}; +use thiserror::Error; + +use crate::DuctExpressionExt; +pub use crate::{ + env::{Env, ExplicitEnv}, + util::ln, +}; + +#[derive(Debug, Error)] +pub enum DetectEditorError { + #[error("No default editor is set: xdg-mime queries for \"text/rust\" and \"text/plain\" both failed")] + NoDefaultEditorSet, + #[error("Entry Not Found: xdg-mime returned an entry name that could not be found")] + FreeDesktopEntryNotFound, + #[error( + "Entry Parse Error: xdg-mime returned an entry that could not be parsed. Caused by {0}" + )] + FreeDesktopEntryParseError(io::Error), + #[error("Entry Parse Error: file lookup failed. Caused by {0}")] + FreeDesktopEntryLookupFailed(io::Error), + #[error("Exec field on desktop entry was not found")] + ExecFieldMissing, +} + +#[derive(Debug, Error)] +pub enum OpenFileError { + #[error("Failed to run {command}: {error}")] + CommandFailed { + command: String, + error: std::io::Error, + }, + #[error("Command parsing failed")] + CommandParsingFailed, +} + +#[derive(Debug)] +pub struct Application { + exec_command: OsString, + icon: Option, + xdg_entry_path: PathBuf, +} + +impl Application { + pub fn detect_editor() -> Result { + let entry = xdg::query_mime_entry("text/rust") + .or_else(|| xdg::query_mime_entry("text/plain")) + .ok_or(DetectEditorError::NoDefaultEditorSet)?; + + xdg::get_xdg_data_dirs() + .iter() + .find_map(|dir| { + let dir = dir.join("applications"); + xdg::find_entry_in_dir(&dir, &entry) + .ok()? + .map(|entry_filepath| { + xdg::parse(&entry_filepath) + .map_err(DetectEditorError::FreeDesktopEntryParseError) + .and_then(|parsed_entry| { + Ok(Self { + exec_command: parsed_entry + .section("Desktop Entry") + .and_then(|s| s.attr("Exec").first()) + .ok_or(DetectEditorError::ExecFieldMissing)? + .into(), + icon: parsed_entry + .section("Desktop Entry") + .and_then(|s| s.attr("Icon").first()) + .map(Into::into), + xdg_entry_path: entry_filepath, + }) + }) + }) + }) + .unwrap_or(Err(DetectEditorError::FreeDesktopEntryNotFound)) + } + + pub fn open_file(&self, path: impl AsRef) -> Result<(), OpenFileError> { + let path = path.as_ref(); + let maybe_icon = self.icon.as_deref(); + + let command_parts = xdg::parse_command( + &self.exec_command, + path.as_os_str(), + maybe_icon, + Some(&self.xdg_entry_path), + ); + + if !command_parts.is_empty() { + let cmd = duct::cmd(&command_parts[0], &command_parts[1..]); + cmd.run_and_detach() + .map_err(|error| OpenFileError::CommandFailed { + command: format!("{cmd:?}"), + error, + }) + } else { + Err(OpenFileError::CommandParsingFailed) + } + } +} + +pub fn open_file_with( + application: impl AsRef, + path: impl AsRef, + env: &Env, +) -> Result<(), OpenFileError> { + let app_str = application.as_ref(); + let path_str = path.as_ref(); + + let command_parts = xdg::get_xdg_data_dirs() + .iter() + .find_map(|dir| { + let dir = dir.join("applications"); + let (entry, entry_path) = xdg::find_entry_by_app_name(&dir, app_str)?; + + let command_parts = entry + .section("Desktop Entry") + .and_then(|s| s.attr("Exec").first()) + .map(|str_entry| { + xdg::parse_command( + str_entry.as_ref(), + path_str, + entry + .section("Desktop Entry") + .and_then(|s| s.attr("Icon").first()) + .map(|s| s.as_ref()), + Some(&entry_path), + ) + })?; + if !command_parts.is_empty() { + Some(command_parts) + } else { + None + } + }) + .unwrap_or_else(|| vec![app_str.to_os_string()]); + + let cmd = duct::cmd(&command_parts[0], &command_parts[1..]).vars(env.explicit_env()); + cmd.run_and_detach() + .map_err(|error| OpenFileError::CommandFailed { + command: format!("{cmd:?}"), + error, + }) +} + +#[cfg(target_os = "freebsd")] +pub fn command_path(name: &str) -> std::io::Result { + duct::cmd("sh", ["-c", format!("command -v {name}").as_str()]).run() +} + +pub fn code_command() -> duct::Expression { + duct::cmd!("code") +} + +pub fn replace_path_separator(path: OsString) -> OsString { + path +} + +pub mod consts { + pub const CLANG: &str = "clang"; + pub const CLANGXX: &str = "clang++"; + pub const AR: &str = "ar"; + pub const LD: &str = "ld"; + pub const READELF: &str = "readelf"; + pub const NDK_STACK: &str = "ndk-stack"; +} From b27841233631fa884a3a9cbab5878fef68bd4edf Mon Sep 17 00:00:00 2001 From: mfwolffe Date: Tue, 26 May 2026 21:29:50 -0400 Subject: [PATCH 3/5] add freebsd info module using uname --- src/os/freebsd/info.rs | 43 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 src/os/freebsd/info.rs diff --git a/src/os/freebsd/info.rs b/src/os/freebsd/info.rs new file mode 100644 index 00000000..e5deb558 --- /dev/null +++ b/src/os/freebsd/info.rs @@ -0,0 +1,43 @@ +use crate::os::Info; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum Error { + #[error("Failed to run `uname {flag}`: {source}")] + UnameFailed { flag: &'static str, source: std::io::Error }, + #[error("`uname {flag}` returned empty output")] + UnameEmpty { flag: &'static str }, +} + +pub fn check() -> Result { + let name = duct::cmd("uname", ["-s"]) + .read() + .map_err(|source| Error::UnameFailed { flag: "-s", source })?; + let name = name.trim().to_owned(); + if name.is_empty() { + return Err(Error::UnameEmpty { flag: "-s" }); + } + + let version = duct::cmd("uname", ["-r"]) + .read() + .map_err(|source| Error::UnameFailed { flag: "-r", source })?; + let version = version.trim().to_owned(); + if version.is_empty() { + return Err(Error::UnameEmpty { flag: "-r" }); + } + + Ok(Info { name, version }) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + #[cfg(target_os = "freebsd")] + fn check_returns_freebsd_info() { + let info = check().expect("uname should succeed on FreeBSD"); + assert_eq!(info.name, "FreeBSD"); + assert!(!info.version.is_empty()); + } +} From 9cb11b00fb257773b51829a519508e6f4db21b00 Mon Sep 17 00:00:00 2001 From: mfwolffe Date: Tue, 26 May 2026 21:30:10 -0400 Subject: [PATCH 4/5] add freebsd host_tag for android ndk --- src/android/ndk.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/android/ndk.rs b/src/android/ndk.rs index cfd27069..7e73a39f 100644 --- a/src/android/ndk.rs +++ b/src/android/ndk.rs @@ -29,6 +29,12 @@ pub fn host_tag() -> &'static str { "linux-x86_64" } +// FreeBSD runs Android NDK tooling via Linux binary compatibility (Linuxulator) +#[cfg(target_os = "freebsd")] +pub fn host_tag() -> &'static str { + "linux-x86_64" +} + #[cfg(all(windows, target_pointer_width = "32"))] pub fn host_tag() -> &'static str { "windows" From b0d827e508e98c55f997a97e3b514affc1451b34 Mon Sep 17 00:00:00 2001 From: mfwolffe Date: Tue, 26 May 2026 21:31:33 -0400 Subject: [PATCH 5/5] include freedesktop_entry_parser dep for freebsd --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 8de02766..d1e7fbbf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -92,7 +92,7 @@ plist = "1" [target."cfg(not(target_os = \"macos\"))".dependencies] ureq = { version = "3", default-features = false, features = ["gzip"] } -[target."cfg(target_os = \"linux\")".dependencies] +[target."cfg(any(target_os = \"linux\", target_os = \"freebsd\"))".dependencies] freedesktop_entry_parser = "2.0" [target."cfg(unix)".dependencies]