build_script/
basic.rs

1//! The most basic usage for [`build_script`](crate).
2//! # Notes
3//! 99% of the time, all of the public functions in this crate can suffice.
4use crate::BuildScript;
5use crate::{
6    cargo_rustc_link_lib as cargo_rustc_link_lib_,
7    cargo_rustc_link_search as cargo_rustc_link_search_,
8};
9use once_cell::sync::Lazy;
10use std::path::PathBuf;
11use std::sync::{LockResult, Mutex, MutexGuard};
12
13static BUILD_SCRIPT: Lazy<Mutex<BuildScript>> = Lazy::new(|| {
14    let mut build_script = BuildScript::default();
15    build_script.now();
16
17    Mutex::new(build_script)
18});
19
20/// Lock the mutex of build script mutex. This panics if the mutex is poisoned.
21fn lock_mutex<T>(lock: LockResult<MutexGuard<T>>) -> MutexGuard<T> {
22    lock.expect("mutex is poisoned")
23}
24
25/// Wrapper for locking the build script mutex. Internally this handles locking the build script
26/// mutex and then panicking if mutex is poisoned.
27fn build_script() -> MutexGuard<'static, BuildScript<'static>> {
28    lock_mutex(BUILD_SCRIPT.lock())
29}
30
31/// Wrapper for `cargo:rerun-if-changed=PATH`. This tells Cargo when to rerun the script.
32pub fn cargo_rerun_if_changed(path: impl Into<PathBuf>) {
33    build_script().cargo_rerun_if_changed(path.into());
34}
35
36/// Wrapper for `cargo:rerun-if-env-changed=VAR`. This tells Cargo when to rerun the script.
37pub fn cargo_rerun_if_env_changed(var: impl Into<String>) {
38    build_script().cargo_rerun_if_env_changed(&var.into());
39}
40
41/// Wrapper for `cargo:rustc-link-lib=[KIND=]NAME`. This adds a library to link.
42pub fn cargo_rustc_link_lib(name: impl Into<String>) {
43    build_script().cargo_rustc_link_lib(None, &name.into());
44}
45
46/// [`cargo_rustc_link_lib()`](cargo_rustc_link_lib), but with the `kind` parameter needed.
47pub fn cargo_rustc_link_lib_mapping(kind: cargo_rustc_link_lib_::Kind, name: impl Into<String>) {
48    build_script().cargo_rustc_link_lib(kind.into(), &name.into());
49}
50
51/// Wrapper for `cargo:rustc-link-search=[KIND=]PATH`. This adds to the library search path.
52pub fn cargo_rustc_link_search(path: impl Into<PathBuf>) {
53    build_script().cargo_rustc_link_search(None, path.into());
54}
55
56/// [`cargo_rustc_link_search()`](cargo_rustc_link_search), but with the `kind` parameter needed.
57pub fn cargo_rustc_link_search_mapping(
58    kind: cargo_rustc_link_search_::Kind,
59    path: impl Into<PathBuf>,
60) {
61    build_script().cargo_rustc_link_search(kind.into(), path.into());
62}
63
64/// Wrapper for `cargo:rustc-flags=FLAGS`. This passes certain flags to the compiler.
65pub fn cargo_rustc_flags(flags: impl Into<String>) {
66    build_script().cargo_rustc_flags(&flags.into());
67}
68
69/// Wrapper for `cargo:rustc-cfg=KEY[="VALUE"]`. This enable compile-time `cfg` settings.
70pub fn cargo_rustc_cfg(key: impl Into<String>) {
71    build_script().cargo_rustc_cfg(&key.into(), None);
72}
73
74/// [`cargo_rustc_cfg()`](cargo_rustc_cfg), but with the `value` parameter needed.
75pub fn cargo_rustc_cfg_mapping(key: impl Into<String>, value: impl Into<String>) {
76    build_script().cargo_rustc_cfg(&key.into(), Some(&value.into()));
77}
78
79/// Wrapper for `cargo:rustc-env=VAR=VALUE`. This sets an environment variable.
80pub fn cargo_rustc_env(var: impl Into<String>, value: impl Into<String>) {
81    build_script().cargo_rustc_env(&var.into(), &value.into());
82}
83
84/// Wrapper for `cargo:rustc-cdylib-link-arg=FLAG`. This passes custom flags to a linker for cdylib
85/// crates.
86pub fn cargo_rustc_cdylib_link_arg(flag: impl Into<String>) {
87    build_script().cargo_rustc_cdylib_link_arg(&flag.into());
88}
89
90/// Wrapper for `cargo:warning=MESSAGE`. This displays a warning on the terminal.
91pub fn cargo_warning(message: impl Into<String>) {
92    build_script().cargo_warning(&message.into());
93}
94
95/// Wrapper for `cargo:KEY=VALUE`. This is metadata, used by `links` scripts.
96pub fn cargo_mapping(key: impl Into<String>, value: impl Into<String>) {
97    build_script().cargo_mapping(&key.into(), &value.into());
98}
99
100#[cfg(test)]
101mod tests {
102    use gag::BufferRedirect;
103    use serial_test::serial;
104    use std::io::Read;
105
106    fn test(func: impl Fn(), expected: &str) -> bool {
107        let mut output = String::new();
108        let mut buffer = BufferRedirect::stdout().unwrap();
109        func();
110        buffer.read_to_string(&mut output).unwrap();
111
112        output.contains(expected)
113    }
114
115    macro_rules! new_test {
116        ($name:ident, $func:expr, $expected:literal) => {
117            #[test]
118            #[serial]
119            fn $name() {
120                assert!(test($func, $expected))
121            }
122        };
123    }
124
125    new_test!(
126        test_cargo_rerun_if_changed,
127        || super::cargo_rerun_if_changed("path"),
128        "cargo:rerun-if-changed=path"
129    );
130    new_test!(
131        test_cargo_rerun_if_env_changed,
132        || super::cargo_rerun_if_env_changed("var"),
133        "cargo:rerun-if-env-changed=var"
134    );
135    new_test!(
136        test_cargo_rustc_link_lib,
137        || super::cargo_rustc_link_lib("name"),
138        "cargo:rustc-link-lib=name"
139    );
140
141    #[test]
142    #[serial]
143    fn test_cargo_rustc_link_lib_mapping() {
144        use crate::cargo_rustc_link_lib::Kind;
145
146        let kinds = [Kind::Framework, Kind::Static, Kind::DynamicLibrary];
147
148        for &kind in kinds.iter() {
149            assert!(test(
150                || super::cargo_rustc_link_lib_mapping(kind, "name"),
151                &format!("cargo:rustc-link-lib={}=name", {
152                    let kind: String = kind.into();
153                    kind
154                })
155            ))
156        }
157    }
158
159    new_test!(
160        test_cargo_rustc_link_search,
161        || super::cargo_rustc_link_search("path"),
162        "cargo:rustc-link-search=path"
163    );
164
165    #[test]
166    #[serial]
167    fn test_cargo_rustc_link_search_mapping() {
168        use crate::cargo_rustc_link_search::Kind;
169
170        let kinds = [
171            Kind::Framework,
172            Kind::All,
173            Kind::Crate,
174            Kind::Dependency,
175            Kind::Native,
176        ];
177
178        for &kind in kinds.iter() {
179            assert!(test(
180                || super::cargo_rustc_link_search_mapping(kind, "path"),
181                &format!("cargo:rustc-link-search={}=path", {
182                    let kind: String = kind.into();
183                    kind
184                })
185            ))
186        }
187    }
188
189    new_test!(
190        test_cargo_rustc_flags,
191        || super::cargo_rustc_flags("flags"),
192        "cargo:rustc-flags=flags"
193    );
194    new_test!(
195        test_cargo_rustc_cfg,
196        || super::cargo_rustc_cfg("key"),
197        "cargo:rustc-cfg=key"
198    );
199    new_test!(
200        test_cargo_rustc_cfg_mapping,
201        || super::cargo_rustc_cfg_mapping("key", "value"),
202        "cargo:rustc-cfg=key=\"value\""
203    );
204    new_test!(
205        test_cargo_rustc_env,
206        || super::cargo_rustc_env("var", "value"),
207        "cargo:rustc-env=var=value"
208    );
209    new_test!(
210        test_cargo_rustc_cdylib_link_arg,
211        || super::cargo_rustc_cdylib_link_arg("flag"),
212        "cargo:rustc-cdylib-link-arg=flag"
213    );
214    new_test!(
215        test_cargo_warning,
216        || super::cargo_warning("message"),
217        "cargo:warning=message"
218    );
219    new_test!(
220        test_cargo_mapping,
221        || super::cargo_mapping("key", "value"),
222        "cargo:key=value"
223    );
224}