use crate::{
bindings::hostfxr::{
hostfxr_resolve_sdk2_flags_t, hostfxr_resolve_sdk2_result_key_t, PATH_LIST_SEPARATOR,
},
error::{HostingError, HostingResult},
hostfxr::{AppOrHostingResult, Hostfxr},
pdcstring::{PdCStr, PdUChar},
};
use coreclr_hosting_shared::char_t;
use std::{
cell::RefCell,
io,
mem::MaybeUninit,
path::{Path, PathBuf},
ptr, slice,
};
use super::UNSUPPORTED_HOST_VERSION_ERROR_CODE;
impl Hostfxr {
#[cfg_attr(
feature = "netcore3_0",
deprecated(note = "Use `HostfxrContext::run_app` instead"),
allow(deprecated)
)]
#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "netcore2_1")))]
pub fn run_app_with_args_and_startup_info<'a, A: AsRef<PdCStr>>(
&'a self,
app_path: &'a PdCStr,
args: impl IntoIterator<Item = &'a PdCStr>,
host_path: &PdCStr,
dotnet_root: &PdCStr,
) -> io::Result<AppOrHostingResult> {
let args = [&self.dotnet_exe, app_path]
.into_iter()
.chain(args)
.map(|s| s.as_ptr())
.collect::<Vec<_>>();
let result = unsafe {
self.lib.hostfxr_main_startupinfo(
args.len().try_into().unwrap(),
args.as_ptr(),
host_path.as_ptr(),
dotnet_root.as_ptr(),
app_path.as_ptr(),
)
}
.unwrap_or(UNSUPPORTED_HOST_VERSION_ERROR_CODE);
Ok(AppOrHostingResult::from(result))
}
#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "netcore2_1")))]
pub fn resolve_sdk(
&self,
sdk_dir: &PdCStr,
working_dir: &PdCStr,
allow_prerelease: bool,
) -> Result<ResolveSdkResult, HostingError> {
let flags = if allow_prerelease {
hostfxr_resolve_sdk2_flags_t::none
} else {
hostfxr_resolve_sdk2_flags_t::disallow_prerelease
};
let result = unsafe {
self.lib.hostfxr_resolve_sdk2(
sdk_dir.as_ptr(),
working_dir.as_ptr(),
flags,
resolve_sdk2_callback,
)
}
.unwrap_or(UNSUPPORTED_HOST_VERSION_ERROR_CODE);
HostingResult::from(result).into_result()?;
let sdk_path = RESOLVE_SDK2_DATA
.with(|sdk| sdk.borrow_mut().take())
.unwrap();
Ok(sdk_path)
}
#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "netcore2_1")))]
#[must_use]
pub fn get_available_sdks(&self) -> Vec<PathBuf> {
self._get_available_sdks(None)
}
#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "netcore2_1")))]
#[must_use]
pub fn get_available_sdks_with_dotnet_path(&self, dotnet_path: &PdCStr) -> Vec<PathBuf> {
self._get_available_sdks(Some(dotnet_path))
}
#[must_use]
fn _get_available_sdks(&self, dotnet_path: Option<&PdCStr>) -> Vec<PathBuf> {
let dotnet_path = dotnet_path.map_or_else(ptr::null, |s| s.as_ptr());
unsafe {
self.lib
.hostfxr_get_available_sdks(dotnet_path, get_available_sdks_callback)
};
GET_AVAILABLE_SDKS_DATA
.with(|sdks| sdks.borrow_mut().take())
.unwrap()
}
#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "netcore2_1")))]
pub fn get_native_search_directories(
&self,
app_path: &PdCStr,
) -> Result<Vec<PathBuf>, HostingError> {
let mut buffer = Vec::<PdUChar>::new();
let args = [self.dotnet_exe.as_ptr(), app_path.as_ptr()];
let mut required_buffer_size = MaybeUninit::uninit();
unsafe {
self.lib.hostfxr_get_native_search_directories(
args.len().try_into().unwrap(),
args.as_ptr(),
buffer.as_mut_ptr().cast(),
0,
required_buffer_size.as_mut_ptr(),
)
};
let mut required_buffer_size = unsafe { required_buffer_size.assume_init() };
buffer.reserve(required_buffer_size.try_into().unwrap());
let result = unsafe {
self.lib.hostfxr_get_native_search_directories(
args.len().try_into().unwrap(),
args.as_ptr(),
buffer.spare_capacity_mut().as_mut_ptr().cast(),
buffer.spare_capacity_mut().len().try_into().unwrap(),
&mut required_buffer_size,
)
}
.unwrap_or(UNSUPPORTED_HOST_VERSION_ERROR_CODE);
HostingResult::from(result).into_result()?;
unsafe { buffer.set_len(required_buffer_size.try_into().unwrap()) };
let mut directories = Vec::new();
let last_start = 0;
for i in 0..buffer.len() {
if buffer[i] == PATH_LIST_SEPARATOR as PdUChar || buffer[i] == 0 {
buffer[i] = 0;
let directory = PdCStr::from_slice_with_nul(&buffer[last_start..=i]).unwrap();
directories.push(PathBuf::from(directory.to_os_string()));
break;
}
}
Ok(directories)
}
}
thread_local! {
static GET_AVAILABLE_SDKS_DATA: RefCell<Option<Vec<PathBuf>>> = const { RefCell::new(None) };
static RESOLVE_SDK2_DATA: RefCell<Option<ResolveSdkResult>> = const { RefCell::new(None) };
}
extern "C" fn get_available_sdks_callback(sdk_count: i32, sdks_ptr: *const *const char_t) {
GET_AVAILABLE_SDKS_DATA.with(|sdks| {
let mut sdks_opt = sdks.borrow_mut();
let sdks = sdks_opt.get_or_insert_with(Vec::new);
let raw_sdks = unsafe { slice::from_raw_parts(sdks_ptr, sdk_count as usize) };
sdks.extend(raw_sdks.iter().copied().map(|raw_sdk| {
unsafe { PdCStr::from_str_ptr(raw_sdk) }
.to_os_string()
.into()
}));
});
}
extern "C" fn resolve_sdk2_callback(key: hostfxr_resolve_sdk2_result_key_t, value: *const char_t) {
RESOLVE_SDK2_DATA.with(|sdks| {
let path = unsafe { PdCStr::from_str_ptr(value) }.to_os_string().into();
*sdks.borrow_mut() = Some(match key {
hostfxr_resolve_sdk2_result_key_t::resolved_sdk_dir => {
ResolveSdkResult::ResolvedSdkDirectory(path)
}
hostfxr_resolve_sdk2_result_key_t::global_json_path => {
ResolveSdkResult::GlobalJsonPath(path)
}
});
});
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "netcore2_1")))]
#[must_use]
pub enum ResolveSdkResult {
ResolvedSdkDirectory(PathBuf),
GlobalJsonPath(PathBuf),
}
impl ResolveSdkResult {
#[must_use]
pub fn into_path(self) -> PathBuf {
match self {
ResolveSdkResult::GlobalJsonPath(path)
| ResolveSdkResult::ResolvedSdkDirectory(path) => path,
}
}
#[must_use]
pub fn path(&self) -> &Path {
match self {
ResolveSdkResult::GlobalJsonPath(path)
| ResolveSdkResult::ResolvedSdkDirectory(path) => path,
}
}
}