extern crate bindgen;
extern crate cc;
extern crate pkg_config;
use std::{
env,
ffi::{OsStr, OsString},
fs,
path::{Path, PathBuf},
process::Command,
};
#[derive(Debug)]
struct BwBindgenCallbacks {}
/// Prints all compiler commands to rerun if any file has changed within the
/// given directory or a subdictory thereof.
fn rerun_if_directory_changed
(_path: P)
where
P: Into,
{
let path: PathBuf = _path.into();
let dir_iterator = match fs::read_dir(&path) {
Err(e) => panic!("Unable to read directory: {}", e),
Ok(iterator) => iterator,
};
for sub_path in dir_iterator {
match sub_path {
Err(e) => panic!(
"Unable to read directory entry for dir {}: {}",
path.as_os_str().to_str().unwrap(),
e
),
Ok(entry) =>
if entry.path().is_dir() {
rerun_if_directory_changed(entry.path());
} else {
println!(
"cargo:rerun-if-changed={}",
entry.path().as_os_str().to_str().unwrap()
);
},
}
}
}
fn to_executable_args<'a>(args: &'a [OsString]) -> Vec<&'a OsStr> {
let ignore_args: [OsString; 1] = ["-fPIC".into()];
let mut new_args = Vec::with_capacity(args.len());
for arg in args {
if !(ignore_args.contains(arg)) {
new_args.push(arg.as_os_str());
}
}
new_args
}
fn to_executable_compiler_command(
tool: cc::Tool, library_args: &[OsString], source_file: &str, out_file: &Path,
) -> Command {
let mut cmd = Command::new(tool.path());
cmd.args(to_executable_args(tool.args()));
if tool.is_like_gnu() || tool.is_like_clang() {
let extra_args: [OsString; 5] = [
"-o".into(),
out_file.as_os_str().into(),
source_file.into(),
"-lcef_dll_wrapper".into(),
"-lcef".into(),
];
cmd.args(&extra_args);
} else if tool.is_like_msvc() {
let extra_args: [OsString; 5] = [
format!("/Fe:{}", out_file.to_str().unwrap()).into(),
source_file.into(),
"/link".into(),
"libcef_dll_wrapper.lib".into(),
"libcef.lib".into(),
];
cmd.args(&extra_args);
} else {
panic!("Compiler type not recognized for seperate executable.")
}
cmd.args(library_args);
return cmd;
}
fn main() {
println!("cargo:rerun-if-env-changed=CEF_PATH");
rerun_if_directory_changed("src");
let target = env::var("TARGET").unwrap();
let out_path = PathBuf::from(
env::var("OUT_DIR").expect("Unable to get output directory for C/C++ code base crate"),
);
let target_dir = out_path
.parent()
.unwrap()
.parent()
.unwrap()
.parent()
.unwrap();
let backup_file = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap() + "/bindgen_backup.rs");
// Workaround for docs.rs
// Docs.rs is not able to compile the C/C++ source files because it doesn't have
// the win32 and cef header files available in their docker system in which they
// test-build.
if let Ok(_) = env::var("DOCS_RS") {
fs::copy(&backup_file, out_path.join("c_bindings.rs"))
.expect("Unable to copy backup c bindings");
return;
}
let mut build = cc::Build::new();
let mut build_se = cc::Build::new(); // For seperate executable
let std_flag = if cfg!(feature = "cef") {
if target.contains("msvc") {
"/std:c++17"
} else {
"-std=c++17"
}
} else {
if target.contains("msvc") {
"/std:c11"
} else {
"-std=c11"
}
};
if target.contains("windows") {
build.define("BW_WINDOWS", None);
build_se.define("BW_WINDOWS", None);
}
if target.contains("msvc") {
build.flag("/MT");
}
/**************************************
* C header files for bindgen
************************* */
let mut bgbuilder = bindgen::Builder::default()
.parse_callbacks(Box::new(BwBindgenCallbacks {}))
.clang_arg("-DBW_BINDGEN")
.header("src/application.h")
.header("src/browser_window.h")
.header("src/cookie.h")
.header("src/common.h")
.header("src/err.h")
.header("src/event.h")
.header("src/string.h")
.header("src/window.h");
// When opting for using GTK:
if cfg!(feature = "gtk") {
bgbuilder = bgbuilder.clang_arg("-DBW_GTK");
// GTK source files
build
.file("src/application/gtk.c")
.file("src/window/gtk.c")
.define("BW_GTK", None);
build_se.define("BW_GTK", None);
match pkg_config::Config::new()
.atleast_version("3.0")
.arg("--cflags")
.probe("gtk+-3.0")
{
Err(e) => panic!("Unable to find GTK 3 development files: {}", e),
Ok(lib) => {
// Manually add GTK includes to compiler
for inc in &lib.include_paths {
build.include(inc);
}
}
}
}
/*******************************************
* The Browser Engine (CEF3) source files *
************************************* */
else if cfg!(feature = "cef") {
bgbuilder = bgbuilder.clang_arg("-DBW_CEF").clang_arg("-DBW_CEF_WINDOW");
build.flag_if_supported("-Wno-unused-parameter"); // CEF's header files produce a lot of unused parameters warnings.
build_se.flag_if_supported("-Wno-unused-parameter");
let mut build_se_lib_args = Vec::::new();
match env::var("CEF_PATH") {
Err(e) => {
match e {
env::VarError::NotPresent => {
// Disable checking CEF_PATH for the docs.rs compiler, it is not on their
// system anyway.
if let Err(_) = env::var("DOCS_RS") {
panic!("Environment variable CEF_PATH is not set! This is needed by Browser Window to find CEF's development files. See https://github.com/bamilab/browser-window/tree/master/docs/getting-started for more information.")
}
}
other => panic!("Unable to use CEF_PATH: {}", other),
}
}
Ok(cef_path) => {
build.include(&cef_path);
build_se.include(&cef_path);
// Link with CEF
println!("cargo:rustc-link-search={}/libcef_dll_wrapper", &cef_path);
println!("cargo:rustc-link-search={}/Release", &cef_path);
if target.contains("msvc") {
build_se.flag("/MT");
println!("cargo:rustc-link-search={}", &cef_path);
println!(
"cargo:rustc-link-search={}/libcef_dll_wrapper/Release",
&cef_path
);
println!("cargo:rustc-link-lib=static=libcef_dll_wrapper");
println!("cargo:rustc-link-lib=static=libcef");
println!("cargo:rustc-link-lib=dylib=libcef");
build_se_lib_args.push(format!("/LIBPATH:{}", &cef_path).into());
build_se_lib_args
.push(format!("/LIBPATH:{}/libcef_dll_wrapper", &cef_path).into());
build_se_lib_args
.push(format!("/LIBPATH:{}/libcef_dll_wrapper/Release", &cef_path).into());
build_se_lib_args.push(format!("/LIBPATH:{}/Release", &cef_path).into());
} else {
println!("cargo:rustc-link-lib=static=cef_dll_wrapper");
println!("cargo:rustc-link-lib=static=cef");
println!("cargo:rustc-link-lib=dylib=cef");
build_se_lib_args.push(format!("-L{}/libcef_dll_wrapper", &cef_path).into());
build_se_lib_args.push(format!("-L{}/Release", &cef_path).into());
}
// Add X flags to compiler
match pkg_config::Config::new()
.arg("--cflags")
.arg("--libs")
.probe("x11")
{
Err(_) => {} // CEF doesn't always use X...
Ok(result) => {
// Includes
for inc in &result.include_paths {
build.include(inc);
build_se.include(inc);
}
}
}
}
}
// Source files
build
.file("src/application/cef.cpp")
.file("src/application/cef_window.cpp")
.file("src/browser_window/cef.cpp")
.file("src/cookie/cef.cpp")
.file("src/cef/bw_handle_map.cpp")
.file("src/cef/client_handler.cpp")
.file("src/cef/exception.cpp")
.file("src/cef/util.cpp")
.file("src/window/cef.cpp")
.define("BW_CEF", None)
.define("BW_CEF_WINDOW", None)
.flag(std_flag)
.cpp(true);
// Build the seperate executable and copy it to target/debug (or target/release)
build_se.define("BW_CEF", None).define("BW_CEF_WINDOW", None).cpp(true).flag(std_flag);
let se_comp = build_se.get_compiler();
for var in se_comp.env() {
env::set_var(&var.0, &var.1);
}
let se_file = if !target.contains("windows") {
out_path.join("browser-window-se")
} else {
out_path.join("browser-window-se.exe")
};
let mut se_cmd = to_executable_compiler_command(
se_comp,
&*build_se_lib_args,
"src/cef/seperate_executable.cpp",
&se_file,
);
let status = se_cmd
.status()
.expect("unable to get status of seperate executable compiler");
assert!(
status.code().unwrap() == 0,
"Seperate executable compiler failed with error code {}.",
status.code().unwrap()
);
if !target.contains("windows") {
fs::copy(se_file, target_dir.join("browser-window-se"))
.expect("unable to copy seperate executable");
} else {
fs::copy(se_file, target_dir.join("browser-window-se.exe"))
.expect("unable to copy seperate executable");
}
}
/****************************************
* Microsoft Edge WebView2 source files
************************************** */
else if cfg!(feature = "edge2") {
bgbuilder = bgbuilder.clang_arg("-DBW_WIN32").clang_arg("-DBW_EDGE2");
// Win32 API
build
.file("src/win32.c")
.file("src/application/win32.c")
.file("src/window/win32.c")
.define("BW_WIN32", None)
.define("BW_EDGE2", None)
.define("_CRT_SECURE_NO_WARNINGS", None); // Disable sprintf_s warnings. sprintf_s tends to cause segfaults anyway...
// Add the MinGW header files and libraries when available
if Path::new("/usr/share/mingw-w64/include/").exists() {
//bgbuilder = bgbuilder.clang_arg("-I/lib/gcc/x86_64-w64-mingw32/12-win32/include");
build.include(PathBuf::from(
env::var("CARGO_MANIFEST_DIR").unwrap() + "/win32/include",
));
bgbuilder = bgbuilder
//.clang_arg("--target=x86_64-pc-windows-gnu")
.clang_arg("-I/usr/lib/gcc/x86_64-w64-mingw32/12-win32/include");
}
bgbuilder = bgbuilder.clang_args(&[
"-DBW_WIN32",
"-DBW_EDGE2"
]);
build
.define("BW_WIN32", None)
.define("BW_EDGE2", None)
//.include(include_dir)
.file("src/application/edge2.c")
.file("src/application/win32.c")
.file("src/browser_window/edge2.c")
.file("src/window/win32.c")
.file("src/cookie/unsupported.c")
.file("src/win32.c");
}
else {
build.file("src/application/other.c");
}
/**************************************
* All other source files
************************* */
build
.file("src/application/common.c")
.file("src/browser_window/common.c")
.file("src/err.c")
.file("src/event.c")
.file("src/string.c")
.file("src/window/common.c")
//.flag(std_flag)
.compile("browser-window-c");
// Let bindgen generate the bindings
bgbuilder
.generate()
.expect("Unable to generate FFI bindings!")
.write_to_file(out_path.join("c_bindings.rs"))
.expect("Unable to write FFI bindings to file!");
// Update bindgen backup
if let Ok(_) = env::var("UPDATE_BINDGEN_BACKUP") {
fs::copy(out_path.join("c_bindings.rs"), backup_file).expect("Unable to copy backup file");
}
}
impl bindgen::callbacks::ParseCallbacks for BwBindgenCallbacks {
fn item_name(&self, item_name: &str) -> Option { Some("c".to_owned() + item_name) }
}