Skip to main content

cargo/compiler/
links.rs

1use super::unit_graph::UnitGraph;
2use crate::resolver::Resolve;
3use crate::resolver::errors::describe_path;
4use crate::util::data_structures::{HashMap, HashSet};
5use crate::util::errors::CargoResult;
6use crate::workspace::PackageId;
7
8/// Validates [`package.links`] field in the manifest file does not conflict
9/// between packages.
10///
11/// NOTE: This is the *old* links validator. Links are usually validated in the
12/// resolver. However, the `links` field was added to the index in early 2018
13/// (see [rust-lang/cargo#4978]). However, `links` has been around since 2014,
14/// so there are still many crates in the index that don't have `links`
15/// properly set in the index (over 600 at the time of this writing in 2019).
16/// This can probably be removed at some point in the future, though it might
17/// be worth considering fixing the index.
18///
19/// [rust-lang/cargo#4978]: https://github.com/rust-lang/cargo/pull/4978
20/// [`package.links`]: https://doc.rust-lang.org/nightly/cargo/reference/build-scripts.html#the-links-manifest-key
21pub fn validate_links(resolve: &Resolve, unit_graph: &UnitGraph) -> CargoResult<()> {
22    let mut validated: HashSet<PackageId> = HashSet::default();
23    let mut links: HashMap<String, PackageId> = HashMap::default();
24    let mut units: Vec<_> = unit_graph.keys().collect();
25    // Sort primarily to make testing easier.
26    units.sort_unstable();
27    for unit in units {
28        if !validated.insert(unit.pkg.package_id()) {
29            continue;
30        }
31        let Some(lib) = unit.pkg.manifest().links() else {
32            continue;
33        };
34        if let Some(&prev) = links.get(lib) {
35            let prev_path = resolve
36                .path_to_top(&prev)
37                .into_iter()
38                .map(|(p, d)| (p, d.and_then(|d| d.iter().next())));
39            let pkg = unit.pkg.package_id();
40            let path = resolve
41                .path_to_top(&pkg)
42                .into_iter()
43                .map(|(p, d)| (p, d.and_then(|d| d.iter().next())));
44            anyhow::bail!(
45                "multiple packages link to native library `{}`, \
46                 but a native library can be linked only once\n\
47                 \n\
48                 {}\nlinks to native library `{}`\n\
49                 \n\
50                 {}\nalso links to native library `{}`",
51                lib,
52                describe_path(prev_path),
53                lib,
54                describe_path(path),
55                lib
56            )
57        }
58        links.insert(lib.to_string(), unit.pkg.package_id());
59    }
60    Ok(())
61}