Skip to content

Commit 6672c6d

Browse files
authored
Support standalone code generation (#2396)
1 parent f3ef2cd commit 6672c6d

File tree

417 files changed

+34421
-30873
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

417 files changed

+34421
-30873
lines changed

.github/workflows/clippy.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ jobs:
110110
cargo clippy -p test_resources &&
111111
cargo clippy -p test_return_struct &&
112112
cargo clippy -p test_simple_component &&
113+
cargo clippy -p test_standalone &&
113114
cargo clippy -p test_string_param &&
114115
cargo clippy -p test_structs &&
115116
cargo clippy -p test_sys &&

.github/workflows/test.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ jobs:
117117
cargo test --target ${{ matrix.target }} -p test_resources &&
118118
cargo test --target ${{ matrix.target }} -p test_return_struct &&
119119
cargo test --target ${{ matrix.target }} -p test_simple_component &&
120+
cargo test --target ${{ matrix.target }} -p test_standalone &&
120121
cargo test --target ${{ matrix.target }} -p test_string_param &&
121122
cargo test --target ${{ matrix.target }} -p test_structs &&
122123
cargo test --target ${{ matrix.target }} -p test_sys &&

crates/libs/bindgen/src/constants.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@ pub fn gen(gen: &Gen, def: Field) -> TokenStream {
1818
quote! {
1919
#doc
2020
#features
21-
pub const #name: ::#crate_name::core::PCSTR = ::#crate_name::s!(#value);
21+
pub const #name: #crate_name PCSTR = #crate_name s!(#value);
2222
}
2323
} else {
2424
let value = gen.value(&gen.reader.constant_value(constant));
2525
quote! {
2626
#doc
2727
#features
28-
pub const #name: ::#crate_name::core::PCWSTR = ::#crate_name::w!(#value);
28+
pub const #name: #crate_name PCWSTR = #crate_name w!(#value);
2929
}
3030
}
3131
} else {

crates/libs/bindgen/src/functions.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,18 @@ fn gen_sys_function(gen: &Gen, def: MethodDef) -> TokenStream {
2929
quote! { #name: #tokens }
3030
});
3131

32-
quote! {
33-
#features
34-
::windows_sys::core::link!(#link #abi #doc fn #name(#(#params),*) #return_type);
32+
if gen.standalone {
33+
quote! {
34+
#[link(name = "windows")]
35+
extern #abi {
36+
pub fn #name(#(#params),*) #return_type;
37+
}
38+
}
39+
} else {
40+
quote! {
41+
#features
42+
::windows_sys::core::link!(#link #abi #doc fn #name(#(#params),*) #return_type);
43+
}
3544
}
3645
}
3746

crates/libs/bindgen/src/gen.rs

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ pub struct Gen<'a> {
77
pub cfg: bool,
88
pub doc: bool,
99
pub component: bool,
10+
pub standalone: bool,
1011
}
1112

1213
impl<'a> Gen<'a> {
@@ -18,6 +19,7 @@ impl<'a> Gen<'a> {
1819
cfg: false,
1920
doc: false,
2021
component: false,
22+
standalone: false,
2123
}
2224
}
2325

@@ -96,43 +98,43 @@ impl<'a> Gen<'a> {
9698
Type::USize => quote! { usize },
9799
Type::String => {
98100
let crate_name = self.crate_name();
99-
quote! { ::#crate_name::core::HSTRING }
101+
quote! { #crate_name HSTRING }
100102
}
101103
Type::BSTR => {
102104
let crate_name = self.crate_name();
103-
quote! { ::#crate_name::core::BSTR }
105+
quote! { #crate_name BSTR }
104106
}
105107
Type::IInspectable => {
106108
let crate_name = self.crate_name();
107-
quote! { ::#crate_name::core::IInspectable }
109+
quote! { #crate_name IInspectable }
108110
}
109111
Type::GUID => {
110112
let crate_name = self.crate_name();
111-
quote! { ::#crate_name::core::GUID }
113+
quote! { #crate_name GUID }
112114
}
113115
Type::IUnknown => {
114116
let crate_name = self.crate_name();
115-
quote! { ::#crate_name::core::IUnknown }
117+
quote! { #crate_name IUnknown }
116118
}
117119
Type::HRESULT => {
118120
let crate_name = self.crate_name();
119-
quote! { ::#crate_name::core::HRESULT }
121+
quote! { #crate_name HRESULT }
120122
}
121123
Type::PSTR => {
122124
let crate_name = self.crate_name();
123-
quote! { ::#crate_name::core::PSTR }
125+
quote! { #crate_name PSTR }
124126
}
125127
Type::PWSTR => {
126128
let crate_name = self.crate_name();
127-
quote! { ::#crate_name::core::PWSTR }
129+
quote! { #crate_name PWSTR }
128130
}
129131
Type::PCSTR => {
130132
let crate_name = self.crate_name();
131-
quote! { ::#crate_name::core::PCSTR }
133+
quote! { #crate_name PCSTR }
132134
}
133135
Type::PCWSTR => {
134136
let crate_name = self.crate_name();
135-
quote! { ::#crate_name::core::PCWSTR }
137+
quote! { #crate_name PCWSTR }
136138
}
137139
Type::Win32Array((ty, len)) => {
138140
let name = self.type_default_name(ty);
@@ -425,7 +427,7 @@ impl<'a> Gen<'a> {
425427
//
426428

427429
pub(crate) fn namespace(&self, namespace: &str) -> TokenStream {
428-
if namespace == self.namespace {
430+
if self.standalone || namespace == self.namespace {
429431
quote! {}
430432
} else {
431433
let is_external =
@@ -461,10 +463,12 @@ impl<'a> Gen<'a> {
461463
}
462464
}
463465
pub fn crate_name(&self) -> TokenStream {
464-
if self.sys {
465-
"windows_sys".into()
466+
if self.standalone {
467+
TokenStream::new()
468+
} else if self.sys {
469+
"::windows_sys::core::".into()
466470
} else {
467-
"windows".into()
471+
"::windows::core::".into()
468472
}
469473
}
470474
fn scoped_name(&self, def: TypeDef) -> String {

crates/libs/bindgen/src/lib.rs

Lines changed: 150 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,7 @@ pub fn namespace(gen: &Gen, tree: &Tree) -> String {
2424
let mut tokens = TokenStream::new();
2525

2626
if tree.namespace == "Windows" || !tree.namespace.starts_with("Windows.") {
27-
tokens.combine(&quote! {
28-
#![allow(non_snake_case, non_upper_case_globals, non_camel_case_types, dead_code, clippy::all)]
29-
});
27+
tokens.combine(&allow());
3028
}
3129

3230
for (name, tree) in &tree.nested {
@@ -187,6 +185,155 @@ pub fn component(namespace: &str, files: &[File]) -> String {
187185
bindings
188186
}
189187

188+
pub fn standalone(names: &[&str]) -> String {
189+
let files = &File::with_default(&[]).unwrap();
190+
let reader = &Reader::new(files);
191+
let mut gen = &mut Gen::new(reader);
192+
gen.standalone = true;
193+
gen.sys = true;
194+
let mut tokens: TokenStream = format!(
195+
r#"// Bindings generated by `windows-bindgen` {}
196+
197+
"#,
198+
std::env!("CARGO_PKG_VERSION")
199+
)
200+
.into();
201+
202+
tokens.combine(&allow());
203+
204+
tokens.combine(&quote! {
205+
pub type HRESULT = i32;
206+
pub type HSTRING = *mut ::core::ffi::c_void;
207+
pub type IUnknown = *mut ::core::ffi::c_void;
208+
pub type IInspectable = *mut ::core::ffi::c_void;
209+
pub type PSTR = *mut u8;
210+
pub type PWSTR = *mut u16;
211+
pub type PCSTR = *const u8;
212+
pub type PCWSTR = *const u16;
213+
pub type BSTR = *const u16;
214+
#[repr(C)]
215+
pub struct GUID {
216+
pub data1: u32,
217+
pub data2: u16,
218+
pub data3: u16,
219+
pub data4: [u8; 8],
220+
}
221+
impl GUID {
222+
pub const fn from_u128(uuid: u128) -> Self {
223+
Self { data1: (uuid >> 96) as u32, data2: (uuid >> 80 & 0xffff) as u16, data3: (uuid >> 64 & 0xffff) as u16, data4: (uuid as u64).to_be_bytes() }
224+
}
225+
}
226+
impl ::core::marker::Copy for GUID {}
227+
impl ::core::clone::Clone for GUID {
228+
fn clone(&self) -> Self {
229+
*self
230+
}
231+
}
232+
});
233+
234+
for name in names {
235+
let type_name = TypeName::parse(name);
236+
let mut found = false;
237+
238+
for def in reader.get(type_name) {
239+
found = true;
240+
let kind = gen.reader.type_def_kind(def);
241+
242+
match kind {
243+
TypeKind::Class | TypeKind::Interface => unimplemented!(),
244+
TypeKind::Enum => tokens.combine(&enums::gen(gen, def)),
245+
TypeKind::Struct => {
246+
if gen.reader.type_def_fields(def).next().is_none() {
247+
if let Some(guid) = gen.reader.type_def_guid(def) {
248+
let ident = to_ident(type_name.name);
249+
let value = gen.guid(&guid);
250+
let guid = gen.type_name(&Type::GUID);
251+
let cfg = gen.reader.type_def_cfg(def, &[]);
252+
let doc = gen.cfg_doc(&cfg);
253+
let constant = quote! {
254+
#doc
255+
pub const #ident: #guid = #value;
256+
};
257+
tokens.combine(&constant);
258+
continue;
259+
}
260+
}
261+
tokens.combine(&structs::gen(gen, def));
262+
}
263+
TypeKind::Delegate => tokens.combine(&delegates::gen(gen, def)),
264+
}
265+
}
266+
267+
if !found {
268+
if let Some(def) = reader
269+
.get(TypeName::new(type_name.namespace, "Apis"))
270+
.next()
271+
{
272+
for method in gen.reader.type_def_methods(def) {
273+
if found {
274+
break;
275+
}
276+
let name = gen.reader.method_def_name(method);
277+
if name == type_name.name {
278+
found = true;
279+
tokens.combine(&functions::gen(gen, method));
280+
}
281+
}
282+
for field in gen.reader.type_def_fields(def) {
283+
if found {
284+
break;
285+
}
286+
let name = gen.reader.field_name(field);
287+
if name == type_name.name {
288+
found = true;
289+
tokens.combine(&constants::gen(gen, field));
290+
}
291+
}
292+
}
293+
}
294+
}
295+
296+
try_format(tokens.into_string())
297+
}
298+
299+
fn try_format(tokens: String) -> String {
300+
use std::io::Write;
301+
302+
let Ok(mut child) = std::process::Command::new("rustfmt").stdin(std::process::Stdio::piped()).stdout(std::process::Stdio::piped()).stderr(std::process::Stdio::null()).spawn() else {
303+
return tokens;
304+
};
305+
306+
let Some(mut stdin) = child.stdin.take() else {
307+
return tokens;
308+
};
309+
310+
if stdin.write_all(tokens.as_bytes()).is_err() {
311+
return tokens;
312+
}
313+
314+
drop(stdin);
315+
316+
let Ok(output) = child.wait_with_output() else {
317+
return tokens;
318+
};
319+
320+
if !output.status.success() {
321+
return tokens;
322+
}
323+
324+
if let Ok(result) = String::from_utf8(output.stdout) {
325+
result
326+
} else {
327+
tokens
328+
}
329+
}
330+
331+
fn allow() -> TokenStream {
332+
quote! {
333+
#![allow(non_snake_case, non_upper_case_globals, non_camel_case_types, dead_code, clippy::all)]
334+
}
335+
}
336+
190337
/// Expand a possibly empty generics list with a new generic
191338
fn expand_generics(generics: TokenStream, new: TokenStream) -> TokenStream {
192339
if generics.is_empty() {

0 commit comments

Comments
 (0)