#
cargo/core/
dependency.rs

    1use cargo_platform::Platform;
    2use semver::VersionReq;
    3use serde::ser;
    4use serde::Serialize;
    5use std::borrow::Cow;
    6use std::fmt;
    7use std::path::PathBuf;
    8use std::sync::Arc;
    9use tracing::trace;
    10
    11use crate::core::compiler::{CompileKind, CompileTarget};
    12use crate::core::{CliUnstable, Feature, Features, PackageId, SourceId, Summary};
    13use crate::util::errors::CargoResult;
    14use crate::util::interning::InternedString;
    15use crate::util::OptVersionReq;
    16
    17/// Information about a dependency requested by a Cargo manifest.
    18/// Cheap to copy.
    19#[derive(PartialEq, Eq, Hash, Clone, Debug)]
    20pub struct Dependency {
    21    inner: Arc<Inner>,
    22}
    23
    24/// The data underlying a `Dependency`.
    25#[derive(PartialEq, Eq, Hash, Clone, Debug)]
    26struct Inner {
    27    name: InternedString,
    28    source_id: SourceId,
    29    /// Source ID for the registry as specified in the manifest.
    30    ///
    31    /// This will be None if it is not specified (crates.io dependency).
    32    /// This is different from `source_id` for example when both a `path` and
    33    /// `registry` is specified. Or in the case of a crates.io dependency,
    34    /// `source_id` will be crates.io and this will be None.
    35    registry_id: Option<SourceId>,
    36    req: OptVersionReq,
    37    specified_req: bool,
    38    kind: DepKind,
    39    only_match_name: bool,
    40    explicit_name_in_toml: Option<InternedString>,
    41
    42    optional: bool,
    43    public: bool,
    44    default_features: bool,
    45    features: Vec<InternedString>,
    46    // The presence of this information turns a dependency into an artifact dependency.
    47    artifact: Option<Artifact>,
    48
    49    // This dependency should be used only for this platform.
    50    // `None` means *all platforms*.
    51    platform: Option<Platform>,
    52}
    53
    54#[derive(Serialize)]
    55pub struct SerializedDependency {
    56    name: InternedString,
    57    source: SourceId,
    58    req: String,
    59    kind: DepKind,
    60    rename: Option<InternedString>,
    61
    62    optional: bool,
    63    uses_default_features: bool,
    64    features: Vec<InternedString>,
    65    #[serde(skip_serializing_if = "Option::is_none")]
    66    artifact: Option<Artifact>,
    67    target: Option<Platform>,
    68    /// The registry URL this dependency is from.
    69    /// If None, then it comes from the default registry (crates.io).
    70    registry: Option<String>,
    71
    72    /// The file system path for a local path dependency.
    73    #[serde(skip_serializing_if = "Option::is_none")]
    74    path: Option<PathBuf>,
    75
    76    /// `public` flag is unset if `-Zpublic-dependency` is not enabled
    77    ///
    78    /// Once that feature is stabilized, `public` will not need to be `Option`
    79    #[serde(skip_serializing_if = "Option::is_none")]
    80    public: Option<bool>,
    81}
    82
    83#[derive(PartialEq, Eq, Hash, Ord, PartialOrd, Clone, Debug, Copy)]
    84pub enum DepKind {
    85    Normal,
    86    Development,
    87    Build,
    88}
    89
    90impl DepKind {
    91    pub fn kind_table(&self) -> &'static str {
    92        match self {
    93            DepKind::Normal => "dependencies",
    94            DepKind::Development => "dev-dependencies",
    95            DepKind::Build => "build-dependencies",
    96        }
    97    }
    98}
    99
    100impl ser::Serialize for DepKind {
    101    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
    102    where
    103        S: ser::Serializer,
    104    {
    105        match *self {
    106            DepKind::Normal => None,
    107            DepKind::Development => Some("dev"),
    108            DepKind::Build => Some("build"),
    109        }
    110        .serialize(s)
    111    }
    112}
    113
    114impl Dependency {
    115    /// Attempt to create a `Dependency` from an entry in the manifest.
    116    pub fn parse(
    117        name: impl Into<InternedString>,
    118        version: Option<&str>,
    119        source_id: SourceId,
    120    ) -> CargoResult<Dependency> {
    121        let name = name.into();
    122        let (specified_req, version_req) = match version {
    123            Some(v) => match VersionReq::parse(v) {
    124                Ok(req) => (true, OptVersionReq::Req(req)),
    125                Err(err) => {
    126                    return Err(anyhow::Error::new(err).context(format!(
    127                        "failed to parse the version requirement `{}` for dependency `{}`",
    128                        v, name,
    129                    )))
    130                }
    131            },
    132            None => (false, OptVersionReq::Any),
    133        };
    134
    135        let mut ret = Dependency::new_override(name, source_id);
    136        {
    137            let ptr = Arc::make_mut(&mut ret.inner);
    138            ptr.only_match_name = false;
    139            ptr.req = version_req;
    140            ptr.specified_req = specified_req;
    141        }
    142        Ok(ret)
    143    }
    144
    145    pub fn new_override(name: InternedString, source_id: SourceId) -> Dependency {
    146        assert!(!name.is_empty());
    147        Dependency {
    148            inner: Arc::new(Inner {
    149                name,
    150                source_id,
    151                registry_id: None,
    152                req: OptVersionReq::Any,
    153                kind: DepKind::Normal,
    154                only_match_name: true,
    155                optional: false,
    156                public: false,
    157                features: Vec::new(),
    158                default_features: true,
    159                specified_req: false,
    160                platform: None,
    161                explicit_name_in_toml: None,
    162                artifact: None,
    163            }),
    164        }
    165    }
    166
    167    pub fn serialized(
    168        &self,
    169        unstable_flags: &CliUnstable,
    170        features: &Features,
    171    ) -> SerializedDependency {
    172        SerializedDependency {
    173            name: self.package_name(),
    174            source: self.source_id(),
    175            req: self.version_req().to_string(),
    176            kind: self.kind(),
    177            optional: self.is_optional(),
    178            uses_default_features: self.uses_default_features(),
    179            features: self.features().to_vec(),
    180            target: self.inner.platform.clone(),
    181            rename: self.explicit_name_in_toml(),
    182            registry: self.registry_id().as_ref().map(|sid| sid.url().to_string()),
    183            path: self.source_id().local_path(),
    184            artifact: self.inner.artifact.clone(),
    185            public: if unstable_flags.public_dependency
    186                || features.is_enabled(Feature::public_dependency())
    187            {
    188                Some(self.inner.public)
    189            } else {
    190                None
    191            },
    192        }
    193    }
    194
    195    pub fn version_req(&self) -> &OptVersionReq {
    196        &self.inner.req
    197    }
    198
    199    /// This is the name of this `Dependency` as listed in `Cargo.toml`.
    200    ///
    201    /// Or in other words, this is what shows up in the `[dependencies]` section
    202    /// on the left hand side. This is *not* the name of the package that's
    203    /// being depended on as the dependency can be renamed. For that use
    204    /// `package_name` below.
    205    ///
    206    /// Both of the dependencies below return `foo` for `name_in_toml`:
    207    ///
    208    /// ```toml
    209    /// [dependencies]
    210    /// foo = "0.1"
    211    /// ```
    212    ///
    213    /// and ...
    214    ///
    215    /// ```toml
    216    /// [dependencies]
    217    /// foo = { version = "0.1", package = 'bar' }
    218    /// ```
    219    pub fn name_in_toml(&self) -> InternedString {
    220        self.explicit_name_in_toml().unwrap_or(self.inner.name)
    221    }
    222
    223    /// The name of the package that this `Dependency` depends on.
    224    ///
    225    /// Usually this is what's written on the left hand side of a dependencies
    226    /// section, but it can also be renamed via the `package` key.
    227    ///
    228    /// Both of the dependencies below return `foo` for `package_name`:
    229    ///
    230    /// ```toml
    231    /// [dependencies]
    232    /// foo = "0.1"
    233    /// ```
    234    ///
    235    /// and ...
    236    ///
    237    /// ```toml
    238    /// [dependencies]
    239    /// bar = { version = "0.1", package = 'foo' }
    240    /// ```
    241    pub fn package_name(&self) -> InternedString {
    242        self.inner.name
    243    }
    244
    245    pub fn source_id(&self) -> SourceId {
    246        self.inner.source_id
    247    }
    248
    249    pub fn registry_id(&self) -> Option<SourceId> {
    250        self.inner.registry_id
    251    }
    252
    253    pub fn set_registry_id(&mut self, registry_id: SourceId) -> &mut Dependency {
    254        Arc::make_mut(&mut self.inner).registry_id = Some(registry_id);
    255        self
    256    }
    257
    258    pub fn kind(&self) -> DepKind {
    259        self.inner.kind
    260    }
    261
    262    pub fn is_public(&self) -> bool {
    263        self.inner.public
    264    }
    265
    266    /// Sets whether the dependency is public.
    267    pub fn set_public(&mut self, public: bool) -> &mut Dependency {
    268        if public {
    269            // Setting 'public' only makes sense for normal dependencies
    270            assert_eq!(self.kind(), DepKind::Normal);
    271        }
    272        Arc::make_mut(&mut self.inner).public = public;
    273        self
    274    }
    275
    276    pub fn specified_req(&self) -> bool {
    277        self.inner.specified_req
    278    }
    279
    280    /// If none, this dependencies must be built for all platforms.
    281    /// If some, it must only be built for the specified platform.
    282    pub fn platform(&self) -> Option<&Platform> {
    283        self.inner.platform.as_ref()
    284    }
    285
    286    /// The renamed name of this dependency, if any.
    287    ///
    288    /// If the `package` key is used in `Cargo.toml` then this returns the same
    289    /// value as `name_in_toml`.
    290    pub fn explicit_name_in_toml(&self) -> Option<InternedString> {
    291        self.inner.explicit_name_in_toml
    292    }
    293
    294    pub fn set_kind(&mut self, kind: DepKind) -> &mut Dependency {
    295        if self.is_public() {
    296            // Setting 'public' only makes sense for normal dependencies
    297            assert_eq!(kind, DepKind::Normal);
    298        }
    299        Arc::make_mut(&mut self.inner).kind = kind;
    300        self
    301    }
    302
    303    /// Sets the list of features requested for the package.
    304    pub fn set_features(
    305        &mut self,
    306        features: impl IntoIterator<Item = impl Into<InternedString>>,
    307    ) -> &mut Dependency {
    308        Arc::make_mut(&mut self.inner).features = features.into_iter().map(|s| s.into()).collect();
    309        self
    310    }
    311
    312    /// Sets whether the dependency requests default features of the package.
    313    pub fn set_default_features(&mut self, default_features: bool) -> &mut Dependency {
    314        Arc::make_mut(&mut self.inner).default_features = default_features;
    315        self
    316    }
    317
    318    /// Sets whether the dependency is optional.
    319    pub fn set_optional(&mut self, optional: bool) -> &mut Dependency {
    320        Arc::make_mut(&mut self.inner).optional = optional;
    321        self
    322    }
    323
    324    /// Sets the source ID for this dependency.
    325    pub fn set_source_id(&mut self, id: SourceId) -> &mut Dependency {
    326        Arc::make_mut(&mut self.inner).source_id = id;
    327        self
    328    }
    329
    330    /// Sets the version requirement for this dependency.
    331    pub fn set_version_req(&mut self, req: OptVersionReq) -> &mut Dependency {
    332        Arc::make_mut(&mut self.inner).req = req;
    333        self
    334    }
    335
    336    pub fn set_platform(&mut self, platform: Option<Platform>) -> &mut Dependency {
    337        Arc::make_mut(&mut self.inner).platform = platform;
    338        self
    339    }
    340
    341    pub fn set_explicit_name_in_toml(
    342        &mut self,
    343        name: impl Into<InternedString>,
    344    ) -> &mut Dependency {
    345        Arc::make_mut(&mut self.inner).explicit_name_in_toml = Some(name.into());
    346        self
    347    }
    348
    349    /// Locks this dependency to depending on the specified package ID.
    350    pub fn lock_to(&mut self, id: PackageId) -> &mut Dependency {
    351        assert_eq!(self.inner.source_id, id.source_id());
    352        trace!(
    353            "locking dep from `{}` with `{}` at {} to {}",
    354            self.package_name(),
    355            self.version_req(),
    356            self.source_id(),
    357            id
    358        );
    359        let me = Arc::make_mut(&mut self.inner);
    360        me.req.lock_to(id.version());
    361
    362        // Only update the `precise` of this source to preserve other
    363        // information about dependency's source which may not otherwise be
    364        // tested during equality/hashing.
    365        me.source_id = me.source_id.with_precise_from(id.source_id());
    366        self
    367    }
    368
    369    /// Locks this dependency to a specified version.
    370    ///
    371    /// Mainly used in dependency patching like `[patch]` or `[replace]`, which
    372    /// doesn't need to lock the entire dependency to a specific [`PackageId`].
    373    pub fn lock_version(&mut self, version: &semver::Version) -> &mut Dependency {
    374        let me = Arc::make_mut(&mut self.inner);
    375        me.req.lock_to(version);
    376        self
    377    }
    378
    379    /// Returns `true` if this is a "locked" dependency. Basically a locked
    380    /// dependency has an exact version req, but not vice versa.
    381    pub fn is_locked(&self) -> bool {
    382        self.inner.req.is_locked()
    383    }
    384
    385    /// Returns `false` if the dependency is only used to build the local package.
    386    pub fn is_transitive(&self) -> bool {
    387        match self.inner.kind {
    388            DepKind::Normal | DepKind::Build => true,
    389            DepKind::Development => false,
    390        }
    391    }
    392
    393    pub fn is_build(&self) -> bool {
    394        matches!(self.inner.kind, DepKind::Build)
    395    }
    396
    397    pub fn is_optional(&self) -> bool {
    398        self.inner.optional
    399    }
    400
    401    /// Returns `true` if the default features of the dependency are requested.
    402    pub fn uses_default_features(&self) -> bool {
    403        self.inner.default_features
    404    }
    405    /// Returns the list of features that are requested by the dependency.
    406    pub fn features(&self) -> &[InternedString] {
    407        &self.inner.features
    408    }
    409
    410    /// Returns `true` if the package (`sum`) can fulfill this dependency request.
    411    pub fn matches(&self, sum: &Summary) -> bool {
    412        self.matches_id(sum.package_id())
    413    }
    414
    415    pub fn matches_prerelease(&self, sum: &Summary) -> bool {
    416        let id = sum.package_id();
    417        self.inner.name == id.name()
    418            && (self.inner.only_match_name
    419                || (self.inner.req.matches_prerelease(id.version())
    420                    && self.inner.source_id == id.source_id()))
    421    }
    422
    423    /// Returns `true` if the package (`id`) can fulfill this dependency request.
    424    pub fn matches_ignoring_source(&self, id: PackageId) -> bool {
    425        self.package_name() == id.name() && self.version_req().matches(id.version())
    426    }
    427
    428    /// Returns `true` if the package (`id`) can fulfill this dependency request.
    429    pub fn matches_id(&self, id: PackageId) -> bool {
    430        self.inner.name == id.name()
    431            && (self.inner.only_match_name
    432                || (self.inner.req.matches(id.version()) && self.inner.source_id == id.source_id()))
    433    }
    434
    435    pub fn map_source(mut self, to_replace: SourceId, replace_with: SourceId) -> Dependency {
    436        if self.source_id() == to_replace {
    437            self.set_source_id(replace_with);
    438        }
    439        self
    440    }
    441
    442    pub(crate) fn set_artifact(&mut self, artifact: Artifact) {
    443        Arc::make_mut(&mut self.inner).artifact = Some(artifact);
    444    }
    445
    446    pub(crate) fn artifact(&self) -> Option<&Artifact> {
    447        self.inner.artifact.as_ref()
    448    }
    449
    450    /// Dependencies are potential rust libs if they are not artifacts or they are an
    451    /// artifact which allows to be seen as library.
    452    /// Previously, every dependency was potentially seen as library.
    453    pub(crate) fn maybe_lib(&self) -> bool {
    454        self.artifact().map(|a| a.is_lib).unwrap_or(true)
    455    }
    456}
    457
    458/// The presence of an artifact turns an ordinary dependency into an Artifact dependency.
    459/// As such, it will build one or more different artifacts of possibly various kinds
    460/// for making them available at build time for rustc invocations or runtime
    461/// for build scripts.
    462///
    463/// This information represents a requirement in the package this dependency refers to.
    464#[derive(PartialEq, Eq, Hash, Clone, Debug)]
    465pub struct Artifact {
    466    inner: Arc<Vec<ArtifactKind>>,
    467    is_lib: bool,
    468    target: Option<ArtifactTarget>,
    469}
    470
    471#[derive(Serialize)]
    472pub struct SerializedArtifact<'a> {
    473    kinds: &'a [ArtifactKind],
    474    lib: bool,
    475    target: Option<&'a str>,
    476}
    477
    478impl ser::Serialize for Artifact {
    479    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
    480    where
    481        S: ser::Serializer,
    482    {
    483        SerializedArtifact {
    484            kinds: self.kinds(),
    485            lib: self.is_lib,
    486            target: self.target.as_ref().map(ArtifactTarget::as_str),
    487        }
    488        .serialize(s)
    489    }
    490}
    491
    492impl Artifact {
    493    pub(crate) fn parse(
    494        artifacts: &[impl AsRef<str>],
    495        is_lib: bool,
    496        target: Option<&str>,
    497    ) -> CargoResult<Self> {
    498        let kinds = ArtifactKind::validate(
    499            artifacts
    500                .iter()
    501                .map(|s| ArtifactKind::parse(s.as_ref()))
    502                .collect::<Result<Vec<_>, _>>()?,
    503        )?;
    504        Ok(Artifact {
    505            inner: Arc::new(kinds),
    506            is_lib,
    507            target: target.map(ArtifactTarget::parse).transpose()?,
    508        })
    509    }
    510
    511    pub(crate) fn kinds(&self) -> &[ArtifactKind] {
    512        &self.inner
    513    }
    514
    515    pub(crate) fn is_lib(&self) -> bool {
    516        self.is_lib
    517    }
    518
    519    pub(crate) fn target(&self) -> Option<ArtifactTarget> {
    520        self.target
    521    }
    522}
    523
    524#[derive(PartialEq, Eq, Hash, Copy, Clone, Ord, PartialOrd, Debug)]
    525pub enum ArtifactTarget {
    526    /// Only applicable to build-dependencies, causing them to be built
    527    /// for the given target (i.e. via `--target <triple>`) instead of for the host.
    528    /// Has no effect on non-build dependencies.
    529    BuildDependencyAssumeTarget,
    530    /// The name of the platform triple, like `x86_64-apple-darwin`, that this
    531    /// artifact will always be built for, no matter if it is a build,
    532    /// normal or dev dependency.
    533    Force(CompileTarget),
    534}
    535
    536impl ArtifactTarget {
    537    pub fn parse(target: &str) -> CargoResult<ArtifactTarget> {
    538        Ok(match target {
    539            "target" => ArtifactTarget::BuildDependencyAssumeTarget,
    540            name => ArtifactTarget::Force(CompileTarget::new(name)?),
    541        })
    542    }
    543
    544    pub fn as_str(&self) -> &str {
    545        match self {
    546            ArtifactTarget::BuildDependencyAssumeTarget => "target",
    547            ArtifactTarget::Force(target) => target.rustc_target().as_str(),
    548        }
    549    }
    550
    551    pub fn to_compile_kind(&self) -> Option<CompileKind> {
    552        self.to_compile_target().map(CompileKind::Target)
    553    }
    554
    555    pub fn to_compile_target(&self) -> Option<CompileTarget> {
    556        match self {
    557            ArtifactTarget::BuildDependencyAssumeTarget => None,
    558            ArtifactTarget::Force(target) => Some(*target),
    559        }
    560    }
    561
    562    pub(crate) fn to_resolved_compile_kind(
    563        &self,
    564        root_unit_compile_kind: CompileKind,
    565    ) -> CompileKind {
    566        match self {
    567            ArtifactTarget::Force(target) => CompileKind::Target(*target),
    568            ArtifactTarget::BuildDependencyAssumeTarget => root_unit_compile_kind,
    569        }
    570    }
    571
    572    pub(crate) fn to_resolved_compile_target(
    573        &self,
    574        root_unit_compile_kind: CompileKind,
    575    ) -> Option<CompileTarget> {
    576        match self.to_resolved_compile_kind(root_unit_compile_kind) {
    577            CompileKind::Host => None,
    578            CompileKind::Target(target) => Some(target),
    579        }
    580    }
    581}
    582
    583#[derive(PartialEq, Eq, Hash, Copy, Clone, Ord, PartialOrd, Debug)]
    584pub enum ArtifactKind {
    585    /// We represent all binaries in this dependency
    586    AllBinaries,
    587    /// We represent a single binary
    588    SelectedBinary(InternedString),
    589    Cdylib,
    590    Staticlib,
    591}
    592
    593impl ser::Serialize for ArtifactKind {
    594    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
    595    where
    596        S: ser::Serializer,
    597    {
    598        self.as_str().serialize(s)
    599    }
    600}
    601
    602impl fmt::Display for ArtifactKind {
    603    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    604        f.write_str(&self.as_str())
    605    }
    606}
    607
    608impl ArtifactKind {
    609    /// Returns a string of crate type of the artifact being built.
    610    ///
    611    /// Note that the name of `SelectedBinary` would be dropped and displayed as `bin`.
    612    pub fn crate_type(&self) -> &'static str {
    613        match self {
    614            ArtifactKind::AllBinaries | ArtifactKind::SelectedBinary(_) => "bin",
    615            ArtifactKind::Cdylib => "cdylib",
    616            ArtifactKind::Staticlib => "staticlib",
    617        }
    618    }
    619
    620    pub fn as_str(&self) -> Cow<'static, str> {
    621        match *self {
    622            ArtifactKind::SelectedBinary(name) => format!("bin:{}", name.as_str()).into(),
    623            _ => self.crate_type().into(),
    624        }
    625    }
    626
    627    pub fn parse(kind: &str) -> CargoResult<Self> {
    628        Ok(match kind {
    629            "bin" => ArtifactKind::AllBinaries,
    630            "cdylib" => ArtifactKind::Cdylib,
    631            "staticlib" => ArtifactKind::Staticlib,
    632            _ => {
    633                return kind
    634                    .strip_prefix("bin:")
    635                    .map(|bin_name| ArtifactKind::SelectedBinary(InternedString::new(bin_name)))
    636                    .ok_or_else(|| anyhow::anyhow!("'{}' is not a valid artifact specifier", kind))
    637            }
    638        })
    639    }
    640
    641    fn validate(kinds: Vec<ArtifactKind>) -> CargoResult<Vec<ArtifactKind>> {
    642        if kinds.iter().any(|k| matches!(k, ArtifactKind::AllBinaries))
    643            && kinds
    644                .iter()
    645                .any(|k| matches!(k, ArtifactKind::SelectedBinary(_)))
    646        {
    647            anyhow::bail!("Cannot specify both 'bin' and 'bin:<name>' binary artifacts, as 'bin' selects all available binaries.");
    648        }
    649        let mut kinds_without_dupes = kinds.clone();
    650        kinds_without_dupes.sort();
    651        kinds_without_dupes.dedup();
    652        let num_dupes = kinds.len() - kinds_without_dupes.len();
    653        if num_dupes != 0 {
    654            anyhow::bail!(
    655                "Found {} duplicate binary artifact{}",
    656                num_dupes,
    657                (num_dupes > 1).then(|| "s").unwrap_or("")
    658            );
    659        }
    660        Ok(kinds)
    661    }
    662}