1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
use std::{env, ffi::OsString, path::PathBuf};

macro_rules! export {
    () => {};
    ($(#[$meta:meta])* $f:ident -> String = $var:ident $(; $($rest:tt)*)? ) => {
        $(#[$meta])*
        pub fn $f() -> String {
            env::var_os(stringify!($var))
                .expect(concat!("cargo buildscript env var $", stringify!($var), " not found"))
                .into_string()
                .expect(concat!("cargo buildscript env var $", stringify!($var), " contained invalid UTF-8"))
        }

        $(export! {
            $($rest)*
        })?
    };
    ($(#[$meta:meta])* $f:ident -> Option<String> = $var:ident $(; $($rest:tt)*)? ) => {
        $(#[$meta])*
        pub fn $f() -> Option<String> {
            env::var_os(stringify!($var)).map(|it| it
                .into_string()
                .expect(concat!("cargo buildscript env var $", stringify!($var), " contained invalid UTF-8"))
            )
        }

        $(export! {
            $($rest)*
        })?
    };
    ($(#[$meta:meta])* $f:ident -> usize = $var:ident $(; $($rest:tt)*)? ) => {
        $(#[$meta])*
        pub fn $f() -> usize {
            env::var_os(stringify!($var))
                .expect(concat!("cargo buildscript env var $", stringify!($var), " not found"))
                .into_string()
                .expect(concat!("cargo buildscript env var $", stringify!($var), " contained invalid UTF-8"))
                .parse()
                .expect(concat!("cargo buildscript env var $", stringify!($var), " did not parse as `usize`"))
        }

        $(export! {
            $($rest)*
        })?
    };
    ($(#[$meta:meta])* $f:ident -> Vec<String> = $var:ident $(; $($rest:tt)*)? ) => {
        $(#[$meta])*
        pub fn $f() -> Vec<String> {
            env::var_os(stringify!($var))
                .expect(concat!("cargo buildscript env var $", stringify!($var), " not found"))
                .into_string()
                .expect(concat!("cargo buildscript env var $", stringify!($var), " contained invalid UTF-8"))
                .split(',')
                .map(Into::into)
                .collect()
        }

        $(export! {
            $($rest)*
        })?
    };
    ($(#[$meta:meta])* $f:ident -> bool = $var:ident $(; $($rest:tt)*)? ) => {
        $(#[$meta])*
        pub fn $f() -> bool {
            env::var_os(stringify!($var))
                .is_some()
        }

        $(export! {
            $($rest)*
        })?
    };
    ($(#[$meta:meta])* $f:ident -> PathBuf = $var:ident $(; $($rest:tt)*)? ) => {
        $(#[$meta])*
        pub fn $f() -> PathBuf {
            env::var_os(stringify!($var))
                .expect(concat!("cargo buildscript env var $", stringify!($var), " not found"))
                .into()
        }

        $(export! {
            $($rest)*
        })?
    };
    ($(#[$meta:meta])* $f:ident -> Option<PathBuf> = $var:ident $(; $($rest:tt)*)? ) => {
        $(#[$meta])*
        pub fn $f() -> Option<PathBuf> {
            env::var_os(stringify!($var))
                .map(Into::into)
        }

        $(export! {
            $($rest)*
        })?
    };
    ($(#[$meta:meta])* $f:ident -> OsString = $var:ident $(; $($rest:tt)*)? ) => {
        $(#[$meta])*
        pub fn $f() -> OsString {
            env::var_os(stringify!($var))
                .expect(concat!("cargo buildscript env var $", stringify!($var), " not found"))
        }

        $(export! {
            $($rest)*
        })?
    };
    ($(#[$meta:meta])* $f:ident -> $out:ty = $var:ident $(; $($rest:tt)*)? ) => {
        compile_error!(concat!("Provided unknown output type ", stringify!($out), " to export!"));

        $(export! {
            $($rest)*
        })?
    };
}

export! {
    /// Path to the `cargo` binary performing the build.
    cargo -> PathBuf = CARGO;
    /// The directory containing the manifest for the package being built (the
    /// package containing the build script). Also note that this is the value
    /// of the current working directory of the build script when it starts.
    cargo_manifest_dir -> PathBuf = CARGO_MANIFEST_DIR;
    /// The manifest links value.
    cargo_manifest_links -> String = CARGO_MANIFEST_LINKS;
    /// Contains parameters needed for Cargo's jobserver implementation to
    /// parallelize subprocesses. Rustc or cargo invocations from build.rs
    /// can already read CARGO_MAKEFLAGS, but GNU Make requires the flags
    /// to be specified either directly as arguments, or through the MAKEFLAGS
    /// environment variable. Currently Cargo doesn't set the MAKEFLAGS
    /// variable, but it's free for build scripts invoking GNU Make to set it
    /// to the contents of CARGO_MAKEFLAGS.
    cargo_makeflags -> OsString = CARGO_MAKEFLAGS;
    /// Set on [unix-like platforms](https://doc.rust-lang.org/reference/conditional-compilation.html#unix-and-windows).
    cargo_cfg_unix -> bool = CARGO_CFG_UNIX;
    /// Set on [windows-like platforms](https://doc.rust-lang.org/reference/conditional-compilation.html#unix-and-windows).
    cargo_cfg_windows -> bool = CARGO_CFG_WINDOWS;
    /// The [target family](https://doc.rust-lang.org/reference/conditional-compilation.html#target_family).
    cargo_cfg_target_family -> Vec<String> = CARGO_CFG_TARGET_FAMILY;
    /// The [target operating system](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os).
    cargo_cfg_target_os -> String = CARGO_CFG_TARGET_OS;
    /// The CPU [target architecture](https://doc.rust-lang.org/reference/conditional-compilation.html#target_arch).
    cargo_cfg_target_arch -> String = CARGO_CFG_TARGET_ARCH;
    /// The [target vendor](https://doc.rust-lang.org/reference/conditional-compilation.html#target_vendor).
    cargo_cfg_target_vendor -> String = CARGO_CFG_TARGET_VENDOR;
    /// The [target environment](https://doc.rust-lang.org/reference/conditional-compilation.html#target_env) ABI.
    cargo_cfg_target_env -> String = CARGO_CFG_TARGET_ENV;
    /// The CPU [pointer width](https://doc.rust-lang.org/reference/conditional-compilation.html#target_pointer_width).
    cargo_cfg_pointer_width -> usize = CARGO_CFG_TARGET_POINTER_WIDTH;
    /// Teh CPU [target endianness](https://doc.rust-lang.org/reference/conditional-compilation.html#target_endian).
    cargo_cfg_target_endian -> String = CARGO_CFG_TARGET_ENDIAN;
    /// List of CPU [target features](https://doc.rust-lang.org/reference/conditional-compilation.html#target_feature) enabled.
    cargo_cfg_target_feature -> Vec<String> = CARGO_CFG_TARGET_FEATURE;
    /// The folder in which all output should be placed. This folder is inside
    /// the build directory for the package being built, and it is unique for
    /// the package in question.
    out_dir -> PathBuf = OUT_DIR;
    /// The target triple that is being compiled for. Native code should be
    /// compiled for this triple. See the [Target Triple] description for
    /// more information.
    ///
    /// [Target Triple]: https://doc.rust-lang.org/cargo/appendix/glossary.html#target
    target -> String = TARGET;
    /// The host triple of the Rust compiler.
    host -> String = HOST;
    /// The parallelism specified as the top-level parallelism. This can be
    /// useful to pass a `-j` parameter to a system like `make`. Note that care
    /// should be taken when interpreting this environment variable. For
    /// historical purposes this is still provided but recent versions of
    /// Cargo, for example, do not need to run `make -j`, and instead can set
    /// the `MAKEFLAGS` env var to the content of `CARGO_MAKEFLAGS` to activate
    /// the use of Cargo's GNU Make compatible [jobserver] for sub-make
    /// invocations.
    ///
    /// [jobserver]: https://www.gnu.org/software/make/manual/html_node/Job-Slots.html
    num_jobs -> String = NUM_JOBS;
    /// Value of the corresponding variable for the profile currently being built.
    opt_level -> String = OPT_LEVEL;
    /// Value of the corresponding variable for the profile currently being built.
    debug -> String = DEBUG;
    /// `release` for release builds, `debug` for other builds. This is
    /// determined based on if the profile inherits from the [`dev`] or
    /// [`release`] profile. Using this environment variable is not
    /// recommended. Using other environment variables like `OPT_LEVEL`
    /// provide a more correct view of the actual settings being used.
    ///
    /// [`dev`]: https://doc.rust-lang.org/cargo/reference/profiles.html#dev
    /// [`release`]: https://doc.rust-lang.org/cargo/reference/profiles.html#release
    profile -> String = PROFILE;
    /// The compiler that Cargo has resolved to use, passed to the build script
    /// so it might use it as well.
    rustc -> PathBuf = RUSTC;
    /// The documentation generator that Cargo has resolved to use, passed to
    /// the build script so it might use it as well.
    rustdoc -> PathBuf = RUSTDOC;
    /// The `rustc` wrapper, if any, that Cargo is using. See
    /// [`build.rustc-wrapper`](https://doc.rust-lang.org/cargo/reference/config.html#buildrustc-wrapper).
    rustc_wrapper -> Option<PathBuf> = RUSTC_WRAPPER;
    /// The `rustc` wrapper, if any, that Cargo is using for workspace members.
    /// See [`build.rustc-workspace-wrapper`](https://doc.rust-lang.org/cargo/reference/config.html#buildrustc-workspace-wrapper).
    rustc_workspace_wrapper -> Option<PathBuf> = RUSTC_WORKSPACE_WRAPPER;
    /// The path to the linker binary that Cargo has resolved to use for the
    /// current target, if specified.
    rustc_linker -> Option<PathBuf> = RUSTC_LINKER;
    /// The full version of your package.
    cargo_pkg_version -> String = CARGO_PKG_VERSION;
    /// The major version of your package.
    cargo_pkg_version_major -> usize = CARGO_PKG_VERSION_MAJOR;
    /// The minor version of your package.
    cargo_pkg_version_minor -> usize = CARGO_PKG_VERSION_MINOR;
    /// The patch version of your package.
    cargo_pkg_version_patch -> usize = CARGO_PKG_VERSION_PATCH;
    /// The pre-release of your package.
    cargo_pkg_version_pre -> String = CARGO_PKG_VERSION_PRE;
    /// The name of your package.
    cargo_pkg_name -> String = CARGO_PKG_NAME;
    /// The description from the manifest of your package.
    cargo_pkg_description -> String = CARGO_PKG_DESCRIPTION;
    /// The home page from the manifest of your package.
    cargo_pkg_homepage -> String = CARGO_PKG_HOMEPAGE;
    /// The repository from the manifest of your package.
    cargo_pkg_repository -> String = CARGO_PKG_REPOSITORY;
    /// The license from the manifest of your package.
    cargo_pkg_license -> String = CARGO_PKG_LICENSE;
    /// The license file from the manifest of your package.
    cargo_pkg_license_file -> String = CARGO_PKG_LICENSE_FILE;
}

/// For each activated feature of the package being built, this will be true.
pub fn cargo_feature(name: &str) -> bool {
    let key = format!("CARGO_FEATURE_{}", name.to_uppercase().replace('-', "_"));
    env::var_os(key).is_some()
}

/// For each [configuration option] of the package being built, this will
/// contain the value of the configuration. Boolean configurations are present
/// if they are set, and not present otherwise. This includes values built-in
/// to the compiler (which can be seen with `rustc --print=cfg`) and values set
/// by build scripts and extra flags passed to `rustc` (such as those defined
/// in `RUSTFLAGS`).
///
/// [configuration option]: https://doc.rust-lang.org/reference/conditional-compilation.html
pub fn cargo_cfg(cfg: &str) -> Option<Vec<String>> {
    let key = format!("CARGO_CFG_{}", cfg.to_uppercase().replace('-', "_"));
    let val = env::var_os(&key)?.into_string().unwrap_or_else(|_| {
        panic!("cargo buildscript env var ${key} contained invalid UTF-8");
    });
    Some(val.split(',').map(Into::into).collect())
}

/// 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 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`.
pub fn dep(name: &str, key: &str) -> Option<String> {
    let key = format!(
        "DEP_{}_{}",
        name.to_uppercase().replace('-', "_"),
        key.to_uppercase().replace('-', "_")
    );
    let val = env::var_os(&key)?.into_string().unwrap_or_else(|_| {
        panic!("cargo buildscript env var ${key} contained invalid UTF-8");
    });
    Some(val)
}

/// Extra flags that Cargo invokes rustc with. See [`build.rustflags`]. Note
/// that since Rust 1.55, `RUSTFLAGS` is removed from the environment; scripts
/// should use `CARGO_ENCODED_RUSTFLAGS` instead.
///
/// [`build.rustflags`]: https://doc.rust-lang.org/cargo/reference/config.html#buildrustflags
pub fn cargo_encoded_rustflags() -> Vec<String> {
    let val = env::var_os("CARGO_ENCODED_RUSTFLAGS")
        .expect("cargo buildscript env var $CARGO_ENCODED_RUSTFLAGS")
        .into_string()
        .expect("cargo buildscript env var $CARGO_ENCODED_RUSTFLAGS contained invalid UTF-8");
    val.split('\x1f').map(Into::into).collect()
}

/// List of authors from the manifest of your package.
pub fn cargo_pkg_authors() -> Vec<String> {
    let val = env::var_os("CARGO_PKG_AUTHORS")
        .expect("cargo buildscript env var $CARGO_PKG_AUTHORS")
        .into_string()
        .expect("cargo buildscript env var $CARGO_PKG_AUTHORS contained invalid UTF-8");
    val.split(':').map(Into::into).collect()
}