use crate::installers::common::DistroFacade;
use crate::platform::detection::DistroFamily;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ShellCommand {
pub program: String,
pub args: Vec<String>,
pub env: Vec<(String, String)>,
}
impl ShellCommand {
pub fn to_command_string(&self) -> String {
let env_prefix = self
.env
.iter()
.map(|(k, v)| format!("{k}={v}"))
.collect::<Vec<_>>()
.join(" ");
let cmd = if self.args.is_empty() {
self.program.clone()
} else {
format!("{} {}", self.program, self.args.join(" "))
};
if env_prefix.is_empty() {
cmd
} else {
format!("{env_prefix} {cmd}")
}
}
}
#[derive(Debug, Clone, Default)]
pub struct AmdgpuConfig {
pub dry_run: bool,
pub force: bool,
}
pub struct AmdgpuInstaller {
config: AmdgpuConfig,
}
impl AmdgpuInstaller {
pub fn new(config: AmdgpuConfig) -> Self {
Self { config }
}
pub fn with_defaults() -> Self {
Self::new(AmdgpuConfig::default())
}
pub fn required_packages(&self, distro: &DistroFacade) -> Vec<&str> {
match distro.family() {
DistroFamily::Debian => vec![
"amdgpu-dkms",
"linux-headers-$(uname -r)",
"linux-modules-extra-$(uname -r)",
],
DistroFamily::Rhel => vec!["amdgpu-dkms", "kernel-devel"],
DistroFamily::Arch => vec!["amdgpu"],
DistroFamily::Suse => vec!["amdgpu-dkms", "kernel-default-devel"],
_ => vec!["amdgpu-dkms"],
}
}
pub fn rocm_packages(&self, _distro: &DistroFacade) -> Vec<&str> {
vec!["rocm-dev", "rocm-libs", "rocprofiler"]
}
pub fn build_package_install_command(
&self,
distro: &DistroFacade,
packages: &[&str],
) -> ShellCommand {
let (program, mut base_args) = match distro.family() {
DistroFamily::Debian => (
"sudo".to_string(),
vec![
"apt-get".to_string(),
"install".to_string(),
"-y".to_string(),
],
),
DistroFamily::Rhel => (
"sudo".to_string(),
vec!["dnf".to_string(), "install".to_string(), "-y".to_string()],
),
DistroFamily::Arch => (
"sudo".to_string(),
vec![
"pacman".to_string(),
"-S".to_string(),
"--noconfirm".to_string(),
],
),
DistroFamily::Suse => (
"sudo".to_string(),
vec![
"zypper".to_string(),
"install".to_string(),
"-y".to_string(),
],
),
_ => (
"sudo".to_string(),
vec![
"apt-get".to_string(),
"install".to_string(),
"-y".to_string(),
],
),
};
for pkg in packages {
base_args.push(pkg.to_string());
}
if self.config.dry_run {
let mut preview = base_args;
preview.insert(0, program.clone());
return ShellCommand {
program: "echo".to_string(),
args: preview,
env: vec![],
};
}
ShellCommand {
program,
args: base_args,
env: vec![],
}
}
pub fn build_amdgpu_install_deb_commands(&self, codename: &str) -> Vec<ShellCommand> {
let deb_url = format!(
"https://repo.radeon.com/amdgpu-install/7.2.4/ubuntu/{}/amdgpu-install_7.2.4.70204-1_all.deb",
codename
);
let deb_file = "amdgpu-install_7.2.4.70204-1_all.deb";
vec![
ShellCommand {
program: "wget".to_string(),
args: vec!["-q".to_string(), deb_url],
env: vec![],
},
ShellCommand {
program: "sudo".to_string(),
args: vec![
"apt".to_string(),
"install".to_string(),
"-y".to_string(),
format!("./{deb_file}"),
],
env: vec![],
},
ShellCommand {
program: "sudo".to_string(),
args: vec!["apt".to_string(), "update".to_string()],
env: vec![],
},
ShellCommand {
program: "sudo".to_string(),
args: vec![
"apt".to_string(),
"install".to_string(),
"-y".to_string(),
"linux-headers-$(uname -r)".to_string(),
"linux-modules-extra-$(uname -r)".to_string(),
],
env: vec![],
},
ShellCommand {
program: "sudo".to_string(),
args: vec![
"apt".to_string(),
"install".to_string(),
"-y".to_string(),
"amdgpu-dkms".to_string(),
],
env: vec![],
},
]
}
pub fn build_verify_command(&self) -> ShellCommand {
ShellCommand {
program: "lsmod".to_string(),
args: vec![],
env: vec![],
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::platform::detection::{DistroFamily, DistroInfo, PackageManager};
fn make_distro(family: DistroFamily) -> DistroFacade {
DistroFacade::from_info(DistroInfo {
id: match family {
DistroFamily::Debian => "ubuntu",
DistroFamily::Rhel => "fedora",
DistroFamily::Arch => "arch",
DistroFamily::Suse => "opensuse",
DistroFamily::Unknown => "unknown",
}
.to_string(),
name: "Test Distro".to_string(),
version: "1.0".to_string(),
codename: "test".to_string(),
id_like: String::new(),
family,
pkg_manager: match family {
DistroFamily::Debian => PackageManager::Apt,
DistroFamily::Rhel => PackageManager::Dnf,
DistroFamily::Arch => PackageManager::Pacman,
DistroFamily::Suse => PackageManager::Zypper,
DistroFamily::Unknown => PackageManager::Apt,
},
})
}
#[test]
fn test_package_install_command_debian() {
let installer = AmdgpuInstaller::with_defaults();
let distro = make_distro(DistroFamily::Debian);
let cmd = installer.build_package_install_command(&distro, &["amdgpu-dkms"]);
assert_eq!(cmd.program, "sudo");
assert!(cmd.args.contains(&"apt-get".to_string()));
assert!(cmd.args.contains(&"install".to_string()));
assert!(cmd.args.contains(&"-y".to_string()));
assert!(cmd.args.contains(&"amdgpu-dkms".to_string()));
}
#[test]
fn test_package_install_command_rhel() {
let installer = AmdgpuInstaller::with_defaults();
let distro = make_distro(DistroFamily::Rhel);
let cmd = installer.build_package_install_command(&distro, &["amdgpu-dkms"]);
assert_eq!(cmd.program, "sudo");
assert!(cmd.args.contains(&"dnf".to_string()));
assert!(cmd.args.contains(&"install".to_string()));
assert!(cmd.args.contains(&"-y".to_string()));
}
#[test]
fn test_package_install_command_arch() {
let installer = AmdgpuInstaller::with_defaults();
let distro = make_distro(DistroFamily::Arch);
let cmd = installer.build_package_install_command(&distro, &["amdgpu"]);
assert_eq!(cmd.program, "sudo");
assert!(cmd.args.contains(&"pacman".to_string()));
assert!(cmd.args.contains(&"-S".to_string()));
assert!(cmd.args.contains(&"--noconfirm".to_string()));
assert!(cmd.args.contains(&"amdgpu".to_string()));
}
#[test]
fn test_package_install_command_suse() {
let installer = AmdgpuInstaller::with_defaults();
let distro = make_distro(DistroFamily::Suse);
let cmd = installer.build_package_install_command(&distro, &["amdgpu-dkms"]);
assert_eq!(cmd.program, "sudo");
assert!(cmd.args.contains(&"zypper".to_string()));
assert!(cmd.args.contains(&"install".to_string()));
assert!(cmd.args.contains(&"-y".to_string()));
}
#[test]
fn test_required_packages_debian() {
let installer = AmdgpuInstaller::with_defaults();
let distro = make_distro(DistroFamily::Debian);
let pkgs = installer.required_packages(&distro);
assert!(pkgs.contains(&"amdgpu-dkms"));
}
#[test]
fn test_required_packages_arch() {
let installer = AmdgpuInstaller::with_defaults();
let distro = make_distro(DistroFamily::Arch);
let pkgs = installer.required_packages(&distro);
assert!(pkgs.contains(&"amdgpu"));
}
#[test]
fn test_amdgpu_install_deb_commands() {
let installer = AmdgpuInstaller::with_defaults();
let cmds = installer.build_amdgpu_install_deb_commands("noble");
assert_eq!(cmds.len(), 5);
assert_eq!(cmds[0].program, "wget");
assert!(cmds[0].args.iter().any(|a| a.contains("repo.radeon.com")));
assert!(cmds[0].args.iter().any(|a| a.contains("noble")));
assert!(cmds[1].args.iter().any(|a| a.contains("amdgpu-install")));
assert!(cmds[2].args.contains(&"update".to_string()));
assert!(cmds[3].args.iter().any(|a| a.contains("linux-headers")));
assert!(cmds[4].args.contains(&"amdgpu-dkms".to_string()));
}
#[test]
fn test_verify_command() {
let installer = AmdgpuInstaller::with_defaults();
let cmd = installer.build_verify_command();
assert_eq!(cmd.program, "lsmod");
}
}