From 2100618d47ae71ba0d272c65a0b5ddd5b3d7baf2 Mon Sep 17 00:00:00 2001 From: Jason Volk Date: Wed, 24 Jul 2024 23:01:00 +0000 Subject: [PATCH] add rustc build flags reflection Signed-off-by: Jason Volk --- Cargo.lock | 11 +++++++++ Cargo.toml | 3 +++ src/admin/mod.rs | 7 +++--- src/core/Cargo.toml | 1 + src/core/info/mod.rs | 3 +++ src/core/info/rustc.rs | 53 ++++++++++++++++++++++++++++++++++++++++++ src/core/mod.rs | 7 +++++- src/core/utils/mod.rs | 1 + src/database/mod.rs | 1 + src/macros/mod.rs | 4 ++++ src/macros/rustc.rs | 27 +++++++++++++++++++++ src/main/main.rs | 4 +++- src/router/mod.rs | 1 + src/service/mod.rs | 1 + 14 files changed, 119 insertions(+), 5 deletions(-) create mode 100644 src/core/info/rustc.rs create mode 100644 src/macros/rustc.rs diff --git a/Cargo.lock b/Cargo.lock index d9af38d2..52ea07c9 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 acaed701..b65ba7ad 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 7d752ff8..cd1110ee 100644 --- a/src/admin/mod.rs +++ b/src/admin/mod.rs @@ -21,15 +21,16 @@ extern crate conduit_api as api; 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 620aad02..ea772a83 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 7749bbdc..e11a6021 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 00000000..048c0cd5 --- /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> = Mutex::new(BTreeMap::new()); + +/// Processed list of enabled features across all project crates. This is +/// generated from the data in FLAGS. +static FEATURES: OnceLock> = 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 749d7b08..b302fdcc 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -12,12 +12,17 @@ pub mod utils; 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 = std::result::Result; +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 d7b6a72a..767b65a9 100644 --- a/src/core/utils/mod.rs +++ b/src/core/utils/mod.rs @@ -15,6 +15,7 @@ pub mod time; 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 283224f6..6446624c 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -23,3 +23,4 @@ pub(crate) use util::{or_else, result}; conduit::mod_ctor! {} conduit::mod_dtor! {} +conduit::rustc_flags_capture! {} diff --git a/src/macros/mod.rs b/src/macros/mod.rs index 718583a4..94ea781e 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 00000000..a3bab26f --- /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::>(); + 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 b13e117d..b8cb24ff 100644 --- a/src/main/main.rs +++ b/src/main/main.rs @@ -14,7 +14,7 @@ use std::{ 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_NAME: &str = "conduwuit:worker"; 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 03c70f6d..13fe3908 100644 --- a/src/router/mod.rs +++ b/src/router/mod.rs @@ -14,6 +14,7 @@ use conduit::{Result, Server}; conduit::mod_ctor! {} conduit::mod_dtor! {} +conduit::rustc_flags_capture! {} #[no_mangle] pub extern "Rust" fn start(server: &Arc) -> Pin> + Send>> { diff --git a/src/service/mod.rs b/src/service/mod.rs index ce106809..b6ec58b5 100644 --- a/src/service/mod.rs +++ b/src/service/mod.rs @@ -37,6 +37,7 @@ pub use crate::services::Services; conduit::mod_ctor! {} conduit::mod_dtor! {} +conduit::rustc_flags_capture! {} static SERVICES: RwLock> = RwLock::new(None);