updated README and a bunch of other stuff
This commit is contained in:
@@ -1,3 +1,255 @@
|
||||
//! C ABI for dynamically loaded parts (see `include/mize.h`).
|
||||
//!
|
||||
//! Names passed to `opt` / `method` / `thread` / `get_config` / `set_config`
|
||||
//! are absolute dotted paths (e.g. `"vicinae.open"`).
|
||||
//!
|
||||
//! Errors use a thread-local slot (like errno): C code calls `set_err(...)`;
|
||||
//! Rust checks via [`take_err`] after the call.
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::ffi::{c_char, c_void, CStr};
|
||||
use std::ptr;
|
||||
|
||||
//pub extern "C" fn
|
||||
use crate::config::{ConfigOpt, MizeFun};
|
||||
use crate::error::MizeError;
|
||||
use crate::get_global_mize;
|
||||
use crate::item::ItemData;
|
||||
use crate::mize_err;
|
||||
use crate::write_to_mize_stdout;
|
||||
|
||||
type CFun = unsafe extern "C" fn(*mut ItemData) -> *mut ItemData;
|
||||
|
||||
thread_local! {
|
||||
/// Per-thread error set by C via `set_err`, consumed by Rust after the call.
|
||||
static CUR_ERR: RefCell<Option<MizeError>> = const { RefCell::new(None) };
|
||||
}
|
||||
|
||||
/// Clear the thread-local C error (call before invoking a C part entry / callback).
|
||||
pub fn clear_err() {
|
||||
CUR_ERR.with(|slot| {
|
||||
*slot.borrow_mut() = None;
|
||||
});
|
||||
}
|
||||
|
||||
/// Take the thread-local C error, if any.
|
||||
pub fn take_err() -> Option<MizeError> {
|
||||
CUR_ERR.with(|slot| slot.borrow_mut().take())
|
||||
}
|
||||
|
||||
/// Opaque pointer stored via `data_from_native` / read via `data_as_native`.
|
||||
struct NativePtr(*mut c_void);
|
||||
|
||||
// SAFETY: the C++ part is responsible for only using the pointer on the right thread.
|
||||
unsafe impl Send for NativePtr {}
|
||||
unsafe impl Sync for NativePtr {}
|
||||
|
||||
fn cstr<'a>(p: *const c_char) -> Option<&'a str> {
|
||||
if p.is_null() {
|
||||
return None;
|
||||
}
|
||||
unsafe { CStr::from_ptr(p) }.to_str().ok()
|
||||
}
|
||||
|
||||
fn wrap_c_fun(c_fun: CFun) -> MizeFun {
|
||||
Box::new(move |args| {
|
||||
clear_err();
|
||||
let mut args_box = Box::new(args);
|
||||
let ret_ptr = unsafe { c_fun(args_box.as_mut()) };
|
||||
drop(args_box);
|
||||
if let Some(err) = take_err() {
|
||||
if !ret_ptr.is_null() {
|
||||
unsafe { drop(Box::from_raw(ret_ptr)) };
|
||||
}
|
||||
return Err(err);
|
||||
}
|
||||
if ret_ptr.is_null() {
|
||||
return Ok(ItemData::new());
|
||||
}
|
||||
Ok(*unsafe { Box::from_raw(ret_ptr) })
|
||||
})
|
||||
}
|
||||
|
||||
fn register_fun(name: &str, kind: &str, signature: &str, fun: MizeFun) {
|
||||
let mut mize = match get_global_mize() {
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
let _ = write_to_mize_stdout(format_args!("[c_api] get_global_mize failed: {e:?}\n"));
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let id = match mize.id_new_dots(name) {
|
||||
Ok(id) => id,
|
||||
Err(e) => {
|
||||
let _ = write_to_mize_stdout(format_args!("[c_api] bad id '{name}': {e:?}\n"));
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if mize.is_generate_mode().unwrap_or(false) {
|
||||
let short = name.rsplit('.').next().unwrap_or(name);
|
||||
let _ = mize.record_method(short, id.clone(), kind, signature);
|
||||
return;
|
||||
}
|
||||
|
||||
let opt = ConfigOpt {
|
||||
name: id.dot(),
|
||||
val: None,
|
||||
fun: None,
|
||||
deps: Vec::new(),
|
||||
};
|
||||
mize.config_opts.lock().unwrap().insert(id.clone(), opt);
|
||||
mize.fun_values.lock().unwrap().insert(id, fun);
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn opt(name: *const c_char) {
|
||||
let Some(name) = cstr(name) else { return };
|
||||
let mut mize = match get_global_mize() {
|
||||
Ok(m) => m,
|
||||
Err(_) => return,
|
||||
};
|
||||
let Ok(id) = mize.id_new_dots(name) else { return };
|
||||
|
||||
if mize.is_generate_mode().unwrap_or(false) {
|
||||
let short = name.rsplit('.').next().unwrap_or(name);
|
||||
let _ = mize.record_opt(short, id.clone(), None);
|
||||
return;
|
||||
}
|
||||
|
||||
let opt = ConfigOpt {
|
||||
name: id.dot(),
|
||||
val: None,
|
||||
fun: None,
|
||||
deps: Vec::new(),
|
||||
};
|
||||
mize.config_opts.lock().unwrap().insert(id, opt);
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn method(name: *const c_char, signature: *const c_char, c_fun: CFun) {
|
||||
let Some(name) = cstr(name) else { return };
|
||||
let sig = cstr(signature).unwrap_or("");
|
||||
register_fun(name, "method", sig, wrap_c_fun(c_fun));
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn effect(name: *const c_char, c_fun: CFun) {
|
||||
let Some(name) = cstr(name) else { return };
|
||||
register_fun(name, "effect", "", wrap_c_fun(c_fun));
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn apply(name: *const c_char, c_fun: CFun) {
|
||||
let Some(name) = cstr(name) else { return };
|
||||
register_fun(name, "apply", "", wrap_c_fun(c_fun));
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn thread(name: *const c_char, c_fun: CFun) {
|
||||
let Some(name) = cstr(name) else { return };
|
||||
register_fun(name, "thread", "", wrap_c_fun(c_fun));
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn set_config(name: *const c_char, value: *mut ItemData) {
|
||||
if value.is_null() {
|
||||
return;
|
||||
}
|
||||
let Some(name) = cstr(name) else {
|
||||
unsafe { drop(Box::from_raw(value)) };
|
||||
return;
|
||||
};
|
||||
let value = unsafe { *Box::from_raw(value) };
|
||||
let mut mize = match get_global_mize() {
|
||||
Ok(m) => m,
|
||||
Err(_) => return,
|
||||
};
|
||||
let Ok(id) = mize.id_new_dots(name) else { return };
|
||||
|
||||
let mut opts = mize.config_opts.lock().unwrap();
|
||||
match opts.get_mut(&id) {
|
||||
Some(opt) => {
|
||||
opt.val = Some(value);
|
||||
}
|
||||
None => {
|
||||
opts.insert(
|
||||
id.clone(),
|
||||
ConfigOpt {
|
||||
name: id.dot(),
|
||||
val: Some(value),
|
||||
fun: None,
|
||||
deps: Vec::new(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn get_config(name: *const c_char) -> *mut ItemData {
|
||||
let Some(name) = cstr(name) else {
|
||||
return ptr::null_mut();
|
||||
};
|
||||
let mut mize = match get_global_mize() {
|
||||
Ok(m) => m,
|
||||
Err(_) => return ptr::null_mut(),
|
||||
};
|
||||
match mize.get_config(name) {
|
||||
Ok(data) => Box::into_raw(Box::new(data)),
|
||||
Err(_) => ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn data_new() -> *mut ItemData {
|
||||
Box::into_raw(Box::new(ItemData::new()))
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn data_from_native(ptr: *mut c_void) -> *mut ItemData {
|
||||
match ItemData::from_native(NativePtr(ptr)) {
|
||||
Ok(data) => Box::into_raw(Box::new(data)),
|
||||
Err(e) => {
|
||||
let _ = write_to_mize_stdout(format_args!("[c_api] data_from_native: {e:?}\n"));
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn data_as_native(data: *mut ItemData) -> *mut c_void {
|
||||
if data.is_null() {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
let data = unsafe { &*data };
|
||||
match data.native::<NativePtr>() {
|
||||
Ok(guard) => guard.0,
|
||||
Err(e) => {
|
||||
let _ = write_to_mize_stdout(format_args!("[c_api] data_as_native: {e:?}\n"));
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn data_free(data: *mut ItemData) {
|
||||
if !data.is_null() {
|
||||
unsafe { drop(Box::from_raw(data)) };
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn mize_write_stdout(str_ptr: *const c_char) {
|
||||
let Some(s) = cstr(str_ptr) else { return };
|
||||
let _ = write_to_mize_stdout(format_args!("{s}"));
|
||||
}
|
||||
|
||||
/// Set the thread-local error (like errno). `load_part` / C-callback wrappers turn this into `Err`.
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn set_err(msg: *const c_char) {
|
||||
let text = cstr(msg).unwrap_or("set_err called with null/invalid message");
|
||||
CUR_ERR.with(|slot| {
|
||||
*slot.borrow_mut() = Some(mize_err!("{}", text));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -98,6 +98,17 @@ impl<T> MizeAny for T {
|
||||
}
|
||||
|
||||
|
||||
/// Load the `.so` for `part_name`, but register it under `at_id`
|
||||
/// (e.g. `load_part_at("priv.spacetime", "spacetime")`).
|
||||
/// No-op in generate mode — bindings come from listing the part in `gen = [...]`.
|
||||
pub fn load_part_at(at_id: &str, part_name: &str) -> MizeResult<()> {
|
||||
let mize = get_global_mize()?;
|
||||
if mize.is_generate_mode().unwrap_or(false) {
|
||||
return Ok(());
|
||||
}
|
||||
crate::platform::load_part(part_name, None, Some(at_id))
|
||||
}
|
||||
|
||||
pub fn opt(name: &str) -> crate::config::ConfigOptId {
|
||||
let mize = get_global_mize().unwrap();
|
||||
let mut cur_part = mize.cur_eval_part.lock().unwrap().clone().unwrap();
|
||||
@@ -162,6 +173,28 @@ pub fn effect<F: Fn(ItemData) -> MizeResult<ItemData> + Send + Sync + 'static>(n
|
||||
ConfigOptId { id }
|
||||
}
|
||||
|
||||
pub fn apply<F: Fn(ItemData) -> MizeResult<ItemData> + Send + Sync + 'static>(name: &str, fun: F) -> ConfigOptId {
|
||||
let mize = get_global_mize().unwrap();
|
||||
let mut cur_part = mize.cur_eval_part.lock().unwrap().clone().unwrap();
|
||||
let id = cur_part.join(name).unwrap();
|
||||
// in generate mode: record apply metadata, skip normal registration
|
||||
if mize.is_generate_mode().unwrap_or(false) {
|
||||
mize.record_method(name, id.clone(), "apply", "").ok();
|
||||
return ConfigOptId { id };
|
||||
}
|
||||
let mut opts = mize.config_opts.lock().unwrap();
|
||||
let opt = ConfigOpt {
|
||||
name: id.dot(),
|
||||
val: None,
|
||||
fun: None,
|
||||
deps: Vec::new(),
|
||||
};
|
||||
let mut fun_values = mize.fun_values.lock().unwrap();
|
||||
fun_values.insert(id.clone(), Box::new(fun));
|
||||
opts.insert(id.clone(), opt);
|
||||
ConfigOptId { id }
|
||||
}
|
||||
|
||||
pub fn thread<F: Fn(ItemData) -> MizeResult<ItemData> + Send + Sync + 'static>(name: &str, fun: F) -> ConfigOptId {
|
||||
let mize = get_global_mize().unwrap();
|
||||
let mut cur_part = mize.cur_eval_part.lock().unwrap().clone().unwrap();
|
||||
@@ -227,6 +260,18 @@ pub mod c_api {
|
||||
})
|
||||
}
|
||||
}
|
||||
pub extern "C" fn apply(name: *const c_char, c_fun: CFun) -> ConfigOptId {
|
||||
unsafe {
|
||||
let c_str = CStr::from_ptr(name);
|
||||
let string = c_str.to_string_lossy();
|
||||
super::apply(&string, move |args| {
|
||||
let raw_ptr = Box::into_raw(Box::new(args));
|
||||
let ret_ptr = c_fun(raw_ptr);
|
||||
let data = *Box::from_raw(ret_ptr);
|
||||
Ok(data)
|
||||
})
|
||||
}
|
||||
}
|
||||
pub extern "C" fn thread(name: *const c_char, c_fun: CFun) -> ConfigOptId {
|
||||
unsafe {
|
||||
let c_str = CStr::from_ptr(name);
|
||||
|
||||
61
core/src/include/mize.h
Normal file
61
core/src/include/mize.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Mize C API — thin header over libmize.so exports (core/src/c_api.rs).
|
||||
*
|
||||
* Names for opt / method / thread / get_config / set_config are absolute
|
||||
* dotted paths for now (e.g. "vicinae.open", "vicinae.nav").
|
||||
*
|
||||
* Errors: thread-local, like errno. Call set_err("...") from C; Rust
|
||||
* (load_part / method wrappers) turns that into Err. Part entry is
|
||||
* `void part_<name>(void)` — no sret / Result return.
|
||||
*
|
||||
* ItemData (`DATA`) is opaque. Ownership:
|
||||
* - Callbacks receive a borrowed `args` (do not free).
|
||||
* - Callbacks must return an owned DATA* (e.g. data_new()); may return NULL for empty.
|
||||
* - get_config returns owned DATA*; free with data_free.
|
||||
* - set_config takes ownership of the value pointer.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct ItemData DATA;
|
||||
|
||||
/** C method / thread / effect callback. args is borrowed; return owned DATA* or NULL. */
|
||||
typedef DATA *(*CFun)(DATA *args);
|
||||
|
||||
void opt(const char *name);
|
||||
|
||||
void method(const char *name, const char *signature, CFun c_fun);
|
||||
|
||||
void effect(const char *name, CFun c_fun);
|
||||
|
||||
void thread(const char *name, CFun c_fun);
|
||||
|
||||
void set_config(const char *name, DATA *value);
|
||||
|
||||
DATA *get_config(const char *name);
|
||||
|
||||
DATA *data_new(void);
|
||||
|
||||
/** Store an opaque pointer in the native-value registry. */
|
||||
DATA *data_from_native(void *ptr);
|
||||
|
||||
/** Read back a pointer previously stored with data_from_native. Returns NULL on error. */
|
||||
void *data_as_native(DATA *data);
|
||||
|
||||
void data_free(DATA *data);
|
||||
|
||||
void mize_write_stdout(const char *str);
|
||||
|
||||
/** Set the thread-local error message (consumed by Rust after the call). */
|
||||
void set_err(const char *msg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -105,6 +105,7 @@ pub struct PartMetadata {
|
||||
pub opts: Vec<OptMetadata>,
|
||||
pub methods: Vec<MethodMetadata>,
|
||||
pub effects: Vec<MethodMetadata>,
|
||||
pub applies: Vec<MethodMetadata>,
|
||||
pub threads: Vec<MethodMetadata>,
|
||||
pub natives: Vec<NativeMetadata>,
|
||||
}
|
||||
@@ -120,7 +121,7 @@ pub struct OptMetadata {
|
||||
pub struct MethodMetadata {
|
||||
pub name: String,
|
||||
pub id: MizeId,
|
||||
pub kind: String, // "method", "effect", "thread"
|
||||
pub kind: String, // "method", "effect", "apply", "thread"
|
||||
/// Raw signature string, e.g. `"String -> ()"` or `"Command, Func -> ()"`.
|
||||
pub signature: String,
|
||||
/// Parsed argument type names (left of `->`).
|
||||
@@ -339,20 +340,23 @@ impl Mize {
|
||||
// end of platform specific init code
|
||||
|
||||
// load the parts, as specified in the parts config
|
||||
match self
|
||||
.get_config("parts")
|
||||
{
|
||||
Ok(parts_to_load) => {
|
||||
for part_name in parts_to_load.cbor().as_array().ok_or("config parts was not an array")? {
|
||||
let part_name = part_name.as_text().ok_or("part name was not a string")?;
|
||||
if part_name.trim().is_empty() || self.has_part(part_name)? {
|
||||
continue;
|
||||
}
|
||||
self.load_part(part_name)?;
|
||||
// Prefer `parts` when it is an array; otherwise `parts.load` (so `parts.workspaces` /
|
||||
// `parts.dist.cd` can coexist under a `parts` table in TOML).
|
||||
let parts_to_load = match self.get_config("parts") {
|
||||
Ok(data) if data.cbor().as_array().is_some() => Some(data),
|
||||
_ => self.get_config("parts.load").ok(),
|
||||
};
|
||||
if let Some(parts_to_load) = parts_to_load {
|
||||
for part_name in parts_to_load
|
||||
.cbor()
|
||||
.as_array()
|
||||
.ok_or("config parts/parts.load was not an array")?
|
||||
{
|
||||
let part_name = part_name.as_text().ok_or("part name was not a string")?;
|
||||
if part_name.trim().is_empty() || self.has_part(part_name)? {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Err(_err) => {
|
||||
// no parts to load
|
||||
self.load_part(part_name)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -534,7 +538,7 @@ impl Mize {
|
||||
|
||||
pub fn load_part(&mut self, name: &str) -> MizeResult<()> {
|
||||
tracing::debug!("loading part: {name}");
|
||||
crate::platform::load_part(self, name, None)
|
||||
crate::platform::load_part(name, None, None)
|
||||
}
|
||||
|
||||
pub fn fetch_part(&mut self, name: &str) -> MizeResult<String> {
|
||||
@@ -542,9 +546,8 @@ impl Mize {
|
||||
crate::platform::fetch_part(self, name)
|
||||
}
|
||||
|
||||
pub fn load_part_at(&mut self, name: &str, path: String) -> MizeResult<()> {
|
||||
// platform specific init code
|
||||
crate::platform::load_part(self, name, Some(path))
|
||||
pub fn load_part_from_path(&mut self, name: &str, path: String) -> MizeResult<()> {
|
||||
crate::platform::load_part(name, Some(path), None)
|
||||
}
|
||||
|
||||
// ===== generate mode accessors =====
|
||||
@@ -600,6 +603,7 @@ impl Mize {
|
||||
};
|
||||
match kind {
|
||||
"effect" => metadata.effects.push(m),
|
||||
"apply" => metadata.applies.push(m),
|
||||
"thread" => metadata.threads.push(m),
|
||||
_ => metadata.methods.push(m),
|
||||
}
|
||||
@@ -1037,9 +1041,7 @@ impl Mize {
|
||||
|
||||
let calling_part = self.clone().cur_eval_part()?;
|
||||
self.clone().set_cur_eval_part(Some(part_id));
|
||||
println!("before call");
|
||||
let res = fun(args);
|
||||
println!("after call");
|
||||
self.clone().set_cur_eval_part(calling_part);
|
||||
let mut fun_values = self.fun_values.lock()?;
|
||||
fun_values.insert(id, fun);
|
||||
|
||||
@@ -48,14 +48,14 @@ fn test_get_module_hash() -> MizeResult<()> {
|
||||
toolchain_version = "rustc 1.78.0 (9b00956e5 2024-04-29)"
|
||||
"#;
|
||||
|
||||
instance.set_blocking("0/config/selector", ItemData::from_toml(&selector_toml)?)?;
|
||||
|
||||
let (hash, selector) = get_part_hash(&mut instance, "String", ItemData::new())?;
|
||||
// Pass selector fields as hash options (merges over defaults).
|
||||
let options = ItemData::from_toml(&selector_toml)?;
|
||||
let (hash, selector) = get_part_hash(&mut instance, "String", options)?;
|
||||
|
||||
println!("hash: {}", hash);
|
||||
println!("selector: {}", selector);
|
||||
|
||||
assert_eq!(hash, "e96edf60216688314efc263c1aa8d6ce".to_owned());
|
||||
assert_eq!(hash, "227b0216f9b7a324a621ff773c5241c9".to_owned());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -238,6 +238,9 @@ impl ItemData {
|
||||
pub fn value_string(self) -> MizeResult<String> {
|
||||
match self.cbor() {
|
||||
CborValue::Text(string) => Ok(string.to_owned()),
|
||||
CborValue::Bool(val) => Ok(format!("{val}")),
|
||||
CborValue::Integer(val) => Ok(format!("{}", u32::try_from(val.clone())?)),
|
||||
CborValue::Float(val) => Ok(format!("{val}")),
|
||||
_ => Err(mize_err!("this ItemData has no string value")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,9 +292,9 @@ pub mod platform {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn load_part(mize: &mut Mize, part_name: &str, path: Option<String>) -> MizeResult<()> {
|
||||
pub fn load_part(part_name: &str, path: Option<String>, at_id: Option<&str>) -> MizeResult<()> {
|
||||
#[cfg(feature = "target-os")] {
|
||||
os::load_part(mize, part_name, path)?;
|
||||
os::load_part(part_name, path, at_id)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -171,12 +171,158 @@ pub fn seconds_since_modification(path: &Path) -> MizeResult<u64> {
|
||||
Ok(duration.as_secs())
|
||||
}
|
||||
|
||||
/// Best-effort rustc target triple for the currently running binary.
|
||||
pub fn host_rust_target() -> &'static str {
|
||||
#[cfg(all(target_arch = "x86_64", target_os = "linux", target_env = "gnu"))]
|
||||
{
|
||||
"x86_64-unknown-linux-gnu"
|
||||
}
|
||||
#[cfg(all(target_arch = "aarch64", target_os = "linux", target_env = "gnu"))]
|
||||
{
|
||||
"aarch64-unknown-linux-gnu"
|
||||
}
|
||||
#[cfg(all(target_arch = "x86_64", target_os = "macos"))]
|
||||
{
|
||||
"x86_64-apple-darwin"
|
||||
}
|
||||
#[cfg(all(target_arch = "aarch64", target_os = "macos"))]
|
||||
{
|
||||
"aarch64-apple-darwin"
|
||||
}
|
||||
#[cfg(all(target_arch = "x86_64", target_os = "windows"))]
|
||||
{
|
||||
"x86_64-pc-windows-gnu"
|
||||
}
|
||||
#[cfg(all(target_arch = "aarch64", target_os = "windows"))]
|
||||
{
|
||||
"aarch64-pc-windows-gnullvm"
|
||||
}
|
||||
#[cfg(not(any(
|
||||
all(target_arch = "x86_64", target_os = "linux", target_env = "gnu"),
|
||||
all(target_arch = "aarch64", target_os = "linux", target_env = "gnu"),
|
||||
all(target_arch = "x86_64", target_os = "macos"),
|
||||
all(target_arch = "aarch64", target_os = "macos"),
|
||||
all(target_arch = "x86_64", target_os = "windows"),
|
||||
all(target_arch = "aarch64", target_os = "windows"),
|
||||
)))]
|
||||
{
|
||||
"unknown"
|
||||
}
|
||||
}
|
||||
|
||||
/// Dist artifact filename: `{hash}-{part_id}-cd.tar.gz` or `{hash}-{part_id}-{version}.tar.gz`.
|
||||
pub fn part_dist_artifact_name(hash: &str, part_id: &str, version: &str, cd: bool) -> String {
|
||||
if cd {
|
||||
format!("{hash}-{part_id}-cd.tar.gz")
|
||||
} else {
|
||||
format!("{hash}-{part_id}-{version}.tar.gz")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parts_dist_cd(mize: &mut Mize) -> bool {
|
||||
mize.get_config("parts.dist.cd")
|
||||
.ok()
|
||||
.and_then(|d| d.value_bool().ok())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Read `version` from a Cargo.toml's `[package]` table.
|
||||
pub fn cargo_package_version(cargo_toml_path: &str) -> MizeResult<String> {
|
||||
let contents = fs::read_to_string(cargo_toml_path)
|
||||
.map_err(|e| mize_err!("could not read {cargo_toml_path}: {e}"))?;
|
||||
let data = ItemData::from_toml(&contents)?;
|
||||
data.get_path("package.version")?
|
||||
.value_string()
|
||||
.map_err(|e| e.msg(format!("no package.version in {cargo_toml_path}")))
|
||||
}
|
||||
|
||||
/// Resolve a part's cargo version for dist URLs (non-cd).
|
||||
pub fn resolve_part_version(mize: &mut Mize, part_name: &str) -> MizeResult<String> {
|
||||
if let Ok(v) = mize.get_config(&format!("{part_name}.version")) {
|
||||
if let Ok(s) = v.value_string() {
|
||||
if !s.is_empty() {
|
||||
return Ok(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if part_name == "mize" {
|
||||
return Ok(env!("CARGO_PKG_VERSION").to_owned());
|
||||
}
|
||||
|
||||
if let Ok(workspaces) = mize.get_config("parts.workspaces") {
|
||||
if let Some(arr) = workspaces.cbor().as_array() {
|
||||
for src in arr {
|
||||
let Some(dir) = src.as_text() else { continue };
|
||||
for candidate in [
|
||||
format!("{dir}/{part_name}/Cargo.toml"),
|
||||
format!("{dir}/mize.{part_name}/Cargo.toml"),
|
||||
] {
|
||||
if PathBuf::from(&candidate).exists() {
|
||||
return cargo_package_version(&candidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(mize_src) = mize.get_config("mize.src").and_then(|d| d.value_string()) {
|
||||
for candidate in [
|
||||
format!("{mize_src}/parts/{part_name}/Cargo.toml"),
|
||||
format!("{mize_src}/parts/mize.{part_name}/Cargo.toml"),
|
||||
format!("{mize_src}/core/Cargo.toml"),
|
||||
] {
|
||||
if part_name != "mize" && candidate.ends_with("core/Cargo.toml") {
|
||||
continue;
|
||||
}
|
||||
if PathBuf::from(&candidate).exists() {
|
||||
return cargo_package_version(&candidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(mize_err!(
|
||||
"could not resolve cargo version for part '{part_name}' (set {part_name}.version or parts.workspaces, or enable parts.dist.cd)"
|
||||
))
|
||||
}
|
||||
|
||||
fn load_selector_base(mize: &mut Mize) -> ItemData {
|
||||
// Prefer config opt; fall back to store path used by tests / older setups.
|
||||
if let Ok(sel) = mize.get("self/config/selector").and_then(|i| i.as_data_full()) {
|
||||
if !sel.cbor().is_null() {
|
||||
return sel;
|
||||
}
|
||||
}
|
||||
mize.get("0/config/selector")
|
||||
.and_then(|i| i.as_data_full())
|
||||
.unwrap_or_else(|_| ItemData::new())
|
||||
}
|
||||
|
||||
pub fn get_part_hash(
|
||||
mize: &mut Mize,
|
||||
name: &str,
|
||||
options: ItemData,
|
||||
) -> MizeResult<(String, String)> {
|
||||
let mut selector_data = mize.get("self/config/selector")?.as_data_full()?;
|
||||
let mut selector_data = load_selector_base(mize);
|
||||
|
||||
if selector_data
|
||||
.get_path("mize_version")
|
||||
.ok()
|
||||
.and_then(|v| v.value_string().ok())
|
||||
.filter(|s| !s.is_empty())
|
||||
.is_none()
|
||||
{
|
||||
selector_data.set_path("mize_version", env!("CARGO_PKG_VERSION"))?;
|
||||
}
|
||||
if selector_data
|
||||
.get_path("system")
|
||||
.ok()
|
||||
.and_then(|v| v.value_string().ok())
|
||||
.filter(|s| !s.is_empty())
|
||||
.is_none()
|
||||
{
|
||||
selector_data.set_path("system", host_rust_target())?;
|
||||
}
|
||||
|
||||
// handle the case, when we want a part for not the system we are running on...
|
||||
debug!("get_part_hash: name: {}", name);
|
||||
@@ -227,21 +373,31 @@ pub fn get_part_hash(
|
||||
|
||||
pub fn fetch_part(mize: &mut Mize, part_name: &str) -> MizeResult<String> {
|
||||
let (part_hash, part_selector) = get_part_hash(mize, part_name, ItemData::new())?;
|
||||
let part_url = "https://mize.works/mize/dist/{part_hash}-{part_name}.tar.gz";
|
||||
let cd = parts_dist_cd(mize);
|
||||
let version = if cd {
|
||||
String::new()
|
||||
} else {
|
||||
resolve_part_version(mize, part_name)?
|
||||
};
|
||||
let artifact = part_dist_artifact_name(&part_hash, part_name, &version, cd);
|
||||
let part_url = format!("https://dist.mize.works/{artifact}");
|
||||
|
||||
let store_path = mize.get_config("store")?.value_string()?;
|
||||
let parts_path = format!("{store_path}/parts");
|
||||
let target_dir = format!("{parts_path}/{part_hash}-{part_name}");
|
||||
|
||||
|
||||
let tmp_file_path_gz = format!("{parts_path}/{part_hash}-{part_name}.tar.gz");
|
||||
let tmp_file_path_gz = format!("{parts_path}/{artifact}");
|
||||
std::fs::create_dir_all(&parts_path)?;
|
||||
|
||||
let tmp_gz_file = if !PathBuf::from(tmp_file_path_gz.clone()).exists() {
|
||||
let mut tmp_gz_file = File::create(&tmp_file_path_gz)?;
|
||||
|
||||
debug!("downloading part '{}' with hash: {}", part_name, part_hash);
|
||||
debug!(
|
||||
"downloading part '{}' from {} (selector: {})",
|
||||
part_name, part_url, part_selector
|
||||
);
|
||||
|
||||
let response = http_req::request::get(part_url, &mut tmp_gz_file)?;
|
||||
let response = http_req::request::get(&part_url, &mut tmp_gz_file)?;
|
||||
|
||||
let code = response.status_code();
|
||||
if code != 200.into() {
|
||||
@@ -252,8 +408,8 @@ pub fn fetch_part(mize: &mut Mize, part_name: &str) -> MizeResult<String> {
|
||||
part_selector
|
||||
)
|
||||
.msg(format!(
|
||||
"the http request to fetch the part returned a status code of: {}",
|
||||
code
|
||||
"the http request to fetch the part from '{}' returned a status code of: {}",
|
||||
part_url, code
|
||||
)));
|
||||
}
|
||||
|
||||
@@ -309,8 +465,11 @@ fn get_part_path(mize: &mut Mize, part_name: &str, _path: Option<String>) -> Miz
|
||||
//Err(mize_err!("part {part_name} not found"))
|
||||
}
|
||||
|
||||
pub fn load_part(mize: &mut Mize, part_name: &str, path: Option<String>) -> MizeResult<()> {
|
||||
let part_path = get_part_path(mize, part_name, path)?;
|
||||
/// Load a part `.so` for `part_name`, registering it under `at_id` (or `part_name` if None).
|
||||
/// Uses `get_global_mize()` so nested loads from inside another part's init work.
|
||||
pub fn load_part(part_name: &str, path: Option<String>, at_id: Option<&str>) -> MizeResult<()> {
|
||||
let mut mize = crate::get_global_mize()?;
|
||||
let part_path = get_part_path(&mut mize, part_name, path)?;
|
||||
|
||||
let lib = unsafe { libloading::Library::new(part_path)? };
|
||||
|
||||
@@ -326,32 +485,35 @@ pub fn load_part(mize: &mut Mize, part_name: &str, path: Option<String>) -> Mize
|
||||
|
||||
let part_symbol_name = part_name.replace(".", "_");
|
||||
|
||||
let func: libloading::Symbol<
|
||||
unsafe extern "C" fn() -> MizeResult<()>,
|
||||
> = unsafe { lib.get(format!("part_{}", part_symbol_name).as_bytes())? };
|
||||
let func: libloading::Symbol<unsafe extern "C" fn()> =
|
||||
unsafe { lib.get(format!("part_{}", part_symbol_name).as_bytes())? };
|
||||
|
||||
let part_id = mize.clone().id_new_dots(part_name)?;
|
||||
let eval_name = at_id.unwrap_or(part_name);
|
||||
let part_id = mize.clone().id_new_dots(eval_name)?;
|
||||
{
|
||||
let mut opts = mize.config_opts.lock()?;
|
||||
opts.entry(part_id.clone()).or_insert_with(|| ConfigOpt {
|
||||
name: part_name.to_owned(),
|
||||
name: eval_name.to_owned(),
|
||||
val: Some(ItemData::new()),
|
||||
fun: None,
|
||||
deps: Vec::new(),
|
||||
});
|
||||
}
|
||||
|
||||
let prev_eval = mize.clone().cur_eval_part()?;
|
||||
mize.clone().set_cur_eval_part(Some(part_id))?;
|
||||
unsafe { func()? };
|
||||
mize.clone().set_cur_eval_part(None)?;
|
||||
|
||||
let mut cur_eval_part = mize.cur_eval_part.lock()?;
|
||||
*cur_eval_part = None;
|
||||
crate::c_api::clear_err();
|
||||
unsafe { func() };
|
||||
let c_err = crate::c_api::take_err();
|
||||
mize.clone().set_cur_eval_part(prev_eval)?;
|
||||
|
||||
// dropping the lib, would (i suspect) free all memory, of the library's code, which would
|
||||
// make the part's vtable point into empty memory
|
||||
std::mem::forget(lib);
|
||||
|
||||
if let Some(err) = c_err {
|
||||
return Err(err);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user