use crate::util::data_structures::HashSet;
use cargo_util::paths::normalize_path;
use std::collections::BTreeSet;
use std::io::{BufWriter, Write};
use std::path::{Path, PathBuf};
use super::{BuildRunner, FileFlavor, Unit, fingerprint};
use crate::util::{CargoResult, internal};
use cargo_util::paths;
use tracing::debug;
fn render_filename<P: AsRef<Path>>(path: P, basedir: Option<&str>) -> CargoResult<String> {
fn wrap_path(path: &Path) -> CargoResult<String> {
path.to_str()
.ok_or_else(|| internal(format!("path `{:?}` not utf-8", path)))
.map(|f| f.replace(" ", "\\ "))
}
let path = path.as_ref();
if let Some(basedir) = basedir {
let norm_path = normalize_path(path);
let norm_basedir = normalize_path(basedir.as_ref());
match norm_path.strip_prefix(norm_basedir) {
Ok(relpath) => wrap_path(relpath),
_ => wrap_path(path),
}
} else {
wrap_path(path)
}
}
fn add_deps_for_unit(
deps: &mut BTreeSet<PathBuf>,
build_runner: &mut BuildRunner<'_, '_>,
unit: &Unit,
visited: &mut HashSet<Unit>,
) -> CargoResult<()> {
if !visited.insert(unit.clone()) {
return Ok(());
}
if !unit.mode.is_run_custom_build() {
let dep_info_loc = fingerprint::dep_info_loc(build_runner, unit);
if let Some(paths) = fingerprint::parse_dep_info(
unit.pkg.root(),
build_runner.files().host_build_root(),
&dep_info_loc,
)? {
for path in paths.files.into_keys() {
deps.insert(path);
}
} else {
debug!(
"can't find dep_info for {:?} {}",
unit.pkg.package_id(),
unit.target
);
return Err(internal("dep_info missing"));
}
}
if let Some(metadata_vec) = build_runner.find_build_script_metadatas(unit) {
for metadata in metadata_vec {
if let Some(output) = build_runner
.build_script_outputs
.lock()
.unwrap()
.get(metadata)
{
for path in &output.rerun_if_changed {
let package_root = unit.pkg.root();
let path = if path.as_os_str().is_empty() {
package_root.to_path_buf()
} else {
package_root.join(path)
};
deps.insert(path);
}
}
}
}
let unit_deps = Vec::from(build_runner.unit_deps(unit)); for dep in unit_deps {
if dep.unit.is_local() {
add_deps_for_unit(deps, build_runner, &dep.unit, visited)?;
}
}
Ok(())
}
pub fn output_depinfo(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult<()> {
let bcx = build_runner.bcx;
let mut deps = BTreeSet::new();
let mut visited = HashSet::default();
let success = add_deps_for_unit(&mut deps, build_runner, unit, &mut visited).is_ok();
let basedir_string;
let basedir = match bcx.gctx.build_config()?.dep_info_basedir.clone() {
Some(value) => {
basedir_string = value
.resolve_path(bcx.gctx)
.as_os_str()
.to_str()
.ok_or_else(|| anyhow::format_err!("build.dep-info-basedir path not utf-8"))?
.to_string();
Some(basedir_string.as_str())
}
None => None,
};
let deps = deps
.iter()
.map(|f| render_filename(f, basedir))
.collect::<CargoResult<Vec<_>>>()?;
for output in build_runner.outputs(unit)?.iter().filter(|o| {
!matches!(
o.flavor,
FileFlavor::DebugInfo | FileFlavor::Auxiliary | FileFlavor::Sbom | FileFlavor::Unremap
)
}) {
if let Some(ref link_dst) = output.hardlink {
let output_path = link_dst.with_extension("d");
if success {
let target_fn = render_filename(link_dst, basedir)?;
if let Ok(previous) = fingerprint::parse_rustc_dep_info(&output_path) {
if previous
.files
.iter()
.map(|(path, _checksum)| path)
.eq(deps.iter().map(Path::new))
{
continue;
}
}
let mut outfile = BufWriter::new(paths::create(output_path)?);
write!(outfile, "{}:", target_fn)?;
for dep in &deps {
write!(outfile, " {}", dep)?;
}
writeln!(outfile)?;
} else if output_path.exists() {
paths::remove_file(output_path)?;
}
}
}
Ok(())
}