#![allow(private_bounds)]
#[cfg(any(not(target_os = "windows"), all(target_os = "windows", target_env = "msvc")))]
extern crate cc;
#[cfg(not(target_os = "windows"))]
extern crate memchr;
#[cfg(all(target_os = "windows", target_env = "msvc"))]
extern crate vswhom;
#[cfg(all(target_os = "windows", target_env = "msvc"))]
extern crate winreg;
extern crate rustc_version;
extern crate toml;
#[cfg(not(target_os = "windows"))]
mod non_windows;
#[cfg(all(target_os = "windows", target_env = "msvc"))]
mod windows_msvc;
#[cfg(all(target_os = "windows", not(target_env = "msvc")))]
mod windows_not_msvc;
#[cfg(not(target_os = "windows"))]
use self::non_windows::*;
#[cfg(all(target_os = "windows", target_env = "msvc"))]
use self::windows_msvc::*;
#[cfg(all(target_os = "windows", not(target_env = "msvc")))]
use self::windows_not_msvc::*;
use std::{env, fs};
use std::ffi::{OsString, OsStr};
use std::borrow::Cow;
use std::process::Command;
use toml::Table as TomlTable;
use std::fmt::{self, Display};
use std::path::{Path, PathBuf};
pub const NONE: &[&OsStr] = &[];
#[derive(PartialEq, Eq, Debug)] struct ParameterBundle<Ms: AsRef<OsStr>, Mi: IntoIterator<Item = Ms>, Is: AsRef<OsStr>, Ii: IntoIterator<Item = Is>> {
macros: Mi,
include_dirs: Ii,
}
impl<Ms: AsRef<OsStr>, Mi: IntoIterator<Item = Ms>> From<Mi> for ParameterBundle<Ms, Mi, &'static &'static OsStr, &'static [&'static OsStr]> {
fn from(macros: Mi) -> Self {
ParamsMacros(macros).into()
}
}
pub struct ParamsMacros<Ms: AsRef<OsStr>, Mi: IntoIterator<Item = Ms>>(pub Mi);
impl<Ms: AsRef<OsStr>, Mi: IntoIterator<Item = Ms>> From<ParamsMacros<Ms, Mi>> for ParameterBundle<Ms, Mi, &'static &'static OsStr, &'static [&'static OsStr]> {
fn from(macros: ParamsMacros<Ms, Mi>) -> Self {
ParamsMacrosAndIncludeDirs(macros.0, NONE).into()
}
}
pub struct ParamsIncludeDirs<Is: AsRef<OsStr>, Ii: IntoIterator<Item = Is>>(pub Ii);
impl<Is: AsRef<OsStr>, Ii: IntoIterator<Item = Is>> From<ParamsIncludeDirs<Is, Ii>>
for ParameterBundle<&'static &'static OsStr, &'static [&'static OsStr], Is, Ii> {
fn from(include_dirs: ParamsIncludeDirs<Is, Ii>) -> Self {
ParamsMacrosAndIncludeDirs(NONE, include_dirs.0).into()
}
}
pub struct ParamsMacrosAndIncludeDirs<Ms: AsRef<OsStr>, Mi: IntoIterator<Item = Ms>, Is: AsRef<OsStr>, Ii: IntoIterator<Item = Is>>(pub Mi, pub Ii);
impl<Ms: AsRef<OsStr>, Mi: IntoIterator<Item = Ms>, Is: AsRef<OsStr>, Ii: IntoIterator<Item = Is>> From<ParamsMacrosAndIncludeDirs<Ms, Mi, Is, Ii>>
for ParameterBundle<Ms, Mi, Is, Ii> {
fn from(maid: ParamsMacrosAndIncludeDirs<Ms, Mi, Is, Ii>) -> Self {
Self {
macros: maid.0,
include_dirs: maid.1,
}
}
}
#[cfg(test)]
#[allow(dead_code)]
fn compat_3_0_5() {
use std::collections::BTreeSet;
let _ = compile("", std::iter::empty::<&str>());
let _ = compile("", None::<&str>);
let marcos = &[format!("VERSION_PATCH={}", env!("CARGO_PKG_VERSION_PATCH"))];
let _ = compile("", marcos);
let marcos = vec![format!("VERSION_PATCH={}", env!("CARGO_PKG_VERSION_PATCH"))];
let _ = compile("", marcos);
let _ = compile("", [""]);
let _ = compile("", &[""]);
let _ = compile("", vec![""]);
let _ = compile("", vec![Path::new("gaming=baming")].into_iter().collect::<BTreeSet<_>>());
let _ = compile("", vec![Path::new("gaming=baming").to_owned()].into_iter().collect::<BTreeSet<_>>());
let _ = compile("", [PathBuf::from("gaming=baming")].iter());
let _ = compile("", [PathBuf::from("gaming=baming")].iter().collect::<BTreeSet<_>>());
let _ = compile("", ParamsIncludeDirs(&[Path::new("include_dir")]));
let _ = compile("", ParamsIncludeDirs([PathBuf::from("include_dir")]));
let _ = compile("", ParamsIncludeDirs(vec![Path::new("include_dir1"), Path::new("include_dir2")]));
let _ = compile("", ParamsMacrosAndIncludeDirs(NONE, NONE));
let _ = compile("", ParamsMacrosAndIncludeDirs([""], [""]));
}
#[test]
fn argument_bundle_into() {
assert_eq!(ParameterBundle::from(NONE),
ParameterBundle {
macros: NONE,
include_dirs: NONE,
});
assert_eq!(ParameterBundle::from([""]),
ParameterBundle {
macros: [""],
include_dirs: NONE,
});
assert_eq!(ParameterBundle::from(ParamsMacros(NONE)),
ParameterBundle {
macros: NONE,
include_dirs: NONE,
});
assert_eq!(ParameterBundle::from(ParamsMacros([""])),
ParameterBundle {
macros: [""],
include_dirs: NONE,
});
assert_eq!(ParameterBundle::from(ParamsIncludeDirs(NONE)),
ParameterBundle {
macros: NONE,
include_dirs: NONE,
});
assert_eq!(ParameterBundle::from(ParamsIncludeDirs([""])),
ParameterBundle {
macros: NONE,
include_dirs: [""],
});
assert_eq!(ParameterBundle::from(ParamsMacrosAndIncludeDirs(NONE, NONE)),
ParameterBundle {
macros: NONE,
include_dirs: NONE,
});
assert_eq!(ParameterBundle::from(ParamsMacrosAndIncludeDirs([""], NONE)),
ParameterBundle {
macros: [""],
include_dirs: NONE,
});
assert_eq!(ParameterBundle::from(ParamsMacrosAndIncludeDirs(NONE, [""])),
ParameterBundle {
macros: NONE,
include_dirs: [""],
});
assert_eq!(ParameterBundle::from(ParamsMacrosAndIncludeDirs([""], [""])),
ParameterBundle {
macros: [""],
include_dirs: [""],
});
}
#[must_use]
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum CompilationResult {
NotWindows,
Ok,
NotAttempted(Cow<'static, str>),
Failed(Cow<'static, str>),
}
impl CompilationResult {
pub fn manifest_optional(self) -> Result<(), CompilationResult> {
match self {
CompilationResult::NotWindows |
CompilationResult::Ok |
CompilationResult::NotAttempted(..) => Ok(()),
err @ CompilationResult::Failed(..) => Err(err),
}
}
pub fn manifest_required(self) -> Result<(), CompilationResult> {
match self {
CompilationResult::NotWindows |
CompilationResult::Ok => Ok(()),
err @ CompilationResult::NotAttempted(..) |
err @ CompilationResult::Failed(..) => Err(err),
}
}
}
impl Display for CompilationResult {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.write_str("embed-resource: ")?;
match self {
CompilationResult::NotWindows => f.write_str("not building for windows"),
CompilationResult::Ok => f.write_str("OK"),
CompilationResult::NotAttempted(why) => {
f.write_str("compilation not attempted: ")?;
if !why.contains(' ') {
f.write_str("missing compiler: ")?;
}
f.write_str(why)
}
CompilationResult::Failed(err) => f.write_str(err),
}
}
}
impl std::error::Error for CompilationResult {}
macro_rules! try_compile_impl {
($expr:expr) => {
match $expr {
Result::Ok(val) => val,
Result::Err(err) => return err,
}
};
}
pub fn compile<T: AsRef<Path>,
Ms: AsRef<OsStr>,
Mi: IntoIterator<Item = Ms>,
Is: AsRef<OsStr>,
Ii: IntoIterator<Item = Is>,
P: Into<ParameterBundle<Ms, Mi, Is, Ii>>>(
resource_file: T, parameters: P)
-> CompilationResult {
let (prefix, out_dir, out_file) = try_compile_impl!(compile_impl(resource_file.as_ref(), parameters.into()));
let hasbins = fs::read_to_string("Cargo.toml")
.unwrap_or_else(|err| {
eprintln!("Couldn't read Cargo.toml: {}; assuming src/main.rs or S_ISDIR(src/bin/)", err);
String::new()
})
.parse::<TomlTable>()
.unwrap_or_else(|err| {
eprintln!("Couldn't parse Cargo.toml: {}; assuming src/main.rs or S_ISDIR(src/bin/)", err);
TomlTable::new()
})
.contains_key("bin") || (Path::new("src/main.rs").exists() || Path::new("src/bin").is_dir());
eprintln!("Final verdict: crate has binaries: {}", hasbins);
if hasbins && rustc_version::version().expect("couldn't get rustc version") >= rustc_version::Version::new(1, 50, 0) {
println!("cargo:rustc-link-arg-bins={}", out_file);
} else {
println!("cargo:rustc-link-search=native={}", out_dir);
println!("cargo:rustc-link-lib=dylib={}", prefix);
}
CompilationResult::Ok
}
pub fn compile_for<T: AsRef<Path>,
J: Display,
I: IntoIterator<Item = J>,
Ms: AsRef<OsStr>,
Mi: IntoIterator<Item = Ms>,
Is: AsRef<OsStr>,
Ii: IntoIterator<Item = Is>,
P: Into<ParameterBundle<Ms, Mi, Is, Ii>>>(
resource_file: T, for_bins: I, parameters: P)
-> CompilationResult {
let (_, _, out_file) = try_compile_impl!(compile_impl(resource_file.as_ref(), parameters.into()));
for bin in for_bins {
println!("cargo:rustc-link-arg-bin={}={}", bin, out_file);
}
CompilationResult::Ok
}
pub fn compile_for_tests<T: AsRef<Path>,
Ms: AsRef<OsStr>,
Mi: IntoIterator<Item = Ms>,
Is: AsRef<OsStr>,
Ii: IntoIterator<Item = Is>,
P: Into<ParameterBundle<Ms, Mi, Is, Ii>>>(
resource_file: T, parameters: P)
-> CompilationResult {
let (_, _, out_file) = try_compile_impl!(compile_impl(resource_file.as_ref(), parameters.into()));
println!("cargo:rustc-link-arg-tests={}", out_file);
CompilationResult::Ok
}
pub fn compile_for_benchmarks<T: AsRef<Path>,
Ms: AsRef<OsStr>,
Mi: IntoIterator<Item = Ms>,
Is: AsRef<OsStr>,
Ii: IntoIterator<Item = Is>,
P: Into<ParameterBundle<Ms, Mi, Is, Ii>>>(
resource_file: T, parameters: P)
-> CompilationResult {
let (_, _, out_file) = try_compile_impl!(compile_impl(resource_file.as_ref(), parameters.into()));
println!("cargo:rustc-link-arg-benches={}", out_file);
CompilationResult::Ok
}
pub fn compile_for_examples<T: AsRef<Path>,
Ms: AsRef<OsStr>,
Mi: IntoIterator<Item = Ms>,
Is: AsRef<OsStr>,
Ii: IntoIterator<Item = Is>,
P: Into<ParameterBundle<Ms, Mi, Is, Ii>>>(
resource_file: T, parameters: P)
-> CompilationResult {
let (_, _, out_file) = try_compile_impl!(compile_impl(resource_file.as_ref(), parameters.into()));
println!("cargo:rustc-link-arg-examples={}", out_file);
CompilationResult::Ok
}
pub fn compile_for_everything<T: AsRef<Path>,
Ms: AsRef<OsStr>,
Mi: IntoIterator<Item = Ms>,
Is: AsRef<OsStr>,
Ii: IntoIterator<Item = Is>,
P: Into<ParameterBundle<Ms, Mi, Is, Ii>>>(
resource_file: T, parameters: P)
-> CompilationResult {
let (_, _, out_file) = try_compile_impl!(compile_impl(resource_file.as_ref(), parameters.into()));
println!("cargo:rustc-link-arg={}", out_file);
CompilationResult::Ok
}
fn compile_impl<Ms: AsRef<OsStr>, Mi: IntoIterator<Item = Ms>, Is: AsRef<OsStr>, Ii: IntoIterator<Item = Is>, P: Into<ParameterBundle<Ms, Mi, Is, Ii>>>(
resource_file: &Path, parameters: P)
-> Result<(&str, String, String), CompilationResult> {
let mut comp = ResourceCompiler::new();
if let Some(missing) = comp.is_supported() {
if missing.is_empty() {
Err(CompilationResult::NotWindows)
} else {
Err(CompilationResult::NotAttempted(missing))
}
} else {
let prefix = &resource_file.file_stem().expect("resource_file has no stem").to_str().expect("resource_file's stem not UTF-8");
let out_dir = env::var("OUT_DIR").expect("No OUT_DIR env var");
let out_file = comp.compile_resource(&out_dir, &prefix, resource_file.to_str().expect("resource_file not UTF-8"), parameters.into())
.map_err(CompilationResult::Failed)?;
Ok((prefix, out_dir, out_file))
}
}
fn apply_parameters<'t, Ms: AsRef<OsStr>, Mi: IntoIterator<Item = Ms>, Is: AsRef<OsStr>, Ii: IntoIterator<Item = Is>>(to: &'t mut Command, macro_pref: &str,
include_dir_pref: &str,
parameters: ParameterBundle<Ms,
Mi,
Is,
Ii>)
-> &'t mut Command {
for m in parameters.macros {
to.arg(macro_pref).arg(m);
}
for id in parameters.include_dirs {
to.arg(include_dir_pref).arg(id);
}
to
}
pub fn find_windows_sdk_tool<T: AsRef<str>>(tool: T) -> Option<PathBuf> {
find_windows_sdk_tool_impl(tool.as_ref())
}
#[allow(unused)]
fn env_target_and_rc() -> Result<(String, Option<OsString>), Cow<'static, str>> {
let target = env::var("TARGET").map_err(|_| Cow::from("no $TARGET"))?;
let rc = env::var_os(&format!("RC_{}", target)).or_else(|| env::var_os(&format!("RC_{}", target.replace('-', "_")))).or_else(|| env::var_os("RC"));
Ok((target, rc))
}