1use crate::installers::common::RocmEnv;
17use std::path::PathBuf;
18
19#[derive(Debug, Clone, PartialEq, Eq)]
25pub enum HipArchs {
26 Gfx1030,
28 Gfx1100,
30 Gfx1200,
32 Default,
34}
35
36impl HipArchs {
37 pub fn from_gpu_arch(gpu_arch: &str) -> Self {
39 if gpu_arch.starts_with("gfx103") {
40 HipArchs::Gfx1030
41 } else if gpu_arch.starts_with("gfx110") {
42 HipArchs::Gfx1100
43 } else if gpu_arch.starts_with("gfx120") {
44 HipArchs::Gfx1200
45 } else {
46 HipArchs::Default
47 }
48 }
49
50 pub fn cmake_hip_architectures(&self) -> &str {
52 match self {
53 HipArchs::Gfx1030 => "gfx1030",
54 HipArchs::Gfx1100 => "gfx1100;gfx1101;gfx1102;gfx1103",
55 HipArchs::Gfx1200 => "gfx1200;gfx1201",
56 HipArchs::Default => "gfx1100",
57 }
58 }
59}
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
67pub enum OnnxInstallMethod {
68 #[default]
76 MigraphxWheel,
77 SourceBuild,
81 PrebuiltWheel,
84}
85
86pub const DEFAULT_ONNXRUNTIME_VERSION: &str = "1.27.1";
89
90pub const PREBUILT_MIGRAPHX_VERSION: &str = "1.27.1";
115
116pub const ONNX_INSTALL_METHOD_ENV: &str = "MLSTACK_ONNX_INSTALL_METHOD";
119
120pub const ONNX_VERSION_ENV: &str = "MLSTACK_ONNX_VERSION";
123
124pub fn install_method_from_env() -> OnnxInstallMethod {
126 match std::env::var(ONNX_INSTALL_METHOD_ENV)
127 .unwrap_or_default()
128 .trim()
129 .to_ascii_lowercase()
130 .as_str()
131 {
132 "source" | "source-build" | "src" => OnnxInstallMethod::SourceBuild,
133 "prebuilt" | "rocm" | "onnxruntime-rocm" => OnnxInstallMethod::PrebuiltWheel,
134 _ => OnnxInstallMethod::MigraphxWheel,
135 }
136}
137
138pub fn prebuilt_version_from_env() -> Option<String> {
140 std::env::var(ONNX_VERSION_ENV)
141 .ok()
142 .map(|s| s.trim().to_string())
143 .filter(|s| !s.is_empty())
144}
145
146#[derive(Debug, Clone)]
148pub struct OnnxRuntimeConfig {
149 pub python_bin: String,
151 pub dry_run: bool,
153 pub rocm_version: Option<String>,
155 pub rocm_release: Option<String>,
157 pub gpu_arch: Option<String>,
159 pub workdir: Option<PathBuf>,
161 pub eigen_path: Option<PathBuf>,
163 pub use_preinstalled_eigen: bool,
165 pub install_method: OnnxInstallMethod,
167 pub runtime_version: Option<String>,
169 pub prebuilt_version: Option<String>,
172}
173
174impl Default for OnnxRuntimeConfig {
175 fn default() -> Self {
176 Self {
177 python_bin: "python3".to_string(),
178 dry_run: false,
179 rocm_version: None,
180 rocm_release: None,
181 gpu_arch: None,
182 workdir: None,
183 eigen_path: None,
184 use_preinstalled_eigen: false,
185 install_method: OnnxInstallMethod::default(),
186 runtime_version: None,
187 prebuilt_version: None,
188 }
189 }
190}
191
192impl OnnxRuntimeConfig {
193 pub fn workdir(&self) -> PathBuf {
195 self.workdir
196 .clone()
197 .unwrap_or_else(|| std::env::temp_dir().join("onnxruntime-rocm"))
198 }
199
200 pub fn gpu_arch(&self) -> &str {
202 self.gpu_arch.as_deref().unwrap_or("gfx1100")
203 }
204
205 pub fn rocm_version(&self) -> &str {
207 self.rocm_version.as_deref().unwrap_or("7.2")
208 }
209
210 pub fn rocm_release(&self) -> &str {
212 self.rocm_release.as_deref().unwrap_or("7.2.4")
213 }
214
215 pub fn runtime_version(&self) -> &str {
222 match self.runtime_version.as_deref() {
223 Some(v) if !v.trim().is_empty() => v,
224 _ => DEFAULT_ONNXRUNTIME_VERSION,
225 }
226 }
227
228 pub fn prebuilt_version(&self) -> &str {
231 self.prebuilt_version
232 .as_deref()
233 .unwrap_or(PREBUILT_MIGRAPHX_VERSION)
234 }
235}
236
237#[derive(Debug, Clone, PartialEq, Eq)]
239pub struct ShellCommand {
240 pub program: String,
242 pub args: Vec<String>,
244 pub env: Vec<(String, String)>,
246 pub working_dir: Option<PathBuf>,
248}
249
250pub struct OnnxRuntimeInstaller {
252 config: OnnxRuntimeConfig,
253}
254
255impl OnnxRuntimeInstaller {
256 pub fn new(config: OnnxRuntimeConfig) -> Self {
258 Self { config }
259 }
260
261 pub fn with_defaults() -> Self {
263 Self::new(OnnxRuntimeConfig::default())
264 }
265
266 pub fn runtime_version(&self) -> &str {
268 self.config.runtime_version()
269 }
270
271 pub fn prebuilt_version(&self) -> &str {
274 self.config.prebuilt_version()
275 }
276
277 pub fn dependencies(&self) -> &[&str] {
285 &["rocm"]
286 }
287
288 pub fn build_prebuilt_install_command(&self) -> ShellCommand {
297 ShellCommand {
298 program: self.config.python_bin.clone(),
299 args: vec![
300 "-m".to_string(),
301 "pip".to_string(),
302 "install".to_string(),
303 "--upgrade".to_string(),
304 "--force-reinstall".to_string(),
305 "--no-deps".to_string(),
306 "--prefer-binary".to_string(),
307 "onnxruntime-rocm".to_string(),
308 ],
309 env: vec![],
310 working_dir: None,
311 }
312 }
313
314 pub fn build_migraphx_install_command(&self) -> ShellCommand {
326 let spec = format!("onnxruntime-migraphx=={}", self.config.prebuilt_version());
327 ShellCommand {
328 program: self.config.python_bin.clone(),
329 args: vec![
330 "-m".to_string(),
331 "pip".to_string(),
332 "install".to_string(),
333 "--upgrade".to_string(),
334 "--force-reinstall".to_string(),
335 "--no-deps".to_string(),
336 "--no-cache-dir".to_string(),
337 spec,
338 ],
339 env: vec![],
340 working_dir: None,
341 }
342 }
343
344 pub fn build_provider_validation_command(&self) -> ShellCommand {
346 let script = r#"
347import ctypes
348import os
349import onnxruntime as ort
350
351if not getattr(ort, "__version__", None) or not hasattr(ort, "get_available_providers"):
352 raise SystemExit(f"ONNX Runtime import incomplete: {getattr(ort, '__file__', '<unknown>')}")
353
354priority = ["MIGraphXExecutionProvider", "ROCMExecutionProvider"]
355available = ort.get_available_providers()
356selected = next((p for p in priority if p in available), None)
357if selected is None:
358 capi = os.path.join(os.path.dirname(ort.__file__), "capi")
359 loader_errors = []
360 for lib in ("libonnxruntime_providers_migraphx.so", "libonnxruntime_providers_rocm.so"):
361 path = os.path.join(capi, lib)
362 if os.path.exists(path):
363 try:
364 ctypes.CDLL(path)
365 except OSError as exc:
366 loader_errors.append(f"{lib}: {exc}")
367 raise SystemExit(
368 "ONNX Runtime AMD provider unavailable; expected MIGraphXExecutionProvider "
369 f"or legacy ROCMExecutionProvider; available={available}; "
370 f"loader_errors={loader_errors or 'none'}"
371 )
372print(f"ONNX Runtime AMD provider ready: {selected}; available={available}")
373"#;
374
375 ShellCommand {
376 program: self.config.python_bin.clone(),
377 args: vec!["-c".to_string(), script.trim().to_string()],
378 env: vec![],
379 working_dir: None,
380 }
381 }
382
383 pub fn build_model_optimizer_command(&self, model_path: &str) -> ShellCommand {
393 let optimized_path = format!("{}.optimized", model_path);
394
395 let script = format!(
396 "import onnxruntime as ort; \
397 providers = [p for p in ['MIGraphXExecutionProvider', 'ROCMExecutionProvider'] if p in ort.get_available_providers()]; \
398 assert providers, f'No AMD ONNX Runtime provider available: {{ort.get_available_providers()}}'; \
399 opts = ort.SessionOptions(); \
400 opts.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL; \
401 opts.optimized_model_filepath = '{optimized_path}'; \
402 ort.InferenceSession('{model_path}', opts, providers=providers); \
403 print('Optimized: {model_path} -> {optimized_path} using ' + providers[0])"
404 );
405
406 ShellCommand {
407 program: self.config.python_bin.clone(),
408 args: vec!["-c".to_string(), script],
409 env: vec![],
410 working_dir: None,
411 }
412 }
413
414 pub fn build_git_clone_command(&self) -> ShellCommand {
423 ShellCommand {
424 program: "git".to_string(),
425 args: vec![
426 "clone".to_string(),
427 "--recursive".to_string(),
428 "https://github.com/microsoft/onnxruntime.git".to_string(),
429 ],
430 env: vec![],
431 working_dir: Some(self.config.workdir()),
432 }
433 }
434
435 pub fn build_git_checkout_command(&self) -> ShellCommand {
438 ShellCommand {
439 program: "git".to_string(),
440 args: vec![
441 "checkout".to_string(),
442 format!("v{}", self.config.runtime_version()),
443 ],
444 env: vec![],
445 working_dir: Some(self.config.workdir().join("onnxruntime")),
446 }
447 }
448
449 pub fn build_git_submodule_command(&self) -> ShellCommand {
451 ShellCommand {
452 program: "git".to_string(),
453 args: vec![
454 "submodule".to_string(),
455 "update".to_string(),
456 "--init".to_string(),
457 "--recursive".to_string(),
458 ],
459 env: vec![],
460 working_dir: Some(self.config.workdir().join("onnxruntime")),
461 }
462 }
463
464 pub fn build_build_command(&self, rocm_env: &RocmEnv) -> ShellCommand {
481 let rocm_path = rocm_env
482 .path()
483 .map(|p| p.to_string_lossy().to_string())
484 .unwrap_or_else(|| "/opt/rocm".to_string());
485
486 let hip_archs = HipArchs::from_gpu_arch(self.config.gpu_arch());
487 let nproc = std::thread::available_parallelism()
488 .map(|n| n.get().saturating_sub(1))
489 .unwrap_or(3);
490
491 let mut args = vec![
498 "--config".to_string(),
499 "Release".to_string(),
500 "--build_wheel".to_string(),
501 "--parallel".to_string(),
502 nproc.to_string(),
503 "--skip_tests".to_string(),
504 "--use_migraphx".to_string(),
505 "--migraphx_home".to_string(),
506 rocm_path.clone(),
507 ];
508
509 if self.config.use_preinstalled_eigen {
510 if let Some(ref eigen_path) = self.config.eigen_path {
511 args.push("--use_preinstalled_eigen".to_string());
512 args.push("--eigen_path".to_string());
513 args.push(eigen_path.to_string_lossy().to_string());
514 }
515 }
516
517 let cmake_extra_defines = vec![
519 format!(
520 "CMAKE_HIP_ARCHITECTURES={}",
521 hip_archs.cmake_hip_architectures()
522 ),
523 "CMAKE_CXX_STANDARD=20".to_string(),
524 "onnxruntime_USE_EXTERNAL_ABSEIL=OFF".to_string(),
525 "CMAKE_DISABLE_FIND_PACKAGE_re2=ON".to_string(),
526 "CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY=ON".to_string(),
527 "CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY=ON".to_string(),
528 "CMAKE_FIND_USE_PACKAGE_REGISTRY=OFF".to_string(),
529 "CMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY=OFF".to_string(),
530 format!("CMAKE_PREFIX_PATH={}", rocm_path),
531 "re2_DIR=RE2_DIR-NOTFOUND".to_string(),
532 "CMAKE_POLICY_VERSION_MINIMUM=3.5".to_string(),
533 ];
534
535 for define in cmake_extra_defines {
536 args.push("--cmake_extra_defines".to_string());
537 args.push(define);
538 }
539
540 if self.config.use_preinstalled_eigen {
541 if let Some(ref eigen_path) = self.config.eigen_path {
542 args.push("--cmake_extra_defines".to_string());
543 args.push(format!(
544 "FETCHCONTENT_SOURCE_DIR_EIGEN={}",
545 eigen_path.to_string_lossy()
546 ));
547 args.push("--cmake_extra_defines".to_string());
548 args.push("FETCHCONTENT_TRY_FIND_PACKAGE_MODE=NEVER".to_string());
549 }
550 }
551
552 args.push("--allow_running_as_root".to_string());
553
554 let env = vec![];
555
556 ShellCommand {
557 program: "./build.sh".to_string(),
558 args,
559 env,
560 working_dir: Some(self.config.workdir().join("onnxruntime")),
561 }
562 }
563
564 pub fn build_uninstall_command(&self) -> ShellCommand {
570 ShellCommand {
571 program: self.config.python_bin.clone(),
572 args: vec![
573 "-m".to_string(),
574 "pip".to_string(),
575 "uninstall".to_string(),
576 "-y".to_string(),
577 "onnxruntime".to_string(),
578 "onnxruntime-rocm".to_string(),
579 "onnxruntime-gpu".to_string(),
580 "onnxruntime-migraphx".to_string(),
581 ],
582 env: vec![],
583 working_dir: None,
584 }
585 }
586
587 pub fn build_wheel_install_command(&self) -> ShellCommand {
592 let wheel_path = self
593 .config
594 .workdir()
595 .join("onnxruntime")
596 .join("build/Linux/Release/dist/*.whl");
597
598 ShellCommand {
599 program: self.config.python_bin.clone(),
600 args: vec![
601 "-m".to_string(),
602 "pip".to_string(),
603 "install".to_string(),
604 wheel_path.to_string_lossy().to_string(),
605 ],
606 env: vec![],
607 working_dir: None,
608 }
609 }
610
611 pub fn check_build_output(
619 &self,
620 stdout: &str,
621 stderr: &str,
622 exit_code: i32,
623 ) -> Result<(), String> {
624 if exit_code != 0 {
625 let error_patterns = [
626 "CMake Error",
627 "error:",
628 "FAILED:",
629 "fatal error:",
630 "Build failed",
631 "ninja: build stopped",
632 "Could NOT find",
633 ];
634
635 let combined = format!("{}\n{}", stdout, stderr);
636 let detected_errors: Vec<&str> = error_patterns
637 .iter()
638 .filter(|p| combined.contains(*p))
639 .copied()
640 .collect();
641
642 if detected_errors.is_empty() {
643 return Err(format!(
644 "ONNX Runtime build failed with exit code {} (no specific error pattern detected)",
645 exit_code
646 ));
647 }
648
649 return Err(format!(
650 "ONNX Runtime build failed with exit code {}. Detected errors: {}",
651 exit_code,
652 detected_errors.join(", ")
653 ));
654 }
655
656 Ok(())
657 }
658}
659
660#[cfg(test)]
665mod tests {
666 use super::*;
667 use std::path::PathBuf;
668
669 #[test]
672 fn test_build_command_uses_migraphx_recipe_not_use_rocm() {
673 let installer = OnnxRuntimeInstaller::new(OnnxRuntimeConfig {
678 rocm_version: Some("7.2.0".to_string()),
679 gpu_arch: Some("gfx1100".to_string()),
680 ..Default::default()
681 });
682 let rocm_env = RocmEnv::from_known(Some(PathBuf::from("/opt/rocm")), "7.2.0".to_string());
683 let cmd = installer.build_build_command(&rocm_env);
684
685 assert_eq!(cmd.program, "./build.sh");
686 assert!(cmd.args.contains(&"--use_migraphx".to_string()));
687 assert!(cmd.args.contains(&"--migraphx_home".to_string()));
688 assert!(cmd.args.contains(&"--build_wheel".to_string()));
689 assert!(cmd.args.contains(&"--skip_tests".to_string()));
690 assert!(cmd.args.contains(&"--allow_running_as_root".to_string()));
691 assert!(cmd.args.iter().any(|a| a == "/opt/rocm"));
692 for removed in ["--use_rocm", "--rocm_home", "--rocm_version"] {
694 assert!(
695 !cmd.args.contains(&removed.to_string()),
696 "removed flag {removed} must not appear: {:?}",
697 cmd.args
698 );
699 }
700 }
701
702 #[test]
703 fn test_build_command_has_hip_architectures() {
704 let installer = OnnxRuntimeInstaller::new(OnnxRuntimeConfig {
705 gpu_arch: Some("gfx1100".to_string()),
706 ..Default::default()
707 });
708 let rocm_env = RocmEnv::from_known(Some(PathBuf::from("/opt/rocm")), "7.2.0".to_string());
709 let cmd = installer.build_build_command(&rocm_env);
710
711 assert!(cmd
712 .args
713 .iter()
714 .any(|a| a.contains("CMAKE_HIP_ARCHITECTURES=gfx1100;gfx1101;gfx1102;gfx1103")));
715 }
716
717 #[test]
718 fn test_build_command_has_cmake_extra_defines() {
719 let installer = OnnxRuntimeInstaller::with_defaults();
720 let rocm_env = RocmEnv::from_known(Some(PathBuf::from("/opt/rocm")), "7.2.0".to_string());
721 let cmd = installer.build_build_command(&rocm_env);
722
723 assert!(cmd
724 .args
725 .iter()
726 .any(|a| a.contains("onnxruntime_USE_EXTERNAL_ABSEIL=OFF")));
727 assert!(cmd
728 .args
729 .iter()
730 .any(|a| a.contains("CMAKE_DISABLE_FIND_PACKAGE_re2=ON")));
731 assert!(cmd
732 .args
733 .iter()
734 .any(|a| a.contains("CMAKE_POLICY_VERSION_MINIMUM=3.5")));
735 }
736
737 #[test]
740 fn test_build_command_includes_cmake_cxx_standard_20() {
741 let installer = OnnxRuntimeInstaller::with_defaults();
742 let rocm_env = RocmEnv::from_known(Some(PathBuf::from("/opt/rocm")), "7.2.0".to_string());
743 let cmd = installer.build_build_command(&rocm_env);
744
745 assert!(
748 cmd.args.iter().any(|a| a.contains("CMAKE_CXX_STANDARD=20")),
749 "build command must include CMAKE_CXX_STANDARD=20 cmake extra define, got args: {:?}",
750 cmd.args
751 );
752 }
753
754 #[test]
755 fn test_build_command_cmake_cxx_standard_is_cmake_extra_define() {
756 let installer = OnnxRuntimeInstaller::with_defaults();
757 let rocm_env = RocmEnv::from_known(Some(PathBuf::from("/opt/rocm")), "7.2.0".to_string());
758 let cmd = installer.build_build_command(&rocm_env);
759
760 let define_idx = cmd
762 .args
763 .iter()
764 .position(|a| a.contains("CMAKE_CXX_STANDARD=20"));
765 assert!(
766 define_idx.is_some(),
767 "CMAKE_CXX_STANDARD=20 not found in args"
768 );
769
770 let idx = define_idx.unwrap();
771 assert!(
772 idx > 0 && cmd.args[idx - 1] == "--cmake_extra_defines",
773 "CMAKE_CXX_STANDARD=20 must be preceded by --cmake_extra_defines"
774 );
775 }
776
777 #[test]
778 fn test_build_command_does_not_override_env_vars() {
779 let installer = OnnxRuntimeInstaller::with_defaults();
780 let rocm_env = RocmEnv::from_known(Some(PathBuf::from("/opt/rocm")), "7.2.0".to_string());
781 let cmd = installer.build_build_command(&rocm_env);
782
783 assert!(
784 cmd.env.is_empty(),
785 "ONNX build must inherit ~/.mlstack_env, got env: {:?}",
786 cmd.env
787 );
788 }
789
790 #[test]
791 fn test_build_command_has_parallel_flag() {
792 let installer = OnnxRuntimeInstaller::with_defaults();
793 let rocm_env = RocmEnv::from_known(Some(PathBuf::from("/opt/rocm")), "7.2.0".to_string());
794 let cmd = installer.build_build_command(&rocm_env);
795
796 assert!(cmd.args.contains(&"--parallel".to_string()));
797 }
798
799 #[test]
800 fn test_build_command_env_vars() {
801 let installer = OnnxRuntimeInstaller::with_defaults();
802 let rocm_env = RocmEnv::from_known(Some(PathBuf::from("/opt/rocm")), "7.2.0".to_string());
803 let cmd = installer.build_build_command(&rocm_env);
804
805 assert!(cmd.env.is_empty());
806 }
807
808 #[test]
809 fn test_build_command_with_eigen() {
810 let installer = OnnxRuntimeInstaller::new(OnnxRuntimeConfig {
811 use_preinstalled_eigen: true,
812 eigen_path: Some(PathBuf::from("/usr/include/eigen3")),
813 ..Default::default()
814 });
815 let rocm_env = RocmEnv::from_known(Some(PathBuf::from("/opt/rocm")), "7.2.0".to_string());
816 let cmd = installer.build_build_command(&rocm_env);
817
818 assert!(cmd.args.contains(&"--use_preinstalled_eigen".to_string()));
819 assert!(cmd.args.iter().any(|a| a.contains("/usr/include/eigen3")));
820 assert!(cmd
821 .args
822 .iter()
823 .any(|a| a.contains("FETCHCONTENT_SOURCE_DIR_EIGEN")));
824 }
825
826 #[test]
829 fn test_wheel_install_command() {
830 let installer = OnnxRuntimeInstaller::with_defaults();
831 let cmd = installer.build_wheel_install_command();
832
833 assert_eq!(cmd.program, "python3");
834 assert!(cmd.args.contains(&"-m".to_string()));
835 assert!(cmd.args.contains(&"pip".to_string()));
836 assert!(cmd.args.contains(&"install".to_string()));
837 assert!(cmd
838 .args
839 .iter()
840 .any(|a| a.contains("build/Linux/Release/dist/*.whl")));
841 }
842
843 #[test]
844 fn test_uninstall_command() {
845 let installer = OnnxRuntimeInstaller::with_defaults();
846 let cmd = installer.build_uninstall_command();
847
848 assert!(cmd.args.contains(&"uninstall".to_string()));
849 assert!(cmd.args.contains(&"-y".to_string()));
850 assert!(cmd.args.contains(&"onnxruntime".to_string()));
851 assert!(cmd.args.contains(&"onnxruntime-rocm".to_string()));
852 assert!(cmd.args.contains(&"onnxruntime-gpu".to_string()));
853 assert!(cmd.args.contains(&"onnxruntime-migraphx".to_string()));
854 }
855
856 #[test]
859 fn test_prebuilt_install_command() {
860 let installer = OnnxRuntimeInstaller::with_defaults();
861 let cmd = installer.build_prebuilt_install_command();
862
863 assert_eq!(cmd.program, "python3");
864 assert!(cmd.args.contains(&"-m".to_string()));
865 assert!(cmd.args.contains(&"pip".to_string()));
866 assert!(cmd.args.contains(&"--upgrade".to_string()));
867 assert!(cmd.args.contains(&"--force-reinstall".to_string()));
868 assert!(cmd.args.contains(&"--no-deps".to_string()));
869 assert!(cmd.args.contains(&"--prefer-binary".to_string()));
870 assert!(cmd.args.contains(&"onnxruntime-rocm".to_string()));
871 assert!(cmd.env.is_empty());
872 }
873
874 #[test]
877 fn test_git_clone_command() {
878 let installer = OnnxRuntimeInstaller::with_defaults();
879 let cmd = installer.build_git_clone_command();
880 assert_eq!(cmd.program, "git");
881 assert!(cmd.args.contains(&"clone".to_string()));
882 assert!(cmd.args.contains(&"--recursive".to_string()));
883 assert!(cmd.args.iter().any(|a| a.contains("microsoft/onnxruntime")));
884 }
885
886 #[test]
887 fn test_git_checkout_command() {
888 let installer = OnnxRuntimeInstaller::with_defaults();
889 let cmd = installer.build_git_checkout_command();
890 assert!(cmd
893 .args
894 .contains(&format!("v{}", DEFAULT_ONNXRUNTIME_VERSION)));
895 assert!(cmd.args.contains(&"v1.27.1".to_string()));
896 }
897
898 #[test]
899 fn test_git_checkout_command_follows_runtime_version() {
900 let installer = OnnxRuntimeInstaller::new(OnnxRuntimeConfig {
901 runtime_version: Some("1.25.0".to_string()),
902 ..Default::default()
903 });
904 let cmd = installer.build_git_checkout_command();
905 assert!(cmd.args.contains(&"v1.25.0".to_string()));
906 }
907
908 #[test]
909 fn test_runtime_version_filters_empty_to_default() {
910 let empty = OnnxRuntimeInstaller::new(OnnxRuntimeConfig {
914 runtime_version: Some(String::new()),
915 ..Default::default()
916 });
917 assert_eq!(empty.config.runtime_version(), DEFAULT_ONNXRUNTIME_VERSION);
918 assert_ne!(empty.config.runtime_version(), "");
919 let ws = OnnxRuntimeInstaller::new(OnnxRuntimeConfig {
920 runtime_version: Some(" ".to_string()),
921 ..Default::default()
922 });
923 assert_eq!(ws.config.runtime_version(), DEFAULT_ONNXRUNTIME_VERSION);
924 let real = OnnxRuntimeInstaller::new(OnnxRuntimeConfig {
926 runtime_version: Some("1.25.0".to_string()),
927 ..Default::default()
928 });
929 assert_eq!(real.config.runtime_version(), "1.25.0");
930 }
931
932 #[test]
935 fn test_hip_archs_gfx1030() {
936 let archs = HipArchs::from_gpu_arch("gfx1030");
937 assert_eq!(archs.cmake_hip_architectures(), "gfx1030");
938 }
939
940 #[test]
941 fn test_hip_archs_gfx1100() {
942 let archs = HipArchs::from_gpu_arch("gfx1100");
943 assert_eq!(
944 archs.cmake_hip_architectures(),
945 "gfx1100;gfx1101;gfx1102;gfx1103"
946 );
947 }
948
949 #[test]
950 fn test_hip_archs_gfx1200() {
951 let archs = HipArchs::from_gpu_arch("gfx1200");
952 assert_eq!(archs.cmake_hip_architectures(), "gfx1200;gfx1201");
953 }
954
955 #[test]
956 fn test_hip_archs_default() {
957 let archs = HipArchs::from_gpu_arch("gfx900");
958 assert_eq!(archs.cmake_hip_architectures(), "gfx1100");
959 }
960
961 #[test]
964 fn test_dependencies() {
965 let installer = OnnxRuntimeInstaller::with_defaults();
966 let deps = installer.dependencies();
967 assert!(deps.contains(&"rocm"));
968 }
969
970 #[test]
973 fn test_build_error_cmake() {
974 let installer = OnnxRuntimeInstaller::with_defaults();
975 let result = installer.check_build_output("", "CMake Error at cmake/CMakeLists.txt:42", 1);
976 assert!(result.is_err());
977 assert!(result.unwrap_err().contains("CMake Error"));
978 }
979
980 #[test]
981 fn test_build_error_ninja() {
982 let installer = OnnxRuntimeInstaller::with_defaults();
983 let result =
984 installer.check_build_output("ninja: build stopped: subcommand failed.", "", 1);
985 assert!(result.is_err());
986 assert!(result.unwrap_err().contains("ninja: build stopped"));
987 }
988
989 #[test]
990 fn test_build_error_success() {
991 let installer = OnnxRuntimeInstaller::with_defaults();
992 let result = installer.check_build_output("Build succeeded", "", 0);
993 assert!(result.is_ok());
994 }
995
996 #[test]
999 fn test_config_defaults() {
1000 let config = OnnxRuntimeConfig::default();
1001 assert_eq!(config.python_bin, "python3");
1002 assert!(!config.dry_run);
1003 assert!(config.rocm_version.is_none());
1004 assert!(config.rocm_release.is_none());
1005 assert!(config.gpu_arch.is_none());
1006 assert!(config.workdir.is_none());
1007 assert!(!config.use_preinstalled_eigen);
1008 assert!(config.eigen_path.is_none());
1009 assert_eq!(config.install_method, OnnxInstallMethod::MigraphxWheel);
1010 assert_eq!(config.runtime_version(), DEFAULT_ONNXRUNTIME_VERSION);
1011 assert_eq!(config.prebuilt_version(), PREBUILT_MIGRAPHX_VERSION);
1012 }
1013
1014 #[test]
1017 fn test_prebuilt_version_defaults_to_pinned() {
1018 let installer = OnnxRuntimeInstaller::with_defaults();
1019 assert_eq!(installer.prebuilt_version(), PREBUILT_MIGRAPHX_VERSION);
1020 assert_eq!(installer.prebuilt_version(), DEFAULT_ONNXRUNTIME_VERSION);
1024 assert_eq!(installer.prebuilt_version(), "1.27.1");
1025 }
1026
1027 #[test]
1028 fn test_install_method_from_env() {
1029 let _env = crate::test_support::lock_env();
1030 std::env::remove_var(ONNX_INSTALL_METHOD_ENV);
1031 assert_eq!(install_method_from_env(), OnnxInstallMethod::MigraphxWheel);
1032 for (v, expected) in [
1033 ("source", OnnxInstallMethod::SourceBuild),
1034 ("SOURCE-BUILD", OnnxInstallMethod::SourceBuild),
1035 ("prebuilt", OnnxInstallMethod::PrebuiltWheel),
1036 ("rocm", OnnxInstallMethod::PrebuiltWheel),
1037 ("migraphx", OnnxInstallMethod::MigraphxWheel),
1038 ("", OnnxInstallMethod::MigraphxWheel),
1039 ] {
1040 if v.is_empty() {
1041 std::env::remove_var(ONNX_INSTALL_METHOD_ENV);
1042 } else {
1043 std::env::set_var(ONNX_INSTALL_METHOD_ENV, v);
1044 }
1045 assert_eq!(install_method_from_env(), expected, "for {v:?}");
1046 }
1047 }
1048
1049 #[test]
1050 fn test_prebuilt_version_from_env_override() {
1051 let _env = crate::test_support::lock_env();
1052 std::env::remove_var(ONNX_VERSION_ENV);
1053 assert_eq!(prebuilt_version_from_env(), None);
1054 std::env::set_var(ONNX_VERSION_ENV, " 1.30.0 ");
1055 assert_eq!(prebuilt_version_from_env(), Some("1.30.0".to_string()));
1056 std::env::set_var(ONNX_VERSION_ENV, " ");
1057 assert_eq!(prebuilt_version_from_env(), None);
1058 }
1059
1060 #[test]
1061 fn test_prebuilt_version_overridable() {
1062 let installer = OnnxRuntimeInstaller::new(OnnxRuntimeConfig {
1063 prebuilt_version: Some("1.23.2".to_string()),
1064 ..Default::default()
1065 });
1066 assert_eq!(installer.prebuilt_version(), "1.23.2");
1067 }
1068
1069 #[test]
1070 fn test_migraphx_install_command_pypi() {
1071 let installer = OnnxRuntimeInstaller::with_defaults();
1072 let cmd = installer.build_migraphx_install_command();
1073 assert_eq!(cmd.program, "python3");
1074 assert!(cmd.args.contains(&"-m".to_string()));
1075 assert!(cmd.args.contains(&"pip".to_string()));
1076 assert!(cmd.args.contains(&"install".to_string()));
1077 assert!(cmd.args.contains(&"--upgrade".to_string()));
1078 assert!(cmd.args.contains(&"--force-reinstall".to_string()));
1079 assert!(cmd.args.contains(&"--no-deps".to_string()));
1081 assert!(cmd.args.contains(&"--no-cache-dir".to_string()));
1082 assert!(cmd
1084 .args
1085 .iter()
1086 .any(|a| a == &format!("onnxruntime-migraphx=={}", PREBUILT_MIGRAPHX_VERSION)));
1087 assert!(
1088 !cmd.args.iter().any(|a| a.contains("repo.radeon.com")),
1089 "must not use the AMD manylinux repo (it lags and 404s): {:?}",
1090 cmd.args
1091 );
1092 assert!(cmd.env.is_empty());
1093 }
1094
1095 #[test]
1098 fn test_provider_validation_command() {
1099 let installer = OnnxRuntimeInstaller::with_defaults();
1100 let cmd = installer.build_provider_validation_command();
1101 assert_eq!(cmd.program, "python3");
1102 assert!(cmd.args.contains(&"-c".to_string()));
1103 let script = &cmd.args[1];
1104 assert!(script.contains("MIGraphXExecutionProvider"));
1105 assert!(script.contains("ROCMExecutionProvider"));
1106 assert!(script.contains("ctypes.CDLL"));
1107 assert!(script.contains("loader_errors"));
1108 assert!(cmd.env.is_empty());
1109 }
1110
1111 #[test]
1112 fn test_model_optimizer_command() {
1113 let installer = OnnxRuntimeInstaller::with_defaults();
1114 let cmd = installer.build_model_optimizer_command("/path/to/model.onnx");
1115 assert_eq!(cmd.program, "python3");
1116 assert!(cmd.args.contains(&"-c".to_string()));
1117 let script = &cmd.args[1];
1118 assert!(script.contains("MIGraphXExecutionProvider"));
1119 assert!(script.contains("ROCMExecutionProvider"));
1120 assert!(!script.contains("CPUExecutionProvider"));
1121 assert!(script.contains("ORT_ENABLE_ALL"));
1122 assert!(script.contains("/path/to/model.onnx"));
1123 assert!(script.contains("/path/to/model.onnx.optimized"));
1124 assert!(cmd.env.is_empty());
1125 }
1126
1127 #[test]
1130 fn test_install_method_default() {
1131 assert_eq!(
1132 OnnxInstallMethod::default(),
1133 OnnxInstallMethod::MigraphxWheel
1134 );
1135 }
1136}