windows_bindgen/
lib.rs

1#![doc = include_str!("../readme.md")]
2#![expect(
3    non_upper_case_globals,
4    non_camel_case_types,
5    dead_code,
6    non_snake_case,
7    clippy::enum_variant_names,
8    clippy::upper_case_acronyms
9)]
10
11mod config;
12mod derive;
13mod derive_writer;
14mod filter;
15mod guid;
16mod index;
17mod io;
18mod libraries;
19mod param;
20mod references;
21mod signature;
22mod tables;
23mod tokens;
24mod type_map;
25mod type_name;
26mod type_tree;
27mod types;
28mod value;
29mod warnings;
30mod winmd;
31
32use config::*;
33use derive::*;
34use derive_writer::*;
35use filter::*;
36use guid::*;
37use io::*;
38pub use libraries::*;
39use param::*;
40use references::*;
41use signature::*;
42use std::cmp::Ordering;
43use std::collections::*;
44use std::fmt::Write;
45use tables::*;
46use tokens::*;
47use type_map::*;
48use type_name::*;
49use type_tree::*;
50use types::*;
51use value::*;
52pub use warnings::*;
53use winmd::*;
54mod method_names;
55use method_names::*;
56
57/// The conventional way of calling the `bindgen` function is as follows:
58///
59/// ```rust,no_run
60/// let args = [
61///     "--out",
62///     "src/bindings.rs",
63///     "--filter",
64///     "GetTickCount",
65/// ];
66///
67/// windows_bindgen::bindgen(args).unwrap();
68/// ```
69///
70/// Here is a list of supported arguments.
71///
72/// | Argument | Description |
73/// |----------|-------------|
74/// | `--in` | .winmd files or directories to include. |
75/// | `--out` | File name where the generated bindings will be saved. |
76/// | `--filter` | APIs to include or exclude in the generated bindings. |
77/// | `--rustfmt` | Overrides the default Rust formatting. |
78/// | `--derive` | Extra traits for types to derive. |
79/// | `--flat` | Avoids the default namespace-to-module conversion. |
80/// | `--no-allow` | Avoids generating the default `allow` attribute. |
81/// | `--no-comment` | Avoids generating the code generation comment. |
82/// | `--no-deps` | Avoids dependencies on the various `windows-*` crates. |
83/// | `--sys` | Generates raw or sys-style Rust bindings. |
84/// | `--sys-fn-ptrs` | Additionally generates function pointers for sys-style Rust bindings. |
85/// | `--sys-fn-extern` | Generates extern declarations rather than link macros for sys-style Rust bindings. |
86/// | `--implement` | Includes implementation traits for WinRT interfaces. |
87/// | `--link` | Overrides the default `windows-link` implementation for system calls. |
88///
89///
90/// # `--out`
91///
92/// Exactly one `--out` argument is required and instructs the `bindgen` function where to write the bindings.
93///
94/// # `--filter`
95///
96/// At least one `--filter` is required and indicates what APIs to include in the generated bindings.
97/// The following will, for example, also include the `Sleep` function:
98///
99/// ```rust
100/// let args = [
101///     "--out",
102///     "src/bindings.rs",
103///     "--filter",
104///     "GetTickCount",
105///     "Sleep",
106/// ];
107/// ```
108///
109/// The `--filter` argument can refer to the function or type name and nothing more. You can also refer
110/// to the namespace that the API metadata uses to group functions and types:
111///
112/// ```rust
113/// let args = [
114///     "--out",
115///     "src/bindings.rs",
116///     "--filter",
117///     "Windows.Foundation.Numerics",
118///     "!Windows.Foundation.Numerics.Matrix3x2",
119/// ];
120/// ```
121///
122/// In this example, all types from the `Windows.Foundation.Numerics` namepace are included with the
123/// exception of `Matrix3x2` which is excluded due to the `!` preamble.
124///
125/// # `--in`
126///
127/// `--in` can indicate a .winmd file or directory containing .winmd files. Alternatively, the special
128/// "default" input can be used to include the particular .winmd files that ship with the `windows-bindgen`
129/// crate. This may used to combine the default metadata with specific .winmd files.
130///
131/// ```rust
132/// let args = [
133///     "--in",
134///     "default",
135///     "Sample.winmd",
136///     "--out",
137///     "src/bindings.rs",
138///     "--filter",
139///     "Sample",
140/// ];
141/// ```
142///
143/// # `--flat`
144///
145/// By default, the bindings include a mapping of namespaces to modules. Consider this example again:
146///
147/// ```rust
148/// let args = [
149///     "--out",
150///     "src/bindings.rs",
151///     "--filter",
152///     "GetTickCount",
153///     "Sleep",
154/// ];
155/// ```
156///
157/// The resulting bindings might look something like this:
158///
159/// ```rust
160/// pub mod Windows {
161///     pub mod Win32 {
162///         pub mod System {
163///             pub mod SystemInformation {
164///                 #[inline]
165///                 pub unsafe fn GetTickCount() -> u32 {
166///                     windows_link::link!("kernel32.dll" "system" fn GetTickCount() -> u32);
167///                     unsafe { GetTickCount() }
168///                 }
169///             }
170///             pub mod Threading {
171///                 #[inline]
172///                 pub unsafe fn Sleep(dwmilliseconds: u32) {
173///                     windows_link::link!("kernel32.dll" "system" fn Sleep(dwmilliseconds : u32));
174///                     unsafe { Sleep(dwmilliseconds) }
175///                 }
176///             }
177///         }
178///     }
179/// }
180/// ```
181///
182/// That's because the default metadata defines `GetTickCount` in the `Windows.Win32.System.SystemInformation`
183/// namespace while `Sleep` is defined in the `Windows.Win32.System.Threading` namespace. Fortunately, it's
184/// easy to turn that off by using the `--flat` argument:
185///
186/// ```rust
187/// let args = [
188///     "--out",
189///     "src/bindings.rs",
190///     "--flat",
191///     "--filter",
192///     "GetTickCount",
193///     "Sleep",
194/// ];
195/// ```
196///
197/// The resulting bindings now look something like this:
198///
199/// ```rust
200/// #[inline]
201/// pub unsafe fn GetTickCount() -> u32 {
202///     windows_link::link!("kernel32.dll" "system" fn GetTickCount() -> u32);
203///     unsafe { GetTickCount() }
204/// }
205/// #[inline]
206/// pub unsafe fn Sleep(dwmilliseconds: u32) {
207///     windows_link::link!("kernel32.dll" "system" fn Sleep(dwmilliseconds : u32));
208///     unsafe { Sleep(dwmilliseconds) }
209/// }
210/// ```
211///
212/// # `--no-allow`
213///
214/// The bindings also include an allow attribute that covers various common warnings inherent in
215/// generated bindings.
216///
217/// ```rust
218/// #![allow(
219///     non_snake_case,
220///     non_upper_case_globals,
221///     non_camel_case_types,
222///     dead_code,
223///     clippy::all
224/// )]
225/// ```
226///
227/// You can prevent this from being generated if you prefer to manage this yourself with the `--no-allow`
228/// argument.
229///
230/// # `--sys`
231///
232/// The `--sys` argument instruct the `bindgen` function to generate raw, sometimes called sys-style Rust
233/// bindings.
234///
235/// ```rust
236/// let args = [
237///     "--out",
238///     "src/bindings.rs",
239///     "--flat",
240///     "--sys",
241///     "--filter",
242///     "GetTickCount",
243///     "Sleep",
244/// ];
245/// ```
246///
247/// The resulting bindings now look something like this:
248///
249/// ```rust
250/// windows_link::link!("kernel32.dll" "system" fn GetTickCount() -> u32);
251/// windows_link::link!("kernel32.dll" "system" fn Sleep(dwmilliseconds : u32));
252/// ```
253///
254/// You'll notice that the bindings are simpler as there's no wrapper functions and other
255/// conveniences. You just need to add a dependency on the tiny [windows-link](https://crates.io/crates/windows-link) crate and you're all set.
256///
257#[track_caller]
258#[must_use]
259pub fn bindgen<I, S>(args: I) -> Warnings
260where
261    I: IntoIterator<Item = S>,
262    S: AsRef<str>,
263{
264    let args = expand_args(args);
265    let mut kind = ArgKind::None;
266    let mut input = Vec::new();
267    let mut include = Vec::new();
268    let mut exclude = Vec::new();
269    let mut references = Vec::new();
270    let mut derive = Vec::new();
271
272    let mut flat = false;
273    let mut no_allow = false;
274    let mut no_comment = false;
275    let mut no_deps = false;
276    let mut no_toml = false;
277    let mut package = false;
278    let mut implement = false;
279    let mut specific_deps = false;
280    let mut rustfmt = String::new();
281    let mut output = String::new();
282    let mut sys = false;
283    let mut sys_fn_ptrs = false;
284    let mut sys_fn_extern = false;
285    let mut link = String::new();
286    let mut index = false;
287
288    for arg in &args {
289        if arg.starts_with('-') {
290            kind = ArgKind::None;
291        }
292
293        match kind {
294            ArgKind::None => match arg.as_str() {
295                "--in" => kind = ArgKind::Input,
296                "--out" => kind = ArgKind::Output,
297                "--filter" => kind = ArgKind::Filter,
298                "--rustfmt" => kind = ArgKind::Rustfmt,
299                "--reference" => kind = ArgKind::Reference,
300                "--derive" => kind = ArgKind::Derive,
301                "--flat" => flat = true,
302                "--no-allow" => no_allow = true,
303                "--no-comment" => no_comment = true,
304                "--no-deps" => no_deps = true,
305                "--no-toml" => no_toml = true,
306                "--package" => package = true,
307                "--sys" => sys = true,
308                "--sys-fn-ptrs" => sys_fn_ptrs = true,
309                "--sys-fn-extern" => sys_fn_extern = true,
310                "--implement" => implement = true,
311                "--specific-deps" => specific_deps = true,
312                "--link" => kind = ArgKind::Link,
313                "--index" => index = true,
314                _ => panic!("invalid option `{arg}`"),
315            },
316            ArgKind::Output => {
317                if output.is_empty() {
318                    output = arg.to_string();
319                } else {
320                    panic!("exactly one `--out` is required");
321                }
322            }
323            ArgKind::Input => input.push(arg.as_str()),
324            ArgKind::Filter => {
325                if let Some(rest) = arg.strip_prefix('!') {
326                    exclude.push(rest);
327                } else {
328                    include.push(arg.as_str());
329                }
330            }
331            ArgKind::Reference => {
332                references.push(ReferenceStage::parse(arg));
333            }
334            ArgKind::Derive => {
335                derive.push(arg.as_str());
336            }
337            ArgKind::Rustfmt => rustfmt = arg.to_string(),
338            ArgKind::Link => link = arg.to_string(),
339        }
340    }
341
342    if link.is_empty() {
343        if sys || specific_deps {
344            link = "windows_link".to_string();
345        } else {
346            link = "windows_core".to_string();
347        }
348    }
349
350    if package && flat {
351        panic!("cannot combine `--package` and `--flat`");
352    }
353
354    if input.is_empty() {
355        input.push("default");
356    };
357
358    if output.is_empty() {
359        panic!("exactly one `--out` is required");
360    };
361
362    // This isn't strictly necessary but avoids a common newbie pitfall where all metadata
363    // would be generated when building a component for a specific API.
364    if include.is_empty() {
365        panic!("at least one `--filter` required");
366    }
367
368    let reader = Reader::new(expand_input(&input));
369
370    if !sys && !no_deps {
371        if reader.contains_key("Windows.Foundation") {
372            references.insert(
373                0,
374                ReferenceStage::parse("windows_collections,flat,Windows.Foundation.Collections"),
375            );
376            references.insert(
377                0,
378                ReferenceStage::parse("windows_numerics,flat,Windows.Foundation.Numerics"),
379            );
380            references.insert(
381                0,
382                ReferenceStage::parse("windows_future,flat,Windows.Foundation.Async*"),
383            );
384            references.insert(
385                0,
386                ReferenceStage::parse("windows_future,flat,Windows.Foundation.IAsync*"),
387            );
388        }
389
390        if reader.contains_key("Windows.Win32.Foundation") {
391            if specific_deps {
392                references.insert(
393                    0,
394                    ReferenceStage::parse(
395                        "windows_result,flat,Windows.Win32.Foundation.WIN32_ERROR",
396                    ),
397                );
398                references.insert(
399                    0,
400                    ReferenceStage::parse("windows_result,flat,Windows.Win32.Foundation.NTSTATUS"),
401                );
402                references.insert(
403                    0,
404                    ReferenceStage::parse(
405                        "windows_result,flat,Windows.Win32.System.Rpc.RPC_STATUS",
406                    ),
407                );
408            } else {
409                references.insert(
410                    0,
411                    ReferenceStage::parse("windows_core,flat,Windows.Win32.Foundation.WIN32_ERROR"),
412                );
413                references.insert(
414                    0,
415                    ReferenceStage::parse("windows_core,flat,Windows.Win32.Foundation.NTSTATUS"),
416                );
417                references.insert(
418                    0,
419                    ReferenceStage::parse("windows_core,flat,Windows.Win32.System.Rpc.RPC_STATUS"),
420                );
421            }
422        }
423    }
424
425    let filter = Filter::new(&reader, &include, &exclude);
426    let references = References::new(&reader, references);
427    let types = TypeMap::filter(&reader, &filter, &references);
428    let derive = Derive::new(&reader, &types, &derive);
429    let warnings = WarningBuilder::default();
430
431    let config = Config {
432        types: &types,
433        flat,
434        references: &references,
435        derive: &derive,
436        no_allow,
437        no_comment,
438        no_deps,
439        no_toml,
440        package,
441        rustfmt: &rustfmt,
442        output: &output,
443        sys,
444        sys_fn_ptrs,
445        sys_fn_extern,
446        implement,
447        specific_deps,
448        link: &link,
449        warnings: &warnings,
450        namespace: "",
451    };
452
453    let tree = TypeTree::new(&types);
454
455    config.write(tree);
456
457    if index {
458        index::write(&types, &format!("{output}/features.json"));
459    }
460
461    warnings.build()
462}
463
464enum ArgKind {
465    None,
466    Input,
467    Output,
468    Filter,
469    Rustfmt,
470    Reference,
471    Derive,
472    Link,
473}
474
475#[track_caller]
476fn expand_args<I, S>(args: I) -> Vec<String>
477where
478    I: IntoIterator<Item = S>,
479    S: AsRef<str>,
480{
481    // This function is needed to avoid a recursion limit in the Rust compiler.
482    #[track_caller]
483    fn from_string(result: &mut Vec<String>, value: &str) {
484        expand_args(result, value.split_whitespace().map(|arg| arg.to_string()))
485    }
486
487    #[track_caller]
488    fn expand_args<I, S>(result: &mut Vec<String>, args: I)
489    where
490        I: IntoIterator<Item = S>,
491        S: AsRef<str>,
492    {
493        let mut expand = false;
494
495        for arg in args.into_iter().map(|arg| arg.as_ref().to_string()) {
496            if arg.starts_with('-') {
497                expand = false;
498            }
499            if expand {
500                for args in io::read_file_lines(&arg) {
501                    if !args.starts_with("//") {
502                        from_string(result, &args);
503                    }
504                }
505            } else if arg == "--etc" {
506                expand = true;
507            } else {
508                result.push(arg);
509            }
510        }
511    }
512
513    let mut result = vec![];
514    expand_args(&mut result, args);
515    result
516}
517
518#[track_caller]
519fn expand_input(input: &[&str]) -> Vec<File> {
520    #[track_caller]
521    fn expand_input(result: &mut Vec<String>, input: &str) {
522        let path = std::path::Path::new(input);
523
524        if path.is_dir() {
525            let prev_len = result.len();
526
527            for path in path
528                .read_dir()
529                .unwrap_or_else(|_| panic!("failed to read directory `{input}`"))
530                .flatten()
531                .map(|entry| entry.path())
532            {
533                if path.is_file()
534                    && path
535                        .extension()
536                        .is_some_and(|extension| extension.eq_ignore_ascii_case("winmd"))
537                {
538                    result.push(path.to_string_lossy().to_string());
539                }
540            }
541
542            if result.len() == prev_len {
543                panic!("failed to find .winmd files in directory `{input}`");
544            }
545        } else {
546            result.push(input.to_string());
547        }
548    }
549
550    let mut paths = vec![];
551    let mut use_default = false;
552
553    for input in input {
554        if *input == "default" {
555            use_default = true;
556        } else {
557            expand_input(&mut paths, input);
558        }
559    }
560
561    let mut input = vec![];
562
563    if use_default {
564        input = [
565            std::include_bytes!("../default/Windows.winmd").to_vec(),
566            std::include_bytes!("../default/Windows.Win32.winmd").to_vec(),
567            std::include_bytes!("../default/Windows.Wdk.winmd").to_vec(),
568        ]
569        .into_iter()
570        .map(|bytes| File::new(bytes).unwrap())
571        .collect();
572    }
573
574    for path in &paths {
575        let Ok(bytes) = std::fs::read(path) else {
576            panic!("failed to read binary file `{path}`");
577        };
578
579        let Some(file) = File::new(bytes) else {
580            panic!("failed to read .winmd format `{path}`");
581        };
582
583        input.push(file);
584    }
585
586    input
587}
588
589fn namespace_starts_with(namespace: &str, starts_with: &str) -> bool {
590    namespace.starts_with(starts_with)
591        && (namespace.len() == starts_with.len()
592            || namespace.as_bytes().get(starts_with.len()) == Some(&b'.'))
593}
594
595#[cfg(test)]
596mod tests {
597    use super::*;
598
599    #[test]
600    fn test_starts_with() {
601        assert!(namespace_starts_with(
602            "Windows.Win32.Graphics.Direct3D11on12",
603            "Windows.Win32.Graphics.Direct3D11on12"
604        ));
605        assert!(namespace_starts_with(
606            "Windows.Win32.Graphics.Direct3D11on12",
607            "Windows.Win32.Graphics"
608        ));
609        assert!(!namespace_starts_with(
610            "Windows.Win32.Graphics.Direct3D11on12",
611            "Windows.Win32.Graphics.Direct3D11"
612        ));
613        assert!(!namespace_starts_with(
614            "Windows.Win32.Graphics.Direct3D",
615            "Windows.Win32.Graphics.Direct3D11"
616        ));
617    }
618}