diff --git a/src/conditional-compilation.md b/src/conditional-compilation.md index 7df8566e7..08de20ced 100644 --- a/src/conditional-compilation.md +++ b/src/conditional-compilation.md @@ -1,18 +1,216 @@ -## Conditional compilation +# Conditional compilation -Sometimes one wants to have different compiler outputs from the same code, -depending on build target, such as targeted operating system, or to enable -release builds. +> **Syntax**\ +> _ConfigurationPredicate_ :\ +>       _ConfigurationOption_\ +>    | _ConfigurationAll_\ +>    | _ConfigurationAny_\ +>    | _ConfigurationNot_ +> +> _ConfigurationOption_ :\ +>    [IDENTIFIER] (`=` ([STRING_LITERAL] | [RAW_STRING_LITERAL]))? +> +> _ConfigurationAll_\ +>    `all` `(` _ConfigurationPredicateList_? `)` +> +> _ConfigurationAny_\ +>    `any` `(` _ConfigurationPredicateList_? `)` +> +> _ConfigurationNot_\ +>    `not` `(` _ConfigurationPredicate_ `)` +> +> _ConfigurationPredicateList_\ +>    _ConfigurationPredicate_ (`,` _ConfigurationPredicate_)\* `,`? -Configuration options are boolean (on or off) and are named either with a -single identifier (e.g. `foo`) or an identifier and a string (e.g. `foo = "bar"`; -the quotes are required and spaces around the `=` are unimportant). Note that -similarly-named options, such as `foo`, `foo="bar"` and `foo="baz"` may each be -set or unset independently. +*Conditionally compiled source code* is source code that may or may not be +considered a part of the source code depending on certain conditions. Source code can be conditionally compiled +using [attributes], [`cfg`] and [`cfg_attr`], and the built-in [`cfg` macro]. +These conditions are based on the target architecture of the compiled crate, +arbitrary values passed to the compiler, and a few other miscellaneous things +further described below in detail. -Configuration options are either provided by the compiler or passed in on the -command line using `--cfg` (e.g. `rustc main.rs --cfg foo --cfg 'bar="baz"'`). -Rust code then checks for their presence using the `#[cfg(...)]` [attribute]: +Each form of conditional compilation takes a _configuration predicate_ that +evaluates to true or false. The predicate is one of the following: + +* A configuration option. It is true if the option is set and false if it is + unset. +* `all()` with a comma separated list of configuration predicates. It is false + if at least one predicate is false. If there are no predicates, it is true. +* `any()` with a comma separated list of configuration predicates. It is true + if at least one predicate is true. If there are no predicates, it is false. +* `not()` with a configuration predicate. It is true if its predicate is false + and false if its predicate is true. + +_Configuration options_ are names and key-value pairs that are either set or +unset. Names are written as a single identifier such as, for example, `unix`. +Key-value pairs are written as an identifier, `=`, and then a string. For +example, `target_arch = "x86_64"` is a configuration option. + +> **Note**: Whitespace around the `=` is ignored. `foo="bar"` and `foo = "bar"` +> are equivalent configuration options. + +Keys are not unique in the set of key-value configuration options. For example, +both `feature = "std"` and `feature = "serde"` can be set at the same time. + +## Set Configuration Options + +Which configuration options are set is determined statically during the +compilation of the crate. Certain options are _compiler-set_ based on data +about the compilation. Other options are _arbitrarily-set_, set based on input +passed to the compiler outside of the code. It is not possible to set a +configuration option from within the source code of the crate being compiled. + +> **Note**: For `rustc`, arbitrary-set configuration options are set using the +> [`--cfg`] flag. + +
+ +Warning: It is possible for arbitrarily-set configuration options to have the +same value as compiler-set configuration options. For example, it is possible +to do `rustc --cfg "unix" program.rs` while compiling to a Windows target, and +have both `unix` and `windows` configuration options set at the same time. It +is unwise to actually do this. + +
+ +### `target_arch` + +Key-value option set once with the target's CPU architecture. The value is +similar to the first element of the platform's target triple, but not +identical. + +Example values: + +* `"x86"` +* `"x86_64"` +* `"mips"` +* `"powerpc"` +* `"powerpc64"` +* `"arm"` +* `"aarch64"` + +### `target_os` + +Key-value option set once with the target's operating system. This value is +similar to the second and third element of the platform's target triple. + +Example values: + +* `"windows"` +* `"macos"` +* `"ios"` +* `"linux"` +* `"android"` +* `"freebsd"` +* `"dragonfly"` +* `"bitrig"` +* `"openbsd"` +* `"netbsd"` + +### `target_family` + +Key-value option set at most once with the target's operating system value. + +Example values: + +* `"unix"` +* `"windows"` + +### `unix` and `windows` + +`unix` is set if `target_family = "unix"` is set and `windows` is set if +`target_family = "windows"` is set. + +### `target_env` + +Key-value option set with further disambiguating information about the target +platform with information about the ABI or `libc` used. For historical reasons, +this value is only defined as not the empty-string when actually needed for +disambiguation. Thus, for example, on many GNU platforms, this value will be +empty. This value is similar to the fourth element of the platform's target +triple. One difference is that embedded ABIs such as `gnueabihf` will simply +define `target_env` as `"gnu"`. + +Example values: + +* `""` +* `"gnu"` +* `"msvc"` +* `"musl"` + +### `target_endian` + +Key-value option set once with either a value of "little" or "big" depending +on the endianness of the target's CPU. + +### `target_pointer_width` + +Key-value option set once with the target's pointer width in bits. For example, +for targets with 32-bit pointers, this is set to `"32"`. Likewise, it is set +to `"64"` for targets with 64-bit pointers. + + + +### `target_has_atomic` + +Key-value option set for each integer size on which the target can perform +atomic operations. + +Possible values: + +* `"8"` +* `"16"` +* `"32"` +* `"64"` +* `"ptr"` + +### `target_vendor` + +Key-value option set once with the vendor of the target. + +Possible values: + +* `"apple"` +* `"pc"` +* `"unknown"` + +### `test` + +Enabled when compiling the test harness. Done with `rustc` by using the +[`--test`] flag. + +### `debug_assertions` + +Enabled by default when compiling without optimizations. +This can be used to enable extra debugging code in development but not in +production. For example, it controls the behavior of the standard library's +[`debug_assert!`] macro. + +### `proc_macro` + +Set when the crate being compiled is being compiled with the `proc_macro` +[crate type]. + +## Forms of conditional compilation + +### The `cfg` attribute + +> **Syntax**\ +> _CfgAttrAttribute_ :\ +>    `cfg` `(` _ConfigurationPredicate_ `)` + + + +The `cfg` [attribute] conditionally includes the thing it is attached to based +on a configuration predicate. + +It is written as `cfg`, `(`, a configuration predicate, and finally `)`. + +If the predicate is true, the thing is rewritten to not have the `cfg` attribute +on it. If the predicate is false, the thing is removed from the source code. + +Some examples on functions: ```rust // The function is only included in the build when compiling for macOS @@ -41,62 +239,72 @@ fn needs_not_foo() { } ``` -This illustrates some conditional compilation can be achieved using the -`#[cfg(...)]` [attribute]. `any`, `all` and `not` can be used to assemble -arbitrarily complex configurations through nesting. - -The following configurations must be defined by the implementation: - -* `target_arch = "..."` - Target CPU architecture, such as `"x86"`, - `"x86_64"` `"mips"`, `"powerpc"`, `"powerpc64"`, `"arm"`, or - `"aarch64"`. This value is closely related to the first element of - the platform target triple, though it is not identical. -* `target_os = "..."` - Operating system of the target, examples - include `"windows"`, `"macos"`, `"ios"`, `"linux"`, `"android"`, - `"freebsd"`, `"dragonfly"`, `"bitrig"` , `"openbsd"` or - `"netbsd"`. This value is closely related to the second and third - element of the platform target triple, though it is not identical. -* `target_family = "..."` - Operating system family of the target, e. g. - `"unix"` or `"windows"`. The value of this configuration option is defined - as a configuration itself, like `unix` or `windows`. -* `unix` - See `target_family`. -* `windows` - See `target_family`. -* `target_env = ".."` - Further disambiguates the target platform with - information about the ABI/libc. Presently this value is either - `"gnu"`, `"msvc"`, `"musl"`, or the empty string. For historical - reasons this value has only been defined as non-empty when needed - for disambiguation. Thus on many GNU platforms this value will be - empty. This value is closely related to the fourth element of the - platform target triple, though it is not identical. For example, - embedded ABIs such as `gnueabihf` will simply define `target_env` as - `"gnu"`. -* `target_endian = "..."` - Endianness of the target CPU, either `"little"` or - `"big"`. -* `target_pointer_width = "..."` - Target pointer width in bits. This is set - to `"32"` for targets with 32-bit pointers, and likewise set to `"64"` for - 64-bit pointers. -* `target_has_atomic = "..."` - Set of integer sizes on which the target can perform - atomic operations. Values are `"8"`, `"16"`, `"32"`, `"64"` and `"ptr"`. -* `target_vendor = "..."` - Vendor of the target, for example `apple`, `pc`, or - simply `"unknown"`. -* `test` - Enabled when compiling the test harness (using the `--test` flag). -* `debug_assertions` - Enabled by default when compiling without optimizations. - This can be used to enable extra debugging code in development but not in - production. For example, it controls the behavior of the standard library's - `debug_assert!` macro. -* `proc_macro` - Set when the crate being compiled is being compiled with the - `proc_macro` [crate type]. - -You can also set another [attribute] based on a `cfg` variable with `cfg_attr`: +The `cfg` attribute is allowed anywhere attributes are allowed except on +generic parameters. + +### The `cfg_attr` attribute + +> **Syntax**\ +> _CfgAttrAttribute_ :\ +>    `cfg_attr` `(` _ConfigurationPredicate_ `,` [_MetaItem_] `,`? `)` + +The `cfg_attr` [attribute] conditionally includes [attributes] based on a +configuration predicate. + +It is written as `cfg_attr` followed by `(`, a configuration predicate, a +[metaitem], an optional `,`, and finally a `)`. + +When the configuration predicate is true, this attribute expands out to be an +attribute of the attribute metaitem. For example, the following module will +either be found at `linux.rs` or `windows.rs` based on the target. ```rust,ignore -#[cfg_attr(a, b)] +#[cfg_attr(linux, path = "linux.rs")] +#[cfg_attr(windows, path = "windows.rs")] +mod os; ``` -This is the same as `#[b]` if `a` is set by `cfg`, and nothing otherwise. +> **Note**: The `cfg_attr` can expand to another `cfg_attr`. For example, +> `#[cfg_attr(linux, cfg_attr(feature = "multithreaded", some_other_attribute))` +> is valid. This example would be equivalent to +> `#[cfg_attr(all(linux, feature ="multithreaded"), some_other_attribute)]`. + +The `cfg_attr` attribute is allowed anywhere attributes are allowed except on +generic parameters. + +### The `cfg` macro + +The built-in `cfg` macro takes in a single configuration predicate and evaluates +to the `true` literal when the predicate is true and the `false` literal when +it is false. -Lastly, configuration options can be used in expressions by invoking the `cfg!` -macro: `cfg!(a)` evaluates to `true` if `a` is set, and `false` otherwise. +For example: + +```rust +let machine_kind = if cfg!(unix) { + "unix" +} else if cfg!(windows) { + "windows" +} else { + "unknown" +}; + +println!("I'm running on a {} machine!", machine_kind); +``` +[IDENTIFIER]: identifiers.html +[RAW_STRING_LITERAL]: tokens.html#raw-string-literals +[STRING_LITERAL]: tokens.html#string-literals +[_MetaItem_]: attributes.html +[`--cfg`]: ../rustc/command-line-arguments.html#a--cfg-configure-the-compilation-environment +[`--test`]: ../rustc/command-line-arguments.html#a--test-build-a-test-harness +[`cfg`]: #the-cfg-attribute +[`cfg` macro]: #the-cfg-macro +[`cfg_attr`]: #the-cfg_attr-attribute +[`debug_assert!`]: ../std/macro.debug_assert.html [attribute]: attributes.html -[crate type]: linkage.html \ No newline at end of file +[attributes]: attributes.html +[crate type]: linkage.html +[expressions]: expressions.html +[items]: items.html +[metaitem]: attributes.html \ No newline at end of file