microsoft / windows-rs

Rust for Windows
https://kennykerr.ca/rust-getting-started/
Apache License 2.0
10.49k stars 497 forks source link

Allow adding attributes to items in `build!` macro #702

Closed rylev closed 2 years ago

rylev commented 3 years ago

As part of support for #432, we want to allow cfg attributes on namespaces in the build! macro so that users can generate code which includes cfg(feature) attributes. In other words, we want the user to be able to specifiy that certain namespaces be generated together with a cfg attribute.

I did a bit of investigation and found two ways that we might want to do it. One way is easier to introduce than the other but it is not as flexible.

Option 1

In option 1, let's assume we have a build! macro that looks like this:

windows::build!(
    Windows::Foundation::Numerics::Matrix3x2,
    Windows::Win32::Direct2D::{D2D1CreateFactory, ID2D1Bitmap},
)

We could allow the user to add cfg flags like so:

windows::build!(
    #[cfg(feature = "numerics")]
    Windows::Foundation::Numerics::Matrix3x2,
    #[cfg(feature = "direct2d")]
    Windows::Win32::Direct2D::{D2D1CreateFactory, ID2D1Bitmap},
)

This would interpret these annotations as applying to the most inner module, so in this case Windows::Foundation::Numerics and Windows::Win32::Direct2D. In other words, the annotations apply to the rightward most namespace.

Pros and Cons

This would be really easy to add to the existing code and would cause the need for minimal refactoring of the build! macro, but unfortunately there are some aspects that aren't so great.

Option 2

Option 2 is more complicated to implement, but it's more flexible. In order to get the semantics we got above the macro would be written like so:

windows::build!(
    Windows::{
        Foundation::{
            #[cfg(feature = "numerics")] 
             Numerics::Matrix3x2,
         },
         Win32::{
            #[cfg(feature = "direct2d")]
            Direct2D::{D2D1CreateFactory, ID2D1Bitmap},
        }
   }
)

This essentially applies the annotations to the item that directly come after the annotation.

Pros and Cons

This would require refactoring how we handle build! completely. In this specific case, the build! macro does look a bit nosier, but I'm not sure this would always be the case. This does allow for arbitrary nesting of namespaces and the annotations can be placed on any modules and if we wanted to, individual types.

Question

Which option should we pursue? Is this a better alternative to the ones proposed here?

kennykerr commented 3 years ago

Is this something that individual projects would use? Seems like individual projects would simply use the build macro as it is today.

If that's true, this is mostly/exclusively for creating pre-built libraries for multi-project use/sharing and such libraries would likely be mostly interested in attaching features to modules, such as Windows::Win32::Direct2D::*, rather than individual types. There are however some very large modules, such as Windows::UI::Xaml::Controls, that may benefit from sub-features but I'm not sure how those would practically be applied.

rylev commented 3 years ago

I imagine this will be useful for projects that are not just creating pre-built bindings. If you are creating a library that has certain features and those features rely on Windows APIs (otherwise certain Windows APIs are not needed) this can be used to only generate those bindings when certain features are enabled.

kennykerr commented 3 years ago

Yes, that's a good example. In that case, it seems like we need the more flexible option of being able to apply it to both modules, including wildcards, and/or types.

kennykerr commented 2 years ago

The build macro is being removed in favor of pre-generated bindings, along with the bindgen crate to cover other scenarios.