Skip to content

Commit e94e4cd

Browse files
committed
Extend the build directive syntax with cargo::metadata=
Signed-off-by: hi-rustin <[email protected]>
1 parent c9b89c6 commit e94e4cd

File tree

1 file changed

+118
-35
lines changed

1 file changed

+118
-35
lines changed

src/cargo/core/compiler/custom_build.rs

Lines changed: 118 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ use std::sync::{Arc, Mutex};
5252
/// build script has finished running. Read [the doc] for more.
5353
///
5454
/// [the doc]: https://doc.rust-lang.org/nightly/cargo/reference/build-scripts.html#cargo-warning
55-
const CARGO_WARNING: &str = "cargo:warning=";
55+
const OLD_CARGO_WARNING_SYNTAX: &str = "cargo:warning=";
56+
const NEW_CARGO_WARNING_SYNTAX: &str = "cargo::warning=";
5657

5758
/// Contains the parsed output of a custom build script.
5859
#[derive(Clone, Debug, Hash, Default)]
@@ -435,7 +436,10 @@ fn build_work(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Job> {
435436
let output = cmd
436437
.exec_with_streaming(
437438
&mut |stdout| {
438-
if let Some(warning) = stdout.strip_prefix(CARGO_WARNING) {
439+
if let Some(warning) = stdout
440+
.strip_prefix(OLD_CARGO_WARNING_SYNTAX)
441+
.or(stdout.strip_prefix(NEW_CARGO_WARNING_SYNTAX))
442+
{
439443
warnings_in_case_of_panic.push(warning.to_owned());
440444
}
441445
if extra_verbose {
@@ -638,47 +642,120 @@ impl BuildOutput {
638642
let mut warnings = Vec::new();
639643
let whence = format!("build script of `{}`", pkg_descr);
640644

645+
// Old syntax:
646+
// cargo:rustc-flags=VALUE
647+
// cargo:KEY=VALUE (for other unreserved keys)
648+
// New syntax:
649+
// cargo::rustc-flags=VALUE
650+
// cargo::metadata=KEY=VALUE (for other unreserved keys)
651+
// Due to backwards compatibility, no new keys can be added to this old format.
652+
// All new keys must use the new format: `cargo::metadata=KEY=VALUE`.
653+
const RESERVED_PREFIXES: &[&str] = &[
654+
"rustc-flags=",
655+
"rustc-link-lib=",
656+
"rustc-link-search=",
657+
"rustc-link-arg-cdylib=",
658+
"rustc-link-arg-bins=",
659+
"rustc-link-arg-bin=",
660+
"rustc-link-arg-tests=",
661+
"rustc-link-arg-benches=",
662+
"rustc-link-arg-examples=",
663+
"rustc-link-arg=",
664+
"rustc-cfg=",
665+
"rustc-check-cfg=",
666+
"rustc-env=",
667+
"warning=",
668+
"rerun-if-changed=",
669+
"rerun-if-env-changed=",
670+
];
671+
const DOCS_LINK_SUGGESTION: &str = "See https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script \
672+
for more information about build script outputs.";
673+
674+
fn parse_directive<'a>(
675+
whence: &str,
676+
line: &str,
677+
data: &'a str,
678+
new_syntax: bool,
679+
is_metadata: bool,
680+
) -> CargoResult<(&'a str, &'a str)> {
681+
let mut iter = data.splitn(2, "=");
682+
let key = iter.next();
683+
let value = iter.next();
684+
match (key, value) {
685+
(Some(a), Some(b)) => Ok((a, b.trim_end())),
686+
_ => bail!(
687+
"invalid output in {}: `{}`\n\
688+
Expected a line with `{}{}KEY=VALUE` with an `=` character, \
689+
but none was found.\n\
690+
{}",
691+
whence,
692+
line,
693+
if new_syntax { "cargo::" } else { "cargo:" },
694+
if is_metadata { "metadata=" } else { "" },
695+
DOCS_LINK_SUGGESTION
696+
),
697+
}
698+
}
699+
641700
for line in input.split(|b| *b == b'\n') {
642701
let line = match str::from_utf8(line) {
643702
Ok(line) => line.trim(),
644703
Err(..) => continue,
645704
};
646-
let mut iter = line.splitn(2, ':');
647-
if iter.next() != Some("cargo") {
648-
// skip this line since it doesn't start with "cargo:"
649-
continue;
705+
let mut new_syntax = false;
706+
let (key, value) = if let Some(data) = line.strip_prefix("cargo::") {
707+
new_syntax = true;
708+
709+
// For instance, `cargo::rustc-flags=foo`.
710+
if RESERVED_PREFIXES
711+
.iter()
712+
.any(|prefix| data.starts_with(prefix))
713+
{
714+
parse_directive(whence.as_str(), line, data, new_syntax, false)?
715+
}
716+
// For instance, `cargo::metadata=foo=bar`.
717+
else if let Some(data) = data.strip_prefix("metadata=") {
718+
// Reserved keys should not be used with the `cargo::metadata=` syntax.
719+
if let Some(prefix) = RESERVED_PREFIXES
720+
.iter()
721+
.find(|prefix| data.starts_with(*prefix))
722+
{
723+
bail!("invalid output in {}: `{}`\n\
724+
The reserved key cannot be used in the `cargo::metadata=` syntax. Please use `cargo::{}VAlUE` instead.\n\
725+
{}", whence, line, prefix, DOCS_LINK_SUGGESTION
726+
)
727+
}
728+
parse_directive(whence.as_str(), line, data, new_syntax, true)?
729+
} else {
730+
bail!("invalid output in {}: `{}`\n\
731+
Expected a line with `cargo::metadata=KEY=VALUE` but it did not have the `metadata=` part.\n\
732+
{}", whence, line, DOCS_LINK_SUGGESTION
733+
)
734+
}
650735
}
651-
let data = match iter.next() {
652-
Some(val) => val,
653-
None => continue,
654-
};
655-
656-
// getting the `key=value` part of the line
657-
let mut iter = data.splitn(2, '=');
658-
let key = iter.next();
659-
let value = iter.next();
660-
let (key, value) = match (key, value) {
661-
(Some(a), Some(b)) => (a, b.trim_end()),
662-
// Line started with `cargo:` but didn't match `key=value`.
663-
_ => bail!("invalid output in {}: `{}`\n\
664-
Expected a line with `cargo:key=value` with an `=` character, \
665-
but none was found.\n\
666-
See https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script \
667-
for more information about build script outputs.", whence, line),
736+
// For backwards compatibility, we also accept lines that start with "cargo:".
737+
else if let Some(data) = line.strip_prefix("cargo:") {
738+
// For instance, `cargo:rustc-flags=foo` or `cargo:foo=bar`.
739+
parse_directive(whence.as_str(), line, data, false, false)?
740+
}
741+
// Skip this line since it doesn't start with "cargo:" or "cargo::".
742+
else {
743+
continue;
668744
};
669745

670746
// This will rewrite paths if the target directory has been moved.
671747
let value = value.replace(
672748
script_out_dir_when_generated.to_str().unwrap(),
673749
script_out_dir.to_str().unwrap(),
674750
);
675-
751+
let syntax_prefix = if new_syntax { "cargo::" } else { "cargo:" };
676752
macro_rules! check_and_add_target {
677753
($target_kind: expr, $is_target_kind: expr, $link_type: expr) => {
678754
if !targets.iter().any(|target| $is_target_kind(target)) {
679755
bail!(
680-
"invalid instruction `cargo:{}` from {}\n\
756+
"invalid instruction `{}{}` from {}\n\
681757
The package {} does not have a {} target.",
758+
syntax_prefix,
682759
key,
683760
whence,
684761
pkg_descr,
@@ -701,14 +778,14 @@ impl BuildOutput {
701778
"rustc-link-arg-cdylib" | "rustc-cdylib-link-arg" => {
702779
if !targets.iter().any(|target| target.is_cdylib()) {
703780
warnings.push(format!(
704-
"cargo:{} was specified in the build script of {}, \
781+
"{}{} was specified in the build script of {}, \
705782
but that package does not contain a cdylib target\n\
706783
\n\
707784
Allowing this was an unintended change in the 1.50 \
708785
release, and may become an error in the future. \
709786
For more information, see \
710787
<https://github.com/rust-lang/cargo/issues/9562>.",
711-
key, pkg_descr
788+
syntax_prefix, key, pkg_descr
712789
));
713790
}
714791
linker_args.push((LinkType::Cdylib, value))
@@ -721,11 +798,13 @@ impl BuildOutput {
721798
let bin_name = parts.next().unwrap().to_string();
722799
let arg = parts.next().ok_or_else(|| {
723800
anyhow::format_err!(
724-
"invalid instruction `cargo:{}={}` from {}\n\
725-
The instruction should have the form cargo:{}=BIN=ARG",
801+
"invalid instruction `{}{}={}` from {}\n\
802+
The instruction should have the form {}{}=BIN=ARG",
803+
syntax_prefix,
726804
key,
727805
value,
728806
whence,
807+
syntax_prefix,
729808
key
730809
)
731810
})?;
@@ -734,8 +813,9 @@ impl BuildOutput {
734813
.any(|target| target.is_bin() && target.name() == bin_name)
735814
{
736815
bail!(
737-
"invalid instruction `cargo:{}` from {}\n\
816+
"invalid instruction `{}{}` from {}\n\
738817
The package {} does not have a bin target with the name `{}`.",
818+
syntax_prefix,
739819
key,
740820
whence,
741821
pkg_descr,
@@ -761,7 +841,10 @@ impl BuildOutput {
761841
if extra_check_cfg {
762842
check_cfgs.push(value.to_string());
763843
} else {
764-
warnings.push(format!("cargo:{} requires -Zcheck-cfg=output flag", key));
844+
warnings.push(format!(
845+
"{}{} requires -Zcheck-cfg=output flag",
846+
syntax_prefix, key
847+
));
765848
}
766849
}
767850
"rustc-env" => {
@@ -836,9 +919,9 @@ impl BuildOutput {
836919
})
837920
}
838921

839-
/// Parses [`cargo:rustc-flags`] instruction.
922+
/// Parses [`cargo::rustc-flags`] instruction.
840923
///
841-
/// [`cargo:rustc-flags`]: https://doc.rust-lang.org/nightly/cargo/reference/build-scripts.html#cargorustc-flagsflags
924+
/// [`cargo::rustc-flags`]: https://doc.rust-lang.org/nightly/cargo/reference/build-scripts.html#cargorustc-flagsflags
842925
pub fn parse_rustc_flags(
843926
value: &str,
844927
whence: &str,
@@ -884,9 +967,9 @@ impl BuildOutput {
884967
Ok((library_paths, library_links))
885968
}
886969

887-
/// Parses [`cargo:rustc-env`] instruction.
970+
/// Parses [`cargo::rustc-env`] instruction.
888971
///
889-
/// [`cargo:rustc-env`]: https://doc.rust-lang.org/nightly/cargo/reference/build-scripts.html#rustc-env
972+
/// [`cargo::rustc-env`]: https://doc.rust-lang.org/nightly/cargo/reference/build-scripts.html#rustc-env
890973
pub fn parse_rustc_env(value: &str, whence: &str) -> CargoResult<(String, String)> {
891974
let mut iter = value.splitn(2, '=');
892975
let name = iter.next();

0 commit comments

Comments
 (0)