From e267b2624162052f036bcdba545809055aa7ba16 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Mon, 28 Oct 2019 09:11:06 -0700 Subject: [PATCH 1/3] Expand documentation on build scripts. --- src/doc/src/SUMMARY.md | 1 + .../src/reference/build-script-examples.md | 501 +++++++++++ src/doc/src/reference/build-scripts.md | 781 +++++++----------- src/doc/src/reference/config.md | 7 + .../src/reference/environment-variables.md | 33 +- src/doc/src/reference/manifest.md | 7 +- 6 files changed, 847 insertions(+), 483 deletions(-) create mode 100644 src/doc/src/reference/build-script-examples.md diff --git a/src/doc/src/SUMMARY.md b/src/doc/src/SUMMARY.md index 1f6bd2cb96d..369fa9627b3 100644 --- a/src/doc/src/SUMMARY.md +++ b/src/doc/src/SUMMARY.md @@ -24,6 +24,7 @@ * [Configuration](reference/config.md) * [Environment Variables](reference/environment-variables.md) * [Build Scripts](reference/build-scripts.md) + * [Build Script Examples](reference/build-script-examples.md) * [Publishing on crates.io](reference/publishing.md) * [Package ID Specifications](reference/pkgid-spec.md) * [Source Replacement](reference/source-replacement.md) diff --git a/src/doc/src/reference/build-script-examples.md b/src/doc/src/reference/build-script-examples.md new file mode 100644 index 00000000000..c2869b5dea9 --- /dev/null +++ b/src/doc/src/reference/build-script-examples.md @@ -0,0 +1,501 @@ +## Build Script Examples + +The following sections illustrate some examples of writing build scripts. + +Some common build script functionality can be found via crates on [crates.io]. +Check out the [`build-dependencies` +keyword](https://crates.io/keywords/build-dependencies) to see what is +available. Some popular crates are: + +* [`bindgen`](https://crates.io/crates/bindgen) — Automatically generate Rust + FFI bindings to C libraries. +* [`cc`](https://crates.io/crates/cc) — Compiles C/C++/assembly. +* [`pkg-config`](https://crates.io/crates/pkg-config) — Detect system + libraries using the `pkg-config` utility. +* [`cmake`](https://crates.io/crates/cmake) — Runs the `cmake` build tool to build a native library. +* [`rustc_version`](https://crates.io/crates/rustc_version), + [`version_check`](https://crates.io/crates/version_check) — These crates + provide ways to implement conditional compilation based on the current + version of `rustc`. + +### Code generation + +Some Cargo packages need to have code generated just before they are compiled +for various reasons. Here we’ll walk through a simple example which generates a +library call as part of the build script. + +First, let’s take a look at the directory structure of this package: + +``` +. +├── Cargo.toml +├── build.rs +└── src + └── main.rs + +1 directory, 3 files +``` + +Here we can see that we have a `build.rs` build script and our binary in +`main.rs`. This package has a basic manifest: + +```toml +# Cargo.toml + +[package] +name = "hello-from-generated-code" +version = "0.1.0" +``` + +Let’s see what’s inside the build script: + +```rust,no_run +// build.rs + +use std::env; +use std::fs; +use std::path::Path; + +fn main() { + let out_dir = env::var_os("OUT_DIR").unwrap(); + let dest_path = Path::new(&out_dir).join("hello.rs"); + fs::write( + &dest_path, + "pub fn message() -> &'static str { + \"Hello, World!\" + } + " + ).unwrap(); + println!("cargo:rerun-if-changed=build.rs"); +} +``` + +There’s a couple of points of note here: + +* The script uses the `OUT_DIR` environment variable to discover where the + output files should be located. It can use the process’ current working + directory to find where the input files should be located, but in this case we + don’t have any input files. +* In general, build scripts should not modify any files outside of `OUT_DIR`. + It may seem fine on the first blush, but it does cause problems when you use + such crate as a dependency, because there's an *implicit* invariant that + sources in `.cargo/registry` should be immutable. `cargo` won't allow such + scripts when packaging. +* This script is relatively simple as it just writes out a small generated file. + One could imagine that other more fanciful operations could take place such as + generating a Rust module from a C header file or another language definition, + for example. +* The [`rerun-if-changed` instruction](build-scripts.md#rerun-if-changed) + tells Cargo that the build script only needs to re-run if the build script + itself changes. Without this line, Cargo will automatically run the build + script if any file in the package changes. If your code generation uses some + input files, this is where you would print a list of each of those files. + +Next, let’s peek at the library itself: + +```rust,ignore +// src/main.rs + +include!(concat!(env!("OUT_DIR"), "/hello.rs")); + +fn main() { + println!("{}", message()); +} +``` + +This is where the real magic happens. The library is using the rustc-defined +[`include!` macro][include-macro] in combination with the +[`concat!`][concat-macro] and [`env!`][env-macro] macros to include the +generated file (`hello.rs`) into the crate’s compilation. + +Using the structure shown here, crates can include any number of generated files +from the build script itself. + +[include-macro]: ../../std/macro.include.html +[concat-macro]: ../../std/macro.concat.html +[env-macro]: ../../std/macro.env.html + +### Building a native library + +Sometimes it’s necessary to build some native C or C++ code as part of a +package. This is another excellent use case of leveraging the build script to +build a native library before the Rust crate itself. As an example, we’ll create +a Rust library which calls into C to print “Hello, World!”. + +Like above, let’s first take a look at the package layout: + +``` +. +├── Cargo.toml +├── build.rs +└── src + ├── hello.c + └── main.rs + +1 directory, 4 files +``` + +Pretty similar to before! Next, the manifest: + +```toml +# Cargo.toml + +[package] +name = "hello-world-from-c" +version = "0.1.0" +edition = "2018" +``` + +For now we’re not going to use any build dependencies, so let’s take a look at +the build script now: + +```rust,no_run +// build.rs + +use std::process::Command; +use std::env; +use std::path::Path; + +fn main() { + let out_dir = env::var("OUT_DIR").unwrap(); + + // Note that there are a number of downsides to this approach, the comments + // below detail how to improve the portability of these commands. + Command::new("gcc").args(&["src/hello.c", "-c", "-fPIC", "-o"]) + .arg(&format!("{}/hello.o", out_dir)) + .status().unwrap(); + Command::new("ar").args(&["crus", "libhello.a", "hello.o"]) + .current_dir(&Path::new(&out_dir)) + .status().unwrap(); + + println!("cargo:rustc-link-search=native={}", out_dir); + println!("cargo:rustc-link-lib=static=hello"); + println!("cargo:rerun-if-changed=src/hello.c"); +} +``` + +This build script starts out by compiling our C file into an object file (by +invoking `gcc`) and then converting this object file into a static library (by +invoking `ar`). The final step is feedback to Cargo itself to say that our +output was in `out_dir` and the compiler should link the crate to `libhello.a` +statically via the `-l static=hello` flag. + +Note that there are a number of drawbacks to this hard-coded approach: + +* The `gcc` command itself is not portable across platforms. For example it’s + unlikely that Windows platforms have `gcc`, and not even all Unix platforms + may have `gcc`. The `ar` command is also in a similar situation. +* These commands do not take cross-compilation into account. If we’re cross + compiling for a platform such as Android it’s unlikely that `gcc` will produce + an ARM executable. + +Not to fear, though, this is where a `build-dependencies` entry would help! +The Cargo ecosystem has a number of packages to make this sort of task much +easier, portable, and standardized. Let's try the [`cc` +crate](https://crates.io/crates/cc) from [crates.io]. First, add it to the +`build-dependencies` in `Cargo.toml`: + +```toml +[build-dependencies] +cc = "1.0" +``` + +And rewrite the build script to use this crate: + +```rust,ignore +// build.rs + +fn main() { + cc::Build::new() + .file("src/hello.c") + .compile("hello"); + println!("cargo:rerun-if-changed=src/hello.c"); +} +``` + +The [`cc` crate] abstracts a range of build script requirements for C code: + +* It invokes the appropriate compiler (MSVC for windows, `gcc` for MinGW, `cc` + for Unix platforms, etc.). +* It takes the `TARGET` variable into account by passing appropriate flags to + the compiler being used. +* Other environment variables, such as `OPT_LEVEL`, `DEBUG`, etc., are all + handled automatically. +* The stdout output and `OUT_DIR` locations are also handled by the `cc` + library. + +Here we can start to see some of the major benefits of farming as much +functionality as possible out to common build dependencies rather than +duplicating logic across all build scripts! + +Back to the case study though, let’s take a quick look at the contents of the +`src` directory: + +```c +// src/hello.c + +#include + +void hello() { + printf("Hello, World!\n"); +} +``` + +```rust,ignore +// src/main.rs + +// Note the lack of the `#[link]` attribute. We’re delegating the responsibility +// of selecting what to link over to the build script rather than hard-coding +// it in the source file. +extern { fn hello(); } + +fn main() { + unsafe { hello(); } +} +``` + +And there we go! This should complete our example of building some C code from a +Cargo package using the build script itself. This also shows why using a build +dependency can be crucial in many situations and even much more concise! + +We’ve also seen a brief example of how a build script can use a crate as a +dependency purely for the build process and not for the crate itself at runtime. + +[`cc` crate]: https://crates.io/crates/cc + +### Linking to system libraries + +This example demonstrates how to link a system library and how the build +script is used to support this use case. + +Quite frequently a Rust crate wants to link to a native library provided on +the system to bind its functionality or just use it as part of an +implementation detail. This is quite a nuanced problem when it comes to +performing this in a platform-agnostic fashion. It is best, if possible, to +farm out as much of this as possible to make this as easy as possible for +consumers. + +For this example, we will be creating a binding to the system's zlib library. +This is a library that is commonly found on most Unix-like systems that +provides data compression. This is already wrapped up in the [`libz-sys` +crate], but for this example, we'll do an extremely simplified version. Check +out [the source code][libz-source] for the full example. + +To make it easy to find the location of the library, we will use the +[`pkg-config` crate]. This crate uses the system's `pkg-config` utility to +discover information about a library. It will automatically tell Cargo what is +needed to link the library. This will likely only work on Unix-like systems +with `pkg-config` installed. Let's start by setting up the manifest: + +```toml +# Cargo.toml + +[package] +name = "libz-sys" +version = "0.1.0" +edition = "2018" +links = "z" + +[build-dependencies] +pkg-config = "0.3.16" +``` + +Take note that we included the `links` key in the `package` table. This tells +Cargo that we are linking to the `libz` library. See ["Using another sys +crate"](#using-another-sys-crate) for an example that will leverage this. + +The build script is fairly simple: + +```rust,ignore +// build.rs + +fn main() { + pkg_config::Config::new().probe("zlib").unwrap(); + println!("cargo:rerun-if-changed=build.rs"); +} +``` + +Let's round out the example with a basic FFI binding: + +```rust,ignore +// src/lib.rs + +use std::os::raw::{c_uint, c_ulong}; + +extern "C" { + pub fn crc32(crc: c_ulong, buf: *const u8, len: c_uint) -> c_ulong; +} + +#[test] +fn test_crc32() { + let s = "hello"; + unsafe { + assert_eq!(crc32(0, s.as_ptr(), s.len() as c_uint), 0x3610a686); + } +} +``` + +Run `cargo build -vv` to see the output from the build script. On a system +with `libz` already installed, it may look something like this: + +```text +[libz-sys 0.1.0] cargo:rustc-link-search=native=/usr/lib +[libz-sys 0.1.0] cargo:rustc-link-lib=z +[libz-sys 0.1.0] cargo:rerun-if-changed=build.rs +``` + +Nice! `pkg-config` did all the work of finding the library and telling Cargo +where it is. + +It is not unusual for packages to include the source for the library, and +build it statically if it is not found on the system, or if a feature or +environment variable is set. For example, the real [`libz-sys` crate] checks the +environment variable `LIBZ_SYS_STATIC` or the `static` feature to build it +from source instead of using the system library. Check out [the +source][libz-source] for a more complete example. + +[`libz-sys` crate]: https://crates.io/crates/libz-sys +[`pkg-config` crate]: https://crates.io/crates/pkg-config +[libz-source]: https://github.com/rust-lang/libz-sys + +### Using another `sys` crate + +When using the `links` key, crates may set metadata that can be read by other +crates that depend on it. This provides a mechanism to communicate information +between crates. In this example, we'll be creating a C library that makes use +of zlib from the real [`libz-sys` crate]. + +If you have a C library that depends on zlib, you can leverage the [`libz-sys` +crate] to automatically find it or build it. This is great for cross-platform +support, such as Windows where zlib is not usually installed. `libz-sys` [sets +the `include` +metadata](https://github.com/rust-lang/libz-sys/blob/3c594e677c79584500da673f918c4d2101ac97a1/build.rs#L156) +to tell other packages where to find the header files for zlib. Our build +script can read that metadata with the `DEP_Z_INCLUDE` environment variable. +Here's an example: + +```toml +# Cargo.toml + +[package] +name = "zuser" +version = "0.1.0" +edition = "2018" + +[dependencies] +libz-sys = "1.0.25" + +[build-dependencies] +cc = "1.0.46" +``` + +Here we have included `libz-sys` which will ensure that there is only one +`libz` used in the final library, and give us access to it from our build +script: + +```rust,ignore +// build.rs + +fn main() { + let mut cfg = cc::Build::new(); + cfg.file("src/zuser.c"); + if let Some(include) = std::env::var_os("DEP_Z_INCLUDE") { + cfg.include(include); + } + cfg.compile("zuser"); + println!("cargo:rerun-if-changed=src/zuser.c"); +} +``` + +With `libz-sys` doing all the heavy lifting, the C source code may now include +the zlib header, and it should find the header, even on systems where it isn't +already installed. + +```c +// src/zuser.c + +#include "zlib.h" + +// … rest of code that makes use of zlib. +``` + +### Conditional compilation + +A build script may emit [`rustc-cfg` instructions] which can enable conditions +that can be checked at compile time. In this example, we'll take a look at how +the [`openssl` crate] uses this to support multiple versions of the OpenSSL +library. + +The [`openssl-sys` crate] implements building and linking the OpenSSL library. +It supports multiple different implementations (like LibreSSL) and multiple +versions. It makes use of the `links` key so that it may pass information to +other build scripts. One of the things it passes is the `version_number` key, +which is the version of OpenSSL that was detected. The code in the build +script looks something [like +this](https://github.com/sfackler/rust-openssl/blob/dc72a8e2c429e46c275e528b61a733a66e7877fc/openssl-sys/build/main.rs#L216): + +```rust,ignore +println!("cargo:version_number={:x}", openssl_version); +``` + +This instruction causes the `DEP_OPENSSL_VERSION_NUMBER` environment variable +to be set in any crates that directly depend on `openssl-sys`. + +The `openssl` crate, which provides the higher-level interface, specifies +`openssl-sys` as a dependency. The `openssl` build script can read the +version information generated by the `openssl-sys` build script with the +`DEP_OPENSSL_VERSION_NUMBER` environment variable. It uses this to generate +some [`cfg` +values](https://github.com/sfackler/rust-openssl/blob/dc72a8e2c429e46c275e528b61a733a66e7877fc/openssl/build.rs#L18-L36): + +```rust,ignore +// (portion of build.rs) + +if let Ok(version) = env::var("DEP_OPENSSL_VERSION_NUMBER") { + let version = u64::from_str_radix(&version, 16).unwrap(); + + if version >= 0x1_00_01_00_0 { + println!("cargo:rustc-cfg=ossl101"); + } + if version >= 0x1_00_02_00_0 { + println!("cargo:rustc-cfg=ossl102"); + } + if version >= 0x1_01_00_00_0 { + println!("cargo:rustc-cfg=ossl110"); + } + if version >= 0x1_01_00_07_0 { + println!("cargo:rustc-cfg=ossl110g"); + } + if version >= 0x1_01_01_00_0 { + println!("cargo:rustc-cfg=ossl111"); + } +} +``` + +These `cfg` values can then be used with the [`cfg` attribute] or the [`cfg` +macro] to conditionally include code. For example, SHA3 support was added in +OpenSSL 1.1.1, so it is [conditionally +excluded](https://github.com/sfackler/rust-openssl/blob/dc72a8e2c429e46c275e528b61a733a66e7877fc/openssl/src/hash.rs#L67-L85) +for older versions: + +```rust,ignore +// (portion of openssl crate) + +#[cfg(ossl111)] +pub fn sha3_224() -> MessageDigest { + unsafe { MessageDigest(ffi::EVP_sha3_224()) } +} +``` + +Of course, one should be careful when using this, since it makes the resulting +binary even more dependent on the build environment. In this example, if the +binary is distributed to another system, it may not have the exact same shared +libraries, which could cause problems. + +[`cfg` attribute]: ../../reference/conditional-compilation.md#the-cfg-attribute +[`cfg` macro]: ../../std/macro.cfg.html +[`rustc-cfg` instructions]: build-scripts.md#rustc-cfg +[`openssl` crate]: https://crates.io/crates/openssl +[`openssl-sys` crate]: https://crates.io/crates/openssl-sys + +[crates.io]: https://crates.io/ diff --git a/src/doc/src/reference/build-scripts.md b/src/doc/src/reference/build-scripts.md index 9d1c9f4062a..e6fbf9e58a6 100644 --- a/src/doc/src/reference/build-scripts.md +++ b/src/doc/src/reference/build-scripts.md @@ -6,131 +6,214 @@ located on the system or possibly need to be built from source. Others still need facilities for functionality such as code generation before building (think parser generators). -Cargo does not aim to replace other tools that are well-optimized for -these tasks, but it does integrate with them with the `build` configuration -option. +Cargo does not aim to replace other tools that are well-optimized for these +tasks, but it does integrate with them with custom build scripts. Placing a +file named `build.rs` in the root of a package will cause Cargo to compile +that script and execute it just before building the package. -```toml -[package] -# ... -build = "build.rs" +```rust,ignore +// Example custom build script. +fn main() { + // Tell Cargo that if the given file changes, to rerun this build script. + println!("cargo:rerun-if-changed=src/hello.c"); + // Use the `cc` crate to build a C file and statically link it. + cc::Build::new() + .file("src/hello.c") + .compile("hello"); +} ``` -The Rust file designated by the `build` command (relative to the package root) -will be compiled and invoked before anything else is compiled in the package, -allowing your Rust code to depend on the built or generated artifacts. -By default Cargo looks for a `"build.rs"` file in a package root (even if you -do not specify a value for `build`). Use `build = "custom_build_name.rs"` to specify -a custom build name or `build = false` to disable automatic detection of the build script. - -Some example use cases of the build command are: +Some example use cases of build scripts are: * Building a bundled C library. * Finding a C library on the host system. * Generating a Rust module from a specification. * Performing any platform-specific configuration needed for the crate. -Each of these use cases will be detailed in full below to give examples of how -the build command works. +The sections below describe how build scripts work, and the [examples +chapter](build-script-examples.md) shows a variety of examples on how to write +scripts. + +> Note: The [`package.build` manifest key](manifest.md#package-build) can be +> used to change the name of the build script, or disable it entirely. + +### Life Cycle of a Build Script + +Just before a package is built, Cargo will compile a build script into an +executable (if it has not already been built). It will then run the script, +which may perform any number of tasks. The script may communicate with Cargo +by printing specially formated commands prefixed with `cargo:` to stdout. + +The build script will be rebuilt if any of its source files or dependencies +change. + +By default, Cargo will re-run the build script if any of the files in the +package changes. Typically it is best to use the `rerun-if` commands, +described in the [change detection](#change-detection) section below, to +narrow the focus of what triggers a build script to run again. + +Once the build script successfully finishes executing, the rest of the package +will be compiled. Scripts should exit with a non-zero exit code to halt the +build if there is an error, in which case the build script's output will be +displayed on the terminal. ### Inputs to the Build Script When the build script is run, there are a number of inputs to the build script, -all passed in the form of [environment variables][env]. +all passed in the form of [environment variables][build-env]. In addition to environment variables, the build script’s current directory is the source directory of the build script’s package. -[env]: environment-variables.md +[build-env]: environment-variables.md#environment-variables-cargo-sets-for-build-scripts ### Outputs of the Build Script +Build scripts may save any output files in the directory specified in the +[`OUT_DIR` environment variable][build-env]. Scripts should not modify any +files outside of that directory. + +Build scripts communicate with Cargo by printing to stdout. Cargo will +interpret each line that starts with `cargo:` as an instruction that will +influence compilation of the package. All other lines are ignored. + +The output of the script is hidden from the terminal during normal +compilation. If you would like to see the output directly in your terminal, +invoke Cargo as "very verbose" with the `-vv` flag. This only happens when the +build script is run. If Cargo determines nothing has changed, it will not +re-run the script, see [change detection](#change-detection) below for more. + All the lines printed to stdout by a build script are written to a file like `target/debug/build//output` (the precise location may depend on your -configuration). If you would like to see such output directly in your terminal, -invoke cargo as 'very verbose' with the `-vv` flag. Note that if neither the -build script nor package source files are modified, subsequent calls to -cargo with `-vv` will **not** print output to the terminal because a -new build is not executed. Run `cargo clean` before each cargo invocation -if you want to ensure that output is always displayed on your terminal. -Any line that starts with `cargo:` is interpreted directly by Cargo. -This line must be of the form `cargo:key=value`, like the examples below: +configuration). The stderr output is also saved in that same directory. -``` -# specially recognized by Cargo -cargo:rustc-link-lib=static=foo -cargo:rustc-link-search=native=/path/to/foo -cargo:rustc-cfg=foo -cargo:rustc-env=FOO=bar -cargo:rustc-cdylib-link-arg=-Wl,-soname,libfoo.so.1.2.3 -# arbitrary user-defined metadata -cargo:root=/path/to/foo -cargo:libdir=/path/to/foo/lib -cargo:include=/path/to/foo/include -``` +The following is a summary of the instructions that Cargo recognizes, with each +one detailed below. -On the other hand, lines printed to stderr are written to a file like -`target/debug/build//stderr` but are not interpreted by cargo. - -There are a few special keys that Cargo recognizes, some affecting how the -crate is built: - -* `rustc-link-lib=[KIND=]NAME` indicates that the specified value is a library - name and should be passed to the compiler as a `-l` flag. The optional `KIND` - can be one of `static`, `dylib` (the default), or `framework`, see - `rustc --help` for more details. -* `rustc-link-search=[KIND=]PATH` indicates the specified value is a library - search path and should be passed to the compiler as a `-L` flag. The optional - `KIND` can be one of `dependency`, `crate`, `native`, `framework` or `all` - (the default), see `rustc --help` for more details. -* `rustc-flags=FLAGS` is a set of flags passed to the compiler, only `-l` and - `-L` flags are supported. -* `rustc-cfg=FEATURE` indicates that the specified feature will be passed as a - `--cfg` flag to the compiler. This is often useful for performing compile-time - detection of various features. -* `rustc-env=VAR=VALUE` indicates that the specified environment variable - will be added to the environment which the compiler is run within. - The value can be then retrieved by the `env!` macro in the compiled crate. - This is useful for embedding additional metadata in crate's code, - such as the hash of Git HEAD or the unique identifier of a continuous - integration server. -* `rustc-cdylib-link-arg=FLAG` is a flag passed to the compiler as - `-C link-arg=FLAG` when building a `cdylib`. Its usage is highly platform - specific. It is useful to set the shared library version or the runtime-path. -* `rerun-if-changed=PATH` is a path to a file or directory which indicates that - the build script should be re-run if it changes (detected by a more-recent - last-modified timestamp on the file). Normally build scripts are re-run if - any file inside the crate root changes, but this can be used to scope changes - to just a small set of files. (If this path points to a directory the entire - directory will not be traversed for changes -- only changes to the timestamp - of the directory itself (which corresponds to some types of changes within the - directory, depending on platform) will trigger a rebuild. To request a re-run - on any changes within an entire directory, print a line for the directory and - another line for everything inside it, recursively.) - Note that if the build script itself (or one of its dependencies) changes, - then it's rebuilt and rerun unconditionally, so - `cargo:rerun-if-changed=build.rs` is almost always redundant (unless you - want to ignore changes in all other files except for `build.rs`). -* `rerun-if-env-changed=VAR` is the name of an environment variable which - indicates that if the environment variable's value changes the build script - should be rerun. This basically behaves the same as `rerun-if-changed` except - that it works with environment variables instead. Note that the environment - variables here are intended for global environment variables like `CC` and - such, it's not necessary to use this for env vars like `TARGET` that Cargo - sets. Also note that if `rerun-if-env-changed` is printed out then Cargo will - *only* rerun the build script if those environment variables change or if - files printed out by `rerun-if-changed` change. - -* `warning=MESSAGE` is a message that will be printed to the main console after - a build script has finished running. Warnings are only shown for path - dependencies (that is, those you're working on locally), so for example - warnings printed out in crates.io crates are not emitted by default. - -Any other element is a user-defined metadata that will be passed to -dependents. More information about this can be found in the [`links`][links] -section. - -[links]: #the-links-manifest-key +* [`cargo:rerun-if-changed=PATH`](#rerun-if-changed) — Used to tell Cargo when + to re-run the script. +* [`cargo:rerun-if-env-changed=VAR`](#rerun-if-env-changed) — Used to tell + Cargo when to re-run the script. +* [`cargo:rustc-link-lib=[KIND=]NAME`](#rustc-link-lib) — Used to link a + library. +* [`cargo:rustc-link-search=[KIND=]PATH`](#rustc-link-search) — Used to add to + the library search path. +* [`cargo:rustc-flags=FLAGS`](#rustc-flags) — Used to pass certain flags to + the compiler. +* [`cargo:rustc-cfg=KEY[="VALUE"]`](#rustc-cfg) — Used to enable compile-time + `cfg` settings. +* [`cargo:rustc-env=VAR=VALUE`](#rustc-env) — Used to set an environment + variable. +* [`cargo:rustc-cdylib-link-arg=FLAG`](#rustc-cdylib-link-arg) — Used to pass + custom flags to a linker for cdylib crates. +* [`cargo:warning=MESSAGE`](#cargo-warning) — Used to display a warning on the + terminal. +* [`cargo:KEY=VALUE`](#the-links-manifest-key) — Metadata, used by `links` + scripts. + + +#### `cargo:rustc-link-lib=[KIND=]NAME` + +The `rustc-link-lib` instruction tells Cargo to link the given library using +the compiler's [`-l` flag][option-link]. This is typically used to link a +native library using [FFI]. + +The `-l` flag is only passed to the library target of the package, unless +there is no library target, in which case it is passed to all targets. This is +done because all other targets have an implicit dependency on the library +target, and the given library to link should only be included once. This means +that if a package has both a library and a binary target, the *library* has +access to the symbols from the given lib, and the binary should access them +through the library target's public API. + +The optional `KIND` may be one of `dylib`, `static`, or `framework`. See the +[rustc book][option-link] for more detail. + +[option-link]: ../../rustc/command-line-arguments.md#option-l-link-lib +[FFI]: ../../nomicon/ffi.md + + +#### `cargo:rustc-link-search=[KIND=]PATH` + +The `rustc-link-search` instruction tells Cargo to pass the [`-L` +flag][option-search] to the compiler to add a directory to the library search +path. + +The optional `KIND` may be one of `dependency`, `crate`, `native`, +`framework`, or `all`. See the [rustc book][option-search] for more detail. + +These paths are also added to the [dynamic library search path environment +variable](environment-variables.md#dynamic-library-paths) if they are within +the `OUT_DIR`. Depending on this behavior is discouraged since this makes it +difficult to use the resulting binary. In general, it is best to avoid +creating dynamic libraries in a build script (using existing system libraries +is fine). + +[option-search]: ../../rustc/command-line-arguments.md#option-l-search-path + + +#### `cargo:rustc-flags=FLAGS` + +The `rustc-flags` instruction tells Cargo to pass the given space-separated +flags to the compiler. This only allows the `-l` and `-L` flags, and is +equivalent to using [`rustc-link-lib`](#rustc-link-lib) and +[`rustc-link-search`](#rustc-link-search). + + +#### `cargo:rustc-cfg=KEY[="VALUE"]` + +The `rustc-cfg` instruction tells Cargo to pass the given value to the +[`--cfg` flag][option-cfg] to the compiler. This may be used for compile-time +detection of features to enable [conditional compilation]. + +Note that this does *not* affect Cargo's dependency resolution. This cannot be +used to enable an optional dependency, or enable other Cargo features. + +Be aware that [Cargo features] use the form `feature="foo"`. `cfg` values +passed with this flag are not restricted to that form, and may provide just a +single identifier, or any arbitrary key/value pair. For example, emitting +`cargo:rustc-cfg=abc` will then allow code to use `#[cfg(abc)]` (note the lack +of `feature=`). Or an arbitrary key/value pair may be used with an `=` symbol +like `cargo:rustc-cfg=my_component="foo"`. The key should be a Rust +identifier, the value should be a string. + +[cargo features]: manifest.md#the-features-section +[conditional compilation]: ../../reference/conditional-compilation.md +[option-cfg]: ../../rustc/command-line-arguments.md#option-cfg + + +#### `cargo:rustc-env=VAR=VALUE` + +The `rustc-env` instruction tells Cargo to set the given environment variable +when compiling the package. The value can be then retrieved by the [`env!` +macro][env-macro] in the compiled crate. This is useful for embedding +additional metadata in crate's code, such as the hash of git HEAD or the +unique identifier of a continuous integration server. + +See also the [environment variables automatically included by +Cargo][env-cargo]. + +[env-macro]: ../../std/macro.env.html +[env-cargo]: environment-variables.md#environment-variables-cargo-sets-for-crates + + +#### `cargo:rustc-cdylib-link-arg=FLAG` + +The `rustc-cdylib-link-arg` instruction tells Cargo to pass the [`-C +link-arg=FLAG` option][link-arg] to the compiler, but only when building a +`cdylib` library target. Its usage is highly platform specific. It is useful +to set the shared library version or the runtime-path. + +[link-arg]: ../../rustc/codegen-options/index.md#link-arg + + +#### `cargo:warning=MESSAGE` + +The `warning` instruction tells Cargo to display a warning after the build +script has finished running. Warnings are only shown for `path` dependencies +(that is, those you're working on locally), so for example warnings printed +out in [crates.io] crates are not emitted by default. The `-vv` "very verbose" +flag may be used to have Cargo display warnings for all crates. ### Build Dependencies @@ -140,52 +223,128 @@ manifest. ```toml [build-dependencies] -foo = { git = "https://github.com/your-packages/foo" } +cc = "1.0.46" ``` The build script **does not** have access to the dependencies listed in the -`dependencies` or `dev-dependencies` section (they’re not built yet!). All build -dependencies will also not be available to the package itself unless explicitly -stated as so. +`dependencies` or `dev-dependencies` section (they’re not built yet!). Also, +build dependencies are not available to the package itself unless also +explicitly added in the `[dependencies]` table. + +### Change Detection + +When rebuilding a package, Cargo does not necessarily know if the build script +needs to be run again. By default, it takes a conservative approach of always +re-running the build script if any file within the package is changed. For +most cases, this is not a good choice, so it is recommended that every build +script emit at least one of the `rerun-if` instructions (described below). If +these are emitted, then Cargo will only re-run the script if the given value +has changed. + + +#### `cargo:rerun-if-changed=PATH` + +The `rerun-if-changed` instruction tells Cargo to re-run the build script if +the file at the given path has changed. Currently, Cargo only uses the +filesystem last-modified "mtime" timestamp to determine if the file has +changed. It compares against an internal cached timestamp of when the build +script last ran. + +If the path points to a directory, it does *not* automatically traverse the +directory for changes. Only the mtime change of the directory itself is +considered (which corresponds to some types of changes within the directory, +depending on platform). To request a re-run on any changes within an entire +directory, print a line for the directory and separate lines for everything +inside it, recursively. + +If the build script inherently does not need to re-run under any circumstance, +then emitting `cargo:rerun-if-changed=build.rs` is a simple way to prevent it +from being re-run. Cargo automatically handles whether or not the script +itself needs to be recompiled, and of course the script will be re-run after +it has been recompiled. Otherwise, specifying `build.rs` is redundant and +unnecessary. + + +#### `cargo:rerun-if-env-changed=NAME` + +The `rerun-if-env-changed` instruction tells Cargo to re-run the build script +if the value of an environment variable of the given name has changed. + +Note that the environment variables here are intended for global environment +variables like `CC` and such, it is not necessary to use this for environment +variables like `TARGET` that Cargo sets. + ### The `links` Manifest Key -In addition to the manifest key `build`, Cargo also supports a `links` manifest -key to declare the name of a native library that is being linked to: +The `package.links` key may be set in the `Cargo.toml` manifest to declare +that the package links with the given native library. The purpose of this +manifest key is to give Cargo an understanding about the set of native +dependencies that a package has, as well as providing a principled system of +passing metadata between package build scripts. ```toml [package] # ... links = "foo" -build = "build.rs" ``` -This manifest states that the package links to the `libfoo` native library, and -it also has a build script for locating and/or building the library. Cargo -requires that a `build` command is specified if a `links` entry is also -specified. - -The purpose of this manifest key is to give Cargo an understanding about the set -of native dependencies that a package has, as well as providing a principled -system of passing metadata between package build scripts. +This manifest states that the package links to the `libfoo` native library. +When using the `links` key, the package must have a build script, and the +build script should use the [`rustc-link-lib` instruction](#rustc-link-lib) to +link the library. Primarily, Cargo requires that there is at most one package per `links` value. -In other words, it’s forbidden to have two packages link to the same native -library. Note, however, that there are [conventions in place][star-sys] to -alleviate this. - -[star-sys]: #-sys-packages +In other words, it is forbidden to have two packages link to the same native +library. This helps prevent duplicate symbols between crates. Note, however, +that there are [conventions in place](#-sys-packages) to alleviate this. As mentioned above in the output format, each build script can generate an arbitrary set of metadata in the form of key-value pairs. This metadata is -passed to the build scripts of **dependent** packages. For example, if `libbar` -depends on `libfoo`, then if `libfoo` generates `key=value` as part of its -metadata, then the build script of `libbar` will have the environment variables -`DEP_FOO_KEY=value`. +passed to the build scripts of **dependent** packages. For example, if the +package `bar` depends on `foo`, then if `foo` generates `key=value` as part of +its build script metadata, then the build script of `bar` will have the +environment variables `DEP_FOO_KEY=value`. See the ["Using another `sys` +crate"][using-another-sys] for an example of +how this can be used. Note that metadata is only passed to immediate dependents, not transitive -dependents. The motivation for this metadata passing is outlined in the linking -to system libraries case study below. +dependents. + +[using-another-sys]: build-script-examples.md#using-another-sys-crate + +### `*-sys` Packages + +Some Cargo packages that link to system libraries have a naming convention of +having a `-sys` suffix. Any package named `foo-sys` should provide two major +pieces of functionality: + +* The library crate should link to the native library `libfoo`. This will often + probe the current system for `libfoo` before resorting to building from + source. +* The library crate should provide **declarations** for functions in `libfoo`, + but **not** bindings or higher-level abstractions. + +The set of `*-sys` packages provides a common set of dependencies for linking +to native libraries. There are a number of benefits earned from having this +convention of native-library-related packages: + +* Common dependencies on `foo-sys` alleviates the rule about one package per + value of `links`. +* Other `-sys` packages can take advantage of the `DEP_NAME_KEY=value` + environment variables to better integrate with other packages. See the + ["Using another `sys` crate"][using-another-sys] example. +* A common dependency allows centralizing logic on discovering `libfoo` itself + (or building it from source). +* These dependencies are easily [overridable](#overriding-build-scripts). + +It is common to have a companion package without the `-sys` suffix that +provides a safe, high-level abstractions on top of the sys package. For +example, the [`git2` crate] provides a high-level interface to the +[`libgit2-sys` crate]. + +[`git2` crate]: https://crates.io/crates/git2 +[`libgit2-sys` crate]: https://crates.io/crates/libgit2-sys ### Overriding Build Scripts @@ -215,360 +374,22 @@ With this configuration, if a package declares that it links to `foo` then the build script will **not** be compiled or run, and the metadata specified will instead be used. -### Case study: Code generation +### Jobserver -Some Cargo packages need to have code generated just before they are compiled -for various reasons. Here we’ll walk through a simple example which generates a -library call as part of the build script. +Cargo and `rustc` use the [jobserver protocol], developed for GNU make, to +coordinate concurrency across processes. It is essentially a semaphore that +controls the number of jobs running concurrently. The concurrency may be set +with the `--jobs` flag, which defaults to the number of logical CPUs. -First, let’s take a look at the directory structure of this package: +Each build script inherits one job slot from Cargo, and should endeavor to +only use one CPU while it runs. If the script wants to use more CPUs in +parallel, it should use the [`jobserver` crate] to coordinate with Cargo. -``` -. -├── Cargo.toml -├── build.rs -└── src - └── main.rs - -1 directory, 3 files -``` - -Here we can see that we have a `build.rs` build script and our binary in -`main.rs`. Next, let’s take a look at the manifest: - -```toml -# Cargo.toml - -[package] -name = "hello-from-generated-code" -version = "0.1.0" -authors = ["you@example.com"] -build = "build.rs" -``` - -Here we can see we’ve got a build script specified which we’ll use to generate -some code. Let’s see what’s inside the build script: - -```rust,no_run -// build.rs - -use std::env; -use std::fs::File; -use std::io::Write; -use std::path::Path; - -fn main() { - let out_dir = env::var("OUT_DIR").unwrap(); - let dest_path = Path::new(&out_dir).join("hello.rs"); - let mut f = File::create(&dest_path).unwrap(); - - f.write_all(b" - pub fn message() -> &'static str { - \"Hello, World!\" - } - ").unwrap(); -} -``` +As an example, the [`cc` crate] may enable the optional `parallel` feature +which will use the jobserver protocol to attempt to build multiple C files +at the same time. -There’s a couple of points of note here: - -* The script uses the `OUT_DIR` environment variable to discover where the - output files should be located. It can use the process’ current working - directory to find where the input files should be located, but in this case we - don’t have any input files. -* In general, build scripts should not modify any files outside of `OUT_DIR`. - It may seem fine on the first blush, but it does cause problems when you use - such crate as a dependency, because there's an *implicit* invariant that - sources in `.cargo/registry` should be immutable. `cargo` won't allow such - scripts when packaging. -* This script is relatively simple as it just writes out a small generated file. - One could imagine that other more fanciful operations could take place such as - generating a Rust module from a C header file or another language definition, - for example. - -Next, let’s peek at the library itself: - -```rust,ignore -// src/main.rs - -include!(concat!(env!("OUT_DIR"), "/hello.rs")); - -fn main() { - println!("{}", message()); -} -``` - -This is where the real magic happens. The library is using the rustc-defined -`include!` macro in combination with the `concat!` and `env!` macros to include -the generated file (`hello.rs`) into the crate’s compilation. - -Using the structure shown here, crates can include any number of generated files -from the build script itself. - -### Case study: Building some native code - -Sometimes it’s necessary to build some native C or C++ code as part of a -package. This is another excellent use case of leveraging the build script to -build a native library before the Rust crate itself. As an example, we’ll create -a Rust library which calls into C to print “Hello, World!”. - -Like above, let’s first take a look at the package layout: - -``` -. -├── Cargo.toml -├── build.rs -└── src - ├── hello.c - └── main.rs - -1 directory, 4 files -``` - -Pretty similar to before! Next, the manifest: - -```toml -# Cargo.toml - -[package] -name = "hello-world-from-c" -version = "0.1.0" -authors = ["you@example.com"] -build = "build.rs" -``` - -For now we’re not going to use any build dependencies, so let’s take a look at -the build script now: - -```rust,no_run -// build.rs - -use std::process::Command; -use std::env; -use std::path::Path; - -fn main() { - let out_dir = env::var("OUT_DIR").unwrap(); - - // note that there are a number of downsides to this approach, the comments - // below detail how to improve the portability of these commands. - Command::new("gcc").args(&["src/hello.c", "-c", "-fPIC", "-o"]) - .arg(&format!("{}/hello.o", out_dir)) - .status().unwrap(); - Command::new("ar").args(&["crus", "libhello.a", "hello.o"]) - .current_dir(&Path::new(&out_dir)) - .status().unwrap(); - - println!("cargo:rustc-link-search=native={}", out_dir); - println!("cargo:rustc-link-lib=static=hello"); -} -``` - -This build script starts out by compiling our C file into an object file (by -invoking `gcc`) and then converting this object file into a static library (by -invoking `ar`). The final step is feedback to Cargo itself to say that our -output was in `out_dir` and the compiler should link the crate to `libhello.a` -statically via the `-l static=hello` flag. - -Note that there are a number of drawbacks to this hardcoded approach: - -* The `gcc` command itself is not portable across platforms. For example it’s - unlikely that Windows platforms have `gcc`, and not even all Unix platforms - may have `gcc`. The `ar` command is also in a similar situation. -* These commands do not take cross-compilation into account. If we’re cross - compiling for a platform such as Android it’s unlikely that `gcc` will produce - an ARM executable. - -Not to fear, though, this is where a `build-dependencies` entry would help! The -Cargo ecosystem has a number of packages to make this sort of task much easier, -portable, and standardized. For example, the build script could be written as: - -```rust,ignore -// build.rs - -// Bring in a dependency on an externally maintained `cc` package which manages -// invoking the C compiler. -extern crate cc; - -fn main() { - cc::Build::new() - .file("src/hello.c") - .compile("hello"); -} -``` - -Add a build time dependency on the `cc` crate with the following addition to -your `Cargo.toml`: - -```toml -[build-dependencies] -cc = "1.0" -``` - -The [`cc` crate](https://crates.io/crates/cc) abstracts a range of build -script requirements for C code: - -* It invokes the appropriate compiler (MSVC for windows, `gcc` for MinGW, `cc` - for Unix platforms, etc.). -* It takes the `TARGET` variable into account by passing appropriate flags to - the compiler being used. -* Other environment variables, such as `OPT_LEVEL`, `DEBUG`, etc., are all - handled automatically. -* The stdout output and `OUT_DIR` locations are also handled by the `cc` - library. - -Here we can start to see some of the major benefits of farming as much -functionality as possible out to common build dependencies rather than -duplicating logic across all build scripts! - -Back to the case study though, let’s take a quick look at the contents of the -`src` directory: - -```c -// src/hello.c - -#include - -void hello() { - printf("Hello, World!\n"); -} -``` - -```rust,ignore -// src/main.rs - -// Note the lack of the `#[link]` attribute. We’re delegating the responsibility -// of selecting what to link to over to the build script rather than hardcoding -// it in the source file. -extern { fn hello(); } - -fn main() { - unsafe { hello(); } -} -``` - -And there we go! This should complete our example of building some C code from a -Cargo package using the build script itself. This also shows why using a build -dependency can be crucial in many situations and even much more concise! - -We’ve also seen a brief example of how a build script can use a crate as a -dependency purely for the build process and not for the crate itself at runtime. - -### Case study: Linking to system libraries - -The final case study here will be investigating how a Cargo library links to a -system library and how the build script is leveraged to support this use case. - -Quite frequently a Rust crate wants to link to a native library often provided -on the system to bind its functionality or just use it as part of an -implementation detail. This is quite a nuanced problem when it comes to -performing this in a platform-agnostic fashion, and the purpose of a build -script is again to farm out as much of this as possible to make this as easy as -possible for consumers. - -As an example to follow, let’s take a look at one of [Cargo’s own -dependencies][git2-rs], [libgit2][libgit2]. The C library has a number of -constraints: - -[git2-rs]: https://github.com/alexcrichton/git2-rs/tree/master/libgit2-sys -[libgit2]: https://github.com/libgit2/libgit2 - -* It has an optional dependency on OpenSSL on Unix to implement the https - transport. -* It has an optional dependency on libssh2 on all platforms to implement the ssh - transport. -* It is often not installed on all systems by default. -* It can be built from source using `cmake`. - -To visualize what’s going on here, let’s take a look at the manifest for the -relevant Cargo package that links to the native C library. - -```toml -[package] -name = "libgit2-sys" -version = "0.1.0" -authors = ["..."] -links = "git2" -build = "build.rs" - -[dependencies] -libssh2-sys = { git = "https://github.com/alexcrichton/ssh2-rs" } - -[target.'cfg(unix)'.dependencies] -openssl-sys = { git = "https://github.com/alexcrichton/openssl-sys" } - -# ... -``` - -As the above manifests show, we’ve got a `build` script specified, but it’s -worth noting that this example has a `links` entry which indicates that the -crate (`libgit2-sys`) links to the `git2` native library. - -Here we also see that we chose to have the Rust crate have an unconditional -dependency on `libssh2` via the `libssh2-sys` crate, as well as a -platform-specific dependency on `openssl-sys` for \*nix (other variants elided -for now). It may seem a little counterintuitive to express *C dependencies* in -the *Cargo manifest*, but this is actually using one of Cargo’s conventions in -this space. - -### `*-sys` Packages - -To alleviate linking to system libraries, crates.io has a *convention* of package -naming and functionality. Any package named `foo-sys` should provide two major -pieces of functionality: - -* The library crate should link to the native library `libfoo`. This will often - probe the current system for `libfoo` before resorting to building from - source. -* The library crate should provide **declarations** for functions in `libfoo`, - but **not** bindings or higher-level abstractions. - -The set of `*-sys` packages provides a common set of dependencies for linking -to native libraries. There are a number of benefits earned from having this -convention of native-library-related packages: - -* Common dependencies on `foo-sys` alleviates the above rule about one package - per value of `links`. -* A common dependency allows centralizing logic on discovering `libfoo` itself - (or building it from source). -* These dependencies are easily overridable. - -### Building libgit2 - -Now that we’ve got libgit2’s dependencies sorted out, we need to actually write -the build script. We’re not going to look at specific snippets of code here and -instead only take a look at the high-level details of the build script of -`libgit2-sys`. This is not recommending all packages follow this strategy, but -rather just outlining one specific strategy. - -The first step of the build script should do is to query whether libgit2 is -already installed on the host system. To do this we’ll leverage the preexisting -tool `pkg-config` (when its available). We’ll also use a `build-dependencies` -section to refactor out all the `pkg-config` related code (or someone’s already -done that!). - -If `pkg-config` failed to find libgit2, or if `pkg-config` just wasn’t -installed, the next step is to build libgit2 from bundled source code -(distributed as part of `libgit2-sys` itself). There are a few nuances when -doing so that we need to take into account, however: - -* The build system of libgit2, `cmake`, needs to be able to find libgit2’s - optional dependency of libssh2. We’re sure we’ve already built it (it’s a - Cargo dependency), we just need to communicate this information. To do this - we leverage the metadata format to communicate information between build - scripts. In this example the libssh2 package printed out `cargo:root=...` to - tell us where libssh2 is installed at, and we can then pass this along to - cmake with the `CMAKE_PREFIX_PATH` environment variable. - -* We’ll need to handle some `CFLAGS` values when compiling C code (and tell - `cmake` about this). Some flags we may want to pass are `-m64` for 64-bit - code, `-m32` for 32-bit code, or `-fPIC` for 64-bit code as well. - -* Finally, we’ll invoke `cmake` to place all output into the `OUT_DIR` - environment variable, and then we’ll print the necessary metadata to instruct - rustc how to link to libgit2. - -Most of the functionality of this build script is easily refactorable into -common dependencies, so our build script isn’t quite as intimidating as this -descriptions! In reality it’s expected that build scripts are quite succinct by -farming logic such as above to build dependencies. +[`cc` crate]: https://crates.io/crates/cc +[`jobserver` crate]: https://crates.io/crates/jobserver +[jobserver protocol]: http://make.mad-scientist.net/papers/jobserver-implementation/ +[crates.io]: https://crates.io/ diff --git a/src/doc/src/reference/config.md b/src/doc/src/reference/config.md index 067a03faff9..ae6bcd8ac00 100644 --- a/src/doc/src/reference/config.md +++ b/src/doc/src/reference/config.md @@ -76,6 +76,13 @@ runner = ".." # this value overrides build.rustflags when both are present rustflags = ["..", ".."] +# A package with the `links` key can override the build script with a +# table with the name of the `links` library. +[target.$triple.$links] +# Any build script outputs may be included here as separate keys. +rustc-link-search = ["/path/to/foo"] +rustc-link-lib = ["foo"] + [target.'cfg(...)'] # Similar for the $triple configuration, but using the `cfg` syntax. # If several `cfg` and $triple targets are candidates, then the rustflags diff --git a/src/doc/src/reference/environment-variables.md b/src/doc/src/reference/environment-variables.md index 28584760767..fda387bad34 100644 --- a/src/doc/src/reference/environment-variables.md +++ b/src/doc/src/reference/environment-variables.md @@ -45,8 +45,9 @@ configuration values, as described in [that documentation][config-env] ### Environment variables Cargo sets for crates Cargo exposes these environment variables to your crate when it is compiled. -Note that this applies for test binaries as well. -To get the value of any of these variables in a Rust program, do this: +Note that this applies for running binaries with `cargo run` and `cargo test` +as well. To get the value of any of these variables in a Rust program, do +this: ```rust let version = env!("CARGO_PKG_VERSION"); @@ -68,6 +69,34 @@ let version = env!("CARGO_PKG_VERSION"); * `CARGO_PKG_REPOSITORY` - The repository from the manifest of your package. * `OUT_DIR` - If the package has a build script, this is set to the folder where the build script should place its output. See below for more information. + (Only set during compilation.) + +#### Dynamic library paths + +Cargo also sets the dynamic library path when compiling and running binaries +with commands like `cargo run` and `cargo test`. This helps with locating +shared libraries that are part of the build process. The variable name depends +on the platform: + +* Windows: `PATH` +* macOS: `DYLD_FALLBACK_LIBRARY_PATH` +* Unix: `LD_LIBRARY_PATH` + +The value is extended from the existing value when Cargo starts. macOS has +special consideration where if `DYLD_FALLBACK_LIBRARY_PATH` is not already +set, it will add the default `$HOME/lib:/usr/local/lib:/usr/lib`. + +Cargo includes the following paths: + +* Search paths included from any build script with the [`rustc-link-search` + instruction](build-scripts.md#rustc-link-search). Paths outside of the + `target` directory are removed. It is the responsibility of the user running + Cargo to properly set the environment if additional libraries on the system + are needed in the search path. +* The base output directory, such as `target/debug`, and the "deps" directory. + This is mostly for legacy support of `rustc` compiler plugins. +* The rustc sysroot library path. This generally is not important to most + users. ### Environment variables Cargo sets for build scripts diff --git a/src/doc/src/reference/manifest.md b/src/doc/src/reference/manifest.md index 6cd87311345..230aa4f7922 100644 --- a/src/doc/src/reference/manifest.md +++ b/src/doc/src/reference/manifest.md @@ -71,6 +71,7 @@ latest edition. Setting the `edition` key in `[package]` will affect all targets/crates in the package, including test suites, benchmarks, binaries, examples, etc. + #### The `build` field (optional) This field specifies a file in the package root which is a [build script] for @@ -85,6 +86,11 @@ guide][build script]. build = "build.rs" ``` +The default is `"build.rs"`, which loads the script from a file named +`build.rs` in the root of the package. Use `build = "custom_build_name.rs"` to +specify a path to a different file or `build = false` to disable automatic +detection of the build script. + #### The `links` field (optional) This field specifies the name of a native library that is being linked to. @@ -97,7 +103,6 @@ script guide. [package] # ... links = "foo" -build = "build.rs" ``` #### The `documentation` field (optional) From da36b81a6b4091e29831aa8e7cfa38c1a6f22aec Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Wed, 6 Nov 2019 10:59:08 -0800 Subject: [PATCH 2/3] Fix some awkward wording. --- src/doc/src/reference/build-scripts.md | 31 +++++++++++++------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/doc/src/reference/build-scripts.md b/src/doc/src/reference/build-scripts.md index e6fbf9e58a6..03b60623fde 100644 --- a/src/doc/src/reference/build-scripts.md +++ b/src/doc/src/reference/build-scripts.md @@ -90,23 +90,22 @@ configuration). The stderr output is also saved in that same directory. The following is a summary of the instructions that Cargo recognizes, with each one detailed below. -* [`cargo:rerun-if-changed=PATH`](#rerun-if-changed) — Used to tell Cargo when +* [`cargo:rerun-if-changed=PATH`](#rerun-if-changed) — Tells Cargo when to + re-run the script. +* [`cargo:rerun-if-env-changed=VAR`](#rerun-if-env-changed) — Tells Cargo when to re-run the script. -* [`cargo:rerun-if-env-changed=VAR`](#rerun-if-env-changed) — Used to tell - Cargo when to re-run the script. -* [`cargo:rustc-link-lib=[KIND=]NAME`](#rustc-link-lib) — Used to link a - library. -* [`cargo:rustc-link-search=[KIND=]PATH`](#rustc-link-search) — Used to add to - the library search path. -* [`cargo:rustc-flags=FLAGS`](#rustc-flags) — Used to pass certain flags to - the compiler. -* [`cargo:rustc-cfg=KEY[="VALUE"]`](#rustc-cfg) — Used to enable compile-time - `cfg` settings. -* [`cargo:rustc-env=VAR=VALUE`](#rustc-env) — Used to set an environment - variable. -* [`cargo:rustc-cdylib-link-arg=FLAG`](#rustc-cdylib-link-arg) — Used to pass - custom flags to a linker for cdylib crates. -* [`cargo:warning=MESSAGE`](#cargo-warning) — Used to display a warning on the +* [`cargo:rustc-link-lib=[KIND=]NAME`](#rustc-link-lib) — Adds a library to + link. +* [`cargo:rustc-link-search=[KIND=]PATH`](#rustc-link-search) — Adds to the + library search path. +* [`cargo:rustc-flags=FLAGS`](#rustc-flags) — Passes certain flags to the + compiler. +* [`cargo:rustc-cfg=KEY[="VALUE"]`](#rustc-cfg) — Enables compile-time `cfg` + settings. +* [`cargo:rustc-env=VAR=VALUE`](#rustc-env) — Sets an environment variable. +* [`cargo:rustc-cdylib-link-arg=FLAG`](#rustc-cdylib-link-arg) — Passes custom + flags to a linker for cdylib crates. +* [`cargo:warning=MESSAGE`](#cargo-warning) — Displays a warning on the terminal. * [`cargo:KEY=VALUE`](#the-links-manifest-key) — Metadata, used by `links` scripts. From dd546ea47ef0fc00d0014ca0d176871be87b9046 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Thu, 7 Nov 2019 09:02:49 -0800 Subject: [PATCH 3/3] Add some warnings/disclaimers about build-dependencies. Also add `autocfg` to the list. --- src/doc/src/reference/build-script-examples.md | 10 +++++++--- src/doc/src/reference/build-scripts.md | 6 ++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/doc/src/reference/build-script-examples.md b/src/doc/src/reference/build-script-examples.md index c2869b5dea9..bd7f9e1e3d5 100644 --- a/src/doc/src/reference/build-script-examples.md +++ b/src/doc/src/reference/build-script-examples.md @@ -5,7 +5,7 @@ The following sections illustrate some examples of writing build scripts. Some common build script functionality can be found via crates on [crates.io]. Check out the [`build-dependencies` keyword](https://crates.io/keywords/build-dependencies) to see what is -available. Some popular crates are: +available. The following is a sample of some popular crates[^†]: * [`bindgen`](https://crates.io/crates/bindgen) — Automatically generate Rust FFI bindings to C libraries. @@ -13,10 +13,14 @@ available. Some popular crates are: * [`pkg-config`](https://crates.io/crates/pkg-config) — Detect system libraries using the `pkg-config` utility. * [`cmake`](https://crates.io/crates/cmake) — Runs the `cmake` build tool to build a native library. -* [`rustc_version`](https://crates.io/crates/rustc_version), +* [`autocfg`](https://crates.io/crates/autocfg), + [`rustc_version`](https://crates.io/crates/rustc_version), [`version_check`](https://crates.io/crates/version_check) — These crates provide ways to implement conditional compilation based on the current - version of `rustc`. + `rustc` such as the version of the compiler. + +[^†]: This list is not an endorsement. Evaluate your dependencies to see which +is right for your project. ### Code generation diff --git a/src/doc/src/reference/build-scripts.md b/src/doc/src/reference/build-scripts.md index 03b60623fde..e8ae3ff8735 100644 --- a/src/doc/src/reference/build-scripts.md +++ b/src/doc/src/reference/build-scripts.md @@ -230,6 +230,12 @@ The build script **does not** have access to the dependencies listed in the build dependencies are not available to the package itself unless also explicitly added in the `[dependencies]` table. +It is recommended to carefully consider each dependency you add, weighing +against the impact on compile time, licensing, maintenance, etc. Cargo will +attempt to reuse a dependency if it is shared between build dependencies and +normal dependencies. However, this is not always possible, for example when +cross-compiling, so keep that in consideration of the impact on compile time. + ### Change Detection When rebuilding a package, Cargo does not necessarily know if the build script