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[](./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