extern crate cc;
extern crate pkg_config;
use pkg_config::Library;
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path;
use std::process::Command;
fn find_library(names: &[&str]) -> Option<Library> {
for name in names {
if let Ok(lib) = pkg_config::probe_library(name) {
return Some(lib);
}
}
None
}
fn main() {
println!("cargo:rerun-if-env-changed=PKG_CONFIG_PATH");
let wide = cfg!(all(feature = "wide", not(target_os = "macos")));
let ncurses_lib_names = if wide {
&["ncursesw5", "ncursesw"]
} else {
&["ncurses5", "ncurses"]
};
let ncurses_lib = find_library(ncurses_lib_names);
if cfg!(feature = "menu") {
if wide {
find_library(&["menuw5", "menuw"]);
} else {
find_library(&["menu5", "menu"]);
}
}
if cfg!(feature = "panel") {
if wide {
find_library(&["panelw5", "panelw"]);
} else {
find_library(&["panel5", "panel"]);
}
}
let mut already_printed:bool=false;
const ENV_VAR_NAME_FOR_LIB:&str="NCURSES_RS_RUSTC_LINK_LIB";
println!("cargo:rerun-if-env-changed={}",ENV_VAR_NAME_FOR_LIB);
let lib_name:String=match std::env::var(ENV_VAR_NAME_FOR_LIB) {
Ok(x) => x,
_ => if let Some(ref lib)=ncurses_lib {
let substring_to_find = "curses";
if let Some(found) = lib.libs.iter().find(|&s| s.contains(substring_to_find)) {
already_printed=true;
found.clone()
} else {
let repeated_pkg_config_command: String = ncurses_lib_names
.iter()
.map(|ncurses_lib_name| format!("pkg-config --libs {}", ncurses_lib_name))
.collect::<Vec<_>>()
.join("` or `");
let warning_message = format!(
"pkg_config reported that it found the ncurses libs but the substring '{}' was not among them, ie. in the output of the shell command(s) eg. `{}`",
substring_to_find,
repeated_pkg_config_command
);
println!("cargo:warning={}", warning_message);
ncurses_lib_names.last().unwrap().to_string()
}
} else {
ncurses_lib_names.last().unwrap().to_string()
}
};
if !already_printed {
println!("cargo:rustc-link-lib={}", lib_name);
}
const ENV_VAR_NAME_FOR_FLAGS:&str="NCURSES_RS_RUSTC_FLAGS";
println!("cargo:rerun-if-env-changed={}",ENV_VAR_NAME_FOR_FLAGS);
if let Ok(x) = std::env::var(ENV_VAR_NAME_FOR_FLAGS) {
println!("cargo:rustc-flags={}", x);
}
check_chtype_size(&ncurses_lib);
gen_constants(&ncurses_lib, &lib_name);
gen_menu_constants(&ncurses_lib, &lib_name);
build_wrap(&ncurses_lib);
}
fn build_wrap(ncurses_lib: &Option<Library>) {
println!("cargo:rerun-if-changed=src/wrap.c");
let mut build = cc::Build::new();
if let Some(lib) = ncurses_lib {
for path in lib.include_paths.iter() {
build.include(path);
}
}
build.file("src/wrap.c")
.compile("wrap");
}
fn gen_constants(ncurses_lib: &Option<Library>, lib_name:&str) {
println!("cargo:rerun-if-changed=src/genconstants.c");
let out_dir = env::var("OUT_DIR").expect("cannot get OUT_DIR");
let bin = format!("{}", Path::new(&out_dir).join(if cfg!(windows) { "genconstants.exe" } else { "genconstants" }).display());
let src = format!("{}", Path::new(&out_dir).join("raw_constants.rs").display());
let mut build = cc::Build::new();
let link_paths_for_linker;
if let Some(lib) = ncurses_lib {
for path in lib.include_paths.iter() {
build.include(path);
}
link_paths_for_linker=lib.link_paths.iter()
.map(|link_path| format!("-L{}", link_path.display()))
.collect::<Vec<String>>();
} else {
link_paths_for_linker=vec!();
}
let compiler = build.try_get_compiler().expect("Failed Build::try_get_compiler");
let mut command = compiler.to_command();
if let Ok(x) = std::env::var("NCURSES_RS_CFLAGS") {
command.args(x.split(" "));
}
command.arg("-o").arg(&bin).arg("src/genconstants.c")
.arg(format!("-l{}",lib_name)).args(link_paths_for_linker);
assert!(command.status().expect("compilation failed").success());
let consts = Command::new(&bin).output()
.expect(&format!("{} failed", bin));
let mut file = File::create(&src).unwrap();
file.write_all(&consts.stdout).unwrap();
}
fn gen_menu_constants(ncurses_lib: &Option<Library>, lib_name:&str) {
println!("cargo:rerun-if-changed=src/menu/genconstants.c");
let out_dir = env::var("OUT_DIR").expect("cannot get OUT_DIR");
let bin = format!("{}", Path::new(&out_dir).join(if cfg!(windows) { "genmenuconstants.exe" } else { "genmenuconstants" }).display());
let src = format!("{}", Path::new(&out_dir).join("menu_constants.rs").display());
let mut build = cc::Build::new();
let link_paths_for_linker;
if let Some(lib) = ncurses_lib {
for path in lib.include_paths.iter() {
build.include(path);
}
link_paths_for_linker=lib.link_paths.iter()
.map(|link_path| format!("-L{}", link_path.display()))
.collect::<Vec<String>>();
} else {
link_paths_for_linker=vec!();
}
let compiler = build.try_get_compiler().expect("Failed Build::try_get_compiler");
let mut command = compiler.to_command();
if let Ok(x) = std::env::var("NCURSES_RS_CFLAGS") {
command.args(x.split(" "));
}
command.arg("-o").arg(&bin).arg("src/menu/genconstants.c")
.arg(format!("-l{}",lib_name)).args(link_paths_for_linker);
assert!(command.status().expect("compilation failed").success());
let consts = Command::new(&bin).output()
.expect(&format!("{} failed", bin));
let mut file = File::create(&src).unwrap();
file.write_all(&consts.stdout).unwrap();
}
fn check_chtype_size(ncurses_lib: &Option<Library>) {
let out_dir = env::var("OUT_DIR").expect("cannot get OUT_DIR");
let src = format!("{}", Path::new(&out_dir).join("chtype_size.c").display());
let bin = format!("{}", Path::new(&out_dir).join(if cfg!(windows) { "chtype_size.exe" } else { "chtype_size" }).display());
let mut fp = File::create(&src).expect(&format!("cannot create {}", src));
fp.write_all(b"
#include <assert.h>
#include <limits.h>
#include <stdio.h>
#include <ncurses.h>
int main(void)
{
if (sizeof(chtype)*CHAR_BIT == 64) {
puts(\"cargo:rustc-cfg=feature=\\\"wide_chtype\\\"\");
} else {
/* We only support 32-bit and 64-bit chtype. */
assert(sizeof(chtype)*CHAR_BIT == 32 && \"unsupported size for chtype\");
}
#if defined(NCURSES_MOUSE_VERSION) && NCURSES_MOUSE_VERSION == 1
puts(\"cargo:rustc-cfg=feature=\\\"mouse_v1\\\"\");
#endif
return 0;
}
").expect(&format!("cannot write into {}", src));
let mut build = cc::Build::new();
if let Some(lib) = ncurses_lib {
for path in lib.include_paths.iter() {
build.include(path);
}
}
let compiler = build.try_get_compiler().expect("Failed Build::try_get_compiler");
let mut command = compiler.to_command();
if let Ok(x) = std::env::var("NCURSES_RS_CFLAGS") {
command.args(x.split(" "));
}
command.arg("-o").arg(&bin).arg(&src);
assert!(command.status().expect("compilation failed").success());
let features = Command::new(&bin).output()
.expect(&format!("{} failed", bin));
print!("{}", String::from_utf8_lossy(&features.stdout));
std::fs::remove_file(&src).expect(&format!("cannot delete {}", src));
std::fs::remove_file(&bin).expect(&format!("cannot delete {}", bin));
}