libperl_sys/lib.rs
1//! # libperl-sys
2//!
3#![doc = concat!(
4 "**Built against Perl ", env!("LIBPERL_SYS_PERL_VERSION"),
5 " (", env!("LIBPERL_SYS_PERL_THREADED"),
6 ", `", env!("LIBPERL_SYS_PERL_ARCHNAME"), "`).**",
7)]
8//!
9//! The function signatures, `PL_*` globals, and `Sv*` / `Av*` / `Hv*`
10//! helpers documented below reflect this specific Perl. Different
11//! Perl versions may have minor signature differences (added /
12//! removed functions, changed integer widths, threading-mode
13//! variations). Use the [`PERL_VERSION`] / [`PERL_THREADED`] /
14//! [`PERL_ARCHNAME`] constants for runtime identification.
15//!
16//! Low-level, raw FFI declarations for the Perl 5 C API (`libperl`).
17//! Generated at build time by `bindgen` (regular C declarations) plus
18//! [`libperl-macrogen`](https://docs.rs/libperl-macrogen) (the C
19//! macros and `static inline` functions that `bindgen` skips).
20//!
21//! This crate is the unsafe foundation under
22//! [`libperl-rs`](https://docs.rs/libperl-rs); most users want that
23//! safer wrapper. Reach for `libperl-sys` directly when you need an
24//! API element that hasn't been wrapped yet, or when you're writing
25//! a sibling crate at the same layer.
26//!
27//! ## What you get
28//!
29//! Re-exported at the crate root:
30//!
31//! - `Perl_*` extern functions and `PL_*` mutable statics (from
32//! bindgen),
33//! - `Sv*` / `Av*` / `Hv*` / `PL_xxx!()` macro helpers and inline
34//! wrappers (from libperl-macrogen) — these unify the threaded vs
35//! non-threaded calling conventions so the same source builds
36//! against both `MULTIPLICITY` modes,
37//! - opcode → name lookup table ([`conv_opcode`]) and per-function
38//! signature dictionary ([`sigdb`]) for downstream codegen.
39//!
40//! ## Safety
41//!
42//! Every public item here is `unsafe` to use. Even reading a `PL_*`
43//! global requires the right interpreter context, and Perl's API
44//! uses raw `*mut` pointers ubiquitously.
45//!
46//! ## Build requirements
47//!
48//! - A working Perl 5 install with development headers
49//! (`Perl.h`, `EXTERN.h`, ...). Typical packages: `perl-dev`,
50//! `perl-devel`.
51//! - LLVM / libclang (for `bindgen`).
52//! - Internet access at first build (libperl-macrogen downloads a
53//! pre-extracted apidoc snapshot from GitHub Releases).
54//!
55//! Threaded vs non-threaded Perl is auto-detected — no feature flag
56//! to set.
57
58pub mod perl_core;
59pub use perl_core::*;
60
61pub mod conv_opcode;
62
63pub mod sigdb;
64
65/// Perl version this binding was generated against (e.g. `"5.38.4"`).
66pub const PERL_VERSION: &str = env!("LIBPERL_SYS_PERL_VERSION");
67
68/// `"threaded"` if the target Perl was built with `useithreads`,
69/// `"non-threaded"` otherwise. Threading mode determines whether
70/// most Perl C API functions take a leading `my_perl: *mut PerlInterpreter`
71/// parameter.
72pub const PERL_THREADED: &str = env!("LIBPERL_SYS_PERL_THREADED");
73
74/// Perl `archname` (e.g. `"x86_64-linux-thread-multi"` or
75/// `"x86_64-linux-gnu"`). Mostly informational; the more useful
76/// invariants are in [`PERL_VERSION`] and [`PERL_THREADED`].
77pub const PERL_ARCHNAME: &str = env!("LIBPERL_SYS_PERL_ARCHNAME");
78
79use std::ffi::CStr;
80
81// use std::os::raw::{c_char, c_int /*, c_void, c_schar*/};
82
83fn core_op_name(o: &op) -> Option<String> {
84 let ty = o.op_type();
85 if (ty as usize) < unsafe {PL_op_name.len()} {
86 let op_name = unsafe {CStr::from_ptr(PL_op_name[ty as usize])};
87 Some(String::from(op_name.to_str().unwrap()))
88 } else {
89 None
90 }
91}
92
93impl std::fmt::Display for op {
94 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95 write!(f, "{{ {:?}={:#?} {:?} }}"
96 , core_op_name(&self)
97 , (self as *const op)
98 , self)
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 #[test]
105 fn it_works() {
106 let perl = unsafe { super::perl_alloc() };
107 unsafe {
108 super::perl_construct(perl);
109 };
110 }
111
112 // Note: a smoke test for the PERLVAR-driven `PL_xxx!($my_perl)` macros
113 // would naturally live here, but `#[macro_export]` macros emitted via
114 // `include!()` are unreachable by absolute path within the *defining*
115 // crate (rejected by the
116 // `macro_expanded_macro_exports_accessed_by_absolute_paths` lint, which
117 // is on by default and slated to become a hard error). The smoke test
118 // is in `libperl-rs/tests/perlvar_macros.rs` instead, where cross-crate
119 // access goes through the normal path resolver and is unaffected.
120
121 #[test]
122 fn sigdb_lookup() {
123 use super::sigdb::{FN_BY_NAME, FUNCS};
124
125 // Test that FN_BY_NAME lookup works
126 if let Some(id) = FN_BY_NAME.get("Perl_sv_isbool") {
127 let sig = &FUNCS[id.0 as usize];
128 assert_eq!(sig.name, "Perl_sv_isbool");
129 assert!(!sig.ret.is_empty());
130 }
131
132 // Test perl_alloc
133 let id = FN_BY_NAME.get("perl_alloc").expect("perl_alloc should exist");
134 let sig = &FUNCS[id.0 as usize];
135 assert_eq!(sig.name, "perl_alloc");
136 assert!(sig.ret.contains("PerlInterpreter"));
137 }
138}