Windows and Linux build environments; check clean on both (E-01)
MuPDF builds with MSBuild on Windows (/MT, OpenSSL from vcpkg); Git for Windows' MinGit stands in for the source build there. CEF loads from where libcef lives and sets its API version first on every platform. File identity on Windows compares volume and index. The checkout and reference/ keep LF on Windows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d59159cea2
commit
68b5af76e0
40 files changed
+380
-82
No files matched your search
@@ -6,7 +6,7 @@ use std::{
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use anyhow::{Context as _, Result};
|
||||
use anyhow::Result;
|
||||
use cef::{args::Args, *};
|
||||
use futures::{FutureExt as _, StreamExt as _, channel::mpsc};
|
||||
use gpui::{App, AsyncApp, Global, WeakEntity};
|
||||
@@ -14,7 +14,9 @@ use gpui::{App, AsyncApp, Global, WeakEntity};
|
||||
use crate::{handlers, surface::Shared, view::WebView};
|
||||
|
||||
/// The framework beside the app, else the fetched one.
|
||||
#[cfg(target_os = "macos")]
|
||||
fn framework_dir() -> Result<PathBuf> {
|
||||
use anyhow::Context as _;
|
||||
const NAME: &str = "Chromium Embedded Framework.framework";
|
||||
let exe = std::env::current_exe()?;
|
||||
let mut places: Vec<PathBuf> = Vec::new();
|
||||
@@ -39,17 +41,12 @@ fn framework_dir() -> Result<PathBuf> {
|
||||
|
||||
static LOADED: OnceLock<std::result::Result<PathBuf, String>> = OnceLock::new();
|
||||
|
||||
/// Loads the framework once; every process needs it first.
|
||||
/// Loads CEF once and sets its API version.
|
||||
fn load() -> Result<PathBuf> {
|
||||
LOADED
|
||||
.get_or_init(|| {
|
||||
let dir = framework_dir().map_err(|error| format!("{error:#}"))?;
|
||||
let library = dir.join("Chromium Embedded Framework");
|
||||
let name = std::ffi::CString::new(library.as_os_str().as_encoded_bytes())
|
||||
.map_err(|error| error.to_string())?;
|
||||
if unsafe { load_library(Some(&*name.as_ptr().cast())) } != 1 {
|
||||
return Err(format!("CEF did not load from {}", library.display()));
|
||||
}
|
||||
let dir = open()?;
|
||||
// Versioned calls exit the process until this runs.
|
||||
let _ = api_hash(sys::CEF_API_VERSION_LAST, 0);
|
||||
log::info!("web: CEF loaded from {}", dir.display());
|
||||
Ok(dir)
|
||||
@@ -58,6 +55,51 @@ fn load() -> Result<PathBuf> {
|
||||
.map_err(anyhow::Error::msg)
|
||||
}
|
||||
|
||||
/// Loads the framework; its folder.
|
||||
#[cfg(target_os = "macos")]
|
||||
fn open() -> std::result::Result<PathBuf, String> {
|
||||
let dir = framework_dir().map_err(|error| format!("{error:#}"))?;
|
||||
let library = dir.join("Chromium Embedded Framework");
|
||||
let name = std::ffi::CString::new(library.as_os_str().as_encoded_bytes()).map_err(|error| error.to_string())?;
|
||||
if unsafe { load_library(Some(&*name.as_ptr().cast())) } != 1 {
|
||||
return Err(format!("CEF did not load from {}", library.display()));
|
||||
}
|
||||
Ok(dir)
|
||||
}
|
||||
|
||||
/// The folder of the libcef.dll this process linked.
|
||||
#[cfg(windows)]
|
||||
fn open() -> std::result::Result<PathBuf, String> {
|
||||
use std::os::windows::ffi::OsStringExt as _;
|
||||
use windows_sys::Win32::System::LibraryLoader::{GetModuleFileNameW, GetModuleHandleW};
|
||||
let name: Vec<u16> = "libcef.dll".encode_utf16().chain([0]).collect();
|
||||
let module = unsafe { GetModuleHandleW(name.as_ptr()) };
|
||||
if module.is_null() {
|
||||
return Err(format!("libcef.dll is not loaded: {}", std::io::Error::last_os_error()));
|
||||
}
|
||||
let mut path = vec![0u16; 32_768];
|
||||
let len = unsafe { GetModuleFileNameW(module, path.as_mut_ptr(), path.len() as u32) } as usize;
|
||||
if len == 0 || len == path.len() {
|
||||
return Err(format!("libcef.dll has no path: {}", std::io::Error::last_os_error()));
|
||||
}
|
||||
let file = PathBuf::from(std::ffi::OsString::from_wide(&path[..len]));
|
||||
file.parent().map(Path::to_path_buf).ok_or_else(|| format!("{} has no folder", file.display()))
|
||||
}
|
||||
|
||||
/// The folder of the libcef.so this process linked.
|
||||
#[cfg(target_os = "linux")]
|
||||
fn open() -> std::result::Result<PathBuf, String> {
|
||||
use std::os::unix::ffi::OsStrExt as _;
|
||||
let mut info: libc::Dl_info = unsafe { std::mem::zeroed() };
|
||||
let entry = sys::cef_api_hash as *const libc::c_void;
|
||||
if unsafe { libc::dladdr(entry, &mut info) } == 0 || info.dli_fname.is_null() {
|
||||
return Err("libcef.so is not loaded".into());
|
||||
}
|
||||
let name = unsafe { std::ffi::CStr::from_ptr(info.dli_fname) };
|
||||
let file = PathBuf::from(std::ffi::OsStr::from_bytes(name.to_bytes()));
|
||||
file.parent().map(Path::to_path_buf).ok_or_else(|| format!("{} has no folder", file.display()))
|
||||
}
|
||||
|
||||
/// In a CEF helper process, runs it to its end.
|
||||
pub fn subprocess() -> Option<i32> {
|
||||
// CEF's own parser needs the framework; plain args do not.
|
||||
@@ -185,10 +227,11 @@ pub fn start(data: &Path, cx: &mut App) -> Result<()> {
|
||||
crate::mac::adopt_app_protocol()?;
|
||||
let exe = std::env::current_exe()?;
|
||||
// Bundled, CEF finds its helper apps; else this binary helps.
|
||||
let bundled = framework.parent().is_some_and(|dir| {
|
||||
let name = exe.file_name().unwrap_or_default().to_string_lossy();
|
||||
dir.join(format!("{name} Helper.app")).is_dir()
|
||||
});
|
||||
let bundled = cfg!(target_os = "macos")
|
||||
&& framework.parent().is_some_and(|dir| {
|
||||
let name = exe.file_name().unwrap_or_default().to_string_lossy();
|
||||
dir.join(format!("{name} Helper.app")).is_dir()
|
||||
});
|
||||
let helper = if bundled {
|
||||
String::new()
|
||||
} else {
|
||||
@@ -201,7 +244,12 @@ pub fn start(data: &Path, cx: &mut App) -> Result<()> {
|
||||
external_message_pump: true as _,
|
||||
no_sandbox: true as _,
|
||||
browser_subprocess_path: helper.as_str().into(),
|
||||
#[cfg(target_os = "macos")]
|
||||
framework_dir_path: framework.to_string_lossy().as_ref().into(),
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
resources_dir_path: framework.to_string_lossy().as_ref().into(),
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
locales_dir_path: framework.join("locales").to_string_lossy().as_ref().into(),
|
||||
root_cache_path: cache.to_string_lossy().as_ref().into(),
|
||||
persist_session_cookies: false as _,
|
||||
log_severity: LogSeverity::WARNING,
|
||||
@@ -267,6 +315,7 @@ fn stop() {
|
||||
}
|
||||
|
||||
/// Why CEF would refuse this cache: another app holds it.
|
||||
#[cfg(unix)]
|
||||
fn held(cache: &Path) -> Option<String> {
|
||||
let lock = std::fs::read_link(cache.join("SingletonLock")).ok()?;
|
||||
let lock = lock.to_string_lossy();
|
||||
@@ -275,3 +324,21 @@ fn held(cache: &Path) -> Option<String> {
|
||||
let alive = unsafe { libc::kill(pid, 0) } == 0;
|
||||
alive.then(|| format!("another copy (process {pid}) is using {}", cache.display()))
|
||||
}
|
||||
|
||||
/// Chromium's Windows lock names no process.
|
||||
#[cfg(windows)]
|
||||
fn held(_cache: &Path) -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn cef_loads_with_its_api_version() {
|
||||
let dir = super::load().expect("CEF loads");
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
assert!(dir.join("locales").is_dir(), "{} holds CEF's files", dir.display());
|
||||
// Without the version this call exits the process.
|
||||
assert!(cef::command_line_create().is_some());
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user