1#[cfg(feature = "session")]
26pub use ort;
27
28#[cfg(feature = "session")]
29pub mod environment;
30#[cfg(feature = "session")]
31pub mod probe;
32#[cfg(feature = "session")]
33pub mod session;
34
35#[cfg(feature = "session")]
36pub use environment::{
37 cpu_thread_budget, init_environment, shared_pool_active, EnvironmentOptions, EnvironmentReport,
38 GlobalPool,
39};
40#[cfg(feature = "session")]
41pub use probe::{probe_providers, ProviderDiagnostic, ProviderKind};
42#[cfg(feature = "session")]
43pub use session::{BuiltSession, ExecutionProvider, SessionError, SessionOptions};
44
45use std::fmt;
46use std::path::{Path, PathBuf};
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq)]
49pub enum CandidateSource {
50 Explicit,
51 Bundled,
52 AppData,
53}
54
55#[derive(Debug, Clone, PartialEq, Eq)]
56pub struct RuntimeCandidate {
57 pub path: PathBuf,
58 pub source: CandidateSource,
59}
60
61impl RuntimeCandidate {
62 pub fn new(path: impl Into<PathBuf>, source: CandidateSource) -> Self {
63 Self {
64 path: path.into(),
65 source,
66 }
67 }
68}
69
70#[derive(Debug, Clone, PartialEq, Eq)]
71pub struct CandidateDiagnostic {
72 pub path: PathBuf,
73 pub source: CandidateSource,
74 pub absolute: bool,
75 pub expected_filename: bool,
76 pub exists: bool,
77 pub is_file: bool,
78}
79
80#[derive(Debug, Clone, PartialEq, Eq)]
81pub struct RuntimeSelection {
82 pub path: PathBuf,
83 pub source: CandidateSource,
84 pub diagnostics: Vec<CandidateDiagnostic>,
85}
86
87#[derive(Debug, Clone, PartialEq, Eq)]
88pub enum RuntimeError {
89 NoCandidates,
90 UnsafePath {
91 path: PathBuf,
92 },
93 UnexpectedFilename {
94 path: PathBuf,
95 expected: &'static str,
96 },
97 NotFound {
98 diagnostics: Vec<CandidateDiagnostic>,
99 },
100 Canonicalize {
101 path: PathBuf,
102 message: String,
103 },
104 AlreadyConfigured {
105 configured: PathBuf,
106 selected: PathBuf,
107 },
108 SystemRuntimeRejected {
111 path: PathBuf,
112 },
113}
114
115impl fmt::Display for RuntimeError {
116 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117 match self {
118 Self::NoCandidates => write!(f, "no ONNX Runtime candidates were supplied"),
119 Self::UnsafePath { path } => {
120 write!(f, "runtime path must be absolute: {}", path.display())
121 }
122 Self::UnexpectedFilename { path, expected } => {
123 write!(f, "runtime path {} must end in {expected}", path.display())
124 }
125 Self::NotFound { .. } => {
126 write!(f, "no supplied ONNX Runtime candidate is a regular file")
127 }
128 Self::Canonicalize { path, message } => {
129 write!(f, "cannot canonicalize {}: {message}", path.display())
130 }
131 Self::SystemRuntimeRejected { path } => write!(
132 f,
133 "runtime {} is the OS-provided copy; bundle and supply an app runtime",
134 path.display()
135 ),
136 Self::AlreadyConfigured {
137 configured,
138 selected,
139 } => write!(
140 f,
141 "ORT_DYLIB_PATH is already {}, refusing replacement with {}",
142 configured.display(),
143 selected.display()
144 ),
145 }
146 }
147}
148
149impl std::error::Error for RuntimeError {}
150
151pub fn runtime_filename() -> &'static str {
152 #[cfg(target_os = "windows")]
153 {
154 "onnxruntime.dll"
155 }
156 #[cfg(target_os = "macos")]
157 {
158 "libonnxruntime.dylib"
159 }
160 #[cfg(all(not(target_os = "windows"), not(target_os = "macos")))]
161 {
162 "libonnxruntime.so"
163 }
164}
165
166pub fn inspect_candidate(candidate: &RuntimeCandidate) -> CandidateDiagnostic {
167 CandidateDiagnostic {
168 path: candidate.path.clone(),
169 source: candidate.source,
170 absolute: candidate.path.is_absolute(),
171 expected_filename: candidate.path.file_name().and_then(|name| name.to_str())
172 == Some(runtime_filename()),
173 exists: candidate.path.exists(),
174 is_file: candidate.path.is_file(),
175 }
176}
177
178pub fn resolve_runtime(
179 candidates: impl IntoIterator<Item = RuntimeCandidate>,
180) -> Result<RuntimeSelection, RuntimeError> {
181 let candidates: Vec<_> = candidates.into_iter().collect();
182 if candidates.is_empty() {
183 return Err(RuntimeError::NoCandidates);
184 }
185 let mut diagnostics = Vec::with_capacity(candidates.len());
186 for candidate in candidates {
187 let diagnostic = inspect_candidate(&candidate);
188 if !diagnostic.absolute {
189 return Err(RuntimeError::UnsafePath {
190 path: candidate.path,
191 });
192 }
193 if !diagnostic.expected_filename {
194 return Err(RuntimeError::UnexpectedFilename {
195 path: candidate.path,
196 expected: runtime_filename(),
197 });
198 }
199 if is_system_runtime(&candidate.path) {
200 return Err(RuntimeError::SystemRuntimeRejected {
201 path: candidate.path,
202 });
203 }
204 diagnostics.push(diagnostic.clone());
205 if diagnostic.is_file {
206 let path =
207 candidate
208 .path
209 .canonicalize()
210 .map_err(|error| RuntimeError::Canonicalize {
211 path: candidate.path,
212 message: error.to_string(),
213 })?;
214 return Ok(RuntimeSelection {
215 path,
216 source: candidate.source,
217 diagnostics,
218 });
219 }
220 }
221 Err(RuntimeError::NotFound { diagnostics })
222}
223
224pub fn is_system_runtime(path: &Path) -> bool {
228 path.components().any(|component| {
229 component
230 .as_os_str()
231 .to_str()
232 .map(|name| {
233 name.eq_ignore_ascii_case("system32") || name.eq_ignore_ascii_case("syswow64")
234 })
235 .unwrap_or(false)
236 })
237}
238
239pub fn installed_resource_dir(executable: &Path) -> Option<PathBuf> {
244 if !executable.is_absolute() {
245 return None;
246 }
247 let dir = executable.parent()?.to_path_buf();
248 let is = |p: &Path, name: &str| {
249 p.file_name()
250 .and_then(|n| n.to_str())
251 .map(|n| n.eq_ignore_ascii_case(name))
252 .unwrap_or(false)
253 };
254 #[cfg(target_os = "macos")]
255 {
256 if is(&dir, "MacOS") {
257 if let Some(contents) = dir.parent() {
258 if is(contents, "Contents") {
259 return Some(contents.join("Resources"));
260 }
261 }
262 }
263 if is(&dir, "Resources") {
264 return Some(dir);
265 }
266 Some(dir.join("Resources"))
267 }
268 #[cfg(target_os = "windows")]
269 {
270 let _ = is;
271 Some(dir)
272 }
273 #[cfg(not(any(target_os = "macos", target_os = "windows")))]
274 {
275 if is(&dir, "resources") {
276 Some(dir)
277 } else {
278 Some(dir.join("resources"))
279 }
280 }
281}
282
283pub fn installed_runtime_candidate(executable: &Path, subdir: &str) -> Option<RuntimeCandidate> {
286 let dir = installed_resource_dir(executable)?;
287 Some(RuntimeCandidate::new(
288 dir.join(subdir).join(runtime_filename()),
289 CandidateSource::Bundled,
290 ))
291}
292
293pub fn configure_runtime(
296 candidates: impl IntoIterator<Item = RuntimeCandidate>,
297) -> Result<RuntimeSelection, RuntimeError> {
298 let selection = resolve_runtime(candidates)?;
299 if let Some(configured) = nonempty_env_path("ORT_DYLIB_PATH") {
300 let configured = canonical_if_file(&configured)?;
301 if configured != selection.path {
302 return Err(RuntimeError::AlreadyConfigured {
303 configured,
304 selected: selection.path,
305 });
306 }
307 return Ok(selection);
308 }
309 std::env::set_var("ORT_DYLIB_PATH", &selection.path);
310 Ok(selection)
311}
312
313fn nonempty_env_path(name: &str) -> Option<PathBuf> {
314 std::env::var_os(name)
315 .filter(|value| !value.is_empty())
316 .map(PathBuf::from)
317}
318
319fn canonical_if_file(path: &Path) -> Result<PathBuf, RuntimeError> {
320 if !path.is_absolute() {
321 return Err(RuntimeError::UnsafePath {
322 path: path.to_path_buf(),
323 });
324 }
325 path.canonicalize()
326 .map_err(|error| RuntimeError::Canonicalize {
327 path: path.to_path_buf(),
328 message: error.to_string(),
329 })
330}