Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support standalone code generation #2396

Merged
merged 6 commits into from
Mar 28, 2023
Merged
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
  •  
  •  
  •  
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
Loading