diff options
author | Nick Fitzgerald <fitzgen@gmail.com> | 2017-04-26 17:39:10 -0400 |
---|---|---|
committer | Nick Fitzgerald <fitzgen@gmail.com> | 2017-04-26 18:05:24 -0400 |
commit | 2d20e7e8ef602d8ecd5a87c0492133296595db74 (patch) | |
tree | f59c22e9c4b59a6aa1751b31f559abeb6ed6f91f | |
parent | e6fa7be6aaccf34d6d2ae9335d357717a16b1839 (diff) |
Add the `bindgen` Users Guide
This commit adds a Users Guide, built with [mdbook][] (the same tool used
by *The Rust Book*).
It moves all the user documentation from the README.md into the Users Guide. It
leaves the README.md as a splash page featuring an example of what `bindgen` is
and does, a link to the Users Guide, a link to the docs.rs/bindgen API reference
documentation, and a link to CONTRIBUTING.md.
It ensures that the Users Guide builds with `mdbook`, and all code snippets
within it build as doctests and pass, on every CI run.
Finally, it builds and uploads the Users Guide every time CI passes on the
master branch.
[mdbook]: https://github.com/azerupi/mdBook
-rw-r--r-- | .travis.yml | 8 | ||||
-rw-r--r-- | README.md | 234 | ||||
-rw-r--r-- | book/.gitignore | 1 | ||||
-rw-r--r-- | book/book.toml | 3 | ||||
-rw-r--r-- | book/src/SUMMARY.md | 20 | ||||
-rw-r--r-- | book/src/blacklisting.md | 26 | ||||
-rw-r--r-- | book/src/chapter_1.md | 1 | ||||
-rw-r--r-- | book/src/command-line-usage.md | 27 | ||||
-rw-r--r-- | book/src/cpp.md | 27 | ||||
-rw-r--r-- | book/src/customizing-generated-bindings.md | 28 | ||||
-rw-r--r-- | book/src/introduction.md | 34 | ||||
-rw-r--r-- | book/src/library-usage.md | 22 | ||||
-rw-r--r-- | book/src/nocopy.md | 20 | ||||
-rw-r--r-- | book/src/opaque.md | 26 | ||||
-rw-r--r-- | book/src/replacing-types.md | 27 | ||||
-rw-r--r-- | book/src/requirements.md | 67 | ||||
-rw-r--r-- | book/src/tutorial-0.md | 12 | ||||
-rw-r--r-- | book/src/tutorial-1.md | 9 | ||||
-rw-r--r-- | book/src/tutorial-2.md | 20 | ||||
-rw-r--r-- | book/src/tutorial-3.md | 58 | ||||
-rw-r--r-- | book/src/tutorial-4.md | 57 | ||||
-rw-r--r-- | book/src/tutorial-5.md | 169 | ||||
-rw-r--r-- | book/src/tutorial-6.md | 13 | ||||
-rw-r--r-- | book/src/whitelisting.md | 31 | ||||
-rwxr-xr-x | ci/deploy-book.sh | 33 | ||||
-rwxr-xr-x | ci/test-book.sh | 10 |
26 files changed, 775 insertions, 208 deletions
diff --git a/.travis.yml b/.travis.yml index 2def8e3a..f66c7d36 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,6 +38,14 @@ script: # - ./ci/assert-rustfmt.sh - BINDGEN_FEATURES="$BINDGEN_FEATURES" ./ci/assert-docs.sh - BINDGEN_FEATURES="$BINDGEN_FEATURES" ./ci/test.sh + - ./ci/test-book.sh + +after_success: + - test "$TRAVIS_PULL_REQUEST" == "false" && + test "$TRAVIS_BRANCH" == "master" && + test "$BINDGEN_FEATURES" == "" && + test "$LLVM_VERSION" == "3.9.0" && + ./ci/deploy-book.sh notifications: webhooks: http://build.servo.org:54856/travis @@ -1,227 +1,45 @@ # `bindgen` -Automatically generates Rust FFI bindings to C and C++ libraries. +**`bindgen` automatically generates Rust FFI bindings to C and C++ libraries.** -<!-- START doctoc generated TOC please keep comment here to allow auto update --> -<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> +For example, given the C header `cool.h`: +```c +typedef struct CoolStruct { + int x; + int y; +} CoolStruct; -- [Usage](#usage) - - [Requirements](#requirements) - - [Installing Clang 3.9](#installing-clang-39) - - [Windows](#windows) - - [OSX](#osx) - - [Debian-based Linuxes](#debian-based-linuxes) - - [Arch](#arch) - - [From source](#from-source) - - [Library usage with `build.rs`](#library-usage-with-buildrs) - - [`build.rs` Tutorial](#buildrs-tutorial) - - [Simple Example: `./bindgen-integration`](#simple-example-bindgen-integration) - - [Real World Example: Stylo](#real-world-example-stylo) - - [Command Line Usage](#command-line-usage) - - [C++](#c) - - [Annotations](#annotations) - - [`opaque`](#opaque) - - [`hide`](#hide) - - [`replaces`](#replaces) - - [`nocopy`](#nocopy) - -<!-- END doctoc generated TOC please keep comment here to allow auto update --> - -## Usage - -### Requirements - -It is recommended to use Clang 3.9 or greater, however `bindgen` can run with -older Clangs with some features disabled. - -#### Installing Clang 3.9 - -##### Windows - -Download and install the official pre-built binary from -[LLVM download page](http://releases.llvm.org/download.html). - -##### OSX - -If you use Homebrew: - -``` -$ brew install llvm -``` - -If you use MacPorts: - -``` -$ port install clang-3.9 -``` - -##### Debian-based Linuxes - +void cool_function(int i, char c, CoolStruct* cs); ``` -# apt-get install llvm-3.9-dev libclang-3.9-dev clang-3.9 -``` - -Ubuntu 16.10 provides the necessary packages directly. If you are using older -version of Ubuntu or other Debian-based distros, you may need to add the LLVM -repos to get version 3.9. See http://apt.llvm.org/. - -##### Arch - -``` -# pacman -S clang -``` - -##### From source - -If your package manager doesn't yet offer Clang 3.9, you'll need to build from -source. For that, follow the instructions -[here](http://clang.llvm.org/get_started.html). - -Those instructions list optional steps. For bindgen: - -* Checkout and build clang -* Checkout and build the extra-clang-tools -* Checkout and build the compiler-rt -* You do not need to checkout or build libcxx - -### Library usage with `build.rs` - -💡 This is the recommended way to use `bindgen`. 💡 - -#### `build.rs` Tutorial - -[Here is a step-by-step tutorial for generating FFI bindings to the `bzip2` C library.][tutorial] - -[tutorial]: http://fitzgeraldnick.com/2016/12/14/using-libbindgen-in-build-rs.html - -#### Simple Example: `./bindgen-integration` - -The [`./bindgen-integration`][integration] directory has an example crate that -generates FFI bindings in `build.rs` and can be used a template for new -projects. - -[integration]: ./bindgen-integration -#### Real World Example: Stylo - -A real world example is [the Stylo build script][stylo-script] used for -integrating Servo's layout system into Gecko. - -[stylo-script]: https://github.com/servo/servo/blob/master/components/style/build_gecko.rs - -In `Cargo.toml`: - -```toml -[package] -# ... -build = "build.rs" - -[build-dependencies] -bindgen = "0.20" -``` - -In `build.rs`: +`bindgen` produces Rust FFI code allowing you to call into the `cool` library's +functions and use its types: ```rust -extern crate bindgen; +/* automatically generated by rust-bindgen */ -use std::env; -use std::path::Path; - -fn main() { - let out_dir = env::var("OUT_DIR").unwrap(); - let _ = bindgen::builder() - .header("example.h") - .use_core() - .generate().unwrap() - .write_to_file(Path::new(&out_dir).join("example.rs")); +#[repr(C)] +pub struct CoolStruct { + pub x: ::std::os::raw::c_int, + pub y: ::std::os::raw::c_int, } -``` - -In `src/main.rs`: - -```rust -include!(concat!(env!("OUT_DIR"), "/example.rs")); -``` - -### Command Line Usage - -``` -$ cargo install bindgen -``` - -There are a few options documented when running `bindgen --help`. Bindgen is installed to `~/.cargo/bin`. You have to add that directory to your path to use `bindgen`. -### C++ - -`bindgen` can handle most C++ features, but not all of them (C++ is hard!) - -Notable C++ features that are unsupported or only partially supported: - -* Partial template specialization -* Traits templates -* SFINAE -* Instantiating new template specializations - -When passing in header files, the file will automatically be treated as C++ if -it ends in ``.hpp``. If it doesn't, ``-x c++`` can be used to force C++ mode. - -You must use whitelisting when working with C++ to avoid pulling in all of the -`std::*` types, some of which `bindgen` cannot handle. Additionally, you may -want to blacklist other types that `bindgen` stumbles on, or make `bindgen` -treat certain types as opaque. - -### Annotations - -The translation of classes, structs, enums, and typedefs can be adjusted using -annotations. Annotations are specifically formatted html tags inside doxygen -style comments. - -#### `opaque` - -The `opaque` annotation instructs bindgen to ignore all fields defined in -a struct/class. - -```cpp -/// <div rustbindgen opaque></div> -``` - -#### `hide` - -The `hide` annotation instructs bindgen to ignore the struct/class/field/enum -completely. - -```cpp -/// <div rustbindgen hide></div> +extern "C" { + pub fn cool_function(i: ::std::os::raw::c_int, + c: ::std::os::raw::c_char, + cs: *mut CoolStruct); +} ``` -#### `replaces` +## Users Guide -The `replaces` annotation can be used to use a type as a replacement for other -(presumably more complex) type. This is used in Stylo to generate bindings for -structures that for multiple reasons are too complex for bindgen to understand. +[📚 Read the `bindgen` users guide here! 📚](https://servo.github.io/rust-bindgen) -For example, in a C++ header: - -```cpp -/** - * <div rustbindgen replaces="nsTArray"></div> - */ -template<typename T> -class nsTArray_Simple { - T* mBuffer; -public: - // The existence of a destructor here prevents bindgen from deriving the Clone - // trait via a simple memory copy. - ~nsTArray_Simple() {}; -}; -``` +## API Reference -That way, after code generation, the bindings for the `nsTArray` type are -the ones that would be generated for `nsTArray_Simple`. +[API reference documentation is on docs.rs](https://docs.rs/bindgen) -#### `nocopy` +## Contributing -The `nocopy` annotation is used to prevent bindgen to autoderive the `Copy` -and `Clone` traits for a type. +[See `CONTRIBUTING.md` for hacking on `bindgen`!](./CONTRIBUTING.md) diff --git a/book/.gitignore b/book/.gitignore new file mode 100644 index 00000000..7585238e --- /dev/null +++ b/book/.gitignore @@ -0,0 +1 @@ +book diff --git a/book/book.toml b/book/book.toml new file mode 100644 index 00000000..e94e3046 --- /dev/null +++ b/book/book.toml @@ -0,0 +1,3 @@ +title = "The `bindgen` User Guide" +author = "The Servo project developers" +description = "`bindgen` automatically generates Rust FFI bindings to C and C++ libraries." diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md new file mode 100644 index 00000000..1a1818f8 --- /dev/null +++ b/book/src/SUMMARY.md @@ -0,0 +1,20 @@ +# Summary + +- [Introduction](./introduction.md) +- [Requirements](./requirements.md) +- [Library Usage with `build.rs`](./library-usage.md) + - [Tutorial](./tutorial-0.md) + - [Add `bindgen` as a Build Dependency](./tutorial-1.md) + - [Create a `wrapper.h` Header](./tutorial-2.md) + - [Create a `build.rs` File](./tutorial-3.md) + - [Include the Generated Bindings in `src/lib.rs`](./tutorial-4.md) + - [Write a Sanity Test](./tutorial-5.md) + - [Publish Your Crate!](./tutorial-6.md) +- [Command Line Usage](./command-line-usage.md) +- [Customizing the Generated Bindings](./customizing-generated-bindings.md) + - [Whitelisting](./whitelisting.md) + - [Blacklisting](./blacklisting.md) + - [Treating a Type as an Opaque Blob of Bytes](./opaque.md) + - [Replacing One Type with Another](./replacing-types.md) + - [Preventing the Derivation of `Copy` and `Clone`](./nocopy.md) +- [Generating Bindings to C++](./cpp.md) diff --git a/book/src/blacklisting.md b/book/src/blacklisting.md new file mode 100644 index 00000000..990947ab --- /dev/null +++ b/book/src/blacklisting.md @@ -0,0 +1,26 @@ +# Blacklisting + +If you need to provide your own custom translation of some type (for example, +because you need to wrap one of its fields in an `UnsafeCell`), you can +explicitly blacklist generation of its definition. Uses of the blacklisted type +will still appear in other types' definitions. (If you don't want the type to +appear in the bindings at +all, [make it opaque](./opaque.html) instead of +blacklisting it.) + +### Library + +* [`bindgen::Builder::hide_type`](https://docs.rs/bindgen/0.23.1/bindgen/struct.Builder.html#method.hide_type) + +### Command Line + +* `--blacklist-type <type>` + +### Annotations + +```cpp +/// <div rustbindgen hide></div> +class Foo { + // ... +}; +``` diff --git a/book/src/chapter_1.md b/book/src/chapter_1.md new file mode 100644 index 00000000..b743fda3 --- /dev/null +++ b/book/src/chapter_1.md @@ -0,0 +1 @@ +# Chapter 1 diff --git a/book/src/command-line-usage.md b/book/src/command-line-usage.md new file mode 100644 index 00000000..d90eb442 --- /dev/null +++ b/book/src/command-line-usage.md @@ -0,0 +1,27 @@ +# Command Line Usage + +Install the `bindgen` executable with `cargo`: + +```bash +$ cargo install bindgen +``` + +The `bindgen` executable is installed to `~/.cargo/bin`. You have to add that +directory to your `$PATH` to use `bindgen`. + +`bindgen` takes the path to an input C or C++ header file, and optionally an +output file path for the generated bindings. If the output file path is not +supplied, the bindings are printed to `stdout`. + +If we wanted to generated Rust FFI bindings from a C header named `input.h` and +put them in the `bindings.rs` file, we would invoke `bindgen` like this: + +```bash +$ bindgen input.h -o bindings.rs +``` + +For more details, pass the `--help` flag: + +```bash +$ bindgen --help +``` diff --git a/book/src/cpp.md b/book/src/cpp.md new file mode 100644 index 00000000..e0fbecb7 --- /dev/null +++ b/book/src/cpp.md @@ -0,0 +1,27 @@ +# Generating Bindings to C++ + +`bindgen` can handle a surprising number of C++ features, but not all of +them. When `bindgen` can't translate some C++ construct into Rust, it usually +comes down to one of two things: + +1. Rust has no equivalent language feature +2. C++ is *hard!* + +Notable C++ features that are unsupported or only partially supported, and for +which `bindgen` *should* generate opaque blobs whenever it finds an occurrence +of them in a type it is generating bindings for: + +* Template specialization +* Partial template specialization +* Traits templates +* SFINAE + +When passing in header files, the file will automatically be treated as C++ if +it ends in `.hpp`. If it doesn't, adding `-x=c++` clang args can be used to +force C++ mode. You probably also want to use `-std=c++14` or similar clang args +as well. + +You pretty much **must** use [whitelisting](./whitelisting.html) when working +with C++ to avoid pulling in all of the `std::*` types, many of which `bindgen` +cannot handle. Additionally, you may want to mark other types +as [opaque](./opaque.html) that `bindgen` stumbles on. diff --git a/book/src/customizing-generated-bindings.md b/book/src/customizing-generated-bindings.md new file mode 100644 index 00000000..aaaaebea --- /dev/null +++ b/book/src/customizing-generated-bindings.md @@ -0,0 +1,28 @@ +# Customizing the Generated Bindings + +The translation of classes, structs, enums, and typedefs can be adjusted in a +few ways: + +1. By using the `bindgen::Builder`'s configuration methods, when using `bindgen` + as a library. + +2. By passing extra flags and options to the `bindgen` executable. + +3. By adding an annotation comment to the C/C++ source code. Annotations are + specially formatted HTML tags inside doxygen style comments: + + * For single line comments: + ```c + /// <div rustbindgen></div> + ``` + + * For multi-line comments: + ```c + /** + * <div rustbindgen></div> + */ + ``` + +We'll leave the nitty-gritty details to +the [docs.rs API reference](https://docs.rs/bindgen) and `bindgen --help`, but +provide higher level concept documentation here. diff --git a/book/src/introduction.md b/book/src/introduction.md new file mode 100644 index 00000000..4c5fbd49 --- /dev/null +++ b/book/src/introduction.md @@ -0,0 +1,34 @@ +# Introduction + +**[`bindgen`](https://github.com/servo/rust-bindgen) automatically generates Rust +FFI bindings to C and C++ libraries.** + +For example, given the C header `cool.h`: + +```c +typedef struct CoolStruct { + int x; + int y; +} CoolStruct; + +void cool_function(int i, char c, CoolStruct* cs); +``` + +`bindgen` produces Rust FFI code allowing you to call into the `cool` library's +functions and use its types: + +```rust +/* automatically generated by rust-bindgen */ + +#[repr(C)] +pub struct CoolStruct { + pub x: ::std::os::raw::c_int, + pub y: ::std::os::raw::c_int, +} + +extern "C" { + pub fn cool_function(i: ::std::os::raw::c_int, + c: ::std::os::raw::c_char, + cs: *mut CoolStruct); +} +``` diff --git a/book/src/library-usage.md b/book/src/library-usage.md new file mode 100644 index 00000000..35b53fe4 --- /dev/null +++ b/book/src/library-usage.md @@ -0,0 +1,22 @@ +# Library Usage with `build.rs` + +💡 This is the recommended way to use `bindgen`. 💡 + +Often times C and C++ headers will have platform- and architecture-specific +`#ifdef`s that affect the shape of the Rust FFI bindings we need to create to +interface Rust code with the outside world. By using `bindgen` as a library +inside your `build.rs`, you can generate bindings for the current target +on-the-fly. Otherwise, you would need to generate and maintain +`x86_64-unknown-linux-gnu-bindings.rs`, `x86_64-apple-darwin-bindings.rs`, +etc... separate bindings files for each of your supported targets, which can be +a huge pain. The downside is that everyone building your crate also needs +`libclang` available to run `bindgen`. + +## Library API Documentation + +[📚 There is complete API reference documentation on docs.rs 📚](https://docs.rs/bindgen) + +## Tutorial + +The next section contains a detailed, step-by-step tutorial for using `bindgen` +as a library inside `build.rs`. diff --git a/book/src/nocopy.md b/book/src/nocopy.md new file mode 100644 index 00000000..893f0932 --- /dev/null +++ b/book/src/nocopy.md @@ -0,0 +1,20 @@ +# Preventing the Derivation of `Copy` and `Clone` + +`bindgen` will attempt to derive the `Copy` and `Clone` traits on a best-effort +basis. Sometimes, it might not understand that although adding `#[derive(Copy, +Clone)]` to a translated type definition will compile, it still shouldn't do +that for reasons it can't know. In these cases, the `nocopy` annotation can be +used to prevent bindgen to autoderive the `Copy` and `Clone` traits for a type. + +```c +/** + * Although bindgen can't know, this struct is not safe to move because pthread + * mutexes can't move in memory! + * + * <div rustbindgen nocopy></div> + */ +struct MyMutexWrapper { + pthread_mutex_t raw; + // ... +}; +``` diff --git a/book/src/opaque.md b/book/src/opaque.md new file mode 100644 index 00000000..e1bf600c --- /dev/null +++ b/book/src/opaque.md @@ -0,0 +1,26 @@ +# Treating a Type as an Opaque Blob of Bytes + +Sometimes a type definition is simply not translatable to Rust, for example it +uses +[C++'s SFINAE](https://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error) for +which Rust has no equivalent. In these cases, it is best to treat all +occurrences of the type as an opaque blob of bytes with a size and +alignment. `bindgen` will attempt to detect such cases and do this +automatically, but other times it needs some explicit help from you. + +### Library + +* [`bindgen::Builder::opaque_type`](https://docs.rs/bindgen/0.23.1/bindgen/struct.Builder.html#method.opaque_type) + +### Command Line + +* `--opaque-type <type>` + +### Annotation + +```cpp +/// <div rustbindgen opaque></div> +class Foo { + // ... +}; +``` diff --git a/book/src/replacing-types.md b/book/src/replacing-types.md new file mode 100644 index 00000000..7426d2ea --- /dev/null +++ b/book/src/replacing-types.md @@ -0,0 +1,27 @@ +# Replacing One Type with Another + +The `replaces` annotation can be used to use a type as a replacement for other +(presumably more complex) type. This is used in Stylo to generate bindings for +structures that for multiple reasons are too complex for bindgen to understand. + +For example, in a C++ header: + +```cpp +/** + * <div rustbindgen replaces="nsTArray"></div> + */ +template<typename T> +class nsTArray_Simple { + T* mBuffer; +public: + // The existence of a destructor here prevents bindgen from deriving the Clone + // trait via a simple memory copy. + ~nsTArray_Simple() {}; +}; +``` + +That way, after code generation, the bindings for the `nsTArray` type are +the ones that would be generated for `nsTArray_Simple`. + +Replacing is only available as an annotation. To replace a C or C++ definition +with a Rust definition, use [blacklisting](./blacklisting.html). diff --git a/book/src/requirements.md b/book/src/requirements.md new file mode 100644 index 00000000..82da9991 --- /dev/null +++ b/book/src/requirements.md @@ -0,0 +1,67 @@ +# Requirements + +This page lists the requirements for running `bindgen` and how to get them. + +## Clang + +`bindgen` leverages `libclang` to preprocess, parse, and type check C and C++ +header files. + +It is recommended to use Clang 3.9 or greater, however `bindgen` can run with +older Clangs with some features disabled. + +* **If you are generating bindings to C,** 3.7 and 3.8 will probably work OK for +you. + +* **If you are generating bindings to C++,** you almost definitely want 3.9 or +greater. + +### Installing Clang 3.9 + +#### Windows + +Download and install the official pre-built binary from +[LLVM download page](http://releases.llvm.org/download.html). + +#### macOS + +If you use Homebrew: + +```bash +$ brew install llvm@3.9 +``` + +If you use MacPorts: + +```bash +$ port install clang-3.9 +``` + +#### Debian-based Linuxes + +```bash +# apt-get install llvm-3.9-dev libclang-3.9-dev clang-3.9 +``` + +Ubuntu 16.10 provides the necessary packages directly. If you are using older +version of Ubuntu or other Debian-based distros, you may need to add the LLVM +repos to get version 3.9. See http://apt.llvm.org/. + +#### Arch + +```bash +# pacman -S clang +``` + +#### From source + +If your package manager doesn't yet offer Clang 3.9, you'll need to build from +source. For that, follow the +instructions [here](http://clang.llvm.org/get_started.html). + +Those instructions list optional steps. For `bindgen`: + +* Checkout and build clang +* Checkout and build the extra-clang-tools +* You do not need to checkout or build compiler-rt +* You do not need to checkout or build libcxx diff --git a/book/src/tutorial-0.md b/book/src/tutorial-0.md new file mode 100644 index 00000000..adb84308 --- /dev/null +++ b/book/src/tutorial-0.md @@ -0,0 +1,12 @@ +# Tutorial + +The following tutorial is adapted from [this blog post][tutorial]. + +What follows is a whirlwind introductory tutorial to using `bindgen` from inside +`build.rs`. We'll generate bindings to `bzip2` (which is available on most +systems) on-the-fly. + +[**TL;DR?** The full tutorial code is available here.][example] + +[tutorial]: http://fitzgeraldnick.com/2016/12/14/using-libbindgen-in-build-rs.html +[example]: https://github.com/fitzgen/libbindgen-tutorial-bzip2-sys diff --git a/book/src/tutorial-1.md b/book/src/tutorial-1.md new file mode 100644 index 00000000..1b109506 --- /dev/null +++ b/book/src/tutorial-1.md @@ -0,0 +1,9 @@ +# Add `bindgen` as a Build Dependency + +Declare a build-time dependency on `bindgen` by adding it to the +`[build-dependencies]` section of our crate's `Cargo.toml` metadata file: + +```toml +[build-dependencies] +bindgen = "0.23" +``` diff --git a/book/src/tutorial-2.md b/book/src/tutorial-2.md new file mode 100644 index 00000000..e1e88811 --- /dev/null +++ b/book/src/tutorial-2.md @@ -0,0 +1,20 @@ +# Create a `wrapper.h` Header + +The `wrapper.h` file will include all the various headers containing +declarations of structs and functions we would like bindings for. In the +particular case of `bzip2`, this is pretty easy since the entire public API is +contained in a single header. For a project like [SpiderMonkey][spidermonkey], +where the public API is split across multiple header files and grouped by +functionality, we'd want to include all those headers we want to bind to in this +single `wrapper.h` entry point for `bindgen`. + +Here is our `wrapper.h`: + +```c +#include <bzlib.h> +``` + +This is also where we would add any [replacement types](./replacing-types.html), +if we were using some. + +[spidermonkey]: https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/How_to_embed_the_JavaScript_engine diff --git a/book/src/tutorial-3.md b/book/src/tutorial-3.md new file mode 100644 index 00000000..7c081a4d --- /dev/null +++ b/book/src/tutorial-3.md @@ -0,0 +1,58 @@ +# Create a `build.rs` File + +First, we have to tell `cargo` that we have a `build.rs` script by adding +another line to the `Cargo.toml`: + +```toml +[package] +build = "build.rs" +``` + +Second, we create the `build.rs` file in our crate's root. This file is compiled +and executed before the rest of the crate is built, and can be used to generate +code at compile time. And of course in our case, we will be generating Rust FFI +bindings to `bzip2` at compile time. The resulting bindings will be written to +`$OUT_DIR/bindings.rs` where `$OUT_DIR` is chosen by `cargo` and is something +like `./target/debug/build/libbindgen-tutorial-bzip2-sys-afc7747d7eafd720/out/`. + +```rust,ignore +extern crate bindgen; + +use std::env; +use std::path::PathBuf; + +fn main() { + // Tell cargo to tell rustc to link the system bzip2 + // shared library. + println!("cargo:rustc-link-lib=bz2"); + + // The bindgen::Builder is the main entry point + // to bindgen, and lets you build up options for + // the resulting bindings. + let bindings = bindgen::Builder::default() + // Do not generate unstable Rust code that + // requires a nightly rustc and enabling + // unstable features. + .no_unstable_rust() + // The input header we would like to generate + // bindings for. + .header("wrapper.h") + // Finish the builder and generate the bindings. + .generate() + // Unwrap the Result and panic on failure. + .expect("Unable to generate bindings"); + + // Write the bindings to the $OUT_DIR/bindings.rs file. + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} +``` + +Now, when we run `cargo build`, our bindings to `bzip2` are generated on the +fly! + +[There's more info about `build.rs` files in the crates.io documentation.][build-rs] + +[build-rs]: http://doc.crates.io/build-script.html diff --git a/book/src/tutorial-4.md b/book/src/tutorial-4.md new file mode 100644 index 00000000..42aa92fd --- /dev/null +++ b/book/src/tutorial-4.md @@ -0,0 +1,57 @@ +# Include the Generated Bindings in `src/lib.rs` + +We can use the `include!` macro to dump our generated bindings right into our +crate's main entry point, `src/lib.rs`: + +```rust,ignore +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); +``` + +Because `bzip2`'s symbols do not follow Rust's style conventions, we suppress a +bunch of warnings with a few `#![allow(...)]` pragmas. + +We can run `cargo build` again to check that the bindings themselves compile: + +```bash +$ cargo build + Compiling libbindgen-tutorial-bzip2-sys v0.1.0 + Finished debug [unoptimized + debuginfo] target(s) in 62.8 secs +``` + +And we can run `cargo test` to verify that the layout, size, and alignment of +our generated Rust FFI structs match what `bindgen` thinks they should be: + +```bash +$ cargo test + Compiling libbindgen-tutorial-bzip2-sys v0.1.0 + Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs + Running target/debug/deps/bzip2_sys-10413fc2af207810 + +running 14 tests +test bindgen_test_layout___darwin_pthread_handler_rec ... ok +test bindgen_test_layout___sFILE ... ok +test bindgen_test_layout___sbuf ... ok +test bindgen_test_layout__bindgen_ty_1 ... ok +test bindgen_test_layout__bindgen_ty_2 ... ok +test bindgen_test_layout__opaque_pthread_attr_t ... ok +test bindgen_test_layout__opaque_pthread_cond_t ... ok +test bindgen_test_layout__opaque_pthread_mutex_t ... ok +test bindgen_test_layout__opaque_pthread_condattr_t ... ok +test bindgen_test_layout__opaque_pthread_mutexattr_t ... ok +test bindgen_test_layout__opaque_pthread_once_t ... ok +test bindgen_test_layout__opaque_pthread_rwlock_t ... ok +test bindgen_test_layout__opaque_pthread_rwlockattr_t ... ok +test bindgen_test_layout__opaque_pthread_t ... ok + +test result: ok. 14 passed; 0 failed; 0 ignored; 0 measured + + Doc-tests libbindgen-tutorial-bzip2-sys + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured +``` diff --git a/book/src/tutorial-5.md b/book/src/tutorial-5.md new file mode 100644 index 00000000..7ff5ffb9 --- /dev/null +++ b/book/src/tutorial-5.md @@ -0,0 +1,169 @@ +# Write a Sanity Test + +Finally, to tie everything together, let's write a sanity test that round trips +some text through compression and decompression, and then asserts that it came +back out the same as it went in. This is a little wordy using the raw FFI +bindings, but hopefully we wouldn't usually ask people to do this, we'd provide +a nice Rust-y API on top of the raw FFI bindings for them. However, since this +is for testing the bindings directly, our sanity test will use the bindings +directly. + +The test data I'm round tripping are some Futurama quotes I got off the internet +and put in the `futurama-quotes.txt` file, which is read into a `&'static str` +at compile time via the `include_str!("../futurama-quotes.txt")` macro +invocation. + +Without further ado, here is the test, which should be appended to the bottom of +our `src/lib.rs` file: + +```rust +#[cfg(test)] +mod tests { + use super::*; + use std::mem; + + #[test] + fn round_trip_compression_decompression() { + unsafe { + let input = include_str!("../futurama-quotes.txt").as_bytes(); + let mut compressed_output: Vec<u8> = vec![0; input.len()]; + let mut decompressed_output: Vec<u8> = vec![0; input.len()]; + + // Construct a compression stream. + let mut stream: bz_stream = mem::zeroed(); + let result = BZ2_bzCompressInit(&mut stream as *mut _, + 1, // 1 x 100000 block size + 4, // verbosity (4 = most verbose) + 0); // default work factor + match result { + r if r == (BZ_CONFIG_ERROR as _) => panic!("BZ_CONFIG_ERROR"), + r if r == (BZ_PARAM_ERROR as _) => panic!("BZ_PARAM_ERROR"), + r if r == (BZ_MEM_ERROR as _) => panic!("BZ_MEM_ERROR"), + r if r == (BZ_OK as _) => {}, + r => panic!("Unknown return value = {}", r), + } + + // Compress `input` into `compressed_output`. + stream.next_in = input.as_ptr() as *mut _; + stream.avail_in = input.len() as _; + stream.next_out = compressed_output.as_mut_ptr() as *mut _; + stream.avail_out = compressed_output.len() as _; + let result = BZ2_bzCompress(&mut stream as *mut _, BZ_FINISH as _); + match result { + r if r == (BZ_RUN_OK as _) => panic!("BZ_RUN_OK"), + r if r == (BZ_FLUSH_OK as _) => panic!("BZ_FLUSH_OK"), + r if r == (BZ_FINISH_OK as _) => panic!("BZ_FINISH_OK"), + r if r == (BZ_SEQUENCE_ERROR as _) => panic!("BZ_SEQUENCE_ERROR"), + r if r == (BZ_STREAM_END as _) => {}, + r => panic!("Unknown return value = {}", r), + } + + // Finish the compression stream. + let result = BZ2_bzCompressEnd(&mut stream as *mut _); + match result { + r if r == (BZ_PARAM_ERROR as _) => panic!(BZ_PARAM_ERROR), + r if r == (BZ_OK as _) => {}, + r => panic!("Unknown return value = {}", r), + } + + // Construct a decompression stream. + let mut stream: bz_stream = mem::zeroed(); + let result = BZ2_bzDecompressInit(&mut stream as *mut _, + 4, // verbosity (4 = most verbose) + 0); // default small factor + match result { + r if r == (BZ_CONFIG_ERROR as _) => panic!("BZ_CONFIG_ERROR"), + r if r == (BZ_PARAM_ERROR as _) => panic!("BZ_PARAM_ERROR"), + r if r == (BZ_MEM_ERROR as _) => panic!("BZ_MEM_ERROR"), + r if r == (BZ_OK as _) => {}, + r => panic!("Unknown return value = {}", r), + } + + // Decompress `compressed_output` into `decompressed_output`. + stream.next_in = compressed_output.as_ptr() as *mut _; + stream.avail_in = compressed_output.len() as _; + stream.next_out = decompressed_output.as_mut_ptr() as *mut _; + stream.avail_out = decompressed_output.len() as _; + let result = BZ2_bzDecompress(&mut stream as *mut _); + match result { + r if r == (BZ_PARAM_ERROR as _) => panic!("BZ_PARAM_ERROR"), + r if r == (BZ_DATA_ERROR as _) => panic!("BZ_DATA_ERROR"), + r if r == (BZ_DATA_ERROR_MAGIC as _) => panic!("BZ_DATA_ERROR"), + r if r == (BZ_MEM_ERROR as _) => panic!("BZ_MEM_ERROR"), + r if r == (BZ_OK as _) => panic!("BZ_OK"), + r if r == (BZ_STREAM_END as _) => {}, + r => panic!("Unknown return value = {}", r), + } + + // Close the decompression stream. + let result = BZ2_bzDecompressEnd(&mut stream as *mut _); + match result { + r if r == (BZ_PARAM_ERROR as _) => panic!("BZ_PARAM_ERROR"), + r if r == (BZ_OK as _) => {}, + r => panic!("Unknown return value = {}", r), + } + + assert_eq!(input, &decompressed_output[..]); + } + } +} +``` + +Now let's run `cargo test` again and verify that everying is linking and binding +properly! + +```bash +$ cargo test + Compiling libbindgen-tutorial-bzip2-sys v0.1.0 + Finished debug [unoptimized + debuginfo] target(s) in 0.54 secs + Running target/debug/deps/libbindgen_tutorial_bzip2_sys-1c5626bbc4401c3a + +running 15 tests +test bindgen_test_layout___darwin_pthread_handler_rec ... ok +test bindgen_test_layout___sFILE ... ok +test bindgen_test_layout___sbuf ... ok +test bindgen_test_layout__bindgen_ty_1 ... ok +test bindgen_test_layout__bindgen_ty_2 ... ok +test bindgen_test_layout__opaque_pthread_attr_t ... ok +test bindgen_test_layout__opaque_pthread_cond_t ... ok +test bindgen_test_layout__opaque_pthread_condattr_t ... ok +test bindgen_test_layout__opaque_pthread_mutex_t ... ok +test bindgen_test_layout__opaque_pthread_mutexattr_t ... ok +test bindgen_test_layout__opaque_pthread_once_t ... ok +test bindgen_test_layout__opaque_pthread_rwlock_t ... ok +test bindgen_test_layout__opaque_pthread_rwlockattr_t ... ok +test bindgen_test_layout__opaque_pthread_t ... ok + block 1: crc = 0x47bfca17, combined CRC = 0x47bfca17, size = 2857 + bucket sorting ... + depth 1 has 2849 unresolved strings + depth 2 has 2702 unresolved strings + depth 4 has 1508 unresolved strings + depth 8 has 538 unresolved strings + depth 16 has 148 unresolved strings + depth 32 has 0 unresolved strings + reconstructing block ... + 2857 in block, 2221 after MTF & 1-2 coding, 61+2 syms in use + initial group 5, [0 .. 1], has 570 syms (25.7%) + initial group 4, [2 .. 2], has 256 syms (11.5%) + initial group 3, [3 .. 6], has 554 syms (24.9%) + initial group 2, [7 .. 12], has 372 syms (16.7%) + initial group 1, [13 .. 62], has 469 syms (21.1%) + pass 1: size is 2743, grp uses are 13 6 15 0 11 + pass 2: size is 1216, grp uses are 13 7 15 0 10 + pass 3: size is 1214, grp uses are 13 8 14 0 10 + pass 4: size is 1213, grp uses are 13 9 13 0 10 + bytes: mapping 19, selectors 17, code lengths 79, codes 1213 + final combined CRC = 0x47bfca17 + + [1: huff+mtf rt+rld {0x47bfca17, 0x47bfca17}] + combined CRCs: stored = 0x47bfca17, computed = 0x47bfca17 +test tests::round_trip_compression_decompression ... ok + +test result: ok. 15 passed; 0 failed; 0 ignored; 0 measured + + Doc-tests libbindgen-tutorial-bzip2-sys + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured +``` diff --git a/book/src/tutorial-6.md b/book/src/tutorial-6.md new file mode 100644 index 00000000..366be421 --- /dev/null +++ b/book/src/tutorial-6.md @@ -0,0 +1,13 @@ +# Publish Your Crate! + +That's it! Now we can publish our crate on crates.io and we can write a nice, +Rust-y API wrapping the raw FFI bindings in a safe interface. However, there is +already a [`bzip2-sys`][bz-sys] crate providing raw FFI bindings, and there is +already a [`bzip2`][bz] crate providing a nice, safe, Rust-y API on top of the +bindings, so we have nothing left to do here! + +Check out the [full code on Github!][example] + +[bz-sys]: https://crates.io/crates/bzip2-sys +[bz]: https://crates.io/crates/bzip2 +[example]: https://github.com/fitzgen/libbindgen-tutorial-bzip2-sys diff --git a/book/src/whitelisting.md b/book/src/whitelisting.md new file mode 100644 index 00000000..1b3eeaf6 --- /dev/null +++ b/book/src/whitelisting.md @@ -0,0 +1,31 @@ +# Whitelisting + +Whitelisting allows us to be precise about which type, function, and global +variable definitions `bindgen` generates bindings for. By default, if we don't +specify any whitelisting rules, everything is considered whitelisted. This may +not be desirable because of either + +* the generated bindings contain a lot of extra defintions we don't plan on using, or +* the header file contains C++ features for which Rust does not have a + corresponding form (such as partial template specialization), and we would + like to avoid these definitions + +If we specify whitelisting rules, then `bindgen` will only generate bindings to +types, functions, and global variables that match the whitelisting rules, or are +transitively used by a definition that matches them. + +### Library + +* [`bindgen::Builder::whitelisted_type`](https://docs.rs/bindgen/0.23.1/bindgen/struct.Builder.html#method.whitelisted_type) +* [`bindgen::Builder::whitelisted_function`](https://docs.rs/bindgen/0.23.1/bindgen/struct.Builder.html#method.whitelisted_function) +* [`bindgen::Builder::whitelisted_var`](https://docs.rs/bindgen/0.23.1/bindgen/struct.Builder.html#method.whitelisted_function) + +### Command Line + +* `--whitelist-type <type>` +* `--whitelist-function <function>` +* `--whitelist-var <var>` + +### Annotations + +None. diff --git a/ci/deploy-book.sh b/ci/deploy-book.sh new file mode 100755 index 00000000..0c2a2b5f --- /dev/null +++ b/ci/deploy-book.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +set -xeu +cd "$(dirname "$0")/../book" + +# Ensure mdbook is installed. +cargo install mdbook || true +export PATH="$PATH:~/.cargo/bin" + +# Get the git revision we are on. +rev=$(git rev-parse --short HEAD) + +# Build the users guide book and go into the built book's directory. +rm -rf ./book +mdbook build +cd ./book + +# Make the built book directory a new git repo, fetch upstream, make a new +# commit on gh-pages, and push it upstream. + +git init +git config user.name "Travis CI" +git config user.email "builds@travis-ci.org" + +git remote add upstream "https://$GH_TOKEN@github.com/servo/rust-bindgen.git" +git fetch upstream +git reset upstream/gh-pages + +touch . + +git add -A . +git commit -m "Rebuild users guide at ${rev}" +git push upstream HEAD:gh-pages diff --git a/ci/test-book.sh b/ci/test-book.sh new file mode 100755 index 00000000..30b23318 --- /dev/null +++ b/ci/test-book.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +set -xeu +cd "$(dirname "$0")/../book" + +cargo install mdbook || true +export PATH="$PATH:~/.cargo/bin" + +mdbook build +mdbook test |