cargo/compiler/
output_depinfo.rs1use cargo_util::collections::HashSet;
5use cargo_util::paths::normalize_path;
6use std::collections::BTreeSet;
7use std::io::{BufWriter, Write};
8use std::path::{Path, PathBuf};
9
10use super::{BuildRunner, FileFlavor, Unit, fingerprint};
11use crate::context::ConfigRelativePathResolveExt;
12use crate::util::{CargoResult, internal};
13use cargo_util::paths;
14use tracing::debug;
15
16fn render_filename<P: AsRef<Path>>(path: P, basedir: Option<&str>) -> CargoResult<String> {
18 fn wrap_path(path: &Path) -> CargoResult<String> {
19 path.to_str()
20 .ok_or_else(|| internal(format!("path `{:?}` not utf-8", path)))
21 .map(|f| f.replace(" ", "\\ "))
22 }
23
24 let path = path.as_ref();
25 if let Some(basedir) = basedir {
26 let norm_path = normalize_path(path);
27 let norm_basedir = normalize_path(basedir.as_ref());
28 match norm_path.strip_prefix(norm_basedir) {
29 Ok(relpath) => wrap_path(relpath),
30 _ => wrap_path(path),
31 }
32 } else {
33 wrap_path(path)
34 }
35}
36
37fn add_deps_for_unit(
47 deps: &mut BTreeSet<PathBuf>,
48 build_runner: &mut BuildRunner<'_, '_>,
49 unit: &Unit,
50 visited: &mut HashSet<Unit>,
51) -> CargoResult<()> {
52 if !visited.insert(unit.clone()) {
53 return Ok(());
54 }
55
56 if !unit.mode.is_run_custom_build() {
59 let dep_info_loc = fingerprint::dep_info_loc(build_runner, unit);
61 if let Some(paths) = fingerprint::parse_dep_info(
62 unit.pkg.root(),
63 build_runner.files().host_build_root(),
64 build_runner.bcx.ws.root(),
65 &dep_info_loc,
66 )? {
67 for path in paths.files.into_keys() {
68 deps.insert(path);
69 }
70 } else {
71 debug!(
72 "can't find dep_info for {:?} {}",
73 unit.pkg.package_id(),
74 unit.target
75 );
76 return Err(internal("dep_info missing"));
77 }
78 }
79
80 if let Some(metadata_vec) = build_runner.find_build_script_metadatas(unit) {
82 for metadata in metadata_vec {
83 if let Some(output) = build_runner
84 .build_script_outputs
85 .lock()
86 .unwrap()
87 .get(metadata)
88 {
89 for path in &output.rerun_if_changed {
90 let package_root = unit.pkg.root();
91
92 let path = if path.as_os_str().is_empty() {
93 package_root.to_path_buf()
96 } else {
97 package_root.join(path)
100 };
101
102 deps.insert(path);
103 }
104 }
105 }
106 }
107
108 let unit_deps = Vec::from(build_runner.unit_deps(unit)); for dep in unit_deps {
111 if dep.unit.is_local() {
112 add_deps_for_unit(deps, build_runner, &dep.unit, visited)?;
113 }
114 }
115 Ok(())
116}
117
118pub fn output_depinfo(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<()> {
136 let bcx = build_runner.bcx;
137 let mut deps = BTreeSet::new();
138 let mut visited = HashSet::default();
139 let success = add_deps_for_unit(&mut deps, build_runner, unit, &mut visited).is_ok();
140 let basedir_string;
141 let basedir = match bcx.gctx.build_config()?.dep_info_basedir.clone() {
142 Some(value) => {
143 basedir_string = value
144 .resolve_path(bcx.gctx)
145 .as_os_str()
146 .to_str()
147 .ok_or_else(|| anyhow::format_err!("build.dep-info-basedir path not utf-8"))?
148 .to_string();
149 Some(basedir_string.as_str())
150 }
151 None => None,
152 };
153 let deps = deps
154 .iter()
155 .map(|f| render_filename(f, basedir))
156 .collect::<CargoResult<Vec<_>>>()?;
157
158 for output in build_runner.outputs(unit)?.iter().filter(|o| {
159 !matches!(
160 o.flavor,
161 FileFlavor::DebugInfo | FileFlavor::Auxiliary | FileFlavor::Sbom | FileFlavor::Unremap
162 )
163 }) {
164 if let Some(ref link_dst) = output.hardlink {
165 let output_path = link_dst.with_extension("d");
166 if success {
167 let target_fn = render_filename(link_dst, basedir)?;
168
169 if let Ok(previous) = fingerprint::parse_rustc_dep_info(&output_path) {
172 if previous
173 .files
174 .iter()
175 .map(|(path, _checksum)| path)
176 .eq(deps.iter().map(Path::new))
177 {
178 continue;
179 }
180 }
181
182 let mut outfile = BufWriter::new(paths::create(output_path)?);
184 write!(outfile, "{}:", target_fn)?;
185 for dep in &deps {
186 write!(outfile, " {}", dep)?;
187 }
188 writeln!(outfile)?;
189
190 } else if output_path.exists() {
194 paths::remove_file(output_path)?;
195 }
196 }
197 }
198 Ok(())
199}