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