-
Notifications
You must be signed in to change notification settings - Fork 125
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
Namespace dependencies need some cleaning up #700
Comments
The WinRT namespaces are relatively clean by comparison. To give you a sense for how impactful this is, here are two examples. The first example uses a simple WinRT API while the second a simple Win32 API. The WinRT API actually requires hundreds of lines of code as it involves activation factories, required interfaces, and much more. The Win32 API just requires a single function declaration. The WinRT project creates a
The Win32 project call the
This is because the This is probably one of the more egregious examples, but many other namespaces have similarly unnecessary dependencies that impact build time. |
When you pull in an API from a namespace, do you generate code for the entire namespace and any dependencies? CsWin32 only pulls in the API and any types it depends on, but maybe you can't do that in Rust. I just built in an app using CsWin32 that calls GetTickCount and it built pretty much instantly. |
I provide a few different options to developers include generating just the function itself. While this is very fast, it includes various tradeoffs that are blocking broad adoption by the Rust ecosystem. Having pre-generated bindings - much like C#/WinRT (not C#/Win32) - ends up being the only viable option for many projects. In the pre-generated world, you end up compiling the entire namespace. |
This is how windows-rs works today for on-demand scenarios, but for pre-generated-entire-world scenario these spurious dependencies really hurt. Another example, it appears // Windows.Win32.Media.Multimedia.JOYREGHWCONFIG
using System.Runtime.InteropServices;
using Windows.Win32.Devices.HumanInterfaceDevice;
using Windows.Win32.Media.Multimedia;
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct JOYREGHWCONFIG
{
public Windows.Win32.Media.Multimedia.JOYREGHWSETTINGS hws;
public uint dwUsageSettings;
public Windows.Win32.Devices.HumanInterfaceDevice.JOYREGHWVALUES hwv;
public uint dwType;
public uint dwReserved;
} |
I thought pre-generated bindings pre-generate all namespaces, like C#/WinRT does. I could imagine as big as the .winmd has gotten that that would be pretty slow. How does the single namespace scenario work with pre-generated bindings? Do developers choose a namespace they want to use, or is your projection choosing which ones to pre-generate? What I'm trying to figure out is how to think about fixing the problem you're running into. If you could give me some targeted namespaces you would like to see be cleaner, that would help in figuring out how to make some progress. |
In Rust, developers are able to choose between:
The first two generate whatever individual APIs the developer chooses down to an individual function. The third option is a package of pre-generated bindings for the entire Windows API - ever last namespace. However each namespace is gated (much like you would use So when I use option 3 and request the
And those namespaces further depend on other namespaces and so on. Clearly, This is analogous to using |
Windows.Win32.System.Com scrapes ole.h, which includes: https://docs.microsoft.com/en-us/windows/win32/api/ole/nf-ole-oledraw OLESTATUS OleDraw(
LPOLEOBJECT ,
HDC ,
const RECT *,
const RECT *,
HDC
); HDC comes from the Gdi namespace. To break this dependency, maybe OleDraw could go into a sub-namespace of Com like Com.UI. Or would you propose a different solution? Windows headers are notoriously enmeshed in each other, so untangling these dependencies is going to be a ton of work. |
Can you gate things other than namespaces? If not, is this a limitation of Rust or just a matter of letting the developers understand which gate they need to open to get the function they want? The Windows SDK documentation lists the header files needed for functions and types. I presume the dependencies between the header files have been reviewed. Perhaps you could gate by them. API sets might have been another alternative but e.g. GetTickCount doesn't seem to be part of any API set. |
Namespace are the natural boundary for something like this. Otherwise, I don't see much point in having these synthesized namespaces for Win32 at all.
Shouldn't |
That could also work. |
Basically, you want to avoid cross-namespace dependencies other than:
The latter can help because it is very common for languages like C++ and Rust to always include the definitions from the parent namespace. This is what C++/WinRT does today. For example, the Obviously, we can't eliminate them all but we should be able to get closer to the ideal. Doing some basic analysis on the resulting win32.winmd might be easier than trying to analyze the raw headers. That's what I've been doing to great effect. Hope that helps. |
By the way, the The |
In fact, almost all of the cross-namespace dependencies come from structs with field types from other namespaces. You can very easily write a script to print those out to get a sense for the scope of the problem. Deal with the structs and the problem largely goes away. That seems like a much more manageable task. |
If at all possible, we try to deal in units of headers, not structs. We can move individual structs from one namespace to another but it gets hard to manage. Sometimes we have no choice like with winuser.h which is piled with all kinds of crap that doesn't belong together. Just looking at these three types, they all come from ocidl.h and are mostly used by things in ocidil.h and ole*.h. We could move ocidl.h out to another namespace like with the ole*.h headers. Most of the stuff in there looks specific to controls, but not all of it. Did I mention the Windows headers aren't layered very well? :) |
I guess I'm not clear on why the headers ultimately matter. Once you have the winmd you can freely move things around to organize as you see fit. That's already happening all over the place. It seems like that ship has sailed and you should just embrace it. 😋 |
Who is moving things around after we get the .winmd? Our win32 metadata project isn't doing that. |
Also, I'm not understanding why you only want structs moved out. For the structs you listed, a bunch of interfaces and functions use those structs in the com namespace. Why is it structs cause the Rust projection problems and not the functions and interfaces that use them? It's beginning to sound like a Rust projection-specific problem you want me to solve. |
No, I mean the win32 metadata already makes decisions about how different APIs get mapped to different namespaces. Right now that's predominantly based on original header grouping but that need not be that way. The original headers are not preserved.
No, both C++ and Rust behave the same way. Structs must be fully defined because their size must be known at compile time. Functions are typically different because parameters, of reference/pointer types, don't need to be known ahead of time. So that can be deferred. I'm not suggesting you move all or even any specific structs or headers around at all. I'm just suggesting you consider where there are cross-namespace dependencies that are undesirable that we clean those up as they negatively affect build time for languages like C++ and Rust. It's your call - I'm just trying to give you as much information as I have and I'm certainly not suggesting we split structs from their associated functions. |
I think I'll just close this issue and open new issues for any specific dependency issues I come across like #701. |
https://gist.github.com/kennykerr/cd45cf6473498707a469abec8dd9e71e
This is from a Rust toml file showing feature dependencies. They basically map to metadata namespaces. You can see that the Win32 namespaces need some work. Note that this only shows certain dependencies (there are many others that are omitted for this list).
Reducing these dependencies would have a huge impact on build time for consuming projects.
The text was updated successfully, but these errors were encountered: