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_build_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 package_root = unit.pkg.root();
88
89 let path = if path.as_os_str().is_empty() {
90 package_root.to_path_buf()
93 } else {
94 package_root.join(path)
97 };
98
99 deps.insert(path);
100 }
101 }
102 }
103 }
104
105 let unit_deps = Vec::from(build_runner.unit_deps(unit)); for dep in unit_deps {
108 if dep.unit.is_local() {
109 add_deps_for_unit(deps, build_runner, &dep.unit, visited)?;
110 }
111 }
112 Ok(())
113}
114
115pub fn output_depinfo(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<()> {
133 let bcx = build_runner.bcx;
134 let mut deps = BTreeSet::new();
135 let mut visited = HashSet::new();
136 let success = add_deps_for_unit(&mut deps, build_runner, unit, &mut visited).is_ok();
137 let basedir_string;
138 let basedir = match bcx.gctx.build_config()?.dep_info_basedir.clone() {
139 Some(value) => {
140 basedir_string = value
141 .resolve_path(bcx.gctx)
142 .as_os_str()
143 .to_str()
144 .ok_or_else(|| anyhow::format_err!("build.dep-info-basedir path not utf-8"))?
145 .to_string();
146 Some(basedir_string.as_str())
147 }
148 None => None,
149 };
150 let deps = deps
151 .iter()
152 .map(|f| render_filename(f, basedir))
153 .collect::<CargoResult<Vec<_>>>()?;
154
155 for output in build_runner.outputs(unit)?.iter().filter(|o| {
156 !matches!(
157 o.flavor,
158 FileFlavor::DebugInfo | FileFlavor::Auxiliary | FileFlavor::Sbom
159 )
160 }) {
161 if let Some(ref link_dst) = output.hardlink {
162 let output_path = link_dst.with_extension("d");
163 if success {
164 let target_fn = render_filename(link_dst, basedir)?;
165
166 if let Ok(previous) = fingerprint::parse_rustc_dep_info(&output_path) {
169 if previous
170 .files
171 .iter()
172 .map(|(path, _checksum)| path)
173 .eq(deps.iter().map(Path::new))
174 {
175 continue;
176 }
177 }
178
179 let mut outfile = BufWriter::new(paths::create(output_path)?);
181 write!(outfile, "{}:", target_fn)?;
182 for dep in &deps {
183 write!(outfile, " {}", dep)?;
184 }
185 writeln!(outfile)?;
186
187 } else if output_path.exists() {
191 paths::remove_file(output_path)?;
192 }
193 }
194 }
195 Ok(())
196}