diff options
Diffstat (limited to 'xtask/src')
-rw-r--r-- | xtask/src/lib.rs | 102 | ||||
-rw-r--r-- | xtask/src/main.rs | 65 |
2 files changed, 167 insertions, 0 deletions
diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs new file mode 100644 index 0000000..5e876d5 --- /dev/null +++ b/xtask/src/lib.rs @@ -0,0 +1,102 @@ +use std::{ + env::consts::ARCH, + fs::File, + path::{Path, PathBuf}, +}; + +use anyhow::{ensure, Result}; +use flate2::{write::GzEncoder, Compression}; +use tar::Builder; + +const BIN_NAME: &str = "projectr"; +const PKG_VER: &str = env!("CARGO_PKG_VERSION"); +const PKG_INCLUDE: &[&str] = &[ + "bin/tmux-projectr", + "CONTRIBUTING.md", + "README.md", + "LICENSE", +]; + +pub fn generate_tar_gz<P: AsRef<Path>>( + root: &P, + profile: &str, + tag: Option<&str>, +) -> Result<PathBuf> { + let pkg_name = format!("{BIN_NAME}-{PKG_VER}-{ARCH}.tar.gz"); + + let target_dir = root.as_ref().join("target"); + let profile_dir = target_dir.join(profile); + let dist_dir = target_dir.join("dist"); + let out_dir = find_out_dir(&profile_dir)?; + + let bin_path = profile_dir.join(BIN_NAME); + let pkg_path = dist_dir.join(pkg_name); + + ensure!( + !tag.is_some_and(|t| t.trim_start_matches('v') != PKG_VER), + "Package version does not match provided tag: {PKG_VER} != {}", + tag.unwrap().trim_start_matches('v') + ); + + ensure!( + bin_path.exists(), + "Package binary does not exist: {}", + bin_path.display() + ); + + ensure!( + out_dir.exists(), + "Build's out directory does not exist: {}", + out_dir.display() + ); + + let _ = std::fs::remove_dir_all(&dist_dir); + std::fs::create_dir_all(&dist_dir)?; + + let tar_gz = File::create(&pkg_path)?; + let enc = GzEncoder::new(tar_gz, Compression::default()); + let mut tar = Builder::new(enc); + + std::env::set_current_dir(root)?; + + tar.append_path_with_name(bin_path, PathBuf::from("bin").join(BIN_NAME))?; + tar.append_dir_all(".", out_dir)?; + PKG_INCLUDE.iter().try_for_each(|p| tar.append_path(p))?; + + tar.into_inner()?.finish()?; + + Ok(pkg_path) +} + +pub fn find_out_dir<P: AsRef<Path>>(profile_dir: P) -> Result<PathBuf> { + let build_dir = profile_dir.as_ref().join("build"); + + build_dir + .read_dir() + .map_err(|_| { + anyhow::anyhow!( + "Package build directory does not exist: {}", + build_dir.display() + ) + })? + .flatten() + .filter_map(|d| { + d.file_name() + .to_str()? + .starts_with(BIN_NAME) + .then(|| d.path().join("invoked.timestamp")) + .filter(|p| p.exists()) + }) + .reduce(|acc, path_buf| { + std::cmp::max_by_key(path_buf, acc, |p| { + p.metadata() + .and_then(|m| m.modified()) + .unwrap_or(std::time::SystemTime::UNIX_EPOCH) + }) + }) + .map(|p| p.with_file_name("out")) + .ok_or(anyhow::anyhow!( + "Package out directory not found: {}", + profile_dir.as_ref().display() + )) +} diff --git a/xtask/src/main.rs b/xtask/src/main.rs new file mode 100644 index 0000000..2282efd --- /dev/null +++ b/xtask/src/main.rs @@ -0,0 +1,65 @@ +//! See <https://github.com/matklad/cargo-xtask/>. +//! +//! This binary defines various auxiliary build commands, which are not +//! expressible with just `cargo`. +//! +//! This binary is integrated into the `cargo` command line by using an alias in +//! `.cargo/config`. + +use std::path::PathBuf; + +use anyhow::Result; +use clap::{Parser, Subcommand}; + +fn main() -> Result<()> { + let cli = Cli::parse(); + + match cli.command { + Commands::Dist { tag } => { + let targz = xtask::generate_tar_gz(&cli.directory, &cli.profile, tag.as_deref())?; + println!("{}", targz.display()); + } + Commands::OutDir => { + let profile_dir = cli.directory.join("target").join(&cli.profile); + let out_dir = xtask::find_out_dir(profile_dir)?; + println!("{}", out_dir.display()); + } + }; + + Ok(()) +} + +#[derive(Debug, Clone, Parser)] +#[command(author, version, about)] +struct Cli { + #[command(subcommand)] + command: Commands, + + /// Cargo profile to use. + #[arg(short, long, default_value = "release")] + profile: String, + + /// Path to root directory of package. + #[arg(short='C', long, default_value_os_t = get_package_dir())] + directory: PathBuf, +} + +#[derive(Debug, Clone, Subcommand)] +enum Commands { + /// Print the default value of OUT_DIR used by cargo when building the package. + OutDir, + + /// Generate distributable package and print it's path + Dist { + /// Verify the package version matches the provided tag. + #[arg(short, long)] + tag: Option<String>, + }, +} + +fn get_package_dir() -> PathBuf { + std::path::Path::new(&env!("CARGO_MANIFEST_DIR")) + .parent() + .unwrap() + .to_path_buf() +} |