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