-
Notifications
You must be signed in to change notification settings - Fork 534
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
RFC: Provide pre-built version of this library #432
Comments
I'm personally in favor of that. The API surface is huge though so it's not exactly clear how best one would offer a pre-built version. So, some additional questions:
|
I don't think it is terribly wrong to give an ability to pre-generate and include in crates just the bindings they need. As you mention it can lead to incompatibilities if these are exposed in the public interface of the crates, but in my experience the need to do so is pretty rare. |
In my experience the winapi 0.3 approach is proven to work well, single huge crate but with features for all areas/modules that are disabled by default so each crate using it has to opt in to the feature they do want to use. This compiles nicely and the cargo additive feature support makes sure winapi is built once with all the features that all dependencies have enabled. In our codebase we have ~50 direct usages from our own and third party crates to |
IME it hurts in large workspaces, since Personally I'd prefer to use it in a pre-generation context, which avoids all these issues. |
Yup that is true and a fair point, we do have a fairly large workspace (~550 crate dependencies) but less common to build and test subparts of it. But definitely does happen and is a common cargo workflow in general. |
What should probably happen is a generator / default-generated split. The generator at work here is amazing work, and it's fine for it to just be a thing that exists on its own. However, many people want to use a pre-generated form, chunked up by feature flags or whatever. Just offer that as a crate separate from the generator itself. Hek, just run this generator and then use the output as the content of |
Yeah, there's a size of workspace where this becomes untenable. |
This feels like something that could be fixed in cargo itself, e.g. through the deduplicate workspace information RFC eventually. Through this RFC, you could specify the features once in the workspace root, and all the packages in your workspace would end up using a common build. Personally, I can't use this crate without a pregenerated version being made available. Having every single crate generate their own bindings through windows-rs feels like a recipe for disaster in terms of compile-time regressions compared to the current winapi-rs approach. |
It feels to me like without a major redesign, it will always be something of an issue, but perhaps I'm too pessimistic.
Yes, I agree here, unless the pregeneration approach is widely taken, which it probably won't be, since it will always be a bit too work intensive. That said, I'd really like it as an option (Plenty of crates already do this by hand, it would be valuable to improve things for them). |
Sorry for the delay in responding. This is an important issue to us. In fact, we had a meeting to discuss this topic just yesterday. This matters for some of our internal teams as well. We would like to provide pre-generated bindings as a collection of crates, but there are pros and cons to doing so today. Here are a few issues to consider:
So there are just a few challenges and ideas we're throwing around. The main benefit we see for pre-generated bindings is to allow crates to share common definitions of Windows types. This is something we would like to support, but there are technical hurdles to overcome. These are hard problems, and we look forward to working together to solve them. |
I've been thinking more about this. I'd like to try to capture the pros and cons of pre-built crates as well as the dependencies. Some of this will be a repeat of what @kennykerr says above but hopefully this provides a bit more structure to think about this problem. Pros
Cons
Next steps
I definitely think no matter what happens we will keep on-demand generation as an option. It's extremely valuable in many situations. However, a pre-built crate may be an additional choice for those who want/need it. More ideasPotentially, we might be able to let these different approaches work well together. For instance, we could provide a Win32 crate which only contains definitions in the Win32 namespace. Then when the user is generating bindings outside this namespace, they could optionally let the code generator know that they want to depend on the Win32 definitions in the After some testing, I found that the Win32 namespace has dependencies only on two other namespaces: Foundation and System. Luckily these namespaces do not depend on any others. So if we go down the route of a Win32 crate, we'll need to decide on some things:
|
Possibly silly idea: could the generator (since it has the full metadata) solve dependency cycles and generate a |
@Plecra this is something that we could explore, but it seems that namespaces often have circular dependencies which would not be served well by a separate crate model. Also, even if this is possible now, we would essentially be committing to this model forever. What happens when new APIs come along that break this model? The Windows APIs have a lot bigger concerns than if the break the Rust bindings, so it's unlikely we would be able to prevent such a thing from happening. Like I said above, perhaps we might be able to build a model that handles both pre-generated and non-pre-generated bindings. Then for some select namespaces, we can pre-generate the bindings if we feel comfortable enough that those namespaces work well in such a model. |
Can you point out some of the circular dependencies that cause trouble? Because I think that enough of Win32 is non-circular that it's not a big deal if a few fringe cases have to be combined into a single loop of stuff that you add all at once. |
@Lokathor I don't have an example off the top of my head. I will try to do some spelunking and find some. For win32 specifically (i.e., the things in the metadata under the win32 namespace), it doesn't seem like there are any dependencies currently outside of |
It would be insightful to use the metadata reader to model the dependencies. I would however be quite reluctant to favor some APIs over others. Ideally, we can find a solution that works for all APIs and not just some blessed APIs that happen to not have cyclic issues. |
Yeah. Not to try and diminish the work on the other APIs also being supported, but I think at this time that most people are only using Win32, particularly because that's where the My suggestion would be to coordinate with @retep998 about possibly having the next semver break of |
Here's a quick little dump of dependencies. use std::collections::{BTreeMap, BTreeSet};
fn main() {
let reader = gen::TypeReader::get();
let mut deps = BTreeMap::<&'static str, BTreeSet<&'static str>>::new();
for ns in reader.namespaces() {
for et in reader.namespace_types(ns) {
for def in et.dependencies() {
if et.namespace() != def.namespace() && !def.namespace().is_empty() {
deps.entry(et.namespace())
.or_default()
.insert(def.namespace());
}
}
}
}
println!("{:#?}", deps);
} Output: https://gist.github.com/kennykerr/b27b6e803078336c2d55780109820c3e |
I did some analysis on the dependency output that Kenny posted.
We need the following two features to be able to experiment with this:
|
I see that there are three strategies:
Notice the case 2. If your library is merely using the bindings, it's wasteful to depend on macro generation because every user of the library would generate the same bindings. Thus it's cheaper for us all if the bindings are pre-generated and bundled as code for that kind of libraries. |
Just a quick update - I've been working on this issue over the last week and have a working proof of concept. There are some rough edges but it definitely looks like this will be a viable solution before long. |
To summarize, developers will then be able to choose between:
I just need the metadata folks to clean up the namespace dependencies to improve build performance, and a few other loose ends like nightly |
Will a 3 proposed solutions be compatible with each other and share code / compile time? |
"3" finally allows Windows types to be shared across crates in a stable way. There are however still a lot of details to work out around how that will interoperate with code generated by |
FWIW, I just switched my work-in-progress Windows implementation of AccessKit to use the new pre-packaged bindings. The only wrinkle was that I had to add the What's blocking the publication of version 0.22.x on crates.io? AccessKit isn't yet on crates.io, so it's not yet a problem for me, but it would be useful to know. |
That's great to hear! microsoft/win32metadata#710 should help with some of those bigger namespaces. If you find more glaring issues like that please do report them to the win32metadata project. I plan to publish a crate today - I was just waiting for an exemption as the crate is a little larger than the max allowed. I'll also close this issue as this feature is now available. |
Published! https://crates.io/crates/windows |
Thanks a lot @kennykerr! Casually releasing 0.22.1 without 0.22.0 😁 |
😄 I had originally planned to release 0.22.0 and got as far as the dependency crates and then the |
The windows api link above gives a 404 |
@nico-abram That was a temporary prototype, the pregenerated bindings are now published right inside the |
@nico-abram The pre-built API bindings are in the windows crate itself, in this repo. The updated readme shows how to use them. |
First off: thanks for a nice addition to the Rust ecosystem, it's really wonderful to see Microsoft actively try to support what is going on and provide support for the win32 apis.
However as a maintainer of several code-generation crates I've found that it's usually significantly nicer for the user of my crates to provide a pre-built version of the wrappers that I'm exposing. This has a few benefits
build.rs
is a serial dependency and very slow for more complex projects since cargo has to stall building code further down the line, and it has to wait for code-gen crates likesyn
andquote
to have been built before it can run abuild.rs
.cargo deny
for example.To see the impact of building this crate, one can run
cargo +nightly build -Ztimings
to see the build dependency graph and this crate's impact on it (for example on theminesweeper-rs
example that you've provided it looks like the vast majority of it's ~70s build time is spent in code-gen related things).If a pre-generated version of this crate becomes available (or nicers: this crate switches into a pre-built library) please add feature toggles similar to how
winapi-rs
does things, this lets thecargo
feature resolver make sure that the minimal set of features is built across the entire dependency tree.The text was updated successfully, but these errors were encountered: