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)
}
}