    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61

    use super::unit_graph::UnitGraph;
    use crate::core::resolver::errors::describe_path;
    use crate::core::{PackageId, Resolve};
    use crate::util::errors::CargoResult;
    use std::collections::{HashMap, HashSet};

    /// Validates [`package.links`] field in the manifest file does not conflict
    /// between packages.
    ///
    /// NOTE: This is the *old* links validator. Links are usually validated in the
    /// resolver. However, the `links` field was added to the index in early 2018
    /// (see [rust-lang/cargo#4978]). However, `links` has been around since 2014,
    /// so there are still many crates in the index that don't have `links`
    /// properly set in the index (over 600 at the time of this writing in 2019).
    /// This can probably be removed at some point in the future, though it might
    /// be worth considering fixing the index.
    ///
    /// [rust-lang/cargo#4978]: https://github.com/rust-lang/cargo/pull/4978
    /// [`package.links`]: https://doc.rust-lang.org/nightly/cargo/reference/build-scripts.html#the-links-manifest-key
    pub fn validate_links(resolve: &Resolve, unit_graph: &UnitGraph) -> CargoResult<()> {
        let mut validated: HashSet<PackageId> = HashSet::new();
        let mut links: HashMap<String, PackageId> = HashMap::new();
        let mut units: Vec<_> = unit_graph.keys().collect();
        // Sort primarily to make testing easier.
        units.sort_unstable();
        for unit in units {
            if !validated.insert(unit.pkg.package_id()) {
                continue;
            }
            let lib = match unit.pkg.manifest().links() {
                Some(lib) => lib,
                None => continue,
            };
            if let Some(&prev) = links.get(lib) {
                let prev_path = resolve
                    .path_to_top(&prev)
                    .into_iter()
                    .map(|(p, d)| (p, d.and_then(|d| d.iter().next())));
                let pkg = unit.pkg.package_id();
                let path = resolve
                    .path_to_top(&pkg)
                    .into_iter()
                    .map(|(p, d)| (p, d.and_then(|d| d.iter().next())));
                anyhow::bail!(
                    "multiple packages link to native library `{}`, \
                     but a native library can be linked only once\n\
                     \n\
                     {}\nlinks to native library `{}`\n\
                     \n\
                     {}\nalso links to native library `{}`",
                    lib,
                    describe_path(prev_path),
                    lib,
                    describe_path(path),
                    lib
                )
            }
            links.insert(lib.to_string(), unit.pkg.package_id());
        }
        Ok(())
    }