Skip to main content

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