-
Notifications
You must be signed in to change notification settings - Fork 503
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
Major windows-bindgen
update
#3359
Conversation
@riverar @ChrisDenton @wravery - would love your feedback. A few notes on housekeeping and changes. The undocumented and largely internal The MSRV for most crates has now moved to 1.74 to avoid some compiler bugs in previous versions. 94e1288 The The lib generator now detects import functions that aren't available on x86. e5c2492 The "bindgen" tool/test crates now provide a much simpler way to test code generation. The older "standalone" tool/test still exists but will be phased out. e9772d6 |
Hmm, am concerned about this being the default behavior. It isn't easy to discover what's needed for a complete interface projection. (I haven't run the new bindgen yet, perhaps there's already a warning that is output?) |
@riverar if its any consolation this never worked before - it would blindly add all methods and then produce invalid code gen unless you manually chase down all the dependencies. We can add such an option but my attempts showed that it would be too expensive to do by default e.g. any method that mentions Xaml in any way can easily add a million lines of code... 😉 As it stands, its pretty easy to see what methods are omitted since the vtable slots are named and padded so you can tell the "Status" method is missing. |
I've been trying it out and so far it works great, though I've not yet tried all the new features. Only minor issue is that the output sort order appears to have changed. E.g. here's the diff when updating the standard library to the new bindgen: rust-lang/rust@1d83a16. That said, it's not a big deal if it's a one time thing with this update. Also, if |
Yes, I've been fiddling with the sort order. Its a little awkward as its primarily based on the Yes, falling back to |
@ChrisDenton you should now find that it is closer to what you had before in terms of sort order. The functions are no longer sorted by library so that might be noticeable but expected. If you have types with different |
I was just about to say those are the exact to differences I see: rust-lang/rust@097e213. Altogether a much more manageable diff, thanks! |
Sweet - thanks for testing! |
I have a WinRT component that I've been moving to the new bindgen crate, and there's some differences between what was published before that might be worth calling out (even if by design). Additionally, there's a snag I haven't been able to figure out. Second, some methods weren't generated in my "*_Impl" traits. I added "Windows.Foundation.Collections.IVectorView" to my filter and that works now, but now I can't seem to leverage the helpers from the Windows crate to create collections. I haven't figured out the best way around this. The repo and branch is here if you're curious. |
Hey Rob, briefly the
There are some tests for this here which might help explain until I get more complete docs: |
I'll work on some samples too - that would probably be more helpful. 😊 |
Aha! Thanks, that seems to be exactly what I want.
Nevermind, it was a typo on my end :) |
Sorry, that was a typo - its "Foundation" not "foundation". But then I just started working on a sample and found a little bug. 🫢 Stay tuned... Note to self: when a type dependency is required (e.g. a required interface vs an optional method signature dependency) then the type is generated as part of the bindings even when the type can be resolved via a reference. |
OK, I've addressed the Check out the "reference" and "reference_client" test crates. I'll do some more testing and probably write a few more samples but let me know if you have any more issues in the mean time. |
This update introduces the next major update to the
windows-bindgen
crate, having been overhauled to provide first-class support for custom code generation. Historically, thewindows-bindgen
crate was written to generate thewindows
crate and then thewindows-sys
crate. This was then further extended to support other scenarios, primarily standalone code generation. This was somewhat awkward as thewindows-bindgen
crate could not easily handle this and thus required some gymnastics to pull it off. This has made it difficult to fix various issues and support new scenarios.Well this update aims to address these issues with a rewrite of the crate with an emphasis on general-purpose code generation that supports standalone code generation just as well or better than the generation of the
windows
andwindows-sys
crates. Indeed, the generation of these crates is really just a special application of the general-purpose code generator. Let's walk through a few examples to illustrate what this looks like. Consider the following build script:This represents the general usage of the
bindgen
function. You'll notice that it no longer returns aResult
and will simply panic with a clear message describing any issues with the arguments or environment. This highlights the fact that this is meant to be used as a build tool and so a build error makes the most sense.The
bindgen
function supports various options but three are required.--in
can indicate a .winmd file or directory containing .winmd files. Alternatively, the special "default" input can be used to use the particular .winmd files that ship with thewindows-bindgen
crate. Previously this "default" metadata was included using a crate feature, but this new approach should be far less error-prone and predictable. If no input is provided then the "default" input is assumed.--out
indicates where thebindgen
function will write the bindings. This is typically a source file likesrc/bindings.rs
but can also point to a directory when generating a crate likewindows
orwindows-sys
.--filter
indicates what bindings to include or exclude as it's unusual to generate bindings for everything described by the input .winmd files.The next thing you'll notice, at least if you've used previous versions of the
windows-bindgen
crate, is that theCoCreateGuid
function or filter simply includes the function name and nothing more. In previous versions you had to spell it out in order for thebindgen
function to find the metadata.This continues to work but is often meaningless to many projects that don't care about the namespace hierarchy used by the .winmd files. This saves developers from having to figure out what these paths might be. If you happen to know the name of the function or type that you need then you can usually just type it in and see if the bindings can be generated correctly. Either way, the bindings will be generated the same way. There are however cases where a name might be ambiguous and then you may have to spell things out to resolve the ambiguity.
Here's what it might look like for the example above:
As you can see, the bindings still default to using the namespace-to-module mapping but it's easy to turn that off as well simply by using the
--flat
option:Previously, this flat output required both of the wordier
"--config", "flatten"
arguments and didn't work too well in more complex scenarios. And here's the resulting flat output:You can also control whether the bindings include the comment and allow attribute with the
--no-comment
and--no-allow
options.The other interesting option is
--sys
which instructs thebindgen
function to generate raw, commonly known as sys-style, Rust bindings. Previously, this output required both of the wordier"--config", "sys"
arguments.Here's how the
CoCreateGuid
function is generated in this case:You'll notice that the bindings are simpler as there's no wrapper function and "core" types like
GUID
andHRESULT
are now provided by thewindows_sys::core
module rather than the richerwindows_core
module that is used by thewindows
crate. This illustrates another difference that was difficult to reconcile in the previous version of thewindows-bindgen
crate. Previously, you could have references to thewindows
crate but not to thewindows-sys
crate. This caused all kinds of problems for sys-style bindings of custom or private APIs. Of course, there may still be cases where a dependency on either thewindows
orwindows-sys
crates are undesirable. For that you have two options. The first is to use the new--no-core
option as follows:This instructs the
bindgen
function to avoid dependencies for these "core" types and instead include them directly in the generated bindings. Here's what the resulting output looks like:As you can see, the
bindgen
function detects that theCoCreateGuid
function depends onGUID
andHRESULT
and includes their definitions directly. At this point, the--no-core
option requires the--sys
option. I may revisit this in future but the richer non-sys core type definitions are not very easy to inline.The other option, which applies equally well to sys and non-sys style bindings is to leverage the Rust language to inject your own definitions for such core types. Let's remove the
--no-core
option as follows:The resulting bindings thus once again look like this:
The simplest thing to do here is to add a dependency for the
windows-targets
crate as well as thewindows-sys
crate and then these bindings will compile just fine. Alternatively, you can provide a local definition ofwindows_sys
that redirects to a local definition for these core types. You can do this in various ways but one simple approach is to create an alias forwindows_sys
in yourlib.rs
file as follows:Here I've simply defined a mini
core
module with the necessary type definitions and an alias forwindows_sys
that points right back to the current crate. You can do something similar to avoid a dependency on thewindows_targets
crate if so desired.Previous versions of the
windows-bindgen
crate were not able to track down dependencies reliably under specific conditions. In the mode that generated crates likewindows
andwindows-sys
it was assumed that dependencies would be discovered at compile time in other modules depending on the inclusion of Cargo features. In the mode that generated standalone bindings it would generate dependencies but only if certain other options lined up just right. The new version of thewindows-bindgen
crate should now reliably tracks down all dependencies in all cases and regardless of the kind of output it produces.Let's consider a struct example first and generate bindings for the
InkTrailPoint
struct.I didn't include the
--flat
option so the output looks like this:You can observe that the
InkTrailPoint
struct was defined in the "Windows.UI.Composition" namespace in the input .winmd file and that it has aPoint
field whose type is defined in the "Windows.Foundation" namespace. Thebindgen
functions is careful to generate both module hierarchies and thesuper::super::
path to refer from the one to the other. All this happened despite the fact that thebindgen
filter only mentioned "InkTrailPoint". We can of course use the--flat
option to ignore the hierarchy:Now the output looks like this:
Dependencies get a lot more interesting when it comes to COM or WinRT style interfaces and classes and particularly class or interface hierarchies as those dependencies can quickly add up. Often however those dependencies can be unrelated or at least uninteresting to a given project and added burden of that cascade of dependencies can be undesirable. As such, the
bindgen
function will only include interface methods whose signatures refer to types that are themselves included in the--filter
option.Consider the following example using the
IAsyncInfo
interface:Now the
IAsyncInfo
interface actually has five methods namelyId
,Status
,ErrorCode
,Cancel
, andClose
but the resulting output omits theStatus
method. Here's a simplified version of the output for clarity:The methods that are included are those that don't depend on anything other than intrinsics or core types. Other methods are simply omitted. But what if you need the
Status
method? Simply include the types needed by that method in the--filter
option as follows:And just like that the extra
Status
method appears as its dependency onAsyncStatus
is now available.In some cases you may not want to generate the dependencies directly but instead defer to some other crate (or module) to provide those definitions. That's where the new
--reference
option comes in handy. Previously, thewindows-bindgen
crate would only allow dependencies on thewindows
crate. Now this is completely controlled by the--reference
option. Let's imagine thatAsyncStatus
is super complicated and you'd rather get that dependency from thewindows
crate. This is a silly example but you can easily imagine how this can be applied to larger dependencies like Direct3D or third-party dependencies. Instead of simply includingAsyncStatus
in the--filter
option, you can instead use the--reference
option to indicate where and how to resolve the type. Here's an example:This is a rather powerful feature with a great deal of flexibility so it may seem a little confusing at first but its actually quite simple. Here we're saying that the
windows
crate should resolve any references to theAsyncStatus
type using the "skip-root" path style. The reason for this should quickly become apparent. Here's what the output looks like now:As you can see, the
Status
method now refers toAsyncStatus
as defined in theFoundation
module within thewindows
crate. Theskip-root
path style is in reference to the fact that both thewindows
andwindows-sys
crates skip the root "Windows" namespace or module when generating their type hierarchies. Depending on how the target crate was generated you may also use the "full" or "flat" path styles instead. Obviously, you can use the--reference
option to generate dependencies to crates of your own. Conversely, you can use the newwindows-bindgen
crate to generate those crates!Naturally, you can use the
--reference
option to generate references to entire namespaces and not just specific types. The following option will work just as well and also resolve references to any other types coming from the given "Windows.Foundation" namespace.Now let's look at a few more new options and features supported by the
windows-bindgen
crate.The
--derive
option allows you to indicate extra traits that you would like specific types to derive. Imagine you need theRectF
andRectInt32
structs so you generate them as follows:The resulting output look like this:
Now you'd like to have the
RectInt32
struct implement theEq
andPartialEq
traits while theRectF
struct should only implement thePartialEq
trait since it contains floating point fields and those can't be derived. Here's how you might do that with the new--derive
option:And the resulting output looks like this:
The
--rustfmt
option allows you to override the default Rust formatting as follows:The formatting options can be found here: https://rust-lang.github.io/rustfmt
Well there's a lot more I could say about this update but I think I'll stop before this becomes too long to read. 😉