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 {}
fn nuget_package_dir(package_name: &str) -> Option<PathBuf> {
let nuget_path = match env::var("USERPROFILE") {
Err(e) => match env::var("NUGET_PATH") {
Ok(string) => PathBuf::from(string),
Err(e) => {
panic!("Couldn't find the Nuget path, please set environment variable NUGET_PATH.")
}
},
Ok(string) => PathBuf::from(format!("{}\\.nuget\\packages\\", string)),
};
#[cfg(windows)]
{
let package_path = nuget_path.join(package_name);
for path in fs::read_dir(package_path).expect("package not found") {
if let Ok(entry) = path {
return Some(entry.path());
}
}
}
#[cfg(not(windows))]
{
for path in fs::read_dir(nuget_path).expect("package not found") {
if let Ok(entry) = path {
if let Some(name) = entry.path().file_name() {
if name.to_string_lossy().starts_with(package_name) {
return Some(entry.path());
}
}
}
}
}
None
}
fn rerun_if_directory_changed<P>(_path: P)
where
P: Into<PathBuf>,
{
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");
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(); let std_flag = if cfg!(feature = "cef") || cfg!(feature = "edge") {
if target.contains("msvc") {
"/std:c++17"
} else {
"-std=c++17"
}
} else {
if target.contains("msvc") {
"/std:c11"
} else {
"-std=c11"
}
};
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/string.h")
.header("src/window.h");
if target.contains("windows") {
bgbuilder = bgbuilder.clang_arg("-DBW_WIN32");
if target.contains("msvc") {
build.flag("/MT");
}
build
.file("src/win32.c")
.file("src/application/win32.c")
.file("src/window/win32.c")
.define("BW_WIN32", None)
.define("_CRT_SECURE_NO_WARNINGS", None);
build_se
.define("BW_WIN32", None)
.define("_CRT_SECURE_NO_WARNINGS", None);
} else if target.contains("macos") {
bgbuilder = bgbuilder.clang_arg("-DBW_MACOS");
build.define("BW_MACOS", None);
build_se.define("BW_MACOS", None);
}
if cfg!(feature = "gtk") {
bgbuilder = bgbuilder.clang_arg("-DBW_GTK");
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) => {
for inc in &lib.include_paths {
build.include(inc);
}
}
}
}
else if cfg!(feature = "cef") && !target.contains("windows") {
bgbuilder = bgbuilder.clang_arg("-DBW_CEF_WINDOW");
build
.file("src/application/cef_window.cpp")
.file("src/window/cef.cpp")
.define("BW_CEF_WINDOW", None);
build_se.define("BW_CEF_WINDOW", None);
}
if cfg!(feature = "cef") {
bgbuilder = bgbuilder.clang_arg("-DBW_CEF");
build.flag_if_supported("-Wno-unused-parameter"); build_se.flag_if_supported("-Wno-unused-parameter");
let mut build_se_lib_args = Vec::<OsString>::new();
match env::var("CEF_PATH") {
Err(e) => {
match e {
env::VarError::NotPresent => {
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);
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=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=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());
}
match pkg_config::Config::new()
.arg("--cflags")
.arg("--libs")
.probe("x11")
{
Err(_) => {} Ok(result) => {
for inc in &result.include_paths {
build.include(inc);
build_se.include(inc);
}
}
}
}
}
build
.file("src/application/cef.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")
.define("BW_CEF", None)
.cpp(true);
build_se.define("BW_CEF", 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");
}
}
else if cfg!(feature = "edge") {
let webview_dir = nuget_package_dir("Microsoft.Web.WebView2")
.expect("Couldn't find Microsoft.Web.WebView2 Nuget package.");
let include_dir = webview_dir.join(PathBuf::from("build/native/include"));
let lib_dir = if cfg!(target_arch = "x86") {
webview_dir.join(PathBuf::from("build/native/x86"))
} else if cfg!(target_arch = "x86_64") {
webview_dir.join(PathBuf::from("build/native/x64"))
} else if cfg!(target_arch = "aarch64") {
webview_dir.join(PathBuf::from("build/native/arm64"))
} else {
panic!("Unsupported target architecture for Edge WebView2 framework.");
};
println!("cargo:rustc-link-search={}", lib_dir.display());
if cfg!(feature = "bundled") {
println!("cargo:rustc-link-lib=static=WebView2LoaderStatic");
} else {
println!("cargo:rustc-link-lib=static=WebView2Loader.dll");
println!("cargo:rustc-link-lib=dylib=WebView2Loader");
}
bgbuilder = bgbuilder.clang_arg("-DBW_EDGE");
if Path::new("/usr/share/mingw-w64/include/").exists() {
build.include("/usr/share/mingw-w64/include/");
build.include(PathBuf::from(
env::var("CARGO_MANIFEST_DIR").unwrap() + "/win32/include",
));
}
build
.define("BW_EDGE", None)
.include(include_dir)
.file("src/browser_window/edge2.cpp")
.file("src/cookie/unsupported.cpp")
.cpp(true);
}
build
.file("src/application/common.c")
.file("src/browser_window/common.c")
.file("src/err.c")
.file("src/string.c")
.file("src/window/common.c")
.flag(std_flag)
.compile("browser-window-c");
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!");
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<String> { Some("c".to_owned() + item_name) }
}