use std::{env, process::Command};
const VERSION: &str = "1.0.18";
const MIN_VERSION: &str = "1.0.12";
#[cfg(not(windows))]
fn main() {
let mut should_build = false;
let force_build = match env::var("SODIUM_BUILD").ok() {
None => false,
Some(ref x) if x == "0" => false,
Some(_) => true,
};
if force_build {
should_build = true;
} else if let Ok(lib_dir) = env::var("SODIUM_LIB_DIR") {
println!("cargo:rustc-link-search=native={}", lib_dir);
let mode = match env::var_os("SODIUM_STATIC") {
Some(_) => "static",
None => "dylib",
};
println!("cargo:rustc-link-lib={0}=sodium", mode);
println!(
"cargo:warning=Using unknown libsodium version. This crate is tested against \
{} and may not be fully compatible with other versions.",
VERSION
);
} else if let Ok(lib_details) = pkg_config::Config::new()
.atleast_version(MIN_VERSION)
.probe("libsodium")
{
println!(" === found libsodium: {:#?}", lib_details);
if lib_details.version != VERSION {
println!(
"cargo:warning=Using libsodium version {}. This crate is tested against {} \
and may not be fully compatible with {}.",
lib_details.version, VERSION, lib_details.version
);
}
} else {
should_build = true;
}
if should_build {
use flate2::read::GzDecoder;
use std::fs::{self, File};
use tar::Archive;
let basename = format!("libsodium-{}", VERSION);
let gz_filename = format!("{}.tar.gz", &basename);
let url = format!(
"https://download.libsodium.org/libsodium/releases/{}",
&gz_filename
);
let mut install_dir = get_install_dir();
let mut source_dir = env::var("OUT_DIR").unwrap() + "/source";
let target = env::var("TARGET").unwrap();
if install_dir.contains(' ') {
let fallback_path = format!("/tmp/{}/{}", &basename, &target);
install_dir = fallback_path.clone() + "/installed";
source_dir = fallback_path.clone() + "/source";
println!(
"cargo:warning=The path to the usual build directory contains spaces and hence \
can't be used to build libsodium. Falling back to use {}. If running `cargo \
clean`, ensure you also delete this fallback directory",
fallback_path
);
}
let gz_path = format!("{}/{}", &source_dir, &gz_filename);
fs::create_dir_all(&install_dir).unwrap();
fs::create_dir_all(&source_dir).unwrap();
let mut curl_cmd = Command::new("curl");
let curl_output = curl_cmd
.arg(&url)
.arg("-sSLvo")
.arg(&gz_path)
.output()
.unwrap_or_else(|error| {
panic!("Failed to run curl command: {}", error);
});
if !curl_output.status.success() {
panic!(
"\n{:?}\n{}\n{}\n",
curl_cmd,
String::from_utf8_lossy(&curl_output.stdout),
String::from_utf8_lossy(&curl_output.stderr)
);
}
let gz_archive = File::open(&gz_path).unwrap();
let gz_decoder = GzDecoder::new(gz_archive);
let mut archive = Archive::new(gz_decoder);
archive.unpack(&source_dir).unwrap();
source_dir.push_str(&format!("/{}", basename));
let _ = fs::remove_file(gz_path);
let target_parts: Vec<&str> = target.split('-').collect();
let mut target_arch = target_parts[0];
if target_arch == "aarch64" {
target_arch = "arm64";
}
let target_sys = target_parts[2];
let mut path = env::var("PATH").expect("Error retrieving $PATH");
let build = cc::Build::new();
let (cc, mut cflags) = if target.contains("i686") {
(
format!("{} -m32", build.get_compiler().path().display()),
env::var("CFLAGS").unwrap_or_else(|_| String::from(" -march=i686 -O3")),
)
} else {
(
format!("{}", build.get_compiler().path().display()),
env::var("CFLAGS").unwrap_or_else(|_| String::from(" -march=native -O3")),
)
};
if target_sys == "ios" {
let xcode_dir_output = Command::new("xcode-select")
.arg("-p")
.output()
.expect("failed to execute xcode-select");
let xcode_dir_stdout = String::from_utf8_lossy(&xcode_dir_output.stdout);
let xcode_dir = xcode_dir_stdout.trim();
let platform = if target_arch == "arm64" {
"iPhoneOS"
} else {
"iPhoneSimulator"
};
let base_dir = format!("{}/Platforms/{}.platform/Developer", xcode_dir, platform);
let sdk = format!("{}/SDKs/{}.sdk", base_dir, platform);
path = format!("{}/usr/bin:{}/usr/sbin:{}", base_dir, base_dir, path);
cflags = env::var("CFLAGS").unwrap_or(format!(
"-arch {} -O3 -fembed-bitcode -isysroot {} -mios-version-min=6.0",
target_arch, sdk
));
}
let prefix_arg = format!("--prefix={}", install_dir);
let host = env::var("HOST").unwrap();
let host_arg = format!("--host={}", target);
let cross_compiling = target != host;
let help = if cross_compiling {
"***********************************************************\n\
Possible missing dependencies.\n\
See https://github.com/maidsafe/rust_sodium#cross-compiling\n\
***********************************************************\n\n"
} else {
""
};
let get_disable_pie_arg = || {
const DISABLE_PIE: &str = "--disable-pie";
let mut lsb_release_cmd = Command::new("lsb_release");
let lsb_release_output = lsb_release_cmd.arg("-irs").output();
let lsb_release_output = match lsb_release_output {
Ok(output) => output,
Err(error) => {
println!("Failed to run 'lsb_release -irs': {}", error);
return DISABLE_PIE;
}
};
if !lsb_release_output.status.success() {
panic!(
"\n{:?}\n{}\n{}\n",
lsb_release_cmd,
String::from_utf8_lossy(&lsb_release_output.stdout),
String::from_utf8_lossy(&lsb_release_output.stderr)
);
}
let stdout = String::from_utf8_lossy(&lsb_release_output.stdout);
let mut lines = stdout.split(|c: char| c.is_whitespace());
let distro = lines.next().expect("Missing distributive name");
if distro == "Ubuntu" {
let version = lines.next().expect("Missing distributive version");
let mut lines = version.split('.');
let major: u32 = lines
.next()
.expect("Missing major version")
.parse()
.expect("Major version is not a number");
if major < 15 || version == "16.04" {
DISABLE_PIE
} else {
""
}
} else {
DISABLE_PIE
}
};
let disable_pie_arg = get_disable_pie_arg();
let mut configure_cmd = Command::new("./configure");
let configure_output = configure_cmd
.current_dir(&source_dir)
.env("PATH", &path)
.env("CC", &cc)
.env("CFLAGS", &cflags)
.arg(&prefix_arg)
.arg(&host_arg)
.arg("--enable-shared=no")
.arg(disable_pie_arg)
.output()
.unwrap_or_else(|error| {
panic!("Failed to run './configure': {}\n{}", error, help);
});
if !configure_output.status.success() {
panic!(
"\n{:?}\nCFLAGS={}\nCC={}\n{}\n{}\n{}\n",
configure_cmd,
cflags,
cc,
String::from_utf8_lossy(&configure_output.stdout),
String::from_utf8_lossy(&configure_output.stderr),
help
);
}
let mut clean_cmd = Command::new("make");
let clean_output = clean_cmd
.current_dir(&source_dir)
.arg("clean")
.output()
.unwrap_or_else(|error| {
panic!("Failed to run 'make clean': {}\n{}", error, help);
});
if !clean_output.status.success() {
panic!(
"\n{:?}\n{}\n{}\n{}\n{}\n",
clean_cmd,
String::from_utf8_lossy(&configure_output.stdout),
String::from_utf8_lossy(&clean_output.stdout),
String::from_utf8_lossy(&clean_output.stderr),
help
);
}
let j_arg = format!("-j{}", num_cpus::get());
let make_arg = if cross_compiling { "all" } else { "check" };
let mut make_cmd = Command::new("make");
let make_output = make_cmd
.current_dir(&source_dir)
.env("V", "1")
.arg(make_arg)
.arg(&j_arg)
.output()
.unwrap_or_else(|error| {
panic!("Failed to run 'make {}': {}\n{}", make_arg, error, help);
});
if !make_output.status.success() {
panic!(
"\n{:?}\n{}\n{}\n{}\n{}\n{}",
make_cmd,
String::from_utf8_lossy(&configure_output.stdout),
String::from_utf8_lossy(&clean_output.stdout),
String::from_utf8_lossy(&make_output.stdout),
String::from_utf8_lossy(&make_output.stderr),
help
);
}
let mut install_cmd = Command::new("make");
let install_output = install_cmd
.current_dir(&source_dir)
.arg("install")
.output()
.unwrap_or_else(|error| {
panic!("Failed to run 'make install': {}", error);
});
if !install_output.status.success() {
panic!(
"\n{:?}\n{}\n{}\n{}\n{}\n{}\n",
install_cmd,
String::from_utf8_lossy(&configure_output.stdout),
String::from_utf8_lossy(&clean_output.stdout),
String::from_utf8_lossy(&make_output.stdout),
String::from_utf8_lossy(&install_output.stdout),
String::from_utf8_lossy(&install_output.stderr)
);
}
println!("cargo:rustc-link-lib=static=sodium");
println!("cargo:rustc-link-search=native={}/lib", install_dir);
println!("cargo:include={}/include", install_dir);
}
}
#[cfg(all(not(windows), not(feature = "use-installed-libsodium")))]
extern crate cc;
#[cfg(all(not(target_env = "msvc"), not(feature = "use-installed-libsodium")))]
extern crate flate2;
#[cfg(all(target_env = "msvc", not(feature = "use-installed-libsodium")))]
extern crate libc;
#[cfg(all(not(target_env = "msvc"), not(feature = "use-installed-libsodium")))]
extern crate tar;
#[cfg(all(target_env = "msvc", not(feature = "use-installed-libsodium")))]
extern crate zip;
#[cfg(not(feature = "use-installed-libsodium"))]
fn get_install_dir() -> String {
env::var("OUT_DIR").unwrap() + "/installed"
}
#[cfg(all(windows, not(feature = "use-installed-libsodium")))]
fn check_powershell_version() {
let mut check_ps_version_cmd = Command::new("powershell");
let check_ps_version_output = check_ps_version_cmd
.arg("-Command")
.arg("{If ($PSVersionTable.PSVersion.Major -lt 4) { exit 1 }}")
.output()
.unwrap_or_else(|error| {
panic!("Failed to run powershell command: {}", error);
});
if !check_ps_version_output.status.success() {
panic!(
"\n{:?}\n{}\n{}\nYou must have Powershell v4.0 or greater installed.\n\n",
check_ps_version_cmd,
String::from_utf8_lossy(&check_ps_version_output.stdout),
String::from_utf8_lossy(&check_ps_version_output.stderr)
);
}
}
#[cfg(all(windows, not(feature = "use-installed-libsodium")))]
fn download_compressed_file() -> String {
let basename = "libsodium-".to_string() + VERSION;
let zip_filename = if cfg!(target_env = "msvc") {
basename.clone() + "-msvc.zip"
} else {
basename.clone() + "-mingw.tar.gz"
};
let url = format!(
"https://download.libsodium.org/libsodium/releases/{}",
&zip_filename,
);
let zip_path = get_install_dir() + "/" + &zip_filename;
let command = "([Net.ServicePointManager]::SecurityProtocol = 'Tls12') -and \
((New-Object System.Net.WebClient).DownloadFile(\""
.to_string()
+ &url
+ "\", \""
+ &zip_path
+ "\"))";
let mut download_cmd = Command::new("powershell");
let download_output = download_cmd
.arg("-Command")
.arg(&command)
.output()
.unwrap_or_else(|error| {
panic!("Failed to run powershell download command: {}", error);
});
if !download_output.status.success() {
panic!(
"\n{:?}\n{}\n{}\n",
download_cmd,
String::from_utf8_lossy(&download_output.stdout),
String::from_utf8_lossy(&download_output.stderr)
);
}
zip_path
}
#[cfg(all(windows, target_env = "msvc", not(feature = "use-installed-libsodium")))]
fn main() {
use libc::S_IFDIR;
use std::{
fs::{self, File},
io::{Read, Write},
path::Path,
};
use zip::ZipArchive;
check_powershell_version();
let install_dir = get_install_dir();
let lib_install_dir = Path::new(&install_dir).join("lib");
fs::create_dir_all(&lib_install_dir).unwrap();
let zip_path = download_compressed_file();
let zip_file = File::open(&zip_path).unwrap();
let mut zip_archive = ZipArchive::new(zip_file).unwrap();
let arch_path = if cfg!(target_pointer_width = "32") {
Path::new("libsodium/Win32")
} else if cfg!(target_pointer_width = "64") {
Path::new("libsodium/x64")
} else {
panic!("target_pointer_width not 32 or 64")
};
let unpacked_lib = arch_path.join("Release/v142/static/libsodium.lib");
for i in 0..zip_archive.len() {
let mut entry = zip_archive.by_index(i).unwrap();
let entry_name = entry.name().to_string();
let entry_path = Path::new(&entry_name);
let opt_install_path = if entry_path.starts_with("include") {
let is_dir = (entry.unix_mode().unwrap() & S_IFDIR as u32) != 0;
if is_dir {
let _ = fs::create_dir(&Path::new(&install_dir).join(entry_path));
None
} else {
Some(Path::new(&install_dir).join(entry_path))
}
} else if entry_path == unpacked_lib {
Some(lib_install_dir.join("libsodium.lib"))
} else {
None
};
if let Some(full_install_path) = opt_install_path {
let mut buffer = Vec::with_capacity(entry.size() as usize);
assert_eq!(entry.size(), entry.read_to_end(&mut buffer).unwrap() as u64);
let mut file = File::create(&full_install_path).unwrap();
file.write_all(&buffer).unwrap();
}
}
let _ = fs::remove_file(zip_path);
println!("cargo:rustc-link-lib=static=libsodium");
println!(
"cargo:rustc-link-search=native={}",
lib_install_dir.display()
);
println!("cargo:include={}/include", install_dir);
}
#[cfg(all(
windows,
not(target_env = "msvc"),
not(feature = "use-installed-libsodium")
))]
fn main() {
use flate2::read::GzDecoder;
use std::fs::{self, File};
use std::path::Path;
use tar::Archive;
check_powershell_version();
let install_dir = get_install_dir();
let lib_install_dir = Path::new(&install_dir).join("lib");
fs::create_dir_all(&lib_install_dir).unwrap();
let gz_path = download_compressed_file();
let gz_archive = File::open(&gz_path).unwrap();
let gz_decoder = GzDecoder::new(gz_archive);
let mut archive = Archive::new(gz_decoder);
let arch_path = if cfg!(target_pointer_width = "32") {
Path::new("libsodium-win32")
} else if cfg!(target_pointer_width = "64") {
Path::new("libsodium-win64")
} else {
panic!("target_pointer_width not 32 or 64")
};
let unpacked_include = arch_path.join("include");
let unpacked_lib = arch_path.join("lib\\libsodium.a");
let entries = archive.entries().unwrap();
for entry_result in entries {
let mut entry = entry_result.unwrap();
let entry_path = entry.path().unwrap().to_path_buf();
let full_install_path = if entry_path.starts_with(&unpacked_include) {
let include_file = entry_path.strip_prefix(arch_path).unwrap();
Path::new(&install_dir).join(include_file)
} else if entry_path == unpacked_lib {
lib_install_dir.join("libsodium.a")
} else {
continue;
};
entry.unpack(full_install_path).unwrap();
}
let _ = fs::remove_file(gz_path);
println!("cargo:rustc-link-lib=static=sodium");
println!(
"cargo:rustc-link-search=native={}",
lib_install_dir.display()
);
println!("cargo:include={}/include", install_dir);
}