#
cargo/core/
dependency.rs

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