cargo_build/
functions.rs

1use std::io::Write;
2use std::path::{Path, PathBuf};
3
4use super::build_out::CARGO_BUILD_OUT;
5
6const ERR_MSG: &str = "Unable to write to CARGO_BUILD_OUT";
7
8/// Tells Cargo to re-run the build script **ONLY** if file or directory with given name changes.
9///
10/// The default if no `rerun-if` instructions are emitted is to scan the entire package
11/// directory for changes.
12///
13/// #### Example: rerun build script **ONLY** if one of specified files or folders changes:
14/// ```rust
15/// cargo_build::rerun_if_changed(["LICENSE.md", "README.md"]);
16/// cargo_build::rerun_if_changed("docs_folder");
17/// cargo_build::rerun_if_changed("src/main.c");
18///
19/// // Always rerun when non-existent file chosen
20/// cargo_build::rerun_if_changed("always");
21///
22/// // `String` can be used as argument
23/// let platform: String = std::env::var("OS").unwrap_or("linux".to_string());
24/// let config_path: String = format!("{platform}-config.toml");
25///
26/// cargo_build::rerun_if_changed(config_path);
27/// ```
28///
29/// See also [`rerun_if_changed!` macro](`crate::rerun_if_changed!`) with compile-time checked formatting
30/// and variable number of arguments.
31///
32/// Currently, Cargo only uses the filesystem last-modified “mtime” timestamp to determine if the
33/// file has changed. It compares against an internal cached timestamp of when the build script last ran.
34///
35/// If the path points to a directory, it will scan the entire directory for any modifications.
36///
37/// If the build script inherently does not need to re-run under any circumstance, then using
38/// `cargo_build::rerun_if_changed(["build.rs"])` is a simple way to prevent it from being re-run.
39/// Cargo automatically handles whether or not the script itself needs to be recompiled, and of course
40/// the script will be re-run after it has been recompiled. Otherwise, specifying build.rs is redundant
41/// and unnecessary.
42///
43/// <https://doc.rust-lang.org/cargo/reference/build-scripts.html#rerun-if-changed>
44#[allow(private_bounds)]
45pub fn rerun_if_changed<I>(file_paths: impl Into<VarArg<I>>)
46where
47    I: IntoIterator,
48    I::Item: AsRef<Path>,
49{
50    for file_path in file_paths.into() {
51        let path = file_path.as_ref();
52
53        if let Some(path) = path.to_str() {
54            assert!(
55                !path.contains('\n'),
56                "Paths containing newlines cannot be used in the build scripts"
57            )
58        }
59        let path = path.display();
60
61        CARGO_BUILD_OUT
62            .with_borrow_mut(|out| writeln!(out, "cargo::rerun-if-changed={path}").expect(ERR_MSG));
63    }
64}
65
66/// Tells Cargo to re-run the build script if environment variable with the given name has changed.
67///
68/// ```rust
69/// cargo_build::rerun_if_env_changed(["LOG", "VERBOSE"]);
70/// cargo_build::rerun_if_env_changed("LOG");
71///
72/// // `String` can be used as argument
73/// let platform: String = std::env::var("OS").unwrap_or("linux".to_string());
74/// let config_path_env: String = format!("{platform}_CONFIG_PATH");
75///
76/// cargo_build::rerun_if_env_changed(config_path_env);
77/// ```
78///
79/// See also [`rerun_if_env_changed!` macro](`crate::rerun_if_env_changed!`) with compile-time
80/// checked formatting and variable number of arguments.
81///
82/// Note that the environment variables here are intended for global environment variables like
83/// `CC` and such, it is not possible to use this for environment variables like `TARGET` that Cargo
84/// sets for build scripts. The environment variables in use are those received by cargo
85/// invocations, not those received by the executable of the build script.
86///
87/// See [full list of `cargo` environment variables](https://doc.rust-lang.org/cargo/reference/environment-variables.html).
88///
89/// As of 1.46, using `env!` and `option_env!` in source code will automatically detect changes
90/// and trigger rebuilds. `rerun-if-env-changed` is no longer needed for variables already
91/// referenced by these macros.
92///
93/// <https://doc.rust-lang.org/cargo/reference/build-scripts.html#rerun-if-env-changed>
94#[allow(private_bounds)]
95pub fn rerun_if_env_changed<I>(env_vars: impl Into<VarArg<I>>)
96where
97    I: IntoIterator,
98    I::Item: AsRef<str>,
99{
100    for env_var in env_vars.into() {
101        let env_var: &str = env_var.as_ref();
102
103        assert!(
104            !env_var.contains('\n'),
105            "Env var names containing newlines cannot be used in the build scripts"
106        );
107
108        CARGO_BUILD_OUT.with_borrow_mut(|out| {
109            writeln!(out, "cargo::rerun-if-env-changed={env_var}").expect(ERR_MSG)
110        });
111    }
112}
113
114/// Passes custom flags to a linker for benchmarks, binaries, `cdylib` crates, examples, and tests.
115///
116/// - To set linker flags for specific targets see [`rustc_link_arg_benches`], [`rustc_link_arg_bins`],
117///   [`rustc_link_arg_cdylib`], [`rustc_link_arg_examples`], [`rustc_link_arg_tests`].
118///
119/// ```rust
120/// cargo_build::rustc_link_arg(["-mlongcalls", "-ffunction-sections"]);
121/// cargo_build::rustc_link_arg("-Wl,--cref");
122///
123/// let stack_size = 8 * 1024 * 1024;
124///
125/// if cfg!(target_os = "windows") {
126///     cargo_build::rustc_link_arg([
127///         "/WX",
128///         "/MANIFEST:EMBED",
129///         &format!("/stack:{}", stack_size)
130///     ]);
131/// }
132/// ```
133///
134/// See also [`rustc_link_arg!` macro](`crate::rustc_link_arg!`) with compile-time checked
135/// formatting, variable number of arguments and improved syntax.
136///
137/// The `rustc-link-arg` instruction tells Cargo to pass the
138/// [`-C link-arg=FLAG` option](https://doc.rust-lang.org/rustc/codegen-options/index.html#link-arg)
139/// to the compiler, but only when building supported targets (benchmarks,
140/// binaries, cdylib crates, examples, and tests). Its usage is highly platform specific.
141/// It is useful to set the shared library version or linker script.
142///
143/// <https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-arg>
144#[allow(private_bounds)]
145pub fn rustc_link_arg<I>(linker_flags: impl Into<VarArg<I>>)
146where
147    I: IntoIterator,
148    I::Item: AsRef<str>,
149{
150    for flag in linker_flags.into() {
151        let flag = flag.as_ref();
152
153        assert!(
154            !flag.contains('\n'),
155            "Compiler flags containing newlines cannot be used in the build scripts"
156        );
157
158        CARGO_BUILD_OUT.with_borrow_mut(|out| {
159            writeln!(out, "cargo::rustc-link-arg={flag}").expect(ERR_MSG);
160        });
161    }
162}
163
164/// Passes custom flags to a linker for `cdylib` crates.
165///
166/// - To set linker flags for all supported targets see [`rustc_link_arg`].
167///
168/// ```rust
169/// cargo_build::rustc_link_arg_cdylib([
170///         "-mlongcalls",
171///         "-ffunction-sections",
172///         "-Wl,--cref",
173/// ]);
174/// ```
175///
176/// See also [`rustc_link_arg!` macro](`crate::rustc_link_arg!`) with compile-time checked
177/// formatting, variable number of arguments and improved syntax.
178///
179/// The `rustc-link-arg-cdylib` instruction tells Cargo to pass the
180/// [`-C link-arg=FLAG` option](https://doc.rust-lang.org/rustc/codegen-options/index.html#link-arg)
181/// to the compiler, but only when building `cdylib` crates. Its usage is highly platform specific.
182/// It is useful to set the shared library version or linker script.
183///
184/// <https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-cdylib-link-arg>
185#[allow(private_bounds)]
186pub fn rustc_link_arg_cdylib<I>(linker_flags: impl Into<VarArg<I>>)
187where
188    I: IntoIterator,
189    I::Item: AsRef<str>,
190{
191    for flag in linker_flags.into() {
192        let flag = flag.as_ref();
193
194        assert!(
195            !flag.contains('\n'),
196            "Compiler flags containing newlines cannot be used in the build scripts"
197        );
198
199        CARGO_BUILD_OUT.with_borrow_mut(|out| {
200            writeln!(out, "cargo::rustc-link-arg-cdylib={flag}").expect(ERR_MSG)
201        });
202    }
203}
204
205/// Passes custom flags to a linker for specific binary name.
206///
207/// - To set linker flags for all bin targets see [`rustc_link_arg_bins`].
208/// - To set linker flags for all supported targets see [`rustc_link_arg`].
209///
210/// ```rust
211/// cargo_build::rustc_link_arg_bin("server", "-Wl,--cref");
212///
213/// cargo_build::rustc_link_arg_bin("client", [
214///         "-mlongcalls",
215///         "-ffunction-sections",
216///         "-Wl,--cref",
217/// ]);
218/// ```
219///
220/// See also [`rustc_link_arg!` macro](`crate::rustc_link_arg!`) with compile-time checked
221/// formatting, variable number of arguments and improved syntax.
222///
223/// The `rustc-link-arg-bin` instruction tells Cargo to pass the
224/// [`-C link-arg=FLAG` option](https://doc.rust-lang.org/rustc/codegen-options/index.html#link-arg)
225/// to the compiler, but only when building specified binary target. Its usage is highly platform
226/// specific. It is useful to set the shared library version or linker script.
227///
228/// <https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-bin-link-arg>
229#[allow(private_bounds)]
230pub fn rustc_link_arg_bin<I>(bin: &str, linker_flags: impl Into<VarArg<I>>)
231where
232    I: IntoIterator,
233    I::Item: AsRef<str>,
234{
235    for flag in linker_flags.into() {
236        CARGO_BUILD_OUT.with_borrow_mut(|out| {
237            let flag = flag.as_ref();
238
239            assert!(
240                !bin.contains('\n'),
241                "Binary names containing newlines cannot be used in the build scripts"
242            );
243            assert!(
244                !flag.contains('\n'),
245                "Compiler flags containing newlines cannot be used in the build scripts"
246            );
247
248            writeln!(out, "cargo::rustc-link-arg-bin={bin}={flag}").expect(ERR_MSG)
249        });
250    }
251}
252
253/// Passes custom flags to a linker for binaries.
254///
255/// To set linker flags for all supported targets see [`rustc_link_arg`].
256/// To set linker flags for specific binary target see [`rustc_link_arg_bin`].
257///
258/// ```rust
259/// cargo_build::rustc_link_arg_bins([
260///         "-mlongcalls",
261///         "-ffunction-sections",
262///         "-Wl,--cref",
263/// ]);
264/// ```
265///
266/// See also [`rustc_link_arg!` macro](`crate::rustc_link_arg!`) with compile-time checked
267/// formatting, variable number of arguments and improved syntax.
268///
269/// The `rustc-link-arg-bins` instruction tells Cargo to pass the
270/// [`-C link-arg=FLAG` option](https://doc.rust-lang.org/rustc/codegen-options/index.html#link-arg)
271/// to the compiler, but only when building binary targets. Its usage is highly platform
272/// specific. It is useful to set the shared library version or linker script.
273///
274/// <https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-arg-bins>
275#[allow(private_bounds)]
276pub fn rustc_link_arg_bins<I>(linker_flags: impl Into<VarArg<I>>)
277where
278    I: IntoIterator,
279    I::Item: AsRef<str>,
280{
281    for flag in linker_flags.into() {
282        let flag = flag.as_ref();
283
284        assert!(
285            !flag.contains('\n'),
286            "Compiler flags containing newlines cannot be used in the build scripts"
287        );
288
289        CARGO_BUILD_OUT.with_borrow_mut(|out| {
290            writeln!(out, "cargo::rustc-link-arg-bins={flag}").expect(ERR_MSG)
291        });
292    }
293}
294
295/// Passes custom flags to a linker for tests.
296///
297/// To set linker flags for all supported targets see [`rustc_link_arg`].
298///
299/// ```rust
300/// cargo_build::rustc_link_arg_tests([
301///         "-mlongcalls",
302///         "-ffunction-sections",
303///         "-Wl,--cref",
304/// ]);
305/// ```
306///
307/// See also [`rustc_link_arg!` macro](`crate::rustc_link_arg!`) with compile-time checked
308/// formatting, variable number of arguments and improved syntax.
309///
310/// The `rustc-link-arg-tests` instruction tells Cargo to pass the
311/// [`-C link-arg=FLAG` option](https://doc.rust-lang.org/rustc/codegen-options/index.html#link-arg)
312/// to the compiler, but only when building tests. Its usage is highly platform
313/// specific. It is useful to set the shared library version or linker script.
314///
315/// <https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-arg-tests>
316#[allow(private_bounds)]
317pub fn rustc_link_arg_tests<I>(linker_flags: impl Into<VarArg<I>>)
318where
319    I: IntoIterator,
320    I::Item: AsRef<str>,
321{
322    for flag in linker_flags.into() {
323        let flag = flag.as_ref();
324
325        assert!(
326            !flag.contains('\n'),
327            "Compiler flags containing newlines cannot be used in the build scripts"
328        );
329
330        CARGO_BUILD_OUT.with_borrow_mut(|out| {
331            writeln!(out, "cargo::rustc-link-arg-tests={flag}").expect(ERR_MSG)
332        });
333    }
334}
335
336/// Passes custom flags to a linker for examples.
337///
338/// To set linker flags for all supported targets see [`rustc_link_arg`].
339///
340/// ```rust
341/// cargo_build::rustc_link_arg_examples([
342///         "-mlongcalls",
343///         "-ffunction-sections",
344///         "-Wl,--cref",
345/// ]);
346/// ```
347///
348/// See also [`rustc_link_arg!` macro](`crate::rustc_link_arg!`) with compile-time checked
349/// formatting, variable number of arguments and improved syntax.
350///
351/// The `rustc-link-arg-examples` instruction tells Cargo to pass the
352/// [`-C link-arg=FLAG` option](https://doc.rust-lang.org/rustc/codegen-options/index.html#link-arg)
353/// to the compiler, but only when building examples. Its usage is highly platform
354/// specific. It is useful to set the shared library version or linker script.
355///
356/// <https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-arg-examples>
357#[allow(private_bounds)]
358pub fn rustc_link_arg_examples<I>(linker_flags: impl Into<VarArg<I>>)
359where
360    I: IntoIterator,
361    I::Item: AsRef<str>,
362{
363    for flag in linker_flags.into() {
364        let flag = flag.as_ref();
365
366        assert!(
367            !flag.contains('\n'),
368            "Compiler flags containing newlines cannot be used in the build scripts"
369        );
370
371        CARGO_BUILD_OUT.with_borrow_mut(|out| {
372            writeln!(out, "cargo::rustc-link-arg-examples={flag}").expect(ERR_MSG)
373        });
374    }
375}
376
377/// Passes custom flags to a linker for benches.
378///
379/// To set linker flags for all supported targets see [`rustc_link_arg`].
380///
381/// ```rust
382/// cargo_build::rustc_link_arg_benches([
383///         "-mlongcalls",
384///         "-ffunction-sections",
385///         "-Wl,--cref",
386/// ]);
387/// ```
388///
389/// See also [`rustc_link_arg!` macro](`crate::rustc_link_arg!`) with compile-time checked
390/// formatting, variable number of arguments and improved syntax.
391///
392/// The `rustc-link-arg-benches` instruction tells Cargo to pass the
393/// [`-C link-arg=FLAG` option](https://doc.rust-lang.org/rustc/codegen-options/index.html#link-arg)
394/// to the compiler, but only when building benches. Its usage is highly platform
395/// specific. It is useful to set the shared library version or linker script.
396///
397/// <https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-arg-benches>
398#[allow(private_bounds)]
399pub fn rustc_link_arg_benches<I>(linker_flags: impl Into<VarArg<I>>)
400where
401    I: IntoIterator,
402    I::Item: AsRef<str>,
403{
404    for flag in linker_flags.into() {
405        let flag = flag.as_ref();
406
407        assert!(
408            !flag.contains('\n'),
409            "Compiler flags containing newlines cannot be used in the build scripts"
410        );
411
412        CARGO_BUILD_OUT.with_borrow_mut(|out| {
413            writeln!(out, "cargo::rustc-link-arg-benches={flag}").expect(ERR_MSG)
414        });
415    }
416}
417
418/// Adds a library to link.
419///
420/// ```rust
421/// cargo_build::rustc_link_lib(["nghttp2", "libssl", "libcrypto"]);
422///
423/// cargo_build::rustc_link_lib([
424///     "nghttp2",
425///     "static=libssl",
426///     "dylib=libcrypto",
427///     "static:+whole-archive,-bundle,-verbatim=mylib:renamed_lib",
428/// ]);
429/// ```
430///
431/// See also [`rustc_link_lib!` macro](`crate::rustc_link_lib!`) with compile-time checked
432/// formatting, variable number of arguments and improved syntax.
433///
434/// The `rustc-link-lib` instruction tells Cargo to link the given library using the compiler’s
435/// [`-l` flag](https://doc.rust-lang.org/rustc/command-line-arguments.html#option-l-link-lib).
436/// This is typically used to link a native library using [FFI](https://doc.rust-lang.org/nomicon/ffi.html).
437///
438/// Argument to this function is `LIB` string which which is directly passed to `rustc`.
439/// Currently the fully supported syntax for LIB is `[KIND[:MODIFIERS]=]NAME[:RENAME]`.
440///
441/// The optional `KIND` may be one of `dylib`, `static`, or `framework`. See the
442/// [rustc book](https://doc.rust-lang.org/rustc/command-line-arguments.html#option-l-link-lib)
443/// for more detail.
444///
445/// Linking modifiers (`[:MODIFIERS]`) are:
446/// - `-whole-archive`(default), `+whole-archive`.
447/// - `+bundle`(default), `-bundle`.
448/// - `-verbatim`(default), `+verbatim`.
449///
450/// See more specific [`rustc_link_lib_dylib`], [`rustc_link_lib_static`], [`rustc_link_lib_framework`].
451///
452/// <https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-lib>
453#[allow(private_bounds)]
454pub fn rustc_link_lib<I>(lib_names: impl Into<VarArg<I>>)
455where
456    I: IntoIterator,
457    I::Item: AsRef<str>,
458{
459    for lib in lib_names.into() {
460        let lib = lib.as_ref();
461
462        assert!(
463            !lib.contains('\n'),
464            "Library names containing newlines cannot be used in the build scripts"
465        );
466
467        CARGO_BUILD_OUT
468            .with_borrow_mut(|out| writeln!(out, "cargo::rustc-link-lib={lib}").expect(ERR_MSG));
469    }
470}
471
472/// [`rustc_link_lib`] alternative that automatically passes `dylib=`.
473///
474/// ```rust
475/// cargo_build::rustc_link_lib_dylib([], ["nghttp2", "libssl", "libcrypto"]);
476///
477/// cargo_build::rustc_link_lib_dylib(
478///     ["+whole_archive", "-bundle", "-verbatim"],
479///     ["nghttp2", "libssl", "libcrypto"],
480/// );
481/// ```
482///
483/// See also [`rustc_link_lib!` macro](`crate::rustc_link_lib!`) with compile-time checked
484/// formatting, variable number of arguments and improved syntax.
485///
486/// <https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-lib>
487#[allow(private_bounds)]
488pub fn rustc_link_lib_dylib<M, I>(modifiers: impl Into<VarArg<M>>, lib_names: impl Into<VarArg<I>>)
489where
490    I: IntoIterator,
491    I::Item: AsRef<str>,
492    M: IntoIterator<Item = I::Item>,
493{
494    let modifiers: String = modifiers
495        .into()
496        .into_iter()
497        .map(|e| e.as_ref().to_string())
498        .inspect(|e| {
499            assert!(
500                !e.contains('\n'),
501                "Link modifiers containing newlines cannot be used in build scripts"
502            );
503        })
504        .collect::<Vec<_>>()
505        .join(",");
506
507    for lib in lib_names.into() {
508        let lib = lib.as_ref();
509
510        assert!(
511            !lib.contains('\n'),
512            "Library names containing newlines cannot be used in the build scripts"
513        );
514
515        CARGO_BUILD_OUT.with_borrow_mut(|out| {
516            if !modifiers.is_empty() {
517                writeln!(out, "cargo::rustc-link-lib=dylib:{modifiers}={lib}").expect(ERR_MSG)
518            } else {
519                writeln!(out, "cargo::rustc-link-lib=dylib={lib}").expect(ERR_MSG)
520            }
521        });
522    }
523}
524
525/// [`rustc_link_lib`] alternative that automatically passes `static=`.
526///
527/// ```rust
528/// cargo_build::rustc_link_lib_static([], ["nghttp2", "libssl", "libcrypto"]);
529///
530/// cargo_build::rustc_link_lib_static(
531///     ["+whole_archive", "-bundle", "-verbatim"],
532///     ["nghttp2", "libssl", "libcrypto"],
533/// );
534/// ```
535///
536/// See also [`rustc_link_lib!` macro](`crate::rustc_link_lib!`) with compile-time checked
537/// formatting, variable number of arguments and improved syntax.
538///
539/// <https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-lib>
540#[allow(private_bounds)]
541pub fn rustc_link_lib_static<M, I>(modifiers: impl Into<VarArg<M>>, lib_names: impl Into<VarArg<I>>)
542where
543    I: IntoIterator,
544    I::Item: AsRef<str>,
545    M: IntoIterator<Item = I::Item>,
546{
547    let modifiers: String = modifiers
548        .into()
549        .into_iter()
550        .map(|e| e.as_ref().to_string())
551        .inspect(|e| {
552            assert!(
553                !e.contains('\n'),
554                "Link modifiers containing newlines cannot be used in build scripts"
555            );
556        })
557        .collect::<Vec<_>>()
558        .join(",");
559
560    for lib in lib_names.into() {
561        let lib = lib.as_ref();
562
563        assert!(
564            !lib.contains('\n'),
565            "Library names containing newlines cannot be used in the build scripts"
566        );
567
568        CARGO_BUILD_OUT.with_borrow_mut(|out| {
569            if !modifiers.is_empty() {
570                writeln!(out, "cargo::rustc-link-lib=static:{modifiers}={lib}").expect(ERR_MSG)
571            } else {
572                writeln!(out, "cargo::rustc-link-lib=static={lib}").expect(ERR_MSG)
573            }
574        });
575    }
576}
577
578/// [`rustc_link_lib`] alternative that automatically passes `framework=`.
579///
580/// ```rust
581/// cargo_build::rustc_link_lib_framework([], ["nghttp2", "libssl", "libcrypto"]);
582///
583/// cargo_build::rustc_link_lib_framework(
584///     ["+whole_archive", "-bundle", "-verbatim"],
585///     ["nghttp2", "libssl", "libcrypto"],
586/// );
587/// ```
588///
589/// See also [`rustc_link_lib!` macro](`crate::rustc_link_lib!`) with compile-time checked
590/// formatting, variable number of arguments and improved syntax.
591///
592/// <https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-lib>
593#[allow(private_bounds)]
594pub fn rustc_link_lib_framework<M, I>(
595    modifiers: impl Into<VarArg<M>>,
596    lib_names: impl Into<VarArg<I>>,
597) where
598    I: IntoIterator,
599    I::Item: AsRef<str>,
600    M: IntoIterator<Item = I::Item>,
601{
602    let modifiers: String = modifiers
603        .into()
604        .into_iter()
605        .map(|e| e.as_ref().to_string())
606        .inspect(|e| {
607            assert!(
608                !e.contains('\n'),
609                "Link modifiers containing newlines cannot be used in build scripts"
610            );
611        })
612        .collect::<Vec<_>>()
613        .join(",");
614
615    for lib in lib_names.into() {
616        let lib = lib.as_ref();
617
618        assert!(
619            !lib.contains('\n'),
620            "Library names containing newlines cannot be used in the build scripts"
621        );
622
623        CARGO_BUILD_OUT.with_borrow_mut(|out| {
624            if !modifiers.is_empty() {
625                writeln!(out, "cargo::rustc-link-lib=framework:{modifiers}={lib}").expect(ERR_MSG)
626            } else {
627                writeln!(out, "cargo::rustc-link-lib=framework={lib}").expect(ERR_MSG)
628            }
629        });
630    }
631}
632
633/// Adds a directory to the library search path.
634///
635/// ```rust
636/// cargo_build::rustc_link_search("libs");
637///
638/// cargo_build::rustc_link_search([
639///     "native=libs",
640///     "framework=mac_os_libs"
641/// ]);
642/// ```
643///
644/// The `rustc-link-search` instruction tells Cargo to pass the
645/// [`-L` flag](https://doc.rust-lang.org/rustc/command-line-arguments.html#option-l-search-path)
646/// to the compiler to add a directory to the library search path.
647///
648/// The kind of search path can optionally be specified with the form `-L KIND=PATH`.
649///
650/// The optional `KIND` may be one of `dependency`, `crate`, `native`, `framework`, or `all`.
651/// See the [rustc book](https://doc.rust-lang.org/rustc/command-line-arguments.html#option-l-search-path)
652/// for more detail.
653///
654/// See also [`rustc_link_search!` macro](`crate::rustc_link_search!`) with compile-time checked
655/// formatting, variable number of arguments and improved syntax.
656///
657/// See more specific [`rustc_link_search_dependency`], [`rustc_link_search_crate`], [`rustc_link_search_native`],
658/// [`rustc_link_search_framework`], [`rustc_link_search_all`].
659///
660/// <https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-search>
661#[allow(private_bounds)]
662pub fn rustc_link_search<I>(lib_paths: impl Into<VarArg<I>>)
663where
664    I: IntoIterator,
665    I::Item: AsRef<Path>,
666{
667    for path in lib_paths.into() {
668        let path = path.as_ref();
669
670        if let Some(path) = path.to_str() {
671            assert!(
672                !path.contains('\n'),
673                "Library paths containing newlines cannot be used in the build scripts"
674            )
675        }
676        let path = path.display();
677
678        CARGO_BUILD_OUT.with_borrow_mut(|out| {
679            writeln!(out, "cargo::rustc-link-search={}", path).expect(ERR_MSG);
680        });
681    }
682}
683
684/// [`rustc_link_search`] alternative that automatically passes `native=`.
685///
686/// ```rust
687/// cargo_build::rustc_link_search_native(["libs", "vendor", "api"]);
688/// ```
689///
690/// See also [`rustc_link_search!` macro](`crate::rustc_link_search!`) with compile-time checked
691/// formatting, variable number of arguments and improved syntax.
692///
693/// <https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-search>
694#[allow(private_bounds)]
695pub fn rustc_link_search_native<I>(lib_paths: impl Into<VarArg<I>>)
696where
697    I: IntoIterator,
698    I::Item: AsRef<Path>,
699{
700    for path in lib_paths.into() {
701        let path = path.as_ref();
702
703        if let Some(path) = path.to_str() {
704            assert!(
705                !path.contains('\n'),
706                "Library paths containing newlines cannot be used in the build scripts"
707            )
708        }
709        let path = path.display();
710
711        CARGO_BUILD_OUT.with_borrow_mut(|out| {
712            writeln!(out, "cargo::rustc-link-search=native={path}").expect(ERR_MSG);
713        });
714    }
715}
716
717/// [`rustc_link_search`] alternative that automatically passes `dependency=`.
718///
719/// ```rust
720/// cargo_build::rustc_link_search_dependency(["libs", "vendor", "api"]);
721/// ```
722///
723/// See also [`rustc_link_search!` macro](`crate::rustc_link_search!`) with compile-time checked
724/// formatting, variable number of arguments and improved syntax.
725///
726/// <https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-search>
727#[allow(private_bounds)]
728pub fn rustc_link_search_dependency<I>(lib_paths: impl Into<VarArg<I>>)
729where
730    I: IntoIterator,
731    I::Item: AsRef<Path>,
732{
733    for path in lib_paths.into() {
734        let path = path.as_ref();
735
736        if let Some(path) = path.to_str() {
737            assert!(
738                !path.contains('\n'),
739                "Library paths containing newlines cannot be used in the build scripts"
740            )
741        }
742        let path = path.display();
743
744        CARGO_BUILD_OUT.with_borrow_mut(|out| {
745            writeln!(out, "cargo::rustc-link-search=dependency={path}").expect(ERR_MSG);
746        });
747    }
748}
749
750/// [`rustc_link_search`] alternative that automatically passes `crate=`.
751///
752/// ```rust
753/// cargo_build::rustc_link_search_crate(["libs", "vendor", "api"]);
754/// ```
755///
756/// See also [`rustc_link_search!` macro](`crate::rustc_link_search!`) with compile-time checked
757/// formatting, variable number of arguments and improved syntax.
758///
759/// <https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-search>
760#[allow(private_bounds)]
761pub fn rustc_link_search_crate<I>(lib_paths: impl Into<VarArg<I>>)
762where
763    I: IntoIterator,
764    I::Item: AsRef<Path>,
765{
766    for path in lib_paths.into() {
767        let path = path.as_ref();
768
769        if let Some(path) = path.to_str() {
770            assert!(
771                !path.contains('\n'),
772                "Library paths containing newlines cannot be used in the build scripts"
773            )
774        }
775        let path = path.display();
776
777        CARGO_BUILD_OUT.with_borrow_mut(|out| {
778            writeln!(out, "cargo::rustc-link-search=crate={path}").expect(ERR_MSG);
779        });
780    }
781}
782
783/// [`rustc_link_search`] alternative that automatically passes `framework=`.
784///
785/// ```rust
786/// cargo_build::rustc_link_search_framework(["libs", "vendor", "api"]);
787/// ```
788///
789/// See also [`rustc_link_search!` macro](`crate::rustc_link_search!`) with compile-time checked
790/// formatting, variable number of arguments and improved syntax.
791///
792/// <https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-search>
793#[allow(private_bounds)]
794pub fn rustc_link_search_framework<I>(lib_paths: impl Into<VarArg<I>>)
795where
796    I: IntoIterator,
797    I::Item: AsRef<Path>,
798{
799    for path in lib_paths.into() {
800        let path = path.as_ref();
801
802        if let Some(path) = path.to_str() {
803            assert!(
804                !path.contains('\n'),
805                "Library paths containing newlines cannot be used in the build scripts"
806            )
807        }
808        let path = path.display();
809
810        CARGO_BUILD_OUT.with_borrow_mut(|out| {
811            writeln!(out, "cargo::rustc-link-search=framework={path}").expect(ERR_MSG);
812        });
813    }
814}
815
816/// [`rustc_link_search`] alternative that automatically passes `all=`.
817///
818/// ```rust
819/// cargo_build::rustc_link_search_all(["libs", "vendor", "api"]);
820/// ```
821///
822/// See also [`rustc_link_search!` macro](`crate::rustc_link_search!`) with compile-time checked
823/// formatting, variable number of arguments and improved syntax.
824///
825/// <https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-search>
826#[allow(private_bounds)]
827pub fn rustc_link_search_all<I>(lib_paths: impl Into<VarArg<I>>)
828where
829    I: IntoIterator,
830    I::Item: AsRef<Path>,
831{
832    for path in lib_paths.into() {
833        let path = path.as_ref();
834
835        if let Some(path) = path.to_str() {
836            assert!(
837                !path.contains('\n'),
838                "Library paths containing newlines cannot be used in the build scripts"
839            )
840        }
841        let path = path.display();
842
843        CARGO_BUILD_OUT.with_borrow_mut(|out| {
844            writeln!(out, "cargo::rustc-link-search=all={path}").expect(ERR_MSG)
845        });
846    }
847}
848
849/// Passes certain flags to the compiler.
850///
851/// #### This only allows the `-l` and `-L` flags.
852///
853/// This function is is equivalent to using [`rustc_link_lib`] and [`rustc_link_search`].
854///
855/// ```rust
856/// cargo_build::rustc_flags(["-L libs -L common_libs"]);
857///
858/// cargo_build::rustc_flags([
859///     "-l ffi",
860///     "-l ncursesw",
861///     "-l stdc++",
862///     "-l z"
863/// ]);
864/// ```
865///
866/// See also [`rustc_link_search!` macro](`crate::rustc_link_search!`) and
867/// [`rustc_link_lib!` macro](`crate::rustc_link_lib!`) with compile-time checked
868/// formatting, variable number of arguments and improved syntax.
869///
870/// <https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-flags>
871#[allow(private_bounds)]
872pub fn rustc_flags<I>(flags: impl Into<VarArg<I>>)
873where
874    I: IntoIterator,
875    I::Item: AsRef<str>,
876{
877    for flag in flags.into() {
878        let flag = flag.as_ref();
879
880        assert!(
881            !flag.contains('\n'),
882            "Rustc flags containing newlines cannot be used in the build scripts"
883        );
884
885        CARGO_BUILD_OUT.with_borrow_mut(|out| {
886            writeln!(out, "cargo::rustc-flags={flag}").expect(ERR_MSG);
887        });
888    }
889}
890
891/// Enables custom compile-time `cfg` settings.
892///
893/// #### Register all `cfg` options with [`rustc_check_cfg`] to avoid `unexpected_cfgs` warnings.
894///
895/// - Allows `str`/`String` as argument for zero-variant `cfg`s.
896/// - Allows `(&str, &str)` as argument for `cfg`s with variants.
897///
898/// ```rust
899/// // build.rs
900/// cargo_build::rustc_check_cfgs("custom_cfg");
901///
902/// cargo_build::rustc_cfg("custom_cfg");
903///
904/// // main.rs
905/// #[cfg(custom_cfg)]
906/// mod optional_mod;
907///
908/// ```
909/// ```rust
910/// // build.rs
911/// cargo_build::rustc_check_cfg("api_version", ["1", "2", "3"]);
912///
913/// // Use pair (&str, &str) as argument to set `cfg` variant
914/// // - Note double parenthesis (( ))
915/// cargo_build::rustc_cfg(("api_version", "1"));
916///
917/// // main.rs
918/// #[cfg(api_version="1")]
919/// fn get_users() -> Vec<String> { todo!() }
920/// #[cfg(api_version="2")]
921/// fn get_users() -> Vec<String> { todo!() }
922/// ```
923///
924/// See also [`rustc_cfg!` macro](`crate::rustc_cfg!`) with improved syntax.
925///
926/// The `rustc-cfg` instruction tells Cargo to pass the given value to the
927/// [`--cfg` flag](https://doc.rust-lang.org/rustc/command-line-arguments.html#option-cfg) to the compiler.
928/// This may be used for compile-time detection of features to enable
929/// [conditional compilation](https://doc.rust-lang.org/reference/conditional-compilation.html).
930///
931/// Custom cfgs must either be defined using the [`rustc_check_cfg`] instruction
932/// or usage will need to allow `the unexpected_cfgs lint` to avoid
933/// [unexpected cfgs](https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#unexpected-cfgs) warnings.
934///
935/// Note that this does not affect Cargo’s dependency resolution. This cannot be used to enable an optional
936/// dependency, or enable other Cargo features.
937///
938/// Be aware that [Cargo features](https://doc.rust-lang.org/cargo/reference/features.html)
939/// use the form `feature="foo"` and `#[cfg(feature = "foo")]` in code. `cfg` values passed with this flag are
940/// not restricted to that form, and may provide just a single identifier, or any arbitrary key/value pair.
941///
942/// For example, using `cargo_build::rustc_cfgs(["abc"])` will then allow code to use `#[cfg(abc)]` (note the lack
943/// of `feature=`). Or an arbitrary key/value pair may be used like
944/// `cargo_build::rustc_cfg(("my_component", "foo"))` which enables `#[cfg(my_component="foo")]` code blocks.
945/// The key should be a Rust identifier, the value should be a string.
946///
947/// See [`rustc_check_cfg`] for more information on custom `cfg`s definitions.
948///
949/// See also:
950/// - [Conditional compilation example](https://doc.rust-lang.org/cargo/reference/build-script-examples.html#conditional-compilation).
951/// - [Syntax of rustc `--cfg` flag](https://doc.rust-lang.org/rustc/command-line-arguments.html#--cfg-configure-the-compilation-environment).
952/// - [Checking conditional configurations](https://doc.rust-lang.org/rustc/check-cfg.html).
953///
954/// <https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-cfg>
955#[allow(private_bounds)]
956pub fn rustc_cfg(cfg: impl Into<RustcCfg>) {
957    let RustcCfg { name, value } = cfg.into();
958
959    assert!(
960        !name.contains('\n'),
961        "Cfg names containing newlines cannot be used in the build scripts"
962    );
963
964    CARGO_BUILD_OUT.with_borrow_mut(|out| match value {
965        None => writeln!(out, "cargo::rustc-cfg={name}").expect(ERR_MSG),
966        Some(value) => {
967            assert!(
968                !value.contains('\n'),
969                "Cfg values containing newlines cannot be used in the build scripts"
970            );
971            writeln!(out, "cargo::rustc-cfg={name}=\"{value}\"").expect(ERR_MSG);
972        }
973    });
974}
975
976/// Helper struct for [`rustc_cfg`] argument.
977///
978/// - Implements `From<&str>` for zero-variant `cfg`s
979/// - Implements `From<(&str, &str)>` for `cfg`s with variants
980///
981/// ```rust
982/// // build.rs
983/// cargo_build::rustc_check_cfgs(["custom_cfg"]);
984/// cargo_build::rustc_cfg("custom_cfg");
985///
986/// // main.rs
987/// #[cfg(custom_cfg)]
988/// mod optional_mod;
989///
990/// // build.rs
991/// cargo_build::rustc_check_cfg("api_version", ["1", "2", "3"]);
992///
993/// // Use pair (&str, &str) as argument to set `cfg` variant
994/// cargo_build::rustc_cfg(("api_version", "1"));
995///
996/// // main.rs
997/// #[cfg(api_version="1")]
998/// fn get_users() -> Vec<String> { todo!() }
999/// #[cfg(api_version="2")]
1000/// fn get_users() -> Vec<String> { todo!() }
1001/// ```
1002struct RustcCfg {
1003    name: String,
1004    value: Option<String>,
1005}
1006
1007impl From<&str> for RustcCfg {
1008    fn from(name: &str) -> Self {
1009        Self {
1010            name: name.to_string(),
1011            value: None,
1012        }
1013    }
1014}
1015
1016impl From<&String> for RustcCfg {
1017    fn from(name: &String) -> Self {
1018        Self {
1019            name: name.to_string(),
1020            value: None,
1021        }
1022    }
1023}
1024
1025impl From<String> for RustcCfg {
1026    fn from(name: String) -> Self {
1027        Self { name, value: None }
1028    }
1029}
1030
1031impl From<(&str, &str)> for RustcCfg {
1032    fn from((name, value): (&str, &str)) -> Self {
1033        Self {
1034            name: name.to_string(),
1035            value: Some(value.to_string()),
1036        }
1037    }
1038}
1039
1040impl From<(String, &str)> for RustcCfg {
1041    fn from((name, value): (String, &str)) -> Self {
1042        Self {
1043            name,
1044            value: Some(value.to_string()),
1045        }
1046    }
1047}
1048
1049impl From<(&str, String)> for RustcCfg {
1050    fn from((name, value): (&str, String)) -> Self {
1051        Self {
1052            name: name.to_string(),
1053            value: Some(value),
1054        }
1055    }
1056}
1057
1058impl From<(String, String)> for RustcCfg {
1059    fn from((name, value): (String, String)) -> Self {
1060        Self {
1061            name,
1062            value: Some(value),
1063        }
1064    }
1065}
1066
1067/// Define expected `cfg` names and values. Those names are used when checking the *reachable* `cfg` expressions
1068/// with the `unexpected_cfgs` lint.
1069///
1070/// #### Note that this function only *defines* expected config names. See [`rustc_cfg`] to set `cfg` option during `build.rs` run.
1071///
1072/// - see [`rustc_check_cfgs`] to register `cfg`s options without variants.
1073///
1074/// ```rust
1075/// // build.rs
1076/// cargo_build::rustc_check_cfgs("custom_cfg");
1077///
1078/// cargo_build::rustc_cfg("custom_cfg");
1079///
1080/// // main.rs
1081/// #[cfg(custom_cfg)]
1082/// mod optional_mod;
1083///
1084/// ```
1085/// ```
1086/// // build.rs
1087/// cargo_build::rustc_check_cfg("api_version", ["1", "2", "3"]);
1088///
1089/// cargo_build::rustc_cfg(("api_version", "1"));
1090///
1091/// // main.rs
1092/// #[cfg(api_version="1")]
1093/// fn get_users() -> Vec<String> { todo!() }
1094/// #[cfg(api_version="2")]
1095/// fn get_users() -> Vec<String> { todo!() }
1096/// ```
1097///
1098/// Note that all possible cfgs should be defined, regardless of which cfgs are currently enabled. This includes
1099/// all possible values of a given `cfg` name.
1100///
1101/// It is recommended to group the [`rustc_check_cfg`] and [`rustc_cfg`] functions as closely
1102/// as possible in order to avoid typos, missing check-cfg, stale cfgs..
1103///
1104/// See also [`rustc_check_cfg!` macro](`crate::rustc_check_cfg!`) with compile-time checked
1105/// formatting, variable number of arguments and improved syntax.
1106///
1107/// See also:
1108/// - [Conditional compilation example](https://doc.rust-lang.org/cargo/reference/build-script-examples.html#conditional-compilation).
1109/// - [Syntax of rustc `--check-cfg` flag](https://doc.rust-lang.org/rustc/command-line-arguments.html#option-check-cfg).
1110/// - [Checking conditional configurations](https://doc.rust-lang.org/rustc/check-cfg.html).
1111///
1112/// <https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-check-cfg>
1113#[allow(private_bounds)]
1114pub fn rustc_check_cfg<I>(name: &str, values: impl Into<VarArg<I>>)
1115where
1116    I: IntoIterator,
1117    I::Item: AsRef<str>,
1118{
1119    assert!(
1120        !name.contains('\n'),
1121        "Cfg names containing newlines cannot be used in the build scripts"
1122    );
1123
1124    let values: String = values
1125        .into()
1126        .into_iter()
1127        .map(|value| {
1128            let value = value.as_ref();
1129            assert!(
1130                !value.contains('\n'),
1131                "Cfg values containing newlines cannot be used in the build scripts"
1132            );
1133            format!("\"{}\"", value)
1134        })
1135        .collect::<Vec<String>>()
1136        .join(", ");
1137
1138    CARGO_BUILD_OUT.with_borrow_mut(|out| {
1139        if values.is_empty() {
1140            writeln!(out, "cargo::rustc-check-cfg=cfg({name})").expect(ERR_MSG);
1141        } else {
1142            writeln!(out, "cargo::rustc-check-cfg=cfg({name}, values({values}))").expect(ERR_MSG);
1143        }
1144    });
1145}
1146
1147/// Define expected config names. Those names are used when checking the *reachable* cfg expressions
1148/// with the `unexpected_cfgs` lint.
1149///
1150/// This function is [`rustc_check_cfg`] alternative with multiple arguments.
1151///
1152/// ```rust
1153/// cargo_build::rustc_check_cfgs(["api_v1", "api_v2"]);
1154/// cargo_build::rustc_cfg("api_v1");
1155/// ```
1156///
1157/// See also [`rustc_check_cfg!` macro](`crate::rustc_check_cfg!`) with compile-time checked
1158/// formatting, variable number of arguments and improved syntax.
1159#[allow(private_bounds)]
1160pub fn rustc_check_cfgs<I>(cfg_names: impl Into<VarArg<I>>)
1161where
1162    I: IntoIterator,
1163    I::Item: AsRef<str>,
1164{
1165    for name in cfg_names.into() {
1166        let name = name.as_ref();
1167
1168        assert!(
1169            !name.contains('\n'),
1170            "Cfg names containing newlines cannot be used in the build scripts"
1171        );
1172
1173        CARGO_BUILD_OUT.with_borrow_mut(|out| {
1174            writeln!(out, "cargo::rustc-check-cfg=cfg({name})").expect(ERR_MSG);
1175        });
1176    }
1177}
1178
1179/// Sets an environment variable.
1180///
1181/// #### Example: Automatically insert env variable during compile time.
1182/// ```ignore
1183/// // build.rs
1184/// use std::process::Command;
1185///
1186/// let com_out = Command::new("git").args(["rev-parse", "HEAD"]).output().unwrap();
1187/// let git_hash = String::from_utf8(com_out.stdout).unwrap();
1188///
1189/// cargo_build::rustc_env("GIT_HASH", &git_hash);
1190///
1191/// // main.rs
1192/// const EMBEDDED_GIT_HASH: &str = env!("GIT_HASH");
1193/// ```
1194///
1195/// See also [`rustc_env!` macro](`crate::rustc_env!`) with improved syntax.
1196///
1197/// The `rustc-env` instruction tells Cargo to set the given environment variable when
1198/// compiling the package. The value can be then retrieved by the
1199/// [`env!` macro](https://doc.rust-lang.org/std/macro.env.html) in the compiled crate.
1200/// This is useful for embedding additional metadata in crate’s code, such as the hash
1201/// of git HEAD or the unique identifier of a continuous integration server.
1202///
1203/// See also the [environment variables automatically included by Cargo](https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates).
1204///
1205/// Note: These environment variables are also set when running an executable with `cargo run`
1206/// or `cargo test`. However, this usage is discouraged since it ties the executable to Cargo’s
1207/// execution environment. Normally, these environment variables should only be checked at
1208/// compile-time with the `env!` macro.
1209///
1210/// <https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-env>
1211pub fn rustc_env(var: &str, value: &str) {
1212    assert!(
1213        !var.contains('\n'),
1214        "Env variables containing newlines cannot be used in the build scripts"
1215    );
1216    assert!(
1217        !value.contains('\n'),
1218        "Env variable values containing newlines cannot be used in the build scripts"
1219    );
1220
1221    CARGO_BUILD_OUT.with_borrow_mut(|out| {
1222        writeln!(out, "cargo::rustc-env={var}={value}").expect(ERR_MSG);
1223    });
1224}
1225
1226/// Displays an error on the terminal.
1227///
1228/// #### This error fails the build even if all the other steps finished successfully.
1229///
1230/// ```rust
1231/// cargo_build::error("Fatal error during build process");
1232///
1233/// cargo_build::error("Fatal multi
1234/// line error
1235/// during build process");
1236/// ```
1237///
1238/// See [`error!` macro](`crate::error!`) with compile-time checked formatting.
1239///
1240/// The error instruction tells Cargo to display an error after the build script has finished running, and then fail the build.
1241///
1242/// Note: Build script libraries should carefully consider if they want to use `cargo::error` versus returning a `Result`.
1243/// It may be better to return a `Result`, and allow the caller to decide if the error is fatal or not. The caller can then
1244/// decide whether or not to display the `Err` variant using `cargo::error`.
1245///
1246/// <https://doc.rust-lang.org/cargo/reference/build-scripts.html#cargo-error>
1247pub fn error(msg: &str) {
1248    CARGO_BUILD_OUT.with_borrow_mut(|out| {
1249        for line in msg.lines() {
1250            writeln!(out, "cargo::error={line}").expect(ERR_MSG);
1251        }
1252    });
1253}
1254
1255/// Displays a warning on the terminal.
1256///  
1257/// ```rust
1258/// cargo_build::warning("Warning during build process");
1259///
1260/// cargo_build::warning("Multi line
1261/// warning
1262/// during build process");
1263/// ```
1264///
1265/// See [`warning!` macro](`crate::warning!`) with compile-time checked formatting.
1266///
1267/// The `warning` instruction tells Cargo to display a warning after the build script has finished running. Warnings are
1268/// only shown for `path` dependencies (that is, those you’re working on locally), so for example warnings printed out in
1269/// [crates.io](https://crates.io/) crates are not emitted by default, unless the build fails. The `-vv` "very verbose"
1270/// flag may be used to have Cargo display warnings for all crates.
1271///
1272/// <https://doc.rust-lang.org/cargo/reference/build-scripts.html#cargo-warning>
1273pub fn warning(msg: &str) {
1274    CARGO_BUILD_OUT.with_borrow_mut(|out| {
1275        for line in msg.lines() {
1276            writeln!(out, "cargo::warning={line}").expect(ERR_MSG);
1277        }
1278    });
1279}
1280
1281/// Metadata, used by links scripts.
1282///
1283/// The `package.links` key may be set in the `Cargo.toml` manifest to declare that the package links with the given native
1284/// library. The purpose of this manifest key is to give Cargo an understanding about the set of native dependencies that a
1285/// package has, as well as providing a principled system of passing metadata between package build scripts.
1286///
1287/// ```toml
1288/// // Cargo.toml
1289/// [package]
1290/// ..
1291/// links = "foo"
1292/// ```
1293/// ```rust
1294/// // build.rs
1295/// cargo_build::metadata("LINKAGE", "static");
1296///
1297/// cargo_build::rustc_link_search_native(["libs"]);
1298/// cargo_build::rustc_link_lib_static([], ["foo"]);
1299/// ```
1300///
1301/// See also [`metadata!` macro](`crate::metadata!`) with compile-time checked
1302/// formatting, variable number of arguments and improved syntax.
1303///
1304/// This manifest states that the package links to the `libfoo` native library. When using the `links` key, the package must
1305/// have a build script, and the build script should use the [`rustc_link_lib`] instruction to link the library.
1306///
1307/// Primarily, Cargo requires that there is at most one package per `links` value. In other words, it is forbidden to have two
1308/// packages link to the same native library. This helps prevent duplicate symbols between crates. Note, however, that there
1309/// are [conventions in place](https://doc.rust-lang.org/cargo/reference/build-scripts.html#-sys-packages) to alleviate this.
1310///
1311/// Build scripts can generate an arbitrary set of metadata in the form of key-value pairs.
1312/// This metadata is set with the [`metadata`] instruction.
1313///
1314/// The metadata is passed to the build scripts of **dependent** packages. For example, if the package `foo` depends on `bar`, which links
1315/// `baz`, then if `bar` generates `key=value` as part of its build script metadata, then the build script of `foo` will have the environment
1316/// variables `DEP_BAZ_KEY=value` (note that the value of the `links` key is used). See the
1317/// [Using another `sys` crate](https://doc.rust-lang.org/cargo/reference/build-script-examples.html#using-another-sys-crate)
1318/// for an example of how this can be used.
1319///
1320/// Note that metadata is only passed to immediate dependents, not transitive dependents.
1321///
1322/// <https://doc.rust-lang.org/cargo/reference/build-scripts.html#the-links-manifest-key>
1323pub fn metadata(key: &str, value: &str) {
1324    assert!(
1325        !key.contains('\n'),
1326        "Metadata keys containing newlines cannot be used in the build scripts"
1327    );
1328    assert!(
1329        !value.contains('\n'),
1330        "Metadata values containing newlines cannot be used in the build scripts"
1331    );
1332
1333    CARGO_BUILD_OUT.with_borrow_mut(|out| {
1334        writeln!(out, "cargo::metadata={key}={value}").expect(ERR_MSG);
1335    });
1336}
1337
1338/// Helper struct for generic `one or many` iterator.
1339///
1340/// - Implements `From<&str>` for single argument.
1341/// - Implements `From<IntoIterator<&str>>` for multiple arguments.
1342///
1343/// This struct implements `IntoIterator<&str>` itself but there is no perfomance const
1344/// unlike using `Option<IntoIterator<&str>>` wrapper and matching it each time in [`Iterator::next`].
1345///
1346/// ```
1347/// cargo_build::rustc_link_lib("foo");
1348/// cargo_build::rustc_link_lib(["bar", "baz"]);
1349///
1350/// let api = std::env::var("API_LIB_NAME").unwrap_or("api".to_string());
1351/// cargo_build::rustc_link_lib(format!("{}", api));
1352/// ```
1353struct VarArg<I: IntoIterator>(I);
1354
1355impl<'a> From<&'a str> for VarArg<std::iter::Once<&'a str>> {
1356    fn from(str: &'a str) -> Self {
1357        Self(std::iter::once(str))
1358    }
1359}
1360
1361impl From<String> for VarArg<std::iter::Once<String>> {
1362    fn from(value: String) -> Self {
1363        Self(std::iter::once(value))
1364    }
1365}
1366
1367impl From<PathBuf> for VarArg<std::iter::Once<PathBuf>> {
1368    fn from(value: PathBuf) -> Self {
1369        Self(std::iter::once(value))
1370    }
1371}
1372
1373impl<I: IntoIterator> From<I> for VarArg<I> {
1374    fn from(into_iter: I) -> Self {
1375        Self(into_iter)
1376    }
1377}
1378
1379impl<I: IntoIterator> IntoIterator for VarArg<I> {
1380    type Item = I::Item;
1381    type IntoIter = I::IntoIter;
1382
1383    fn into_iter(self) -> Self::IntoIter {
1384        self.0.into_iter()
1385    }
1386}