#
cargo/core/compiler/
custom_build.rs

    1//! How to execute a build script and parse its output.
    2//!
    3//! ## Preparing a build script run
    4//!
    5//! A [build script] is an optional Rust script Cargo will run before building
    6//! your package. As of this writing, two kinds of special [`Unit`]s will be
    7//! constructed when there is a build script in a package.
    8//!
    9//! * Build script compilation --- This unit is generally the same as units
    10//!   that would compile other Cargo targets. It will recursively creates units
    11//!   of its dependencies. One biggest difference is that the [`Unit`] of
    12//!   compiling a build script is flagged as [`TargetKind::CustomBuild`].
    13//! * Build script execution --- During the construction of the [`UnitGraph`],
    14//!   Cargo inserts a [`Unit`] with [`CompileMode::RunCustomBuild`]. This unit
    15//!   depends on the unit of compiling the associated build script, to ensure
    16//!   the executable is available before running. The [`Work`] of running the
    17//!   build script is prepared in the function [`prepare`].
    18//!
    19//! ## Running a build script
    20//!
    21//! When running a build script, Cargo is aware of the progress and the result
    22//! of a build script. Standard output is the chosen interprocess communication
    23//! between Cargo and build script processes. A set of strings is defined for
    24//! that purpose. These strings, a.k.a. instructions, are interpreted by
    25//! [`BuildOutput::parse`] and stored in [`BuildRunner::build_script_outputs`].
    26//! The entire execution work is constructed by [`build_work`].
    27//!
    28//! [build script]: https://doc.rust-lang.org/nightly/cargo/reference/build-scripts.html
    29//! [`TargetKind::CustomBuild`]: crate::core::manifest::TargetKind::CustomBuild
    30//! [`UnitGraph`]: super::unit_graph::UnitGraph
    31//! [`CompileMode::RunCustomBuild`]: crate::core::compiler::CompileMode::RunCustomBuild
    32//! [instructions]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script
    33
    34use super::{BuildRunner, Job, Unit, Work, fingerprint, get_dynamic_search_path};
    35use crate::core::compiler::CompileMode;
    36use crate::core::compiler::artifact;
    37use crate::core::compiler::build_runner::UnitHash;
    38use crate::core::compiler::fingerprint::DirtyReason;
    39use crate::core::compiler::job_queue::JobState;
    40use crate::core::{PackageId, Target, profiles::ProfileRoot};
    41use crate::util::errors::CargoResult;
    42use crate::util::internal;
    43use crate::util::machine_message::{self, Message};
    44use anyhow::{Context as _, bail};
    45use cargo_platform::Cfg;
    46use cargo_util::paths;
    47use cargo_util_schemas::manifest::RustVersion;
    48use std::collections::hash_map::{Entry, HashMap};
    49use std::collections::{BTreeSet, HashSet};
    50use std::path::{Path, PathBuf};
    51use std::str::{self, FromStr};
    52use std::sync::{Arc, Mutex};
    53
    54/// A build script instruction that tells Cargo to display an error after the
    55/// build script has finished running. Read [the doc] for more.
    56///
    57/// [the doc]: https://doc.rust-lang.org/nightly/cargo/reference/build-scripts.html#cargo-error
    58const CARGO_ERROR_SYNTAX: &str = "cargo::error=";
    59/// Deprecated: A build script instruction that tells Cargo to display a warning after the
    60/// build script has finished running. Read [the doc] for more.
    61///
    62/// [the doc]: https://doc.rust-lang.org/nightly/cargo/reference/build-scripts.html#cargo-warning
    63const OLD_CARGO_WARNING_SYNTAX: &str = "cargo:warning=";
    64/// A build script instruction that tells Cargo to display a warning after the
    65/// build script has finished running. Read [the doc] for more.
    66///
    67/// [the doc]: https://doc.rust-lang.org/nightly/cargo/reference/build-scripts.html#cargo-warning
    68const NEW_CARGO_WARNING_SYNTAX: &str = "cargo::warning=";
    69
    70#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
    71pub enum Severity {
    72    Error,
    73    Warning,
    74}
    75
    76pub type LogMessage = (Severity, String);
    77
    78/// Represents a path added to the library search path.
    79///
    80/// We need to keep track of requests to add search paths within the cargo build directory
    81/// separately from paths outside of Cargo. The reason is that we want to give precedence to linking
    82/// against libraries within the Cargo build directory even if a similar library exists in the
    83/// system (e.g. crate A adds `/usr/lib` to the search path and then a later build of crate B adds
    84/// `target/debug/...` to satisfy its request to link against the library B that it built, but B is
    85/// also found in `/usr/lib`).
    86///
    87/// There's some nuance here because we want to preserve relative order of paths of the same type.
    88/// For example, if the build process would in declaration order emit the following linker line:
    89/// ```bash
    90/// -L/usr/lib -Ltarget/debug/build/crate1/libs -L/lib -Ltarget/debug/build/crate2/libs)
    91/// ```
    92///
    93/// we want the linker to actually receive:
    94/// ```bash
    95/// -Ltarget/debug/build/crate1/libs -Ltarget/debug/build/crate2/libs) -L/usr/lib -L/lib
    96/// ```
    97///
    98/// so that the library search paths within the crate artifacts directory come first but retain
    99/// relative ordering while the system library paths come after while still retaining relative
    100/// ordering among them; ordering is the order they are emitted within the build process,
    101/// not lexicographic order.
    102///
    103/// WARNING: Even though this type implements PartialOrd + Ord, this is a lexicographic ordering.
    104/// The linker line will require an explicit sorting algorithm. PartialOrd + Ord is derived because
    105/// BuildOutput requires it but that ordering is different from the one for the linker search path,
    106/// at least today. It may be worth reconsidering & perhaps it's ok if BuildOutput doesn't have
    107/// a lexicographic ordering for the library_paths? I'm not sure the consequence of that.
    108#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
    109pub enum LibraryPath {
    110    /// The path is pointing within the output folder of the crate and takes priority over
    111    /// external paths when passed to the linker.
    112    CargoArtifact(PathBuf),
    113    /// The path is pointing outside of the crate's build location. The linker will always
    114    /// receive such paths after `CargoArtifact`.
    115    External(PathBuf),
    116}
    117
    118impl LibraryPath {
    119    fn new(p: PathBuf, script_out_dir: &Path) -> Self {
    120        let search_path = get_dynamic_search_path(&p);
    121        if search_path.starts_with(script_out_dir) {
    122            Self::CargoArtifact(p)
    123        } else {
    124            Self::External(p)
    125        }
    126    }
    127
    128    pub fn into_path_buf(self) -> PathBuf {
    129        match self {
    130            LibraryPath::CargoArtifact(p) | LibraryPath::External(p) => p,
    131        }
    132    }
    133}
    134
    135impl AsRef<PathBuf> for LibraryPath {
    136    fn as_ref(&self) -> &PathBuf {
    137        match self {
    138            LibraryPath::CargoArtifact(p) | LibraryPath::External(p) => p,
    139        }
    140    }
    141}
    142
    143/// Contains the parsed output of a custom build script.
    144#[derive(Clone, Debug, Hash, Default, PartialEq, Eq, PartialOrd, Ord)]
    145pub struct BuildOutput {
    146    /// Paths to pass to rustc with the `-L` flag.
    147    pub library_paths: Vec<LibraryPath>,
    148    /// Names and link kinds of libraries, suitable for the `-l` flag.
    149    pub library_links: Vec<String>,
    150    /// Linker arguments suitable to be passed to `-C link-arg=<args>`
    151    pub linker_args: Vec<(LinkArgTarget, String)>,
    152    /// Various `--cfg` flags to pass to the compiler.
    153    pub cfgs: Vec<String>,
    154    /// Various `--check-cfg` flags to pass to the compiler.
    155    pub check_cfgs: Vec<String>,
    156    /// Additional environment variables to run the compiler with.
    157    pub env: Vec<(String, String)>,
    158    /// Metadata to pass to the immediate dependencies.
    159    pub metadata: Vec<(String, String)>,
    160    /// Paths to trigger a rerun of this build script.
    161    /// May be absolute or relative paths (relative to package root).
    162    pub rerun_if_changed: Vec<PathBuf>,
    163    /// Environment variables which, when changed, will cause a rebuild.
    164    pub rerun_if_env_changed: Vec<String>,
    165    /// Errors and warnings generated by this build.
    166    ///
    167    /// These are only displayed if this is a "local" package, `-vv` is used, or
    168    /// there is a build error for any target in this package. Note that any log
    169    /// message of severity `Error` will by itself cause a build error, and will
    170    /// cause all log messages to be displayed.
    171    pub log_messages: Vec<LogMessage>,
    172}
    173
    174/// Map of packages to build script output.
    175///
    176/// This initially starts out as empty. Overridden build scripts get
    177/// inserted during `build_map`. The rest of the entries are added
    178/// immediately after each build script runs.
    179///
    180/// The [`UnitHash`] is the unique metadata hash for the `RunCustomBuild` Unit of
    181/// the package. It needs a unique key, since the build script can be run
    182/// multiple times with different profiles or features. We can't embed a
    183/// `Unit` because this structure needs to be shareable between threads.
    184#[derive(Default)]
    185pub struct BuildScriptOutputs {
    186    outputs: HashMap<UnitHash, BuildOutput>,
    187}
    188
    189/// Linking information for a `Unit`.
    190///
    191/// See [`build_map`] for more details.
    192#[derive(Default)]
    193pub struct BuildScripts {
    194    /// List of build script outputs this Unit needs to include for linking. Each
    195    /// element is an index into `BuildScriptOutputs`.
    196    ///
    197    /// Cargo will use this `to_link` vector to add `-L` flags to compiles as we
    198    /// propagate them upwards towards the final build. Note, however, that we
    199    /// need to preserve the ordering of `to_link` to be topologically sorted.
    200    /// This will ensure that build scripts which print their paths properly will
    201    /// correctly pick up the files they generated (if there are duplicates
    202    /// elsewhere).
    203    ///
    204    /// To preserve this ordering, the (id, metadata) is stored in two places, once
    205    /// in the `Vec` and once in `seen_to_link` for a fast lookup. We maintain
    206    /// this as we're building interactively below to ensure that the memory
    207    /// usage here doesn't blow up too much.
    208    ///
    209    /// For more information, see #2354.
    210    pub to_link: Vec<(PackageId, UnitHash)>,
    211    /// This is only used while constructing `to_link` to avoid duplicates.
    212    seen_to_link: HashSet<(PackageId, UnitHash)>,
    213    /// Host-only dependencies that have build scripts. Each element is an
    214    /// index into `BuildScriptOutputs`.
    215    ///
    216    /// This is the set of transitive dependencies that are host-only
    217    /// (proc-macro, plugin, build-dependency) that contain a build script.
    218    /// Any `BuildOutput::library_paths` path relative to `target` will be
    219    /// added to `LD_LIBRARY_PATH` so that the compiler can find any dynamic
    220    /// libraries a build script may have generated.
    221    pub plugins: BTreeSet<(PackageId, UnitHash)>,
    222}
    223
    224/// Dependency information as declared by a build script that might trigger
    225/// a recompile of itself.
    226#[derive(Debug)]
    227pub struct BuildDeps {
    228    /// Absolute path to the file in the target directory that stores the
    229    /// output of the build script.
    230    pub build_script_output: PathBuf,
    231    /// Files that trigger a rebuild if they change.
    232    pub rerun_if_changed: Vec<PathBuf>,
    233    /// Environment variables that trigger a rebuild if they change.
    234    pub rerun_if_env_changed: Vec<String>,
    235}
    236
    237/// Represents one of the instructions from `cargo::rustc-link-arg-*` build
    238/// script instruction family.
    239///
    240/// In other words, indicates targets that custom linker arguments applies to.
    241///
    242/// See the [build script documentation][1] for more.
    243///
    244/// [1]: https://doc.rust-lang.org/nightly/cargo/reference/build-scripts.html#cargorustc-link-argflag
    245#[derive(Clone, Hash, Debug, PartialEq, Eq, PartialOrd, Ord)]
    246pub enum LinkArgTarget {
    247    /// Represents `cargo::rustc-link-arg=FLAG`.
    248    All,
    249    /// Represents `cargo::rustc-cdylib-link-arg=FLAG`.
    250    Cdylib,
    251    /// Represents `cargo::rustc-link-arg-bins=FLAG`.
    252    Bin,
    253    /// Represents `cargo::rustc-link-arg-bin=BIN=FLAG`.
    254    SingleBin(String),
    255    /// Represents `cargo::rustc-link-arg-tests=FLAG`.
    256    Test,
    257    /// Represents `cargo::rustc-link-arg-benches=FLAG`.
    258    Bench,
    259    /// Represents `cargo::rustc-link-arg-examples=FLAG`.
    260    Example,
    261}
    262
    263impl LinkArgTarget {
    264    /// Checks if this link type applies to a given [`Target`].
    265    pub fn applies_to(&self, target: &Target, mode: CompileMode) -> bool {
    266        let is_test = mode.is_any_test();
    267        match self {
    268            LinkArgTarget::All => true,
    269            LinkArgTarget::Cdylib => !is_test && target.is_cdylib(),
    270            LinkArgTarget::Bin => target.is_bin(),
    271            LinkArgTarget::SingleBin(name) => target.is_bin() && target.name() == name,
    272            LinkArgTarget::Test => target.is_test(),
    273            LinkArgTarget::Bench => target.is_bench(),
    274            LinkArgTarget::Example => target.is_exe_example(),
    275        }
    276    }
    277}
    278
    279/// Prepares a `Work` that executes the target as a custom build script.
    280#[tracing::instrument(skip_all)]
    281pub fn prepare(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<Job> {
    282    let metadata = build_runner.get_run_build_script_metadata(unit);
    283    if build_runner
    284        .build_script_outputs
    285        .lock()
    286        .unwrap()
    287        .contains_key(metadata)
    288    {
    289        // The output is already set, thus the build script is overridden.
    290        fingerprint::prepare_target(build_runner, unit, false)
    291    } else {
    292        build_work(build_runner, unit)
    293    }
    294}
    295
    296/// Emits the output of a build script as a [`machine_message::BuildScript`]
    297/// JSON string to standard output.
    298fn emit_build_output(
    299    state: &JobState<'_, '_>,
    300    output: &BuildOutput,
    301    out_dir: &Path,
    302    package_id: PackageId,
    303) -> CargoResult<()> {
    304    let library_paths = output
    305        .library_paths
    306        .iter()
    307        .map(|l| l.as_ref().display().to_string())
    308        .collect::<Vec<_>>();
    309
    310    let msg = machine_message::BuildScript {
    311        package_id: package_id.to_spec(),
    312        linked_libs: &output.library_links,
    313        linked_paths: &library_paths,
    314        cfgs: &output.cfgs,
    315        env: &output.env,
    316        out_dir,
    317    }
    318    .to_json_string();
    319    state.stdout(msg)?;
    320    Ok(())
    321}
    322
    323/// Constructs the unit of work of running a build script.
    324///
    325/// The construction includes:
    326///
    327/// * Set environment variables for the build script run.
    328/// * Create the output dir (`OUT_DIR`) for the build script output.
    329/// * Determine if the build script needs a re-run.
    330/// * Run the build script and store its output.
    331fn build_work(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<Job> {
    332    assert!(unit.mode.is_run_custom_build());
    333    let bcx = &build_runner.bcx;
    334    let dependencies = build_runner.unit_deps(unit);
    335    let build_script_unit = dependencies
    336        .iter()
    337        .find(|d| !d.unit.mode.is_run_custom_build() && d.unit.target.is_custom_build())
    338        .map(|d| &d.unit)
    339        .expect("running a script not depending on an actual script");
    340    let script_dir = build_runner.files().build_script_dir(build_script_unit);
    341    let script_out_dir = build_runner.files().build_script_out_dir(unit);
    342    let script_run_dir = build_runner.files().build_script_run_dir(unit);
    343    let build_plan = bcx.build_config.build_plan;
    344    let invocation_name = unit.buildkey();
    345
    346    if let Some(deps) = unit.pkg.manifest().metabuild() {
    347        prepare_metabuild(build_runner, build_script_unit, deps)?;
    348    }
    349
    350    // Building the command to execute
    351    let to_exec = script_dir.join(unit.target.name());
    352
    353    // Start preparing the process to execute, starting out with some
    354    // environment variables. Note that the profile-related environment
    355    // variables are not set with this the build script's profile but rather the
    356    // package's library profile.
    357    // NOTE: if you add any profile flags, be sure to update
    358    // `Profiles::get_profile_run_custom_build` so that those flags get
    359    // carried over.
    360    let to_exec = to_exec.into_os_string();
    361    let mut cmd = build_runner.compilation.host_process(to_exec, &unit.pkg)?;
    362    let debug = unit.profile.debuginfo.is_turned_on();
    363    cmd.env("OUT_DIR", &script_out_dir)
    364        .env("CARGO_MANIFEST_DIR", unit.pkg.root())
    365        .env("CARGO_MANIFEST_PATH", unit.pkg.manifest_path())
    366        .env("NUM_JOBS", &bcx.jobs().to_string())
    367        .env("TARGET", bcx.target_data.short_name(&unit.kind))
    368        .env("DEBUG", debug.to_string())
    369        .env("OPT_LEVEL", &unit.profile.opt_level)
    370        .env(
    371            "PROFILE",
    372            match unit.profile.root {
    373                ProfileRoot::Release => "release",
    374                ProfileRoot::Debug => "debug",
    375            },
    376        )
    377        .env("HOST", &bcx.host_triple())
    378        .env("RUSTC", &bcx.rustc().path)
    379        .env("RUSTDOC", &*bcx.gctx.rustdoc()?)
    380        .inherit_jobserver(&build_runner.jobserver);
    381
    382    // Find all artifact dependencies and make their file and containing directory discoverable using environment variables.
    383    for (var, value) in artifact::get_env(build_runner, dependencies)? {
    384        cmd.env(&var, value);
    385    }
    386
    387    if let Some(linker) = &build_runner.compilation.target_linker(unit.kind) {
    388        cmd.env("RUSTC_LINKER", linker);
    389    }
    390
    391    if let Some(links) = unit.pkg.manifest().links() {
    392        cmd.env("CARGO_MANIFEST_LINKS", links);
    393    }
    394
    395    if let Some(trim_paths) = unit.profile.trim_paths.as_ref() {
    396        cmd.env("CARGO_TRIM_PATHS", trim_paths.to_string());
    397    }
    398
    399    // Be sure to pass along all enabled features for this package, this is the
    400    // last piece of statically known information that we have.
    401    for feat in &unit.features {
    402        cmd.env(&format!("CARGO_FEATURE_{}", super::envify(feat)), "1");
    403    }
    404
    405    let mut cfg_map = HashMap::new();
    406    cfg_map.insert(
    407        "feature",
    408        unit.features.iter().map(|s| s.as_str()).collect::<Vec<_>>(),
    409    );
    410    for cfg in bcx.target_data.cfg(unit.kind) {
    411        match *cfg {
    412            Cfg::Name(ref n) => {
    413                cfg_map.insert(n.as_str(), Vec::new());
    414            }
    415            Cfg::KeyPair(ref k, ref v) => {
    416                let values = cfg_map.entry(k.as_str()).or_default();
    417                values.push(v.as_str());
    418            }
    419        }
    420    }
    421    for (k, v) in cfg_map {
    422        if k == "debug_assertions" {
    423            // This cfg is always true and misleading, so avoid setting it.
    424            // That is because Cargo queries rustc without any profile settings.
    425            continue;
    426        }
    427        // FIXME: We should handle raw-idents somehow instead of predenting they
    428        // don't exist here
    429        let k = format!("CARGO_CFG_{}", super::envify(k));
    430        cmd.env(&k, v.join(","));
    431    }
    432
    433    // Also inform the build script of the rustc compiler context.
    434    if let Some(wrapper) = bcx.rustc().wrapper.as_ref() {
    435        cmd.env("RUSTC_WRAPPER", wrapper);
    436    } else {
    437        cmd.env_remove("RUSTC_WRAPPER");
    438    }
    439    cmd.env_remove("RUSTC_WORKSPACE_WRAPPER");
    440    if build_runner.bcx.ws.is_member(&unit.pkg) {
    441        if let Some(wrapper) = bcx.rustc().workspace_wrapper.as_ref() {
    442            cmd.env("RUSTC_WORKSPACE_WRAPPER", wrapper);
    443        }
    444    }
    445    cmd.env("CARGO_ENCODED_RUSTFLAGS", unit.rustflags.join("\x1f"));
    446    cmd.env_remove("RUSTFLAGS");
    447
    448    if build_runner.bcx.ws.gctx().extra_verbose() {
    449        cmd.display_env_vars();
    450    }
    451
    452    // Gather the set of native dependencies that this package has along with
    453    // some other variables to close over.
    454    //
    455    // This information will be used at build-time later on to figure out which
    456    // sorts of variables need to be discovered at that time.
    457    let lib_deps = dependencies
    458        .iter()
    459        .filter_map(|dep| {
    460            if dep.unit.mode.is_run_custom_build() {
    461                let dep_metadata = build_runner.get_run_build_script_metadata(&dep.unit);
    462                Some((
    463                    dep.unit.pkg.manifest().links().unwrap().to_string(),
    464                    dep.unit.pkg.package_id(),
    465                    dep_metadata,
    466                ))
    467            } else {
    468                None
    469            }
    470        })
    471        .collect::<Vec<_>>();
    472    let library_name = unit.pkg.library().map(|t| t.crate_name());
    473    let pkg_descr = unit.pkg.to_string();
    474    let build_script_outputs = Arc::clone(&build_runner.build_script_outputs);
    475    let id = unit.pkg.package_id();
    476    let output_file = script_run_dir.join("output");
    477    let err_file = script_run_dir.join("stderr");
    478    let root_output_file = script_run_dir.join("root-output");
    479    let host_target_root = build_runner.files().host_dest().to_path_buf();
    480    let all = (
    481        id,
    482        library_name.clone(),
    483        pkg_descr.clone(),
    484        Arc::clone(&build_script_outputs),
    485        output_file.clone(),
    486        script_out_dir.clone(),
    487    );
    488    let build_scripts = build_runner.build_scripts.get(unit).cloned();
    489    let json_messages = bcx.build_config.emit_json();
    490    let extra_verbose = bcx.gctx.extra_verbose();
    491    let (prev_output, prev_script_out_dir) = prev_build_output(build_runner, unit);
    492    let metadata_hash = build_runner.get_run_build_script_metadata(unit);
    493
    494    paths::create_dir_all(&script_dir)?;
    495    paths::create_dir_all(&script_out_dir)?;
    496
    497    let nightly_features_allowed = build_runner.bcx.gctx.nightly_features_allowed;
    498    let targets: Vec<Target> = unit.pkg.targets().to_vec();
    499    let msrv = unit.pkg.rust_version().cloned();
    500    // Need a separate copy for the fresh closure.
    501    let targets_fresh = targets.clone();
    502    let msrv_fresh = msrv.clone();
    503
    504    let env_profile_name = unit.profile.name.to_uppercase();
    505    let built_with_debuginfo = build_runner
    506        .bcx
    507        .unit_graph
    508        .get(unit)
    509        .and_then(|deps| deps.iter().find(|dep| dep.unit.target == unit.target))
    510        .map(|dep| dep.unit.profile.debuginfo.is_turned_on())
    511        .unwrap_or(false);
    512
    513    // Prepare the unit of "dirty work" which will actually run the custom build
    514    // command.
    515    //
    516    // Note that this has to do some extra work just before running the command
    517    // to determine extra environment variables and such.
    518    let dirty = Work::new(move |state| {
    519        // Make sure that OUT_DIR exists.
    520        //
    521        // If we have an old build directory, then just move it into place,
    522        // otherwise create it!
    523        paths::create_dir_all(&script_out_dir)
    524            .context("failed to create script output directory for build command")?;
    525
    526        // For all our native lib dependencies, pick up their metadata to pass
    527        // along to this custom build command. We're also careful to augment our
    528        // dynamic library search path in case the build script depended on any
    529        // native dynamic libraries.
    530        if !build_plan {
    531            let build_script_outputs = build_script_outputs.lock().unwrap();
    532            for (name, dep_id, dep_metadata) in lib_deps {
    533                let script_output = build_script_outputs.get(dep_metadata).ok_or_else(|| {
    534                    internal(format!(
    535                        "failed to locate build state for env vars: {}/{}",
    536                        dep_id, dep_metadata
    537                    ))
    538                })?;
    539                let data = &script_output.metadata;
    540                for (key, value) in data.iter() {
    541                    cmd.env(
    542                        &format!("DEP_{}_{}", super::envify(&name), super::envify(key)),
    543                        value,
    544                    );
    545                }
    546            }
    547            if let Some(build_scripts) = build_scripts {
    548                super::add_plugin_deps(
    549                    &mut cmd,
    550                    &build_script_outputs,
    551                    &build_scripts,
    552                    &host_target_root,
    553                )?;
    554            }
    555        }
    556
    557        if build_plan {
    558            state.build_plan(invocation_name, cmd.clone(), Arc::new(Vec::new()));
    559            return Ok(());
    560        }
    561
    562        // And now finally, run the build command itself!
    563        state.running(&cmd);
    564        let timestamp = paths::set_invocation_time(&script_run_dir)?;
    565        let prefix = format!("[{} {}] ", id.name(), id.version());
    566        let mut log_messages_in_case_of_panic = Vec::new();
    567        let span = tracing::debug_span!("build_script", process = cmd.to_string());
    568        let output = span.in_scope(|| {
    569            cmd.exec_with_streaming(
    570                &mut |stdout| {
    571                    if let Some(error) = stdout.strip_prefix(CARGO_ERROR_SYNTAX) {
    572                        log_messages_in_case_of_panic.push((Severity::Error, error.to_owned()));
    573                    }
    574                    if let Some(warning) = stdout
    575                        .strip_prefix(OLD_CARGO_WARNING_SYNTAX)
    576                        .or(stdout.strip_prefix(NEW_CARGO_WARNING_SYNTAX))
    577                    {
    578                        log_messages_in_case_of_panic.push((Severity::Warning, warning.to_owned()));
    579                    }
    580                    if extra_verbose {
    581                        state.stdout(format!("{}{}", prefix, stdout))?;
    582                    }
    583                    Ok(())
    584                },
    585                &mut |stderr| {
    586                    if extra_verbose {
    587                        state.stderr(format!("{}{}", prefix, stderr))?;
    588                    }
    589                    Ok(())
    590                },
    591                true,
    592            )
    593            .with_context(|| {
    594                let mut build_error_context =
    595                    format!("failed to run custom build command for `{}`", pkg_descr);
    596
    597                // If we're opting into backtraces, mention that build dependencies' backtraces can
    598                // be improved by requesting debuginfo to be built, if we're not building with
    599                // debuginfo already.
    600                //
    601                // ALLOWED: Other tools like `rustc` might read it directly
    602                // through `std::env`. We should make their behavior consistent.
    603                #[allow(clippy::disallowed_methods)]
    604                if let Ok(show_backtraces) = std::env::var("RUST_BACKTRACE") {
    605                    if !built_with_debuginfo && show_backtraces != "0" {
    606                        build_error_context.push_str(&format!(
    607                            "\n\
    608                            note: To improve backtraces for build dependencies, set the \
    609                            CARGO_PROFILE_{env_profile_name}_BUILD_OVERRIDE_DEBUG=true environment \
    610                            variable to enable debug information generation.",
    611                        ));
    612                    }
    613                }
    614
    615                build_error_context
    616            })
    617        });
    618
    619        // If the build failed
    620        if let Err(error) = output {
    621            insert_log_messages_in_build_outputs(
    622                build_script_outputs,
    623                id,
    624                metadata_hash,
    625                log_messages_in_case_of_panic,
    626            );
    627            return Err(error);
    628        }
    629        // ... or it logged any errors
    630        else if log_messages_in_case_of_panic
    631            .iter()
    632            .any(|(severity, _)| *severity == Severity::Error)
    633        {
    634            insert_log_messages_in_build_outputs(
    635                build_script_outputs,
    636                id,
    637                metadata_hash,
    638                log_messages_in_case_of_panic,
    639            );
    640            anyhow::bail!("build script logged errors");
    641        }
    642
    643        let output = output.unwrap();
    644
    645        // After the build command has finished running, we need to be sure to
    646        // remember all of its output so we can later discover precisely what it
    647        // was, even if we don't run the build command again (due to freshness).
    648        //
    649        // This is also the location where we provide feedback into the build
    650        // state informing what variables were discovered via our script as
    651        // well.
    652        paths::write(&output_file, &output.stdout)?;
    653        // This mtime shift allows Cargo to detect if a source file was
    654        // modified in the middle of the build.
    655        paths::set_file_time_no_err(output_file, timestamp);
    656        paths::write(&err_file, &output.stderr)?;
    657        paths::write(&root_output_file, paths::path2bytes(&script_out_dir)?)?;
    658        let parsed_output = BuildOutput::parse(
    659            &output.stdout,
    660            library_name,
    661            &pkg_descr,
    662            &script_out_dir,
    663            &script_out_dir,
    664            nightly_features_allowed,
    665            &targets,
    666            &msrv,
    667        )?;
    668
    669        if json_messages {
    670            emit_build_output(state, &parsed_output, script_out_dir.as_path(), id)?;
    671        }
    672        build_script_outputs
    673            .lock()
    674            .unwrap()
    675            .insert(id, metadata_hash, parsed_output);
    676        Ok(())
    677    });
    678
    679    // Now that we've prepared our work-to-do, we need to prepare the fresh work
    680    // itself to run when we actually end up just discarding what we calculated
    681    // above.
    682    let fresh = Work::new(move |state| {
    683        let (id, library_name, pkg_descr, build_script_outputs, output_file, script_out_dir) = all;
    684        let output = match prev_output {
    685            Some(output) => output,
    686            None => BuildOutput::parse_file(
    687                &output_file,
    688                library_name,
    689                &pkg_descr,
    690                &prev_script_out_dir,
    691                &script_out_dir,
    692                nightly_features_allowed,
    693                &targets_fresh,
    694                &msrv_fresh,
    695            )?,
    696        };
    697
    698        if json_messages {
    699            emit_build_output(state, &output, script_out_dir.as_path(), id)?;
    700        }
    701
    702        build_script_outputs
    703            .lock()
    704            .unwrap()
    705            .insert(id, metadata_hash, output);
    706        Ok(())
    707    });
    708
    709    let mut job = if build_runner.bcx.build_config.build_plan {
    710        Job::new_dirty(Work::noop(), DirtyReason::FreshBuild)
    711    } else {
    712        fingerprint::prepare_target(build_runner, unit, false)?
    713    };
    714    if job.freshness().is_dirty() {
    715        job.before(dirty);
    716    } else {
    717        job.before(fresh);
    718    }
    719    Ok(job)
    720}
    721
    722/// When a build script run fails, store only log messages, and nuke other
    723/// outputs, as they are likely broken.
    724fn insert_log_messages_in_build_outputs(
    725    build_script_outputs: Arc<Mutex<BuildScriptOutputs>>,
    726    id: PackageId,
    727    metadata_hash: UnitHash,
    728    log_messages: Vec<LogMessage>,
    729) {
    730    let build_output_with_only_log_messages = BuildOutput {
    731        log_messages,
    732        ..BuildOutput::default()
    733    };
    734    build_script_outputs.lock().unwrap().insert(
    735        id,
    736        metadata_hash,
    737        build_output_with_only_log_messages,
    738    );
    739}
    740
    741impl BuildOutput {
    742    /// Like [`BuildOutput::parse`] but from a file path.
    743    pub fn parse_file(
    744        path: &Path,
    745        library_name: Option<String>,
    746        pkg_descr: &str,
    747        script_out_dir_when_generated: &Path,
    748        script_out_dir: &Path,
    749        nightly_features_allowed: bool,
    750        targets: &[Target],
    751        msrv: &Option<RustVersion>,
    752    ) -> CargoResult<BuildOutput> {
    753        let contents = paths::read_bytes(path)?;
    754        BuildOutput::parse(
    755            &contents,
    756            library_name,
    757            pkg_descr,
    758            script_out_dir_when_generated,
    759            script_out_dir,
    760            nightly_features_allowed,
    761            targets,
    762            msrv,
    763        )
    764    }
    765
    766    /// Parses the output instructions of a build script.
    767    ///
    768    /// * `pkg_descr` --- for error messages
    769    /// * `library_name` --- for determining if `RUSTC_BOOTSTRAP` should be allowed
    770    pub fn parse(
    771        input: &[u8],
    772        // Takes String instead of InternedString so passing `unit.pkg.name()` will give a compile error.
    773        library_name: Option<String>,
    774        pkg_descr: &str,
    775        script_out_dir_when_generated: &Path,
    776        script_out_dir: &Path,
    777        nightly_features_allowed: bool,
    778        targets: &[Target],
    779        msrv: &Option<RustVersion>,
    780    ) -> CargoResult<BuildOutput> {
    781        let mut library_paths = Vec::new();
    782        let mut library_links = Vec::new();
    783        let mut linker_args = Vec::new();
    784        let mut cfgs = Vec::new();
    785        let mut check_cfgs = Vec::new();
    786        let mut env = Vec::new();
    787        let mut metadata = Vec::new();
    788        let mut rerun_if_changed = Vec::new();
    789        let mut rerun_if_env_changed = Vec::new();
    790        let mut log_messages = Vec::new();
    791        let whence = format!("build script of `{}`", pkg_descr);
    792        // Old syntax:
    793        //    cargo:rustc-flags=VALUE
    794        //    cargo:KEY=VALUE (for other unreserved keys)
    795        // New syntax:
    796        //    cargo::rustc-flags=VALUE
    797        //    cargo::metadata=KEY=VALUE (for other unreserved keys)
    798        // Due to backwards compatibility, no new keys can be added to this old format.
    799        const RESERVED_PREFIXES: &[&str] = &[
    800            "rustc-flags=",
    801            "rustc-link-lib=",
    802            "rustc-link-search=",
    803            "rustc-link-arg-cdylib=",
    804            "rustc-cdylib-link-arg=",
    805            "rustc-link-arg-bins=",
    806            "rustc-link-arg-bin=",
    807            "rustc-link-arg-tests=",
    808            "rustc-link-arg-benches=",
    809            "rustc-link-arg-examples=",
    810            "rustc-link-arg=",
    811            "rustc-cfg=",
    812            "rustc-check-cfg=",
    813            "rustc-env=",
    814            "warning=",
    815            "rerun-if-changed=",
    816            "rerun-if-env-changed=",
    817        ];
    818        const DOCS_LINK_SUGGESTION: &str = "See https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script \
    819                for more information about build script outputs.";
    820
    821        fn has_reserved_prefix(flag: &str) -> bool {
    822            RESERVED_PREFIXES
    823                .iter()
    824                .any(|reserved_prefix| flag.starts_with(reserved_prefix))
    825        }
    826
    827        fn check_minimum_supported_rust_version_for_new_syntax(
    828            pkg_descr: &str,
    829            msrv: &Option<RustVersion>,
    830            flag: &str,
    831        ) -> CargoResult<()> {
    832            if let Some(msrv) = msrv {
    833                let new_syntax_added_in = RustVersion::from_str("1.77.0")?;
    834                if !new_syntax_added_in.is_compatible_with(msrv.as_partial()) {
    835                    let old_syntax_suggestion = if has_reserved_prefix(flag) {
    836                        format!(
    837                            "Switch to the old `cargo:{flag}` syntax (note the single colon).\n"
    838                        )
    839                    } else if flag.starts_with("metadata=") {
    840                        let old_format_flag = flag.strip_prefix("metadata=").unwrap();
    841                        format!(
    842                            "Switch to the old `cargo:{old_format_flag}` syntax instead of `cargo::{flag}` (note the single colon).\n"
    843                        )
    844                    } else {
    845                        String::new()
    846                    };
    847
    848                    bail!(
    849                        "the `cargo::` syntax for build script output instructions was added in \
    850                        Rust 1.77.0, but the minimum supported Rust version of `{pkg_descr}` is {msrv}.\n\
    851                        {old_syntax_suggestion}\
    852                        {DOCS_LINK_SUGGESTION}"
    853                    );
    854                }
    855            }
    856
    857            Ok(())
    858        }
    859
    860        fn parse_directive<'a>(
    861            whence: &str,
    862            line: &str,
    863            data: &'a str,
    864            old_syntax: bool,
    865        ) -> CargoResult<(&'a str, &'a str)> {
    866            let mut iter = data.splitn(2, "=");
    867            let key = iter.next();
    868            let value = iter.next();
    869            match (key, value) {
    870                (Some(a), Some(b)) => Ok((a, b.trim_end())),
    871                _ => bail!(
    872                    "invalid output in {whence}: `{line}`\n\
    873                    Expected a line with `{syntax}KEY=VALUE` with an `=` character, \
    874                    but none was found.\n\
    875                    {DOCS_LINK_SUGGESTION}",
    876                    syntax = if old_syntax { "cargo:" } else { "cargo::" },
    877                ),
    878            }
    879        }
    880
    881        fn parse_metadata<'a>(
    882            whence: &str,
    883            line: &str,
    884            data: &'a str,
    885            old_syntax: bool,
    886        ) -> CargoResult<(&'a str, &'a str)> {
    887            let mut iter = data.splitn(2, "=");
    888            let key = iter.next();
    889            let value = iter.next();
    890            match (key, value) {
    891                (Some(a), Some(b)) => Ok((a, b.trim_end())),
    892                _ => bail!(
    893                    "invalid output in {whence}: `{line}`\n\
    894                    Expected a line with `{syntax}KEY=VALUE` with an `=` character, \
    895                    but none was found.\n\
    896                    {DOCS_LINK_SUGGESTION}",
    897                    syntax = if old_syntax {
    898                        "cargo:"
    899                    } else {
    900                        "cargo::metadata="
    901                    },
    902                ),
    903            }
    904        }
    905
    906        for line in input.split(|b| *b == b'\n') {
    907            let line = match str::from_utf8(line) {
    908                Ok(line) => line.trim(),
    909                Err(..) => continue,
    910            };
    911            let mut old_syntax = false;
    912            let (key, value) = if let Some(data) = line.strip_prefix("cargo::") {
    913                check_minimum_supported_rust_version_for_new_syntax(pkg_descr, msrv, data)?;
    914                // For instance, `cargo::rustc-flags=foo` or `cargo::metadata=foo=bar`.
    915                parse_directive(whence.as_str(), line, data, old_syntax)?
    916            } else if let Some(data) = line.strip_prefix("cargo:") {
    917                old_syntax = true;
    918                // For instance, `cargo:rustc-flags=foo`.
    919                if has_reserved_prefix(data) {
    920                    parse_directive(whence.as_str(), line, data, old_syntax)?
    921                } else {
    922                    // For instance, `cargo:foo=bar`.
    923                    ("metadata", data)
    924                }
    925            } else {
    926                // Skip this line since it doesn't start with "cargo:" or "cargo::".
    927                continue;
    928            };
    929            // This will rewrite paths if the target directory has been moved.
    930            let value = value.replace(
    931                script_out_dir_when_generated.to_str().unwrap(),
    932                script_out_dir.to_str().unwrap(),
    933            );
    934
    935            let syntax_prefix = if old_syntax { "cargo:" } else { "cargo::" };
    936            macro_rules! check_and_add_target {
    937                ($target_kind: expr, $is_target_kind: expr, $link_type: expr) => {
    938                    if !targets.iter().any(|target| $is_target_kind(target)) {
    939                        bail!(
    940                            "invalid instruction `{}{}` from {}\n\
    941                                The package {} does not have a {} target.",
    942                            syntax_prefix,
    943                            key,
    944                            whence,
    945                            pkg_descr,
    946                            $target_kind
    947                        );
    948                    }
    949                    linker_args.push(($link_type, value));
    950                };
    951            }
    952
    953            // Keep in sync with TargetConfig::parse_links_overrides.
    954            match key {
    955                "rustc-flags" => {
    956                    let (paths, links) = BuildOutput::parse_rustc_flags(&value, &whence)?;
    957                    library_links.extend(links.into_iter());
    958                    library_paths.extend(
    959                        paths
    960                            .into_iter()
    961                            .map(|p| LibraryPath::new(p, script_out_dir)),
    962                    );
    963                }
    964                "rustc-link-lib" => library_links.push(value.to_string()),
    965                "rustc-link-search" => {
    966                    library_paths.push(LibraryPath::new(PathBuf::from(value), script_out_dir))
    967                }
    968                "rustc-link-arg-cdylib" | "rustc-cdylib-link-arg" => {
    969                    if !targets.iter().any(|target| target.is_cdylib()) {
    970                        log_messages.push((
    971                            Severity::Warning,
    972                            format!(
    973                                "{}{} was specified in the build script of {}, \
    974                             but that package does not contain a cdylib target\n\
    975                             \n\
    976                             Allowing this was an unintended change in the 1.50 \
    977                             release, and may become an error in the future. \
    978                             For more information, see \
    979                             <https://github.com/rust-lang/cargo/issues/9562>.",
    980                                syntax_prefix, key, pkg_descr
    981                            ),
    982                        ));
    983                    }
    984                    linker_args.push((LinkArgTarget::Cdylib, value))
    985                }
    986                "rustc-link-arg-bins" => {
    987                    check_and_add_target!("bin", Target::is_bin, LinkArgTarget::Bin);
    988                }
    989                "rustc-link-arg-bin" => {
    990                    let (bin_name, arg) = value.split_once('=').ok_or_else(|| {
    991                        anyhow::format_err!(
    992                            "invalid instruction `{}{}={}` from {}\n\
    993                                The instruction should have the form {}{}=BIN=ARG",
    994                            syntax_prefix,
    995                            key,
    996                            value,
    997                            whence,
    998                            syntax_prefix,
    999                            key
    1000                        )
    1001                    })?;
    1002                    if !targets
    1003                        .iter()
    1004                        .any(|target| target.is_bin() && target.name() == bin_name)
    1005                    {
    1006                        bail!(
    1007                            "invalid instruction `{}{}` from {}\n\
    1008                                The package {} does not have a bin target with the name `{}`.",
    1009                            syntax_prefix,
    1010                            key,
    1011                            whence,
    1012                            pkg_descr,
    1013                            bin_name
    1014                        );
    1015                    }
    1016                    linker_args.push((
    1017                        LinkArgTarget::SingleBin(bin_name.to_owned()),
    1018                        arg.to_string(),
    1019                    ));
    1020                }
    1021                "rustc-link-arg-tests" => {
    1022                    check_and_add_target!("test", Target::is_test, LinkArgTarget::Test);
    1023                }
    1024                "rustc-link-arg-benches" => {
    1025                    check_and_add_target!("benchmark", Target::is_bench, LinkArgTarget::Bench);
    1026                }
    1027                "rustc-link-arg-examples" => {
    1028                    check_and_add_target!("example", Target::is_example, LinkArgTarget::Example);
    1029                }
    1030                "rustc-link-arg" => {
    1031                    linker_args.push((LinkArgTarget::All, value));
    1032                }
    1033                "rustc-cfg" => cfgs.push(value.to_string()),
    1034                "rustc-check-cfg" => check_cfgs.push(value.to_string()),
    1035                "rustc-env" => {
    1036                    let (key, val) = BuildOutput::parse_rustc_env(&value, &whence)?;
    1037                    // Build scripts aren't allowed to set RUSTC_BOOTSTRAP.
    1038                    // See https://github.com/rust-lang/cargo/issues/7088.
    1039                    if key == "RUSTC_BOOTSTRAP" {
    1040                        // If RUSTC_BOOTSTRAP is already set, the user of Cargo knows about
    1041                        // bootstrap and still wants to override the channel. Give them a way to do
    1042                        // so, but still emit a warning that the current crate shouldn't be trying
    1043                        // to set RUSTC_BOOTSTRAP.
    1044                        // If this is a nightly build, setting RUSTC_BOOTSTRAP wouldn't affect the
    1045                        // behavior, so still only give a warning.
    1046                        // NOTE: cargo only allows nightly features on RUSTC_BOOTSTRAP=1, but we
    1047                        // want setting any value of RUSTC_BOOTSTRAP to downgrade this to a warning
    1048                        // (so that `RUSTC_BOOTSTRAP=library_name` will work)
    1049                        let rustc_bootstrap_allows = |name: Option<&str>| {
    1050                            let name = match name {
    1051                                // as of 2021, no binaries on crates.io use RUSTC_BOOTSTRAP, so
    1052                                // fine-grained opt-outs aren't needed. end-users can always use
    1053                                // RUSTC_BOOTSTRAP=1 from the top-level if it's really a problem.
    1054                                None => return false,
    1055                                Some(n) => n,
    1056                            };
    1057                            // ALLOWED: the process of rustc bootstrapping reads this through
    1058                            // `std::env`. We should make the behavior consistent. Also, we
    1059                            // don't advertise this for bypassing nightly.
    1060                            #[allow(clippy::disallowed_methods)]
    1061                            std::env::var("RUSTC_BOOTSTRAP")
    1062                                .map_or(false, |var| var.split(',').any(|s| s == name))
    1063                        };
    1064                        if nightly_features_allowed
    1065                            || rustc_bootstrap_allows(library_name.as_deref())
    1066                        {
    1067                            log_messages.push((Severity::Warning, format!("Cannot set `RUSTC_BOOTSTRAP={}` from {}.\n\
    1068                                note: Crates cannot set `RUSTC_BOOTSTRAP` themselves, as doing so would subvert the stability guarantees of Rust for your project.",
    1069                                val, whence
    1070                            )));
    1071                        } else {
    1072                            // Setting RUSTC_BOOTSTRAP would change the behavior of the crate.
    1073                            // Abort with an error.
    1074                            bail!(
    1075                                "Cannot set `RUSTC_BOOTSTRAP={}` from {}.\n\
    1076                                note: Crates cannot set `RUSTC_BOOTSTRAP` themselves, as doing so would subvert the stability guarantees of Rust for your project.\n\
    1077                                help: If you're sure you want to do this in your project, set the environment variable `RUSTC_BOOTSTRAP={}` before running cargo instead.",
    1078                                val,
    1079                                whence,
    1080                                library_name.as_deref().unwrap_or("1"),
    1081                            );
    1082                        }
    1083                    } else {
    1084                        env.push((key, val));
    1085                    }
    1086                }
    1087                "error" => log_messages.push((Severity::Error, value.to_string())),
    1088                "warning" => log_messages.push((Severity::Warning, value.to_string())),
    1089                "rerun-if-changed" => rerun_if_changed.push(PathBuf::from(value)),
    1090                "rerun-if-env-changed" => rerun_if_env_changed.push(value.to_string()),
    1091                "metadata" => {
    1092                    let (key, value) = parse_metadata(whence.as_str(), line, &value, old_syntax)?;
    1093                    metadata.push((key.to_owned(), value.to_owned()));
    1094                }
    1095                _ => bail!(
    1096                    "invalid output in {whence}: `{line}`\n\
    1097                    Unknown key: `{key}`.\n\
    1098                    {DOCS_LINK_SUGGESTION}",
    1099                ),
    1100            }
    1101        }
    1102
    1103        Ok(BuildOutput {
    1104            library_paths,
    1105            library_links,
    1106            linker_args,
    1107            cfgs,
    1108            check_cfgs,
    1109            env,
    1110            metadata,
    1111            rerun_if_changed,
    1112            rerun_if_env_changed,
    1113            log_messages,
    1114        })
    1115    }
    1116
    1117    /// Parses [`cargo::rustc-flags`] instruction.
    1118    ///
    1119    /// [`cargo::rustc-flags`]: https://doc.rust-lang.org/nightly/cargo/reference/build-scripts.html#cargorustc-flagsflags
    1120    pub fn parse_rustc_flags(
    1121        value: &str,
    1122        whence: &str,
    1123    ) -> CargoResult<(Vec<PathBuf>, Vec<String>)> {
    1124        let value = value.trim();
    1125        let mut flags_iter = value
    1126            .split(|c: char| c.is_whitespace())
    1127            .filter(|w| w.chars().any(|c| !c.is_whitespace()));
    1128        let (mut library_paths, mut library_links) = (Vec::new(), Vec::new());
    1129
    1130        while let Some(flag) = flags_iter.next() {
    1131            if flag.starts_with("-l") || flag.starts_with("-L") {
    1132                // Check if this flag has no space before the value as is
    1133                // common with tools like pkg-config
    1134                // e.g. -L/some/dir/local/lib or -licui18n
    1135                let (flag, mut value) = flag.split_at(2);
    1136                if value.is_empty() {
    1137                    value = match flags_iter.next() {
    1138                        Some(v) => v,
    1139                        None => bail! {
    1140                            "Flag in rustc-flags has no value in {}: {}",
    1141                            whence,
    1142                            value
    1143                        },
    1144                    }
    1145                }
    1146
    1147                match flag {
    1148                    "-l" => library_links.push(value.to_string()),
    1149                    "-L" => library_paths.push(PathBuf::from(value)),
    1150
    1151                    // This was already checked above
    1152                    _ => unreachable!(),
    1153                };
    1154            } else {
    1155                bail!(
    1156                    "Only `-l` and `-L` flags are allowed in {}: `{}`",
    1157                    whence,
    1158                    value
    1159                )
    1160            }
    1161        }
    1162        Ok((library_paths, library_links))
    1163    }
    1164
    1165    /// Parses [`cargo::rustc-env`] instruction.
    1166    ///
    1167    /// [`cargo::rustc-env`]: https://doc.rust-lang.org/nightly/cargo/reference/build-scripts.html#rustc-env
    1168    pub fn parse_rustc_env(value: &str, whence: &str) -> CargoResult<(String, String)> {
    1169        match value.split_once('=') {
    1170            Some((n, v)) => Ok((n.to_owned(), v.to_owned())),
    1171            _ => bail!("Variable rustc-env has no value in {whence}: {value}"),
    1172        }
    1173    }
    1174}
    1175
    1176/// Prepares the Rust script for the unstable feature [metabuild].
    1177///
    1178/// [metabuild]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#metabuild
    1179fn prepare_metabuild(
    1180    build_runner: &BuildRunner<'_, '_>,
    1181    unit: &Unit,
    1182    deps: &[String],
    1183) -> CargoResult<()> {
    1184    let mut output = Vec::new();
    1185    let available_deps = build_runner.unit_deps(unit);
    1186    // Filter out optional dependencies, and look up the actual lib name.
    1187    let meta_deps: Vec<_> = deps
    1188        .iter()
    1189        .filter_map(|name| {
    1190            available_deps
    1191                .iter()
    1192                .find(|d| d.unit.pkg.name().as_str() == name.as_str())
    1193                .map(|d| d.unit.target.crate_name())
    1194        })
    1195        .collect();
    1196    output.push("fn main() {\n".to_string());
    1197    for dep in &meta_deps {
    1198        output.push(format!("    {}::metabuild();\n", dep));
    1199    }
    1200    output.push("}\n".to_string());
    1201    let output = output.join("");
    1202    let path = unit
    1203        .pkg
    1204        .manifest()
    1205        .metabuild_path(build_runner.bcx.ws.build_dir());
    1206    paths::create_dir_all(path.parent().unwrap())?;
    1207    paths::write_if_changed(path, &output)?;
    1208    Ok(())
    1209}
    1210
    1211impl BuildDeps {
    1212    /// Creates a build script dependency information from a previous
    1213    /// build script output path and the content.
    1214    pub fn new(output_file: &Path, output: Option<&BuildOutput>) -> BuildDeps {
    1215        BuildDeps {
    1216            build_script_output: output_file.to_path_buf(),
    1217            rerun_if_changed: output
    1218                .map(|p| &p.rerun_if_changed)
    1219                .cloned()
    1220                .unwrap_or_default(),
    1221            rerun_if_env_changed: output
    1222                .map(|p| &p.rerun_if_env_changed)
    1223                .cloned()
    1224                .unwrap_or_default(),
    1225        }
    1226    }
    1227}
    1228
    1229/// Computes several maps in [`BuildRunner`].
    1230///
    1231/// - [`build_scripts`]: A map that tracks which build scripts each package
    1232///   depends on.
    1233/// - [`build_explicit_deps`]: Dependency statements emitted by build scripts
    1234///   from a previous run.
    1235/// - [`build_script_outputs`]: Pre-populates this with any overridden build
    1236///   scripts.
    1237///
    1238/// The important one here is [`build_scripts`], which for each `(package,
    1239/// metadata)` stores a [`BuildScripts`] object which contains a list of
    1240/// dependencies with build scripts that the unit should consider when linking.
    1241/// For example this lists all dependencies' `-L` flags which need to be
    1242/// propagated transitively.
    1243///
    1244/// The given set of units to this function is the initial set of
    1245/// targets/profiles which are being built.
    1246///
    1247/// [`build_scripts`]: BuildRunner::build_scripts
    1248/// [`build_explicit_deps`]: BuildRunner::build_explicit_deps
    1249/// [`build_script_outputs`]: BuildRunner::build_script_outputs
    1250pub fn build_map(build_runner: &mut BuildRunner<'_, '_>) -> CargoResult<()> {
    1251    let mut ret = HashMap::new();
    1252    for unit in &build_runner.bcx.roots {
    1253        build(&mut ret, build_runner, unit)?;
    1254    }
    1255    build_runner
    1256        .build_scripts
    1257        .extend(ret.into_iter().map(|(k, v)| (k, Arc::new(v))));
    1258    return Ok(());
    1259
    1260    // Recursive function to build up the map we're constructing. This function
    1261    // memoizes all of its return values as it goes along.
    1262    fn build<'a>(
    1263        out: &'a mut HashMap<Unit, BuildScripts>,
    1264        build_runner: &mut BuildRunner<'_, '_>,
    1265        unit: &Unit,
    1266    ) -> CargoResult<&'a BuildScripts> {
    1267        // Do a quick pre-flight check to see if we've already calculated the
    1268        // set of dependencies.
    1269        if out.contains_key(unit) {
    1270            return Ok(&out[unit]);
    1271        }
    1272
    1273        // If there is a build script override, pre-fill the build output.
    1274        if unit.mode.is_run_custom_build() {
    1275            if let Some(links) = unit.pkg.manifest().links() {
    1276                if let Some(output) = unit.links_overrides.get(links) {
    1277                    let metadata = build_runner.get_run_build_script_metadata(unit);
    1278                    build_runner.build_script_outputs.lock().unwrap().insert(
    1279                        unit.pkg.package_id(),
    1280                        metadata,
    1281                        output.clone(),
    1282                    );
    1283                }
    1284            }
    1285        }
    1286
    1287        let mut ret = BuildScripts::default();
    1288
    1289        // If a package has a build script, add itself as something to inspect for linking.
    1290        if !unit.target.is_custom_build() && unit.pkg.has_custom_build() {
    1291            let script_metas = build_runner
    1292                .find_build_script_metadatas(unit)
    1293                .expect("has_custom_build should have RunCustomBuild");
    1294            for script_meta in script_metas {
    1295                add_to_link(&mut ret, unit.pkg.package_id(), script_meta);
    1296            }
    1297        }
    1298
    1299        if unit.mode.is_run_custom_build() {
    1300            parse_previous_explicit_deps(build_runner, unit);
    1301        }
    1302
    1303        // We want to invoke the compiler deterministically to be cache-friendly
    1304        // to rustc invocation caching schemes, so be sure to generate the same
    1305        // set of build script dependency orderings via sorting the targets that
    1306        // come out of the `Context`.
    1307        let mut dependencies: Vec<Unit> = build_runner
    1308            .unit_deps(unit)
    1309            .iter()
    1310            .map(|d| d.unit.clone())
    1311            .collect();
    1312        dependencies.sort_by_key(|u| u.pkg.package_id());
    1313
    1314        for dep_unit in dependencies.iter() {
    1315            let dep_scripts = build(out, build_runner, dep_unit)?;
    1316
    1317            if dep_unit.target.for_host() {
    1318                ret.plugins.extend(dep_scripts.to_link.iter().cloned());
    1319            } else if dep_unit.target.is_linkable() {
    1320                for &(pkg, metadata) in dep_scripts.to_link.iter() {
    1321                    add_to_link(&mut ret, pkg, metadata);
    1322                }
    1323            }
    1324        }
    1325
    1326        match out.entry(unit.clone()) {
    1327            Entry::Vacant(entry) => Ok(entry.insert(ret)),
    1328            Entry::Occupied(_) => panic!("cyclic dependencies in `build_map`"),
    1329        }
    1330    }
    1331
    1332    // When adding an entry to 'to_link' we only actually push it on if the
    1333    // script hasn't seen it yet (e.g., we don't push on duplicates).
    1334    fn add_to_link(scripts: &mut BuildScripts, pkg: PackageId, metadata: UnitHash) {
    1335        if scripts.seen_to_link.insert((pkg, metadata)) {
    1336            scripts.to_link.push((pkg, metadata));
    1337        }
    1338    }
    1339
    1340    /// Load any dependency declarations from a previous build script run.
    1341    fn parse_previous_explicit_deps(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) {
    1342        let script_run_dir = build_runner.files().build_script_run_dir(unit);
    1343        let output_file = script_run_dir.join("output");
    1344        let (prev_output, _) = prev_build_output(build_runner, unit);
    1345        let deps = BuildDeps::new(&output_file, prev_output.as_ref());
    1346        build_runner.build_explicit_deps.insert(unit.clone(), deps);
    1347    }
    1348}
    1349
    1350/// Returns the previous parsed `BuildOutput`, if any, from a previous
    1351/// execution.
    1352///
    1353/// Also returns the directory containing the output, typically used later in
    1354/// processing.
    1355fn prev_build_output(
    1356    build_runner: &mut BuildRunner<'_, '_>,
    1357    unit: &Unit,
    1358) -> (Option<BuildOutput>, PathBuf) {
    1359    let script_out_dir = build_runner.files().build_script_out_dir(unit);
    1360    let script_run_dir = build_runner.files().build_script_run_dir(unit);
    1361    let root_output_file = script_run_dir.join("root-output");
    1362    let output_file = script_run_dir.join("output");
    1363
    1364    let prev_script_out_dir = paths::read_bytes(&root_output_file)
    1365        .and_then(|bytes| paths::bytes2path(&bytes))
    1366        .unwrap_or_else(|_| script_out_dir.clone());
    1367
    1368    (
    1369        BuildOutput::parse_file(
    1370            &output_file,
    1371            unit.pkg.library().map(|t| t.crate_name()),
    1372            &unit.pkg.to_string(),
    1373            &prev_script_out_dir,
    1374            &script_out_dir,
    1375            build_runner.bcx.gctx.nightly_features_allowed,
    1376            unit.pkg.targets(),
    1377            &unit.pkg.rust_version().cloned(),
    1378        )
    1379        .ok(),
    1380        prev_script_out_dir,
    1381    )
    1382}
    1383
    1384impl BuildScriptOutputs {
    1385    /// Inserts a new entry into the map.
    1386    fn insert(&mut self, pkg_id: PackageId, metadata: UnitHash, parsed_output: BuildOutput) {
    1387        match self.outputs.entry(metadata) {
    1388            Entry::Vacant(entry) => {
    1389                entry.insert(parsed_output);
    1390            }
    1391            Entry::Occupied(entry) => panic!(
    1392                "build script output collision for {}/{}\n\
    1393                old={:?}\nnew={:?}",
    1394                pkg_id,
    1395                metadata,
    1396                entry.get(),
    1397                parsed_output
    1398            ),
    1399        }
    1400    }
    1401
    1402    /// Returns `true` if the given key already exists.
    1403    fn contains_key(&self, metadata: UnitHash) -> bool {
    1404        self.outputs.contains_key(&metadata)
    1405    }
    1406
    1407    /// Gets the build output for the given key.
    1408    pub fn get(&self, meta: UnitHash) -> Option<&BuildOutput> {
    1409        self.outputs.get(&meta)
    1410    }
    1411
    1412    /// Returns an iterator over all entries.
    1413    pub fn iter(&self) -> impl Iterator<Item = (&UnitHash, &BuildOutput)> {
    1414        self.outputs.iter()
    1415    }
    1416}