diff options
-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 |