• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Contributing to `bindgen`
2
3Hi! We'd love to have your contributions! If you want help or mentorship, reach
4out to us in a GitHub issue, or stop by
5[#rust on chat.mozilla.org](https://chat.mozilla.org/#/room/#rust:mozilla.org)
6and introduce yourself.
7
8<!-- START doctoc generated TOC please keep comment here to allow auto update -->
9<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
10
11- [Code of Conduct](#code-of-conduct)
12- [Filing an Issue](#filing-an-issue)
13- [Looking to Start Contributing to `bindgen`?](#looking-to-start-contributing-to-bindgen)
14- [Prerequisites](#prerequisites)
15  - [`rustfmt` / `cargo fmt`](#rustfmt--cargo-fmt)
16- [Building](#building)
17- [Testing](#testing)
18  - [Overview](#overview)
19  - [Testing Bindings Generation](#testing-bindings-generation)
20  - [Testing Generated Bindings](#testing-generated-bindings)
21  - [Testing a Single Header's Bindings Generation and Compiling its Bindings](#testing-a-single-headers-bindings-generation-and-compiling-its-bindings)
22  - [Authoring New Tests](#authoring-new-tests)
23  - [Test Expectations and `libclang` Versions](#test-expectations-and-libclang-versions)
24  - [Integration Tests](#integration-tests)
25  - [Fuzzing `bindgen` with `csmith`](#fuzzing-bindgen-with-csmith)
26  - [Property tests for `bindgen` with `quickchecking`](#property-tests-for-bindgen-with-quickchecking)
27- [Code Overview](#code-overview)
28  - [Implementing new options using `syn`](#implementing-new-options-using-syn)
29- [Pull Requests and Code Reviews](#pull-requests-and-code-reviews)
30- [Generating Graphviz Dot Files](#generating-graphviz-dot-files)
31- [Debug Logging](#debug-logging)
32- [Using `creduce` to Minimize Test Cases](#using-creduce-to-minimize-test-cases)
33  - [Getting `creduce`](#getting-creduce)
34  - [Isolating Your Test Case](#isolating-your-test-case)
35  - [Writing a Predicate Script](#writing-a-predicate-script)
36- [Cutting a new bindgen release](#cutting-a-new-bindgen-release)
37  - [Updating the changelog](#updating-the-changelog)
38  - [Merge to `main`](#merge-to-main)
39  - [Tag and publish](#tag-and-publish)
40  - [Create a new release on Github](#create-a-new-release-on-github)
41  - [What to do if a Github release fails](#what-to-do-if-a-github-release-fails)
42  - [Create a new crates.io release](#create-a-new-cratesio-release)
43
44<!-- END doctoc generated TOC please keep comment here to allow auto update -->
45
46## Code of Conduct
47
48We abide by the [Rust Code of Conduct][coc] and ask that you do as well.
49
50[coc]: https://www.rust-lang.org/policies/code-of-conduct
51
52## Filing an Issue
53
54Think you've found a bug? File an issue! To help us understand and reproduce the
55issue, provide us with:
56
57* A (preferably reduced) C/C++ header file that reproduces the issue
58* The `bindgen` flags used to reproduce the issue with the header file
59* The expected `bindgen` output
60* The actual `bindgen` output
61* The [debugging logs](#debug-logging) generated when running `bindgen` on this testcase
62
63## Looking to Start Contributing to `bindgen`?
64
65* [Issues labeled "easy"](https://github.com/rust-lang/rust-bindgen/issues?q=is%3Aopen+is%3Aissue+label%3AE-easy)
66* [Issues labeled "less easy"](https://github.com/rust-lang/rust-bindgen/issues?q=is%3Aopen+is%3Aissue+label%3AE-less-easy)
67* [Issues labeled "help wanted"](https://github.com/rust-lang/rust-bindgen/labels/help%20wanted)
68* Still can't find something to work on? [Drop a comment here](https://github.com/rust-lang/rust-bindgen/issues/747)
69
70## Prerequisites
71
72### `rustfmt` / `cargo fmt`
73
74We use `nightly` channel for `rustfmt`,
75so please set the appropriate setting in your editor/IDE for that.
76
77For rust-analyzer, you can set `rustfmt.extraArgs = ['+nightly']`.
78
79To check via command line, you can run `cargo +nightly fmt --check`.
80
81## Building
82
83To build the `bindgen` library and the `bindgen` executable:
84
85```
86$ cargo build
87```
88
89If you installed multiple versions of llvm, it may not be able to locate the
90latest version of libclang. In that case, you may want to either uninstall other
91versions of llvm, or specify the path of the desired libclang explicitly:
92
93```
94$ export LIBCLANG_PATH=path/to/clang-9.0/lib
95```
96
97## Testing
98
99### Overview
100
101Input C/C++ test headers reside in the `bindgen-tests/tests/headers` directory. Expected
102output Rust bindings live in `bindgen-tests/tests/expectations/tests`. For example,
103`bindgen-tests/tests/headers/my_header.h`'s expected generated Rust bindings would be
104`bindgen-tests/tests/expectations/tests/my_header.rs`.
105
106There are also some integration tests in the `./bindgen-integration` crate, which uses `bindgen` to
107generate bindings to some C++ code, and then uses the bindings, asserting that
108values are what we expect them to be, both on the Rust and C++ side.
109
110The generated and expected bindings are formatted with [prettyplease] before they are
111compared. It is a default (but optional) dependency of `bindgen`,
112so be sure to keep that in mind
113(if you built `bindgen` with the `--no-default-features` option of Cargo).
114Note also that `rustfmt` formatting is disabled for the `bindgen-tests/tests/expectations/`
115directory tree, which helps avoid failing ui tests.
116
117Note: running `cargo test` from the root directory of `bindgen`'s repository does not
118automatically test the generated bindings or run the integration tests.
119These steps must be performed manually when needed.
120
121
122### Testing Bindings Generation
123
124To regenerate bindings from the corpus of test headers in `bindgen-tests/tests/headers` and
125compare them against the expected bindings in `bindgen-tests/tests/expectations/tests`, run:
126
127```
128$ cargo test
129```
130
131As long as you aren't making any changes to `bindgen`'s output, running this
132should be sufficient to test your local modifications.
133
134You may set the `BINDGEN_OVERWRITE_EXPECTED` environment variable to overwrite
135the expected bindings with `bindgen`'s current output:
136
137```
138$ BINDGEN_OVERWRITE_EXPECTED=1 cargo test
139```
140
141If you set the BINDGEN_TESTS_DIFFTOOL environment variable, `cargo test` will
142execute $BINDGEN_TESTS_DIFFTOOL /path/of/expected/output /path/of/actual/output
143when the expected output differs from the actual output. You can use this to
144hand check differences by setting it to e.g. "meld" (assuming you have meld
145installed).
146
147If you're not changing command line arguments, you may want to set
148`BINDGEN_DISABLE_ROUNDTRIP_TEST` to avoid a lot of tests for round-tripping of
149those.
150
151### Testing Generated Bindings
152
153If your local changes are introducing expected modifications in the
154`bindgen-tests/tests/expectations/tests/*` bindings files, then you should test that the
155generated bindings files still compile, and that their struct layout tests still
156pass. Also, run the integration tests (see below).
157
158You can do this with these commands:
159
160```
161$ cd bindgen-tests/tests/expectations
162$ cargo test
163```
164
165### Testing a Single Header's Bindings Generation and Compiling its Bindings
166
167Note: You will need to install [Graphviz](https://graphviz.org/) since that
168is a dependency for running `test-one.sh`.
169
170Sometimes it's useful to work with one test header from start (generating
171bindings for it) to finish (compiling the bindings and running their layout
172tests). This can be done with the `bindgen-tests/tests/test-one.sh` script. It supports fuzzy
173searching for test headers. For example, to test
174`tests/headers/what_is_going_on.hpp`, execute this command:
175
176```
177$ ./bindgen-tests/tests/test-one.sh going
178```
179
180Note that `test-one.sh` does not recompile `bindgen`, so if you change the code,
181you'll need to rebuild it before running the script again.
182
183### Authoring New Tests
184
185To add a new test header to the suite, simply put it in the `bindgen-tests/tests/headers`
186directory. Next, run `bindgen` to generate the initial expected output Rust
187bindings. Put those in `bindgen-tests/tests/expectations/tests`.
188
189If your new test requires certain flags to be passed to `bindgen`, you can
190specify them at the top of the test header, with a comment like this:
191
192`new_test_header.hpp`:
193
194```c
195// bindgen-flags: --enable-cxx-namespaces -- -std=c++14
196```
197
198Then verify the new Rust bindings compile and pass their layout tests:
199
200```
201$ cd bindgen-tests/tests/expectations
202$ cargo test new_test_header
203```
204
205### Test Expectations and `libclang` Versions
206
207If a test generates different bindings across different `libclang` versions (for
208example, because we take advantage of better/newer APIs when possible), then you
209can add multiple test expectations, one for each supported `libclang`
210version. Instead of having a single `bindgen-tests/tests/expectations/tests/my_test.rs` file,
211add each of:
212
213* `bindgen-tests/tests/expectations/tests/libclang-16/my_test.rs`
214* `bindgen-tests/tests/expectations/tests/libclang-9/my_test.rs`
215
216If you need to update the test expectations for a test file that generates
217different bindings for different `libclang` versions, you *don't* need to have
218many versions of `libclang` installed locally. Just make a work-in-progress pull
219request, and then when CI fails, it will log a diff of the
220expectations. Use the diff to patch the appropriate expectation file locally and
221then update your pull request.
222
223Usually, `bindgen`'s test runner can infer which version of `libclang` you
224have. If for some reason it can't, you can force a specific `libclang` version
225to check the bindings against with a cargo feature:
226
227```
228$ cargo test --features __testing_only_libclang_$VERSION
229```
230
231depending on which version of `libclang` you have installed.
232
233### Integration Tests
234
235The `./bindgen-integration` crate uses `bindgen` to
236generate bindings to some C++ code, and then uses the bindings, asserting that
237values are what we expect them to be, both on the Rust and C++ side.
238
239To run the integration tests, issue the following:
240
241```
242$ cd bindgen-integration
243$ cargo test
244```
245
246### Fuzzing `bindgen` with `csmith`
247
248We <3 finding hidden bugs and the people who help us find them! One way to help
249uncover hidden bugs is by running `csmith` to generate random headers to test
250`bindgen` against.
251
252See [./csmith-fuzzing/README.md](./csmith-fuzzing/README.md) for details.
253
254### Property tests for `bindgen` with `quickchecking`
255
256The `tests/quickchecking` crate generates property tests for `bindgen`.
257From the crate's directory you can run the tests with `cargo run`. For details
258on additional configuration including how to preserve / inspect the generated
259property tests, see
260[./tests/quickchecking/README.md](./tests/quickchecking/README.md).
261
262## Code Overview
263
264`bindgen` takes C and C++ header files as input and generates corresponding Rust
265`#[repr(C)]` type definitions and `extern` foreign function declarations.
266
267First, we use `libclang` to parse the input headers. See `src/clang.rs` for our
268Rust-y wrappers over the raw C `libclang` API that the `clang-sys` crate
269exposes. We walk over `libclang`'s AST and construct our own internal
270representation (IR).  The `ir` module and submodules (`src/ir/*`) contain the IR
271type definitions and `libclang` AST into IR parsing code.
272
273The umbrella IR type is the `Item`. It contains various nested `enum`s that let
274us drill down and get more specific about the kind of construct that we're
275looking at. Here is a summary of the IR types and their relationships:
276
277* `Item` contains:
278    * An `ItemId` to uniquely identify it.
279    * An `ItemKind`, which is one of:
280        * A `Module`, which is originally a C++ namespace and becomes a Rust
281          module. It contains the set of `ItemId`s of `Item`s that are defined
282          within it.
283        * A `Type`, which contains:
284            * A `Layout`, describing the type's size and alignment.
285            * A `TypeKind`, which is one of:
286                * Some integer type.
287                * Some float type.
288                * A `Pointer` to another type.
289                * A function pointer type, with `ItemId`s of its parameter types
290                  and return type.
291                * An `Alias` to another type (`typedef` or `using X = ...`).
292                * A fixed size `Array` of `n` elements of another type.
293                * A `Comp` compound type, which is either a `struct`, `class`,
294                  or `union`. This is potentially a template definition.
295                * A `TemplateInstantiation` referencing some template definition
296                  and a set of template argument types.
297                * Etc...
298        * A `Function`, which contains:
299            * An ABI
300            * A mangled name
301            * a `FunctionKind`, which describes whether this function is a plain
302              function, method, static method, constructor, destructor, etc.
303            * The `ItemId` of its function pointer type.
304        * A `Var` representing a static variable or `#define` constant, which
305          contains:
306            * Its type's `ItemId`
307            * Optionally, a mangled name
308            * Optionally, a value
309    * An optional `clang::SourceLocation` that holds the first source code
310      location where the `Item` was encountered.
311
312The IR forms a graph of interconnected and inter-referencing types and
313functions. The `ir::traversal` module provides IR graph traversal
314infrastructure: edge kind definitions (base member vs field type vs function
315parameter, etc...), the `Trace` trait to enumerate an IR thing's outgoing edges,
316various traversal types.
317
318After constructing the IR, we run a series of analyses on it. These analyses do
319everything from allocate logical bitfields into physical units, compute for
320which types we can `#[derive(Debug)]`, to determining which implicit template
321parameters a given type uses. The analyses are defined in
322`src/ir/analysis/*`. They are implemented as fixed-point algorithms, using the
323`ir::analysis::MonotoneFramework` trait.
324
325The final phase is generating Rust source text from the analyzed IR, and it is
326defined in `src/codegen/*`. We use the `quote` crate, which provides the `quote!
327{ ... }` macro for quasi-quoting Rust forms. Some options that affect the
328generated Rust code are implemented using the [`syn`](https://docs.rs/syn) crate.
329
330### Implementing new options using `syn`
331
332If a new option can be implemented using the `syn` crate it should be added to
333the `codegen::postprocessing` module by following these steps:
334
335- Introduce a new field to `BindgenOptions` for the option.
336- Write a free function inside `codegen::postprocessing` implementing the
337  option. This function with the same name of the `BindgenOptions` field.
338- Add a new value to the `codegen::postprocessing::PASSES` for the option using
339  the `pass!` macro.
340
341## Pull Requests and Code Reviews
342
343Ensure that each commit stands alone, and passes tests. This enables better `git
344bisect`ing when needed. If your commits do not stand on their own, then rebase
345them on top of the latest main and squash them into a single commit.
346
347All pull requests undergo code review before merging. To request review, comment
348`r? @github_username_of_reviewer`. They we will respond with `r+` to approve the
349pull request, or may leave feedback and request changes to the pull request. Any
350changes should be squashed into the original commit.
351
352Unsure who to ask for review? Ask any of:
353
354* `@emilio`
355* `@pvdrz`
356
357More resources:
358
359* [Servo's GitHub Workflow](https://github.com/servo/servo/wiki/Github-workflow)
360* [Beginner's Guide to Rebasing and Squashing](https://github.com/servo/servo/wiki/Beginner's-guide-to-rebasing-and-squashing)
361
362## Generating Graphviz Dot Files
363
364We can generate [Graphviz](http://graphviz.org/pdf/dotguide.pdf) dot files from
365our internal representation of a C/C++ input header, and then you can create a
366PNG or PDF from it with Graphviz's `dot` program. This is very useful when
367debugging bindgen!
368
369First, make sure you have Graphviz and `dot` installed:
370
371```
372$ brew install graphviz         # OS X
373$ sudo dnf install graphviz     # Fedora
374$ # Etc...
375```
376
377Then, use the `--emit-ir-graphviz` flag to generate a `dot` file from our IR:
378
379```
380$ cargo run -- example.hpp --emit-ir-graphviz output.dot
381```
382
383Finally, convert the `dot` file to an image:
384
385```
386$ dot -Tpng output.dot -o output.png
387```
388
389The final result will look something like this:
390
391[![An example graphviz rendering of our IR](./example-graphviz-ir.png)](./example-graphviz-ir.png)
392
393## Debug Logging
394
395To help debug what `bindgen` is doing, you can define the environment variable
396`RUST_LOG=bindgen` to get a bunch of debugging log spew.
397
398```
399$ RUST_LOG=bindgen ./target/debug/bindgen [flags...] ~/path/to/some/header.h
400```
401
402This logging can also be used when debugging failing tests:
403
404```
405$ RUST_LOG=bindgen cargo test
406```
407
408## Using `creduce` to Minimize Test Cases
409
410If you find a test case that triggers an unexpected panic in `bindgen`, causes
411`bindgen` to emit bindings that won't compile, define structs with the wrong
412size/alignment, or results in any other kind of incorrectness, then using
413`creduce` can help reduce the test case to a minimal one that still exhibits
414that same bad behavior.
415
416***Reduced test cases are SUPER helpful when filing bug reports!***
417
418### Getting `creduce`
419
420Often, you can install `creduce` from your OS's package manager:
421
422```
423$ sudo apt install creduce
424$ brew install creduce
425$ # Etc...
426```
427
428Otherwise, follow [these instructions](https://github.com/csmith-project/creduce/blob/master/INSTALL.md) for building and/or installing `creduce`.
429
430Running `creduce` requires two things:
431
4321. Your isolated test case, and
433
4342. A script to act as a predicate script describing whether the behavior you're
435   trying to isolate occurred.
436
437With those two things in hand, running `creduce` looks like this:
438
439    $ creduce ./predicate.sh ./isolated-test-case.h
440
441### Isolating Your Test Case
442
443If you're using `bindgen` as a command line tool, pass
444`--dump-preprocessed-input` flag.
445
446If you're using `bindgen` as a Rust library, invoke the
447`bindgen::Builder::dump_preprocessed_input` method where you call
448`bindgen::Builder::generate`.
449
450Afterwards, there should be a `__bindgen.i` or `__bindgen.ii` file containing
451the combined and preprocessed input headers, which is usable as an isolated,
452standalone test case.
453
454### Writing a Predicate Script
455
456Writing a `predicate.sh` script for a `bindgen` test case is straightforward. We
457already have a general purpose predicate script that you can use, you just have
458to wrap and configure it.
459
460```bash
461#!/usr/bin/env bash
462
463# Exit the script with a nonzero exit code if:
464# * any individual command finishes with a nonzero exit code, or
465# * we access any undefined variable.
466set -eu
467
468# Invoke the general purpose predicate script that comes in the
469# `bindgen` repository.
470#
471# You'll need to replace `--whatever-flags` with things that are specific to the
472# incorrectness you're trying to pin down. See below for details.
473path/to/rust-bindgen/csmith-fuzzing/predicate.py \
474    --whatever-flags \
475    ./isolated-test-case.h
476```
477
478When hunting down a particular panic emanating from inside `bindgen`, you can
479invoke `predicate.py` like this:
480
481```bash
482path/to/rust-bindgen/csmith-fuzzing/predicate.py \
483    --expect-bindgen-fail \
484    --bindgen-grep "thread main panicked at '<insert panic message here>'" \
485    ./isolated-test-case.h
486```
487
488Alternatively, when hunting down a bad `#[derive(Eq)]` that is causing `rustc`
489to fail to compile `bindgen`'s emitted bindings, you can invoke `predicate.py`
490like this:
491
492```bash
493# the rustc-grep argument expects a regex, thus escape where necessary
494path/to/rust-bindgen/csmith-fuzzing/predicate.py \
495    --bindings-grep NameOfTheStructThatIsErroneouslyDerivingEq \
496    --expect-compile-fail \
497    --rustc-grep 'error\[E0277\]: the trait bound `f64: std::cmp::Eq` is not satisfied' \
498    ./isolated-test-case.h
499```
500
501Or, when minimizing a failing layout test in the compiled bindings, you can
502invoke `predicate.py` like this:
503
504```bash
505path/to/rust-bindgen/csmith-fuzzing/predicate.py \
506    --bindings-grep MyStruct \
507    --expect-layout-tests-fail \
508    --layout-tests-grep "thread 'bindgen_test_layout_MyStruct' panicked" \
509    ./isolated-test-case.h
510```
511
512For details on all the flags that you can pass to `predicate.py`, run:
513
514```
515$ path/to/rust-bindgen/csmith-fuzzing/predicate.py --help
516```
517
518And you can always write your own, arbitrary predicate script if you prefer.
519(Although, maybe we should add extra functionality to `predicate.py` -- file an
520issue if you think so!)
521
522`creduce` is *really* helpful and can cut hundreds of thousands of lines of test
523case down to 5 lines.
524
525Happy bug hunting and test case reducing!
526
527[More information on using `creduce`.](https://embed.cs.utah.edu/creduce/using/)
528
529## Cutting a new bindgen release
530
531To cut a release, the following needs to happen:
532
533### Updating the changelog
534
535Update the CHANGELOG.md file with the changes from the last release. Something
536like the following is a useful way to check what has landed:
537
538 ```
539 $ git log --oneline v0.62.0..HEAD
540 ```
541
542Also worth checking the [next-release
543tag](https://github.com/rust-lang/rust-bindgen/pulls?q=is%3Apr+label%3Anext-release).
544It is very important that you do not rename the `Unreleased` section of the
545changelog as this will be done automatically using `cargo release` on a further
546step.
547
548### Merge to `main`
549
550For regular releases, the changes above should end up in `main` before
551publishing. For dot-releases of an old version (e.g., cherry-picking an
552important fix) you can skip this.
553
554### Tag and publish
555
556Once you're in `main`. Remember to install `doctoc` by running:
557```
558npm install doctoc
559```
560
561And then run:
562```
563cargo release [patch|minor] --no-publish --execute
564```
565
566This does the following:
567
568- Bump the version.
569- Turn the `Unreleased` section of the changelog into the section for the version being released.
570- Update the table of contents of the changelog using `doctoc`.
571- Tag (`git tag`) the HEAD commit
572- Push (`git push`) to GitHub
573
574The `patch` and `minor` refer to semver concepts:
575
576- `patch` would bump __v0.68.1__ to __v0.68.2__
577- `minor` would bump __v0.68.2__ to __v0.69.0__
578
579> NOTE:
580> We use the `--no-publish` so that the crates are only published after the release is complete.
581> This is automatic, provided the release CI job is successful.
582
583### Create a new release on Github
584
585The release is automated with the help of `.github/workflows/release.yml`,
586and will only be created...
587
588- when a Git tag is pushed
589- when all tests succeed
590
591While the tests are still running,
592a draft GitHub release will be created,
593to avoid notifying watchers of the repo should a CI step fail.
594
595If everything succeeds,
596tarballs containing bindgen cli executables for Linux and MacOS
597(both for x86 and Arm) will be created.
598See `[workspace.metadata.dist]` section in Cargo.toml for the configuration.
599
600To update the release configuration,
601when a new cargo-dist is available:
602
603```
604cargo dist init # from "cargo install cargo-dist"
605```
606
607### What to do if a Github release fails
608
609If the release process fails after you run `cargo release`, you can manually
610delete the tag and release from Github. Also remember to delete the tag locally
611by running `git tag -d`. Once all the extra changes are in the `main` branch,
612you can trigger a release by creating a new tag using `git tag` and push it
613using `git push --tag`.
614
615### Create a new crates.io release
616
617Go to [the Publish
618workflow](https://github.com/rust-lang/rust-bindgen/actions/workflows/publish.yml)
619and run a new workflow using the "Run Workflow" button.
620
621Remember that crates.io releases cannot be deleted!
622
623[prettyplease]: https://github.com/dtolnay/prettyplease
624