#![allow(dead_code)]
use std::{
env::{set_var, var},
ffi::OsStr,
fs::{create_dir_all, remove_dir_all},
io::{ErrorKind, Result as IoResult},
path::{Path, PathBuf},
process::{Command, Stdio},
};
#[cfg(feature = "compile_csource")]
extern crate cc;
pub const C_SRC_REPO: &str = "https://github.com/dankamongmen/notcurses";
pub const C_SRC_BASENAME: &str = "notcurses4libnotcurses-sys";
pub const BINDINGS_BASENAME: &str = "bindings.rs";
#[derive(Clone, Debug, Default)]
pub struct NcCSource {
version: String,
root_path: PathBuf,
vendored_path: PathBuf,
source_path: PathBuf,
compressed_source_file: String,
compressed_source_filepath: PathBuf,
vendored_compressed_source_path: PathBuf,
headers_path: PathBuf,
build_path: PathBuf,
deployed_bindings_filepath: PathBuf,
}
impl NcCSource {
pub fn vendor_csource(&self) {
println!("cargo:warning=Vendoring C source…");
self.clone_repo(true);
self.compress_csource();
self.vendor_file(&self.compressed_source_filepath)
}
pub fn delete_vendored(&self) {
Self::rm(&self.vendored_path)
.unwrap_or_else(|_| panic!["rm -rf vendored: {:?}", self.vendored_path]);
}
pub fn compile_csource(&self) {
println!("cargo:warning=Compiling…");
self.decompress_csource(&self.vendored_compressed_source_path);
create_dir_all(&self.build_path).expect("couldn't create 'build/' directory");
set_var("PKG_CONFIG_PATH", &self.build_path);
set_var("CPATH", &self.build_path.join("include/notcurses"));
Self::run(
Command::new("cmake")
.arg("-DUSE_DOCTEST=off")
.arg("-DUSE_PANDOC=off")
.arg("..")
.current_dir(&self.build_path),
);
Self::run(
Command::new("make")
.arg(format!("-j{}", var("NUM_JOBS").expect("ERR: NUM_JOBS")))
.current_dir(&self.build_path),
);
}
pub fn use_vendored_bindings(&self) {
Self::run(
Command::new("cp")
.arg(&self.vendored_path.join(BINDINGS_BASENAME))
.arg(&self.root_path)
.current_dir(&self.root_path),
);
}
pub fn vendor_bindings(&self) {
println!("cargo:warning=Vendoring bindings…");
self.vendor_file(&self.deployed_bindings_filepath)
}
pub fn vendor_file<P: AsRef<Path> + AsRef<OsStr>>(&self, file: P) {
create_dir_all(&self.vendored_path).expect("couldn't create 'vendored/' directory");
Self::run(
Command::new("cp")
.arg(file)
.arg(&self.vendored_path)
.current_dir(&self.root_path),
);
}
}
impl NcCSource {
pub fn new(version: &str) -> Self {
let mut self0 = NcCSource {
compressed_source_file: format!["{}.tar.xz", C_SRC_BASENAME],
..Default::default()
};
self0.set_root_path(PathBuf::from(var("OUT_DIR").expect("ERR: OUT_DIR")));
let vendored_path =
PathBuf::from(var("CARGO_MANIFEST_DIR").expect("ERR: CARGO_MANIFEST_DIR"))
.join("build/vendored");
let vendored_compressed_source_path = vendored_path.join(&self0.compressed_source_file);
Self { version: version.into(), vendored_path, vendored_compressed_source_path, ..self0 }
}
pub fn version(&self) -> &str {
&self.version
}
pub fn set_version(&mut self, version: &str) {
self.version = version.to_string();
}
pub fn root_path(&self) -> PathBuf {
self.root_path.clone()
}
pub fn set_root_path(&mut self, root_path: PathBuf) {
self.root_path = root_path;
self.source_path = self.root_path.join(C_SRC_BASENAME);
self.compressed_source_filepath = self.root_path.join(&self.compressed_source_file);
self.headers_path = self.source_path.join("include");
self.build_path = self.source_path.join("build");
self.deployed_bindings_filepath = self.root_path.join(BINDINGS_BASENAME);
}
pub fn headers_include_string(&self) -> String {
format!["-I{}", self.headers_path.to_string_lossy()]
}
pub fn deployed_bindings(&self) -> PathBuf {
self.deployed_bindings_filepath.clone()
}
pub fn clone_repo(&self, optimize_size: bool) {
if optimize_size {
println!("cargo:warning=Cloning the repository (size optimize)…");
} else {
println!("cargo:warning=Cloning the full repository…");
}
Self::rm(&self.source_path)
.unwrap_or_else(|_| panic!["rm -rf source_path: {:?}", self.source_path]);
let mut git_cmd = Command::new("git");
if optimize_size {
git_cmd
.arg("clone")
.arg("--depth")
.arg("1")
.arg("--branch")
.arg(&format!["v{}", self.version])
.arg(C_SRC_REPO)
.arg(&self.source_path)
.stderr(Stdio::null())
.current_dir(&self.root_path)
} else {
git_cmd
.arg("clone")
.arg(C_SRC_REPO)
.arg(&self.source_path)
.stderr(Stdio::null())
.current_dir(&self.root_path)
};
Self::run(&mut git_cmd);
if optimize_size {
let delete_files = ["build", "data", "doc", ".git", "python", "cffi"];
println!("cargo:warning=deleting files: {:?}...", &delete_files);
for file in delete_files {
let file_path = self.source_path.join(file);
Self::rm(&file_path).unwrap_or_else(|_| panic!["rm -rf {:?}", file_path]);
}
}
}
pub fn compress_csource(&self) {
println!("cargo:warning=Compressing…");
set_var("XZ_OPT", "-e9");
Self::run(
Command::new("tar")
.arg("cJf")
.arg(&self.compressed_source_file)
.arg("-C")
.arg(&self.root_path)
.arg(C_SRC_BASENAME)
.current_dir(&self.root_path),
);
}
pub fn decompress_csource<P: AsRef<Path>>(&self, file_path: P) {
println!("cargo:warning=Decompressing…");
Self::run(
Command::new("tar")
.arg("xJf")
.arg(file_path.as_ref())
.arg("-C")
.arg(&self.root_path)
.current_dir(&self.root_path),
);
}
pub fn install_dependencies(&self) {
Self::run(
Command::new("apt")
.arg("install")
.arg("-y")
.arg("libunistring-dev")
.arg("libdeflate-dev")
.arg("doctest-dev"),
);
}
}
impl NcCSource {
fn run(command: &mut Command) {
println!(
"cargo:warning=Running: {:?}",
format!["{:?}", command].replace('"', "")
);
match command.status() {
Ok(status) => {
if !status.success() {
panic!("`{:?}` failed: {}", command, status);
}
}
Err(error) => {
panic!("failed to execute `{:?}`: {}", command, error);
}
}
}
fn rm<P: AsRef<Path>>(path: P) -> IoResult<()> {
match remove_dir_all(path) {
Ok(_) => Ok(()),
Err(e) => match e.kind() {
ErrorKind::NotFound => Ok(()),
_ => Err(e),
},
}
}
pub fn print_debug(&self) {
println!("cargo:warning=root_: {:?}", self.root_path);
println!("cargo:warning=sourc: {:?}", self.source_path);
println!("cargo:warning=cfile: {:?}", self.compressed_source_file);
println!("cargo:warning=cpath: {:?}", self.compressed_source_filepath);
println!("cargo:warning=vpath: {:?}", self.vendored_path);
println!(
"cargo:warning=vcomp: {:?}",
self.vendored_compressed_source_path
);
println!("cargo:warning=heade: {:?}", self.headers_path);
println!("cargo:warning=build:{:?}", self.build_path);
}
}