use std::{
fs::OpenOptions,
io::{Cursor, Read, Seek, SeekFrom, Write},
path::Path,
};
use regex::Regex;
fn main() {
download_cef();
compile_lib();
}
fn download_cef() {
let out_dir = std::env::var("OUT_DIR").unwrap();
let lib_dir = Path::new(out_dir.as_str()).join("libcef");
if lib_dir.exists() {
return;
}
let opt_level = "Debug".to_string();
let target_os = std::env::var("CARGO_CFG_TARGET_OS");
let target_os = target_os.as_ref().map(|x| &**x);
let version = "119.4.7+g55e15c8+chromium-119.0.6045.199";
let mut mappings: Vec<(Regex, String)> = Vec::new();
let maps = match target_os {
Ok("macos") => vec![
(
format!(
r"^[^/]+/{}/(Chromium Embedded Framework\.framework/.+)$",
opt_level
),
"${1}",
),
(r"^[^/]+/include/(.+\.h)".to_owned(), "include/${1}"),
(r#"^[^/]+/libcef_dll/(.+)"#.to_owned(), "libcef_dll/${1}"),
(r#"^[^/]+/cmake/(.+)"#.to_owned(), "cmake/${1}"),
(format!(r"^[^/]+/{}/(cef_sandbox.a)$", opt_level), "${1}"),
],
Ok("windows") => vec![
(
format!(r"^[^/]+/{}/([^/]+\.(lib|dll|bin))$", opt_level),
"${1}",
),
(
format!(r"^[^/]+/{}/(swiftshader/[^/]+\.dll)$", opt_level),
"${1}",
),
(r"^[^/]+/Resources/icudtl\.dat$".to_owned(), "icudtl.dat"),
(
r"^[^/]+/Resources/((locales/)?[^/]+\.pak)$".to_owned(),
"${1}",
),
],
_ => panic!("unsupported platform"),
};
mappings.extend(maps.into_iter().map(|(src, dest)| {
(
Regex::new(&src).unwrap(),
format!("{}/{}", lib_dir.display(), dest),
)
}));
match target_os {
Ok("macos") => {
let reader: Box<dyn Read> = {
let link = format!(
"https://cef-builds.spotifycdn.com/cef_binary_{version}_macosarm64.tar.bz2"
);
let response = ureq::get(link.as_str()).call();
let mut buf = Vec::new();
let _ = response.into_reader().read_to_end(&mut buf); Box::new(Cursor::new(buf))
};
let tar_file = bzip2::read::BzDecoder::new(reader);
let mut archive = tar::Archive::new(tar_file);
for mut entry in archive.entries().unwrap().flatten() {
if let Ok(path) = entry.path() {
let path_string = path.to_string_lossy().to_string();
for (regex, destination) in mappings.iter() {
if regex.is_match(&path_string) {
let filename = regex
.replace(&path_string, destination.as_str())
.to_string();
let path = std::path::Path::new(&filename);
if let Some(folder) = path.parent() {
let _ = std::fs::create_dir_all(folder);
}
if !path.exists() {
let _ = entry.unpack(&filename); }
break;
}
}
}
}
}
Ok("windows") => {
let reader: Box<dyn Read> = {
let link = format!(
"https://cef-builds.spotifycdn.com/cef_binary_{version}_windows64.tar.bz2"
);
let response = ureq::get(link.as_str()).call();
let mut buf = Vec::new();
let _ = response.into_reader().read_to_end(&mut buf); Box::new(Cursor::new(buf))
};
let tar_file = bzip2::read::BzDecoder::new(reader);
let mut archive = tar::Archive::new(tar_file);
for mut entry in archive.entries().unwrap().flatten() {
if let Ok(path) = entry.path() {
let path_string = path.to_string_lossy().to_string();
for (regex, destination) in mappings.iter() {
if regex.is_match(&path_string) {
let filename = regex
.replace(&path_string, destination.as_str())
.to_string();
let path = std::path::Path::new(&filename);
if let Some(folder) = path.parent() {
let _ = std::fs::create_dir_all(folder);
}
if !path.exists() {
let _ = entry.unpack(&filename); }
break;
}
}
}
}
}
_ => panic!("unsupported platform"),
}
}
fn compile_lib() {
let target_os = std::env::var("CARGO_CFG_TARGET_OS");
let target_os = target_os.as_ref().map(|x| &**x);
let out_dir = std::env::var("OUT_DIR").unwrap();
let lib_dir = Path::new(out_dir.as_str()).join("libcef");
match target_os {
Ok("macos") => {
remove_find_package_dep(&lib_dir.join("cmake").join("cef_macros.cmake"));
remove_find_package_dep(&lib_dir.join("cmake").join("cef_variables.cmake"));
let cmake_file = "
cmake_minimum_required(VERSION 3.0)
project(dll_wrapper)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} \"./\")
set(CEF_USE_SANDBOX TRUE)
include(\"./cmake/cef_macros.cmake\")
include(\"./cmake/cef_variables.cmake\")
include_directories(
\"./include/\"
\"./libcef_dll/\"
\".\"
)
add_subdirectory(\"./libcef_dll/\")
install(TARGETS libcef_dll_wrapper DESTINATION .)
configure_file(cef_sandbox.a ../libcef_sandbox.a COPYONLY)
"
.to_string();
std::fs::write(lib_dir.join("CMakeLists.txt"), cmake_file).unwrap();
let dst = cmake::Config::new(&lib_dir).generator("Ninja").build();
println!(
"cargo:warning=compiled CEF DLL wrapper to {}",
dst.display()
);
println!("cargo:rustc-link-search=native={}", dst.display());
println!("cargo:rustc-link-lib=static=cef_dll_wrapper");
println!("cargo:rustc-link-lib=static=cef_sandbox");
println!("cargo:rustc-link-lib=sandbox");
let framework_dir = lib_dir
.join("Chromium Embedded Framework.framework")
.canonicalize()
.unwrap();
assert!(framework_dir.exists());
println!(
"cargo:rustc-env=CEF_SYS_FRAMEWORK_PATH={}",
framework_dir.display()
);
}
Ok("windows") => {
println!("cargo:rustc-link-lib=libcef");
println!("cargo:rustc-link-search={}", lib_dir.display());
}
_ => panic!("unsupported OS"),
}
}
fn remove_find_package_dep(path: &Path) {
let mut cmake_macros_file = OpenOptions::new()
.read(true)
.write(true)
.open(path)
.expect("cef_macros.cmake not found in expected location");
let mut cmake_macros_str = String::new();
cmake_macros_file
.read_to_string(&mut cmake_macros_str)
.unwrap();
#[cfg(target_os = "windows")]
{
cmake_macros_str = cmake_macros_str.replace("\r\n", "\n");
}
let new_str = cmake_macros_str.replace(CEF_MACROS_REMOVE, "");
cmake_macros_str = new_str;
#[cfg(target_os = "windows")]
{
cmake_macros_str = cmake_macros_str.replace('\n', "\r\n");
}
cmake_macros_file.seek(SeekFrom::Start(0)).unwrap();
cmake_macros_file
.set_len(cmake_macros_str.len() as u64)
.unwrap();
cmake_macros_file
.write_all(cmake_macros_str.as_bytes())
.unwrap();
}
const CEF_MACROS_REMOVE: &str = "if(NOT DEFINED _CEF_ROOT_EXPLICIT)
message(FATAL_ERROR \"Use find_package(CEF) to load this file.\")
endif()";