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