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}