Skip to content

Commit

Permalink
Support standalone code generation (#2396)
Browse files Browse the repository at this point in the history
  • Loading branch information
kennykerr committed Mar 28, 2023
1 parent f3ef2cd commit 6672c6d
Show file tree
Hide file tree
Showing 417 changed files with 34,421 additions and 30,873 deletions.
1 change: 1 addition & 0 deletions .github/workflows/clippy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ jobs:
cargo clippy -p test_resources &&
cargo clippy -p test_return_struct &&
cargo clippy -p test_simple_component &&
cargo clippy -p test_standalone &&
cargo clippy -p test_string_param &&
cargo clippy -p test_structs &&
cargo clippy -p test_sys &&
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ jobs:
cargo test --target ${{ matrix.target }} -p test_resources &&
cargo test --target ${{ matrix.target }} -p test_return_struct &&
cargo test --target ${{ matrix.target }} -p test_simple_component &&
cargo test --target ${{ matrix.target }} -p test_standalone &&
cargo test --target ${{ matrix.target }} -p test_string_param &&
cargo test --target ${{ matrix.target }} -p test_structs &&
cargo test --target ${{ matrix.target }} -p test_sys &&
Expand Down
4 changes: 2 additions & 2 deletions crates/libs/bindgen/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ pub fn gen(gen: &Gen, def: Field) -> TokenStream {
quote! {
#doc
#features
pub const #name: ::#crate_name::core::PCSTR = ::#crate_name::s!(#value);
pub const #name: #crate_name PCSTR = #crate_name s!(#value);
}
} else {
let value = gen.value(&gen.reader.constant_value(constant));
quote! {
#doc
#features
pub const #name: ::#crate_name::core::PCWSTR = ::#crate_name::w!(#value);
pub const #name: #crate_name PCWSTR = #crate_name w!(#value);
}
}
} else {
Expand Down
15 changes: 12 additions & 3 deletions crates/libs/bindgen/src/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,18 @@ fn gen_sys_function(gen: &Gen, def: MethodDef) -> TokenStream {
quote! { #name: #tokens }
});

quote! {
#features
::windows_sys::core::link!(#link #abi #doc fn #name(#(#params),*) #return_type);
if gen.standalone {
quote! {
#[link(name = "windows")]
extern #abi {
pub fn #name(#(#params),*) #return_type;
}
}
} else {
quote! {
#features
::windows_sys::core::link!(#link #abi #doc fn #name(#(#params),*) #return_type);
}
}
}

Expand Down
32 changes: 18 additions & 14 deletions crates/libs/bindgen/src/gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub struct Gen<'a> {
pub cfg: bool,
pub doc: bool,
pub component: bool,
pub standalone: bool,
}

impl<'a> Gen<'a> {
Expand All @@ -18,6 +19,7 @@ impl<'a> Gen<'a> {
cfg: false,
doc: false,
component: false,
standalone: false,
}
}

Expand Down Expand Up @@ -96,43 +98,43 @@ impl<'a> Gen<'a> {
Type::USize => quote! { usize },
Type::String => {
let crate_name = self.crate_name();
quote! { ::#crate_name::core::HSTRING }
quote! { #crate_name HSTRING }
}
Type::BSTR => {
let crate_name = self.crate_name();
quote! { ::#crate_name::core::BSTR }
quote! { #crate_name BSTR }
}
Type::IInspectable => {
let crate_name = self.crate_name();
quote! { ::#crate_name::core::IInspectable }
quote! { #crate_name IInspectable }
}
Type::GUID => {
let crate_name = self.crate_name();
quote! { ::#crate_name::core::GUID }
quote! { #crate_name GUID }
}
Type::IUnknown => {
let crate_name = self.crate_name();
quote! { ::#crate_name::core::IUnknown }
quote! { #crate_name IUnknown }
}
Type::HRESULT => {
let crate_name = self.crate_name();
quote! { ::#crate_name::core::HRESULT }
quote! { #crate_name HRESULT }
}
Type::PSTR => {
let crate_name = self.crate_name();
quote! { ::#crate_name::core::PSTR }
quote! { #crate_name PSTR }
}
Type::PWSTR => {
let crate_name = self.crate_name();
quote! { ::#crate_name::core::PWSTR }
quote! { #crate_name PWSTR }
}
Type::PCSTR => {
let crate_name = self.crate_name();
quote! { ::#crate_name::core::PCSTR }
quote! { #crate_name PCSTR }
}
Type::PCWSTR => {
let crate_name = self.crate_name();
quote! { ::#crate_name::core::PCWSTR }
quote! { #crate_name PCWSTR }
}
Type::Win32Array((ty, len)) => {
let name = self.type_default_name(ty);
Expand Down Expand Up @@ -425,7 +427,7 @@ impl<'a> Gen<'a> {
//

pub(crate) fn namespace(&self, namespace: &str) -> TokenStream {
if namespace == self.namespace {
if self.standalone || namespace == self.namespace {
quote! {}
} else {
let is_external =
Expand Down Expand Up @@ -461,10 +463,12 @@ impl<'a> Gen<'a> {
}
}
pub fn crate_name(&self) -> TokenStream {
if self.sys {
"windows_sys".into()
if self.standalone {
TokenStream::new()
} else if self.sys {
"::windows_sys::core::".into()
} else {
"windows".into()
"::windows::core::".into()
}
}
fn scoped_name(&self, def: TypeDef) -> String {
Expand Down
153 changes: 150 additions & 3 deletions crates/libs/bindgen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@ pub fn namespace(gen: &Gen, tree: &Tree) -> String {
let mut tokens = TokenStream::new();

if tree.namespace == "Windows" || !tree.namespace.starts_with("Windows.") {
tokens.combine(&quote! {
#![allow(non_snake_case, non_upper_case_globals, non_camel_case_types, dead_code, clippy::all)]
});
tokens.combine(&allow());
}

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

pub fn standalone(names: &[&str]) -> String {
let files = &File::with_default(&[]).unwrap();
let reader = &Reader::new(files);
let mut gen = &mut Gen::new(reader);
gen.standalone = true;
gen.sys = true;
let mut tokens: TokenStream = format!(
r#"// Bindings generated by `windows-bindgen` {}
"#,
std::env!("CARGO_PKG_VERSION")
)
.into();

tokens.combine(&allow());

tokens.combine(&quote! {
pub type HRESULT = i32;
pub type HSTRING = *mut ::core::ffi::c_void;
pub type IUnknown = *mut ::core::ffi::c_void;
pub type IInspectable = *mut ::core::ffi::c_void;
pub type PSTR = *mut u8;
pub type PWSTR = *mut u16;
pub type PCSTR = *const u8;
pub type PCWSTR = *const u16;
pub type BSTR = *const u16;
#[repr(C)]
pub struct GUID {
pub data1: u32,
pub data2: u16,
pub data3: u16,
pub data4: [u8; 8],
}
impl GUID {
pub const fn from_u128(uuid: u128) -> Self {
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() }
}
}
impl ::core::marker::Copy for GUID {}
impl ::core::clone::Clone for GUID {
fn clone(&self) -> Self {
*self
}
}
});

for name in names {
let type_name = TypeName::parse(name);
let mut found = false;

for def in reader.get(type_name) {
found = true;
let kind = gen.reader.type_def_kind(def);

match kind {
TypeKind::Class | TypeKind::Interface => unimplemented!(),
TypeKind::Enum => tokens.combine(&enums::gen(gen, def)),
TypeKind::Struct => {
if gen.reader.type_def_fields(def).next().is_none() {
if let Some(guid) = gen.reader.type_def_guid(def) {
let ident = to_ident(type_name.name);
let value = gen.guid(&guid);
let guid = gen.type_name(&Type::GUID);
let cfg = gen.reader.type_def_cfg(def, &[]);
let doc = gen.cfg_doc(&cfg);
let constant = quote! {
#doc
pub const #ident: #guid = #value;
};
tokens.combine(&constant);
continue;
}
}
tokens.combine(&structs::gen(gen, def));
}
TypeKind::Delegate => tokens.combine(&delegates::gen(gen, def)),
}
}

if !found {
if let Some(def) = reader
.get(TypeName::new(type_name.namespace, "Apis"))
.next()
{
for method in gen.reader.type_def_methods(def) {
if found {
break;
}
let name = gen.reader.method_def_name(method);
if name == type_name.name {
found = true;
tokens.combine(&functions::gen(gen, method));
}
}
for field in gen.reader.type_def_fields(def) {
if found {
break;
}
let name = gen.reader.field_name(field);
if name == type_name.name {
found = true;
tokens.combine(&constants::gen(gen, field));
}
}
}
}
}

try_format(tokens.into_string())
}

fn try_format(tokens: String) -> String {
use std::io::Write;

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 {
return tokens;
};

let Some(mut stdin) = child.stdin.take() else {
return tokens;
};

if stdin.write_all(tokens.as_bytes()).is_err() {
return tokens;
}

drop(stdin);

let Ok(output) = child.wait_with_output() else {
return tokens;
};

if !output.status.success() {
return tokens;
}

if let Ok(result) = String::from_utf8(output.stdout) {
result
} else {
tokens
}
}

fn allow() -> TokenStream {
quote! {
#![allow(non_snake_case, non_upper_case_globals, non_camel_case_types, dead_code, clippy::all)]
}
}

/// Expand a possibly empty generics list with a new generic
fn expand_generics(generics: TokenStream, new: TokenStream) -> TokenStream {
if generics.is_empty() {
Expand Down

0 comments on commit 6672c6d

Please sign in to comment.