1use crate::installers::common::{command_exists, DistroFacade};
12use std::fmt;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
20pub enum MlStackComponent {
21 RocmConfig,
23 PyTorch,
25 OnnxRuntime,
27 MigraphX,
29 Megatron,
31 FlashAttention,
33 Rccl,
35 Mpi,
37}
38
39impl fmt::Display for MlStackComponent {
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 match self {
42 MlStackComponent::RocmConfig => write!(f, "rocm_config"),
43 MlStackComponent::PyTorch => write!(f, "pytorch"),
44 MlStackComponent::OnnxRuntime => write!(f, "onnxruntime"),
45 MlStackComponent::MigraphX => write!(f, "migraphx"),
46 MlStackComponent::Megatron => write!(f, "megatron"),
47 MlStackComponent::FlashAttention => write!(f, "flash_attention"),
48 MlStackComponent::Rccl => write!(f, "rccl"),
49 MlStackComponent::Mpi => write!(f, "mpi"),
50 }
51 }
52}
53
54#[derive(Debug, Clone, Copy, PartialEq, Eq)]
56pub enum InstallMode {
57 All,
59 Selective,
61}
62
63#[derive(Debug, Clone)]
65pub struct MlStackConfig {
66 pub python_bin: String,
68 pub force_reinstall: bool,
70 pub dry_run: bool,
72 pub mode: InstallMode,
74 pub selected_components: Vec<MlStackComponent>,
76}
77
78impl Default for MlStackConfig {
79 fn default() -> Self {
80 Self {
81 python_bin: "python3".to_string(),
82 force_reinstall: false,
83 dry_run: false,
84 mode: InstallMode::All,
85 selected_components: vec![],
86 }
87 }
88}
89
90#[derive(Debug, Clone, PartialEq, Eq)]
92pub struct PipCommand {
93 pub program: String,
95 pub args: Vec<String>,
97}
98
99impl PipCommand {
100 pub fn to_command_string(&self) -> String {
102 if self.args.is_empty() {
103 self.program.clone()
104 } else {
105 format!("{} {}", self.program, self.args.join(" "))
106 }
107 }
108}
109
110#[derive(Debug, Clone, PartialEq, Eq)]
112pub struct SystemCommand {
113 pub program: String,
115 pub args: Vec<String>,
117}
118
119impl SystemCommand {
120 pub fn to_command_string(&self) -> String {
122 if self.args.is_empty() {
123 self.program.clone()
124 } else {
125 format!("{} {}", self.program, self.args.join(" "))
126 }
127 }
128}
129
130pub struct MlStackInstaller {
132 config: MlStackConfig,
133}
134
135impl MlStackInstaller {
136 pub fn new(config: MlStackConfig) -> Self {
138 Self { config }
139 }
140
141 pub fn with_defaults() -> Self {
143 Self::new(MlStackConfig::default())
144 }
145
146 pub fn install_order() -> Vec<MlStackComponent> {
162 vec![
163 MlStackComponent::RocmConfig,
164 MlStackComponent::PyTorch,
165 MlStackComponent::OnnxRuntime,
166 MlStackComponent::MigraphX,
167 MlStackComponent::Megatron,
168 MlStackComponent::FlashAttention,
169 MlStackComponent::Rccl,
170 MlStackComponent::Mpi,
171 ]
172 }
173
174 pub fn build_rocm_config_content(&self) -> String {
183 r#"# ROCm Configuration File
184# Created by ML Stack Installation Script
185
186export HSA_OVERRIDE_GFX_VERSION=11.0.0
187export PYTORCH_ROCM_ARCH=gfx1100
188export ROCM_PATH=/opt/rocm
189
190# Performance Settings
191export HSA_ENABLE_SDMA=0
192export GPU_MAX_HEAP_SIZE=100
193export GPU_MAX_ALLOC_PERCENT=100
194
195# MIOpen Settings
196export MIOPEN_DEBUG_CONV_IMPLICIT_GEMM=1
197export MIOPEN_FIND_MODE=3
198export MIOPEN_FIND_ENFORCE=3
199"#
200 .to_string()
201 }
202
203 pub fn rocm_config_path(&self) -> String {
205 format!(
206 "{}/.rocmrc",
207 std::env::var("HOME").unwrap_or_else(|_| "/root".to_string())
208 )
209 }
210
211 pub fn build_migraphx_install_command(&self, distro: &DistroFacade) -> SystemCommand {
217 if distro.uses_apt() {
218 SystemCommand {
219 program: "sudo".to_string(),
220 args: vec![
221 "apt-get".to_string(),
222 "install".to_string(),
223 "-y".to_string(),
224 "migraphx".to_string(),
225 "python3-migraphx".to_string(),
226 ],
227 }
228 } else if distro.uses_dnf() {
229 SystemCommand {
230 program: "sudo".to_string(),
231 args: vec![
232 "dnf".to_string(),
233 "install".to_string(),
234 "-y".to_string(),
235 "migraphx".to_string(),
236 "python3-migraphx".to_string(),
237 ],
238 }
239 } else if distro.uses_pacman() {
240 SystemCommand {
241 program: "sudo".to_string(),
242 args: vec![
243 "pacman".to_string(),
244 "-S".to_string(),
245 "--noconfirm".to_string(),
246 "migraphx".to_string(),
247 "python-migraphx".to_string(),
248 ],
249 }
250 } else if distro.uses_zypper() {
251 SystemCommand {
252 program: "sudo".to_string(),
253 args: vec![
254 "zypper".to_string(),
255 "install".to_string(),
256 "-y".to_string(),
257 "migraphx".to_string(),
258 "python3-migraphx".to_string(),
259 ],
260 }
261 } else {
262 SystemCommand {
263 program: "sudo".to_string(),
264 args: vec![
265 "apt-get".to_string(),
266 "install".to_string(),
267 "-y".to_string(),
268 "migraphx".to_string(),
269 "python3-migraphx".to_string(),
270 ],
271 }
272 }
273 }
274
275 pub fn build_rccl_install_command(&self, distro: &DistroFacade) -> SystemCommand {
281 if distro.uses_apt() {
282 SystemCommand {
283 program: "sudo".to_string(),
284 args: vec![
285 "apt-get".to_string(),
286 "install".to_string(),
287 "-y".to_string(),
288 "rccl".to_string(),
289 ],
290 }
291 } else if distro.uses_dnf() {
292 SystemCommand {
293 program: "sudo".to_string(),
294 args: vec![
295 "dnf".to_string(),
296 "install".to_string(),
297 "-y".to_string(),
298 "rccl".to_string(),
299 ],
300 }
301 } else if distro.uses_pacman() {
302 SystemCommand {
303 program: "sudo".to_string(),
304 args: vec![
305 "pacman".to_string(),
306 "-S".to_string(),
307 "--noconfirm".to_string(),
308 "rccl".to_string(),
309 ],
310 }
311 } else {
312 SystemCommand {
313 program: "sudo".to_string(),
314 args: vec![
315 "apt-get".to_string(),
316 "install".to_string(),
317 "-y".to_string(),
318 "rccl".to_string(),
319 ],
320 }
321 }
322 }
323
324 pub fn build_mpi_install_command(&self, distro: &DistroFacade) -> SystemCommand {
330 if distro.uses_apt() {
331 SystemCommand {
332 program: "sudo".to_string(),
333 args: vec![
334 "apt-get".to_string(),
335 "install".to_string(),
336 "-y".to_string(),
337 "openmpi-bin".to_string(),
338 "libopenmpi-dev".to_string(),
339 ],
340 }
341 } else if distro.uses_dnf() {
342 SystemCommand {
343 program: "sudo".to_string(),
344 args: vec![
345 "dnf".to_string(),
346 "install".to_string(),
347 "-y".to_string(),
348 "openmpi".to_string(),
349 "openmpi-devel".to_string(),
350 ],
351 }
352 } else if distro.uses_pacman() {
353 SystemCommand {
354 program: "sudo".to_string(),
355 args: vec![
356 "pacman".to_string(),
357 "-S".to_string(),
358 "--noconfirm".to_string(),
359 "openmpi".to_string(),
360 ],
361 }
362 } else {
363 SystemCommand {
364 program: "sudo".to_string(),
365 args: vec![
366 "apt-get".to_string(),
367 "install".to_string(),
368 "-y".to_string(),
369 "openmpi-bin".to_string(),
370 "libopenmpi-dev".to_string(),
371 ],
372 }
373 }
374 }
375
376 pub fn build_megatron_clone_command(&self) -> SystemCommand {
382 SystemCommand {
383 program: "git".to_string(),
384 args: vec![
385 "clone".to_string(),
386 "https://github.com/NVIDIA/Megatron-LM.git".to_string(),
387 ],
388 }
390 }
391
392 pub fn build_megatron_install_command(&self) -> PipCommand {
394 PipCommand {
395 program: self.config.python_bin.clone(),
396 args: vec![
397 "-m".to_string(),
398 "pip".to_string(),
399 "install".to_string(),
400 "--break-system-packages".to_string(),
401 "-e".to_string(),
402 ".".to_string(),
403 ],
404 }
405 }
406
407 pub fn prerequisite_commands(&self) -> &[&str] {
413 &["rocminfo", "python3", "pip3", "git", "cmake"]
414 }
415
416 pub fn check_prerequisite(&self, cmd: &str) -> bool {
418 command_exists(cmd)
419 }
420}
421
422#[cfg(test)]
427mod tests {
428 use super::*;
429 use crate::platform::detection::{DistroFamily, DistroInfo, PackageManager};
430
431 fn ubuntu_distro() -> DistroFacade {
432 DistroFacade::from_info(DistroInfo {
433 id: "ubuntu".to_string(),
434 name: "Ubuntu 24.04".to_string(),
435 version: "24.04".to_string(),
436 codename: "noble".to_string(),
437 family: DistroFamily::Debian,
438 pkg_manager: PackageManager::Apt,
439 id_like: "debian".to_string(),
440 })
441 }
442
443 fn fedora_distro() -> DistroFacade {
444 DistroFacade::from_info(DistroInfo {
445 id: "fedora".to_string(),
446 name: "Fedora 41".to_string(),
447 version: "41".to_string(),
448 codename: String::new(),
449 family: DistroFamily::Rhel,
450 pkg_manager: PackageManager::Dnf,
451 id_like: "fedora".to_string(),
452 })
453 }
454
455 fn arch_distro() -> DistroFacade {
456 DistroFacade::from_info(DistroInfo {
457 id: "arch".to_string(),
458 name: "Arch Linux".to_string(),
459 version: "rolling".to_string(),
460 codename: String::new(),
461 family: DistroFamily::Arch,
462 pkg_manager: PackageManager::Pacman,
463 id_like: "arch".to_string(),
464 })
465 }
466
467 #[test]
470 fn test_install_order() {
471 let order = MlStackInstaller::install_order();
472 assert_eq!(order.len(), 8);
473 assert_eq!(order[0], MlStackComponent::RocmConfig);
474 assert_eq!(order[1], MlStackComponent::PyTorch);
475 assert_eq!(order[7], MlStackComponent::Mpi);
476 }
477
478 #[test]
479 fn test_rocm_config_content() {
480 let installer = MlStackInstaller::with_defaults();
481 let content = installer.build_rocm_config_content();
482 assert!(content.contains("HSA_OVERRIDE_GFX_VERSION"));
483 assert!(content.contains("PYTORCH_ROCM_ARCH"));
484 assert!(content.contains("ROCM_PATH"));
485 assert!(content.contains("MIOPEN_DEBUG_CONV_IMPLICIT_GEMM"));
486 }
487
488 #[test]
489 fn test_rocm_config_path() {
490 let installer = MlStackInstaller::with_defaults();
491 let path = installer.rocm_config_path();
492 assert!(path.ends_with("/.rocmrc"));
493 }
494
495 #[test]
496 fn test_migraphx_install_apt() {
497 let installer = MlStackInstaller::with_defaults();
498 let distro = ubuntu_distro();
499 let cmd = installer.build_migraphx_install_command(&distro);
500 assert!(cmd.args.contains(&"migraphx".to_string()));
501 assert!(cmd.args.contains(&"python3-migraphx".to_string()));
502 }
503
504 #[test]
505 fn test_migraphx_install_dnf() {
506 let installer = MlStackInstaller::with_defaults();
507 let distro = fedora_distro();
508 let cmd = installer.build_migraphx_install_command(&distro);
509 assert!(cmd.args.contains(&"migraphx".to_string()));
510 }
511
512 #[test]
513 fn test_migraphx_install_pacman() {
514 let installer = MlStackInstaller::with_defaults();
515 let distro = arch_distro();
516 let cmd = installer.build_migraphx_install_command(&distro);
517 assert!(cmd.args.contains(&"python-migraphx".to_string()));
518 }
519
520 #[test]
521 fn test_rccl_install_apt() {
522 let installer = MlStackInstaller::with_defaults();
523 let distro = ubuntu_distro();
524 let cmd = installer.build_rccl_install_command(&distro);
525 assert!(cmd.args.contains(&"rccl".to_string()));
526 }
527
528 #[test]
529 fn test_mpi_install_apt() {
530 let installer = MlStackInstaller::with_defaults();
531 let distro = ubuntu_distro();
532 let cmd = installer.build_mpi_install_command(&distro);
533 assert!(cmd.args.contains(&"openmpi-bin".to_string()));
534 assert!(cmd.args.contains(&"libopenmpi-dev".to_string()));
535 }
536
537 #[test]
538 fn test_mpi_install_dnf() {
539 let installer = MlStackInstaller::with_defaults();
540 let distro = fedora_distro();
541 let cmd = installer.build_mpi_install_command(&distro);
542 assert!(cmd.args.contains(&"openmpi".to_string()));
543 assert!(cmd.args.contains(&"openmpi-devel".to_string()));
544 }
545
546 #[test]
547 fn test_megatron_clone_command() {
548 let installer = MlStackInstaller::with_defaults();
549 let cmd = installer.build_megatron_clone_command();
550 assert_eq!(cmd.program, "git");
551 assert!(cmd.args.contains(&"clone".to_string()));
552 assert!(cmd.args.iter().any(|a| a.contains("NVIDIA/Megatron-LM")));
553 }
554
555 #[test]
556 fn test_megatron_install_command() {
557 let installer = MlStackInstaller::with_defaults();
558 let cmd = installer.build_megatron_install_command();
559 assert_eq!(cmd.program, "python3");
560 assert!(cmd.args.contains(&"-e".to_string()));
561 assert!(cmd.args.contains(&".".to_string()));
562 }
563
564 #[test]
565 fn test_prerequisite_commands() {
566 let installer = MlStackInstaller::with_defaults();
567 let cmds = installer.prerequisite_commands();
568 assert!(cmds.contains(&"python3"));
569 assert!(cmds.contains(&"git"));
570 assert!(cmds.contains(&"cmake"));
571 }
572
573 #[test]
574 fn test_component_display() {
575 assert_eq!(format!("{}", MlStackComponent::PyTorch), "pytorch");
576 assert_eq!(format!("{}", MlStackComponent::RocmConfig), "rocm_config");
577 assert_eq!(format!("{}", MlStackComponent::Mpi), "mpi");
578 }
579}