1 use std
::collections
::{BTreeMap, BTreeSet, HashMap, HashSet}
;
3 use std
::marker
::PhantomData
;
4 use std
::path
::{Path, PathBuf}
;
8 use anyhow
::{anyhow, bail, Context as _}
;
9 use cargo_platform
::Platform
;
10 use cargo_util
::paths
;
11 use log
::{debug, trace}
;
12 use semver
::{self, VersionReq}
;
15 use serde
::{Deserialize, Serialize}
;
18 use crate::core
::compiler
::{CompileKind, CompileTarget}
;
19 use crate::core
::dependency
::DepKind
;
20 use crate::core
::manifest
::{ManifestMetadata, TargetSourcePath, Warnings}
;
21 use crate::core
::resolver
::ResolveBehavior
;
22 use crate::core
::{Dependency, Manifest, PackageId, Summary, Target}
;
23 use crate::core
::{Edition, EitherManifest, Feature, Features, VirtualManifest, Workspace}
;
24 use crate::core
::{GitReference, PackageIdSpec, SourceId, WorkspaceConfig, WorkspaceRootConfig}
;
25 use crate::sources
::{CRATES_IO_INDEX, CRATES_IO_REGISTRY}
;
26 use crate::util
::errors
::{CargoResult, ManifestError}
;
27 use crate::util
::interning
::InternedString
;
29 self, config
::ConfigRelativePath
, validate_package_name
, Config
, IntoUrl
, VersionReqExt
,
33 use self::targets
::targets
;
35 /// Loads a `Cargo.toml` from a file on disk.
37 /// This could result in a real or virtual manifest being returned.
39 /// A list of nested paths is also returned, one for each path dependency
40 /// within the manifest. For virtual manifests, these paths can only
41 /// come from patched or replaced dependencies. These paths are not
47 ) -> Result
<(EitherManifest
, Vec
<PathBuf
>), ManifestError
> {
49 "read_manifest; path={}; source-id={}",
53 let contents
= paths
::read(path
).map_err(|err
| ManifestError
::new(err
, path
.into()))?
;
55 do_read_manifest(&contents
, path
, source_id
, config
)
56 .with_context(|| format
!("failed to parse manifest at `{}`", path
.display()))
57 .map_err(|err
| ManifestError
::new(err
, path
.into()))
65 ) -> CargoResult
<(EitherManifest
, Vec
<PathBuf
>)> {
66 let package_root
= manifest_file
.parent().unwrap();
69 let pretty_filename
= manifest_file
70 .strip_prefix(config
.cwd())
71 .unwrap_or(manifest_file
);
72 parse(contents
, pretty_filename
, config
)?
75 // Provide a helpful error message for a common user error.
76 if let Some(package
) = toml
.get("package").or_else(|| toml
.get("project")) {
77 if let Some(feats
) = package
.get("cargo-features") {
79 "cargo-features = {} was found in the wrong location, it \
80 should be set at the top of Cargo.toml before any tables",
81 toml
::to_string(feats
).unwrap()
86 let mut unused
= BTreeSet
::new();
87 let manifest
: TomlManifest
= serde_ignored
::deserialize(toml
, |path
| {
88 let mut key
= String
::new();
89 stringify(&mut key
, &path
);
92 let add_unused
= |warnings
: &mut Warnings
| {
94 warnings
.add_warning(format
!("unused manifest key: {}", key
));
95 if key
== "profiles.debug" {
96 warnings
.add_warning("use `[profile.dev]` to configure debug builds".to_string());
101 let manifest
= Rc
::new(manifest
);
102 return if manifest
.project
.is_some() || manifest
.package
.is_some() {
103 let (mut manifest
, paths
) =
104 TomlManifest
::to_real_manifest(&manifest
, source_id
, package_root
, config
)?
;
105 add_unused(manifest
.warnings_mut());
106 if manifest
.targets().iter().all(|t
| t
.is_custom_build()) {
108 "no targets specified in the manifest\n\
109 either src/lib.rs, src/main.rs, a [lib] section, or \
110 [[bin]] section must be present"
113 Ok((EitherManifest
::Real(manifest
), paths
))
116 TomlManifest
::to_virtual_manifest(&manifest
, source_id
, package_root
, config
)?
;
117 add_unused(m
.warnings_mut());
118 Ok((EitherManifest
::Virtual(m
), paths
))
121 fn stringify(dst
: &mut String
, path
: &serde_ignored
::Path
<'_
>) {
122 use serde_ignored
::Path
;
126 Path
::Seq { parent, index }
=> {
127 stringify(dst
, parent
);
131 dst
.push_str(&index
.to_string());
133 Path
::Map { parent, ref key }
=> {
134 stringify(dst
, parent
);
140 Path
::Some { parent }
141 | Path
::NewtypeVariant { parent }
142 | Path
::NewtypeStruct { parent }
=> stringify(dst
, parent
),
147 /// Attempts to parse a string into a [`toml::Value`]. This is not specific to any
148 /// particular kind of TOML file.
150 /// The purpose of this wrapper is to detect invalid TOML which was previously
151 /// accepted and display a warning to the user in that case. The `file` and `config`
152 /// parameters are only used by this fallback path.
153 pub fn parse(toml
: &str, file
: &Path
, config
: &Config
) -> CargoResult
<toml
::Value
> {
154 let first_error
= match toml
.parse() {
155 Ok(ret
) => return Ok(ret
),
159 let mut second_parser
= toml
::de
::Deserializer
::new(toml
);
160 second_parser
.set_require_newline_after_table(false);
161 if let Ok(ret
) = toml
::Value
::deserialize(&mut second_parser
) {
164 TOML file found which contains invalid syntax and will soon not parse
167 The TOML spec requires newlines after table definitions (e.g., `[a] b = 1` is
168 invalid), but this file has a table header which does not have a newline after
169 it. A newline needs to be added and this warning will soon become a hard error
173 config
.shell().warn(&msg
)?
;
177 let mut third_parser
= toml
::de
::Deserializer
::new(toml
);
178 third_parser
.set_allow_duplicate_after_longer_table(true);
179 if let Ok(ret
) = toml
::Value
::deserialize(&mut third_parser
) {
182 TOML file found which contains invalid syntax and will soon not parse
185 The TOML spec requires that each table header is defined at most once, but
186 historical versions of Cargo have erroneously accepted this file. The table
187 definitions will need to be merged together with one table header to proceed,
188 and this will become a hard error in the future.",
191 config
.shell().warn(&msg
)?
;
195 let first_error
= anyhow
::Error
::from(first_error
);
196 Err(first_error
.context("could not parse input as TOML"))
199 type TomlLibTarget
= TomlTarget
;
200 type TomlBinTarget
= TomlTarget
;
201 type TomlExampleTarget
= TomlTarget
;
202 type TomlTestTarget
= TomlTarget
;
203 type TomlBenchTarget
= TomlTarget
;
205 #[derive(Clone, Debug, Serialize)]
207 pub enum TomlDependency
<P
= String
> {
208 /// In the simple format, only a version is specified, eg.
209 /// `package = "<version>"`
211 /// The simple format is equivalent to a detailed dependency
212 /// specifying only a version, eg.
213 /// `package = { version = "<version>" }`
214 Detailed(DetailedTomlDependency
<P
>),
217 impl<'de
, P
: Deserialize
<'de
>> de
::Deserialize
<'de
> for TomlDependency
<P
> {
218 fn deserialize
<D
>(deserializer
: D
) -> Result
<Self, D
::Error
>
220 D
: de
::Deserializer
<'de
>,
222 struct TomlDependencyVisitor
<P
>(PhantomData
<P
>);
224 impl<'de
, P
: Deserialize
<'de
>> de
::Visitor
<'de
> for TomlDependencyVisitor
<P
> {
225 type Value
= TomlDependency
<P
>;
227 fn expecting(&self, formatter
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
229 "a version string like \"0.9.8\" or a \
230 detailed dependency like { version = \"0.9.8\" }",
234 fn visit_str
<E
>(self, s
: &str) -> Result
<Self::Value
, E
>
238 Ok(TomlDependency
::Simple(s
.to_owned()))
241 fn visit_map
<V
>(self, map
: V
) -> Result
<Self::Value
, V
::Error
>
243 V
: de
::MapAccess
<'de
>,
245 let mvd
= de
::value
::MapAccessDeserializer
::new(map
);
246 DetailedTomlDependency
::deserialize(mvd
).map(TomlDependency
::Detailed
)
250 deserializer
.deserialize_any(TomlDependencyVisitor(PhantomData
))
254 pub trait ResolveToPath
{
255 fn resolve(&self, config
: &Config
) -> PathBuf
;
258 impl ResolveToPath
for String
{
259 fn resolve(&self, _
: &Config
) -> PathBuf
{
264 impl ResolveToPath
for ConfigRelativePath
{
265 fn resolve(&self, c
: &Config
) -> PathBuf
{
270 #[derive(Deserialize, Serialize, Clone, Debug)]
271 #[serde(rename_all = "kebab-case")]
272 pub struct DetailedTomlDependency
<P
= String
> {
273 version
: Option
<String
>,
274 registry
: Option
<String
>,
275 /// The URL of the `registry` field.
276 /// This is an internal implementation detail. When Cargo creates a
277 /// package, it replaces `registry` with `registry-index` so that the
278 /// manifest contains the correct URL. All users won't have the same
279 /// registry names configured, so Cargo can't rely on just the name for
280 /// crates published by other users.
281 registry_index
: Option
<String
>,
282 // `path` is relative to the file it appears in. If that's a `Cargo.toml`, it'll be relative to
283 // that TOML file, and if it's a `.cargo/config` file, it'll be relative to that file.
286 branch
: Option
<String
>,
289 features
: Option
<Vec
<String
>>,
290 optional
: Option
<bool
>,
291 default_features
: Option
<bool
>,
292 #[serde(rename = "default_features")]
293 default_features2
: Option
<bool
>,
294 package
: Option
<String
>,
295 public
: Option
<bool
>,
298 // Explicit implementation so we avoid pulling in P: Default
299 impl<P
> Default
for DetailedTomlDependency
<P
> {
300 fn default() -> Self {
302 version
: Default
::default(),
303 registry
: Default
::default(),
304 registry_index
: Default
::default(),
305 path
: Default
::default(),
306 git
: Default
::default(),
307 branch
: Default
::default(),
308 tag
: Default
::default(),
309 rev
: Default
::default(),
310 features
: Default
::default(),
311 optional
: Default
::default(),
312 default_features
: Default
::default(),
313 default_features2
: Default
::default(),
314 package
: Default
::default(),
315 public
: Default
::default(),
320 /// This type is used to deserialize `Cargo.toml` files.
321 #[derive(Debug, Deserialize, Serialize)]
322 #[serde(rename_all = "kebab-case")]
323 pub struct TomlManifest
{
324 cargo_features
: Option
<Vec
<String
>>,
325 package
: Option
<Box
<TomlProject
>>,
326 project
: Option
<Box
<TomlProject
>>,
327 profile
: Option
<TomlProfiles
>,
328 lib
: Option
<TomlLibTarget
>,
329 bin
: Option
<Vec
<TomlBinTarget
>>,
330 example
: Option
<Vec
<TomlExampleTarget
>>,
331 test
: Option
<Vec
<TomlTestTarget
>>,
332 bench
: Option
<Vec
<TomlTestTarget
>>,
333 dependencies
: Option
<BTreeMap
<String
, TomlDependency
>>,
334 dev_dependencies
: Option
<BTreeMap
<String
, TomlDependency
>>,
335 #[serde(rename = "dev_dependencies")]
336 dev_dependencies2
: Option
<BTreeMap
<String
, TomlDependency
>>,
337 build_dependencies
: Option
<BTreeMap
<String
, TomlDependency
>>,
338 #[serde(rename = "build_dependencies")]
339 build_dependencies2
: Option
<BTreeMap
<String
, TomlDependency
>>,
340 features
: Option
<BTreeMap
<InternedString
, Vec
<InternedString
>>>,
341 target
: Option
<BTreeMap
<String
, TomlPlatform
>>,
342 replace
: Option
<BTreeMap
<String
, TomlDependency
>>,
343 patch
: Option
<BTreeMap
<String
, BTreeMap
<String
, TomlDependency
>>>,
344 workspace
: Option
<TomlWorkspace
>,
345 badges
: Option
<BTreeMap
<String
, BTreeMap
<String
, String
>>>,
348 #[derive(Deserialize, Serialize, Clone, Debug, Default)]
349 pub struct TomlProfiles(BTreeMap
<InternedString
, TomlProfile
>);
352 pub fn get_all(&self) -> &BTreeMap
<InternedString
, TomlProfile
> {
356 pub fn get(&self, name
: &str) -> Option
<&TomlProfile
> {
360 pub fn validate(&self, features
: &Features
, warnings
: &mut Vec
<String
>) -> CargoResult
<()> {
361 for (name
, profile
) in &self.0 {
362 profile
.validate(name
, features
, warnings
)?
;
368 #[derive(Clone, Debug, Eq, PartialEq)]
369 pub struct TomlOptLevel(pub String
);
371 impl<'de
> de
::Deserialize
<'de
> for TomlOptLevel
{
372 fn deserialize
<D
>(d
: D
) -> Result
<TomlOptLevel
, D
::Error
>
374 D
: de
::Deserializer
<'de
>,
378 impl<'de
> de
::Visitor
<'de
> for Visitor
{
379 type Value
= TomlOptLevel
;
381 fn expecting(&self, formatter
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
382 formatter
.write_str("an optimization level")
385 fn visit_i64
<E
>(self, value
: i64) -> Result
<TomlOptLevel
, E
>
389 Ok(TomlOptLevel(value
.to_string()))
392 fn visit_str
<E
>(self, value
: &str) -> Result
<TomlOptLevel
, E
>
396 if value
== "s" || value
== "z" {
397 Ok(TomlOptLevel(value
.to_string()))
399 Err(E
::custom(format
!(
400 "must be `0`, `1`, `2`, `3`, `s` or `z`, \
401 but found the string: \"{}\"",
408 d
.deserialize_any(Visitor
)
412 impl ser
::Serialize
for TomlOptLevel
{
413 fn serialize
<S
>(&self, serializer
: S
) -> Result
<S
::Ok
, S
::Error
>
417 match self.0.parse
::<u32>() {
418 Ok(n
) => n
.serialize(serializer
),
419 Err(_
) => self.0.serialize(serializer
),
424 #[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
425 #[serde(untagged, expecting = "expected a boolean or an integer")]
431 #[derive(Deserialize, Serialize, Clone, Debug, Default, Eq, PartialEq)]
432 #[serde(default, rename_all = "kebab-case")]
433 pub struct TomlProfile
{
434 pub opt_level
: Option
<TomlOptLevel
>,
435 pub lto
: Option
<StringOrBool
>,
436 pub codegen_units
: Option
<u32>,
437 pub debug
: Option
<U32OrBool
>,
438 pub split_debuginfo
: Option
<String
>,
439 pub debug_assertions
: Option
<bool
>,
440 pub rpath
: Option
<bool
>,
441 pub panic
: Option
<String
>,
442 pub overflow_checks
: Option
<bool
>,
443 pub incremental
: Option
<bool
>,
444 pub package
: Option
<BTreeMap
<ProfilePackageSpec
, TomlProfile
>>,
445 pub build_override
: Option
<Box
<TomlProfile
>>,
446 pub dir_name
: Option
<InternedString
>,
447 pub inherits
: Option
<InternedString
>,
448 pub strip
: Option
<StringOrBool
>,
451 #[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
452 pub enum ProfilePackageSpec
{
457 impl ser
::Serialize
for ProfilePackageSpec
{
458 fn serialize
<S
>(&self, s
: S
) -> Result
<S
::Ok
, S
::Error
>
463 ProfilePackageSpec
::Spec(ref spec
) => spec
.serialize(s
),
464 ProfilePackageSpec
::All
=> "*".serialize(s
),
469 impl<'de
> de
::Deserialize
<'de
> for ProfilePackageSpec
{
470 fn deserialize
<D
>(d
: D
) -> Result
<ProfilePackageSpec
, D
::Error
>
472 D
: de
::Deserializer
<'de
>,
474 let string
= String
::deserialize(d
)?
;
476 Ok(ProfilePackageSpec
::All
)
478 PackageIdSpec
::parse(&string
)
479 .map_err(de
::Error
::custom
)
480 .map(ProfilePackageSpec
::Spec
)
490 warnings
: &mut Vec
<String
>,
491 ) -> CargoResult
<()> {
493 warnings
.push("use `[profile.dev]` to configure debug builds".to_string());
496 if let Some(ref profile
) = self.build_override
{
497 features
.require(Feature
::profile_overrides())?
;
498 profile
.validate_override("build-override")?
;
500 if let Some(ref packages
) = self.package
{
501 features
.require(Feature
::profile_overrides())?
;
502 for profile
in packages
.values() {
503 profile
.validate_override("package")?
;
507 // Feature gate definition of named profiles
509 "dev" | "release" | "bench" | "test" | "doc" => {}
511 features
.require(Feature
::named_profiles())?
;
515 // Profile name validation
516 Self::validate_name(name
, "profile name")?
;
518 // Feature gate on uses of keys related to named profiles
519 if self.inherits
.is_some() {
520 features
.require(Feature
::named_profiles())?
;
523 if self.dir_name
.is_some() {
524 features
.require(Feature
::named_profiles())?
;
527 // `dir-name` validation
528 match &self.dir_name
{
531 Self::validate_name(dir_name
, "dir-name")?
;
535 // `inherits` validation
536 match &self.inherits
{
539 Self::validate_name(inherits
, "inherits")?
;
545 warnings
.push("profile `doc` is deprecated and has no effect".to_string());
547 "test" | "bench" => {
548 if self.panic
.is_some() {
549 warnings
.push(format
!("`panic` setting is ignored for `{}` profile", name
))
555 if let Some(panic
) = &self.panic
{
556 if panic
!= "unwind" && panic
!= "abort" {
558 "`panic` setting of `{}` is not a valid setting, \
559 must be `unwind` or `abort`",
565 if self.strip
.is_some() {
566 features
.require(Feature
::strip())?
;
571 /// Validate dir-names and profile names according to RFC 2678.
572 pub fn validate_name(name
: &str, what
: &str) -> CargoResult
<()> {
573 if let Some(ch
) = name
575 .find(|ch
| !ch
.is_alphanumeric() && *ch
!= '_'
&& *ch
!= '
-'
)
577 bail
!("Invalid character `{}` in {}: `{}`", ch
, what
, name
);
581 "package" | "build" => {
582 bail
!("Invalid {}: `{}`", what
, name
);
584 "debug" if what
== "profile" => {
585 if what
== "profile name" {
586 // Allowed, but will emit warnings
588 bail
!("Invalid {}: `{}`", what
, name
);
591 "doc" if what
== "dir-name" => {
592 bail
!("Invalid {}: `{}`", what
, name
);
600 fn validate_override(&self, which
: &str) -> CargoResult
<()> {
601 if self.package
.is_some() {
602 bail
!("package-specific profiles cannot be nested");
604 if self.build_override
.is_some() {
605 bail
!("build-override profiles cannot be nested");
607 if self.panic
.is_some() {
608 bail
!("`panic` may not be specified in a `{}` profile", which
)
610 if self.lto
.is_some() {
611 bail
!("`lto` may not be specified in a `{}` profile", which
)
613 if self.rpath
.is_some() {
614 bail
!("`rpath` may not be specified in a `{}` profile", which
)
619 /// Overwrite self's values with the given profile.
620 pub fn merge(&mut self, profile
: &TomlProfile
) {
621 if let Some(v
) = &profile
.opt_level
{
622 self.opt_level
= Some(v
.clone());
625 if let Some(v
) = &profile
.lto
{
626 self.lto
= Some(v
.clone());
629 if let Some(v
) = profile
.codegen_units
{
630 self.codegen_units
= Some(v
);
633 if let Some(v
) = &profile
.debug
{
634 self.debug
= Some(v
.clone());
637 if let Some(v
) = profile
.debug_assertions
{
638 self.debug_assertions
= Some(v
);
641 if let Some(v
) = &profile
.split_debuginfo
{
642 self.split_debuginfo
= Some(v
.clone());
645 if let Some(v
) = profile
.rpath
{
646 self.rpath
= Some(v
);
649 if let Some(v
) = &profile
.panic
{
650 self.panic
= Some(v
.clone());
653 if let Some(v
) = profile
.overflow_checks
{
654 self.overflow_checks
= Some(v
);
657 if let Some(v
) = profile
.incremental
{
658 self.incremental
= Some(v
);
661 if let Some(other_package
) = &profile
.package
{
662 match &mut self.package
{
663 Some(self_package
) => {
664 for (spec
, other_pkg_profile
) in other_package
{
665 match self_package
.get_mut(spec
) {
666 Some(p
) => p
.merge(other_pkg_profile
),
668 self_package
.insert(spec
.clone(), other_pkg_profile
.clone());
673 None
=> self.package
= Some(other_package
.clone()),
677 if let Some(other_bo
) = &profile
.build_override
{
678 match &mut self.build_override
{
679 Some(self_bo
) => self_bo
.merge(other_bo
),
680 None
=> self.build_override
= Some(other_bo
.clone()),
684 if let Some(v
) = &profile
.inherits
{
685 self.inherits
= Some(*v
);
688 if let Some(v
) = &profile
.dir_name
{
689 self.dir_name
= Some(*v
);
692 if let Some(v
) = &profile
.strip
{
693 self.strip
= Some(v
.clone());
698 #[derive(Clone, Debug, Serialize, Eq, PartialEq)]
699 pub struct StringOrVec(Vec
<String
>);
701 impl<'de
> de
::Deserialize
<'de
> for StringOrVec
{
702 fn deserialize
<D
>(deserializer
: D
) -> Result
<Self, D
::Error
>
704 D
: de
::Deserializer
<'de
>,
708 impl<'de
> de
::Visitor
<'de
> for Visitor
{
709 type Value
= StringOrVec
;
711 fn expecting(&self, formatter
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
712 formatter
.write_str("string or list of strings")
715 fn visit_str
<E
>(self, s
: &str) -> Result
<Self::Value
, E
>
719 Ok(StringOrVec(vec
![s
.to_string()]))
722 fn visit_seq
<V
>(self, v
: V
) -> Result
<Self::Value
, V
::Error
>
724 V
: de
::SeqAccess
<'de
>,
726 let seq
= de
::value
::SeqAccessDeserializer
::new(v
);
727 Vec
::deserialize(seq
).map(StringOrVec
)
731 deserializer
.deserialize_any(Visitor
)
735 #[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
736 #[serde(untagged, expecting = "expected a boolean or a string")]
737 pub enum StringOrBool
{
742 #[derive(PartialEq, Clone, Debug, Serialize)]
744 pub enum VecStringOrBool
{
745 VecString(Vec
<String
>),
749 impl<'de
> de
::Deserialize
<'de
> for VecStringOrBool
{
750 fn deserialize
<D
>(deserializer
: D
) -> Result
<Self, D
::Error
>
752 D
: de
::Deserializer
<'de
>,
756 impl<'de
> de
::Visitor
<'de
> for Visitor
{
757 type Value
= VecStringOrBool
;
759 fn expecting(&self, formatter
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
760 formatter
.write_str("a boolean or vector of strings")
763 fn visit_seq
<V
>(self, v
: V
) -> Result
<Self::Value
, V
::Error
>
765 V
: de
::SeqAccess
<'de
>,
767 let seq
= de
::value
::SeqAccessDeserializer
::new(v
);
768 Vec
::deserialize(seq
).map(VecStringOrBool
::VecString
)
771 fn visit_bool
<E
>(self, b
: bool
) -> Result
<Self::Value
, E
>
775 Ok(VecStringOrBool
::Bool(b
))
779 deserializer
.deserialize_any(Visitor
)
783 fn version_trim_whitespace
<'de
, D
>(deserializer
: D
) -> Result
<semver
::Version
, D
::Error
>
785 D
: de
::Deserializer
<'de
>,
789 impl<'de
> de
::Visitor
<'de
> for Visitor
{
790 type Value
= semver
::Version
;
792 fn expecting(&self, formatter
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
793 formatter
.write_str("SemVer version")
796 fn visit_str
<E
>(self, string
: &str) -> Result
<Self::Value
, E
>
800 string
.trim().parse().map_err(de
::Error
::custom
)
804 deserializer
.deserialize_str(Visitor
)
807 /// Represents the `package`/`project` sections of a `Cargo.toml`.
809 /// Note that the order of the fields matters, since this is the order they
810 /// are serialized to a TOML file. For example, you cannot have values after
811 /// the field `metadata`, since it is a table and values cannot appear after
813 #[derive(Deserialize, Serialize, Clone, Debug)]
814 #[serde(rename_all = "kebab-case")]
815 pub struct TomlProject
{
816 edition
: Option
<String
>,
817 rust_version
: Option
<String
>,
818 name
: InternedString
,
819 #[serde(deserialize_with = "version_trim_whitespace")]
820 version
: semver
::Version
,
821 authors
: Option
<Vec
<String
>>,
822 build
: Option
<StringOrBool
>,
823 metabuild
: Option
<StringOrVec
>,
824 #[serde(rename = "default-target")]
825 default_target
: Option
<String
>,
826 #[serde(rename = "forced-target")]
827 forced_target
: Option
<String
>,
828 links
: Option
<String
>,
829 exclude
: Option
<Vec
<String
>>,
830 include
: Option
<Vec
<String
>>,
831 publish
: Option
<VecStringOrBool
>,
832 workspace
: Option
<String
>,
833 im_a_teapot
: Option
<bool
>,
834 autobins
: Option
<bool
>,
835 autoexamples
: Option
<bool
>,
836 autotests
: Option
<bool
>,
837 autobenches
: Option
<bool
>,
838 default_run
: Option
<String
>,
841 description
: Option
<String
>,
842 homepage
: Option
<String
>,
843 documentation
: Option
<String
>,
844 readme
: Option
<StringOrBool
>,
845 keywords
: Option
<Vec
<String
>>,
846 categories
: Option
<Vec
<String
>>,
847 license
: Option
<String
>,
848 license_file
: Option
<String
>,
849 repository
: Option
<String
>,
850 resolver
: Option
<String
>,
852 // Note that this field must come last due to the way toml serialization
853 // works which requires tables to be emitted after all values.
854 metadata
: Option
<toml
::Value
>,
857 #[derive(Debug, Deserialize, Serialize)]
858 pub struct TomlWorkspace
{
859 members
: Option
<Vec
<String
>>,
860 #[serde(rename = "default-members")]
861 default_members
: Option
<Vec
<String
>>,
862 exclude
: Option
<Vec
<String
>>,
863 resolver
: Option
<String
>,
865 // Note that this field must come last due to the way toml serialization
866 // works which requires tables to be emitted after all values.
867 metadata
: Option
<toml
::Value
>,
871 pub fn to_package_id(&self, source_id
: SourceId
) -> CargoResult
<PackageId
> {
872 PackageId
::new(self.name
, self.version
.clone(), source_id
)
876 struct Context
<'a
, 'b
> {
877 deps
: &'a
mut Vec
<Dependency
>,
879 nested_paths
: &'a
mut Vec
<PathBuf
>,
881 warnings
: &'a
mut Vec
<String
>,
882 platform
: Option
<Platform
>,
884 features
: &'a Features
,
888 /// Prepares the manifest for publishing.
889 // - Path and git components of dependency specifications are removed.
890 // - License path is updated to point within the package.
891 pub fn prepare_for_publish(
895 ) -> CargoResult
<TomlManifest
> {
896 let config
= ws
.config();
897 let mut package
= self
900 .or_else(|| self.project
.as_ref())
903 package
.workspace
= None
;
904 package
.resolver
= ws
.resolve_behavior().to_manifest();
905 if let Some(license_file
) = &package
.license_file
{
906 let license_path
= Path
::new(&license_file
);
907 let abs_license_path
= paths
::normalize_path(&package_root
.join(license_path
));
908 if abs_license_path
.strip_prefix(package_root
).is_err() {
909 // This path points outside of the package root. `cargo package`
910 // will copy it into the root, so adjust the path to this location.
911 package
.license_file
= Some(
921 let all
= |_d
: &TomlDependency
| true;
922 return Ok(TomlManifest
{
923 package
: Some(package
),
925 profile
: self.profile
.clone(),
926 lib
: self.lib
.clone(),
927 bin
: self.bin
.clone(),
928 example
: self.example
.clone(),
929 test
: self.test
.clone(),
930 bench
: self.bench
.clone(),
931 dependencies
: map_deps(config
, self.dependencies
.as_ref(), all
)?
,
932 dev_dependencies
: map_deps(
934 self.dev_dependencies
936 .or_else(|| self.dev_dependencies2
.as_ref()),
937 TomlDependency
::is_version_specified
,
939 dev_dependencies2
: None
,
940 build_dependencies
: map_deps(
942 self.build_dependencies
944 .or_else(|| self.build_dependencies2
.as_ref()),
947 build_dependencies2
: None
,
948 features
: self.features
.clone(),
949 target
: match self.target
.as_ref().map(|target_map
| {
956 dependencies
: map_deps(config
, v
.dependencies
.as_ref(), all
)?
,
957 dev_dependencies
: map_deps(
961 .or_else(|| v
.dev_dependencies2
.as_ref()),
962 TomlDependency
::is_version_specified
,
964 dev_dependencies2
: None
,
965 build_dependencies
: map_deps(
969 .or_else(|| v
.build_dependencies2
.as_ref()),
972 build_dependencies2
: None
,
978 Some(Ok(v
)) => Some(v
),
979 Some(Err(e
)) => return Err(e
),
985 badges
: self.badges
.clone(),
986 cargo_features
: self.cargo_features
.clone(),
991 deps
: Option
<&BTreeMap
<String
, TomlDependency
>>,
992 filter
: impl Fn(&TomlDependency
) -> bool
,
993 ) -> CargoResult
<Option
<BTreeMap
<String
, TomlDependency
>>> {
994 let deps
= match deps
{
996 None
=> return Ok(None
),
1000 .filter(|(_k
, v
)| filter(v
))
1001 .map(|(k
, v
)| Ok((k
.clone(), map_dependency(config
, v
)?
)))
1002 .collect
::<CargoResult
<BTreeMap
<_
, _
>>>()?
;
1006 fn map_dependency(config
: &Config
, dep
: &TomlDependency
) -> CargoResult
<TomlDependency
> {
1008 TomlDependency
::Detailed(d
) => {
1009 let mut d
= d
.clone();
1010 // Path dependencies become crates.io deps.
1012 // Same with git dependencies.
1017 // registry specifications are elaborated to the index URL
1018 if let Some(registry
) = d
.registry
.take() {
1019 let src
= SourceId
::alt_registry(config
, ®istry
)?
;
1020 d
.registry_index
= Some(src
.url().to_string());
1022 Ok(TomlDependency
::Detailed(d
))
1024 TomlDependency
::Simple(s
) => Ok(TomlDependency
::Detailed(DetailedTomlDependency
{
1025 version
: Some(s
.clone()),
1026 ..Default
::default()
1032 pub fn to_real_manifest(
1033 me
: &Rc
<TomlManifest
>,
1034 source_id
: SourceId
,
1035 package_root
: &Path
,
1037 ) -> CargoResult
<(Manifest
, Vec
<PathBuf
>)> {
1038 let mut nested_paths
= vec
![];
1039 let mut warnings
= vec
![];
1040 let mut errors
= vec
![];
1042 // Parse features first so they will be available when parsing other parts of the TOML.
1043 let empty
= Vec
::new();
1044 let cargo_features
= me
.cargo_features
.as_ref().unwrap_or(&empty
);
1045 let features
= Features
::new(cargo_features
, config
, &mut warnings
)?
;
1047 let project
= me
.project
.as_ref().or_else(|| me
.package
.as_ref());
1048 let project
= project
.ok_or_else(|| anyhow
!("no `package` section found"))?
;
1050 let package_name
= project
.name
.trim();
1051 if package_name
.is_empty() {
1052 bail
!("package name cannot be an empty string")
1055 validate_package_name(package_name
, "package name", "")?
;
1057 let pkgid
= project
.to_package_id(source_id
)?
;
1059 let edition
= if let Some(ref edition
) = project
.edition
{
1061 .require(Feature
::edition())
1062 .with_context(|| "editions are unstable")?
;
1065 .with_context(|| "failed to parse the `edition` key")?
1067 Edition
::Edition2015
1069 if edition
== Edition
::Edition2021
{
1070 features
.require(Feature
::edition2021())?
;
1071 } else if !edition
.is_stable() {
1072 // Guard in case someone forgets to add .require()
1073 return Err(util
::errors
::internal(format
!(
1074 "edition {} should be gated",
1079 let rust_version
= if let Some(rust_version
) = &project
.rust_version
{
1080 if features
.require(Feature
::rust_version()).is_err() {
1082 "`rust-version` is not supported on this version of Cargo and will be ignored"
1084 if config
.nightly_features_allowed
{
1087 consider adding `cargo-features = [\"rust-version\"]` to the manifest",
1092 this Cargo does not support nightly features, but if you\n\
1093 switch to nightly channel you can add\n\
1094 `cargo-features = [\"rust-version\"]` to enable this feature",
1100 let req
= match semver
::VersionReq
::parse(rust_version
) {
1101 // Exclude semver operators like `^` and pre-release identifiers
1102 Ok(req
) if rust_version
.chars().all(|c
| c
.is_ascii_digit() || c
== '
.'
) => req
,
1103 _
=> bail
!("`rust-version` must be a value like \"1.32\""),
1105 if let Some(first_version
) = edition
.first_version() {
1107 semver
::Version
::new(first_version
.major
, first_version
.minor
- 1, 9999);
1108 if req
.matches(&unsupported
) {
1110 "rust-version {} is older than first version ({}) required by \
1111 the specified edition ({})",
1118 Some(rust_version
.clone())
1124 if project
.metabuild
.is_some() {
1125 features
.require(Feature
::metabuild())?
;
1128 if project
.resolver
.is_some()
1132 .map_or(false, |ws
| ws
.resolver
.is_some())
1134 features
.require(Feature
::resolver())?
;
1136 let resolve_behavior
= match (
1137 project
.resolver
.as_ref(),
1138 me
.workspace
.as_ref().and_then(|ws
| ws
.resolver
.as_ref()),
1140 (None
, None
) => None
,
1141 (Some(s
), None
) | (None
, Some(s
)) => Some(ResolveBehavior
::from_manifest(s
)?
),
1142 (Some(_
), Some(_
)) => {
1143 bail
!("cannot specify `resolver` field in both `[workspace]` and `[package]`")
1147 // If we have no lib at all, use the inferred lib, if available.
1148 // If we have a lib with a path, we're done.
1149 // If we have a lib with no path, use the inferred lib or else the package name.
1150 let targets
= targets(
1162 if targets
.is_empty() {
1163 debug
!("manifest has no build targets");
1166 if let Err(e
) = unique_build_targets(&targets
, package_root
) {
1167 warnings
.push(format
!(
1168 "file found to be present in multiple \
1174 if let Some(links
) = &project
.links
{
1175 if !targets
.iter().any(|t
| t
.is_custom_build()) {
1177 "package `{}` specifies that it links to `{}` but does not \
1178 have a custom build script",
1185 let mut deps
= Vec
::new();
1190 let mut cx
= Context
{
1193 nested_paths
: &mut nested_paths
,
1195 warnings
: &mut warnings
,
1196 features
: &features
,
1201 fn process_dependencies(
1202 cx
: &mut Context
<'_
, '_
>,
1203 new_deps
: Option
<&BTreeMap
<String
, TomlDependency
>>,
1204 kind
: Option
<DepKind
>,
1205 ) -> CargoResult
<()> {
1206 let dependencies
= match new_deps
{
1207 Some(dependencies
) => dependencies
,
1208 None
=> return Ok(()),
1210 for (n
, v
) in dependencies
.iter() {
1211 let dep
= v
.to_dependency(n
, cx
, kind
)?
;
1212 validate_package_name(dep
.name_in_toml().as_str(), "dependency name", "")?
;
1219 // Collect the dependencies.
1220 process_dependencies(&mut cx
, me
.dependencies
.as_ref(), None
)?
;
1224 .or_else(|| me
.dev_dependencies2
.as_ref());
1225 process_dependencies(&mut cx
, dev_deps
, Some(DepKind
::Development
))?
;
1229 .or_else(|| me
.build_dependencies2
.as_ref());
1230 process_dependencies(&mut cx
, build_deps
, Some(DepKind
::Build
))?
;
1232 for (name
, platform
) in me
.target
.iter().flatten() {
1234 let platform
: Platform
= name
.parse()?
;
1235 platform
.check_cfg_attributes(&mut cx
.warnings
);
1238 process_dependencies(&mut cx
, platform
.dependencies
.as_ref(), None
)?
;
1239 let build_deps
= platform
1242 .or_else(|| platform
.build_dependencies2
.as_ref());
1243 process_dependencies(&mut cx
, build_deps
, Some(DepKind
::Build
))?
;
1244 let dev_deps
= platform
1247 .or_else(|| platform
.dev_dependencies2
.as_ref());
1248 process_dependencies(&mut cx
, dev_deps
, Some(DepKind
::Development
))?
;
1251 replace
= me
.replace(&mut cx
)?
;
1252 patch
= me
.patch(&mut cx
)?
;
1256 let mut names_sources
= BTreeMap
::new();
1258 let name
= dep
.name_in_toml();
1259 let prev
= names_sources
.insert(name
.to_string(), dep
.source_id());
1260 if prev
.is_some() && prev
!= Some(dep
.source_id()) {
1262 "Dependency '{}' has different source paths depending on the build \
1263 target. Each dependency must have a single canonical source path \
1264 irrespective of build target.",
1271 let exclude
= project
.exclude
.clone().unwrap_or_default();
1272 let include
= project
.include
.clone().unwrap_or_default();
1273 let empty_features
= BTreeMap
::new();
1275 let summary
= Summary
::new(
1279 me
.features
.as_ref().unwrap_or(&empty_features
),
1280 project
.links
.as_deref(),
1282 let unstable
= config
.cli_unstable();
1283 summary
.unstable_gate(unstable
.namespaced_features
, unstable
.weak_dep_features
)?
;
1285 let metadata
= ManifestMetadata
{
1286 description
: project
.description
.clone(),
1287 homepage
: project
.homepage
.clone(),
1288 documentation
: project
.documentation
.clone(),
1289 readme
: readme_for_project(package_root
, project
),
1290 authors
: project
.authors
.clone().unwrap_or_default(),
1291 license
: project
.license
.clone(),
1292 license_file
: project
.license_file
.clone(),
1293 repository
: project
.repository
.clone(),
1294 keywords
: project
.keywords
.clone().unwrap_or_default(),
1295 categories
: project
.categories
.clone().unwrap_or_default(),
1296 badges
: me
.badges
.clone().unwrap_or_default(),
1297 links
: project
.links
.clone(),
1300 let workspace_config
= match (me
.workspace
.as_ref(), project
.workspace
.as_ref()) {
1301 (Some(config
), None
) => WorkspaceConfig
::Root(WorkspaceRootConfig
::new(
1304 &config
.default_members
,
1308 (None
, root
) => WorkspaceConfig
::Member
{
1309 root
: root
.cloned(),
1311 (Some(..), Some(..)) => bail
!(
1312 "cannot configure both `package.workspace` and \
1313 `[workspace]`, only one can be specified"
1316 let profiles
= me
.profile
.clone();
1317 if let Some(profiles
) = &profiles
{
1318 profiles
.validate(&features
, &mut warnings
)?
;
1320 let publish
= match project
.publish
{
1321 Some(VecStringOrBool
::VecString(ref vecstring
)) => Some(vecstring
.clone()),
1322 Some(VecStringOrBool
::Bool(false)) => Some(vec
![]),
1323 None
| Some(VecStringOrBool
::Bool(true)) => None
,
1326 if summary
.features().contains_key("default-features") {
1328 "`default-features = [\"..\"]` was found in [features]. \
1329 Did you mean to use `default = [\"..\"]`?"
1334 if let Some(run
) = &project
.default_run
{
1337 .filter(|t
| t
.is_bin())
1338 .any(|t
| t
.name() == run
)
1341 util
::closest_msg(run
, targets
.iter().filter(|t
| t
.is_bin()), |t
| t
.name());
1342 bail
!("default-run target `{}` not found{}", run
, suggestion
);
1346 let default_kind
= project
1349 .map(|t
| CompileTarget
::new(&*t
))
1351 .map(CompileKind
::Target
);
1352 let forced_kind
= project
1355 .map(|t
| CompileTarget
::new(&*t
))
1357 .map(CompileKind
::Target
);
1359 let custom_metadata
= project
.metadata
.clone();
1360 let mut manifest
= Manifest
::new(
1367 project
.links
.clone(),
1378 project
.im_a_teapot
,
1379 project
.default_run
.clone(),
1381 project
.metabuild
.clone().map(|sov
| sov
.0),
1384 if project
.license_file
.is_some() && project
.license
.is_some() {
1385 manifest
.warnings_mut().add_warning(
1386 "only one of `license` or \
1387 `license-file` is necessary"
1391 for warning
in warnings
{
1392 manifest
.warnings_mut().add_warning(warning
);
1394 for error
in errors
{
1395 manifest
.warnings_mut().add_critical_warning(error
);
1398 manifest
.feature_gate()?
;
1400 Ok((manifest
, nested_paths
))
1403 fn to_virtual_manifest(
1404 me
: &Rc
<TomlManifest
>,
1405 source_id
: SourceId
,
1408 ) -> CargoResult
<(VirtualManifest
, Vec
<PathBuf
>)> {
1409 if me
.project
.is_some() {
1410 bail
!("this virtual manifest specifies a [project] section, which is not allowed");
1412 if me
.package
.is_some() {
1413 bail
!("this virtual manifest specifies a [package] section, which is not allowed");
1415 if me
.lib
.is_some() {
1416 bail
!("this virtual manifest specifies a [lib] section, which is not allowed");
1418 if me
.bin
.is_some() {
1419 bail
!("this virtual manifest specifies a [[bin]] section, which is not allowed");
1421 if me
.example
.is_some() {
1422 bail
!("this virtual manifest specifies a [[example]] section, which is not allowed");
1424 if me
.test
.is_some() {
1425 bail
!("this virtual manifest specifies a [[test]] section, which is not allowed");
1427 if me
.bench
.is_some() {
1428 bail
!("this virtual manifest specifies a [[bench]] section, which is not allowed");
1430 if me
.dependencies
.is_some() {
1431 bail
!("this virtual manifest specifies a [dependencies] section, which is not allowed");
1433 if me
.dev_dependencies
.is_some() || me
.dev_dependencies2
.is_some() {
1434 bail
!("this virtual manifest specifies a [dev-dependencies] section, which is not allowed");
1436 if me
.build_dependencies
.is_some() || me
.build_dependencies2
.is_some() {
1437 bail
!("this virtual manifest specifies a [build-dependencies] section, which is not allowed");
1439 if me
.features
.is_some() {
1440 bail
!("this virtual manifest specifies a [features] section, which is not allowed");
1442 if me
.target
.is_some() {
1443 bail
!("this virtual manifest specifies a [target] section, which is not allowed");
1445 if me
.badges
.is_some() {
1446 bail
!("this virtual manifest specifies a [badges] section, which is not allowed");
1449 let mut nested_paths
= Vec
::new();
1450 let mut warnings
= Vec
::new();
1451 let mut deps
= Vec
::new();
1452 let empty
= Vec
::new();
1453 let cargo_features
= me
.cargo_features
.as_ref().unwrap_or(&empty
);
1454 let features
= Features
::new(cargo_features
, config
, &mut warnings
)?
;
1456 let (replace
, patch
) = {
1457 let mut cx
= Context
{
1460 nested_paths
: &mut nested_paths
,
1462 warnings
: &mut warnings
,
1464 features
: &features
,
1467 (me
.replace(&mut cx
)?
, me
.patch(&mut cx
)?
)
1469 let profiles
= me
.profile
.clone();
1470 if let Some(profiles
) = &profiles
{
1471 profiles
.validate(&features
, &mut warnings
)?
;
1476 .map_or(false, |ws
| ws
.resolver
.is_some())
1478 features
.require(Feature
::resolver())?
;
1480 let resolve_behavior
= me
1483 .and_then(|ws
| ws
.resolver
.as_deref())
1484 .map(|r
| ResolveBehavior
::from_manifest(r
))
1486 let workspace_config
= match me
.workspace
{
1487 Some(ref config
) => WorkspaceConfig
::Root(WorkspaceRootConfig
::new(
1490 &config
.default_members
,
1495 bail
!("virtual manifests must be configured with [workspace]");
1499 VirtualManifest
::new(
1511 fn replace(&self, cx
: &mut Context
<'_
, '_
>) -> CargoResult
<Vec
<(PackageIdSpec
, Dependency
)>> {
1512 if self.patch
.is_some() && self.replace
.is_some() {
1513 bail
!("cannot specify both [replace] and [patch]");
1515 let mut replace
= Vec
::new();
1516 for (spec
, replacement
) in self.replace
.iter().flatten() {
1517 let mut spec
= PackageIdSpec
::parse(spec
).with_context(|| {
1519 "replacements must specify a valid semver \
1520 version to replace, but `{}` does not",
1524 if spec
.url().is_none() {
1525 spec
.set_url(CRATES_IO_INDEX
.parse().unwrap());
1528 if replacement
.is_version_specified() {
1530 "replacements cannot specify a version \
1531 requirement, but found one for `{}`",
1536 let mut dep
= replacement
.to_dependency(spec
.name().as_str(), cx
, None
)?
;
1538 let version
= spec
.version().ok_or_else(|| {
1540 "replacements must specify a version \
1541 to replace, but `{}` does not",
1545 dep
.set_version_req(VersionReq
::exact(version
));
1547 replace
.push((spec
, dep
));
1552 fn patch(&self, cx
: &mut Context
<'_
, '_
>) -> CargoResult
<HashMap
<Url
, Vec
<Dependency
>>> {
1553 let mut patch
= HashMap
::new();
1554 for (url
, deps
) in self.patch
.iter().flatten() {
1555 let url
= match &url
[..] {
1556 CRATES_IO_REGISTRY
=> CRATES_IO_INDEX
.parse().unwrap(),
1559 .get_registry_index(url
)
1560 .or_else(|_
| url
.into_url())
1562 format
!("[patch] entry `{}` should be a URL or registry name", url
)
1568 .map(|(name
, dep
)| dep
.to_dependency(name
, cx
, None
))
1569 .collect
::<CargoResult
<Vec
<_
>>>()?
,
1575 /// Returns the path to the build script if one exists for this crate.
1576 fn maybe_custom_build(
1578 build
: &Option
<StringOrBool
>,
1579 package_root
: &Path
,
1580 ) -> Option
<PathBuf
> {
1581 let build_rs
= package_root
.join("build.rs");
1583 // Explicitly no build script.
1584 Some(StringOrBool
::Bool(false)) => None
,
1585 Some(StringOrBool
::Bool(true)) => Some(build_rs
),
1586 Some(StringOrBool
::String(ref s
)) => Some(PathBuf
::from(s
)),
1588 // If there is a `build.rs` file next to the `Cargo.toml`, assume it is
1590 if build_rs
.is_file() {
1599 pub fn has_profiles(&self) -> bool
{
1600 self.profile
.is_some()
1603 pub fn features(&self) -> Option
<&BTreeMap
<InternedString
, Vec
<InternedString
>>> {
1604 self.features
.as_ref()
1608 /// Returns the name of the README file for a `TomlProject`.
1609 fn readme_for_project(package_root
: &Path
, project
: &TomlProject
) -> Option
<String
> {
1610 match &project
.readme
{
1611 None
=> default_readme_from_package_root(package_root
),
1612 Some(value
) => match value
{
1613 StringOrBool
::Bool(false) => None
,
1614 StringOrBool
::Bool(true) => Some("README.md".to_string()),
1615 StringOrBool
::String(v
) => Some(v
.clone()),
1620 const DEFAULT_README_FILES
: [&str; 3] = ["README.md", "README.txt", "README"];
1622 /// Checks if a file with any of the default README file names exists in the package root.
1623 /// If so, returns a `String` representing that name.
1624 fn default_readme_from_package_root(package_root
: &Path
) -> Option
<String
> {
1625 for &readme_filename
in DEFAULT_README_FILES
.iter() {
1626 if package_root
.join(readme_filename
).is_file() {
1627 return Some(readme_filename
.to_string());
1634 /// Checks a list of build targets, and ensures the target names are unique within a vector.
1635 /// If not, the name of the offending build target is returned.
1636 fn unique_build_targets(targets
: &[Target
], package_root
: &Path
) -> Result
<(), String
> {
1637 let mut seen
= HashSet
::new();
1638 for target
in targets
{
1639 if let TargetSourcePath
::Path(path
) = target
.src_path() {
1640 let full
= package_root
.join(path
);
1641 if !seen
.insert(full
.clone()) {
1642 return Err(full
.display().to_string());
1649 impl<P
: ResolveToPath
> TomlDependency
<P
> {
1650 pub(crate) fn to_dependency_split(
1653 source_id
: SourceId
,
1654 nested_paths
: &mut Vec
<PathBuf
>,
1656 warnings
: &mut Vec
<String
>,
1657 platform
: Option
<Platform
>,
1659 features
: &Features
,
1660 kind
: Option
<DepKind
>,
1661 ) -> CargoResult
<Dependency
> {
1665 deps
: &mut Vec
::new(),
1681 cx
: &mut Context
<'_
, '_
>,
1682 kind
: Option
<DepKind
>,
1683 ) -> CargoResult
<Dependency
> {
1685 TomlDependency
::Simple(ref version
) => DetailedTomlDependency
::<P
> {
1686 version
: Some(version
.clone()),
1687 ..Default
::default()
1689 .to_dependency(name
, cx
, kind
),
1690 TomlDependency
::Detailed(ref details
) => details
.to_dependency(name
, cx
, kind
),
1694 fn is_version_specified(&self) -> bool
{
1696 TomlDependency
::Detailed(d
) => d
.version
.is_some(),
1697 TomlDependency
::Simple(..) => true,
1702 impl<P
: ResolveToPath
> DetailedTomlDependency
<P
> {
1706 cx
: &mut Context
<'_
, '_
>,
1707 kind
: Option
<DepKind
>,
1708 ) -> CargoResult
<Dependency
> {
1709 if self.version
.is_none() && self.path
.is_none() && self.git
.is_none() {
1711 "dependency ({}) specified without \
1712 providing a local path, Git repository, or \
1713 version to use. This will be considered an \
1714 error in future versions",
1717 cx
.warnings
.push(msg
);
1720 if let Some(version
) = &self.version
{
1721 if version
.contains('
+'
) {
1722 cx
.warnings
.push(format
!(
1723 "version requirement `{}` for dependency `{}` \
1724 includes semver metadata which will be ignored, removing the \
1725 metadata is recommended to avoid confusion",
1726 version
, name_in_toml
1731 if self.git
.is_none() {
1732 let git_only_keys
= [
1733 (&self.branch
, "branch"),
1738 for &(key
, key_name
) in &git_only_keys
{
1741 "key `{}` is ignored for dependency ({}). \
1742 This will be considered an error in future versions",
1743 key_name
, name_in_toml
1745 cx
.warnings
.push(msg
)
1750 // Early detection of potentially misused feature syntax
1751 // instead of generating a "feature not found" error.
1752 if let Some(features
) = &self.features
{
1753 for feature
in features
{
1754 if feature
.contains('
/'
) {
1756 "feature `{}` in dependency `{}` is not allowed to contain slashes\n\
1757 If you want to enable features of a transitive dependency, \
1758 the direct dependency needs to re-export those features from \
1759 the `[features]` table.",
1764 if feature
.starts_with("dep:") {
1766 "feature `{}` in dependency `{}` is not allowed to use explicit \
1768 If you want to enable an optional dependency, specify the name \
1769 of the optional dependency without the `dep:` prefix, or specify \
1770 a feature from the dependency's `[features]` table that enables \
1771 the optional dependency.",
1779 let new_source_id
= match (
1782 self.registry
.as_ref(),
1783 self.registry_index
.as_ref(),
1785 (Some(_
), _
, Some(_
), _
) | (Some(_
), _
, _
, Some(_
)) => bail
!(
1786 "dependency ({}) specification is ambiguous. \
1787 Only one of `git` or `registry` is allowed.",
1790 (_
, _
, Some(_
), Some(_
)) => bail
!(
1791 "dependency ({}) specification is ambiguous. \
1792 Only one of `registry` or `registry-index` is allowed.",
1795 (Some(git
), maybe_path
, _
, _
) => {
1796 if maybe_path
.is_some() {
1798 "dependency ({}) specification is ambiguous. \
1799 Only one of `git` or `path` is allowed. \
1800 This will be considered an error in future versions",
1803 cx
.warnings
.push(msg
)
1806 let n_details
= [&self.branch
, &self.tag
, &self.rev
]
1808 .filter(|d
| d
.is_some())
1813 "dependency ({}) specification is ambiguous. \
1814 Only one of `branch`, `tag` or `rev` is allowed.",
1819 let reference
= self
1822 .map(GitReference
::Branch
)
1823 .or_else(|| self.tag
.clone().map(GitReference
::Tag
))
1824 .or_else(|| self.rev
.clone().map(GitReference
::Rev
))
1825 .unwrap_or(GitReference
::DefaultBranch
);
1826 let loc
= git
.into_url()?
;
1828 if let Some(fragment
) = loc
.fragment() {
1830 "URL fragment `#{}` in git URL is ignored for dependency ({}). \
1831 If you were trying to specify a specific git revision, \
1832 use `rev = \"{}\"` in the dependency declaration.",
1833 fragment
, name_in_toml
, fragment
1835 cx
.warnings
.push(msg
)
1838 SourceId
::for_git(&loc
, reference
)?
1840 (None
, Some(path
), _
, _
) => {
1841 let path
= path
.resolve(cx
.config
);
1842 cx
.nested_paths
.push(path
.clone());
1843 // If the source ID for the package we're parsing is a path
1844 // source, then we normalize the path here to get rid of
1845 // components like `..`.
1847 // The purpose of this is to get a canonical ID for the package
1848 // that we're depending on to ensure that builds of this package
1849 // always end up hashing to the same value no matter where it's
1851 if cx
.source_id
.is_path() {
1852 let path
= cx
.root
.join(path
);
1853 let path
= paths
::normalize_path(&path
);
1854 SourceId
::for_path(&path
)?
1859 (None
, None
, Some(registry
), None
) => SourceId
::alt_registry(cx
.config
, registry
)?
,
1860 (None
, None
, None
, Some(registry_index
)) => {
1861 let url
= registry_index
.into_url()?
;
1862 SourceId
::for_registry(&url
)?
1864 (None
, None
, None
, None
) => SourceId
::crates_io(cx
.config
)?
,
1867 let (pkg_name
, explicit_name_in_toml
) = match self.package
{
1868 Some(ref s
) => (&s
[..], Some(name_in_toml
)),
1869 None
=> (name_in_toml
, None
),
1872 let version
= self.version
.as_deref();
1873 let mut dep
= Dependency
::parse(pkg_name
, version
, new_source_id
)?
;
1874 dep
.set_features(self.features
.iter().flatten())
1875 .set_default_features(
1876 self.default_features
1877 .or(self.default_features2
)
1880 .set_optional(self.optional
.unwrap_or(false))
1881 .set_platform(cx
.platform
.clone());
1882 if let Some(registry
) = &self.registry
{
1883 let registry_id
= SourceId
::alt_registry(cx
.config
, registry
)?
;
1884 dep
.set_registry_id(registry_id
);
1886 if let Some(registry_index
) = &self.registry_index
{
1887 let url
= registry_index
.into_url()?
;
1888 let registry_id
= SourceId
::for_registry(&url
)?
;
1889 dep
.set_registry_id(registry_id
);
1892 if let Some(kind
) = kind
{
1895 if let Some(name_in_toml
) = explicit_name_in_toml
{
1896 cx
.features
.require(Feature
::rename_dependency())?
;
1897 dep
.set_explicit_name_in_toml(name_in_toml
);
1900 if let Some(p
) = self.public
{
1901 cx
.features
.require(Feature
::public_dependency())?
;
1903 if dep
.kind() != DepKind
::Normal
{
1904 bail
!("'public' specifier can only be used on regular dependencies, not {:?} dependencies", dep
.kind());
1913 #[derive(Default, Serialize, Deserialize, Debug, Clone)]
1915 name
: Option
<String
>,
1917 // The intention was to only accept `crate-type` here but historical
1918 // versions of Cargo also accepted `crate_type`, so look for both.
1919 #[serde(rename = "crate-type")]
1920 crate_type
: Option
<Vec
<String
>>,
1921 #[serde(rename = "crate_type")]
1922 crate_type2
: Option
<Vec
<String
>>,
1924 path
: Option
<PathValue
>,
1926 doctest
: Option
<bool
>,
1927 bench
: Option
<bool
>,
1929 plugin
: Option
<bool
>,
1930 #[serde(rename = "proc-macro")]
1931 proc_macro_raw
: Option
<bool
>,
1932 #[serde(rename = "proc_macro")]
1933 proc_macro_raw2
: Option
<bool
>,
1934 harness
: Option
<bool
>,
1935 #[serde(rename = "required-features")]
1936 required_features
: Option
<Vec
<String
>>,
1937 edition
: Option
<String
>,
1941 struct PathValue(PathBuf
);
1943 impl<'de
> de
::Deserialize
<'de
> for PathValue
{
1944 fn deserialize
<D
>(deserializer
: D
) -> Result
<Self, D
::Error
>
1946 D
: de
::Deserializer
<'de
>,
1948 Ok(PathValue(String
::deserialize(deserializer
)?
.into()))
1952 impl ser
::Serialize
for PathValue
{
1953 fn serialize
<S
>(&self, serializer
: S
) -> Result
<S
::Ok
, S
::Error
>
1957 self.0.serialize(serializer
)
1961 /// Corresponds to a `target` entry, but `TomlTarget` is already used.
1962 #[derive(Serialize, Deserialize, Debug)]
1963 struct TomlPlatform
{
1964 dependencies
: Option
<BTreeMap
<String
, TomlDependency
>>,
1965 #[serde(rename = "build-dependencies")]
1966 build_dependencies
: Option
<BTreeMap
<String
, TomlDependency
>>,
1967 #[serde(rename = "build_dependencies")]
1968 build_dependencies2
: Option
<BTreeMap
<String
, TomlDependency
>>,
1969 #[serde(rename = "dev-dependencies")]
1970 dev_dependencies
: Option
<BTreeMap
<String
, TomlDependency
>>,
1971 #[serde(rename = "dev_dependencies")]
1972 dev_dependencies2
: Option
<BTreeMap
<String
, TomlDependency
>>,
1976 fn new() -> TomlTarget
{
1977 TomlTarget
::default()
1980 fn name(&self) -> String
{
1982 Some(ref name
) => name
.clone(),
1983 None
=> panic
!("target name is required"),
1987 fn proc_macro(&self) -> Option
<bool
> {
1988 self.proc_macro_raw
.or(self.proc_macro_raw2
).or_else(|| {
1989 if let Some(types
) = self.crate_types() {
1990 if types
.contains(&"proc-macro".to_string()) {
1998 fn crate_types(&self) -> Option
<&Vec
<String
>> {
2001 .or_else(|| self.crate_type2
.as_ref())
2005 impl fmt
::Debug
for PathValue
{
2006 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{