Improve FreeBSD support (#33162)

Hiroki Tagato and Peter Tripp created

This PR contains a set of changes for improving FreeBSD support (#15309,
#29550) and is a kind of follow up to the PR #20480 which added an
initial support for FreeBSD.

A summary of changes is as follows:
- Add some more freebsd conditionals which seem missing in the previous
PR.
- Implement `anonymous_fd()` and `current_path()` functions for FreeBSD.
- Improve detection of FreeBSD in telemetry and GPU detection.
- Temporarily disable LiveKit/WebRTC support to make build succeed.
- Remove support for flatpak since it is Linux-only packaging format.

Adding `RUSTFLAGS="-C link-dead-code"` does not seem necessary anymore.
It builds fine without the flag.

Known issues:
- Integrated terminal is painfully laggy and virtually unusable in my
environment. This might be specific to my setup.
- I cannot input Japanese using IME. When I type characters, they appear
on the screen. But when I hit return key, they disappears. Seems the
same issue as #15409.

My environment is MATE desktop on X11 on FreeBSD 14.2 on Intel Core
i5-7260U integrated graphics.

P.S. For those who might be interested, a work-in-progress FreeBSD port
and binary packages are available at
https://github.com/tagattie/FreeBSD-Zed

Release Notes:

- N/A

---------

Co-authored-by: Peter Tripp <peter@zed.dev>

Change summary

crates/cli/src/main.rs                           | 10 +++---
crates/client/src/telemetry.rs                   | 12 +++++++-
crates/docs_preprocessor/src/main.rs             |  2 
crates/feedback/src/system_specs.rs              |  4 +-
crates/fs/src/fs.rs                              | 23 +++++++++++++++++
crates/gpui/src/platform/blade/blade_renderer.rs |  5 +++
crates/livekit_client/Cargo.toml                 |  2 
crates/livekit_client/src/lib.rs                 | 10 +++---
8 files changed, 50 insertions(+), 18 deletions(-)

Detailed changes

crates/cli/src/main.rs 🔗

@@ -134,7 +134,7 @@ fn main() -> Result<()> {
     util::prevent_root_execution();
 
     // Exit flatpak sandbox if needed
-    #[cfg(any(target_os = "linux", target_os = "freebsd"))]
+    #[cfg(target_os = "linux")]
     {
         flatpak::try_restart_to_host();
         flatpak::ld_extra_libs();
@@ -158,7 +158,7 @@ fn main() -> Result<()> {
         paths::set_custom_data_dir(dir);
     }
 
-    #[cfg(any(target_os = "linux", target_os = "freebsd"))]
+    #[cfg(target_os = "linux")]
     let args = flatpak::set_bin_if_no_escape(args);
 
     let app = Detect::detect(args.zed.as_deref()).context("Bundle detection")?;
@@ -374,7 +374,7 @@ fn anonymous_fd(path: &str) -> Option<fs::File> {
         let file = unsafe { fs::File::from_raw_fd(fd) };
         return Some(file);
     }
-    #[cfg(target_os = "macos")]
+    #[cfg(any(target_os = "macos", target_os = "freebsd"))]
     {
         use std::os::{
             fd::{self, FromRawFd},
@@ -392,7 +392,7 @@ fn anonymous_fd(path: &str) -> Option<fs::File> {
         let file = unsafe { fs::File::from_raw_fd(fd) };
         return Some(file);
     }
-    #[cfg(not(any(target_os = "linux", target_os = "macos")))]
+    #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "freebsd")))]
     {
         _ = path;
         // not implemented for bsd, windows. Could be, but isn't yet
@@ -537,7 +537,7 @@ mod linux {
     }
 }
 
-#[cfg(any(target_os = "linux", target_os = "freebsd"))]
+#[cfg(target_os = "linux")]
 mod flatpak {
     use std::ffi::OsString;
     use std::path::PathBuf;

crates/client/src/telemetry.rs 🔗

@@ -83,10 +83,14 @@ pub fn os_name() -> String {
     {
         "macOS".to_string()
     }
-    #[cfg(any(target_os = "linux", target_os = "freebsd"))]
+    #[cfg(target_os = "linux")]
     {
         format!("Linux {}", gpui::guess_compositor())
     }
+    #[cfg(target_os = "freebsd")]
+    {
+        format!("FreeBSD {}", gpui::guess_compositor())
+    }
 
     #[cfg(target_os = "windows")]
     {
@@ -120,8 +124,12 @@ pub fn os_version() -> String {
             file
         } else if let Ok(file) = std::fs::read_to_string(&Path::new("/usr/lib/os-release")) {
             file
+        } else if let Ok(file) = std::fs::read_to_string(&Path::new("/var/run/os-release")) {
+            file
         } else {
-            log::error!("Failed to load /etc/os-release, /usr/lib/os-release");
+            log::error!(
+                "Failed to load /etc/os-release, /usr/lib/os-release, or /var/run/os-release"
+            );
             "".to_string()
         };
         let mut name = "unknown";

crates/docs_preprocessor/src/main.rs 🔗

@@ -171,7 +171,7 @@ fn find_action_by_name(name: &str) -> Option<&ActionDef> {
 fn find_binding(os: &str, action: &str) -> Option<String> {
     let keymap = match os {
         "macos" => &KEYMAP_MACOS,
-        "linux" => &KEYMAP_LINUX,
+        "linux" | "freebsd" => &KEYMAP_LINUX,
         _ => unreachable!("Not a valid OS: {}", os),
     };
 

crates/feedback/src/system_specs.rs 🔗

@@ -133,7 +133,7 @@ impl Display for SystemSpecs {
 }
 
 fn try_determine_available_gpus() -> Option<String> {
-    #[cfg(target_os = "linux")]
+    #[cfg(any(target_os = "linux", target_os = "freebsd"))]
     {
         return std::process::Command::new("vulkaninfo")
             .args(&["--summary"])
@@ -152,7 +152,7 @@ fn try_determine_available_gpus() -> Option<String> {
             })
             .or(Some("Failed to run `vulkaninfo --summary`".to_string()));
     }
-    #[cfg(not(target_os = "linux"))]
+    #[cfg(not(any(target_os = "linux", target_os = "freebsd")))]
     {
         return None;
     }

crates/fs/src/fs.rs 🔗

@@ -272,7 +272,7 @@ impl FileHandle for std::fs::File {
         Ok(path)
     }
 
-    #[cfg(any(target_os = "linux", target_os = "freebsd"))]
+    #[cfg(target_os = "linux")]
     fn current_path(&self, _: &Arc<dyn Fs>) -> Result<PathBuf> {
         let fd = self.as_fd();
         let fd_path = format!("/proc/self/fd/{}", fd.as_raw_fd());
@@ -287,6 +287,27 @@ impl FileHandle for std::fs::File {
         Ok(new_path)
     }
 
+    #[cfg(target_os = "freebsd")]
+    fn current_path(&self, _: &Arc<dyn Fs>) -> Result<PathBuf> {
+        use std::{
+            ffi::{CStr, OsStr},
+            os::unix::ffi::OsStrExt,
+        };
+
+        let fd = self.as_fd();
+        let mut kif: libc::kinfo_file = unsafe { std::mem::zeroed() };
+        kif.kf_structsize = libc::KINFO_FILE_SIZE;
+
+        let result = unsafe { libc::fcntl(fd.as_raw_fd(), libc::F_KINFO, &mut kif) };
+        if result == -1 {
+            anyhow::bail!("fcntl returned -1".to_string());
+        }
+
+        let c_str = unsafe { CStr::from_ptr(kif.kf_path.as_ptr()) };
+        let path = PathBuf::from(OsStr::from_bytes(c_str.to_bytes()));
+        Ok(path)
+    }
+
     #[cfg(target_os = "windows")]
     fn current_path(&self, _: &Arc<dyn Fs>) -> Result<PathBuf> {
         anyhow::bail!("unimplemented")

crates/gpui/src/platform/blade/blade_renderer.rs 🔗

@@ -421,7 +421,10 @@ impl BladeRenderer {
     /// Like `update_drawable_size` but skips the check that the size has changed. This is useful in
     /// cases like restoring a window from minimization where the size is the same but the
     /// renderer's swap chain needs to be recreated.
-    #[cfg_attr(any(target_os = "macos", target_os = "linux"), allow(dead_code))]
+    #[cfg_attr(
+        any(target_os = "macos", target_os = "linux", target_os = "freebsd"),
+        allow(dead_code)
+    )]
     pub fn update_drawable_size_even_if_unchanged(&mut self, size: Size<DevicePixels>) {
         self.update_drawable_size_impl(size, true);
     }

crates/livekit_client/Cargo.toml 🔗

@@ -39,7 +39,7 @@ tokio-tungstenite.workspace = true
 util.workspace = true
 workspace-hack.workspace = true
 
-[target.'cfg(not(all(target_os = "windows", target_env = "gnu")))'.dependencies]
+[target.'cfg(not(any(all(target_os = "windows", target_env = "gnu"), target_os = "freebsd")))'.dependencies]
 libwebrtc = { rev = "80bb8f4c9112789f7c24cc98d8423010977806a6", git = "https://github.com/zed-industries/livekit-rust-sdks" }
 livekit = { rev = "80bb8f4c9112789f7c24cc98d8423010977806a6", git = "https://github.com/zed-industries/livekit-rust-sdks", features = [
     "__rustls-tls"

crates/livekit_client/src/lib.rs 🔗

@@ -6,32 +6,32 @@ pub use remote_video_track_view::{RemoteVideoTrackView, RemoteVideoTrackViewEven
 #[cfg(not(any(
     test,
     feature = "test-support",
-    all(target_os = "windows", target_env = "gnu")
+    any(all(target_os = "windows", target_env = "gnu"), target_os = "freebsd")
 )))]
 mod livekit_client;
 #[cfg(not(any(
     test,
     feature = "test-support",
-    all(target_os = "windows", target_env = "gnu")
+    any(all(target_os = "windows", target_env = "gnu"), target_os = "freebsd")
 )))]
 pub use livekit_client::*;
 
 #[cfg(any(
     test,
     feature = "test-support",
-    all(target_os = "windows", target_env = "gnu")
+    any(all(target_os = "windows", target_env = "gnu"), target_os = "freebsd")
 ))]
 mod mock_client;
 #[cfg(any(
     test,
     feature = "test-support",
-    all(target_os = "windows", target_env = "gnu")
+    any(all(target_os = "windows", target_env = "gnu"), target_os = "freebsd")
 ))]
 pub mod test;
 #[cfg(any(
     test,
     feature = "test-support",
-    all(target_os = "windows", target_env = "gnu")
+    any(all(target_os = "windows", target_env = "gnu"), target_os = "freebsd")
 ))]
 pub use mock_client::*;