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_backend
: Option
<InternedString
>,
437 pub codegen_units
: Option
<u32>,
438 pub debug
: Option
<U32OrBool
>,
439 pub split_debuginfo
: Option
<String
>,
440 pub debug_assertions
: Option
<bool
>,
441 pub rpath
: Option
<bool
>,
442 pub panic
: Option
<String
>,
443 pub overflow_checks
: Option
<bool
>,
444 pub incremental
: Option
<bool
>,
445 pub dir_name
: Option
<InternedString
>,
446 pub inherits
: Option
<InternedString
>,
447 pub strip
: Option
<StringOrBool
>,
448 // These two fields must be last because they are sub-tables, and TOML
449 // requires all non-tables to be listed first.
450 pub package
: Option
<BTreeMap
<ProfilePackageSpec
, TomlProfile
>>,
451 pub build_override
: Option
<Box
<TomlProfile
>>,
454 #[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
455 pub enum ProfilePackageSpec
{
460 impl ser
::Serialize
for ProfilePackageSpec
{
461 fn serialize
<S
>(&self, s
: S
) -> Result
<S
::Ok
, S
::Error
>
466 ProfilePackageSpec
::Spec(ref spec
) => spec
.serialize(s
),
467 ProfilePackageSpec
::All
=> "*".serialize(s
),
472 impl<'de
> de
::Deserialize
<'de
> for ProfilePackageSpec
{
473 fn deserialize
<D
>(d
: D
) -> Result
<ProfilePackageSpec
, D
::Error
>
475 D
: de
::Deserializer
<'de
>,
477 let string
= String
::deserialize(d
)?
;
479 Ok(ProfilePackageSpec
::All
)
481 PackageIdSpec
::parse(&string
)
482 .map_err(de
::Error
::custom
)
483 .map(ProfilePackageSpec
::Spec
)
493 warnings
: &mut Vec
<String
>,
494 ) -> CargoResult
<()> {
495 if let Some(ref profile
) = self.build_override
{
496 features
.require(Feature
::profile_overrides())?
;
497 profile
.validate_override("build-override", features
)?
;
499 if let Some(ref packages
) = self.package
{
500 features
.require(Feature
::profile_overrides())?
;
501 for profile
in packages
.values() {
502 profile
.validate_override("package", features
)?
;
506 // Feature gate definition of named profiles
508 "dev" | "release" | "bench" | "test" | "doc" => {}
510 features
.require(Feature
::named_profiles())?
;
514 // Profile name validation
515 Self::validate_name(name
)?
;
517 // Feature gate on uses of keys related to named profiles
518 if self.inherits
.is_some() {
519 features
.require(Feature
::named_profiles())?
;
522 if let Some(dir_name
) = self.dir_name
{
523 // This is disabled for now, as we would like to stabilize named
524 // profiles without this, and then decide in the future if it is
525 // needed. This helps simplify the UI a little.
527 "dir-name=\"{}\" in profile `{}` is not currently allowed, \
528 directory names are tied to the profile name for custom profiles",
534 // `inherits` validation
535 if matches
!(self.inherits
.map(|s
| s
.as_str()), Some("debug")) {
537 "profile.{}.inherits=\"debug\" should be profile.{}.inherits=\"dev\"",
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())?
;
569 if let Some(codegen_backend
) = &self.codegen_backend
{
570 features
.require(Feature
::codegen_backend())?
;
571 if codegen_backend
.contains(|c
: char| !c
.is_ascii_alphanumeric() && c
!= '_'
) {
573 "`profile.{}.codegen-backend` setting of `{}` is not a valid backend name.",
583 /// Validate dir-names and profile names according to RFC 2678.
584 pub fn validate_name(name
: &str) -> CargoResult
<()> {
585 if let Some(ch
) = name
587 .find(|ch
| !ch
.is_alphanumeric() && *ch
!= '_'
&& *ch
!= '
-'
)
590 "invalid character `{}` in profile name `{}`\n\
591 Allowed characters are letters, numbers, underscore, and hyphen.",
597 const SEE_DOCS
: &str = "See https://doc.rust-lang.org/cargo/reference/profiles.html \
598 for more on configuring profiles.";
600 let lower_name
= name
.to_lowercase();
601 if lower_name
== "debug" {
603 "profile name `{}` is reserved\n\
604 To configure the default development profile, use the name `dev` \
605 as in [profile.dev]\n\
611 if lower_name
== "build-override" {
613 "profile name `{}` is reserved\n\
614 To configure build dependency settings, use [profile.dev.build-override] \
615 and [profile.release.build-override]\n\
622 // These are some arbitrary reservations. We have no plans to use
623 // these, but it seems safer to reserve a few just in case we want to
624 // add more built-in profiles in the future. We can also uses special
625 // syntax like cargo:foo if needed. But it is unlikely these will ever
647 ) || lower_name
.starts_with("cargo")
650 "profile name `{}` is reserved\n\
651 Please choose a different name.\n\
661 fn validate_override(&self, which
: &str, features
: &Features
) -> CargoResult
<()> {
662 if self.package
.is_some() {
663 bail
!("package-specific profiles cannot be nested");
665 if self.build_override
.is_some() {
666 bail
!("build-override profiles cannot be nested");
668 if self.panic
.is_some() {
669 bail
!("`panic` may not be specified in a `{}` profile", which
)
671 if self.lto
.is_some() {
672 bail
!("`lto` may not be specified in a `{}` profile", which
)
674 if self.rpath
.is_some() {
675 bail
!("`rpath` may not be specified in a `{}` profile", which
)
677 if self.codegen_backend
.is_some() {
678 features
.require(Feature
::codegen_backend())?
;
683 /// Overwrite self's values with the given profile.
684 pub fn merge(&mut self, profile
: &TomlProfile
) {
685 if let Some(v
) = &profile
.opt_level
{
686 self.opt_level
= Some(v
.clone());
689 if let Some(v
) = &profile
.lto
{
690 self.lto
= Some(v
.clone());
693 if let Some(v
) = profile
.codegen_backend
{
694 self.codegen_backend
= Some(v
);
697 if let Some(v
) = profile
.codegen_units
{
698 self.codegen_units
= Some(v
);
701 if let Some(v
) = &profile
.debug
{
702 self.debug
= Some(v
.clone());
705 if let Some(v
) = profile
.debug_assertions
{
706 self.debug_assertions
= Some(v
);
709 if let Some(v
) = &profile
.split_debuginfo
{
710 self.split_debuginfo
= Some(v
.clone());
713 if let Some(v
) = profile
.rpath
{
714 self.rpath
= Some(v
);
717 if let Some(v
) = &profile
.panic
{
718 self.panic
= Some(v
.clone());
721 if let Some(v
) = profile
.overflow_checks
{
722 self.overflow_checks
= Some(v
);
725 if let Some(v
) = profile
.incremental
{
726 self.incremental
= Some(v
);
729 if let Some(other_package
) = &profile
.package
{
730 match &mut self.package
{
731 Some(self_package
) => {
732 for (spec
, other_pkg_profile
) in other_package
{
733 match self_package
.get_mut(spec
) {
734 Some(p
) => p
.merge(other_pkg_profile
),
736 self_package
.insert(spec
.clone(), other_pkg_profile
.clone());
741 None
=> self.package
= Some(other_package
.clone()),
745 if let Some(other_bo
) = &profile
.build_override
{
746 match &mut self.build_override
{
747 Some(self_bo
) => self_bo
.merge(other_bo
),
748 None
=> self.build_override
= Some(other_bo
.clone()),
752 if let Some(v
) = &profile
.inherits
{
753 self.inherits
= Some(*v
);
756 if let Some(v
) = &profile
.dir_name
{
757 self.dir_name
= Some(*v
);
760 if let Some(v
) = &profile
.strip
{
761 self.strip
= Some(v
.clone());
766 /// A StringOrVec can be parsed from either a TOML string or array,
767 /// but is always stored as a vector.
768 #[derive(Clone, Debug, Serialize, Eq, PartialEq, PartialOrd, Ord)]
769 pub struct StringOrVec(Vec
<String
>);
771 impl<'de
> de
::Deserialize
<'de
> for StringOrVec
{
772 fn deserialize
<D
>(deserializer
: D
) -> Result
<Self, D
::Error
>
774 D
: de
::Deserializer
<'de
>,
778 impl<'de
> de
::Visitor
<'de
> for Visitor
{
779 type Value
= StringOrVec
;
781 fn expecting(&self, formatter
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
782 formatter
.write_str("string or list of strings")
785 fn visit_str
<E
>(self, s
: &str) -> Result
<Self::Value
, E
>
789 Ok(StringOrVec(vec
![s
.to_string()]))
792 fn visit_seq
<V
>(self, v
: V
) -> Result
<Self::Value
, V
::Error
>
794 V
: de
::SeqAccess
<'de
>,
796 let seq
= de
::value
::SeqAccessDeserializer
::new(v
);
797 Vec
::deserialize(seq
).map(StringOrVec
)
801 deserializer
.deserialize_any(Visitor
)
806 pub fn iter
<'a
>(&'a
self) -> std
::slice
::Iter
<'a
, String
> {
811 #[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
812 #[serde(untagged, expecting = "expected a boolean or a string")]
813 pub enum StringOrBool
{
818 #[derive(PartialEq, Clone, Debug, Serialize)]
820 pub enum VecStringOrBool
{
821 VecString(Vec
<String
>),
825 impl<'de
> de
::Deserialize
<'de
> for VecStringOrBool
{
826 fn deserialize
<D
>(deserializer
: D
) -> Result
<Self, D
::Error
>
828 D
: de
::Deserializer
<'de
>,
832 impl<'de
> de
::Visitor
<'de
> for Visitor
{
833 type Value
= VecStringOrBool
;
835 fn expecting(&self, formatter
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
836 formatter
.write_str("a boolean or vector of strings")
839 fn visit_seq
<V
>(self, v
: V
) -> Result
<Self::Value
, V
::Error
>
841 V
: de
::SeqAccess
<'de
>,
843 let seq
= de
::value
::SeqAccessDeserializer
::new(v
);
844 Vec
::deserialize(seq
).map(VecStringOrBool
::VecString
)
847 fn visit_bool
<E
>(self, b
: bool
) -> Result
<Self::Value
, E
>
851 Ok(VecStringOrBool
::Bool(b
))
855 deserializer
.deserialize_any(Visitor
)
859 fn version_trim_whitespace
<'de
, D
>(deserializer
: D
) -> Result
<semver
::Version
, D
::Error
>
861 D
: de
::Deserializer
<'de
>,
865 impl<'de
> de
::Visitor
<'de
> for Visitor
{
866 type Value
= semver
::Version
;
868 fn expecting(&self, formatter
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
869 formatter
.write_str("SemVer version")
872 fn visit_str
<E
>(self, string
: &str) -> Result
<Self::Value
, E
>
876 string
.trim().parse().map_err(de
::Error
::custom
)
880 deserializer
.deserialize_str(Visitor
)
883 /// Represents the `package`/`project` sections of a `Cargo.toml`.
885 /// Note that the order of the fields matters, since this is the order they
886 /// are serialized to a TOML file. For example, you cannot have values after
887 /// the field `metadata`, since it is a table and values cannot appear after
889 #[derive(Deserialize, Serialize, Clone, Debug)]
890 #[serde(rename_all = "kebab-case")]
891 pub struct TomlProject
{
892 edition
: Option
<String
>,
893 rust_version
: Option
<String
>,
894 name
: InternedString
,
895 #[serde(deserialize_with = "version_trim_whitespace")]
896 version
: semver
::Version
,
897 authors
: Option
<Vec
<String
>>,
898 build
: Option
<StringOrBool
>,
899 metabuild
: Option
<StringOrVec
>,
900 #[serde(rename = "default-target")]
901 default_target
: Option
<String
>,
902 #[serde(rename = "forced-target")]
903 forced_target
: Option
<String
>,
904 links
: Option
<String
>,
905 exclude
: Option
<Vec
<String
>>,
906 include
: Option
<Vec
<String
>>,
907 publish
: Option
<VecStringOrBool
>,
908 workspace
: Option
<String
>,
909 im_a_teapot
: Option
<bool
>,
910 autobins
: Option
<bool
>,
911 autoexamples
: Option
<bool
>,
912 autotests
: Option
<bool
>,
913 autobenches
: Option
<bool
>,
914 default_run
: Option
<String
>,
917 description
: Option
<String
>,
918 homepage
: Option
<String
>,
919 documentation
: Option
<String
>,
920 readme
: Option
<StringOrBool
>,
921 keywords
: Option
<Vec
<String
>>,
922 categories
: Option
<Vec
<String
>>,
923 license
: Option
<String
>,
924 license_file
: Option
<String
>,
925 repository
: Option
<String
>,
926 resolver
: Option
<String
>,
928 // Note that this field must come last due to the way toml serialization
929 // works which requires tables to be emitted after all values.
930 metadata
: Option
<toml
::Value
>,
933 #[derive(Debug, Deserialize, Serialize)]
934 pub struct TomlWorkspace
{
935 members
: Option
<Vec
<String
>>,
936 #[serde(rename = "default-members")]
937 default_members
: Option
<Vec
<String
>>,
938 exclude
: Option
<Vec
<String
>>,
939 resolver
: Option
<String
>,
941 // Note that this field must come last due to the way toml serialization
942 // works which requires tables to be emitted after all values.
943 metadata
: Option
<toml
::Value
>,
947 pub fn to_package_id(&self, source_id
: SourceId
) -> CargoResult
<PackageId
> {
948 PackageId
::new(self.name
, self.version
.clone(), source_id
)
952 struct Context
<'a
, 'b
> {
953 deps
: &'a
mut Vec
<Dependency
>,
955 nested_paths
: &'a
mut Vec
<PathBuf
>,
957 warnings
: &'a
mut Vec
<String
>,
958 platform
: Option
<Platform
>,
960 features
: &'a Features
,
964 /// Prepares the manifest for publishing.
965 // - Path and git components of dependency specifications are removed.
966 // - License path is updated to point within the package.
967 pub fn prepare_for_publish(
971 ) -> CargoResult
<TomlManifest
> {
972 let config
= ws
.config();
973 let mut package
= self
976 .or_else(|| self.project
.as_ref())
979 package
.workspace
= None
;
980 package
.resolver
= ws
.resolve_behavior().to_manifest();
981 if let Some(license_file
) = &package
.license_file
{
982 let license_path
= Path
::new(&license_file
);
983 let abs_license_path
= paths
::normalize_path(&package_root
.join(license_path
));
984 if abs_license_path
.strip_prefix(package_root
).is_err() {
985 // This path points outside of the package root. `cargo package`
986 // will copy it into the root, so adjust the path to this location.
987 package
.license_file
= Some(
997 let all
= |_d
: &TomlDependency
| true;
998 return Ok(TomlManifest
{
999 package
: Some(package
),
1001 profile
: self.profile
.clone(),
1002 lib
: self.lib
.clone(),
1003 bin
: self.bin
.clone(),
1004 example
: self.example
.clone(),
1005 test
: self.test
.clone(),
1006 bench
: self.bench
.clone(),
1007 dependencies
: map_deps(config
, self.dependencies
.as_ref(), all
)?
,
1008 dev_dependencies
: map_deps(
1010 self.dev_dependencies
1012 .or_else(|| self.dev_dependencies2
.as_ref()),
1013 TomlDependency
::is_version_specified
,
1015 dev_dependencies2
: None
,
1016 build_dependencies
: map_deps(
1018 self.build_dependencies
1020 .or_else(|| self.build_dependencies2
.as_ref()),
1023 build_dependencies2
: None
,
1024 features
: self.features
.clone(),
1025 target
: match self.target
.as_ref().map(|target_map
| {
1032 dependencies
: map_deps(config
, v
.dependencies
.as_ref(), all
)?
,
1033 dev_dependencies
: map_deps(
1037 .or_else(|| v
.dev_dependencies2
.as_ref()),
1038 TomlDependency
::is_version_specified
,
1040 dev_dependencies2
: None
,
1041 build_dependencies
: map_deps(
1043 v
.build_dependencies
1045 .or_else(|| v
.build_dependencies2
.as_ref()),
1048 build_dependencies2
: None
,
1054 Some(Ok(v
)) => Some(v
),
1055 Some(Err(e
)) => return Err(e
),
1061 badges
: self.badges
.clone(),
1062 cargo_features
: self.cargo_features
.clone(),
1067 deps
: Option
<&BTreeMap
<String
, TomlDependency
>>,
1068 filter
: impl Fn(&TomlDependency
) -> bool
,
1069 ) -> CargoResult
<Option
<BTreeMap
<String
, TomlDependency
>>> {
1070 let deps
= match deps
{
1072 None
=> return Ok(None
),
1076 .filter(|(_k
, v
)| filter(v
))
1077 .map(|(k
, v
)| Ok((k
.clone(), map_dependency(config
, v
)?
)))
1078 .collect
::<CargoResult
<BTreeMap
<_
, _
>>>()?
;
1082 fn map_dependency(config
: &Config
, dep
: &TomlDependency
) -> CargoResult
<TomlDependency
> {
1084 TomlDependency
::Detailed(d
) => {
1085 let mut d
= d
.clone();
1086 // Path dependencies become crates.io deps.
1088 // Same with git dependencies.
1093 // registry specifications are elaborated to the index URL
1094 if let Some(registry
) = d
.registry
.take() {
1095 let src
= SourceId
::alt_registry(config
, ®istry
)?
;
1096 d
.registry_index
= Some(src
.url().to_string());
1098 Ok(TomlDependency
::Detailed(d
))
1100 TomlDependency
::Simple(s
) => Ok(TomlDependency
::Detailed(DetailedTomlDependency
{
1101 version
: Some(s
.clone()),
1102 ..Default
::default()
1108 pub fn to_real_manifest(
1109 me
: &Rc
<TomlManifest
>,
1110 source_id
: SourceId
,
1111 package_root
: &Path
,
1113 ) -> CargoResult
<(Manifest
, Vec
<PathBuf
>)> {
1114 let mut nested_paths
= vec
![];
1115 let mut warnings
= vec
![];
1116 let mut errors
= vec
![];
1118 // Parse features first so they will be available when parsing other parts of the TOML.
1119 let empty
= Vec
::new();
1120 let cargo_features
= me
.cargo_features
.as_ref().unwrap_or(&empty
);
1121 let features
= Features
::new(cargo_features
, config
, &mut warnings
, source_id
.is_path())?
;
1123 let project
= me
.project
.as_ref().or_else(|| me
.package
.as_ref());
1124 let project
= project
.ok_or_else(|| anyhow
!("no `package` section found"))?
;
1126 let package_name
= project
.name
.trim();
1127 if package_name
.is_empty() {
1128 bail
!("package name cannot be an empty string")
1131 validate_package_name(package_name
, "package name", "")?
;
1133 let pkgid
= project
.to_package_id(source_id
)?
;
1135 let edition
= if let Some(ref edition
) = project
.edition
{
1137 .require(Feature
::edition())
1138 .with_context(|| "editions are unstable")?
;
1141 .with_context(|| "failed to parse the `edition` key")?
1143 Edition
::Edition2015
1145 if edition
== Edition
::Edition2021
{
1146 features
.require(Feature
::edition2021())?
;
1147 } else if !edition
.is_stable() {
1148 // Guard in case someone forgets to add .require()
1149 return Err(util
::errors
::internal(format
!(
1150 "edition {} should be gated",
1155 let rust_version
= if let Some(rust_version
) = &project
.rust_version
{
1156 let req
= match semver
::VersionReq
::parse(rust_version
) {
1157 // Exclude semver operators like `^` and pre-release identifiers
1158 Ok(req
) if rust_version
.chars().all(|c
| c
.is_ascii_digit() || c
== '
.'
) => req
,
1159 _
=> bail
!("`rust-version` must be a value like \"1.32\""),
1161 if let Some(first_version
) = edition
.first_version() {
1163 semver
::Version
::new(first_version
.major
, first_version
.minor
- 1, 9999);
1164 if req
.matches(&unsupported
) {
1166 "rust-version {} is older than first version ({}) required by \
1167 the specified edition ({})",
1174 Some(rust_version
.clone())
1179 if project
.metabuild
.is_some() {
1180 features
.require(Feature
::metabuild())?
;
1183 if project
.resolver
.is_some()
1187 .map_or(false, |ws
| ws
.resolver
.is_some())
1189 features
.require(Feature
::resolver())?
;
1191 let resolve_behavior
= match (
1192 project
.resolver
.as_ref(),
1193 me
.workspace
.as_ref().and_then(|ws
| ws
.resolver
.as_ref()),
1195 (None
, None
) => None
,
1196 (Some(s
), None
) | (None
, Some(s
)) => Some(ResolveBehavior
::from_manifest(s
)?
),
1197 (Some(_
), Some(_
)) => {
1198 bail
!("cannot specify `resolver` field in both `[workspace]` and `[package]`")
1202 // If we have no lib at all, use the inferred lib, if available.
1203 // If we have a lib with a path, we're done.
1204 // If we have a lib with no path, use the inferred lib or else the package name.
1205 let targets
= targets(
1217 if targets
.is_empty() {
1218 debug
!("manifest has no build targets");
1221 if let Err(e
) = unique_build_targets(&targets
, package_root
) {
1222 warnings
.push(format
!(
1223 "file found to be present in multiple \
1229 if let Some(links
) = &project
.links
{
1230 if !targets
.iter().any(|t
| t
.is_custom_build()) {
1232 "package `{}` specifies that it links to `{}` but does not \
1233 have a custom build script",
1240 let mut deps
= Vec
::new();
1245 let mut cx
= Context
{
1248 nested_paths
: &mut nested_paths
,
1250 warnings
: &mut warnings
,
1251 features
: &features
,
1256 fn process_dependencies(
1257 cx
: &mut Context
<'_
, '_
>,
1258 new_deps
: Option
<&BTreeMap
<String
, TomlDependency
>>,
1259 kind
: Option
<DepKind
>,
1260 ) -> CargoResult
<()> {
1261 let dependencies
= match new_deps
{
1262 Some(dependencies
) => dependencies
,
1263 None
=> return Ok(()),
1265 for (n
, v
) in dependencies
.iter() {
1266 let dep
= v
.to_dependency(n
, cx
, kind
)?
;
1267 validate_package_name(dep
.name_in_toml().as_str(), "dependency name", "")?
;
1274 // Collect the dependencies.
1275 process_dependencies(&mut cx
, me
.dependencies
.as_ref(), None
)?
;
1279 .or_else(|| me
.dev_dependencies2
.as_ref());
1280 process_dependencies(&mut cx
, dev_deps
, Some(DepKind
::Development
))?
;
1284 .or_else(|| me
.build_dependencies2
.as_ref());
1285 process_dependencies(&mut cx
, build_deps
, Some(DepKind
::Build
))?
;
1287 for (name
, platform
) in me
.target
.iter().flatten() {
1289 let platform
: Platform
= name
.parse()?
;
1290 platform
.check_cfg_attributes(&mut cx
.warnings
);
1293 process_dependencies(&mut cx
, platform
.dependencies
.as_ref(), None
)?
;
1294 let build_deps
= platform
1297 .or_else(|| platform
.build_dependencies2
.as_ref());
1298 process_dependencies(&mut cx
, build_deps
, Some(DepKind
::Build
))?
;
1299 let dev_deps
= platform
1302 .or_else(|| platform
.dev_dependencies2
.as_ref());
1303 process_dependencies(&mut cx
, dev_deps
, Some(DepKind
::Development
))?
;
1306 replace
= me
.replace(&mut cx
)?
;
1307 patch
= me
.patch(&mut cx
)?
;
1311 let mut names_sources
= BTreeMap
::new();
1313 let name
= dep
.name_in_toml();
1314 let prev
= names_sources
.insert(name
.to_string(), dep
.source_id());
1315 if prev
.is_some() && prev
!= Some(dep
.source_id()) {
1317 "Dependency '{}' has different source paths depending on the build \
1318 target. Each dependency must have a single canonical source path \
1319 irrespective of build target.",
1326 let exclude
= project
.exclude
.clone().unwrap_or_default();
1327 let include
= project
.include
.clone().unwrap_or_default();
1328 let empty_features
= BTreeMap
::new();
1330 let summary
= Summary
::new(
1334 me
.features
.as_ref().unwrap_or(&empty_features
),
1335 project
.links
.as_deref(),
1337 let unstable
= config
.cli_unstable();
1338 summary
.unstable_gate(unstable
.namespaced_features
, unstable
.weak_dep_features
)?
;
1340 let metadata
= ManifestMetadata
{
1341 description
: project
.description
.clone(),
1342 homepage
: project
.homepage
.clone(),
1343 documentation
: project
.documentation
.clone(),
1344 readme
: readme_for_project(package_root
, project
),
1345 authors
: project
.authors
.clone().unwrap_or_default(),
1346 license
: project
.license
.clone(),
1347 license_file
: project
.license_file
.clone(),
1348 repository
: project
.repository
.clone(),
1349 keywords
: project
.keywords
.clone().unwrap_or_default(),
1350 categories
: project
.categories
.clone().unwrap_or_default(),
1351 badges
: me
.badges
.clone().unwrap_or_default(),
1352 links
: project
.links
.clone(),
1355 let workspace_config
= match (me
.workspace
.as_ref(), project
.workspace
.as_ref()) {
1356 (Some(config
), None
) => WorkspaceConfig
::Root(WorkspaceRootConfig
::new(
1359 &config
.default_members
,
1363 (None
, root
) => WorkspaceConfig
::Member
{
1364 root
: root
.cloned(),
1366 (Some(..), Some(..)) => bail
!(
1367 "cannot configure both `package.workspace` and \
1368 `[workspace]`, only one can be specified"
1371 let profiles
= me
.profile
.clone();
1372 if let Some(profiles
) = &profiles
{
1373 profiles
.validate(&features
, &mut warnings
)?
;
1375 let publish
= match project
.publish
{
1376 Some(VecStringOrBool
::VecString(ref vecstring
)) => Some(vecstring
.clone()),
1377 Some(VecStringOrBool
::Bool(false)) => Some(vec
![]),
1378 None
| Some(VecStringOrBool
::Bool(true)) => None
,
1381 if summary
.features().contains_key("default-features") {
1383 "`default-features = [\"..\"]` was found in [features]. \
1384 Did you mean to use `default = [\"..\"]`?"
1389 if let Some(run
) = &project
.default_run
{
1392 .filter(|t
| t
.is_bin())
1393 .any(|t
| t
.name() == run
)
1396 util
::closest_msg(run
, targets
.iter().filter(|t
| t
.is_bin()), |t
| t
.name());
1397 bail
!("default-run target `{}` not found{}", run
, suggestion
);
1401 let default_kind
= project
1404 .map(|t
| CompileTarget
::new(&*t
))
1406 .map(CompileKind
::Target
);
1407 let forced_kind
= project
1410 .map(|t
| CompileTarget
::new(&*t
))
1412 .map(CompileKind
::Target
);
1414 let custom_metadata
= project
.metadata
.clone();
1415 let mut manifest
= Manifest
::new(
1422 project
.links
.clone(),
1433 project
.im_a_teapot
,
1434 project
.default_run
.clone(),
1436 project
.metabuild
.clone().map(|sov
| sov
.0),
1439 if project
.license_file
.is_some() && project
.license
.is_some() {
1440 manifest
.warnings_mut().add_warning(
1441 "only one of `license` or \
1442 `license-file` is necessary"
1446 for warning
in warnings
{
1447 manifest
.warnings_mut().add_warning(warning
);
1449 for error
in errors
{
1450 manifest
.warnings_mut().add_critical_warning(error
);
1453 manifest
.feature_gate()?
;
1455 Ok((manifest
, nested_paths
))
1458 fn to_virtual_manifest(
1459 me
: &Rc
<TomlManifest
>,
1460 source_id
: SourceId
,
1463 ) -> CargoResult
<(VirtualManifest
, Vec
<PathBuf
>)> {
1464 if me
.project
.is_some() {
1465 bail
!("this virtual manifest specifies a [project] section, which is not allowed");
1467 if me
.package
.is_some() {
1468 bail
!("this virtual manifest specifies a [package] section, which is not allowed");
1470 if me
.lib
.is_some() {
1471 bail
!("this virtual manifest specifies a [lib] section, which is not allowed");
1473 if me
.bin
.is_some() {
1474 bail
!("this virtual manifest specifies a [[bin]] section, which is not allowed");
1476 if me
.example
.is_some() {
1477 bail
!("this virtual manifest specifies a [[example]] section, which is not allowed");
1479 if me
.test
.is_some() {
1480 bail
!("this virtual manifest specifies a [[test]] section, which is not allowed");
1482 if me
.bench
.is_some() {
1483 bail
!("this virtual manifest specifies a [[bench]] section, which is not allowed");
1485 if me
.dependencies
.is_some() {
1486 bail
!("this virtual manifest specifies a [dependencies] section, which is not allowed");
1488 if me
.dev_dependencies
.is_some() || me
.dev_dependencies2
.is_some() {
1489 bail
!("this virtual manifest specifies a [dev-dependencies] section, which is not allowed");
1491 if me
.build_dependencies
.is_some() || me
.build_dependencies2
.is_some() {
1492 bail
!("this virtual manifest specifies a [build-dependencies] section, which is not allowed");
1494 if me
.features
.is_some() {
1495 bail
!("this virtual manifest specifies a [features] section, which is not allowed");
1497 if me
.target
.is_some() {
1498 bail
!("this virtual manifest specifies a [target] section, which is not allowed");
1500 if me
.badges
.is_some() {
1501 bail
!("this virtual manifest specifies a [badges] section, which is not allowed");
1504 let mut nested_paths
= Vec
::new();
1505 let mut warnings
= Vec
::new();
1506 let mut deps
= Vec
::new();
1507 let empty
= Vec
::new();
1508 let cargo_features
= me
.cargo_features
.as_ref().unwrap_or(&empty
);
1509 let features
= Features
::new(cargo_features
, config
, &mut warnings
, source_id
.is_path())?
;
1511 let (replace
, patch
) = {
1512 let mut cx
= Context
{
1515 nested_paths
: &mut nested_paths
,
1517 warnings
: &mut warnings
,
1519 features
: &features
,
1522 (me
.replace(&mut cx
)?
, me
.patch(&mut cx
)?
)
1524 let profiles
= me
.profile
.clone();
1525 if let Some(profiles
) = &profiles
{
1526 profiles
.validate(&features
, &mut warnings
)?
;
1531 .map_or(false, |ws
| ws
.resolver
.is_some())
1533 features
.require(Feature
::resolver())?
;
1535 let resolve_behavior
= me
1538 .and_then(|ws
| ws
.resolver
.as_deref())
1539 .map(|r
| ResolveBehavior
::from_manifest(r
))
1541 let workspace_config
= match me
.workspace
{
1542 Some(ref config
) => WorkspaceConfig
::Root(WorkspaceRootConfig
::new(
1545 &config
.default_members
,
1550 bail
!("virtual manifests must be configured with [workspace]");
1554 VirtualManifest
::new(
1566 fn replace(&self, cx
: &mut Context
<'_
, '_
>) -> CargoResult
<Vec
<(PackageIdSpec
, Dependency
)>> {
1567 if self.patch
.is_some() && self.replace
.is_some() {
1568 bail
!("cannot specify both [replace] and [patch]");
1570 let mut replace
= Vec
::new();
1571 for (spec
, replacement
) in self.replace
.iter().flatten() {
1572 let mut spec
= PackageIdSpec
::parse(spec
).with_context(|| {
1574 "replacements must specify a valid semver \
1575 version to replace, but `{}` does not",
1579 if spec
.url().is_none() {
1580 spec
.set_url(CRATES_IO_INDEX
.parse().unwrap());
1583 if replacement
.is_version_specified() {
1585 "replacements cannot specify a version \
1586 requirement, but found one for `{}`",
1591 let mut dep
= replacement
.to_dependency(spec
.name().as_str(), cx
, None
)?
;
1593 let version
= spec
.version().ok_or_else(|| {
1595 "replacements must specify a version \
1596 to replace, but `{}` does not",
1600 dep
.set_version_req(VersionReq
::exact(version
));
1602 replace
.push((spec
, dep
));
1607 fn patch(&self, cx
: &mut Context
<'_
, '_
>) -> CargoResult
<HashMap
<Url
, Vec
<Dependency
>>> {
1608 let mut patch
= HashMap
::new();
1609 for (url
, deps
) in self.patch
.iter().flatten() {
1610 let url
= match &url
[..] {
1611 CRATES_IO_REGISTRY
=> CRATES_IO_INDEX
.parse().unwrap(),
1614 .get_registry_index(url
)
1615 .or_else(|_
| url
.into_url())
1617 format
!("[patch] entry `{}` should be a URL or registry name", url
)
1623 .map(|(name
, dep
)| dep
.to_dependency(name
, cx
, None
))
1624 .collect
::<CargoResult
<Vec
<_
>>>()?
,
1630 /// Returns the path to the build script if one exists for this crate.
1631 fn maybe_custom_build(
1633 build
: &Option
<StringOrBool
>,
1634 package_root
: &Path
,
1635 ) -> Option
<PathBuf
> {
1636 let build_rs
= package_root
.join("build.rs");
1638 // Explicitly no build script.
1639 Some(StringOrBool
::Bool(false)) => None
,
1640 Some(StringOrBool
::Bool(true)) => Some(build_rs
),
1641 Some(StringOrBool
::String(ref s
)) => Some(PathBuf
::from(s
)),
1643 // If there is a `build.rs` file next to the `Cargo.toml`, assume it is
1645 if build_rs
.is_file() {
1654 pub fn has_profiles(&self) -> bool
{
1655 self.profile
.is_some()
1658 pub fn features(&self) -> Option
<&BTreeMap
<InternedString
, Vec
<InternedString
>>> {
1659 self.features
.as_ref()
1663 /// Returns the name of the README file for a `TomlProject`.
1664 fn readme_for_project(package_root
: &Path
, project
: &TomlProject
) -> Option
<String
> {
1665 match &project
.readme
{
1666 None
=> default_readme_from_package_root(package_root
),
1667 Some(value
) => match value
{
1668 StringOrBool
::Bool(false) => None
,
1669 StringOrBool
::Bool(true) => Some("README.md".to_string()),
1670 StringOrBool
::String(v
) => Some(v
.clone()),
1675 const DEFAULT_README_FILES
: [&str; 3] = ["README.md", "README.txt", "README"];
1677 /// Checks if a file with any of the default README file names exists in the package root.
1678 /// If so, returns a `String` representing that name.
1679 fn default_readme_from_package_root(package_root
: &Path
) -> Option
<String
> {
1680 for &readme_filename
in DEFAULT_README_FILES
.iter() {
1681 if package_root
.join(readme_filename
).is_file() {
1682 return Some(readme_filename
.to_string());
1689 /// Checks a list of build targets, and ensures the target names are unique within a vector.
1690 /// If not, the name of the offending build target is returned.
1691 fn unique_build_targets(targets
: &[Target
], package_root
: &Path
) -> Result
<(), String
> {
1692 let mut seen
= HashSet
::new();
1693 for target
in targets
{
1694 if let TargetSourcePath
::Path(path
) = target
.src_path() {
1695 let full
= package_root
.join(path
);
1696 if !seen
.insert(full
.clone()) {
1697 return Err(full
.display().to_string());
1704 impl<P
: ResolveToPath
> TomlDependency
<P
> {
1705 pub(crate) fn to_dependency_split(
1708 source_id
: SourceId
,
1709 nested_paths
: &mut Vec
<PathBuf
>,
1711 warnings
: &mut Vec
<String
>,
1712 platform
: Option
<Platform
>,
1714 features
: &Features
,
1715 kind
: Option
<DepKind
>,
1716 ) -> CargoResult
<Dependency
> {
1720 deps
: &mut Vec
::new(),
1736 cx
: &mut Context
<'_
, '_
>,
1737 kind
: Option
<DepKind
>,
1738 ) -> CargoResult
<Dependency
> {
1740 TomlDependency
::Simple(ref version
) => DetailedTomlDependency
::<P
> {
1741 version
: Some(version
.clone()),
1742 ..Default
::default()
1744 .to_dependency(name
, cx
, kind
),
1745 TomlDependency
::Detailed(ref details
) => details
.to_dependency(name
, cx
, kind
),
1749 fn is_version_specified(&self) -> bool
{
1751 TomlDependency
::Detailed(d
) => d
.version
.is_some(),
1752 TomlDependency
::Simple(..) => true,
1757 impl<P
: ResolveToPath
> DetailedTomlDependency
<P
> {
1761 cx
: &mut Context
<'_
, '_
>,
1762 kind
: Option
<DepKind
>,
1763 ) -> CargoResult
<Dependency
> {
1764 if self.version
.is_none() && self.path
.is_none() && self.git
.is_none() {
1766 "dependency ({}) specified without \
1767 providing a local path, Git repository, or version to use.",
1772 if let Some(version
) = &self.version
{
1773 if version
.contains('
+'
) {
1774 cx
.warnings
.push(format
!(
1775 "version requirement `{}` for dependency `{}` \
1776 includes semver metadata which will be ignored, removing the \
1777 metadata is recommended to avoid confusion",
1778 version
, name_in_toml
1783 if self.git
.is_none() {
1784 let git_only_keys
= [
1785 (&self.branch
, "branch"),
1790 for &(key
, key_name
) in &git_only_keys
{
1793 "key `{}` is ignored for dependency ({}).",
1801 // Early detection of potentially misused feature syntax
1802 // instead of generating a "feature not found" error.
1803 if let Some(features
) = &self.features
{
1804 for feature
in features
{
1805 if feature
.contains('
/'
) {
1807 "feature `{}` in dependency `{}` is not allowed to contain slashes\n\
1808 If you want to enable features of a transitive dependency, \
1809 the direct dependency needs to re-export those features from \
1810 the `[features]` table.",
1815 if feature
.starts_with("dep:") {
1817 "feature `{}` in dependency `{}` is not allowed to use explicit \
1819 If you want to enable an optional dependency, specify the name \
1820 of the optional dependency without the `dep:` prefix, or specify \
1821 a feature from the dependency's `[features]` table that enables \
1822 the optional dependency.",
1830 let new_source_id
= match (
1833 self.registry
.as_ref(),
1834 self.registry_index
.as_ref(),
1836 (Some(_
), _
, Some(_
), _
) | (Some(_
), _
, _
, Some(_
)) => bail
!(
1837 "dependency ({}) specification is ambiguous. \
1838 Only one of `git` or `registry` is allowed.",
1841 (_
, _
, Some(_
), Some(_
)) => bail
!(
1842 "dependency ({}) specification is ambiguous. \
1843 Only one of `registry` or `registry-index` is allowed.",
1846 (Some(git
), maybe_path
, _
, _
) => {
1847 if maybe_path
.is_some() {
1849 "dependency ({}) specification is ambiguous. \
1850 Only one of `git` or `path` is allowed.",
1855 let n_details
= [&self.branch
, &self.tag
, &self.rev
]
1857 .filter(|d
| d
.is_some())
1862 "dependency ({}) specification is ambiguous. \
1863 Only one of `branch`, `tag` or `rev` is allowed.",
1868 let reference
= self
1871 .map(GitReference
::Branch
)
1872 .or_else(|| self.tag
.clone().map(GitReference
::Tag
))
1873 .or_else(|| self.rev
.clone().map(GitReference
::Rev
))
1874 .unwrap_or(GitReference
::DefaultBranch
);
1875 let loc
= git
.into_url()?
;
1877 if let Some(fragment
) = loc
.fragment() {
1879 "URL fragment `#{}` in git URL is ignored for dependency ({}). \
1880 If you were trying to specify a specific git revision, \
1881 use `rev = \"{}\"` in the dependency declaration.",
1882 fragment
, name_in_toml
, fragment
1884 cx
.warnings
.push(msg
)
1887 SourceId
::for_git(&loc
, reference
)?
1889 (None
, Some(path
), _
, _
) => {
1890 let path
= path
.resolve(cx
.config
);
1891 cx
.nested_paths
.push(path
.clone());
1892 // If the source ID for the package we're parsing is a path
1893 // source, then we normalize the path here to get rid of
1894 // components like `..`.
1896 // The purpose of this is to get a canonical ID for the package
1897 // that we're depending on to ensure that builds of this package
1898 // always end up hashing to the same value no matter where it's
1900 if cx
.source_id
.is_path() {
1901 let path
= cx
.root
.join(path
);
1902 let path
= paths
::normalize_path(&path
);
1903 SourceId
::for_path(&path
)?
1908 (None
, None
, Some(registry
), None
) => SourceId
::alt_registry(cx
.config
, registry
)?
,
1909 (None
, None
, None
, Some(registry_index
)) => {
1910 let url
= registry_index
.into_url()?
;
1911 SourceId
::for_registry(&url
)?
1913 (None
, None
, None
, None
) => SourceId
::crates_io(cx
.config
)?
,
1916 let (pkg_name
, explicit_name_in_toml
) = match self.package
{
1917 Some(ref s
) => (&s
[..], Some(name_in_toml
)),
1918 None
=> (name_in_toml
, None
),
1921 let version
= self.version
.as_deref();
1922 let mut dep
= Dependency
::parse(pkg_name
, version
, new_source_id
)?
;
1923 dep
.set_features(self.features
.iter().flatten())
1924 .set_default_features(
1925 self.default_features
1926 .or(self.default_features2
)
1929 .set_optional(self.optional
.unwrap_or(false))
1930 .set_platform(cx
.platform
.clone());
1931 if let Some(registry
) = &self.registry
{
1932 let registry_id
= SourceId
::alt_registry(cx
.config
, registry
)?
;
1933 dep
.set_registry_id(registry_id
);
1935 if let Some(registry_index
) = &self.registry_index
{
1936 let url
= registry_index
.into_url()?
;
1937 let registry_id
= SourceId
::for_registry(&url
)?
;
1938 dep
.set_registry_id(registry_id
);
1941 if let Some(kind
) = kind
{
1944 if let Some(name_in_toml
) = explicit_name_in_toml
{
1945 cx
.features
.require(Feature
::rename_dependency())?
;
1946 dep
.set_explicit_name_in_toml(name_in_toml
);
1949 if let Some(p
) = self.public
{
1950 cx
.features
.require(Feature
::public_dependency())?
;
1952 if dep
.kind() != DepKind
::Normal
{
1953 bail
!("'public' specifier can only be used on regular dependencies, not {:?} dependencies", dep
.kind());
1962 #[derive(Default, Serialize, Deserialize, Debug, Clone)]
1964 name
: Option
<String
>,
1966 // The intention was to only accept `crate-type` here but historical
1967 // versions of Cargo also accepted `crate_type`, so look for both.
1968 #[serde(rename = "crate-type")]
1969 crate_type
: Option
<Vec
<String
>>,
1970 #[serde(rename = "crate_type")]
1971 crate_type2
: Option
<Vec
<String
>>,
1973 path
: Option
<PathValue
>,
1974 // Note that `filename` is used for the cargo-feature `different_binary_name`
1975 filename
: Option
<String
>,
1977 doctest
: Option
<bool
>,
1978 bench
: Option
<bool
>,
1980 plugin
: Option
<bool
>,
1981 #[serde(rename = "proc-macro")]
1982 proc_macro_raw
: Option
<bool
>,
1983 #[serde(rename = "proc_macro")]
1984 proc_macro_raw2
: Option
<bool
>,
1985 harness
: Option
<bool
>,
1986 #[serde(rename = "required-features")]
1987 required_features
: Option
<Vec
<String
>>,
1988 edition
: Option
<String
>,
1992 struct PathValue(PathBuf
);
1994 impl<'de
> de
::Deserialize
<'de
> for PathValue
{
1995 fn deserialize
<D
>(deserializer
: D
) -> Result
<Self, D
::Error
>
1997 D
: de
::Deserializer
<'de
>,
1999 Ok(PathValue(String
::deserialize(deserializer
)?
.into()))
2003 impl ser
::Serialize
for PathValue
{
2004 fn serialize
<S
>(&self, serializer
: S
) -> Result
<S
::Ok
, S
::Error
>
2008 self.0.serialize(serializer
)
2012 /// Corresponds to a `target` entry, but `TomlTarget` is already used.
2013 #[derive(Serialize, Deserialize, Debug)]
2014 struct TomlPlatform
{
2015 dependencies
: Option
<BTreeMap
<String
, TomlDependency
>>,
2016 #[serde(rename = "build-dependencies")]
2017 build_dependencies
: Option
<BTreeMap
<String
, TomlDependency
>>,
2018 #[serde(rename = "build_dependencies")]
2019 build_dependencies2
: Option
<BTreeMap
<String
, TomlDependency
>>,
2020 #[serde(rename = "dev-dependencies")]
2021 dev_dependencies
: Option
<BTreeMap
<String
, TomlDependency
>>,
2022 #[serde(rename = "dev_dependencies")]
2023 dev_dependencies2
: Option
<BTreeMap
<String
, TomlDependency
>>,
2027 fn new() -> TomlTarget
{
2028 TomlTarget
::default()
2031 fn name(&self) -> String
{
2033 Some(ref name
) => name
.clone(),
2034 None
=> panic
!("target name is required"),
2038 fn proc_macro(&self) -> Option
<bool
> {
2039 self.proc_macro_raw
.or(self.proc_macro_raw2
).or_else(|| {
2040 if let Some(types
) = self.crate_types() {
2041 if types
.contains(&"proc-macro".to_string()) {
2049 fn crate_types(&self) -> Option
<&Vec
<String
>> {
2052 .or_else(|| self.crate_type2
.as_ref())
2056 impl fmt
::Debug
for PathValue
{
2057 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{