use std::env;
use std::fs;
use std::io::{self, Write};
use std::path::{Path, PathBuf};
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
struct Config {
include_paths: Vec<PathBuf>,
have_cv448: bool,
}
#[allow(dead_code)]
fn is_version_at_least<V: AsRef<str>>(nettle_version: V, need: &[u8]) -> bool {
for (i, (n, component)) in need.iter()
.zip(nettle_version.as_ref().split('.'))
.enumerate()
{
if let Ok(c) = component.parse::<u8>() {
if c < *n {
return false;
} else if c > *n {
return true;
} else {
}
} else {
panic!("Failed to parse the {}th component of the Nettle version \
{:?}: {:?}", i + 1, nettle_version.as_ref(), component);
}
}
true
}
fn check_override(feature: &str) -> Option<bool> {
env::var(&format!("NETTLE_HAVE_{}", feature))
.ok()
.map(|v| match v.to_lowercase().as_str() {
"1" | "y" | "yes" | "enable" | "true" => true,
_ => false,
})
}
fn check_cc(includes: &[PathBuf], header: &str, symbol: &str) -> bool {
let t = || -> Result<bool> {
let wd = tempfile::tempdir()?;
let testpath = wd.path().join("test.c");
let mut testfile = fs::File::create(&testpath)?;
write!(testfile, "#include <nettle/{}>
int
main()
{{
(void) {};
}}
", header, symbol)?;
let tool = cc::Build::new()
.warnings(false)
.includes(includes)
.try_get_compiler()?;
let output = tool.to_command()
.current_dir(&wd)
.arg(&testpath)
.output()?;
Ok(output.status.success())
};
t().unwrap_or(false)
}
fn check_cv448(includes: &[PathBuf]) -> bool {
check_override("CV448")
.unwrap_or_else(|| check_cc(includes, "eddsa.h",
"nettle_ed448_shake256_sign"))
}
#[cfg(target_env = "msvc")]
fn try_vcpkg() -> Result<Config> {
let lib = vcpkg::Config::new()
.emit_includes(true)
.find_package("nettle")?;
Ok(Config {
have_cv448: check_cv448(&include_paths),
include_paths,
})
}
#[cfg(not(target_env = "msvc"))]
fn try_vcpkg() -> Result<Config> { Err("not applicable")?; unreachable!() }
fn print_library(lib: &pkg_config::Library, mode: &str) {
for p in &lib.include_paths {
println!("cargo:include={}", p.display());
}
for p in &lib.frameworks {
println!("cargo:rustc-link-lib=framework={}", p);
}
for p in &lib.framework_paths {
println!("cargo:rustc-link-search=framework={}", p.display());
}
for p in &lib.libs {
println!("cargo:rustc-link-lib={}={}", mode, p);
}
for p in &lib.link_paths {
println!("cargo:rustc-link-search=native={}", p.display());
}
}
fn try_pkg_config() -> Result<Config> {
let mut nettle = pkg_config::probe_library("nettle hogweed")?;
let mode = match env::var_os("NETTLE_STATIC") {
Some(_) => "static",
None => "dylib",
};
print_library(&nettle, mode);
if let Ok(mut gmp) = pkg_config::probe_library("gmp") {
print_library(&gmp, mode);
nettle.include_paths.append(&mut gmp.include_paths);
} else {
println!("cargo:rustc-link-lib={}=gmp", mode);
}
Ok(Config {
have_cv448: check_cv448(&nettle.include_paths),
include_paths: nettle.include_paths,
})
}
const NETTLE_PREGENERATED_BINDINGS: &str = "NETTLE_PREGENERATED_BINDINGS";
fn real_main() -> Result<()> {
println!("cargo:rerun-if-env-changed=NETTLE_STATIC");
println!("cargo:rerun-if-env-changed={}", NETTLE_PREGENERATED_BINDINGS);
let config = try_vcpkg().or_else(|_| try_pkg_config())?;
let out_path = Path::new(&env::var("OUT_DIR").unwrap()).join("bindings.rs");
if let Ok(bundled) = env::var(NETTLE_PREGENERATED_BINDINGS)
{
let p = Path::new(&bundled);
if p.exists() {
fs::copy(&p, &out_path)?;
println!("cargo:rerun-if-changed={:?}", p);
return Ok(());
}
}
let mut builder = bindgen::Builder::default()
.header("bindgen-wrapper.h")
.blacklist_type("max_align_t")
.blacklist_function("strtold")
.blacklist_function("qecvt")
.blacklist_function("qfcvt")
.blacklist_function("qgcvt")
.blacklist_function("qecvt_r")
.blacklist_function("qfcvt_r")
.size_t_is_usize(true);
if config.have_cv448 {
builder = builder.header_contents("cv448-wrapper.h", "#include <nettle/curve448.h>");
}
for p in config.include_paths {
builder = builder.clang_arg(format!("-I{}", p.display()));
}
let bindings = builder.generate().unwrap();
bindings.write_to_file(&out_path)?;
let mut s = fs::OpenOptions::new().append(true).open(out_path)?;
writeln!(s, "\
/// Compile-time configuration.
pub mod config {{
/// Cv448/Ed448 support in Nettle.
pub const HAVE_CV448: bool = {};
}}
", config.have_cv448)?;
if ! config.have_cv448 {
let mut stubs = fs::File::open("cv448-stubs.rs")?;
io::copy(&mut stubs, &mut s)?;
}
Ok(())
}
fn main() -> Result<()> {
match real_main() {
Ok(()) => Ok(()),
Err(e) => {
eprintln!("Building nettle-sys failed.");
eprintln!("Because: {}", e);
eprintln!();
print_hints();
Err("Building nettle-sys failed.")?;
unreachable!()
}
}
}
fn print_hints() {
eprintln!("Please make sure the necessary build dependencies are \
installed.\n");
if cfg!(target_os = "windows") {
eprintln!("If you are using MSYS2, try:
$ pacboy -S clang:x nettle:x
");
} else if cfg!(target_os = "macos") {
eprintln!("If you are using MacPorts, try:
$ sudo port install nettle pkgconfig
");
} else if cfg!(target_os = "linux") {
eprintln!("If you are using Debian (or a derivative), try:
$ sudo apt install clang llvm pkg-config nettle-dev
");
eprintln!("If you are using Arch (or a derivative), try:
$ sudo pacman -S clang pkg-config nettle --needed
");
eprintln!("If you are using Fedora (or a derivative), try:
$ sudo dnf install clang pkg-config nettle-devel
");
}
eprintln!("See https://gitlab.com/sequoia-pgp/nettle-sys#building \
for more information.\n");
}
#[allow(dead_code)]
fn log<S: AsRef<str>>(m: S) {
writeln!(&mut fs::OpenOptions::new().append(true).open("/tmp/l").unwrap(),
"{}", m.as_ref()).unwrap();
}