cargo/core/compiler/
output_depinfo.rs1use cargo_util::paths::normalize_path;
5use std::collections::{BTreeSet, HashSet};
6use std::io::{BufWriter, Write};
7use std::path::{Path, PathBuf};
8
9use super::{BuildRunner, FileFlavor, Unit, fingerprint};
10use crate::util::{CargoResult, internal};
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_vec) = build_runner.find_build_script_metadatas(unit) {
79 for metadata in metadata_vec {
80 if let Some(output) = build_runner
81 .build_script_outputs
82 .lock()
83 .unwrap()
84 .get(metadata)
85 {
86 for path in &output.rerun_if_changed {
87 let path = unit.pkg.root().join(path);
90 deps.insert(path);
91 }
92 }
93 }
94 }
95
96 let unit_deps = Vec::from(build_runner.unit_deps(unit)); for dep in unit_deps {
99 if dep.unit.is_local() {
100 add_deps_for_unit(deps, build_runner, &dep.unit, visited)?;
101 }
102 }
103 Ok(())
104}
105
106pub fn output_depinfo(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<()> {
124 let bcx = build_runner.bcx;
125 let mut deps = BTreeSet::new();
126 let mut visited = HashSet::new();
127 let success = add_deps_for_unit(&mut deps, build_runner, unit, &mut visited).is_ok();
128 let basedir_string;
129 let basedir = match bcx.gctx.build_config()?.dep_info_basedir.clone() {
130 Some(value) => {
131 basedir_string = value
132 .resolve_path(bcx.gctx)
133 .as_os_str()
134 .to_str()
135 .ok_or_else(|| anyhow::format_err!("build.dep-info-basedir path not utf-8"))?
136 .to_string();
137 Some(basedir_string.as_str())
138 }
139 None => None,
140 };
141 let deps = deps
142 .iter()
143 .map(|f| render_filename(f, basedir))
144 .collect::<CargoResult<Vec<_>>>()?;
145
146 for output in build_runner.outputs(unit)?.iter().filter(|o| {
147 !matches!(
148 o.flavor,
149 FileFlavor::DebugInfo | FileFlavor::Auxiliary | FileFlavor::Sbom
150 )
151 }) {
152 if let Some(ref link_dst) = output.hardlink {
153 let output_path = link_dst.with_extension("d");
154 if success {
155 let target_fn = render_filename(link_dst, basedir)?;
156
157 if let Ok(previous) = fingerprint::parse_rustc_dep_info(&output_path) {
160 if previous
161 .files
162 .iter()
163 .map(|(path, _checksum)| path)
164 .eq(deps.iter().map(Path::new))
165 {
166 continue;
167 }
168 }
169
170 let mut outfile = BufWriter::new(paths::create(output_path)?);
172 write!(outfile, "{}:", target_fn)?;
173 for dep in &deps {
174 write!(outfile, " {}", dep)?;
175 }
176 writeln!(outfile)?;
177
178 } else if output_path.exists() {
182 paths::remove_file(output_path)?;
183 }
184 }
185 }
186 Ok(())
187}