Skip to main content

rusty_stack/installers/common/
package_mappings.rs

1//! Per-distro package name mappings.
2//!
3//! Maps generic (Debian-style) package names to the native names for each
4//! package manager (apt, pacman, dnf, yum, zypper). Covers the same packages
5//! as `scripts/lib/package_mappings.sh`.
6//!
7//! # Validation Assertions
8//!
9//! - **VAL-INFRA-004**: Package name mapping correct
10
11use crate::platform::detection::PackageManager;
12
13/// Lookup a mapped package name for a given package manager.
14///
15/// Returns the native package name if a mapping exists, otherwise returns
16/// the original name unchanged.
17pub fn map_package_name(generic_name: &str, pkg_mgr: PackageManager) -> String {
18    match pkg_mgr {
19        PackageManager::Apt => map_apt(generic_name),
20        PackageManager::Pacman => map_pacman(generic_name),
21        PackageManager::Dnf => map_dnf(generic_name),
22        PackageManager::Yum => map_yum(generic_name),
23        PackageManager::Zypper => map_zypper(generic_name),
24        PackageManager::Unknown => generic_name.to_string(),
25    }
26}
27
28/// Map multiple package names at once.
29pub fn map_package_names(generic_names: &[&str], pkg_mgr: PackageManager) -> Vec<String> {
30    generic_names
31        .iter()
32        .map(|name| map_package_name(name, pkg_mgr))
33        .collect()
34}
35
36/// Check if a mapping exists for a given package name and package manager.
37pub fn has_mapping(generic_name: &str, pkg_mgr: PackageManager) -> bool {
38    let mapped = map_package_name(generic_name, pkg_mgr);
39    // If the mapped name is different from the input, a mapping exists
40    mapped != generic_name || get_known_packages().contains(&generic_name)
41}
42
43/// Get all known generic package names.
44pub fn get_known_packages() -> &'static [&'static str] {
45    &[
46        // Build essentials
47        "build-essential",
48        "gcc",
49        "g++",
50        "make",
51        "cmake",
52        "ninja-build",
53        "pkg-config",
54        "autoconf",
55        "automake",
56        "libtool",
57        // Python development
58        "python3",
59        "python3-dev",
60        "python3-pip",
61        "python3-venv",
62        "python3-setuptools",
63        "python3-wheel",
64        "python3-numpy",
65        // ROCm tools
66        "rocminfo",
67        "rocprofiler",
68        "rocm-smi-lib",
69        "rocm-dev",
70        "rocm-libs",
71        "hip-runtime",
72        // ROCm libraries
73        "librccl-dev",
74        "librccl1",
75        "rccl",
76        "rccl-dev",
77        "migraphx",
78        "migraphx-dev",
79        "half",
80        "miopen-hip",
81        "rocblas",
82        // MPI
83        "libopenmpi-dev",
84        "openmpi-bin",
85        "openmpi",
86        "openmpi-devel",
87        "environment-modules",
88        // Build tools
89        "llvm-dev",
90        "clang",
91        "lld",
92        "lldb",
93        "libssl-dev",
94        "libffi-dev",
95        "zlib1g-dev",
96        // System utilities
97        "pciutils",
98        "mesa-utils",
99        "clinfo",
100        "libnuma-dev",
101        "numactl",
102        "hwloc",
103        "lshw",
104        "dmidecode",
105        // Version control
106        "git",
107        "git-lfs",
108        "gh",
109        // Network
110        "wget",
111        "curl",
112        "gnupg",
113        "ca-certificates",
114        "rsync",
115        "ssh",
116        // Libraries
117        "libbz2-dev",
118        "liblzma-dev",
119        "libsqlite3-dev",
120        "libncurses-dev",
121        "libreadline-dev",
122        "uuid-dev",
123        "libgdbm-dev",
124    ]
125}
126
127// =========================================================================
128// Helper macro to avoid repeating .to_string() on every match arm
129// =========================================================================
130
131/// Map a package name using a match expression where all arms return `&str`.
132/// The `_ => name` fallback returns the original name.
133macro_rules! pkg_map {
134    ($name:expr, { $($pat:pat => $val:expr),* $(,)? }) => {
135        match $name {
136            $($pat => $val.to_string(),)*
137            _ => $name.to_string(),
138        }
139    };
140}
141
142// =========================================================================
143// Apt mappings (Debian/Ubuntu) — mostly identity
144// =========================================================================
145
146fn map_apt(name: &str) -> String {
147    // Apt packages are mostly the canonical names, but a few need mapping
148    match name {
149        "openmpi" => "openmpi-bin libopenmpi-dev".to_string(),
150        "hip-runtime" => "hip-runtime-amd".to_string(),
151        _ => name.to_string(),
152    }
153}
154
155// =========================================================================
156// Pacman mappings (Arch Linux)
157// =========================================================================
158
159fn map_pacman(name: &str) -> String {
160    pkg_map!(name, {
161        // Build essentials
162        "build-essential" => "base-devel",
163        "g++" => "gcc",
164        "ninja-build" => "ninja",
165        "pkg-config" => "pkgconf",
166
167        // Python development
168        "python3" => "python",
169        "python3-dev" => "python",
170        "python3-pip" => "python-pip",
171        "python3-venv" => "python",
172        "python3-setuptools" => "python-setuptools",
173        "python3-wheel" => "python-wheel",
174        "python3-numpy" => "python-numpy",
175
176        // ROCm tools
177        "hip-runtime" => "hip-runtime-amd",
178
179        // ROCm libraries
180        "librccl-dev" => "rccl",
181        "librccl1" => "rccl",
182        "rccl-dev" => "rccl",
183        "migraphx-dev" => "migraphx",
184
185        // MPI
186        "libopenmpi-dev" => "openmpi",
187        "openmpi-bin" => "openmpi",
188        "openmpi" => "openmpi",
189        "openmpi-devel" => "openmpi",
190
191        // Build tools
192        "llvm-dev" => "llvm",
193        "libssl-dev" => "openssl",
194        "libffi-dev" => "libffi",
195        "zlib1g-dev" => "zlib",
196
197        // System utilities
198        "mesa-utils" => "mesa-demos",
199        "libnuma-dev" => "numactl",
200
201        // Version control
202        "gh" => "github-cli",
203
204        // Network
205        "ssh" => "openssh",
206
207        // Libraries
208        "libbz2-dev" => "bzip2",
209        "liblzma-dev" => "xz",
210        "libsqlite3-dev" => "sqlite",
211        "libncurses-dev" => "ncurses",
212        "libreadline-dev" => "readline",
213        "uuid-dev" => "util-linux",
214        "libgdbm-dev" => "gdbm",
215    })
216}
217
218// =========================================================================
219// DNF mappings (Fedora/RHEL)
220// =========================================================================
221
222fn map_dnf(name: &str) -> String {
223    pkg_map!(name, {
224        // Build essentials
225        "build-essential" => "@development-tools",
226        "g++" => "gcc-c++",
227        "pkg-config" => "pkgconf",
228
229        // Python development
230        "python3-dev" => "python3-devel",
231        "python3-venv" => "python3",
232
233        // ROCm tools
234        "rocm-smi-lib" => "rocm-smi",
235
236        // ROCm libraries
237        "librccl-dev" => "rccl-devel",
238        "rccl-dev" => "rccl-devel",
239        "migraphx-dev" => "migraphx-devel",
240
241        // MPI
242        "libopenmpi-dev" => "openmpi-devel",
243        "openmpi" => "openmpi openmpi-devel",
244        "openmpi-devel" => "openmpi-devel",
245
246        // Build tools
247        "llvm-dev" => "llvm-devel",
248        "libssl-dev" => "openssl-devel",
249        "libffi-dev" => "libffi-devel",
250        "zlib1g-dev" => "zlib-devel",
251
252        // System utilities
253        "mesa-utils" => "mesa-demos",
254        "libnuma-dev" => "numactl-devel",
255
256        // Network
257        "gnupg" => "gnupg2",
258        "ssh" => "openssh-clients",
259
260        // Libraries
261        "libbz2-dev" => "bzip2-devel",
262        "liblzma-dev" => "xz-devel",
263        "libsqlite3-dev" => "sqlite-devel",
264        "libncurses-dev" => "ncurses-devel",
265        "libreadline-dev" => "readline-devel",
266        "uuid-dev" => "libuuid-devel",
267        "libgdbm-dev" => "gdbm-devel",
268    })
269}
270
271// =========================================================================
272// YUM mappings (older RHEL/CentOS)
273// =========================================================================
274
275fn map_yum(name: &str) -> String {
276    pkg_map!(name, {
277        // Build essentials
278        "build-essential" => "@development-tools",
279        "g++" => "gcc-c++",
280        "pkg-config" => "pkgconf",
281
282        // Python development
283        "python3-dev" => "python3-devel",
284        "python3-venv" => "python3",
285
286        // ROCm tools
287        "rocm-smi-lib" => "rocm-smi",
288
289        // ROCm libraries
290        "librccl-dev" => "rccl-devel",
291        "rccl-dev" => "rccl-devel",
292        "migraphx-dev" => "migraphx-devel",
293
294        // MPI
295        "libopenmpi-dev" => "openmpi-devel",
296        "openmpi" => "openmpi openmpi-devel",
297        "openmpi-devel" => "openmpi-devel",
298
299        // Build tools
300        "llvm-dev" => "llvm-devel",
301        "libssl-dev" => "openssl-devel",
302        "libffi-dev" => "libffi-devel",
303        "zlib1g-dev" => "zlib-devel",
304
305        // System utilities
306        "mesa-utils" => "mesa-demos",
307        "libnuma-dev" => "numactl-devel",
308
309        // Network
310        "gnupg" => "gnupg2",
311        "ssh" => "openssh-clients",
312
313        // Libraries
314        "libbz2-dev" => "bzip2-devel",
315        "liblzma-dev" => "xz-devel",
316        "libsqlite3-dev" => "sqlite-devel",
317        "libncurses-dev" => "ncurses-devel",
318        "libreadline-dev" => "readline-devel",
319        "uuid-dev" => "libuuid-devel",
320        "libgdbm-dev" => "gdbm-devel",
321    })
322}
323
324// =========================================================================
325// Zypper mappings (openSUSE/SLES)
326// =========================================================================
327
328fn map_zypper(name: &str) -> String {
329    pkg_map!(name, {
330        // Build essentials
331        "build-essential" => "patterns-devel-base-devel_basis",
332        "g++" => "gcc-c++",
333        "ninja-build" => "ninja",
334        "pkg-config" => "pkgconf",
335
336        // Python development
337        "python3-dev" => "python3-devel",
338        "python3-venv" => "python3",
339
340        // ROCm tools
341        "rocm-smi-lib" => "rocm-smi",
342
343        // ROCm libraries
344        "librccl-dev" => "rccl-devel",
345        "rccl-dev" => "rccl-devel",
346        "migraphx-dev" => "migraphx-devel",
347
348        // MPI
349        "libopenmpi-dev" => "openmpi-devel",
350        "openmpi" => "openmpi openmpi-devel",
351        "openmpi-devel" => "openmpi-devel",
352
353        // Build tools
354        "llvm-dev" => "llvm-devel",
355        "libssl-dev" => "libopenssl-devel",
356        "libffi-dev" => "libffi-devel",
357        "zlib1g-dev" => "zlib-devel",
358
359        // System utilities
360        "mesa-utils" => "Mesa-demo",
361        "libnuma-dev" => "libnuma-devel",
362
363        // Network
364        "gnupg" => "gpg2",
365        "ssh" => "openssh",
366
367        // Libraries
368        "libbz2-dev" => "libbz2-devel",
369        "liblzma-dev" => "xz-devel",
370        "libsqlite3-dev" => "sqlite3-devel",
371        "libncurses-dev" => "ncurses-devel",
372        "libreadline-dev" => "readline-devel",
373        "uuid-dev" => "libuuid-devel",
374        "libgdbm-dev" => "gdbm-devel",
375    })
376}
377
378#[cfg(test)]
379mod tests {
380    use super::*;
381
382    // =====================================================================
383    // VAL-INFRA-004: Package name mapping correct
384    // =====================================================================
385
386    // --- Build Essentials ---
387
388    #[test]
389    fn test_build_essential_apt() {
390        assert_eq!(
391            map_package_name("build-essential", PackageManager::Apt),
392            "build-essential"
393        );
394    }
395    #[test]
396    fn test_build_essential_pacman() {
397        assert_eq!(
398            map_package_name("build-essential", PackageManager::Pacman),
399            "base-devel"
400        );
401    }
402    #[test]
403    fn test_build_essential_dnf() {
404        assert_eq!(
405            map_package_name("build-essential", PackageManager::Dnf),
406            "@development-tools"
407        );
408    }
409    #[test]
410    fn test_build_essential_yum() {
411        assert_eq!(
412            map_package_name("build-essential", PackageManager::Yum),
413            "@development-tools"
414        );
415    }
416    #[test]
417    fn test_build_essential_zypper() {
418        assert_eq!(
419            map_package_name("build-essential", PackageManager::Zypper),
420            "patterns-devel-base-devel_basis"
421        );
422    }
423
424    // --- g++ ---
425    #[test]
426    fn test_gplusplus_pacman() {
427        assert_eq!(map_package_name("g++", PackageManager::Pacman), "gcc");
428    }
429    #[test]
430    fn test_gplusplus_dnf() {
431        assert_eq!(map_package_name("g++", PackageManager::Dnf), "gcc-c++");
432    }
433
434    // --- pkg-config ---
435    #[test]
436    fn test_pkg_config_pacman() {
437        assert_eq!(
438            map_package_name("pkg-config", PackageManager::Pacman),
439            "pkgconf"
440        );
441    }
442    #[test]
443    fn test_pkg_config_dnf() {
444        assert_eq!(
445            map_package_name("pkg-config", PackageManager::Dnf),
446            "pkgconf"
447        );
448    }
449
450    // --- ninja-build ---
451    #[test]
452    fn test_ninja_build_pacman() {
453        assert_eq!(
454            map_package_name("ninja-build", PackageManager::Pacman),
455            "ninja"
456        );
457    }
458
459    // --- Python Development ---
460
461    #[test]
462    fn test_python3_pacman() {
463        assert_eq!(
464            map_package_name("python3", PackageManager::Pacman),
465            "python"
466        );
467    }
468    #[test]
469    fn test_python3_dev_pacman() {
470        assert_eq!(
471            map_package_name("python3-dev", PackageManager::Pacman),
472            "python"
473        );
474    }
475    #[test]
476    fn test_python3_dev_dnf() {
477        assert_eq!(
478            map_package_name("python3-dev", PackageManager::Dnf),
479            "python3-devel"
480        );
481    }
482    #[test]
483    fn test_python3_dev_zypper() {
484        assert_eq!(
485            map_package_name("python3-dev", PackageManager::Zypper),
486            "python3-devel"
487        );
488    }
489    #[test]
490    fn test_python3_pip_pacman() {
491        assert_eq!(
492            map_package_name("python3-pip", PackageManager::Pacman),
493            "python-pip"
494        );
495    }
496    #[test]
497    fn test_python3_venv_pacman() {
498        assert_eq!(
499            map_package_name("python3-venv", PackageManager::Pacman),
500            "python"
501        );
502    }
503
504    // --- ROCm Tools ---
505
506    #[test]
507    fn test_hip_runtime_apt() {
508        assert_eq!(
509            map_package_name("hip-runtime", PackageManager::Apt),
510            "hip-runtime-amd"
511        );
512    }
513    #[test]
514    fn test_hip_runtime_pacman() {
515        assert_eq!(
516            map_package_name("hip-runtime", PackageManager::Pacman),
517            "hip-runtime-amd"
518        );
519    }
520    #[test]
521    fn test_rocm_smi_lib_dnf() {
522        assert_eq!(
523            map_package_name("rocm-smi-lib", PackageManager::Dnf),
524            "rocm-smi"
525        );
526    }
527
528    // --- ROCm Libraries ---
529
530    #[test]
531    fn test_librccl_dev_pacman() {
532        assert_eq!(
533            map_package_name("librccl-dev", PackageManager::Pacman),
534            "rccl"
535        );
536    }
537    #[test]
538    fn test_librccl_dev_dnf() {
539        assert_eq!(
540            map_package_name("librccl-dev", PackageManager::Dnf),
541            "rccl-devel"
542        );
543    }
544    #[test]
545    fn test_migraphx_dev_dnf() {
546        assert_eq!(
547            map_package_name("migraphx-dev", PackageManager::Dnf),
548            "migraphx-devel"
549        );
550    }
551    #[test]
552    fn test_migraphx_dev_pacman() {
553        assert_eq!(
554            map_package_name("migraphx-dev", PackageManager::Pacman),
555            "migraphx"
556        );
557    }
558
559    // --- MPI ---
560
561    #[test]
562    fn test_libopenmpi_dev_pacman() {
563        assert_eq!(
564            map_package_name("libopenmpi-dev", PackageManager::Pacman),
565            "openmpi"
566        );
567    }
568    #[test]
569    fn test_libopenmpi_dev_dnf() {
570        assert_eq!(
571            map_package_name("libopenmpi-dev", PackageManager::Dnf),
572            "openmpi-devel"
573        );
574    }
575    #[test]
576    fn test_openmpi_apt() {
577        assert_eq!(
578            map_package_name("openmpi", PackageManager::Apt),
579            "openmpi-bin libopenmpi-dev"
580        );
581    }
582    #[test]
583    fn test_openmpi_dnf() {
584        assert_eq!(
585            map_package_name("openmpi", PackageManager::Dnf),
586            "openmpi openmpi-devel"
587        );
588    }
589
590    // --- Build Tools ---
591
592    #[test]
593    fn test_llvm_dev_pacman() {
594        assert_eq!(map_package_name("llvm-dev", PackageManager::Pacman), "llvm");
595    }
596    #[test]
597    fn test_llvm_dev_dnf() {
598        assert_eq!(
599            map_package_name("llvm-dev", PackageManager::Dnf),
600            "llvm-devel"
601        );
602    }
603    #[test]
604    fn test_libssl_dev_pacman() {
605        assert_eq!(
606            map_package_name("libssl-dev", PackageManager::Pacman),
607            "openssl"
608        );
609    }
610    #[test]
611    fn test_libssl_dev_dnf() {
612        assert_eq!(
613            map_package_name("libssl-dev", PackageManager::Dnf),
614            "openssl-devel"
615        );
616    }
617    #[test]
618    fn test_libssl_dev_zypper() {
619        assert_eq!(
620            map_package_name("libssl-dev", PackageManager::Zypper),
621            "libopenssl-devel"
622        );
623    }
624    #[test]
625    fn test_libffi_dev_dnf() {
626        assert_eq!(
627            map_package_name("libffi-dev", PackageManager::Dnf),
628            "libffi-devel"
629        );
630    }
631    #[test]
632    fn test_zlib1g_dev_pacman() {
633        assert_eq!(
634            map_package_name("zlib1g-dev", PackageManager::Pacman),
635            "zlib"
636        );
637    }
638    #[test]
639    fn test_zlib1g_dev_dnf() {
640        assert_eq!(
641            map_package_name("zlib1g-dev", PackageManager::Dnf),
642            "zlib-devel"
643        );
644    }
645
646    // --- System Utilities ---
647
648    #[test]
649    fn test_mesa_utils_pacman() {
650        assert_eq!(
651            map_package_name("mesa-utils", PackageManager::Pacman),
652            "mesa-demos"
653        );
654    }
655    #[test]
656    fn test_mesa_utils_dnf() {
657        assert_eq!(
658            map_package_name("mesa-utils", PackageManager::Dnf),
659            "mesa-demos"
660        );
661    }
662    #[test]
663    fn test_mesa_utils_zypper() {
664        assert_eq!(
665            map_package_name("mesa-utils", PackageManager::Zypper),
666            "Mesa-demo"
667        );
668    }
669    #[test]
670    fn test_libnuma_dev_pacman() {
671        assert_eq!(
672            map_package_name("libnuma-dev", PackageManager::Pacman),
673            "numactl"
674        );
675    }
676    #[test]
677    fn test_libnuma_dev_dnf() {
678        assert_eq!(
679            map_package_name("libnuma-dev", PackageManager::Dnf),
680            "numactl-devel"
681        );
682    }
683
684    // --- Version Control ---
685
686    #[test]
687    fn test_gh_pacman() {
688        assert_eq!(map_package_name("gh", PackageManager::Pacman), "github-cli");
689    }
690
691    // --- Network ---
692
693    #[test]
694    fn test_gnupg_dnf() {
695        assert_eq!(map_package_name("gnupg", PackageManager::Dnf), "gnupg2");
696    }
697    #[test]
698    fn test_gnupg_zypper() {
699        assert_eq!(map_package_name("gnupg", PackageManager::Zypper), "gpg2");
700    }
701    #[test]
702    fn test_ssh_pacman() {
703        assert_eq!(map_package_name("ssh", PackageManager::Pacman), "openssh");
704    }
705    #[test]
706    fn test_ssh_dnf() {
707        assert_eq!(
708            map_package_name("ssh", PackageManager::Dnf),
709            "openssh-clients"
710        );
711    }
712
713    // --- Libraries ---
714
715    #[test]
716    fn test_libbz2_dev_pacman() {
717        assert_eq!(
718            map_package_name("libbz2-dev", PackageManager::Pacman),
719            "bzip2"
720        );
721    }
722    #[test]
723    fn test_libbz2_dev_dnf() {
724        assert_eq!(
725            map_package_name("libbz2-dev", PackageManager::Dnf),
726            "bzip2-devel"
727        );
728    }
729    #[test]
730    fn test_libsqlite3_dev_pacman() {
731        assert_eq!(
732            map_package_name("libsqlite3-dev", PackageManager::Pacman),
733            "sqlite"
734        );
735    }
736    #[test]
737    fn test_libsqlite3_dev_zypper() {
738        assert_eq!(
739            map_package_name("libsqlite3-dev", PackageManager::Zypper),
740            "sqlite3-devel"
741        );
742    }
743    #[test]
744    fn test_uuid_dev_pacman() {
745        assert_eq!(
746            map_package_name("uuid-dev", PackageManager::Pacman),
747            "util-linux"
748        );
749    }
750    #[test]
751    fn test_uuid_dev_dnf() {
752        assert_eq!(
753            map_package_name("uuid-dev", PackageManager::Dnf),
754            "libuuid-devel"
755        );
756    }
757
758    // --- Unknown package returns original name ---
759
760    #[test]
761    fn test_unknown_package_returns_original() {
762        assert_eq!(
763            map_package_name("some-unknown-package", PackageManager::Apt),
764            "some-unknown-package"
765        );
766        assert_eq!(
767            map_package_name("some-unknown-package", PackageManager::Pacman),
768            "some-unknown-package"
769        );
770    }
771
772    #[test]
773    fn test_unknown_package_manager_returns_original() {
774        assert_eq!(
775            map_package_name("build-essential", PackageManager::Unknown),
776            "build-essential"
777        );
778    }
779
780    // --- map_package_names batch ---
781
782    #[test]
783    fn test_map_package_names_batch() {
784        let names = vec!["build-essential", "cmake", "python3-dev"];
785        let mapped = map_package_names(&names, PackageManager::Pacman);
786        assert_eq!(mapped, vec!["base-devel", "cmake", "python"]);
787    }
788
789    // --- Identity mappings (same on all platforms) ---
790
791    #[test]
792    fn test_identity_mappings() {
793        // These should be the same on all platforms
794        for pkg in &[
795            "gcc", "make", "cmake", "git", "curl", "wget", "clang", "lld",
796        ] {
797            assert_eq!(map_package_name(pkg, PackageManager::Apt), *pkg);
798            assert_eq!(map_package_name(pkg, PackageManager::Pacman), *pkg);
799        }
800    }
801
802    // --- has_mapping ---
803
804    #[test]
805    fn test_has_mapping_known() {
806        assert!(has_mapping("build-essential", PackageManager::Pacman));
807    }
808
809    #[test]
810    fn test_has_mapping_identity() {
811        // gcc maps to itself on all platforms, but it's still a known package
812        assert!(has_mapping("gcc", PackageManager::Apt));
813    }
814
815    #[test]
816    fn test_has_mapping_unknown() {
817        assert!(!has_mapping("totally-fake-package", PackageManager::Apt));
818    }
819
820    // --- get_known_packages coverage ---
821
822    #[test]
823    fn test_known_packages_contains_key_packages() {
824        let known = get_known_packages();
825        assert!(known.contains(&"build-essential"));
826        assert!(known.contains(&"python3-dev"));
827        assert!(known.contains(&"libopenmpi-dev"));
828        assert!(known.contains(&"git"));
829        assert!(known.contains(&"libssl-dev"));
830        assert!(known.contains(&"rocminfo"));
831    }
832}