diff --git a/Cargo.lock b/Cargo.lock index d9af38d24d973ffbc797c1d5ca13014dff182113..52ea07c9e672b0b2db859b4c6f47c7a8442b5c67 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -681,6 +681,7 @@ dependencies = [ "chrono", "conduit_macros", "const-str", + "ctor", "either", "figment", "hardened_malloc-rs", @@ -1018,6 +1019,16 @@ dependencies = [ "typenum", ] +[[package]] +name = "ctor" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f" +dependencies = [ + "quote", + "syn 2.0.71", +] + [[package]] name = "curve25519-dalek" version = "4.1.3" diff --git a/Cargo.toml b/Cargo.toml index acaed701714bd408061790c502c9a6a2ab81218c..b65ba7ad357e63b15d3c4071ec7c713e5224ca93 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,9 @@ name = "conduit" [workspace.dependencies.const-str] version = "0.5.7" +[workspace.dependencies.ctor] +version = "0.2.8" + [workspace.dependencies.cargo_toml] version = "0.20" features = ["features"] diff --git a/src/admin/mod.rs b/src/admin/mod.rs index 7d752ff869207c68ca2a63e303739105911d95e1..cd1110ee3d0b87418ea143fa0a24cb9729a039d3 100644 --- a/src/admin/mod.rs +++ b/src/admin/mod.rs @@ -21,15 +21,16 @@ extern crate conduit_core as conduit; extern crate conduit_service as service; -pub(crate) use conduit::{mod_ctor, mod_dtor, Result}; +pub(crate) use conduit::Result; pub(crate) use service::services; pub(crate) use crate::utils::{escape_html, get_room_info}; pub(crate) const PAGE_SIZE: usize = 100; -mod_ctor! {} -mod_dtor! {} +conduit::mod_ctor! {} +conduit::mod_dtor! {} +conduit::rustc_flags_capture! {} /// Install the admin command handler pub async fn init() { diff --git a/src/core/Cargo.toml b/src/core/Cargo.toml index 620aad02d8727fba3f315edf8c39415da0d2d057..ea772a839f35823c388757a6c20ad55d210e432a 100644 --- a/src/core/Cargo.toml +++ b/src/core/Cargo.toml @@ -58,6 +58,7 @@ checked_ops.workspace = true chrono.workspace = true conduit-macros.workspace = true const-str.workspace = true +ctor.workspace = true either.workspace = true figment.workspace = true http-body-util.workspace = true diff --git a/src/core/info/mod.rs b/src/core/info/mod.rs index 7749bbdc8410f15ba27981707ebb92f8e5b97a6a..e11a602194ee737c64d1988ee99bb82b6439ab84 100644 --- a/src/core/info/mod.rs +++ b/src/core/info/mod.rs @@ -2,4 +2,7 @@ //! etc information which can be queried by admins or used by developers. pub mod cargo; +pub mod rustc; pub mod version; + +pub use conduit_macros::rustc_flags_capture; diff --git a/src/core/info/rustc.rs b/src/core/info/rustc.rs new file mode 100644 index 0000000000000000000000000000000000000000..048c0cd591129cf79ad8c719f8805e0dcd745528 --- /dev/null +++ b/src/core/info/rustc.rs @@ -0,0 +1,53 @@ +//! Information about the build related to rustc. This is a frontend interface +//! informed by proc-macros at build time. Since the project is split into +//! several crates, lower-level information is supplied from each crate during +//! static initialization. + +use std::{ + collections::BTreeMap, + sync::{Mutex, OnceLock}, +}; + +use crate::utils::exchange; + +/// Raw capture of rustc flags used to build each crate in the project. Informed +/// by rustc_flags_capture macro (one in each crate's mod.rs). This is +/// done during static initialization which is why it's mutex-protected and pub. +/// Should not be written to by anything other than our macro. +pub static FLAGS: Mutex<BTreeMap<&str, &[&str]>> = Mutex::new(BTreeMap::new()); + +/// Processed list of enabled features across all project crates. This is +/// generated from the data in FLAGS. +static FEATURES: OnceLock<Vec<&'static str>> = OnceLock::new(); + +/// List of features enabled for the project. +pub fn features() -> &'static Vec<&'static str> { FEATURES.get_or_init(init_features) } + +fn init_features() -> Vec<&'static str> { + let mut features = Vec::new(); + FLAGS + .lock() + .expect("locked") + .iter() + .for_each(|(_, flags)| append_features(&mut features, flags)); + + features.sort_unstable(); + features.dedup(); + features +} + +fn append_features(features: &mut Vec<&'static str>, flags: &[&'static str]) { + let mut next_is_cfg = false; + for flag in flags { + let is_cfg = *flag == "--cfg"; + let is_feature = flag.starts_with("feature="); + if exchange(&mut next_is_cfg, is_cfg) && is_feature { + if let Some(feature) = flag + .split_once('=') + .map(|(_, feature)| feature.trim_matches('"')) + { + features.push(feature); + } + } + } +} diff --git a/src/core/mod.rs b/src/core/mod.rs index 749d7b080c2e72daff4030afbcff9d90b6768cd6..b302fdcc6c9b144014c304e3b7daf6caa7a06b58 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -12,12 +12,17 @@ pub use config::Config; pub use error::Error; -pub use info::{version, version::version}; +pub use info::{rustc_flags_capture, version, version::version}; pub use pdu::{PduBuilder, PduCount, PduEvent}; pub use server::Server; +pub use utils::{ctor, dtor}; + +pub use crate as conduit_core; pub type Result<T, E = Error> = std::result::Result<T, E>; +rustc_flags_capture! {} + #[cfg(not(conduit_mods))] pub mod mods { #[macro_export] diff --git a/src/core/utils/mod.rs b/src/core/utils/mod.rs index d7b6a72a15336f8c95d26bfc8f3e9ff18916bf27..767b65a93a45fe025164ecc337405879f80dc914 100644 --- a/src/core/utils/mod.rs +++ b/src/core/utils/mod.rs @@ -15,6 +15,7 @@ use std::cmp::{self, Ordering}; +pub use ::ctor::{ctor, dtor}; pub use bytes::{increment, u64_from_bytes, u64_from_u8, u64_from_u8x8}; pub use debug::slice_truncated as debug_slice_truncated; pub use hash::calculate_hash; diff --git a/src/database/mod.rs b/src/database/mod.rs index 283224f6610bb207cddd1b96c84ec8309eed76e2..6446624caa2d79d8a7843fefe5f53193b614a2d6 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -23,3 +23,4 @@ conduit::mod_ctor! {} conduit::mod_dtor! {} +conduit::rustc_flags_capture! {} diff --git a/src/macros/mod.rs b/src/macros/mod.rs index 718583a4d47ab507ab265cc8f53453c21290f1d0..94ea781e317a14524642489478232f6c7661c3f8 100644 --- a/src/macros/mod.rs +++ b/src/macros/mod.rs @@ -1,5 +1,6 @@ mod admin; mod cargo; +mod rustc; mod utils; use proc_macro::TokenStream; @@ -11,3 +12,6 @@ pub fn admin_command_dispatch(args: TokenStream, input: TokenStream) -> TokenStr #[proc_macro_attribute] pub fn cargo_manifest(args: TokenStream, input: TokenStream) -> TokenStream { cargo::manifest(args, input) } + +#[proc_macro] +pub fn rustc_flags_capture(args: TokenStream) -> TokenStream { rustc::flags_capture(args) } diff --git a/src/macros/rustc.rs b/src/macros/rustc.rs new file mode 100644 index 0000000000000000000000000000000000000000..a3bab26f32369fdf0f4e63a9632f1a670c0fd5e4 --- /dev/null +++ b/src/macros/rustc.rs @@ -0,0 +1,27 @@ +use proc_macro::TokenStream; +use quote::quote; + +pub(super) fn flags_capture(args: TokenStream) -> TokenStream { + let cargo_crate_name = std::env::var("CARGO_CRATE_NAME"); + let crate_name = match cargo_crate_name.as_ref() { + Err(_) => return args, + Ok(crate_name) => crate_name.trim_start_matches("conduit_"), + }; + + let flag = std::env::args().collect::<Vec<_>>(); + let ret = quote! { + #[conduit_core::ctor] + fn _set_rustc_flags() { + let flags = &[#( #flag ),*]; + conduit_core::info::rustc::FLAGS.lock().expect("locked").insert(#crate_name, flags); + } + + // static strings have to be yanked on module unload + #[conduit_core::dtor] + fn _unset_rustc_flags() { + conduit_core::info::rustc::FLAGS.lock().expect("locked").remove(#crate_name); + } + }; + + ret.into() +} diff --git a/src/main/main.rs b/src/main/main.rs index b13e117db97b2b4c97d8ff3e03064bc20e11a2f2..b8cb24ff1650a8d5ddbdc8b6cf2a404a29cfcf16 100644 --- a/src/main/main.rs +++ b/src/main/main.rs @@ -14,7 +14,7 @@ time::Duration, }; -use conduit::{debug_info, error, utils::available_parallelism, Error, Result}; +use conduit::{debug_info, error, rustc_flags_capture, utils::available_parallelism, Error, Result}; use server::Server; use tokio::runtime; @@ -22,6 +22,8 @@ const WORKER_MIN: usize = 2; const WORKER_KEEPALIVE: u64 = 36; +rustc_flags_capture! {} + fn main() -> Result<(), Error> { let args = clap::parse(); let runtime = runtime::Builder::new_multi_thread() diff --git a/src/router/mod.rs b/src/router/mod.rs index 03c70f6d72e08b11c5ef5838dddbe7f397816b83..13fe390877f5980407bfad38d4251bddaf8b9bac 100644 --- a/src/router/mod.rs +++ b/src/router/mod.rs @@ -14,6 +14,7 @@ conduit::mod_ctor! {} conduit::mod_dtor! {} +conduit::rustc_flags_capture! {} #[no_mangle] pub extern "Rust" fn start(server: &Arc<Server>) -> Pin<Box<dyn Future<Output = Result<()>> + Send>> { diff --git a/src/service/mod.rs b/src/service/mod.rs index ce106809cf118ef2f00726a6402c8b7abdc99489..b6ec58b5cbae5c833b231553b328693625f87456 100644 --- a/src/service/mod.rs +++ b/src/service/mod.rs @@ -37,6 +37,7 @@ conduit::mod_ctor! {} conduit::mod_dtor! {} +conduit::rustc_flags_capture! {} static SERVICES: RwLock<Option<&Services>> = RwLock::new(None);