Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/workflows/clippy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,4 @@ jobs:
- uses: Swatinem/rust-cache@v2
- name: Check
# test_no_std is excluded because --all-targets feature-unification pulls std into its no_std graph (duplicate panic_impl)
run: cargo clippy --workspace --all-targets --exclude test_no_std --exclude tool_validate -- -D warnings
run: cargo clippy --workspace --all-targets --exclude test_no_std -- -D warnings
2 changes: 1 addition & 1 deletion .github/workflows/gen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
strategy:
fail-fast: false
matrix:
tool: [bindings, package, reactor, webview, win32, wdk, windows, yml, license, workspace, features, validate]
tool: [bindings, package, reactor, webview, win32, wdk, windows, yml, license, workspace, features]
steps:
- name: Checkout
uses: actions/checkout@v7
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ jobs:
with:
key: ${{ matrix.target }}
- name: Test
run: cargo test --all --target ${{ matrix.target }} --exclude windows_aarch64_gnullvm --exclude windows_aarch64_msvc --exclude windows_i686_gnu --exclude windows_i686_gnullvm --exclude windows_i686_msvc --exclude windows_x86_64_gnu --exclude windows_x86_64_gnullvm --exclude windows_x86_64_msvc --exclude tool_validate
run: cargo test --all --target ${{ matrix.target }} --exclude windows_aarch64_gnullvm --exclude windows_aarch64_msvc --exclude windows_i686_gnu --exclude windows_i686_gnullvm --exclude windows_i686_msvc --exclude windows_x86_64_gnu --exclude windows_x86_64_gnullvm --exclude windows_x86_64_msvc
- name: Check diff
shell: bash
run: |
Expand Down
3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@
resolver = "3"
members = [
"crates/libs/*",
"crates/tools/*",
"crates/samples/*/*",
"crates/targets/*",
"crates/tests/*/*",
"crates/tools/*",
]
exclude = [
"crates/targets/baseline",
Expand Down
Binary file modified crates/libs/bindgen/default/Windows.Wdk.winmd
Binary file not shown.
Binary file modified crates/libs/bindgen/default/Windows.Win32.winmd
Binary file not shown.
2 changes: 1 addition & 1 deletion crates/libs/bindgen/src/filter_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ mod resolution_tests {
fn test_reader() -> &'static Reader {
use std::sync::OnceLock;
static READER: OnceLock<Reader> = OnceLock::new();
READER.get_or_init(|| Reader::new(expand_input(&["default"])))
READER.get_or_init(|| Reader::new(expand_input(&["default"]), false))
}

#[test]
Expand Down
30 changes: 25 additions & 5 deletions crates/libs/bindgen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,10 +184,11 @@ impl Style {
!self.is_sys()
}

/// Whether handle structs and unscoped (non-`ScopedEnumAttribute`) enums are
/// emitted as a bare `pub type X = <underlying>` alias rather than a newtype
/// wrapper. Both sys and minimal bindings collapse them; this is why their
/// constants also drop the `Self(value)` constructor (see `cpp_const`).
/// Whether **handle** structs are emitted as a bare `pub type X = <underlying>` alias rather
/// than a newtype wrapper. Both sys and minimal bindings collapse them; this is why their
/// handle constants also drop the `Self(value)` constructor (see `cpp_const`). (Unscoped enums
/// are collapsed to bare aliases in *every* style — that decision lives in `cpp_enum`, not
/// here.)
fn emit_bare_typedef(self) -> bool {
self.is_sys() || self.is_minimal()
}
Expand Down Expand Up @@ -402,7 +403,7 @@ impl Bindgen {
self.input.iter().map(|s| s.as_str()).collect()
};

let reader = Reader::new(expand_input(&input));
let reader = Reader::new(expand_input(&input), sys);

let mut references: Vec<ReferenceStage> = Vec::new();

Expand Down Expand Up @@ -673,6 +674,25 @@ fn namespace_starts_with(namespace: &str, starts_with: &str) -> bool {
|| namespace.as_bytes().get(starts_with.len()) == Some(&b'.'))
}

/// Derives the cargo-feature name for a `--package` namespace.
///
/// Win32/WDK namespaces are flat (`Windows.Win32.<header>`) with globally unique
/// header stems, so the feature is just the stem — the `Win32_`/`Wdk_` prefix
/// would be redundant. The `Win32`/`Wdk` umbrella modules and the hierarchical
/// WinRT namespaces keep their full path (after `Windows.`) joined with `_`.
fn namespace_feature(namespace: &str) -> String {
if let Some(stem) = namespace
.strip_prefix("Windows.Win32.")
.or_else(|| namespace.strip_prefix("Windows.Wdk."))
{
stem.replace('.', "_")
} else if let Some((_, rest)) = namespace.split_once('.') {
rest.replace('.', "_")
} else {
namespace.to_string()
}
}

/// Prepend reference entries so they take precedence.
fn prepend_default_refs(refs: &mut Vec<ReferenceStage>, crate_name: &str, paths: &[&str]) {
refs.splice(
Expand Down
2 changes: 1 addition & 1 deletion crates/libs/bindgen/src/libraries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub enum CallingConvention {
pub fn libraries() -> BTreeMap<String, BTreeMap<String, CallingConvention>> {
let mut libraries = BTreeMap::new();

let reader = Reader::new(expand_input(&["default"]));
let reader = Reader::new(expand_input(&["default"]), false);
combine_libraries(&reader, &mut libraries);
libraries
}
Expand Down
64 changes: 45 additions & 19 deletions crates/libs/bindgen/src/package_writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,20 +68,11 @@ impl Cfg {
if dependency.is_empty()
|| namespace_starts_with(config.namespace, dependency)
|| dependency == "Windows.Foundation"
|| dependency == "Windows.Win32.Foundation"
{
continue;
}

let mut feature = String::new();

for name in dependency.split('.').skip(1) {
feature.push_str(name);
feature.push('_');
}

feature.truncate(feature.len() - 1);
features.insert(feature);
features.insert(namespace_feature(dependency));
}

let mut tokens = quote! {};
Expand Down Expand Up @@ -155,21 +146,56 @@ impl Config<'_> {
}
}

let feature_namespaces: BTreeSet<&str> =
trees.iter().skip(1).map(|tree| tree.namespace).collect();

for tree in trees.iter().skip(1) {
let feature = tree.feature();

if let Some(pos) = feature.rfind('_') {
let dependency = &feature[..pos];
// Derive the dependency from the namespace's dot structure. A nested WinRT
// namespace depends on its parent; a crate-root module is either a flat Win32/WDK
// header stem (lowercase leaf, e.g. `pathcch`, `wdm`) or a WinRT root (PascalCase
// leaf, e.g. `Storage`).
let (parent, leaf) = tree.namespace.rsplit_once('.').unwrap();

if parent != "Windows" {
// A nested WinRT namespace (e.g. `Foundation.Collections`) depends on its
// parent root feature.
let dependency = namespace_feature(parent);

toml.push_str(&format!("{feature} = [\"{dependency}\"]\n"));
} else if namespace_starts_with(tree.namespace, "Windows.Win32")
|| namespace_starts_with(tree.namespace, "Windows.Wdk")
{
toml.push_str(&format!("{feature} = [\"Win32_Foundation\"]\n"));
} else if tree.namespace != "Windows.Foundation" {
toml.push_str(&format!("{feature} = [\"Foundation\"]\n"));
} else {
} else if leaf.starts_with(|c: char| c.is_ascii_lowercase()) {
// A flat Win32/WDK header-stem module. There is no umbrella feature, so the
// stem's cargo feature must pull in exactly the other stems whose types its
// APIs reference (the same namespaces that gate its items per `Cfg`), so that
// enabling one header's feature makes its whole surface usable. This mirrors
// the classic per-feature dependency lists (cargo tolerates the resulting
// cycles between mutually-referencing headers). References that resolve to
// always-on core types (no emitted feature) are filtered out.
let config = self.with_namespace(tree.namespace);
let mut dependencies = BTreeSet::new();

for ty in &tree.types {
let cfg = Cfg::new(&ty.dependencies(config.reader), &config);
dependencies.extend(cfg.features);
}

dependencies.remove(tree.namespace);

let list = dependencies
.iter()
.filter(|namespace| feature_namespaces.contains(*namespace))
.map(|namespace| format!("\"{}\"", namespace_feature(namespace)))
.collect::<Vec<_>>()
.join(", ");

toml.push_str(&format!("{feature} = [{list}]\n"));
} else if tree.namespace == "Windows.Foundation" {
// The WinRT `Foundation` base is always available with no dependency.
toml.push_str(&format!("{feature} = []\n"));
} else {
// Other WinRT roots depend on the always-on `Foundation` base.
toml.push_str(&format!("{feature} = [\"Foundation\"]\n"));
}
}

Expand Down
48 changes: 7 additions & 41 deletions crates/libs/bindgen/src/tables/type_def.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ pub trait TypeDefExt {
fn type_name(&self) -> TypeName;
fn generics(&self) -> Vec<Type>;
fn underlying_type_ext(&self, reader: &Reader) -> Type;
fn invalid_values(&self) -> Vec<i64>;
fn free_function(&self, reader: &Reader) -> Option<CppFn>;
fn is_agile(&self) -> bool;
fn is_async(&self) -> bool;
}
Expand All @@ -28,47 +26,15 @@ impl TypeDefExt for TypeDef {
}
}

fn invalid_values(&self) -> Vec<i64> {
let mut values = Vec::new();
for attribute in self.attributes() {
if attribute.name() == "InvalidHandleValueAttribute" {
if let Some((_, Value::I64(value))) = attribute.value().first() {
values.push(*value);
}
}
}
values
}

fn free_function(&self, reader: &Reader) -> Option<CppFn> {
if let Some(attribute) = self.find_attribute("RAIIFreeAttribute") {
if let Some((_, Value::Utf8(name))) = attribute.value().first() {
if let Some(Type::CppFn(ty)) = reader.with_full_name(self.namespace(), name).next()
{
let signature = ty.method.method_signature(ty.namespace, &[], reader);

if signature.params.len() == 1 {
return Some(ty);
}
}
}
}
None
}

fn is_agile(&self) -> bool {
for attribute in self.attributes() {
match attribute.name() {
"AgileAttribute" => return true,
"MarshalingBehaviorAttribute" => {
if matches!(
attribute.value().first(),
Some((_, Value::EnumValue(_, inner))) if matches!(**inner, Value::I32(2))
) {
return true;
}
}
_ => {}
if attribute.name() == "MarshalingBehaviorAttribute"
&& matches!(
attribute.value().first(),
Some((_, Value::EnumValue(_, inner))) if matches!(**inner, Value::I32(2))
)
{
return true;
}
}
self.is_async()
Expand Down
2 changes: 1 addition & 1 deletion crates/libs/bindgen/src/type_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ impl TypeTree {
}

pub fn feature(&self) -> String {
self.namespace.split_once('.').unwrap().1.replace('.', "_")
namespace_feature(self.namespace)
}

fn with_namespace(namespace: &'static str) -> Self {
Expand Down
Loading
Loading