diff --git a/README.md b/README.md index 0a7ee44..4b8ce9e 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,7 @@ Pull requests for additional backends are welcome! | Python | `pip` | `[python]` | built-in | | | Rust | `cargo` | `[rust]` | built-in | | | Rustup | `rustup` | `[rustup]` | built-in | See the comments [below](#rustup) about the syntax of the packages in the group file. | +| Void Linux | `xbps` | `[void]` | built-in | | Backends that have a `feature flag` require setting the respective flag for the build process. The appropriate system libraries and their header files must be present on the machine and be detectable by `pkg-config`. diff --git a/crates/pacdef_core/Cargo.toml b/crates/pacdef_core/Cargo.toml index 802d8fa..95a5240 100644 --- a/crates/pacdef_core/Cargo.toml +++ b/crates/pacdef_core/Cargo.toml @@ -17,6 +17,7 @@ path-absolutize = "3.1" regex = { version = "1.10", default-features = false, features = ["std"] } termios = "0.3" walkdir = "2.5" +libc = "0.2" serde = "1.0" serde_derive = "1.0" @@ -29,12 +30,10 @@ pacdef_macros = { path = "../pacdef_macros", version = "1.0" } alpm = { version = "3.0", optional = true } rust-apt = { version = "0.7", optional = true } -libc = { version = "0.2", optional = true } # for debian - [dev-dependencies] rstest = "0.18" [features] default = [] arch = ["dep:alpm"] -debian = ["dep:rust-apt", "dep:libc"] +debian = ["dep:rust-apt"] diff --git a/crates/pacdef_core/src/backend/actual/debian.rs b/crates/pacdef_core/src/backend/actual/debian.rs index 55e2088..075db4b 100644 --- a/crates/pacdef_core/src/backend/actual/debian.rs +++ b/crates/pacdef_core/src/backend/actual/debian.rs @@ -1,5 +1,4 @@ use std::collections::HashSet; -use std::process::Command; use std::process::ExitStatus; use anyhow::{Context, Result}; @@ -8,6 +7,7 @@ use rust_apt::new_cache; use crate::backend::backend_trait::{Backend, Switches, Text}; use crate::backend::macros::impl_backend_constants; +use crate::backend::root::build_base_command_with_privileges; use crate::{Group, Package}; #[derive(Debug, Clone)] @@ -104,19 +104,3 @@ impl Debian { } } } - -fn we_are_root() -> bool { - let uid = unsafe { libc::geteuid() }; - uid == 0 -} - -fn build_base_command_with_privileges(binary: &str) -> Command { - let cmd = if we_are_root() { - Command::new(binary) - } else { - let mut cmd = Command::new("sudo"); - cmd.arg(binary); - cmd - }; - cmd -} diff --git a/crates/pacdef_core/src/backend/actual/mod.rs b/crates/pacdef_core/src/backend/actual/mod.rs index 36ad67e..ea93f66 100644 --- a/crates/pacdef_core/src/backend/actual/mod.rs +++ b/crates/pacdef_core/src/backend/actual/mod.rs @@ -6,3 +6,4 @@ pub mod flatpak; pub mod python; pub mod rust; pub mod rustup; +pub mod void; diff --git a/crates/pacdef_core/src/backend/actual/void.rs b/crates/pacdef_core/src/backend/actual/void.rs new file mode 100644 index 0000000..de476cd --- /dev/null +++ b/crates/pacdef_core/src/backend/actual/void.rs @@ -0,0 +1,136 @@ +use std::collections::HashSet; +use std::process::{Command, ExitStatus}; + +use anyhow::{Context, Result}; +use regex::Regex; + +use crate::backend::backend_trait::{Backend, Switches, Text}; +use crate::backend::macros::impl_backend_constants; +use crate::backend::root::build_base_command_with_privileges; +use crate::{Group, Package}; + +#[derive(Debug, Clone)] +pub struct Void { + pub(crate) packages: HashSet, +} + +const BINARY: Text = "xbps-install"; +const INSTALL_BINARY: Text = "xbps-install"; +const REMOVE_BINARY: Text = "xbps-remove"; +const QUERY_BINARY: Text = "xbps-query"; +const PKGDB_BINARY: Text = "xbps-pkgdb"; +const SECTION: Text = "void"; + +const SWITCHES_INFO: Switches = &[]; +const SWITCHES_INSTALL: Switches = &["-S"]; +const SWITCHES_MAKE_DEPENDENCY: Switches = &["-m", "auto"]; +const SWITCHES_NOCONFIRM: Switches = &["-y"]; +const SWITCHES_REMOVE: Switches = &["-R"]; + +const SUPPORTS_AS_DEPENDENCY: bool = true; + +impl Backend for Void { + impl_backend_constants!(); + + fn get_all_installed_packages(&self) -> Result> { + // Removes the package status and description from output + let re_str_1 = r"^ii |^uu |^hr |^\?\? | .*"; + // Removes the package version from output + let re_str_2 = r"-[^-]*$"; + let re1 = Regex::new(re_str_1)?; + let re2 = Regex::new(re_str_2)?; + let mut cmd = Command::new(QUERY_BINARY); + cmd.args(["-l"]); + let output = String::from_utf8(cmd.output()?.stdout)?; + + let packages = output + .lines() + .map(|line| { + let result = re1.replace_all(line, ""); + let result = re2.replace_all(&result, ""); + result.to_string().into() + }) + .collect(); + Ok(packages) + } + + fn get_explicitly_installed_packages(&self) -> Result> { + // Removes the package version from output + let re_str = r"-[^-]*$"; + let re = Regex::new(re_str)?; + let mut cmd = Command::new(QUERY_BINARY); + cmd.args(["-m"]); + let output = String::from_utf8(cmd.output()?.stdout)?; + + let packages = output + .lines() + .map(|line| { + let result = re.replace_all(line, "").to_string(); + result.into() + }) + .collect(); + Ok(packages) + } + + /// Install the specified packages. + fn install_packages(&self, packages: &[Package], noconfirm: bool) -> Result { + let mut cmd = build_base_command_with_privileges(INSTALL_BINARY); + cmd.args(self.get_switches_install()); + + if noconfirm { + cmd.args(self.get_switches_noconfirm()); + } + + for p in packages { + cmd.arg(format!("{p}")); + } + + cmd.status() + .with_context(|| format!("running command {cmd:?}")) + } + + fn remove_packages(&self, packages: &[Package], noconfirm: bool) -> Result { + let mut cmd = build_base_command_with_privileges(REMOVE_BINARY); + cmd.args(self.get_switches_remove()); + + if noconfirm { + cmd.args(self.get_switches_noconfirm()); + } + + for p in packages { + cmd.arg(format!("{p}")); + } + + cmd.status() + .with_context(|| format!("running command [{cmd:?}]")) + } + + fn make_dependency(&self, packages: &[Package]) -> Result { + let mut cmd = build_base_command_with_privileges(PKGDB_BINARY); + cmd.args(self.get_switches_make_dependency()); + + for p in packages { + cmd.arg(format!("{p}")); + } + + cmd.status() + .with_context(|| format!("running command [{cmd:?}]")) + } + + /// Show information from package manager for package. + fn show_package_info(&self, package: &Package) -> Result { + let mut cmd = Command::new(QUERY_BINARY); + cmd.args(self.get_switches_info()); + cmd.arg(format!("{package}")); + cmd.status() + .with_context(|| format!("running command {cmd:?}")) + } +} + +impl Void { + pub(crate) fn new() -> Self { + Self { + packages: HashSet::new(), + } + } +} diff --git a/crates/pacdef_core/src/backend/mod.rs b/crates/pacdef_core/src/backend/mod.rs index 0664d7e..ab67304 100644 --- a/crates/pacdef_core/src/backend/mod.rs +++ b/crates/pacdef_core/src/backend/mod.rs @@ -2,6 +2,7 @@ mod actual; mod backend_trait; mod iter; mod macros; +mod root; mod todo_per_backend; pub use backend_trait::Backend; @@ -20,4 +21,5 @@ pub enum Backends { Python, Rust, Rustup, + Void, } diff --git a/crates/pacdef_core/src/backend/root.rs b/crates/pacdef_core/src/backend/root.rs new file mode 100644 index 0000000..3f17861 --- /dev/null +++ b/crates/pacdef_core/src/backend/root.rs @@ -0,0 +1,17 @@ +use std::process::Command; + +pub fn we_are_root() -> bool { + let uid = unsafe { libc::geteuid() }; + uid == 0 +} + +pub fn build_base_command_with_privileges(binary: &str) -> Command { + let cmd = if we_are_root() { + Command::new(binary) + } else { + let mut cmd = Command::new("sudo"); + cmd.arg(binary); + cmd + }; + cmd +}