build/
lib.rs

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
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
//! A small crate used for supplying helpers to build.rs scripts.
//!
//! This just simplifies writing `build.rs` scripts and helps to make them more
//! generally readable.
//!
//! This crate should not ever be included as a `[dependency]`.
#![deny(missing_docs)]
#![deny(unused_results)]
#![deny(rust_2018_idioms)]

/// 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.
///
/// For more information, see this [cargo reference].
///
/// [cargo reference]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#cargo-warning
#[macro_export]
macro_rules! warning {
  ($($tokens:tt)*) => {
    println!("cargo:warning={}", ::std::format!($($tokens)*))
  }
}

/// The [`rustc_link_search`] instruction tells Cargo to pass the -L flag 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 for more detail.
///
/// These paths are also added to the dynamic library search path environment
/// variable 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).
///
/// For more information, see this [cargo reference].
///
/// [cargo reference]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-search
#[macro_export]
macro_rules! rustc_link_search {
  ($($tokens:tt)*) => {
    println!("cargo:rustc-link-search={}", ::std::format!($($tokens)*))
  };
}

/// The [`rustc_link_lib`] instruction tells Cargo to link the given library using
/// the compiler’s `-l` flag. This is typically used to link a native library
/// using FFI.
///
/// The LIB string is passed directly to rustc, so it supports any syntax that
/// `-l` does.
/// Currently the full supported syntax for `LIB` is
/// `[KIND[:MODIFIERS]=]NAME[:RENAME]`.
///
/// 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 for more detail.
///
/// For more information, see this [cargo reference].
///
/// [cargo reference]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-lib
#[macro_export]
macro_rules! rustc_link_lib {
  ($($tokens:tt)*) => {
    println!("cargo:rustc-link-lib={}", ::std::format!($($tokens)*))
  };
}

/// 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 will scan the entire directory for any
/// modifications.
///
/// 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 (otherwise, the default if no rerun-if instructions are
/// emitted is to scan the entire package directory for changes). 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.
///
/// For more information, see this [cargo reference].
///
/// [cargo reference]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#rerun-if-changed
#[macro_export]
macro_rules! rerun_if_changed {
  ($($tokens:tt)*) => {
    println!("cargo:rerun-if-changed={}", ::std::format!($($tokens)*))
  };
}

/// 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 possible to use this for environment
/// variables like TARGET that Cargo sets for build scripts. The environment
/// variables in use are those received by cargo invocations, not those received
/// by the executable of the build script.
///
/// For more information, see this [cargo reference].
///
/// [cargo reference]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#rerun-if-env-changed
#[macro_export]
macro_rules! rerun_if_env_changed {
  ($($tokens:tt)*) => {
    println!("cargo:rerun-if-env-changed={}", ::std::format!($($tokens)*))
  };
}

/// The [`rustc_link_arg_bin`] instruction tells Cargo to pass the
/// `-C link-arg=FLAG` option to the compiler, but only when building the binary
/// target with name BIN. Its usage is highly platform specific. It is useful
/// to set a linker script or other linker options.
///
/// For more information, see this [cargo reference].
///
/// [cargo reference]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-arg-bin
#[macro_export]
macro_rules! rustc_link_arg_bin {
  ($($tokens:tt)*) => {
    println!("cargo:rustc-link-arg-bin={}", ::std::format!($($tokens)*))
  };
}

/// The [`rustc_link_arg_bins`] instruction tells Cargo to pass the
/// `-C link-arg=FLAG` option to the compiler, but only when building a binary
/// target. Its usage is highly platform specific. It is useful to set a linker
/// script or other linker options.
///
/// For more information, see this [cargo reference].
///
/// [cargo reference]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-arg-bins
#[macro_export]
macro_rules! rustc_link_arg_bins {
  ($($tokens:tt)*) => {
    println!("cargo:rustc-link-arg-bins={}", ::std::format!($($tokens)*))
  };
}

/// The [`rustc_link_arg_tests`] instruction tells Cargo to pass the
/// `-C link-arg=FLAG` option to the compiler, but only when building a tests
/// target.
///
/// For more information, see this [cargo reference].
///
/// [cargo reference]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-arg-tests
#[macro_export]
macro_rules! rustc_link_arg_tests {
  ($($tokens:tt)*) => {
    println!("cargo:rustc-link-arg-tests={}", ::std::format!($($tokens)*))
  };
}

/// The [`rustc_link_arg_examples`] instruction tells Cargo to pass the
/// `-C link-arg=FLAG` option to the compiler, but only when building an
/// examples target.
///
/// For more information, see this [cargo reference].
///
/// [cargo reference]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-arg-examples
#[macro_export]
macro_rules! rustc_link_arg_examples {
  ($($tokens:tt)*) => {
    println!("cargo:rustc-link-arg-examples={}", ::std::format!($($tokens)*))
  };
}

/// The [`rustc_link_arg_benches`] instruction tells Cargo to pass the
/// `-C link-arg=FLAG` option to the compiler, but only when building a
/// benchmark target.
///
/// For more information, see this [cargo reference].
///
/// [cargo reference]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-arg-benches
#[macro_export]
macro_rules! rustc_link_arg_benches {
  ($($tokens:tt)*) => {
    println!("cargo:rustc-link-arg-benches={}", ::std::format!($($tokens)*))
  };
}

/// 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` and `rustc-link-search`.
///
/// For more information, see this [cargo reference].
///
/// [cargo reference]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-flags
///
/// # Examples
///
/// basic use:
///
/// ```rust
/// # use build::rustc_flags;
/// rustc_flags!("-lfoo");
/// rustc_flags!(r#"-L"{}""#, "/lib")
/// ```
#[macro_export]
macro_rules! rustc_flags {
  ($($tokens:tt)*) => {
    println!("cargo:rustc-flags={}", ::std::format!($($tokens)*))
  };
}

/// The [`rustc_cfg`] instruction tells Cargo to pass the given value to the
/// `--cfg flag` 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.
///
/// For more information, see this [cargo reference].
///
/// [cargo reference]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-cfg
///
/// # Examples
///
/// basic use:
///
/// ```rust
/// # use build::rustc_cfg;
/// rustc_cfg!("abc");                        // allows #[cfg(abc)]
/// rustc_cfg!(r#"my_component="{}""#, "foo") // allows #[cfg(my_component="foo")]
/// ```
#[macro_export]
macro_rules! rustc_cfg   {
  ($($tokens:tt)*) => {
    println!("cargo:rustc-cfg={}", ::std::format!($($tokens)*))
  }
}

/// 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 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][link].
///
/// For more information, see this [cargo reference].
///
/// [link]: https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates
/// [cargo reference]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-env
#[macro_export]
macro_rules! rustc_env {
  ($($tokens:tt)*) => {
    println!("cargo:rustc-env={}", ::std::format!($($tokens)*))
  };
}

/// The [`rustc_cdylib_link_arg`] instruction tells Cargo to pass the
/// `-C link-arg=FLAG` option 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.
///
/// For more information, see this [cargo reference].
///
/// [cargo reference]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-cdylib-link-arg
#[macro_export]
macro_rules! rustc_cdylib_link_arg {
  ($($tokens:tt)*) => {
    println!("cargo:rustc-cdylib-link-arg={}", ::std::format!($($tokens)*))
  };
}

/// The [`out_dir`] macro provides an easy mechanism for retrieving the `OUT_DIR`
/// environment variable that is implicitly defined at runtime in build scripts.
#[macro_export]
macro_rules! out_dir {
  () => {
    std::env::var("OUT_DIR").expect("OUT_DIR must be defined in build.rs scripts")
  };
}

/// A module that is only available when building for Apple devices.
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub mod apple {
  use std::process::Stdio;

  /// The explicit Xcode SDK to use.
  pub enum SDK {
    /// SDK for the MacOS
    MacOSX,

    /// SDK for the iPhone
    Phone,

    /// SDK for the iPhone simulator
    Simulator,
  }

  impl SDK {
    fn suffix(&self) -> &str {
      match self {
        SDK::MacOSX => "MacOSX.platform/Developer/SDKs/MacOSX.sdk",
        SDK::Phone => "iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk",
        SDK::Simulator => "iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk",
      }
    }
  }

  /// Gets the underlying sysroot from the specified SDK.
  ///
  /// This function will panic if `xcode-select` is not found, or if
  /// `xcode-select` fails to find a path.
  ///
  /// # Parameters
  ///
  /// * `sdk` - the SDK to use
  pub fn xcode_sysroot(sdk: SDK) -> String {
    use std::process::Command;

    let child = Command::new("xcode-select")
      .arg("--print-path")
      .stdout(Stdio::piped())
      .spawn()
      .expect("xcode-select command failed to start");
    let output = child
      .wait_with_output()
      .expect("failed to output xcode path");
    let path = String::from_utf8(output.stdout).expect("failed to parse output");

    format!("{}/Platforms/{}", path.trim(), sdk.suffix())
  }

  /// Gets the Xcode sysroot the target_os.
  ///
  /// This will always choose the most up-to-date SDK installed on the system.
  pub fn target_xcode_sysroot() -> String {
    impl_target_xcode_sysroot()
  }

  #[cfg(target_os = "macos")]
  fn impl_target_xcode_sysroot() -> String {
    xcode_sysroot(SDK::MacOSX)
  }

  #[cfg(target_os = "ios")]
  fn impl_target_xcode_sysroot() -> String {
    xcode_sysroot(SDK::Phone)
  }
}