Skip to main content

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    /// The keys to this entry in a `Cargo.toml` file
297    pub fn toml_path(&self) -> Vec<String> {
298        let mut path = Vec::new();
299        if let Some(platform) = self.platform() {
300            path.push("target".to_owned());
301            path.push(platform.to_string());
302        }
303        path.push(self.kind().kind_table().to_owned());
304        path.push((&*self.name_in_toml()).to_owned());
305        path
306    }
307
308    pub fn set_kind(&mut self, kind: DepKind) -> &mut Dependency {
309        if self.is_public() {
310            // Setting 'public' only makes sense for normal dependencies
311            assert_eq!(kind, DepKind::Normal);
312        }
313        Arc::make_mut(&mut self.inner).kind = kind;
314        self
315    }
316
317    /// Sets the list of features requested for the package.
318    pub fn set_features(
319        &mut self,
320        features: impl IntoIterator<Item = impl Into<InternedString>>,
321    ) -> &mut Dependency {
322        Arc::make_mut(&mut self.inner).features = features.into_iter().map(|s| s.into()).collect();
323        self
324    }
325
326    /// Sets whether the dependency requests default features of the package.
327    pub fn set_default_features(&mut self, default_features: bool) -> &mut Dependency {
328        Arc::make_mut(&mut self.inner).default_features = default_features;
329        self
330    }
331
332    /// Sets whether the dependency is optional.
333    pub fn set_optional(&mut self, optional: bool) -> &mut Dependency {
334        Arc::make_mut(&mut self.inner).optional = optional;
335        self
336    }
337
338    /// Sets the source ID for this dependency.
339    pub fn set_source_id(&mut self, id: SourceId) -> &mut Dependency {
340        Arc::make_mut(&mut self.inner).source_id = id;
341        self
342    }
343
344    /// Sets the version requirement for this dependency.
345    pub fn set_version_req(&mut self, req: OptVersionReq) -> &mut Dependency {
346        Arc::make_mut(&mut self.inner).req = req;
347        self
348    }
349
350    pub fn set_platform(&mut self, platform: Option<Platform>) -> &mut Dependency {
351        Arc::make_mut(&mut self.inner).platform = platform;
352        self
353    }
354
355    pub fn set_explicit_name_in_toml(
356        &mut self,
357        name: impl Into<InternedString>,
358    ) -> &mut Dependency {
359        Arc::make_mut(&mut self.inner).explicit_name_in_toml = Some(name.into());
360        self
361    }
362
363    /// Locks this dependency to depending on the specified package ID.
364    pub fn lock_to(&mut self, id: PackageId) -> &mut Dependency {
365        assert_eq!(self.inner.source_id, id.source_id());
366        trace!(
367            "locking dep from `{}` with `{}` at {} to {}",
368            self.package_name(),
369            self.version_req(),
370            self.source_id(),
371            id
372        );
373        let me = Arc::make_mut(&mut self.inner);
374        me.req.lock_to(id.version());
375
376        // Only update the `precise` of this source to preserve other
377        // information about dependency's source which may not otherwise be
378        // tested during equality/hashing.
379        me.source_id = me.source_id.with_precise_from(id.source_id());
380        self
381    }
382
383    /// Locks this dependency to a specified version.
384    ///
385    /// Mainly used in dependency patching like `[patch]` or `[replace]`, which
386    /// doesn't need to lock the entire dependency to a specific [`PackageId`].
387    pub fn lock_version(&mut self, version: &semver::Version) -> &mut Dependency {
388        let me = Arc::make_mut(&mut self.inner);
389        me.req.lock_to(version);
390        self
391    }
392
393    /// Returns `true` if this is a "locked" dependency. Basically a locked
394    /// dependency has an exact version req, but not vice versa.
395    pub fn is_locked(&self) -> bool {
396        self.inner.req.is_locked()
397    }
398
399    /// Returns `false` if the dependency is only used to build the local package.
400    pub fn is_transitive(&self) -> bool {
401        match self.inner.kind {
402            DepKind::Normal | DepKind::Build => true,
403            DepKind::Development => false,
404        }
405    }
406
407    pub fn is_build(&self) -> bool {
408        matches!(self.inner.kind, DepKind::Build)
409    }
410
411    pub fn is_optional(&self) -> bool {
412        self.inner.optional
413    }
414
415    /// Returns `true` if the default features of the dependency are requested.
416    pub fn uses_default_features(&self) -> bool {
417        self.inner.default_features
418    }
419    /// Returns the list of features that are requested by the dependency.
420    pub fn features(&self) -> &[InternedString] {
421        &self.inner.features
422    }
423
424    /// Returns `true` if the package (`sum`) can fulfill this dependency request.
425    pub fn matches(&self, sum: &Summary) -> bool {
426        self.matches_id(sum.package_id())
427    }
428
429    pub fn matches_prerelease(&self, sum: &Summary) -> bool {
430        let id = sum.package_id();
431        self.inner.name == id.name()
432            && (self.inner.only_match_name
433                || (self.inner.req.matches_prerelease(id.version())
434                    && self.inner.source_id == id.source_id()))
435    }
436
437    /// Returns `true` if the package (`id`) can fulfill this dependency request.
438    pub fn matches_ignoring_source(&self, id: PackageId) -> bool {
439        self.package_name() == id.name() && self.version_req().matches(id.version())
440    }
441
442    /// Returns `true` if the package (`id`) can fulfill this dependency request.
443    pub fn matches_id(&self, id: PackageId) -> bool {
444        self.inner.name == id.name()
445            && (self.inner.only_match_name
446                || (self.inner.req.matches(id.version()) && self.inner.source_id == id.source_id()))
447    }
448
449    pub fn map_source(mut self, to_replace: SourceId, replace_with: SourceId) -> Dependency {
450        if self.source_id() == to_replace {
451            self.set_source_id(replace_with);
452        }
453        self
454    }
455
456    pub(crate) fn set_artifact(&mut self, artifact: Artifact) {
457        Arc::make_mut(&mut self.inner).artifact = Some(artifact);
458    }
459
460    pub fn artifact(&self) -> Option<&Artifact> {
461        self.inner.artifact.as_ref()
462    }
463
464    /// Dependencies are potential rust libs if they are not artifacts or they are an
465    /// artifact which allows to be seen as library.
466    /// Previously, every dependency was potentially seen as library.
467    pub(crate) fn maybe_lib(&self) -> bool {
468        self.artifact().map(|a| a.is_lib).unwrap_or(true)
469    }
470}
471
472/// The presence of an artifact turns an ordinary dependency into an Artifact dependency.
473/// As such, it will build one or more different artifacts of possibly various kinds
474/// for making them available at build time for rustc invocations or runtime
475/// for build scripts.
476///
477/// This information represents a requirement in the package this dependency refers to.
478#[derive(PartialEq, Eq, Hash, Clone, Debug)]
479pub struct Artifact {
480    inner: Arc<Vec<ArtifactKind>>,
481    is_lib: bool,
482    target: Option<ArtifactTarget>,
483}
484
485#[derive(Serialize)]
486pub struct SerializedArtifact<'a> {
487    kinds: &'a [ArtifactKind],
488    lib: bool,
489    target: Option<&'a str>,
490}
491
492impl ser::Serialize for Artifact {
493    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
494    where
495        S: ser::Serializer,
496    {
497        SerializedArtifact {
498            kinds: self.kinds(),
499            lib: self.is_lib,
500            target: self.target.as_ref().map(ArtifactTarget::as_str),
501        }
502        .serialize(s)
503    }
504}
505
506impl Artifact {
507    pub(crate) fn parse(
508        artifacts: &[impl AsRef<str>],
509        is_lib: bool,
510        target: Option<&str>,
511        unstable_json: bool,
512    ) -> CargoResult<Self> {
513        let kinds = ArtifactKind::validate(
514            artifacts
515                .iter()
516                .map(|s| ArtifactKind::parse(s.as_ref()))
517                .collect::<Result<Vec<_>, _>>()?,
518        )?;
519        Ok(Artifact {
520            inner: Arc::new(kinds),
521            is_lib,
522            target: target
523                .map(|name| ArtifactTarget::parse(name, unstable_json))
524                .transpose()?,
525        })
526    }
527
528    pub fn kinds(&self) -> &[ArtifactKind] {
529        &self.inner
530    }
531
532    pub fn is_lib(&self) -> bool {
533        self.is_lib
534    }
535
536    pub fn target(&self) -> Option<ArtifactTarget> {
537        self.target
538    }
539}
540
541#[derive(PartialEq, Eq, Hash, Copy, Clone, Ord, PartialOrd, Debug)]
542pub enum ArtifactTarget {
543    /// Only applicable to build-dependencies, causing them to be built
544    /// for the given target (i.e. via `--target <triple>`) instead of for the host.
545    /// Has no effect on non-build dependencies.
546    BuildDependencyAssumeTarget,
547    /// The name of the platform triple, like `x86_64-apple-darwin`, that this
548    /// artifact will always be built for, no matter if it is a build,
549    /// normal or dev dependency.
550    Force(CompileTarget),
551}
552
553impl ArtifactTarget {
554    pub fn parse(target: &str, unstable_json: bool) -> CargoResult<ArtifactTarget> {
555        Ok(match target {
556            "target" => ArtifactTarget::BuildDependencyAssumeTarget,
557            name => ArtifactTarget::Force(CompileTarget::new(name, unstable_json)?),
558        })
559    }
560
561    pub fn as_str(&self) -> &str {
562        match self {
563            ArtifactTarget::BuildDependencyAssumeTarget => "target",
564            ArtifactTarget::Force(target) => target.rustc_target().as_str(),
565        }
566    }
567
568    pub fn to_compile_kind(&self) -> Option<CompileKind> {
569        self.to_compile_target().map(CompileKind::Target)
570    }
571
572    pub fn to_compile_target(&self) -> Option<CompileTarget> {
573        match self {
574            ArtifactTarget::BuildDependencyAssumeTarget => None,
575            ArtifactTarget::Force(target) => Some(*target),
576        }
577    }
578
579    pub(crate) fn to_resolved_compile_kind(
580        &self,
581        root_unit_compile_kind: CompileKind,
582    ) -> CompileKind {
583        match self {
584            ArtifactTarget::Force(target) => CompileKind::Target(*target),
585            ArtifactTarget::BuildDependencyAssumeTarget => root_unit_compile_kind,
586        }
587    }
588
589    pub(crate) fn to_resolved_compile_target(
590        &self,
591        root_unit_compile_kind: CompileKind,
592    ) -> Option<CompileTarget> {
593        match self.to_resolved_compile_kind(root_unit_compile_kind) {
594            CompileKind::Host => None,
595            CompileKind::Target(target) => Some(target),
596        }
597    }
598}
599
600#[derive(PartialEq, Eq, Hash, Copy, Clone, Ord, PartialOrd, Debug)]
601pub enum ArtifactKind {
602    /// We represent all binaries in this dependency
603    AllBinaries,
604    /// We represent a single binary
605    SelectedBinary(InternedString),
606    Cdylib,
607    Staticlib,
608}
609
610impl ser::Serialize for ArtifactKind {
611    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
612    where
613        S: ser::Serializer,
614    {
615        self.as_str().serialize(s)
616    }
617}
618
619impl fmt::Display for ArtifactKind {
620    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
621        f.write_str(&self.as_str())
622    }
623}
624
625impl ArtifactKind {
626    /// Returns a string of crate type of the artifact being built.
627    ///
628    /// Note that the name of `SelectedBinary` would be dropped and displayed as `bin`.
629    pub fn crate_type(&self) -> &'static str {
630        match self {
631            ArtifactKind::AllBinaries | ArtifactKind::SelectedBinary(_) => "bin",
632            ArtifactKind::Cdylib => "cdylib",
633            ArtifactKind::Staticlib => "staticlib",
634        }
635    }
636
637    pub fn as_str(&self) -> Cow<'static, str> {
638        match *self {
639            ArtifactKind::SelectedBinary(name) => format!("bin:{}", name.as_str()).into(),
640            _ => self.crate_type().into(),
641        }
642    }
643
644    pub fn parse(kind: &str) -> CargoResult<Self> {
645        Ok(match kind {
646            "bin" => ArtifactKind::AllBinaries,
647            "cdylib" => ArtifactKind::Cdylib,
648            "staticlib" => ArtifactKind::Staticlib,
649            _ => {
650                return kind
651                    .strip_prefix("bin:")
652                    .map(|bin_name| ArtifactKind::SelectedBinary(bin_name.into()))
653                    .ok_or_else(|| {
654                        anyhow::anyhow!("'{}' is not a valid artifact specifier", kind)
655                    });
656            }
657        })
658    }
659
660    fn validate(kinds: Vec<ArtifactKind>) -> CargoResult<Vec<ArtifactKind>> {
661        if kinds.iter().any(|k| matches!(k, ArtifactKind::AllBinaries))
662            && kinds
663                .iter()
664                .any(|k| matches!(k, ArtifactKind::SelectedBinary(_)))
665        {
666            anyhow::bail!(
667                "Cannot specify both 'bin' and 'bin:<name>' binary artifacts, as 'bin' selects all available binaries."
668            );
669        }
670        let mut kinds_without_dupes = kinds.clone();
671        kinds_without_dupes.sort();
672        kinds_without_dupes.dedup();
673        let num_dupes = kinds.len() - kinds_without_dupes.len();
674        if num_dupes != 0 {
675            anyhow::bail!(
676                "Found {} duplicate binary artifact{}",
677                num_dupes,
678                (num_dupes > 1).then(|| "s").unwrap_or("")
679            );
680        }
681        Ok(kinds)
682    }
683}
684
685/// Patch is a dependency override that knows where it has been defined.
686/// See [`PatchLocation`] for possible locations.
687#[derive(Clone, Debug)]
688pub struct Patch {
689    pub dep: Dependency,
690    pub loc: PatchLocation,
691}
692
693/// Place where a [`Patch`] has been defined.
694#[derive(Clone, Debug)]
695pub enum PatchLocation {
696    /// Defined in a manifest.
697    Manifest(PathBuf),
698    /// Defined in cargo configuration.
699    Config(Definition),
700}
701
702impl Display for PatchLocation {
703    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
704        match self {
705            PatchLocation::Manifest(p) => Path::display(p).fmt(f),
706            PatchLocation::Config(def) => def.fmt(f),
707        }
708    }
709}