extern crate cc;
use std::{
env, fs,
process::{Command, Stdio},
};
use cc::{Build, Tool};
fn print_env(compiler: &Tool) {
eprintln!("Environment variables:");
for (k, v) in env::vars() {
eprintln!("{}={}", k, v);
}
eprintln!("\nCompiler:\n{}", compiler.path().to_string_lossy());
eprintln!("\nCompiler arguments:");
for arg in compiler.args().iter() {
eprintln!("{}", arg.to_string_lossy());
}
eprintln!("\nCompiler environment variables:");
for (k, v) in compiler.env().iter() {
eprintln!("{}={}", k.to_string_lossy(), v.to_string_lossy());
}
}
fn run_generator(compiler: &Tool) -> String {
let out_dir = env::var("OUT_DIR").unwrap();
let mut compiler_cmd = compiler.to_command();
compiler_cmd
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.arg("-Isrc/dokany/dokan")
.arg("-Isrc/dokany/sys");
if compiler.is_like_msvc() {
compiler_cmd
.arg(format!("/Fo{}/", out_dir))
.arg("src/generate_version.c")
.arg("/link")
.arg(format!("/OUT:{}/generate_version.exe", out_dir))
} else {
compiler_cmd
.arg(format!("-o{}/generate_version.exe", out_dir))
.arg("src/generate_version.c")
};
assert!(compiler_cmd.output().unwrap().status.success());
let generate_output = Command::new(format!("{}/generate_version.exe", out_dir))
.current_dir(&out_dir)
.output()
.unwrap();
assert!(generate_output.status.success());
println!("cargo:rerun-if-changed=src/generate_version.c");
String::from_utf8(fs::read(format!("{}/version.txt", out_dir)).unwrap()).unwrap()
}
fn check_dokan_env(version_major: &str) -> bool {
let arch = match env::var("CARGO_CFG_TARGET_ARCH").unwrap().as_ref() {
"x86" => "x86",
"x86_64" => "x64",
_ => panic!("Unsupported target architecture!"),
};
let env_name = format!("DokanLibrary{}_LibraryPath_{}", version_major, arch);
println!("cargo:rerun-if-env-changed={}", env_name);
if let Ok(lib_path) = env::var(&env_name) {
println!("cargo:rustc-link-search=native={}", lib_path);
true
} else {
println!(
"cargo:warning=Environment variable {} not found, building Dokan from source.",
env_name
);
false
}
}
fn build_dokan(compiler: &Tool, version_major: &str) {
let out_dir = env::var("OUT_DIR").unwrap();
let src = fs::read_dir("src/dokany/dokan")
.unwrap()
.map(|d| d.unwrap().path())
.filter(|p| {
if let Some(ext) = p.extension() {
ext == "c"
} else {
false
}
});
let dll_name = format!("dokan{}.dll", version_major);
let dll_path = format!("{}/{}", out_dir, dll_name);
let mut compiler_cmd = compiler.to_command();
compiler_cmd
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.arg("-D_WINDLL")
.arg("-D_EXPORTING")
.arg("-DUNICODE")
.arg("-D_UNICODE")
.arg("-DWINVER=0x0A00")
.arg("-D_WIN32_WINNT=0x0A00")
.arg("-Isrc/dokany/sys");
if compiler.is_like_msvc() {
compiler_cmd
.arg(format!("/Fo{}/", out_dir))
.args(src)
.arg("/link")
.arg("/DLL")
.arg("/DEF:src/dokany/dokan/dokan.def")
.arg(format!("/OUT:{}", dll_path))
.arg(format!("/IMPLIB:{}/dokan{}.lib", out_dir, version_major))
.arg("advapi32.lib")
.arg("shell32.lib")
.arg("user32.lib")
} else {
compiler_cmd
.arg("-shared")
.arg(format!("-o{}", dll_path))
.args(src)
.arg(format!(
"-Wl,--out-implib,{}/dokan{}.lib",
out_dir, version_major
))
};
assert!(compiler_cmd.output().unwrap().status.success());
if let Ok(output_path) = env::var("DOKAN_DLL_OUTPUT_PATH") {
fs::copy(dll_path, format!("{}/{}", output_path, dll_name)).unwrap();
}
println!("cargo:rerun-if-env-changed=DOKAN_DLL_OUTPUT_PATH");
println!("cargo:rustc-link-search=native={}", out_dir);
println!("cargo:rerun-if-changed=src/dokany");
}
fn main() {
let compiler = Build::new().get_compiler();
print_env(&compiler);
let version = run_generator(&compiler);
assert_eq!(
format!("dokan{}", version),
env::var("CARGO_PKG_VERSION")
.unwrap()
.split('+')
.last()
.unwrap(),
"Mismatch detected between crate version and bundled Dokan source version.",
);
let version_major = &version[..1];
println!("cargo:rustc-link-lib=dylib=dokan{}", version_major);
if !check_dokan_env(version_major) {
build_dokan(&compiler, version_major);
};
}