aboutsummaryrefslogtreecommitdiffstats
path: root/xtask/src/main.rs
diff options
context:
space:
mode:
authorToby Vincent <tobyv13@gmail.com>2023-06-22 21:17:15 -0500
committerToby Vincent <tobyv13@gmail.com>2023-07-08 11:46:15 -0500
commit2e4fa58f7df52e6d6c67f476bd51b99bce8056cf (patch)
tree122f21b37a885bb14de81b5101eee37d6c24d480 /xtask/src/main.rs
parentd5af0a52b60efcbb832b63962b273fcc70c24ff0 (diff)
build: simplify `xtask dist` and add --check
Diffstat (limited to 'xtask/src/main.rs')
-rw-r--r--xtask/src/main.rs166
1 files changed, 138 insertions, 28 deletions
diff --git a/xtask/src/main.rs b/xtask/src/main.rs
index 7454bf0..1de4147 100644
--- a/xtask/src/main.rs
+++ b/xtask/src/main.rs
@@ -6,26 +6,40 @@
//! This binary is integrated into the `cargo` command line by using an alias in
//! `.cargo/config`.
-use std::path::PathBuf;
+use std::path::{Path, PathBuf};
+use std::{fs::File, process::Command};
-use anyhow::Result;
+use anyhow::{anyhow, bail, ensure, Context, Result};
+use build_info::BuildInfo;
use clap::{Parser, Subcommand};
-use xtask::{dist, release};
+use flate2::{write::GzEncoder, Compression};
+use once_cell::sync::Lazy;
+use tar::Builder;
+use xtask::release;
+
+const PKG_NAME: &str = "projectr";
+const PKG_VER: &str = env!("CARGO_PKG_VERSION");
+const PKG_INCLUDE: &[&str] = &[
+ "bin/tmux-projectr",
+ "CONTRIBUTING.md",
+ "README.md",
+ "LICENSE",
+];
fn main() -> Result<()> {
let cli = Cli::parse();
- std::env::set_current_dir(cli.directory)?;
-
match cli.command {
- Commands::Dist { tag } => {
- let targz = dist::generate_tar_gz(&cli.profile, tag.as_deref())?;
+ Commands::OutDir => println!("{}", out_dir()?.display()),
+ Commands::Dist { check } => {
+ let version = match version(cli.pre_release) {
+ Ok(_) if check => std::process::exit(0),
+ Err(_) if check => std::process::exit(1),
+ res => res?,
+ };
+ let targz = generate_tar_gz(version)?;
println!("{}", targz.display());
}
- Commands::OutDir => {
- let out_dir = dist::find_out_dir(&cli.profile)?;
- println!("{}", out_dir.display());
- }
Commands::Release(release) => release.run()?,
};
@@ -35,16 +49,12 @@ fn main() -> Result<()> {
#[derive(Debug, Clone, Parser)]
#[command(author, version, about)]
struct Cli {
+ /// Disable version/git tag check and appends `-dev` to the version
+ #[arg(short, long, global = true, required = false)]
+ pre_release: bool,
+
#[command(subcommand)]
command: Commands,
-
- /// Cargo profile to use.
- #[arg(short, long, default_value = "release")]
- profile: String,
-
- /// Change to DIRECTORY before doing anything
- #[arg(short='C', long, default_value_os_t = get_package_dir())]
- directory: PathBuf,
}
#[derive(Debug, Clone, Subcommand)]
@@ -52,19 +62,119 @@ 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
+ /// Generate distributable package
Dist {
- /// Verify the package version matches the provided tag.
- #[arg(short, long)]
- tag: Option<String>,
+ /// Validate a git tag matching the package version exists and exit.
+ #[arg(short, long, required = false)]
+ check: bool,
},
Release(release::Release),
}
-fn get_package_dir() -> PathBuf {
- std::path::Path::new(&env!("CARGO_MANIFEST_DIR"))
- .parent()
- .unwrap()
- .to_path_buf()
+fn version(pre_release: bool) -> Result<String> {
+ use build_info::VersionControl::Git;
+
+ let BuildInfo {
+ version_control: Some(Git(git)),
+ ..
+ } = build_info() else {
+ bail!("Failed to get version control info.");
+ };
+
+ if pre_release {
+ Ok(format!("{PKG_VER}-dev"))
+ } else if git.tags.contains(&format!("v{PKG_VER}")) {
+ Ok(PKG_VER.to_owned())
+ } else {
+ Err(anyhow!("Failed to find git tag matching package version."))
+ }
+}
+
+fn out_dir() -> Result<PathBuf> {
+ RELEASE_DIR
+ .join("build")
+ .read_dir()
+ .context("Failed to read build directory.")?
+ .flatten()
+ .filter_map(|d| {
+ d.file_name()
+ .to_str()?
+ .starts_with(PKG_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"))
+ .filter(|o| o.exists())
+ .context("Failed to find `out` directory for latest build")
}
+
+fn generate_tar_gz(version: String) -> Result<PathBuf> {
+ let target = build_info::format!("{}", $.target.triple);
+ let dist_pkg = DIST_DIR.join(format!("{PKG_NAME}-v{version}-{target}.tar.gz"));
+
+ let binary = build_binary()?;
+ ensure!(binary.exists(), "Failed to find package binary",);
+
+ let _ = std::fs::remove_dir_all(&*DIST_DIR);
+ std::fs::create_dir_all(&*DIST_DIR)?;
+
+ let tar_gz = File::create(&dist_pkg)?;
+ let enc = GzEncoder::new(tar_gz, Compression::default());
+ let mut tar = Builder::new(enc);
+
+ tar.append_path_with_name(binary, PathBuf::from("bin").join(PKG_NAME))?;
+ tar.append_dir_all(".", out_dir()?)?;
+ PKG_INCLUDE.iter().try_for_each(|p| tar.append_path(p))?;
+
+ tar.into_inner()?.finish()?;
+
+ Ok(dist_pkg)
+}
+
+fn build_binary() -> Result<PathBuf> {
+ let status = Command::new("cargo")
+ .arg("build")
+ .arg("--release")
+ .arg(format!("--package={PKG_NAME}"))
+ .status()
+ .context("Failed to invoke `cargo build`")?;
+
+ anyhow::ensure!(status.success(), "Cargo returned an error");
+
+ let mut binary = RELEASE_DIR.join(PKG_NAME);
+ if cfg!(windows) {
+ binary.set_extension("exe");
+ };
+
+ if let Err(e) = Command::new("strip").arg(&binary).status() {
+ eprintln!("Failed to strip the binary: {}", e)
+ }
+
+ Ok(binary)
+}
+
+static PROJECT_ROOT: Lazy<PathBuf> = Lazy::new(|| {
+ let dir = std::env::current_dir().unwrap_or_else(|_| {
+ Path::new(env!("CARGO_MANIFEST_DIR"))
+ .parent()
+ .unwrap()
+ .to_path_buf()
+ });
+
+ dir.ancestors()
+ .find(|p| p.join(".git").is_dir())
+ .unwrap_or(&dir)
+ .to_path_buf()
+});
+static DIST_DIR: Lazy<PathBuf> = Lazy::new(|| PROJECT_ROOT.join("target").join("dist"));
+static RELEASE_DIR: Lazy<PathBuf> = Lazy::new(|| PROJECT_ROOT.join("target").join("release"));
+
+build_info::build_info!(fn build_info);