Skip to content

Commit 2febdc5

Browse files
Merge pull request #444 from Havvy/conditional-configuration
Referenceify conditional compilation
2 parents 376b3c1 + 1a5197a commit 2febdc5

File tree

1 file changed

+272
-64
lines changed

1 file changed

+272
-64
lines changed

src/conditional-compilation.md

Lines changed: 272 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,216 @@
1-
## Conditional compilation
1+
# Conditional compilation
22

3-
Sometimes one wants to have different compiler outputs from the same code,
4-
depending on build target, such as targeted operating system, or to enable
5-
release builds.
3+
> **<sup>Syntax</sup>**\
4+
> _ConfigurationPredicate_ :\
5+
> &nbsp;&nbsp; &nbsp;&nbsp; _ConfigurationOption_\
6+
> &nbsp;&nbsp; | _ConfigurationAll_\
7+
> &nbsp;&nbsp; | _ConfigurationAny_\
8+
> &nbsp;&nbsp; | _ConfigurationNot_
9+
>
10+
> _ConfigurationOption_ :\
11+
> &nbsp;&nbsp; [IDENTIFIER]&nbsp;(`=` ([STRING_LITERAL] | [RAW_STRING_LITERAL]))<sup>?</sup>
12+
>
13+
> _ConfigurationAll_\
14+
> &nbsp;&nbsp; `all` `(` _ConfigurationPredicateList_<sup>?</sup> `)`
15+
>
16+
> _ConfigurationAny_\
17+
> &nbsp;&nbsp; `any` `(` _ConfigurationPredicateList_<sup>?</sup> `)`
18+
>
19+
> _ConfigurationNot_\
20+
> &nbsp;&nbsp; `not` `(` _ConfigurationPredicate_ `)`
21+
>
22+
> _ConfigurationPredicateList_\
23+
> &nbsp;&nbsp; _ConfigurationPredicate_ (`,` _ConfigurationPredicate_)<sup>\*</sup> `,`<sup>?</sup>
624
7-
Configuration options are boolean (on or off) and are named either with a
8-
single identifier (e.g. `foo`) or an identifier and a string (e.g. `foo = "bar"`;
9-
the quotes are required and spaces around the `=` are unimportant). Note that
10-
similarly-named options, such as `foo`, `foo="bar"` and `foo="baz"` may each be
11-
set or unset independently.
25+
*Conditionally compiled source code* is source code that may or may not be
26+
considered a part of the source code depending on certain conditions. <!-- This
27+
definition is sort of vacuous --> Source code can be conditionally compiled
28+
using [attributes], [`cfg`] and [`cfg_attr`], and the built-in [`cfg` macro].
29+
These conditions are based on the target architecture of the compiled crate,
30+
arbitrary values passed to the compiler, and a few other miscellaneous things
31+
further described below in detail.
1232

13-
Configuration options are either provided by the compiler or passed in on the
14-
command line using `--cfg` (e.g. `rustc main.rs --cfg foo --cfg 'bar="baz"'`).
15-
Rust code then checks for their presence using the `#[cfg(...)]` [attribute]:
33+
Each form of conditional compilation takes a _configuration predicate_ that
34+
evaluates to true or false. The predicate is one of the following:
35+
36+
* A configuration option. It is true if the option is set and false if it is
37+
unset.
38+
* `all()` with a comma separated list of configuration predicates. It is false
39+
if at least one predicate is false. If there are no predicates, it is true.
40+
* `any()` with a comma separated list of configuration predicates. It is true
41+
if at least one predicate is true. If there are no predicates, it is false.
42+
* `not()` with a configuration predicate. It is true if its predicate is false
43+
and false if its predicate is true.
44+
45+
_Configuration options_ are names and key-value pairs that are either set or
46+
unset. Names are written as a single identifier such as, for example, `unix`.
47+
Key-value pairs are written as an identifier, `=`, and then a string. For
48+
example, `target_arch = "x86_64"` is a configuration option.
49+
50+
> **Note**: Whitespace around the `=` is ignored. `foo="bar"` and `foo = "bar"`
51+
> are equivalent configuration options.
52+
53+
Keys are not unique in the set of key-value configuration options. For example,
54+
both `feature = "std"` and `feature = "serde"` can be set at the same time.
55+
56+
## Set Configuration Options
57+
58+
Which configuration options are set is determined statically during the
59+
compilation of the crate. Certain options are _compiler-set_ based on data
60+
about the compilation. Other options are _arbitrarily-set_, set based on input
61+
passed to the compiler outside of the code. It is not possible to set a
62+
configuration option from within the source code of the crate being compiled.
63+
64+
> **Note**: For `rustc`, arbitrary-set configuration options are set using the
65+
> [`--cfg`] flag.
66+
67+
<div class="warning">
68+
69+
Warning: It is possible for arbitrarily-set configuration options to have the
70+
same value as compiler-set configuration options. For example, it is possible
71+
to do `rustc --cfg "unix" program.rs` while compiling to a Windows target, and
72+
have both `unix` and `windows` configuration options set at the same time. It
73+
is unwise to actually do this.
74+
75+
</div>
76+
77+
### `target_arch`
78+
79+
Key-value option set once with the target's CPU architecture. The value is
80+
similar to the first element of the platform's target triple, but not
81+
identical.
82+
83+
Example values:
84+
85+
* `"x86"`
86+
* `"x86_64"`
87+
* `"mips"`
88+
* `"powerpc"`
89+
* `"powerpc64"`
90+
* `"arm"`
91+
* `"aarch64"`
92+
93+
### `target_os`
94+
95+
Key-value option set once with the target's operating system. This value is
96+
similar to the second and third element of the platform's target triple.
97+
98+
Example values:
99+
100+
* `"windows"`
101+
* `"macos"`
102+
* `"ios"`
103+
* `"linux"`
104+
* `"android"`
105+
* `"freebsd"`
106+
* `"dragonfly"`
107+
* `"bitrig"`
108+
* `"openbsd"`
109+
* `"netbsd"`
110+
111+
### `target_family`
112+
113+
Key-value option set at most once with the target's operating system value.
114+
115+
Example values:
116+
117+
* `"unix"`
118+
* `"windows"`
119+
120+
### `unix` and `windows`
121+
122+
`unix` is set if `target_family = "unix"` is set and `windows` is set if
123+
`target_family = "windows"` is set.
124+
125+
### `target_env`
126+
127+
Key-value option set with further disambiguating information about the target
128+
platform with information about the ABI or `libc` used. For historical reasons,
129+
this value is only defined as not the empty-string when actually needed for
130+
disambiguation. Thus, for example, on many GNU platforms, this value will be
131+
empty. This value is similar to the fourth element of the platform's target
132+
triple. One difference is that embedded ABIs such as `gnueabihf` will simply
133+
define `target_env` as `"gnu"`.
134+
135+
Example values:
136+
137+
* `""`
138+
* `"gnu"`
139+
* `"msvc"`
140+
* `"musl"`
141+
142+
### `target_endian`
143+
144+
Key-value option set once with either a value of "little" or "big" depending
145+
on the endianness of the target's CPU.
146+
147+
### `target_pointer_width`
148+
149+
Key-value option set once with the target's pointer width in bits. For example,
150+
for targets with 32-bit pointers, this is set to `"32"`. Likewise, it is set
151+
to `"64"` for targets with 64-bit pointers.
152+
153+
<!-- Are there targets that have a different bit number? -->
154+
155+
### `target_has_atomic`
156+
157+
Key-value option set for each integer size on which the target can perform
158+
atomic operations.
159+
160+
Possible values:
161+
162+
* `"8"`
163+
* `"16"`
164+
* `"32"`
165+
* `"64"`
166+
* `"ptr"`
167+
168+
### `target_vendor`
169+
170+
Key-value option set once with the vendor of the target.
171+
172+
Possible values:
173+
174+
* `"apple"`
175+
* `"pc"`
176+
* `"unknown"`
177+
178+
### `test`
179+
180+
Enabled when compiling the test harness. Done with `rustc` by using the
181+
[`--test`] flag.
182+
183+
### `debug_assertions`
184+
185+
Enabled by default when compiling without optimizations.
186+
This can be used to enable extra debugging code in development but not in
187+
production. For example, it controls the behavior of the standard library's
188+
[`debug_assert!`] macro.
189+
190+
### `proc_macro`
191+
192+
Set when the crate being compiled is being compiled with the `proc_macro`
193+
[crate type].
194+
195+
## Forms of conditional compilation
196+
197+
### The `cfg` attribute
198+
199+
> **<sup>Syntax</sup>**\
200+
> _CfgAttrAttribute_ :\
201+
> &nbsp;&nbsp; `cfg` `(` _ConfigurationPredicate_ `)`
202+
203+
<!-- should we say they're active attributes here? -->
204+
205+
The `cfg` [attribute] conditionally includes the thing it is attached to based
206+
on a configuration predicate.
207+
208+
It is written as `cfg`, `(`, a configuration predicate, and finally `)`.
209+
210+
If the predicate is true, the thing is rewritten to not have the `cfg` attribute
211+
on it. If the predicate is false, the thing is removed from the source code.
212+
213+
Some examples on functions:
16214

17215
```rust
18216
// The function is only included in the build when compiling for macOS
@@ -41,62 +239,72 @@ fn needs_not_foo() {
41239
}
42240
```
43241

44-
This illustrates some conditional compilation can be achieved using the
45-
`#[cfg(...)]` [attribute]. `any`, `all` and `not` can be used to assemble
46-
arbitrarily complex configurations through nesting.
47-
48-
The following configurations must be defined by the implementation:
49-
50-
* `target_arch = "..."` - Target CPU architecture, such as `"x86"`,
51-
`"x86_64"` `"mips"`, `"powerpc"`, `"powerpc64"`, `"arm"`, or
52-
`"aarch64"`. This value is closely related to the first element of
53-
the platform target triple, though it is not identical.
54-
* `target_os = "..."` - Operating system of the target, examples
55-
include `"windows"`, `"macos"`, `"ios"`, `"linux"`, `"android"`,
56-
`"freebsd"`, `"dragonfly"`, `"bitrig"` , `"openbsd"` or
57-
`"netbsd"`. This value is closely related to the second and third
58-
element of the platform target triple, though it is not identical.
59-
* `target_family = "..."` - Operating system family of the target, e. g.
60-
`"unix"` or `"windows"`. The value of this configuration option is defined
61-
as a configuration itself, like `unix` or `windows`.
62-
* `unix` - See `target_family`.
63-
* `windows` - See `target_family`.
64-
* `target_env = ".."` - Further disambiguates the target platform with
65-
information about the ABI/libc. Presently this value is either
66-
`"gnu"`, `"msvc"`, `"musl"`, or the empty string. For historical
67-
reasons this value has only been defined as non-empty when needed
68-
for disambiguation. Thus on many GNU platforms this value will be
69-
empty. This value is closely related to the fourth element of the
70-
platform target triple, though it is not identical. For example,
71-
embedded ABIs such as `gnueabihf` will simply define `target_env` as
72-
`"gnu"`.
73-
* `target_endian = "..."` - Endianness of the target CPU, either `"little"` or
74-
`"big"`.
75-
* `target_pointer_width = "..."` - Target pointer width in bits. This is set
76-
to `"32"` for targets with 32-bit pointers, and likewise set to `"64"` for
77-
64-bit pointers.
78-
* `target_has_atomic = "..."` - Set of integer sizes on which the target can perform
79-
atomic operations. Values are `"8"`, `"16"`, `"32"`, `"64"` and `"ptr"`.
80-
* `target_vendor = "..."` - Vendor of the target, for example `apple`, `pc`, or
81-
simply `"unknown"`.
82-
* `test` - Enabled when compiling the test harness (using the `--test` flag).
83-
* `debug_assertions` - Enabled by default when compiling without optimizations.
84-
This can be used to enable extra debugging code in development but not in
85-
production. For example, it controls the behavior of the standard library's
86-
`debug_assert!` macro.
87-
* `proc_macro` - Set when the crate being compiled is being compiled with the
88-
`proc_macro` [crate type].
89-
90-
You can also set another [attribute] based on a `cfg` variable with `cfg_attr`:
242+
The `cfg` attribute is allowed anywhere attributes are allowed except on
243+
generic parameters.
244+
245+
### The `cfg_attr` attribute
246+
247+
> **<sup>Syntax</sup>**\
248+
> _CfgAttrAttribute_ :\
249+
> &nbsp;&nbsp; `cfg_attr` `(` _ConfigurationPredicate_ `,` [_MetaItem_] `,`<sup>?</sup> `)`
250+
251+
The `cfg_attr` [attribute] conditionally includes [attributes] based on a
252+
configuration predicate.
253+
254+
It is written as `cfg_attr` followed by `(`, a configuration predicate, a
255+
[metaitem], an optional `,`, and finally a `)`.
256+
257+
When the configuration predicate is true, this attribute expands out to be an
258+
attribute of the attribute metaitem. For example, the following module will
259+
either be found at `linux.rs` or `windows.rs` based on the target.
91260

92261
```rust,ignore
93-
#[cfg_attr(a, b)]
262+
#[cfg_attr(linux, path = "linux.rs")]
263+
#[cfg_attr(windows, path = "windows.rs")]
264+
mod os;
94265
```
95266

96-
This is the same as `#[b]` if `a` is set by `cfg`, and nothing otherwise.
267+
> **Note**: The `cfg_attr` can expand to another `cfg_attr`. For example,
268+
> `#[cfg_attr(linux, cfg_attr(feature = "multithreaded", some_other_attribute))`
269+
> is valid. This example would be equivalent to
270+
> `#[cfg_attr(all(linux, feature ="multithreaded"), some_other_attribute)]`.
271+
272+
The `cfg_attr` attribute is allowed anywhere attributes are allowed except on
273+
generic parameters.
274+
275+
### The `cfg` macro
276+
277+
The built-in `cfg` macro takes in a single configuration predicate and evaluates
278+
to the `true` literal when the predicate is true and the `false` literal when
279+
it is false.
97280

98-
Lastly, configuration options can be used in expressions by invoking the `cfg!`
99-
macro: `cfg!(a)` evaluates to `true` if `a` is set, and `false` otherwise.
281+
For example:
282+
283+
```rust
284+
let machine_kind = if cfg!(unix) {
285+
"unix"
286+
} else if cfg!(windows) {
287+
"windows"
288+
} else {
289+
"unknown"
290+
};
291+
292+
println!("I'm running on a {} machine!", machine_kind);
293+
```
100294

295+
[IDENTIFIER]: identifiers.html
296+
[RAW_STRING_LITERAL]: tokens.html#raw-string-literals
297+
[STRING_LITERAL]: tokens.html#string-literals
298+
[_MetaItem_]: attributes.html
299+
[`--cfg`]: ../rustc/command-line-arguments.html#a--cfg-configure-the-compilation-environment
300+
[`--test`]: ../rustc/command-line-arguments.html#a--test-build-a-test-harness
301+
[`cfg`]: #the-cfg-attribute
302+
[`cfg` macro]: #the-cfg-macro
303+
[`cfg_attr`]: #the-cfg_attr-attribute
304+
[`debug_assert!`]: ../std/macro.debug_assert.html
101305
[attribute]: attributes.html
102-
[crate type]: linkage.html
306+
[attributes]: attributes.html
307+
[crate type]: linkage.html
308+
[expressions]: expressions.html
309+
[items]: items.html
310+
[metaitem]: attributes.html

0 commit comments

Comments
 (0)