// cargo-deps: serde_json use std::env::current_dir; use std::process::Command; use std::path::{ Path, PathBuf }; use std::fs::{ File, read_dir, copy, create_dir, create_dir_all, remove_dir_all }; use std::io::{ BufReader, Result as IOResult, Error as IOError, ErrorKind as IOErrorKind }; extern crate serde_json; use serde_json::{ Value, Error, from_reader as json_from_reader }; fn load_json > (path: P) -> Result { let file = match File::open(path) { Ok(f) => f, Err(e) => return Err(Error::io(e)) }; Ok(json_from_reader(BufReader::new(file))?) } fn copy_dir_impl (base: &Path, from: &Path, to: &Path) -> IOResult { let local_from = from.strip_prefix(base).expect("Failed to strip prefix for from"); let sub_to = to.join(local_from); create_dir(sub_to)?; let iter = read_dir(from)?; let mut counter = 0u64; for res in iter { let res = res?; let path = res.path(); let local_path = path.strip_prefix(base).expect("Failed to strip prefix for a path"); if path.is_file() { let to = to.join(&local_path); copy(&path, to)?; counter += 1; } else if path.is_dir() { let count = copy_dir_impl(base, &path, to)?; counter += count; } else { return Err(IOError::from(IOErrorKind::InvalidInput)); } } Ok(counter) } fn copy_dir (from: &Path, to: &Path) -> IOResult { if from.is_dir() { copy_dir_impl(from, from, to) } else { Err(IOError::from(IOErrorKind::InvalidInput)) } } fn main () { let cargo = Command::new("cargo").arg("build").arg("--release").spawn().expect("Failed to execute cargo").wait_with_output().expect("Did not receive output from cargo"); assert!(cargo.status.code().expect("Did not receive an exit code from cargo") == 0, "Cargo failed to execute"); let cwd = current_dir().unwrap().to_str().unwrap().to_owned(); let build_dir = [ &cwd, "target", "release" ].iter().collect::(); let mut publish_dir = [ &cwd, "publish" ].iter().collect::(); #[cfg(target_os = "windows")] publish_dir.push("windows"); #[cfg(target_os = "linux")] publish_dir.push("linux"); #[cfg(not(any(target_os = "linux", target_os = "windows")))] panic!("Publish script does not support this OS"); if publish_dir.exists() { remove_dir_all(&publish_dir).expect("Failed to remove existing publish dir"); } create_dir_all(&publish_dir).expect("Failed to create publish dir"); let publish_dir = publish_dir.to_str().unwrap().to_owned(); #[cfg(target_os = "windows")] { for entry in read_dir(build_dir).expect("Can't read build_dir") { let entry_path = entry.unwrap().path(); if let Some(entry_name) = entry_path.file_name() { let entry_name = entry_name.to_str().unwrap(); // TODO: Don't copy all dll's and exe's indescriminately // for one thing, if the names cargo.toml change, there will be multiple exe's // for another, the build script needs a list of dll's too to avoid blind copy if entry_name.ends_with(".dll") || entry_name.ends_with(".exe") { let new_path = [ &publish_dir, entry_name ].iter().collect::(); copy(&entry_path, new_path.as_path()).expect("Can't copy file to publish directory"); } } } } #[cfg(not(target_os = "windows"))] { panic!("Publish script is not implemented for this OS"); } let publish_deps = load_json("./meta/publish_deps.json").expect("Failed to load publish_deps.json"); for entry in publish_deps.as_array().expect("publish_deps.json must be an array").iter() { let local_path = entry.as_str().expect("publish_deps.json array elements must be strings"); let entry_path = [ &cwd, local_path ].iter().collect::(); assert!(entry_path.exists(), "publish_deps.json path {} does not exist", entry_path.display()); let new_path = [ &publish_dir, local_path ].iter().collect::(); match if entry_path.is_dir() { copy_dir(&entry_path, new_path.as_path()) } else { copy(&entry_path, new_path.as_path()) } { Ok(_) => println!("Copied dependency {} to publish directory", local_path), Err(e) => panic!("Failed to copy dependency {} to {}: {}", entry_path.display(), new_path.display(), e) } } }