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}