simics_python_utils/
environment.rs1use crate::version::PythonVersion;
5use anyhow::{anyhow, Result};
6use std::path::PathBuf;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
10pub enum PackageSource {
11 Bundled,
13 SeparatePackage,
15}
16
17#[derive(Debug, Clone)]
19pub struct PythonEnvironment {
20 pub mini_python: PathBuf,
22 pub include_dir: PathBuf,
24 pub include_flag: String,
26 pub lib_dir: PathBuf,
28 pub lib_path: PathBuf,
30 pub import_lib_dir: PathBuf,
33 pub version: PythonVersion,
35 pub package_source: PackageSource,
37}
38
39impl PythonEnvironment {
40 pub fn new(
42 mini_python: PathBuf,
43 include_dir: PathBuf,
44 lib_dir: PathBuf,
45 lib_path: PathBuf,
46 import_lib_dir: PathBuf,
47 version: PythonVersion,
48 package_source: PackageSource,
49 ) -> Self {
50 let include_flag = format!("-I{}", include_dir.display());
51
52 Self {
53 mini_python,
54 include_dir,
55 include_flag,
56 lib_dir,
57 lib_path,
58 import_lib_dir,
59 version,
60 package_source,
61 }
62 }
63
64 pub fn with_source(mut self, source: PackageSource) -> Self {
66 self.package_source = source;
67 self
68 }
69
70 pub fn validate(&self) -> Result<()> {
72 if !self.mini_python.exists() {
73 return Err(anyhow!(
74 "Mini-python executable not found: {}",
75 self.mini_python.display()
76 ));
77 }
78
79 if !self.mini_python.is_file() {
80 return Err(anyhow!(
81 "Mini-python path is not a file: {}",
82 self.mini_python.display()
83 ));
84 }
85
86 if !self.include_dir.exists() {
87 return Err(anyhow!(
88 "Python include directory not found: {}",
89 self.include_dir.display()
90 ));
91 }
92
93 if !self.include_dir.is_dir() {
94 return Err(anyhow!(
95 "Python include path is not a directory: {}",
96 self.include_dir.display()
97 ));
98 }
99
100 if !self.lib_dir.exists() {
101 return Err(anyhow!(
102 "Python library directory not found: {}",
103 self.lib_dir.display()
104 ));
105 }
106
107 if !self.lib_dir.is_dir() {
108 return Err(anyhow!(
109 "Python library path is not a directory: {}",
110 self.lib_dir.display()
111 ));
112 }
113
114 if !self.lib_path.exists() {
115 return Err(anyhow!(
116 "Python library file not found: {}",
117 self.lib_path.display()
118 ));
119 }
120
121 if !self.lib_path.is_file() {
122 return Err(anyhow!(
123 "Python library path is not a file: {}",
124 self.lib_path.display()
125 ));
126 }
127
128 #[cfg(windows)]
129 {
130 if !self.import_lib_dir.exists() {
131 return Err(anyhow!(
132 "Python import library directory not found: {}",
133 self.import_lib_dir.display()
134 ));
135 }
136
137 if !self.import_lib_dir.is_dir() {
138 return Err(anyhow!(
139 "Python import library path is not a directory: {}",
140 self.import_lib_dir.display()
141 ));
142 }
143 }
144
145 Ok(())
146 }
147
148 pub fn major_version_str(&self) -> String {
150 self.version.major.to_string()
151 }
152
153 pub fn minor_version_str(&self) -> String {
155 self.version.minor.to_string()
156 }
157
158 pub fn py_limited_api_define(&self) -> String {
160 format!("-DPy_LIMITED_API={}", self.version.py_limited_api_hex())
161 }
162
163 pub fn import_lib_path(&self) -> PathBuf {
165 self.import_lib_dir.join("python3.lib")
166 }
167
168 pub fn lib_filename(&self) -> Result<String> {
170 self.lib_path
171 .file_name()
172 .and_then(|name| name.to_str())
173 .map(|s| s.to_string())
174 .ok_or_else(|| {
175 anyhow!(
176 "Failed to get library filename from {}",
177 self.lib_path.display()
178 )
179 })
180 }
181}
182
183impl std::fmt::Display for PythonEnvironment {
184 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
185 write!(
186 f,
187 "PythonEnvironment {{ version: {}, source: {:?}, mini_python: {}, include: {}, lib: {}, import_lib: {} }}",
188 self.version,
189 self.package_source,
190 self.mini_python.display(),
191 self.include_dir.display(),
192 self.lib_path.display(),
193 self.import_lib_dir.display()
194 )
195 }
196}