cargo/compiler/
artifact.rs1use cargo_util_schemas::core::CrateType;
4
5use crate::CargoResult;
6use crate::compiler::unit_graph::UnitDep;
7use crate::compiler::{BuildRunner, FileFlavor, Unit};
8use crate::workspace::dependency::ArtifactKind;
9use crate::workspace::{Dependency, Target, TargetKind};
10use cargo_util::collections::{HashMap, HashSet};
11use std::ffi::OsString;
12
13pub fn get_env(
16 build_runner: &BuildRunner<'_, '_>,
17 unit: &Unit,
18 dependencies: &[UnitDep],
19) -> CargoResult<HashMap<String, OsString>> {
20 let mut env = HashMap::default();
21
22 if unit.target.is_test() || unit.target.is_bench() {
26 for bin_target in unit
27 .pkg
28 .manifest()
29 .targets()
30 .iter()
31 .filter(|target| target.is_bin())
32 {
33 let name = bin_target
34 .binary_filename()
35 .unwrap_or_else(|| bin_target.name().to_string());
36
37 let exe_path = build_runner
41 .files()
42 .bin_link_for_target(bin_target, unit.kind, build_runner.bcx)?
43 .map(|path| path.as_os_str().to_os_string())
44 .unwrap_or_else(|| OsString::from(format!("placeholder:{name}")));
45
46 let key = format!("CARGO_BIN_EXE_{name}");
47 env.insert(key, exe_path);
48 }
49 }
50
51 for unit_dep in dependencies.iter().filter(|d| d.unit.artifact.is_true()) {
52 for artifact_path in build_runner
53 .outputs(&unit_dep.unit)?
54 .iter()
55 .filter_map(|f| (f.flavor == FileFlavor::Normal).then(|| &f.path))
56 {
57 let artifact_type_upper = unit_artifact_type_name_upper(&unit_dep.unit);
58 let dep_name = unit_dep.dep_name.unwrap_or(unit_dep.unit.pkg.name());
59 let dep_name_upper = dep_name.to_uppercase().replace("-", "_");
60
61 let var = format!("CARGO_{}_DIR_{}", artifact_type_upper, dep_name_upper);
62 let path = artifact_path.parent().expect("parent dir for artifacts");
63 env.insert(var, path.to_owned().into());
64
65 let var_file = format!(
66 "CARGO_{}_FILE_{}_{}",
67 artifact_type_upper,
68 dep_name_upper,
69 unit_dep.unit.target.name()
70 );
71 env.insert(var_file, artifact_path.to_owned().into());
72
73 if unit_dep.unit.target.name() == dep_name.as_str() {
78 let var = format!("CARGO_{}_FILE_{}", artifact_type_upper, dep_name_upper,);
79 env.insert(var, artifact_path.to_owned().into());
80 }
81 }
82 }
83 Ok(env)
84}
85
86fn unit_artifact_type_name_upper(unit: &Unit) -> &'static str {
87 match unit.target.kind() {
88 TargetKind::Lib(kinds) => match kinds.as_slice() {
89 &[CrateType::Cdylib] => "CDYLIB",
90 &[CrateType::Staticlib] => "STATICLIB",
91 invalid => unreachable!("BUG: artifacts cannot be of type {:?}", invalid),
92 },
93 TargetKind::Bin => "BIN",
94 invalid => unreachable!("BUG: artifacts cannot be of type {:?}", invalid),
95 }
96}
97
98pub(crate) fn match_artifacts_kind_with_targets<'t, 'd>(
104 artifact_dep: &'d Dependency,
105 targets: &'t [Target],
106 parent_package: &str,
107) -> CargoResult<HashSet<(&'d ArtifactKind, &'t Target)>> {
108 let mut out = HashSet::default();
109 let artifact_requirements = artifact_dep.artifact().expect("artifact present");
110 for artifact_kind in artifact_requirements.kinds() {
111 let mut extend = |kind, filter: &dyn Fn(&&Target) -> bool| {
112 let mut iter = targets.iter().filter(filter).peekable();
113 let found = iter.peek().is_some();
114 out.extend(std::iter::repeat(kind).zip(iter));
115 found
116 };
117 let found = match artifact_kind {
118 ArtifactKind::Cdylib => extend(artifact_kind, &|t| t.is_cdylib()),
119 ArtifactKind::Staticlib => extend(artifact_kind, &|t| t.is_staticlib()),
120 ArtifactKind::AllBinaries => extend(artifact_kind, &|t| t.is_bin()),
121 ArtifactKind::SelectedBinary(bin_name) => extend(artifact_kind, &|t| {
122 t.is_bin() && t.name() == bin_name.as_str()
123 }),
124 };
125 if !found {
126 anyhow::bail!(
127 "dependency `{}` in package `{}` requires a `{}` artifact to be present.",
128 artifact_dep.name_in_toml(),
129 parent_package,
130 artifact_kind
131 );
132 }
133 }
134 Ok(out)
135}