cargo/core/compiler/
output_depinfo.rs
1use cargo_util::paths::normalize_path;
5use std::collections::{BTreeSet, HashSet};
6use std::io::{BufWriter, Write};
7use std::path::{Path, PathBuf};
8
9use super::{fingerprint, BuildRunner, FileFlavor, Unit};
10use crate::util::{internal, CargoResult};
11use cargo_util::paths;
12use tracing::debug;
13
14fn render_filename<P: AsRef<Path>>(path: P, basedir: Option<&str>) -> CargoResult<String> {
16 fn wrap_path(path: &Path) -> CargoResult<String> {
17 path.to_str()
18 .ok_or_else(|| internal(format!("path `{:?}` not utf-8", path)))
19 .map(|f| f.replace(" ", "\\ "))
20 }
21
22 let path = path.as_ref();
23 if let Some(basedir) = basedir {
24 let norm_path = normalize_path(path);
25 let norm_basedir = normalize_path(basedir.as_ref());
26 match norm_path.strip_prefix(norm_basedir) {
27 Ok(relpath) => wrap_path(relpath),
28 _ => wrap_path(path),
29 }
30 } else {
31 wrap_path(path)
32 }
33}
34
35fn add_deps_for_unit(
45 deps: &mut BTreeSet<PathBuf>,
46 build_runner: &mut BuildRunner<'_, '_>,
47 unit: &Unit,
48 visited: &mut HashSet<Unit>,
49) -> CargoResult<()> {
50 if !visited.insert(unit.clone()) {
51 return Ok(());
52 }
53
54 if !unit.mode.is_run_custom_build() {
57 let dep_info_loc = fingerprint::dep_info_loc(build_runner, unit);
59 if let Some(paths) = fingerprint::parse_dep_info(
60 unit.pkg.root(),
61 build_runner.files().host_root(),
62 &dep_info_loc,
63 )? {
64 for path in paths.files.into_keys() {
65 deps.insert(path);
66 }
67 } else {
68 debug!(
69 "can't find dep_info for {:?} {}",
70 unit.pkg.package_id(),
71 unit.target
72 );
73 return Err(internal("dep_info missing"));
74 }
75 }
76
77 if let Some(metadata) = build_runner.find_build_script_metadata(unit) {
79 if let Some(output) = build_runner
80 .build_script_outputs
81 .lock()
82 .unwrap()
83 .get(metadata)
84 {
85 for path in &output.rerun_if_changed {
86 let path = unit.pkg.root().join(path);
89 deps.insert(path);
90 }
91 }
92 }
93
94 let unit_deps = Vec::from(build_runner.unit_deps(unit)); for dep in unit_deps {
97 if dep.unit.is_local() {
98 add_deps_for_unit(deps, build_runner, &dep.unit, visited)?;
99 }
100 }
101 Ok(())
102}
103
104pub fn output_depinfo(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<()> {
122 let bcx = build_runner.bcx;
123 let mut deps = BTreeSet::new();
124 let mut visited = HashSet::new();
125 let success = add_deps_for_unit(&mut deps, build_runner, unit, &mut visited).is_ok();
126 let basedir_string;
127 let basedir = match bcx.gctx.build_config()?.dep_info_basedir.clone() {
128 Some(value) => {
129 basedir_string = value
130 .resolve_path(bcx.gctx)
131 .as_os_str()
132 .to_str()
133 .ok_or_else(|| anyhow::format_err!("build.dep-info-basedir path not utf-8"))?
134 .to_string();
135 Some(basedir_string.as_str())
136 }
137 None => None,
138 };
139 let deps = deps
140 .iter()
141 .map(|f| render_filename(f, basedir))
142 .collect::<CargoResult<Vec<_>>>()?;
143
144 for output in build_runner.outputs(unit)?.iter().filter(|o| {
145 !matches!(
146 o.flavor,
147 FileFlavor::DebugInfo | FileFlavor::Auxiliary | FileFlavor::Sbom
148 )
149 }) {
150 if let Some(ref link_dst) = output.hardlink {
151 let output_path = link_dst.with_extension("d");
152 if success {
153 let target_fn = render_filename(link_dst, basedir)?;
154
155 if let Ok(previous) = fingerprint::parse_rustc_dep_info(&output_path) {
158 if previous
159 .files
160 .iter()
161 .map(|(path, _checksum)| path)
162 .eq(deps.iter().map(Path::new))
163 {
164 continue;
165 }
166 }
167
168 let mut outfile = BufWriter::new(paths::create(output_path)?);
170 write!(outfile, "{}:", target_fn)?;
171 for dep in &deps {
172 write!(outfile, " {}", dep)?;
173 }
174 writeln!(outfile)?;
175
176 } else if output_path.exists() {
180 paths::remove_file(output_path)?;
181 }
182 }
183 }
184 Ok(())
185}