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