diff --git a/crates/libs/bindgen/src/lib.rs b/crates/libs/bindgen/src/lib.rs index 5a8b5ef1f75..9a59ca57851 100644 --- a/crates/libs/bindgen/src/lib.rs +++ b/crates/libs/bindgen/src/lib.rs @@ -2,7 +2,8 @@ mod args; mod error; mod metadata; mod rdl; -mod rust; +#[doc(hidden)] +pub mod rust; mod tokens; mod tree; mod winmd; @@ -160,7 +161,8 @@ fn filter_input(input: &[&str], extensions: &[&str]) -> Result> { Ok(results) } -fn read_input(input: &[&str]) -> Result> { +#[doc(hidden)] +pub fn read_input(input: &[&str]) -> Result> { let input = filter_input(input, &["winmd", "rdl"])?; let mut results = vec![]; diff --git a/crates/libs/bindgen/src/rust/mod.rs b/crates/libs/bindgen/src/rust/mod.rs index 342a6879a98..cc7f4747d60 100644 --- a/crates/libs/bindgen/src/rust/mod.rs +++ b/crates/libs/bindgen/src/rust/mod.rs @@ -1,4 +1,5 @@ -mod cfg; +#[doc(hidden)] +pub mod cfg; mod classes; mod com_methods; mod constants; @@ -18,8 +19,9 @@ mod winrt_methods; mod writer; use super::*; use crate::{Error, Result, Tree}; -use cfg::*; +pub use cfg::*; use rayon::prelude::*; +pub use writer::*; pub fn from_reader(reader: &'static metadata::Reader, mut config: std::collections::BTreeMap<&str, &str>, output: &str) -> Result<()> { let mut writer = Writer::new(reader, output); @@ -135,7 +137,6 @@ use method_names::*; use std::collections::*; use std::fmt::Write; use try_format::*; -use writer::*; fn namespace(writer: &Writer, tree: &Tree) -> String { let writer = &mut writer.clone(); diff --git a/crates/libs/bindgen/src/rust/writer.rs b/crates/libs/bindgen/src/rust/writer.rs index 532e433c503..28f34ed06a4 100644 --- a/crates/libs/bindgen/src/rust/writer.rs +++ b/crates/libs/bindgen/src/rust/writer.rs @@ -1155,6 +1155,22 @@ fn const_ptrs(pointers: usize) -> TokenStream { "*const ".repeat(pointers).into() } +pub fn cfg_features(cfg: &Cfg) -> Vec { + let mut compact = Vec::<&'static str>::new(); + for feature in cfg.types.keys() { + if !feature.is_empty() { + for pos in 0..compact.len() { + if starts_with(feature, unsafe { compact.get_unchecked(pos) }) { + compact.remove(pos); + break; + } + } + compact.push(feature); + } + } + compact.into_iter().map(to_feature).collect() +} + fn to_feature(name: &str) -> String { let mut feature = String::new(); diff --git a/crates/tools/features/Cargo.toml b/crates/tools/features/Cargo.toml new file mode 100644 index 00000000000..d537ff777a1 --- /dev/null +++ b/crates/tools/features/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "tool_features" +version = "0.1.0" +authors = ["Microsoft"] +edition = "2021" +rust-version = "1.56" +license = "MIT OR Apache-2.0" +description = "Windows cargo feature search tool" +repository = "https://github.com/microsoft/windows-rs" +readme = "readme.md" + +[dependencies.indexmap] +version = "2.1.0" +features = ["serde"] + +[dependencies.serde] +version = "*" +features = ["derive"] + +[dependencies.serde_json] +version = "1.0" + +[dependencies.windows] +path = "../../libs/windows" +version = "0.52.0" + +[dependencies.windows-bindgen] +path = "../../libs/bindgen" +version = "0.52.0" + +[dependencies.windows-metadata] +path = "../../libs/metadata" +version = "0.52.0" diff --git a/crates/tools/features/src/main.rs b/crates/tools/features/src/main.rs new file mode 100644 index 00000000000..2743f319652 --- /dev/null +++ b/crates/tools/features/src/main.rs @@ -0,0 +1,136 @@ +use indexmap::IndexMap; +use serde::ser::SerializeSeq; +use serde::{Serialize, Serializer}; +use std::{fs::OpenOptions, io::Write}; +use windows_metadata::Item; + +fn main() { + match feature_index() { + Ok(ok) => { + OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open("index.json") + .unwrap() + .write_all(ok.as_bytes()) + .expect("Failed to write out index."); + } + Err(error) => { + eprintln!("{}", error); + std::process::exit(1); + } + } +} + +#[derive(Default, Serialize)] +struct FeatureIndex { + #[serde(serialize_with = "serialize_keys")] + namespace_map: IndexMap, + #[serde(serialize_with = "serialize_keys")] + feature_map: IndexMap, + namespaces: IndexMap>, +} + +#[derive(Default, Serialize)] +struct FeatureIndexItem { + name: String, + features: Vec, +} + +fn serialize_keys(map: &IndexMap, serializer: S) -> Result +where + S: Serializer, +{ + let mut seq = serializer.serialize_seq(Some(map.len()))?; + for (k, _v) in map { + seq.serialize_element(k)?; + } + seq.end() +} + +pub fn feature_index() -> windows::core::Result { + let files = windows_bindgen::read_input(&[]).unwrap(); + let reader = windows_metadata::Reader::filter( + files, + &["Windows"], + &[ + "Windows.AI.MachineLearning.Preview", + "Windows.ApplicationModel.SocialInfo", + "Windows.Devices.AllJoyn", + "Windows.Devices.Perception", + "Windows.Security.Authentication.Identity.Provider", + "Windows.Services.Cortana", + "Windows.System.Power.Diagnostics", + "Windows.System.Preview", + "Windows.UI.Xaml", + "Windows.Win32.Foundation.Metadata", + "Windows.Win32.System.Diagnostics.Debug.WebApp", + "Windows.Win32.System.WinRT.Xaml", + "Windows.Win32.UI.Xaml", + "Windows.Win32.Web.MsHtml", + ], + ); + + let mut feature_index = FeatureIndex { + ..Default::default() + }; + let mut next_ns_idx = 0; + let mut next_feature_idx = 0; + + for namespace in reader.namespaces() { + for item in reader.namespace_items(namespace) { + let namespace_idx = feature_index + .namespace_map + .entry(namespace.to_string()) + .or_insert_with(|| { + next_ns_idx += 1; + next_ns_idx + }); + + let mut index_item = FeatureIndexItem { + ..Default::default() + }; + let mut cfg = windows_bindgen::rust::Cfg::default(); + cfg.add_feature(namespace); + + cfg = match item { + Item::Type(def) => { + index_item.name = def.name().to_string(); + cfg.union(&windows_bindgen::rust::type_def_cfg(def, &[])) + } + Item::Const(field) => { + index_item.name = field.name().to_string(); + cfg.union(&windows_bindgen::rust::field_cfg(field)) + } + Item::Fn(method, _) => { + index_item.name = method.name().to_string(); + cfg.union(&windows_bindgen::rust::signature_cfg(method)) + } + }; + + let cfg_features = windows_bindgen::rust::cfg_features(&cfg); + index_item.features = cfg_features + .iter() + .map(|feature| { + let feature_idx = feature_index + .feature_map + .entry(feature.clone()) + .or_insert_with(|| { + next_feature_idx += 1; + next_feature_idx + }); + *feature_idx + }) + .collect(); + + feature_index + .namespaces + .entry(*namespace_idx) + .or_default() + .push(index_item); + } + } + + Ok(serde_json::to_string(&feature_index).unwrap()) +}