From f95d80da43d8c9413346b9c934832e3280c452a2 Mon Sep 17 00:00:00 2001 From: "J. Gerhards" Date: Thu, 12 Jun 2025 16:22:24 +0200 Subject: [PATCH 1/9] Add xtask workspace Add an extra workspace named xtask for creating build tasks. Adds a cargo alias to quickly use it with 'cargo xtask'. --- .cargo/config.toml | 2 + Cargo.lock | 192 +++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 3 + xtask/Cargo.toml | 6 ++ xtask/src/main.rs | 2 + 5 files changed, 205 insertions(+) create mode 100644 .cargo/config.toml create mode 100644 xtask/Cargo.toml create mode 100644 xtask/src/main.rs diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..0f0e2a3 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[alias] +xtask = "run --frozen --package xtask --" diff --git a/Cargo.lock b/Cargo.lock index 5847d61..0ee1875 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,18 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "anyhow" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + [[package]] name = "argh" version = "0.1.13" @@ -248,6 +260,15 @@ version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "blocking" version = "1.6.1" @@ -319,12 +340,40 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + [[package]] name = "crossbeam-utils" version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "deranged" version = "0.4.0" @@ -334,6 +383,16 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -433,6 +492,29 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "filetime" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +dependencies = [ + "cfg-if", + "libc", + "libredox", + "windows-sys 0.59.0", +] + +[[package]] +name = "flate2" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +dependencies = [ + "crc32fast", + "libz-rs-sys", + "miniz_oxide", +] + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -497,6 +579,16 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.3.3" @@ -736,6 +828,17 @@ dependencies = [ "windows-targets 0.53.0", ] +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags", + "libc", + "redox_syscall", +] + [[package]] name = "libssh2-sys" version = "0.3.1" @@ -750,6 +853,15 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "libz-rs-sys" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "172a788537a2221661b480fee8dc5f96c580eb34fa88764d3205dc356c7e4221" +dependencies = [ + "zlib-rs", +] + [[package]] name = "libz-sys" version = "1.1.22" @@ -804,6 +916,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "miniz_oxide" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +dependencies = [ + "adler2", +] + [[package]] name = "mpdris" version = "1.2.0" @@ -992,6 +1113,15 @@ version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +[[package]] +name = "redox_syscall" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" +dependencies = [ + "bitflags", +] + [[package]] name = "rust-fuzzy-search" version = "0.1.1" @@ -1070,6 +1200,17 @@ dependencies = [ "serde", ] +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "shlex" version = "1.3.0" @@ -1176,6 +1317,17 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "tar" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" +dependencies = [ + "filetime", + "libc", + "xattr", +] + [[package]] name = "tempfile" version = "3.20.0" @@ -1313,6 +1465,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + [[package]] name = "uds_windows" version = "1.1.0" @@ -1365,6 +1523,12 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + [[package]] name = "wasi" version = "0.14.2+wasi-0.2.4" @@ -1722,6 +1886,28 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" +[[package]] +name = "xattr" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d65cbf2f12c15564212d48f4e3dfb87923d25d611f2aed18f4cb23f0413d89e" +dependencies = [ + "libc", + "rustix 1.0.7", +] + +[[package]] +name = "xtask" +version = "0.1.0" +dependencies = [ + "anyhow", + "argh", + "flate2", + "hex", + "sha2", + "tar", +] + [[package]] name = "yoke" version = "0.8.0" @@ -1860,6 +2046,12 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "zlib-rs" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "626bd9fa9734751fc50d6060752170984d7053f5a39061f524cda68023d4db8a" + [[package]] name = "zvariant" version = "5.5.3" diff --git a/Cargo.toml b/Cargo.toml index af3c8a1..35e00f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,9 @@ authors = [ "jasger9000 | jasger_" ] license = "MIT" repository = "https://github.com/jasger9000/mpdris" +[workspace] +members = [ "xtask" ] + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml new file mode 100644 index 0000000..e240aec --- /dev/null +++ b/xtask/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "xtask" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/xtask/src/main.rs b/xtask/src/main.rs new file mode 100644 index 0000000..f79c691 --- /dev/null +++ b/xtask/src/main.rs @@ -0,0 +1,2 @@ +fn main() { +} From 41a6b38fc4ebb342812e02e3069e9ffbe19cd34f Mon Sep 17 00:00:00 2001 From: "J. Gerhards" Date: Thu, 12 Jun 2025 16:53:32 +0200 Subject: [PATCH 2/9] Create xtask task to build/compress man pages --- xtask/Cargo.toml | 3 +++ xtask/src/args.rs | 27 +++++++++++++++++++ xtask/src/main.rs | 38 +++++++++++++++++++++++++++ xtask/src/man.rs | 67 +++++++++++++++++++++++++++++++++++++++++++++++ xtask/src/task.rs | 45 +++++++++++++++++++++++++++++++ 5 files changed, 180 insertions(+) create mode 100644 xtask/src/args.rs create mode 100644 xtask/src/man.rs create mode 100644 xtask/src/task.rs diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index e240aec..ab8b7c7 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -4,3 +4,6 @@ version = "0.1.0" edition = "2024" [dependencies] +anyhow = "1.0.98" +argh = "0.1.13" +flate2 = { version = "1.1.2", default-features = false, features = ["zlib-rs"] } diff --git a/xtask/src/args.rs b/xtask/src/args.rs new file mode 100644 index 0000000..486a45a --- /dev/null +++ b/xtask/src/args.rs @@ -0,0 +1,27 @@ +use std::path::PathBuf; + +use argh::FromArgs; + +/// XTasks +#[derive(FromArgs)] +#[argh(help_triggers("-h", "--help"))] +pub(crate) struct Args { + #[argh(subcommand)] + /// the task to execute + pub(crate) task: Task, +} + +#[derive(FromArgs)] +#[argh(subcommand)] +pub(crate) enum Task { + Man(ManTask), +} + +#[derive(FromArgs, PartialEq, Debug)] +/// Write & compress manpages to +#[argh(subcommand, name = "man", help_triggers("-h", "--help"))] +pub(crate) struct ManTask { + #[argh(positional)] + /// the directory to which the compressed manpages should be written to + pub(crate) dir: PathBuf, +} diff --git a/xtask/src/main.rs b/xtask/src/main.rs index f79c691..93d82a1 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -1,2 +1,40 @@ +use std::path::{Path, PathBuf}; +use std::{env, process::exit, sync::LazyLock}; + +use anyhow::Result; + +pub(crate) use man::build_man; +pub(crate) use task::Task; + +mod args; +mod man; +mod task; + +static PROJECT_ROOT: LazyLock = LazyLock::new(|| { + Path::new(&env!("CARGO_MANIFEST_DIR")) + .ancestors() + .nth(1) + .unwrap() + .to_path_buf() +}); +static DIST_DIR: LazyLock = LazyLock::new(|| PROJECT_ROOT.join("target/dist")); +static TARGET_DIR: LazyLock = LazyLock::new(|| PROJECT_ROOT.join("target")); + +const MANPATH: &str = "resources/man"; + fn main() { + if let Err(e) = try_main() { + eprintln!("{e:?}"); + exit(-1); + } +} + +fn try_main() -> Result<()> { + use args::Task::*; + + let args: args::Args = argh::from_env(); + + match args.task { + Man(task) => build_man(&task.dir), + } } diff --git a/xtask/src/man.rs b/xtask/src/man.rs new file mode 100644 index 0000000..2cfa3a9 --- /dev/null +++ b/xtask/src/man.rs @@ -0,0 +1,67 @@ +use std::fs::{self, File, Permissions, create_dir_all}; +use std::io::{BufWriter, Write}; +use std::os::unix::fs::PermissionsExt; +use std::path::{Path, PathBuf}; + +use anyhow::{Context, Result}; +use flate2::{Compression, write::GzEncoder}; + +use crate::{MANPATH, PROJECT_ROOT, Task}; + +pub(crate) fn build_man(outdir: &Path) -> Result<()> { + let indir = PROJECT_ROOT.join(MANPATH); + let skip = indir.components().count(); + + if outdir.exists() { + let t = Task::new("Removing old manpage output directory"); + fs::remove_dir_all(outdir).with_context(|| "Failed to delete manpage output directory")?; + fs::create_dir_all(outdir).with_context(|| "Failed to create manpage output directory")?; + t.success(); + } + + let t = Task::new("Building man pages"); + for inpath in search_files_recursive(&indir)? { + let mut outpath: PathBuf = outdir.components().chain(inpath.components().skip(skip)).collect(); + outpath.as_mut_os_string().push(".gz"); + + create_dir_all(outpath.parent().with_context(|| "Failed to get manpage output directory")?) + .with_context(|| "Failed to create manpage output directory")?; + + let infile = fs::read(&inpath).with_context(|| "Failed to read manpage infile")?; + let writer = BufWriter::new(File::create(&outpath).with_context(|| "Failed to open manpage outfile")?); + + let mut encoder = GzEncoder::new(writer, Compression::new(9)); + encoder.write_all(&infile)?; + encoder.try_finish()?; + + fs::set_permissions(&outpath, Permissions::from_mode(0o644)).with_context(|| "Failed to set manpage permissions")?; + } + + t.success(); + Ok(()) +} + +/// Finds all files present in a given start directory, +fn search_files_recursive(start: &Path) -> Result> { + fn inner(start: &Path, result: &mut Vec) -> Result<()> { + if start.is_dir() { + for entry in start.read_dir().with_context(|| "failed to read dir")? { + let entry = entry.with_context(|| "failed to get entry")?; + let path = entry.path(); + + if path.is_dir() { + inner(&path, result)?; + } else { + result.push(path); + } + } + } + + Ok(()) + } + + let mut result: Vec = Vec::new(); + inner(start, &mut result)?; + + Ok(result) +} diff --git a/xtask/src/task.rs b/xtask/src/task.rs new file mode 100644 index 0000000..f43ab40 --- /dev/null +++ b/xtask/src/task.rs @@ -0,0 +1,45 @@ +use std::io::{Write, stdout}; +use std::sync::atomic::{AtomicBool, Ordering::Relaxed}; + +#[derive(Debug)] +pub(crate) struct Task { + running: AtomicBool, + text: Box, +} + +impl Task { + pub(crate) fn new(text: &str) -> Self { + // hide cursor and print message + let mut stdout = stdout().lock(); + stdout.write_all(text.as_bytes()).unwrap(); + stdout.flush().unwrap(); + Self { + running: AtomicBool::new(true), + text: text.into(), + } + } + + pub(crate) fn success(&self) { + if self.running.load(Relaxed) { + println!(" - Done"); + self.running.store(false, Relaxed); + } + } + + pub(crate) fn failure(&self) { + if self.running.load(Relaxed) { + println!(" - Failed"); + self.running.store(false, Relaxed); + } + } + + pub(crate) fn fix_text(&self) { + print!("{}", self.text); + } +} + +impl Drop for Task { + fn drop(&mut self) { + self.failure(); + } +} From 3897e0ca276033e5b66750bbf0e7cbbc9270902c Mon Sep 17 00:00:00 2001 From: "J. Gerhards" Date: Thu, 12 Jun 2025 17:16:41 +0200 Subject: [PATCH 3/9] Add xtask build task Add an xtask that builds the project in release mode and compress the man pages. --- xtask/src/args.rs | 16 ++++++++++++- xtask/src/dist.rs | 59 +++++++++++++++++++++++++++++++++++++++++++++++ xtask/src/main.rs | 4 ++++ 3 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 xtask/src/dist.rs diff --git a/xtask/src/args.rs b/xtask/src/args.rs index 486a45a..e3fe53b 100644 --- a/xtask/src/args.rs +++ b/xtask/src/args.rs @@ -1,4 +1,4 @@ -use std::path::PathBuf; +use std::{env, path::PathBuf}; use argh::FromArgs; @@ -15,6 +15,7 @@ pub(crate) struct Args { #[argh(subcommand)] pub(crate) enum Task { Man(ManTask), + Build(BuildTask), } #[derive(FromArgs, PartialEq, Debug)] @@ -25,3 +26,16 @@ pub(crate) struct ManTask { /// the directory to which the compressed manpages should be written to pub(crate) dir: PathBuf, } + +#[derive(FromArgs, PartialEq, Debug)] +/// Compile/Build all project assets for the default arch or the one provided. +/// Result is written to target/dist/ or if provided. +#[argh(subcommand, name = "build", help_triggers("-h", "--help"))] +pub(crate) struct BuildTask { + #[argh(option, default = "env::consts::ARCH.to_string()")] + /// the arch to compile for + pub(crate) arch: String, + #[argh(positional)] + /// path to install the files to instead of target/dist/ + pub(crate) path: Option, +} diff --git a/xtask/src/dist.rs b/xtask/src/dist.rs new file mode 100644 index 0000000..ff5eb28 --- /dev/null +++ b/xtask/src/dist.rs @@ -0,0 +1,59 @@ +use std::fs; +use std::path::{Path, PathBuf}; +use std::{env, io::Write, os::unix::fs::PermissionsExt, process::Command, sync::Arc}; + +use crate::{DIST_DIR, NAME, PROJECT_ROOT, TARGET_DIR, Task, build_man}; +use anyhow::{Context, Result, anyhow}; +macro_rules! cp { + ($indir:expr, $outdir:expr, $src:literal, $dst:literal, $perm:expr) => { + $crate::dist::copy($indir, $outdir, &::std::format!($src), &::std::format!($dst), $perm) + }; +} + +fn copy>(indir: &Path, outdir: &Path, src: P, dst: P, perm: u32) -> Result<()> { + let (src, dst) = (indir.join(src), outdir.join(dst)); + fs::copy(&src, &dst).with_context(|| format!("Failed to copy {}", dst.file_name().unwrap().display()))?; + fs::set_permissions(&dst, Permissions::from_mode(perm))?; + Ok(()) +} + +pub(crate) fn build_binary(arch: &str) -> Result<()> { + let t = Arc::new(Task::new(&format!("Compiling binary for {arch}"))); + let cargo = env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()); + println!(); + let status = Command::new(cargo) + .current_dir(&*PROJECT_ROOT) + .env("CARGO_TARGET_DIR", &*TARGET_DIR) + .args([ + "build", + "--frozen", + "--release", + &format!("--target={arch}-unknown-linux-gnu"), + ]) + .status() + .with_context(|| "Failed to execute build command")?; + t.fix_text(); + + if !status.success() { + t.failure(); + return Err(anyhow!("Failed to compile binary")); + } + t.success(); + + let t = Task::new("Copying binary to dist"); + fs::create_dir_all(&*DIST_DIR).with_context(|| "Failed to create dist directory")?; + #[rustfmt::skip] + cp!(TARGET_DIR, DIST_DIR, "{arch}-unknown-linux-gnu/release/{NAME}", "{NAME}_{arch}-linux-gnu"); + t.success(); + + Ok(()) +} + +pub(crate) fn build(path: Option, arch: &str) -> Result<()> { + let outdir = path.unwrap_or(DIST_DIR.to_path_buf()); + + build_binary(arch)?; + build_man(&outdir.join("man"))?; + + Ok(()) +} diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 93d82a1..586e4f1 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -2,11 +2,13 @@ use std::path::{Path, PathBuf}; use std::{env, process::exit, sync::LazyLock}; use anyhow::Result; +use dist::build; pub(crate) use man::build_man; pub(crate) use task::Task; mod args; +mod dist; mod man; mod task; @@ -20,6 +22,7 @@ static PROJECT_ROOT: LazyLock = LazyLock::new(|| { static DIST_DIR: LazyLock = LazyLock::new(|| PROJECT_ROOT.join("target/dist")); static TARGET_DIR: LazyLock = LazyLock::new(|| PROJECT_ROOT.join("target")); +const NAME: &str = "mpdris"; const MANPATH: &str = "resources/man"; fn main() { @@ -36,5 +39,6 @@ fn try_main() -> Result<()> { match args.task { Man(task) => build_man(&task.dir), + Build(task) => build(task.path, &task.arch), } } From ddbb386cdb49fe835830c24a293b0f9bee5df80d Mon Sep 17 00:00:00 2001 From: "J. Gerhards" Date: Thu, 12 Jun 2025 17:23:00 +0200 Subject: [PATCH 4/9] Add xtask to clean the dist directory --- xtask/src/args.rs | 6 ++++++ xtask/src/dist.rs | 11 +++++++++++ xtask/src/main.rs | 3 ++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/xtask/src/args.rs b/xtask/src/args.rs index e3fe53b..a8c1107 100644 --- a/xtask/src/args.rs +++ b/xtask/src/args.rs @@ -16,6 +16,7 @@ pub(crate) struct Args { pub(crate) enum Task { Man(ManTask), Build(BuildTask), + CleanDist(CleanTask), } #[derive(FromArgs, PartialEq, Debug)] @@ -39,3 +40,8 @@ pub(crate) struct BuildTask { /// path to install the files to instead of target/dist/ pub(crate) path: Option, } + +#[derive(FromArgs, PartialEq, Debug)] +/// Clean the target/dist directory +#[argh(subcommand, name = "clean-dist", help_triggers("-h", "--help"))] +pub(crate) struct CleanTask {} diff --git a/xtask/src/dist.rs b/xtask/src/dist.rs index ff5eb28..64c0529 100644 --- a/xtask/src/dist.rs +++ b/xtask/src/dist.rs @@ -17,6 +17,17 @@ fn copy>(indir: &Path, outdir: &Path, src: P, dst: P, perm: u32) Ok(()) } +pub(crate) fn clean_dist() -> Result<()> { + let t = Task::new("Cleaning dist"); + + if DIST_DIR.exists() { + fs::remove_dir_all(&*DIST_DIR).with_context(|| "Failed to delete the dist directory")?; + } + + t.success(); + Ok(()) +} + pub(crate) fn build_binary(arch: &str) -> Result<()> { let t = Arc::new(Task::new(&format!("Compiling binary for {arch}"))); let cargo = env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()); diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 586e4f1..a7ce60c 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -2,7 +2,7 @@ use std::path::{Path, PathBuf}; use std::{env, process::exit, sync::LazyLock}; use anyhow::Result; -use dist::build; +use dist::{build, clean_dist}; pub(crate) use man::build_man; pub(crate) use task::Task; @@ -40,5 +40,6 @@ fn try_main() -> Result<()> { match args.task { Man(task) => build_man(&task.dir), Build(task) => build(task.path, &task.arch), + CleanDist(..) => clean_dist(), } } From 2b73f316235d208590440658db08f67e41b0477c Mon Sep 17 00:00:00 2001 From: "J. Gerhards" Date: Thu, 12 Jun 2025 17:26:04 +0200 Subject: [PATCH 5/9] Add xtask to make an install --- xtask/src/args.rs | 15 +++++++++++++ xtask/src/dist.rs | 56 +++++++++++++++++++++++++++++++++++++++++++++++ xtask/src/main.rs | 3 ++- 3 files changed, 73 insertions(+), 1 deletion(-) diff --git a/xtask/src/args.rs b/xtask/src/args.rs index a8c1107..c4b8a35 100644 --- a/xtask/src/args.rs +++ b/xtask/src/args.rs @@ -16,6 +16,7 @@ pub(crate) struct Args { pub(crate) enum Task { Man(ManTask), Build(BuildTask), + Install(InstallTask), CleanDist(CleanTask), } @@ -41,6 +42,20 @@ pub(crate) struct BuildTask { pub(crate) path: Option, } +#[derive(FromArgs, PartialEq, Debug)] +/// Create an install using the default arch or if provided. +/// Result is written to target/dist/ or if provided. +/// Note: install does NOT compile anything, for that please use build +#[argh(subcommand, name = "install", help_triggers("-h", "--help"))] +pub(crate) struct InstallTask { + #[argh(option, default = "env::consts::ARCH.to_string()")] + /// the arch to compile for + pub(crate) arch: String, + #[argh(positional)] + /// path to install the files to instead of target/dist/ + pub(crate) path: Option, +} + #[derive(FromArgs, PartialEq, Debug)] /// Clean the target/dist directory #[argh(subcommand, name = "clean-dist", help_triggers("-h", "--help"))] diff --git a/xtask/src/dist.rs b/xtask/src/dist.rs index 64c0529..6e6545f 100644 --- a/xtask/src/dist.rs +++ b/xtask/src/dist.rs @@ -5,9 +5,15 @@ use std::{env, io::Write, os::unix::fs::PermissionsExt, process::Command, sync:: use crate::{DIST_DIR, NAME, PROJECT_ROOT, TARGET_DIR, Task, build_man}; use anyhow::{Context, Result, anyhow}; macro_rules! cp { + ($outdir:expr, $src:literal, $dst:literal, $perm:expr) => { + cp!(&$crate::PROJECT_ROOT, $outdir, $src, $dst, $perm) + }; ($indir:expr, $outdir:expr, $src:literal, $dst:literal, $perm:expr) => { $crate::dist::copy($indir, $outdir, &::std::format!($src), &::std::format!($dst), $perm) }; + ($indir:expr, $outdir:expr, $src:literal, $dst:literal) => { + $crate::dist::copy_dir_all($indir.join(::std::format!($src)), $outdir.join(::std::format!($dst))) + }; } fn copy>(indir: &Path, outdir: &Path, src: P, dst: P, perm: u32) -> Result<()> { @@ -17,6 +23,20 @@ fn copy>(indir: &Path, outdir: &Path, src: P, dst: P, perm: u32) Ok(()) } +fn copy_dir_all, Q: AsRef>(src: P, dst: Q) -> Result<()> { + fs::create_dir_all(&dst)?; + for entry in fs::read_dir(src)? { + let entry = entry?; + let path = entry.path(); + if path.is_dir() { + copy_dir_all(path, dst.as_ref().join(entry.file_name()))?; + } else { + fs::copy(path, dst.as_ref().join(entry.file_name()))?; + } + } + Ok(()) +} + pub(crate) fn clean_dist() -> Result<()> { let t = Task::new("Cleaning dist"); @@ -68,3 +88,39 @@ pub(crate) fn build(path: Option, arch: &str) -> Result<()> { Ok(()) } + +pub(crate) fn install(path: Option, arch: &str) -> Result<()> { + let outdir = path.unwrap_or(DIST_DIR.join(arch)); + + install_create_dirs(&outdir).with_context(|| "Failed to create dist directory structure")?; + install_copy_files(&outdir, arch).with_context(|| "Failed to copy assets to install dir")?; + + Ok(()) +} + +fn install_create_dirs(outdir: &Path) -> Result<()> { + let t = Task::new("Creating directory structure"); + fs::create_dir_all(outdir)?; + fs::create_dir_all(outdir.join("usr/bin"))?; + fs::create_dir_all(outdir.join("usr/lib/systemd/user"))?; + fs::create_dir_all(outdir.join(format!("usr/share/doc/{NAME}")))?; + fs::create_dir_all(outdir.join(format!("usr/share/licenses/{NAME}")))?; + fs::create_dir_all(outdir.join("usr/share/man"))?; + + t.success(); + Ok(()) +} + +#[rustfmt::skip] +fn install_copy_files(outdir: &Path, arch: &str) -> Result<()> { + let t = Task::new("Copying files to dist"); + cp!(&DIST_DIR, outdir, "{NAME}_{arch}-linux-gnu", "usr/bin/{NAME}", 0o755)?; + cp!(outdir, "resources/mpdris.service", "usr/lib/systemd/user/mpdris.service", 0o644)?; + cp!(outdir, "resources/sample.mpdris.conf", "usr/share/doc/{NAME}/sample.mpdris.conf", 0o644)?; + cp!(outdir, "README.md", "usr/share/doc/{NAME}/README.md", 0o644)?; + cp!(outdir, "LICENSE", "usr/share/licenses/{NAME}/LICENSE", 0o644)?; + cp!(DIST_DIR, outdir, "man", "usr/share/man")?; + + t.success(); + Ok(()) +} diff --git a/xtask/src/main.rs b/xtask/src/main.rs index a7ce60c..93bb8c7 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -2,7 +2,7 @@ use std::path::{Path, PathBuf}; use std::{env, process::exit, sync::LazyLock}; use anyhow::Result; -use dist::{build, clean_dist}; +use dist::{build, clean_dist, install}; pub(crate) use man::build_man; pub(crate) use task::Task; @@ -40,6 +40,7 @@ fn try_main() -> Result<()> { match args.task { Man(task) => build_man(&task.dir), Build(task) => build(task.path, &task.arch), + Install(task) => install(task.path, &task.arch), CleanDist(..) => clean_dist(), } } From b58deda86ccdfb1b033450e998787636a62e98fb Mon Sep 17 00:00:00 2001 From: "J. Gerhards" Date: Thu, 12 Jun 2025 17:30:36 +0200 Subject: [PATCH 6/9] Seperate mpdris.service file for /usr/local/ and /usr/ install Add a seperate mpdris.service.local file for /usr/local/ installs and a mpdris.service file for /usr/ installs --- README.md | 7 ++++--- resources/mpdris.service | 2 +- resources/mpdris.service.local | 13 +++++++++++++ 3 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 resources/mpdris.service.local diff --git a/README.md b/README.md index 1da2a4d..49b84af 100644 --- a/README.md +++ b/README.md @@ -80,8 +80,8 @@ You can either build the AUR-package yourself, as detailed below, or use your fa ```bash cargo build --release ``` -3. Copy the resulting file from `target/release/mpdris` to `/usr/local/bin` -4. Copy `resources/mpdris.service` to `/usr/local/lib/systemd/user` (You might have to create that directory first) +3. Move the resulting file from `target/release/mpdris` to `/usr/local/bin` +4. Copy `resources/mpdris.service.local` to `/usr/local/lib/systemd/user` (You might have to create that directory first) and rename it to `mpdris.service` 5. Enable the service to start it with MPD ```bash systemctl --user enable mpdris.service @@ -96,7 +96,8 @@ You can either build the AUR-package yourself, as detailed below, or use your fa ```bash chmod +x /usr/local/bin/mpdris ``` -5. Download and move [mpdris.service](https://github.com/jasger9000/mpdris/blob/main/resources/mpdris.service) to `/usr/local/lib/systemd/user` (You might have to create that directory first) +5. Move `mpdris.service.local` to `/usr/local/lib/systemd/user` (You might have to create that directory first) and rename it to `mpdris.service` + - mpdris.service.local can be found in the release tarball or downloaded [here](https://github.com/jasger9000/mpdris/blob/main/resources/mpdris.service.local) 6. Enable the service to start it with MPD ```bash systemctl --user enable mpdris.service diff --git a/resources/mpdris.service b/resources/mpdris.service index 9f51b81..99b147d 100644 --- a/resources/mpdris.service +++ b/resources/mpdris.service @@ -7,7 +7,7 @@ After=mpd.service Type=notify-reload NotifyAccess=main Restart=on-failure -ExecStart=/usr/local/bin/mpdris --service +ExecStart=/usr/bin/mpdris --service [Install] WantedBy=mpd.service diff --git a/resources/mpdris.service.local b/resources/mpdris.service.local new file mode 100644 index 0000000..9f51b81 --- /dev/null +++ b/resources/mpdris.service.local @@ -0,0 +1,13 @@ +[Unit] +Description=Music Player Daemon MPRIS bridge +BindsTo=mpd.service +After=mpd.service + +[Service] +Type=notify-reload +NotifyAccess=main +Restart=on-failure +ExecStart=/usr/local/bin/mpdris --service + +[Install] +WantedBy=mpd.service From 0420976665e3b0b266df98f47d82fb7c90f5c12d Mon Sep 17 00:00:00 2001 From: "J. Gerhards" Date: Thu, 12 Jun 2025 17:33:05 +0200 Subject: [PATCH 7/9] Add xtask to build release assets --- xtask/Cargo.toml | 4 +++ xtask/src/args.rs | 6 +++++ xtask/src/dist.rs | 68 +++++++++++++++++++++++++++++++++++++++++++++-- xtask/src/main.rs | 3 ++- 4 files changed, 78 insertions(+), 3 deletions(-) diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index ab8b7c7..099a237 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -6,4 +6,8 @@ edition = "2024" [dependencies] anyhow = "1.0.98" argh = "0.1.13" +tar = "0.4.44" +sha2 = "0.10.9" flate2 = { version = "1.1.2", default-features = false, features = ["zlib-rs"] } +hex = "0.4.3" + diff --git a/xtask/src/args.rs b/xtask/src/args.rs index c4b8a35..e8195f9 100644 --- a/xtask/src/args.rs +++ b/xtask/src/args.rs @@ -18,6 +18,7 @@ pub(crate) enum Task { Build(BuildTask), Install(InstallTask), CleanDist(CleanTask), + MakeRelease(ReleaseTask), } #[derive(FromArgs, PartialEq, Debug)] @@ -60,3 +61,8 @@ pub(crate) struct InstallTask { /// Clean the target/dist directory #[argh(subcommand, name = "clean-dist", help_triggers("-h", "--help"))] pub(crate) struct CleanTask {} + +#[derive(FromArgs, PartialEq, Debug)] +/// Create release assets (tarballs, binaries and SHA256 checksums) for x86_64, aarch64, i68 +#[argh(subcommand, name = "make-release-assets", help_triggers("-h", "--help"))] +pub(crate) struct ReleaseTask {} diff --git a/xtask/src/dist.rs b/xtask/src/dist.rs index 6e6545f..219748b 100644 --- a/xtask/src/dist.rs +++ b/xtask/src/dist.rs @@ -1,9 +1,12 @@ use std::fs; use std::path::{Path, PathBuf}; -use std::{env, io::Write, os::unix::fs::PermissionsExt, process::Command, sync::Arc}; +use std::{env, fs::Permissions, io::Write, os::unix::fs::PermissionsExt, process::Command, sync::Arc}; use crate::{DIST_DIR, NAME, PROJECT_ROOT, TARGET_DIR, Task, build_man}; use anyhow::{Context, Result, anyhow}; +use flate2::{Compression, write::GzEncoder}; +use sha2::{Digest, Sha256}; + macro_rules! cp { ($outdir:expr, $src:literal, $dst:literal, $perm:expr) => { cp!(&$crate::PROJECT_ROOT, $outdir, $src, $dst, $perm) @@ -74,7 +77,7 @@ pub(crate) fn build_binary(arch: &str) -> Result<()> { let t = Task::new("Copying binary to dist"); fs::create_dir_all(&*DIST_DIR).with_context(|| "Failed to create dist directory")?; #[rustfmt::skip] - cp!(TARGET_DIR, DIST_DIR, "{arch}-unknown-linux-gnu/release/{NAME}", "{NAME}_{arch}-linux-gnu"); + cp!(TARGET_DIR, DIST_DIR, "{arch}-unknown-linux-gnu/release/{NAME}", "{NAME}_{arch}-linux-gnu")?; t.success(); Ok(()) @@ -124,3 +127,64 @@ fn install_copy_files(outdir: &Path, arch: &str) -> Result<()> { t.success(); Ok(()) } + +pub(crate) fn make_release_assets() -> Result<()> { + let archs = ["x86_64", "i686", "aarch64"]; + let mandir = DIST_DIR.join("man"); + let mut checksums = (Vec::new(), Vec::new()); + + if !DIST_DIR.is_dir() { + fs::create_dir_all(&*DIST_DIR).with_context(|| "Failed to create dist directory")?; + } + build_man(&mandir)?; + + for arch in archs { + println!("Making release for {arch}"); + + build_binary(arch)?; + let tarball_filename = format!("{NAME}_{arch}.tar.gz"); + let binary_filename = format!("{NAME}_{arch}-linux-gnu"); + let binary_outpath = PROJECT_ROOT.join(DIST_DIR.join(&binary_filename)); + + let t = Task::new("Making tar archive"); + let mut builder = tar::Builder::new(Vec::new()); + builder.mode(tar::HeaderMode::Deterministic); + builder.append_path_with_name(&binary_outpath, NAME)?; + builder.append_path_with_name(PROJECT_ROOT.join("resources/mpdris.service"), "mpdris.service")?; + builder.append_path_with_name(PROJECT_ROOT.join("resources/mpdris.service.local"), "mpdris.service.local")?; + builder.append_path_with_name(PROJECT_ROOT.join("resources/sample.mpdris.conf"), "sample.mpdris.conf")?; + builder.append_path_with_name(PROJECT_ROOT.join("README.md"), "README.md")?; + builder.append_path_with_name(PROJECT_ROOT.join("LICENSE"), "LICENSE")?; + builder.append_dir_all("man", &mandir)?; + + let archive = builder.into_inner()?; + t.success(); + + let t = Task::new("Compressing archive"); + let mut encoder = GzEncoder::new(Vec::new(), Compression::new(9)); + encoder.write_all(&archive)?; + + let compressed = encoder.finish()?; + drop(archive); + t.success(); + + let t = Task::new("Calculating checksums"); + let binary_hash = hex::encode(Sha256::digest(fs::read(&binary_outpath)?)); + let archive_hash = hex::encode(Sha256::digest(&compressed)); + checksums.0.push(format!("{binary_hash} {binary_filename}")); + checksums.1.push(format!("{archive_hash} {tarball_filename}")); + t.success(); + + let t = Task::new("Writing tarball"); + fs::write(DIST_DIR.join(tarball_filename), compressed).with_context(|| "failed to write compressed archive")?; + t.success(); + println!(); + } + + let t = Task::new("Writing checksum file"); + checksums.0.append(&mut checksums.1); + fs::write(DIST_DIR.join("SHA256sums.txt"), checksums.0.join("\n").as_bytes())?; + t.success(); + + Ok(()) +} diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 93bb8c7..3b88e58 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -2,7 +2,7 @@ use std::path::{Path, PathBuf}; use std::{env, process::exit, sync::LazyLock}; use anyhow::Result; -use dist::{build, clean_dist, install}; +use dist::{build, clean_dist, install, make_release_assets}; pub(crate) use man::build_man; pub(crate) use task::Task; @@ -42,5 +42,6 @@ fn try_main() -> Result<()> { Build(task) => build(task.path, &task.arch), Install(task) => install(task.path, &task.arch), CleanDist(..) => clean_dist(), + MakeRelease(..) => make_release_assets(), } } From 0dff8822779337114c1831aa24112d5f70556c76 Mon Sep 17 00:00:00 2001 From: "J. Gerhards" Date: Thu, 12 Jun 2025 17:39:04 +0200 Subject: [PATCH 8/9] Add mention of release tarball in README installation section Add a mention of release tarballs in the README section for installing using a release binary. Additionally replace the mentions of mpdris.service with mpdris.service.local in that section. --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 49b84af..8508661 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ __Table of Contents:__ To install this application, you can either... - [Use the AUR package (Arch Linux only)](#use-the-aur-package) - [Build the application yourself](#build-the-application-yourself) -- [Install the application from a release binary](#install-using-release-binary) +- [Install the application from a release binary](#install-using-release-binarytarball) ### Use the AUR package > [!IMPORTANT] @@ -87,11 +87,11 @@ You can either build the AUR-package yourself, as detailed below, or use your fa systemctl --user enable mpdris.service ``` -### Install using release binary -2. Download the correct binary for your architecture +### Install using release binary/tarball 1. Go to the [release tab](https://github.com/jasger9000/mpdris/releases) +2. Download the correct tarball or binary for your architecture - If you don't know what your architecture is, you can find out by running `lscpu` -3. Copy the file to `/usr/local/bin` and rename it to `mpdris` +3. Move the binary to `/usr/local/bin` and rename it to `mpdris` if needed 4. Add the execute permission to the file with ```bash chmod +x /usr/local/bin/mpdris From ca58d811236d4d942b3dd465e8df6470b71eb77f Mon Sep 17 00:00:00 2001 From: "J. Gerhards" Date: Thu, 12 Jun 2025 17:41:49 +0200 Subject: [PATCH 9/9] Add Packaging section to README Add a Packaging section to the README mentioning the xtask build system and some short command references. --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 8508661..a9935f6 100644 --- a/README.md +++ b/README.md @@ -157,6 +157,13 @@ Contributions are always welcome! If you feel there's something missing/wrong/something that could be improved please open an [issue](https://github.com/jasger9000/mpdris/issues).
Or if you want to add something yourself, just [open a pull request](https://github.com/jasger9000/mpdris/pulls) and I will have a look at it as soon as I can. +## Packaging +If you want to create a package of this application yourself, you can use the xtask cargo subcommand.
+Simply use `cargo xtask build [] [--arch ]` to build the project.
+To create an install from that build, use `cargo xtask install [--arch ]`. Note that you have to run the build command first though.
+You can also just create the manpage structure using `cargo xtask man `.
+To create release assets (tarballs, binaries, SHA256sums) for x86_64, i686 and aarch64 with `cargo xtask make-release-assets`. + ## Licence The Project is Licensed under the [MIT Licence](https://github.com/jasger9000/mpdris/?tab=MIT-1-ov-file)