rt/lib.rs
1// rt -- the runtime under everything: allocator, memory intrinsics, exact SSE float
2// semantics, the minimal OS calls the runtime itself needs, panic handling.
3//
4// rt::os::{pages_alloc, pages_free, write_stdout, write_stderr, exit, yield_now} (and on
5// Linux the raw syscall helpers plat builds on)
6// rt::kernel the Linux kernel ABI per architecture: SYS_*, O_*, Stat, clone_args (also as rt::os::*)
7// rt::fx cvtt / cvt / sqrt / minss / maxss / floor_i / ceil_i / div255
8// rt::x87 the extended-precision steps of f64 <-> decimal text (ini.s), fpatan
9// rt::sync SpinLock
10// rt::cpu CPU feature bits (CPUID, cached; force_baseline for --simd sse2)
11// rt::crash the crash hook, CrashInfo, report (exit 3) -- also for panics
12// rt::boot what build_rs.py builds into a program (name, manifest, assets) for plat's start
13// rt::main!(body) the entry symbol of console programs (boot first, then body); with the
14// feature std also their fn main
15// rt::panic_hook() (feature std) std's panics take the crash report of rt's panic handler
16// rt::Alloc (feature std, Windows and Linux) rt's allocator for a program with std that wants it
17// rt::VERSION, VERSION_MAJOR / _MINOR / _PATCH the library's version (the root Cargo.toml, SemVer;
18// docs/VERSIONING.md), also as plat::VERSION and ui::VERSION
19// rt::app_version!() the app's own version (version= of apps/NAME/PACKAGE)
20//
21// One of the four crates that may contain `unsafe` (rt, simd, plat, plugin).
22#![no_std]
23
24extern crate alloc;
25#[cfg(feature = "std")]
26extern crate std;
27
28/// the library's version, "MAJOR.MINOR.PATCH" (the root Cargo.toml; docs/VERSIONING.md says what a
29/// change of each part promises). Not the app's version: that is `rt::app_version!()`.
30pub const VERSION: &str = env!("CARGO_PKG_VERSION");
31/// the first number of VERSION (incompatible API changes; while 0, the MINOR number counts them)
32pub const VERSION_MAJOR: u32 = version_part(VERSION, 0);
33/// the second number of VERSION
34pub const VERSION_MINOR: u32 = version_part(VERSION, 1);
35/// the third number of VERSION
36pub const VERSION_PATCH: u32 = version_part(VERSION, 2);
37const _: () = assert!(version_part(VERSION, 3) == u32::MAX, "the version in Cargo.toml must be MAJOR.MINOR.PATCH");
38
39/// the n-th number of "1.2.3" (or "1.2.3-pre+build"); u32::MAX when it has no n-th number
40const fn version_part(v: &str, n: usize) -> u32 {
41 let b = v.as_bytes();
42 let (mut i, mut part, mut value, mut digits) = (0, 0, 0u32, 0);
43 while i <= b.len() {
44 let c = if i < b.len() { b[i] } else { b'.' };
45 if c.is_ascii_digit() && digits < 9 {
46 value = value * 10 + (c - b'0') as u32;
47 digits += 1;
48 } else if c == b'.' || c == b'-' || c == b'+' {
49 if digits == 0 {
50 return u32::MAX;
51 }
52 if part == n {
53 return value;
54 }
55 if c != b'.' {
56 return u32::MAX; // the pre-release / build part has no numbers to count
57 }
58 (part, value, digits) = (part + 1, 0, 0);
59 } else {
60 return u32::MAX;
61 }
62 i += 1;
63 }
64 u32::MAX
65}
66
67/// The app's own version: `version=` of apps/NAME/PACKAGE, which build_rs.py hands to rustc as
68/// JFGUI_VERSION (every program of the app); "0.0.0" without one. An expression of type
69/// `&'static str`: `const APP_VERSION: &str = rt::app_version!();`, `update::Config::new(app,
70/// rt::app_version!(), ..)`.
71#[macro_export]
72macro_rules! app_version {
73 () => {
74 match option_env!("JFGUI_VERSION") {
75 Some(v) => v,
76 None => "0.0.0",
77 }
78 };
79}
80
81/// Define the common app entry symbol without requiring the GUI crate in headless tools. It
82/// first hands what build_rs.py built in (boot.rs: the app's name, its manifest, its assets)
83/// to the platform layer, which installs them as ui::main! does; a refused manifest ends the
84/// program with exit code 2. A Cargo build hands the manifest over as a file
85/// (JFGUI_PERMISSIONS_FILE with --cfg jfgui_manifest), because an environment variable of a build
86/// script holds one line only.
87///
88/// In a program with std (feature `std`) it also defines `fn main`, which hands over to plat's
89/// start of a std program (`jfgui_std_start`: crash handlers, the panic hook, then `jfgui_main`,
90/// the end through `std::process::exit`); the crate then has neither `#![no_std]` nor `#![no_main]`.
91#[cfg(not(feature = "std"))]
92#[macro_export]
93macro_rules! main {
94 ($body:expr) => {
95 $crate::__jfgui_main!($body);
96 };
97}
98
99/// (see above)
100#[cfg(feature = "std")]
101#[macro_export]
102macro_rules! main {
103 ($body:expr) => {
104 $crate::__jfgui_main!($body);
105 $crate::__std_main!();
106 };
107}
108
109/// rt::main!'s `jfgui_main`, without and with std
110#[doc(hidden)]
111#[macro_export]
112macro_rules! __jfgui_main {
113 ($body:expr) => {
114 #[cfg(panic = "unwind")]
115 compile_error!("JFLib needs panic = \"abort\": add [profile.dev] and [profile.release] with panic = \"abort\" to this project's Cargo.toml");
116 #[allow(unsafe_code, unexpected_cfgs)]
117 #[unsafe(no_mangle)]
118 fn jfgui_main() -> i32 {
119 #[cfg(jfgui_manifest)]
120 const JF_MANIFEST: Option<&str> = Some(include_str!(env!("JFGUI_PERMISSIONS_FILE")));
121 #[cfg(not(jfgui_manifest))]
122 const JF_MANIFEST: Option<&str> = option_env!("JFGUI_PERMISSIONS");
123 #[cfg(jfgui_assets)]
124 static ASSETS: &[u8] = include_bytes!(env!("JFGUI_ASSETS"));
125 #[cfg(not(jfgui_assets))]
126 static ASSETS: &[u8] = &[];
127 let boot = $crate::boot::Boot { app: option_env!("JFGUI_APP"), manifest: JF_MANIFEST, assets: ASSETS };
128 if let Err(code) = $crate::boot::start(&boot) {
129 return code;
130 }
131 $body
132 }
133 };
134}
135
136/// the `fn main` of a program with std: plat's start (jfgui_std_start), which calls jfgui_main. In
137/// a library crate (an app whose program only links it, like the template) it is not the entry
138/// point and stays unused: the program's own main calls `plat::std_start()`.
139#[doc(hidden)]
140#[macro_export]
141macro_rules! __std_main {
142 () => {
143 #[allow(unsafe_code, dead_code)]
144 fn main() {
145 unsafe extern "Rust" {
146 fn jfgui_std_start() -> !;
147 }
148 // SAFETY: plat defines it in a program with std; it takes no arguments.
149 unsafe { jfgui_std_start() }
150 }
151 };
152}
153
154pub mod boot;
155#[cfg_attr(any(target_arch = "wasm32", target_os = "android"), path = "portable/cpu.rs")]
156pub mod cpu;
157pub mod crash;
158#[cfg_attr(target_arch = "aarch64", path = "aarch64/fx.rs")] #[cfg_attr(all(not(target_arch = "aarch64"), any(target_arch = "wasm32", target_os = "android")), path = "portable/fx.rs")]
159pub mod fx;
160#[cfg(target_os = "linux")]
161pub mod kernel;
162#[cfg(not(target_os = "android"))]
163#[cfg_attr(target_arch = "wasm32", path = "portable/mem.rs")]
164#[cfg_attr(feature = "std", allow(dead_code))] // std brings the allocator and the memory functions
165mod mem;
166/// rt's allocator, for a program with std that wants it instead of the system's:
167/// `#[global_allocator] static ALLOC: jflib::rt::Alloc = jflib::rt::Alloc::new();`
168#[cfg(all(feature = "std", not(any(target_os = "android", target_arch = "wasm32"))))]
169pub use mem::Alloc;
170#[cfg_attr(target_arch = "wasm32", path = "portable/os.rs")]
171#[cfg_attr(target_os = "android", path = "android.rs")]
172pub mod os;
173pub mod sync;
174#[cfg_attr(any(target_arch = "wasm32", target_os = "android"), path = "portable/math.rs")]
175pub mod x87;
176
177/// Portable arithmetic, available on native targets for exact backend comparisons.
178#[cfg(not(target_arch = "wasm32"))]
179pub mod portable {
180 pub mod fx;
181 pub mod math;
182}
183
184/// panic: one line on stderr, the crash hook (CK_PANIC), exit code 3
185#[cfg(not(feature = "std"))]
186#[panic_handler]
187fn panic(info: &core::panic::PanicInfo) -> ! {
188 panic_report(info)
189}
190
191#[cfg(feature = "std")]
192mod hosted;
193#[cfg(feature = "std")]
194pub use hosted::panic_hook;
195
196pub(crate) fn panic_report(info: &dyn core::fmt::Display) -> ! {
197 use core::fmt::Write;
198 if crash::busy() {
199 os::exit(3); // a panic inside the crash hook
200 }
201 let mut s = crash::StackText::<1024>::new(); // no allocation on the crash path
202 let _ = writeln!(s, "panic: {info}");
203 os::write_stderr(s.bytes());
204 let text = s.as_str().trim_end();
205 let mut ci = crash::CrashInfo::empty();
206 ci.kind = crash::CK_PANIC;
207 ci.msg = text.strip_prefix("panic: ").unwrap_or(text);
208 crash::report(&ci)
209}