Compare commits

...

12 commits

27 changed files with 277 additions and 114 deletions

2
.clangd Normal file
View file

@ -0,0 +1,2 @@
CompileFlags:
Add: [-std=c++23]

View file

@ -15,6 +15,7 @@ targets = [ "x86_64-unknown-linux-gnu" ]
[lib]
path = "src/lib.rs"
# crate-type = ["dylib"] # build libnixide_sys.so
# NOTE: `[features]` have a 1-1 correspondence to the
# NOTE: shared libraries produced by the Nix C API.

View file

@ -92,36 +92,39 @@ const LIBS: &[&'static str] = &[
];
fn main() {
let include_paths: Vec<PathBuf> = LIBS
let libs: Vec<pkg_config::Library> = LIBS
.iter()
.map(|&name| {
let lib = pkg_config::probe_library(name)
.expect(&format!("Unable to find .pc file for {}", name));
for p in lib.link_files {
println!("cargo::rustc-link-lib={}", p.display());
}
lib.include_paths
pkg_config::probe_library(name).expect(&format!("Unable to find .pc file for {}", name))
})
.collect();
let include_paths: Vec<PathBuf> = libs
.clone()
.into_iter()
.map(|lib| lib.include_paths)
.flatten()
.unique()
.collect();
let link_paths: Vec<PathBuf> = libs
.clone()
.into_iter()
.map(|lib| lib.link_files)
.flatten()
.unique()
.collect();
// let clang_target = format!(
// "--target={}",
// env::var("CARGO_BUILD_TARGET")
// .expect("Environment variable `CARGO_BUILD_TARGET` not set! nixide/build.rs won't be able to set pointer sizes correctly!"));
let clang_args: Vec<String> = vec![
"-x",
"c++",
"-std=c++23",
"--target=x86_64-unknown-linux-gnu", // DEBUG
]
.into_iter()
.map(|s: &str| s.to_owned())
.chain(include_paths.iter().map(|p| format!("-I{}", p.display())))
.collect();
// ::DEBUG:DEBUG::
// for path in link_paths {
// println!("cargo::rustc-link-lib={}", path.display());
// }
// ::DEBUG:DEBUG::
let clang_args: Vec<String> = vec!["-x", "c++", "-std=c++23"]
.into_iter()
.map(|s: &str| s.to_owned())
.chain(include_paths.iter().map(|p| format!("-I{}", p.display())))
.collect();
dbg!(&clang_args);
@ -139,7 +142,7 @@ fn main() {
// .files(LIBS.iter().map(|s| (*s).strip_prefix("nix-").unwrap().strip_suffix("-c").unwrap()))
.opt_level((!cfg!(debug_assertions)) as u32 * 3)
.includes(&include_paths)
.compile("libnixide");
.compile("nixide");
let mut builder = bindgen::Builder::default()
.rust_edition(RustEdition::Edition2024)
@ -152,26 +155,11 @@ fn main() {
.formatter(bindgen::Formatter::Rustfmt)
.rustfmt_configuration_file(std::fs::canonicalize("rustfmt.toml").ok())
// Control allow/block listing
.allowlist_recursively(true) // DEBUG(TEMP): should remain `false`
.allowlist_recursively(true)
// .allowlist_file(r".*nix_api_[a-z]+(_internal)?\.h")
.allowlist_file(r".*nix_api_[a-z]+\.h")
.allowlist_file(r".*nix_api_[a-z]+(/[a-z_]+)?\.h")
// .allowlist_type("nix_.*")
// .allowlist_function("nix_.*")
// DEBUG
// .allowlist_type(r"nix_locked_flake")
// .allowlist_type(r"nix_flake_settings")
// .allowlist_type(r"nix_flake_reference")
// .allowlist_type(r"nix_flake_reference_parse_flags")
// .allowlist_type(r"nix_flake_lock_flags")
// DEBUG
// .allowlist_file(r".+-gcc-14\.3\.0/include/c\+\+/14\.3\.0/optional") // allow optional type
// .blocklist_file(r".*-gcc-[^/]+/include/c++/[0-9\.]+/^(optional).*") // allow optional type
// .blocklist_file(r".*-(glibc|clang-wrapper|boost)-.*") // block stdlib (gcc|glibc|clang-wrapper|boost)
// .blocklist_type(r".*Optional.*")
.layout_tests(false) // DEBUG
// .layout_tests(false) // DEBUG
.use_core() // use ::core instead of ::std
.ctypes_prefix("::core::ffi") // use ::core::ffi instead of ::std::os::raw
.time_phases(true)
@ -182,13 +170,13 @@ fn main() {
.default_enum_style(bindgen::EnumVariation::Rust { non_exhaustive: false })
// .translate_enum_integer_types(false)
.size_t_is_usize(true)
.use_distinct_char16_t(false) // DEBUG (comment this)
// .use_distinct_char16_t(false) // DEBUG
.generate_comments(false)
.generate_cstr(true) // use &CStr instead of &[u8]
.fit_macro_constants(true) // DEBUG (comment this)
.fit_macro_constants(false)
.explicit_padding(true)
.enable_cxx_namespaces()
.represent_cxx_operators(false) // DEBUG (comment this)
// .enable_cxx_namespaces()
// .represent_cxx_operators(false) // DEBUG
.enable_function_attribute_detection()
.raw_line("/** These bindings were auto-generated for the Nixide project (https://github.com/cry128/nixide) */");

View file

@ -6,4 +6,7 @@
#include <nix_api_store.h>
// #include <nix_api_store_internal.h>
#include <nix_api_store/derivation.h>
#include <nix_api_store/store_path.h>
#endif

View file

@ -0,0 +1,12 @@
#ifndef NIXIDE_API_EXPR_H
#define NIXIDE_API_EXPR_H
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
} // extern "C"
#endif
#endif // NIXIDE_API_EXPR_H

View file

@ -1,7 +1,7 @@
#include "nix_api_util_internal.h"
#include "nix_api_fetchers_internal.hh"
#include <nix_api_util_internal.h>
#include <nix_api_fetchers_internal.hh>
#include "nix/fetchers/fetch-settings.hh"
#include <nix/fetchers/fetch-settings.hh>
extern "C" {

View file

@ -1,9 +1,10 @@
#ifndef NIXIDE_API_FETCHERS_H
#define NIXIDE_API_FETCHERS_H
#include "nix_api_fetchers.h"
#include <sys/types.h>
#include <nix_api_fetchers.h>
#ifdef __cplusplus
extern "C" {
#endif

View file

@ -1,12 +1,12 @@
// #include <string>
// #include "nix_api_flake.h"
#include "nix_api_flake_internal.hh"
// #include "nix_api_util.h"
#include "nix_api_util_internal.h"
// #include "nix_api_expr_internal.h"
// #include "nix_api_fetchers_internal.hh"
// #include "nix_api_fetchers.h"
// #include <nix_api_flake.h>
#include <nix_api_flake_internal.hh>
// #include <nix_api_util.h>
#include <nix_api_util_internal.h>
// #include <nix_api_expr_internal.h>
// #include <nix_api_fetchers_internal.hh>
// #include <nix_api_fetchers.h>
#include "nix/flake/flake.hh"

View file

@ -1,7 +1,7 @@
#ifndef NIXIDE_API_FLAKE_H
#define NIXIDE_API_FLAKE_H
#include "nix_api_flake.h"
#include <nix_api_flake.h>
#ifdef __cplusplus
extern "C" {

View file

@ -0,0 +1,30 @@
#include <dlfcn.h>
#include <nix_api_util.h>
#include <nix_api_util_internal.h>
#include <nix/main/plugin.hh>
extern "C" {
nix_err nixide_register_plugin(nix_c_context * context, char * plugin)
{
if (context)
context->last_err_code = NIX_OK;
if (plugin == nullptr) {
// TODO: should this be `NIX_ERR_RECOVERABLE` or `NIX_ERR_UNKNOWN`?
return nix_set_err_msg(context, NIX_ERR_RECOVERABLE, "Plugin is null");
}
void * handle = dlopen("libnixmainc.so", RTLD_LAZY | RTLD_GLOBAL);
if (!handle) {
return nix_set_err_msg(context, NIX_ERR_UNKNOWN, dlerror());
}
void * sym_addr = dlsym(handle, "pluginSettings");
try {
}
NIXC_CATCH_ERRS
}
}

View file

@ -0,0 +1,17 @@
#ifndef NIXIDE_API_MAIN_H
#define NIXIDE_API_MAIN_H
#include <nix_api_main.h>
#ifdef __cplusplus
extern "C" {
#endif
// NOTE: all plugins should be registered BEFORE `nix_init_plugins` is run
nix_err nixide_register_plugin(nix_c_context * context, char * plugin);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // NIXIDE_API_MAIN_H

View file

@ -0,0 +1,12 @@
#ifndef NIXIDE_API_STORE_H
#define NIXIDE_API_STORE_H
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
} // extern "C"
#endif
#endif // NIXIDE_API_STORE_H

View file

@ -0,0 +1,12 @@
#ifndef NIXIDE_API_UTIL_H
#define NIXIDE_API_UTIL_H
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
} // extern "C"
#endif
#endif // NIXIDE_API_UTIL_H

View file

@ -20,7 +20,7 @@ mod bindings {
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
}
pub use bindings::root::*;
pub use bindings::*;
mod exts;
#[allow(unused_imports)]

View file

@ -13,8 +13,9 @@ build = "build.rs"
path = "src/lib.rs"
[features]
default = []
store = ["nixide-sys/nix-store-c"]
default = ["nix"]
nix = ["nixide-sys/nix-util-c", "nixide-sys/nix-main-c"]
store = ["nix", "nixide-sys/nix-store-c"]
exprs = ["store", "nixide-sys/nix-expr-c"]
flakes = ["exprs", "nixide-sys/nix-flake-c", "nixide-sys/nix-fetchers-c"]
@ -22,7 +23,7 @@ flakes = ["exprs", "nixide-sys/nix-flake-c", "nixide-sys/nix-fetchers-c"]
libc = "0.2.183"
stdext = "0.3.3"
ctor = "0.6.3"
nixide-sys = { path = "../nixide-sys", version = "0.1.0", features = ["nix-util-c", "nix-main-c"]}
nixide-sys = { path = "../nixide-sys", version = "0.1.0" }
tempfile = "3.27.0"
[dev-dependencies]

View file

@ -118,6 +118,8 @@ impl Into<NixideResult<()>> for &ErrorContext {
.unwrap_or_else(|| panic_issue_call_failed!()),
},
// XXX: WARNING: Recoverable only exists in later version of Nix
sys::NixErr::Recoverable => NixError::Recoverable,
sys::NixErr::Unknown => NixError::Unknown,
};

View file

@ -6,6 +6,22 @@ use crate::sys;
/// produced by the libnix C API.
#[derive(Debug, Clone)]
pub enum NixError {
/// A recoverable error occurred.
///
/// # Reason
///
/// This is used primarily by C API *consumers* to communicate that a failed
/// primop call should be retried on the next evaluation attempt.
///
/// # Nix C++ API Internals
///
/// ```cpp
/// // `NIX_ERR_NIX_ERROR` variant of the `nix_err` enum type
/// NIX_ERR_RECOVERABLE = -4
/// ```
///
Recoverable,
/// A generic Nix error occurred.
///
/// # Reason
@ -19,6 +35,7 @@ pub enum NixError {
/// // `NIX_ERR_NIX_ERROR` variant of the `nix_err` enum type
/// NIX_ERR_NIX_ERROR = -4
/// ```
///
ExprEval { name: String, info_msg: String },
/// A key/index access error occurred in C API functions.
@ -47,6 +64,7 @@ pub enum NixError {
/// // `NIX_ERR_KEY` variant of the `nix_err` enum type
/// NIX_ERR_KEY = -3
/// ```
///
KeyNotFound(Option<String>),
/// An overflow error occurred.
@ -62,6 +80,7 @@ pub enum NixError {
/// // `NIX_ERR_OVERFLOW` variant of the `nix_err` enum type
/// NIX_ERR_OVERFLOW = -2
/// ```
///
Overflow,
/// An unknown error occurred.
@ -77,6 +96,7 @@ pub enum NixError {
/// // `NIX_ERR_OVERFLOW` variant of the `nix_err` enum type
/// NIX_ERR_UNKNOWN = -1
/// ```
///
Unknown,
///
@ -95,12 +115,14 @@ pub enum NixError {
/// This is solely a language difference between C++ and Rust, since
/// [sys::NixErr] is defined over the *"continuous" (not realy)*
/// type [::core::ffi::c_int].
///
Undocumented(sys::NixErr),
}
impl Display for NixError {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
match self {
NixError::Recoverable => write!(f, "[libnix] Recoverable error occurred"),
NixError::ExprEval { name, info_msg } => write!(
f,
"[libnix] NixExpr evaluation failed [name=\"{name}\", info_msg=\"{info_msg}\"]"
@ -120,12 +142,13 @@ impl Display for NixError {
impl NixError {
pub fn err_code(&self) -> sys::NixErr {
match self {
NixError::Overflow => sys::NixErr::Overflow,
NixError::KeyNotFound(_) => sys::NixErr::Key,
NixError::Recoverable => sys::NixErr::Recoverable,
NixError::ExprEval {
name: _,
info_msg: _,
} => sys::NixErr::NixError,
NixError::KeyNotFound(_) => sys::NixErr::Key,
NixError::Overflow => sys::NixErr::Overflow,
NixError::Unknown => sys::NixErr::NixError,
NixError::Undocumented(err) => err.clone(),
}

View file

@ -1,6 +1,7 @@
use std::cell::RefCell;
use std::ffi::c_char;
use std::ptr::NonNull;
use std::rc::Rc;
use crate::errors::ErrorContext;
use crate::expr::values::NixString;
@ -74,15 +75,18 @@ impl<'a> RealisedString<'a> {
Ok(Self {
inner: RefCell::new(inner),
path: Self::parse_path(inner.as_ptr(), &state.store_ref().borrow()),
path: Self::parse_path(inner.as_ptr(), state.store_ref().clone()),
children: LazyArray::new(
size,
Box::new(|_| StorePath::fake_path(&state.store_ref().borrow()).unwrap()),
Box::new(|_| StorePath::fake_path(state.store_ref().clone()).unwrap()),
),
})
}
fn parse_path(realised_string: *mut sys::NixRealisedString, store: &Store) -> StorePath {
fn parse_path(
realised_string: *mut sys::NixRealisedString,
store: Rc<RefCell<Store>>,
) -> StorePath {
let buffer_ptr = unsafe { sys::nix_realised_string_get_buffer_start(realised_string) };
let buffer_size = unsafe { sys::nix_realised_string_get_buffer_size(realised_string) };

View file

@ -12,6 +12,8 @@ use crate::util::wrappers::AsInnerPtr;
pub enum FlakeLockMode {
/// Configures [LockedFlake::lock] to make incremental changes to the lock file as needed. Changes are written to file.
///
/// This is the default mode.
///
WriteAsNeeded,
/// Like [FlakeLockMode::WriteAsNeeded], but does not write to the lock file.

View file

@ -166,7 +166,10 @@ mod tests {
assert_eq!(flakeref.fragment(), "subthing");
let outputs = LockedFlake::lock(, flakeref, &eval_state).unwrap().outputs().unwrap();
let outputs = LockedFlake::lock(FlakeLockMode::WriteAsNeeded, flakeref, &eval_state)
.unwrap()
.outputs()
.unwrap();
assert!(matches!(outputs, Value::Attrs(_)));
if let Value::Attrs(outputs) = outputs {
@ -262,9 +265,9 @@ mod tests {
assert_eq!(flakeref_a.fragment(), "");
// Step 1: Do not update (check), fails
flake_lock_flags.set_mode(&FlakeLockMode::Check).unwrap();
flake_lock_flags.set_mode(FlakeLockMode::Check).unwrap();
let locked_flake = LockedFlake::lock(flake_lock_flags, flakeref_a, &eval_state);
let locked_flake = LockedFlake::lock(FlakeLockMode::Check, flakeref_a, &eval_state);
// Has not been locked and would need to write a lock file.
assert!(locked_flake.is_err());
let saved_err = match locked_flake {
@ -273,7 +276,7 @@ mod tests {
};
// Step 2: Update but do not write, succeeds
flake_lock_flags.set_mode(&FlakeLockMode::Virtual).unwrap();
flake_lock_flags.set_mode(FlakeLockMode::Virtual).unwrap();
let locked_flake = LockedFlake::lock(flake_lock_flags, flakeref_a, &eval_state).unwrap();
@ -290,7 +293,7 @@ mod tests {
}
// Step 3: The lock was not written, so Step 1 would fail again
flake_lock_flags.set_mode(&FlakeLockMode::Check).unwrap();
flake_lock_flags.set_mode(FlakeLockMode::Check).unwrap();
let locked_flake = LockedFlake::lock(flake_lock_flags, flakeref_a, &eval_state);
// Has not been locked and would need to write a lock file.
@ -303,7 +306,7 @@ mod tests {
// Step 4: Update and write, succeeds
flake_lock_flags
.set_mode(&FlakeLockMode::WriteAsNeeded)
.set_mode(FlakeLockMode::WriteAsNeeded)
.unwrap();
let locked_flake = LockedFlake::lock(flake_lock_flags, flakeref_a, &eval_state).unwrap();
@ -321,7 +324,7 @@ mod tests {
}
// Step 5: Lock was written, so Step 1 succeeds
flake_lock_flags.set_mode(&FlakeLockMode::Check).unwrap();
flake_lock_flags.set_mode(FlakeLockMode::Check).unwrap();
let locked_flake = LockedFlake::lock(flake_lock_flags, flakeref_a, &eval_state).unwrap();
@ -341,7 +344,7 @@ mod tests {
// This shouldn't matter; write_as_needed will be overridden
flake_lock_flags
.set_mode(&FlakeLockMode::WriteAsNeeded)
.set_mode(FlakeLockMode::WriteAsNeeded)
.unwrap();
let flakeref_c = FlakeRef::parse(&format!("path:{}", &flake_dir_c_str)).unwrap();
@ -367,7 +370,7 @@ mod tests {
let mut flake_lock_flags = FlakeLockFlags::new(&flake_settings).unwrap();
// Step 7: Override was not written; lock still points to b
flake_lock_flags.set_mode(&FlakeLockMode::Check).unwrap();
flake_lock_flags.set_mode(FlakeLockMode::Check).unwrap();
let locked_flake = LockedFlake::lock(flake_lock_flags, flakeref_a, &eval_state).unwrap();

View file

@ -8,10 +8,11 @@ pub extern crate nixide_sys as sys;
pub(crate) mod errors;
mod init;
pub mod logging;
mod nix_settings;
pub mod plugins;
mod stdext;
pub(crate) mod util;
mod verbosity;
mod version;
#[cfg(feature = "exprs")]
@ -23,7 +24,6 @@ mod store;
pub use errors::{NixError, NixideError, NixideResult};
pub use nix_settings::{get_global_setting, set_global_setting};
pub use verbosity::{NixVerbosity, set_verbosity};
pub use version::NixVersion;
#[cfg(feature = "exprs")]

76
nixide/src/logging.rs Normal file
View file

@ -0,0 +1,76 @@
use crate::errors::ErrorContext;
use crate::stdext::AsCPtr as _;
use crate::sys;
use crate::util::wrap;
use crate::util::wrappers::AsInnerPtr as _;
pub use sys::NixVerbosity;
/// Sets the verbosity level.
///
/// **This function should never fail!**
/// A panic would indicate a bug in nixide itself.
///
/// # Nix C++ API Internals
///
/// ```cpp
/// nix_err nix_set_verbosity(nix_c_context * context, nix_verbosity level)
/// {
/// if (context)
/// context->last_err_code = NIX_OK;
/// if (level > NIX_LVL_VOMIT || level < NIX_LVL_ERROR)
/// return nix_set_err_msg(context, NIX_ERR_UNKNOWN, "Invalid verbosity level");
/// try {
/// nix::verbosity = static_cast<nix::Verbosity>(level);
/// } catch (...) {
/// return nix_context_error(context);
/// }
/// return NIX_OK;
/// }
/// ```
///
pub fn set_verbosity(level: NixVerbosity) {
wrap::nix_fn!(|ctx: &ErrorContext| unsafe {
sys::nix_set_verbosity(ctx.as_ptr(), level);
})
.unwrap()
}
/// The log formats `libnix` can output.
///
/// # Nix C++ API Internals
///
/// Defined in `/libmain/include/nix/main/loggers.hh`.
///
pub enum LogFormat {
Raw,
RawWithLogs,
InternalJSON,
Bar,
BarWithLogs,
}
impl ToString for LogFormat {
fn to_string(&self) -> String {
match self {
LogFormat::Raw => "raw".to_owned(),
LogFormat::RawWithLogs => "raw-with-logs".to_owned(),
LogFormat::InternalJSON => "internal-json".to_owned(),
LogFormat::Bar => "bar".to_owned(),
LogFormat::BarWithLogs => "bar-with-logs".to_owned(),
}
}
}
/// Configure the global (default) logger's output format.
///
/// **This function should never fail!**
/// A panic would indicate a nix's `enum LogFormat` has been
/// modified without nixide updating to account for this.
///
pub fn set_log_format(format: LogFormat) {
wrap::nix_fn!(|ctx: &ErrorContext| unsafe {
sys::nix_set_log_format(ctx.as_ptr(), format.to_string().as_c_ptr().unwrap());
})
.unwrap()
}

10
nixide/src/plugins.rs Normal file
View file

@ -0,0 +1,10 @@
use crate::NixideResult;
use crate::errors::ErrorContext;
use crate::util::wrap;
use crate::util::wrappers::AsInnerPtr as _;
pub fn load_plugins() -> NixideResult<()> {
wrap::nix_fn!(|ctx: &ErrorContext| unsafe {
sys::nix_init_plugins(ctx.as_ptr());
})
}

View file

@ -1,36 +0,0 @@
use crate::errors::ErrorContext;
use crate::sys;
use crate::util::wrap;
use crate::util::wrappers::AsInnerPtr as _;
pub use sys::NixVerbosity;
/// Sets the verbosity level.
///
/// **This function should never fail!**
/// A panic would indicate a bug in nixide itself.
///
/// # Nix C++ API Internals
///
/// ```cpp
/// nix_err nix_set_verbosity(nix_c_context * context, nix_verbosity level)
/// {
/// if (context)
/// context->last_err_code = NIX_OK;
/// if (level > NIX_LVL_VOMIT || level < NIX_LVL_ERROR)
/// return nix_set_err_msg(context, NIX_ERR_UNKNOWN, "Invalid verbosity level");
/// try {
/// nix::verbosity = static_cast<nix::Verbosity>(level);
/// } catch (...) {
/// return nix_context_error(context);
/// }
/// return NIX_OK;
/// }
/// ```
///
pub fn set_verbosity(level: NixVerbosity) {
wrap::nix_fn!(|ctx: &ErrorContext| unsafe {
sys::nix_set_verbosity(ctx.as_ptr(), level);
})
.unwrap()
}