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
646 ) || lower_name
.starts_with("cargo")
649 "profile name `{}` is reserved\n\
650 Please choose a different name.\n\
660 fn validate_override(&self, which
: &str, features
: &Features
) -> CargoResult
<()> {
661 if self.package
.is_some() {
662 bail
!("package-specific profiles cannot be nested");
664 if self.build_override
.is_some() {
665 bail
!("build-override profiles cannot be nested");
667 if self.panic
.is_some() {
668 bail
!("`panic` may not be specified in a `{}` profile", which
)
670 if self.lto
.is_some() {
671 bail
!("`lto` may not be specified in a `{}` profile", which
)
673 if self.rpath
.is_some() {
674 bail
!("`rpath` may not be specified in a `{}` profile", which
)
676 if self.codegen_backend
.is_some() {
677 features
.require(Feature
::codegen_backend())?
;
682 /// Overwrite self's values with the given profile.
683 pub fn merge(&mut self, profile
: &TomlProfile
) {
684 if let Some(v
) = &profile
.opt_level
{
685 self.opt_level
= Some(v
.clone());
688 if let Some(v
) = &profile
.lto
{
689 self.lto
= Some(v
.clone());
692 if let Some(v
) = profile
.codegen_backend
{
693 self.codegen_backend
= Some(v
);
696 if let Some(v
) = profile
.codegen_units
{
697 self.codegen_units
= Some(v
);
700 if let Some(v
) = &profile
.debug
{
701 self.debug
= Some(v
.clone());
704 if let Some(v
) = profile
.debug_assertions
{
705 self.debug_assertions
= Some(v
);
708 if let Some(v
) = &profile
.split_debuginfo
{
709 self.split_debuginfo
= Some(v
.clone());
712 if let Some(v
) = profile
.rpath
{
713 self.rpath
= Some(v
);
716 if let Some(v
) = &profile
.panic
{
717 self.panic
= Some(v
.clone());
720 if let Some(v
) = profile
.overflow_checks
{
721 self.overflow_checks
= Some(v
);
724 if let Some(v
) = profile
.incremental
{
725 self.incremental
= Some(v
);
728 if let Some(other_package
) = &profile
.package
{
729 match &mut self.package
{
730 Some(self_package
) => {
731 for (spec
, other_pkg_profile
) in other_package
{
732 match self_package
.get_mut(spec
) {
733 Some(p
) => p
.merge(other_pkg_profile
),
735 self_package
.insert(spec
.clone(), other_pkg_profile
.clone());
740 None
=> self.package
= Some(other_package
.clone()),
744 if let Some(other_bo
) = &profile
.build_override
{
745 match &mut self.build_override
{
746 Some(self_bo
) => self_bo
.merge(other_bo
),
747 None
=> self.build_override
= Some(other_bo
.clone()),
751 if let Some(v
) = &profile
.inherits
{
752 self.inherits
= Some(*v
);
755 if let Some(v
) = &profile
.dir_name
{
756 self.dir_name
= Some(*v
);
759 if let Some(v
) = &profile
.strip
{
760 self.strip
= Some(v
.clone());
765 /// A StringOrVec can be parsed from either a TOML string or array,
766 /// but is always stored as a vector.
767 #[derive(Clone, Debug, Serialize, Eq, PartialEq, PartialOrd, Ord)]
768 pub struct StringOrVec(Vec
<String
>);
770 impl<'de
> de
::Deserialize
<'de
> for StringOrVec
{
771 fn deserialize
<D
>(deserializer
: D
) -> Result
<Self, D
::Error
>
773 D
: de
::Deserializer
<'de
>,
777 impl<'de
> de
::Visitor
<'de
> for Visitor
{
778 type Value
= StringOrVec
;
780 fn expecting(&self, formatter
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
781 formatter
.write_str("string or list of strings")
784 fn visit_str
<E
>(self, s
: &str) -> Result
<Self::Value
, E
>
788 Ok(StringOrVec(vec
![s
.to_string()]))
791 fn visit_seq
<V
>(self, v
: V
) -> Result
<Self::Value
, V
::Error
>
793 V
: de
::SeqAccess
<'de
>,
795 let seq
= de
::value
::SeqAccessDeserializer
::new(v
);
796 Vec
::deserialize(seq
).map(StringOrVec
)
800 deserializer
.deserialize_any(Visitor
)
805 pub fn iter
<'a
>(&'a
self) -> std
::slice
::Iter
<'a
, String
> {
810 #[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
811 #[serde(untagged, expecting = "expected a boolean or a string")]
812 pub enum StringOrBool
{
817 #[derive(PartialEq, Clone, Debug, Serialize)]
819 pub enum VecStringOrBool
{
820 VecString(Vec
<String
>),
824 impl<'de
> de
::Deserialize
<'de
> for VecStringOrBool
{
825 fn deserialize
<D
>(deserializer
: D
) -> Result
<Self, D
::Error
>
827 D
: de
::Deserializer
<'de
>,
831 impl<'de
> de
::Visitor
<'de
> for Visitor
{
832 type Value
= VecStringOrBool
;
834 fn expecting(&self, formatter
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
835 formatter
.write_str("a boolean or vector of strings")
838 fn visit_seq
<V
>(self, v
: V
) -> Result
<Self::Value
, V
::Error
>
840 V
: de
::SeqAccess
<'de
>,
842 let seq
= de
::value
::SeqAccessDeserializer
::new(v
);
843 Vec
::deserialize(seq
).map(VecStringOrBool
::VecString
)
846 fn visit_bool
<E
>(self, b
: bool
) -> Result
<Self::Value
, E
>
850 Ok(VecStringOrBool
::Bool(b
))
854 deserializer
.deserialize_any(Visitor
)
858 fn version_trim_whitespace
<'de
, D
>(deserializer
: D
) -> Result
<semver
::Version
, D
::Error
>
860 D
: de
::Deserializer
<'de
>,
864 impl<'de
> de
::Visitor
<'de
> for Visitor
{
865 type Value
= semver
::Version
;
867 fn expecting(&self, formatter
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
868 formatter
.write_str("SemVer version")
871 fn visit_str
<E
>(self, string
: &str) -> Result
<Self::Value
, E
>
875 string
.trim().parse().map_err(de
::Error
::custom
)
879 deserializer
.deserialize_str(Visitor
)
882 /// Represents the `package`/`project` sections of a `Cargo.toml`.
884 /// Note that the order of the fields matters, since this is the order they
885 /// are serialized to a TOML file. For example, you cannot have values after
886 /// the field `metadata`, since it is a table and values cannot appear after
888 #[derive(Deserialize, Serialize, Clone, Debug)]
889 #[serde(rename_all = "kebab-case")]
890 pub struct TomlProject
{
891 edition
: Option
<String
>,
892 rust_version
: Option
<String
>,
893 name
: InternedString
,
894 #[serde(deserialize_with = "version_trim_whitespace")]
895 version
: semver
::Version
,
896 authors
: Option
<Vec
<String
>>,
897 build
: Option
<StringOrBool
>,
898 metabuild
: Option
<StringOrVec
>,
899 #[serde(rename = "default-target")]
900 default_target
: Option
<String
>,
901 #[serde(rename = "forced-target")]
902 forced_target
: Option
<String
>,
903 links
: Option
<String
>,
904 exclude
: Option
<Vec
<String
>>,
905 include
: Option
<Vec
<String
>>,
906 publish
: Option
<VecStringOrBool
>,
907 workspace
: Option
<String
>,
908 im_a_teapot
: Option
<bool
>,
909 autobins
: Option
<bool
>,
910 autoexamples
: Option
<bool
>,
911 autotests
: Option
<bool
>,
912 autobenches
: Option
<bool
>,
913 default_run
: Option
<String
>,
916 description
: Option
<String
>,
917 homepage
: Option
<String
>,
918 documentation
: Option
<String
>,
919 readme
: Option
<StringOrBool
>,
920 keywords
: Option
<Vec
<String
>>,
921 categories
: Option
<Vec
<String
>>,
922 license
: Option
<String
>,
923 license_file
: Option
<String
>,
924 repository
: Option
<String
>,
925 resolver
: Option
<String
>,
927 // Note that this field must come last due to the way toml serialization
928 // works which requires tables to be emitted after all values.
929 metadata
: Option
<toml
::Value
>,
932 #[derive(Debug, Deserialize, Serialize)]
933 pub struct TomlWorkspace
{
934 members
: Option
<Vec
<String
>>,
935 #[serde(rename = "default-members")]
936 default_members
: Option
<Vec
<String
>>,
937 exclude
: Option
<Vec
<String
>>,
938 resolver
: Option
<String
>,
940 // Note that this field must come last due to the way toml serialization
941 // works which requires tables to be emitted after all values.
942 metadata
: Option
<toml
::Value
>,
946 pub fn to_package_id(&self, source_id
: SourceId
) -> CargoResult
<PackageId
> {
947 PackageId
::new(self.name
, self.version
.clone(), source_id
)
951 struct Context
<'a
, 'b
> {
952 deps
: &'a
mut Vec
<Dependency
>,
954 nested_paths
: &'a
mut Vec
<PathBuf
>,
956 warnings
: &'a
mut Vec
<String
>,
957 platform
: Option
<Platform
>,
959 features
: &'a Features
,
963 /// Prepares the manifest for publishing.
964 // - Path and git components of dependency specifications are removed.
965 // - License path is updated to point within the package.
966 pub fn prepare_for_publish(
970 ) -> CargoResult
<TomlManifest
> {
971 let config
= ws
.config();
972 let mut package
= self
975 .or_else(|| self.project
.as_ref())
978 package
.workspace
= None
;
979 package
.resolver
= ws
.resolve_behavior().to_manifest();
980 if let Some(license_file
) = &package
.license_file
{
981 let license_path
= Path
::new(&license_file
);
982 let abs_license_path
= paths
::normalize_path(&package_root
.join(license_path
));
983 if abs_license_path
.strip_prefix(package_root
).is_err() {
984 // This path points outside of the package root. `cargo package`
985 // will copy it into the root, so adjust the path to this location.
986 package
.license_file
= Some(
996 let all
= |_d
: &TomlDependency
| true;
997 return Ok(TomlManifest
{
998 package
: Some(package
),
1000 profile
: self.profile
.clone(),
1001 lib
: self.lib
.clone(),
1002 bin
: self.bin
.clone(),
1003 example
: self.example
.clone(),
1004 test
: self.test
.clone(),
1005 bench
: self.bench
.clone(),
1006 dependencies
: map_deps(config
, self.dependencies
.as_ref(), all
)?
,
1007 dev_dependencies
: map_deps(
1009 self.dev_dependencies
1011 .or_else(|| self.dev_dependencies2
.as_ref()),
1012 TomlDependency
::is_version_specified
,
1014 dev_dependencies2
: None
,
1015 build_dependencies
: map_deps(
1017 self.build_dependencies
1019 .or_else(|| self.build_dependencies2
.as_ref()),
1022 build_dependencies2
: None
,
1023 features
: self.features
.clone(),
1024 target
: match self.target
.as_ref().map(|target_map
| {
1031 dependencies
: map_deps(config
, v
.dependencies
.as_ref(), all
)?
,
1032 dev_dependencies
: map_deps(
1036 .or_else(|| v
.dev_dependencies2
.as_ref()),
1037 TomlDependency
::is_version_specified
,
1039 dev_dependencies2
: None
,
1040 build_dependencies
: map_deps(
1042 v
.build_dependencies
1044 .or_else(|| v
.build_dependencies2
.as_ref()),
1047 build_dependencies2
: None
,
1053 Some(Ok(v
)) => Some(v
),
1054 Some(Err(e
)) => return Err(e
),
1060 badges
: self.badges
.clone(),
1061 cargo_features
: self.cargo_features
.clone(),
1066 deps
: Option
<&BTreeMap
<String
, TomlDependency
>>,
1067 filter
: impl Fn(&TomlDependency
) -> bool
,
1068 ) -> CargoResult
<Option
<BTreeMap
<String
, TomlDependency
>>> {
1069 let deps
= match deps
{
1071 None
=> return Ok(None
),
1075 .filter(|(_k
, v
)| filter(v
))
1076 .map(|(k
, v
)| Ok((k
.clone(), map_dependency(config
, v
)?
)))
1077 .collect
::<CargoResult
<BTreeMap
<_
, _
>>>()?
;
1081 fn map_dependency(config
: &Config
, dep
: &TomlDependency
) -> CargoResult
<TomlDependency
> {
1083 TomlDependency
::Detailed(d
) => {
1084 let mut d
= d
.clone();
1085 // Path dependencies become crates.io deps.
1087 // Same with git dependencies.
1092 // registry specifications are elaborated to the index URL
1093 if let Some(registry
) = d
.registry
.take() {
1094 let src
= SourceId
::alt_registry(config
, ®istry
)?
;
1095 d
.registry_index
= Some(src
.url().to_string());
1097 Ok(TomlDependency
::Detailed(d
))
1099 TomlDependency
::Simple(s
) => Ok(TomlDependency
::Detailed(DetailedTomlDependency
{
1100 version
: Some(s
.clone()),
1101 ..Default
::default()
1107 pub fn to_real_manifest(
1108 me
: &Rc
<TomlManifest
>,
1109 source_id
: SourceId
,
1110 package_root
: &Path
,
1112 ) -> CargoResult
<(Manifest
, Vec
<PathBuf
>)> {
1113 let mut nested_paths
= vec
![];
1114 let mut warnings
= vec
![];
1115 let mut errors
= vec
![];
1117 // Parse features first so they will be available when parsing other parts of the TOML.
1118 let empty
= Vec
::new();
1119 let cargo_features
= me
.cargo_features
.as_ref().unwrap_or(&empty
);
1120 let features
= Features
::new(cargo_features
, config
, &mut warnings
, source_id
.is_path())?
;
1122 let project
= me
.project
.as_ref().or_else(|| me
.package
.as_ref());
1123 let project
= project
.ok_or_else(|| anyhow
!("no `package` section found"))?
;
1125 let package_name
= project
.name
.trim();
1126 if package_name
.is_empty() {
1127 bail
!("package name cannot be an empty string")
1130 validate_package_name(package_name
, "package name", "")?
;
1132 let pkgid
= project
.to_package_id(source_id
)?
;
1134 let edition
= if let Some(ref edition
) = project
.edition
{
1136 .require(Feature
::edition())
1137 .with_context(|| "editions are unstable")?
;
1140 .with_context(|| "failed to parse the `edition` key")?
1142 Edition
::Edition2015
1144 if edition
== Edition
::Edition2021
{
1145 features
.require(Feature
::edition2021())?
;
1146 } else if !edition
.is_stable() {
1147 // Guard in case someone forgets to add .require()
1148 return Err(util
::errors
::internal(format
!(
1149 "edition {} should be gated",
1154 let rust_version
= if let Some(rust_version
) = &project
.rust_version
{
1155 let req
= match semver
::VersionReq
::parse(rust_version
) {
1156 // Exclude semver operators like `^` and pre-release identifiers
1157 Ok(req
) if rust_version
.chars().all(|c
| c
.is_ascii_digit() || c
== '
.'
) => req
,
1158 _
=> bail
!("`rust-version` must be a value like \"1.32\""),
1160 if let Some(first_version
) = edition
.first_version() {
1162 semver
::Version
::new(first_version
.major
, first_version
.minor
- 1, 9999);
1163 if req
.matches(&unsupported
) {
1165 "rust-version {} is older than first version ({}) required by \
1166 the specified edition ({})",
1173 Some(rust_version
.clone())
1178 if project
.metabuild
.is_some() {
1179 features
.require(Feature
::metabuild())?
;
1182 if project
.resolver
.is_some()
1186 .map_or(false, |ws
| ws
.resolver
.is_some())
1188 features
.require(Feature
::resolver())?
;
1190 let resolve_behavior
= match (
1191 project
.resolver
.as_ref(),
1192 me
.workspace
.as_ref().and_then(|ws
| ws
.resolver
.as_ref()),
1194 (None
, None
) => None
,
1195 (Some(s
), None
) | (None
, Some(s
)) => Some(ResolveBehavior
::from_manifest(s
)?
),
1196 (Some(_
), Some(_
)) => {
1197 bail
!("cannot specify `resolver` field in both `[workspace]` and `[package]`")
1201 // If we have no lib at all, use the inferred lib, if available.
1202 // If we have a lib with a path, we're done.
1203 // If we have a lib with no path, use the inferred lib or else the package name.
1204 let targets
= targets(
1216 if targets
.is_empty() {
1217 debug
!("manifest has no build targets");
1220 if let Err(e
) = unique_build_targets(&targets
, package_root
) {
1221 warnings
.push(format
!(
1222 "file found to be present in multiple \
1228 if let Some(links
) = &project
.links
{
1229 if !targets
.iter().any(|t
| t
.is_custom_build()) {
1231 "package `{}` specifies that it links to `{}` but does not \
1232 have a custom build script",
1239 let mut deps
= Vec
::new();
1244 let mut cx
= Context
{
1247 nested_paths
: &mut nested_paths
,
1249 warnings
: &mut warnings
,
1250 features
: &features
,
1255 fn process_dependencies(
1256 cx
: &mut Context
<'_
, '_
>,
1257 new_deps
: Option
<&BTreeMap
<String
, TomlDependency
>>,
1258 kind
: Option
<DepKind
>,
1259 ) -> CargoResult
<()> {
1260 let dependencies
= match new_deps
{
1261 Some(dependencies
) => dependencies
,
1262 None
=> return Ok(()),
1264 for (n
, v
) in dependencies
.iter() {
1265 let dep
= v
.to_dependency(n
, cx
, kind
)?
;
1266 validate_package_name(dep
.name_in_toml().as_str(), "dependency name", "")?
;
1273 // Collect the dependencies.
1274 process_dependencies(&mut cx
, me
.dependencies
.as_ref(), None
)?
;
1278 .or_else(|| me
.dev_dependencies2
.as_ref());
1279 process_dependencies(&mut cx
, dev_deps
, Some(DepKind
::Development
))?
;
1283 .or_else(|| me
.build_dependencies2
.as_ref());
1284 process_dependencies(&mut cx
, build_deps
, Some(DepKind
::Build
))?
;
1286 for (name
, platform
) in me
.target
.iter().flatten() {
1288 let platform
: Platform
= name
.parse()?
;
1289 platform
.check_cfg_attributes(&mut cx
.warnings
);
1292 process_dependencies(&mut cx
, platform
.dependencies
.as_ref(), None
)?
;
1293 let build_deps
= platform
1296 .or_else(|| platform
.build_dependencies2
.as_ref());
1297 process_dependencies(&mut cx
, build_deps
, Some(DepKind
::Build
))?
;
1298 let dev_deps
= platform
1301 .or_else(|| platform
.dev_dependencies2
.as_ref());
1302 process_dependencies(&mut cx
, dev_deps
, Some(DepKind
::Development
))?
;
1305 replace
= me
.replace(&mut cx
)?
;
1306 patch
= me
.patch(&mut cx
)?
;
1310 let mut names_sources
= BTreeMap
::new();
1312 let name
= dep
.name_in_toml();
1313 let prev
= names_sources
.insert(name
.to_string(), dep
.source_id());
1314 if prev
.is_some() && prev
!= Some(dep
.source_id()) {
1316 "Dependency '{}' has different source paths depending on the build \
1317 target. Each dependency must have a single canonical source path \
1318 irrespective of build target.",
1325 let exclude
= project
.exclude
.clone().unwrap_or_default();
1326 let include
= project
.include
.clone().unwrap_or_default();
1327 let empty_features
= BTreeMap
::new();
1329 let summary
= Summary
::new(
1333 me
.features
.as_ref().unwrap_or(&empty_features
),
1334 project
.links
.as_deref(),
1336 let unstable
= config
.cli_unstable();
1337 summary
.unstable_gate(unstable
.namespaced_features
, unstable
.weak_dep_features
)?
;
1339 let metadata
= ManifestMetadata
{
1340 description
: project
.description
.clone(),
1341 homepage
: project
.homepage
.clone(),
1342 documentation
: project
.documentation
.clone(),
1343 readme
: readme_for_project(package_root
, project
),
1344 authors
: project
.authors
.clone().unwrap_or_default(),
1345 license
: project
.license
.clone(),
1346 license_file
: project
.license_file
.clone(),
1347 repository
: project
.repository
.clone(),
1348 keywords
: project
.keywords
.clone().unwrap_or_default(),
1349 categories
: project
.categories
.clone().unwrap_or_default(),
1350 badges
: me
.badges
.clone().unwrap_or_default(),
1351 links
: project
.links
.clone(),
1354 let workspace_config
= match (me
.workspace
.as_ref(), project
.workspace
.as_ref()) {
1355 (Some(config
), None
) => WorkspaceConfig
::Root(WorkspaceRootConfig
::new(
1358 &config
.default_members
,
1362 (None
, root
) => WorkspaceConfig
::Member
{
1363 root
: root
.cloned(),
1365 (Some(..), Some(..)) => bail
!(
1366 "cannot configure both `package.workspace` and \
1367 `[workspace]`, only one can be specified"
1370 let profiles
= me
.profile
.clone();
1371 if let Some(profiles
) = &profiles
{
1372 profiles
.validate(&features
, &mut warnings
)?
;
1374 let publish
= match project
.publish
{
1375 Some(VecStringOrBool
::VecString(ref vecstring
)) => Some(vecstring
.clone()),
1376 Some(VecStringOrBool
::Bool(false)) => Some(vec
![]),
1377 None
| Some(VecStringOrBool
::Bool(true)) => None
,
1380 if summary
.features().contains_key("default-features") {
1382 "`default-features = [\"..\"]` was found in [features]. \
1383 Did you mean to use `default = [\"..\"]`?"
1388 if let Some(run
) = &project
.default_run
{
1391 .filter(|t
| t
.is_bin())
1392 .any(|t
| t
.name() == run
)
1395 util
::closest_msg(run
, targets
.iter().filter(|t
| t
.is_bin()), |t
| t
.name());
1396 bail
!("default-run target `{}` not found{}", run
, suggestion
);
1400 let default_kind
= project
1403 .map(|t
| CompileTarget
::new(&*t
))
1405 .map(CompileKind
::Target
);
1406 let forced_kind
= project
1409 .map(|t
| CompileTarget
::new(&*t
))
1411 .map(CompileKind
::Target
);
1413 let custom_metadata
= project
.metadata
.clone();
1414 let mut manifest
= Manifest
::new(
1421 project
.links
.clone(),
1432 project
.im_a_teapot
,
1433 project
.default_run
.clone(),
1435 project
.metabuild
.clone().map(|sov
| sov
.0),
1438 if project
.license_file
.is_some() && project
.license
.is_some() {
1439 manifest
.warnings_mut().add_warning(
1440 "only one of `license` or \
1441 `license-file` is necessary"
1445 for warning
in warnings
{
1446 manifest
.warnings_mut().add_warning(warning
);
1448 for error
in errors
{
1449 manifest
.warnings_mut().add_critical_warning(error
);
1452 manifest
.feature_gate()?
;
1454 Ok((manifest
, nested_paths
))
1457 fn to_virtual_manifest(
1458 me
: &Rc
<TomlManifest
>,
1459 source_id
: SourceId
,
1462 ) -> CargoResult
<(VirtualManifest
, Vec
<PathBuf
>)> {
1463 if me
.project
.is_some() {
1464 bail
!("this virtual manifest specifies a [project] section, which is not allowed");
1466 if me
.package
.is_some() {
1467 bail
!("this virtual manifest specifies a [package] section, which is not allowed");
1469 if me
.lib
.is_some() {
1470 bail
!("this virtual manifest specifies a [lib] section, which is not allowed");
1472 if me
.bin
.is_some() {
1473 bail
!("this virtual manifest specifies a [[bin]] section, which is not allowed");
1475 if me
.example
.is_some() {
1476 bail
!("this virtual manifest specifies a [[example]] section, which is not allowed");
1478 if me
.test
.is_some() {
1479 bail
!("this virtual manifest specifies a [[test]] section, which is not allowed");
1481 if me
.bench
.is_some() {
1482 bail
!("this virtual manifest specifies a [[bench]] section, which is not allowed");
1484 if me
.dependencies
.is_some() {
1485 bail
!("this virtual manifest specifies a [dependencies] section, which is not allowed");
1487 if me
.dev_dependencies
.is_some() || me
.dev_dependencies2
.is_some() {
1488 bail
!("this virtual manifest specifies a [dev-dependencies] section, which is not allowed");
1490 if me
.build_dependencies
.is_some() || me
.build_dependencies2
.is_some() {
1491 bail
!("this virtual manifest specifies a [build-dependencies] section, which is not allowed");
1493 if me
.features
.is_some() {
1494 bail
!("this virtual manifest specifies a [features] section, which is not allowed");
1496 if me
.target
.is_some() {
1497 bail
!("this virtual manifest specifies a [target] section, which is not allowed");
1499 if me
.badges
.is_some() {
1500 bail
!("this virtual manifest specifies a [badges] section, which is not allowed");
1503 let mut nested_paths
= Vec
::new();
1504 let mut warnings
= Vec
::new();
1505 let mut deps
= Vec
::new();
1506 let empty
= Vec
::new();
1507 let cargo_features
= me
.cargo_features
.as_ref().unwrap_or(&empty
);
1508 let features
= Features
::new(cargo_features
, config
, &mut warnings
, source_id
.is_path())?
;
1510 let (replace
, patch
) = {
1511 let mut cx
= Context
{
1514 nested_paths
: &mut nested_paths
,
1516 warnings
: &mut warnings
,
1518 features
: &features
,
1521 (me
.replace(&mut cx
)?
, me
.patch(&mut cx
)?
)
1523 let profiles
= me
.profile
.clone();
1524 if let Some(profiles
) = &profiles
{
1525 profiles
.validate(&features
, &mut warnings
)?
;
1530 .map_or(false, |ws
| ws
.resolver
.is_some())
1532 features
.require(Feature
::resolver())?
;
1534 let resolve_behavior
= me
1537 .and_then(|ws
| ws
.resolver
.as_deref())
1538 .map(|r
| ResolveBehavior
::from_manifest(r
))
1540 let workspace_config
= match me
.workspace
{
1541 Some(ref config
) => WorkspaceConfig
::Root(WorkspaceRootConfig
::new(
1544 &config
.default_members
,
1549 bail
!("virtual manifests must be configured with [workspace]");
1553 VirtualManifest
::new(
1565 fn replace(&self, cx
: &mut Context
<'_
, '_
>) -> CargoResult
<Vec
<(PackageIdSpec
, Dependency
)>> {
1566 if self.patch
.is_some() && self.replace
.is_some() {
1567 bail
!("cannot specify both [replace] and [patch]");
1569 let mut replace
= Vec
::new();
1570 for (spec
, replacement
) in self.replace
.iter().flatten() {
1571 let mut spec
= PackageIdSpec
::parse(spec
).with_context(|| {
1573 "replacements must specify a valid semver \
1574 version to replace, but `{}` does not",
1578 if spec
.url().is_none() {
1579 spec
.set_url(CRATES_IO_INDEX
.parse().unwrap());
1582 if replacement
.is_version_specified() {
1584 "replacements cannot specify a version \
1585 requirement, but found one for `{}`",
1590 let mut dep
= replacement
.to_dependency(spec
.name().as_str(), cx
, None
)?
;
1592 let version
= spec
.version().ok_or_else(|| {
1594 "replacements must specify a version \
1595 to replace, but `{}` does not",
1599 dep
.set_version_req(VersionReq
::exact(version
));
1601 replace
.push((spec
, dep
));
1606 fn patch(&self, cx
: &mut Context
<'_
, '_
>) -> CargoResult
<HashMap
<Url
, Vec
<Dependency
>>> {
1607 let mut patch
= HashMap
::new();
1608 for (url
, deps
) in self.patch
.iter().flatten() {
1609 let url
= match &url
[..] {
1610 CRATES_IO_REGISTRY
=> CRATES_IO_INDEX
.parse().unwrap(),
1613 .get_registry_index(url
)
1614 .or_else(|_
| url
.into_url())
1616 format
!("[patch] entry `{}` should be a URL or registry name", url
)
1622 .map(|(name
, dep
)| dep
.to_dependency(name
, cx
, None
))
1623 .collect
::<CargoResult
<Vec
<_
>>>()?
,
1629 /// Returns the path to the build script if one exists for this crate.
1630 fn maybe_custom_build(
1632 build
: &Option
<StringOrBool
>,
1633 package_root
: &Path
,
1634 ) -> Option
<PathBuf
> {
1635 let build_rs
= package_root
.join("build.rs");
1637 // Explicitly no build script.
1638 Some(StringOrBool
::Bool(false)) => None
,
1639 Some(StringOrBool
::Bool(true)) => Some(build_rs
),
1640 Some(StringOrBool
::String(ref s
)) => Some(PathBuf
::from(s
)),
1642 // If there is a `build.rs` file next to the `Cargo.toml`, assume it is
1644 if build_rs
.is_file() {
1653 pub fn has_profiles(&self) -> bool
{
1654 self.profile
.is_some()
1657 pub fn features(&self) -> Option
<&BTreeMap
<InternedString
, Vec
<InternedString
>>> {
1658 self.features
.as_ref()
1662 /// Returns the name of the README file for a `TomlProject`.
1663 fn readme_for_project(package_root
: &Path
, project
: &TomlProject
) -> Option
<String
> {
1664 match &project
.readme
{
1665 None
=> default_readme_from_package_root(package_root
),
1666 Some(value
) => match value
{
1667 StringOrBool
::Bool(false) => None
,
1668 StringOrBool
::Bool(true) => Some("README.md".to_string()),
1669 StringOrBool
::String(v
) => Some(v
.clone()),
1674 const DEFAULT_README_FILES
: [&str; 3] = ["README.md", "README.txt", "README"];
1676 /// Checks if a file with any of the default README file names exists in the package root.
1677 /// If so, returns a `String` representing that name.
1678 fn default_readme_from_package_root(package_root
: &Path
) -> Option
<String
> {
1679 for &readme_filename
in DEFAULT_README_FILES
.iter() {
1680 if package_root
.join(readme_filename
).is_file() {
1681 return Some(readme_filename
.to_string());
1688 /// Checks a list of build targets, and ensures the target names are unique within a vector.
1689 /// If not, the name of the offending build target is returned.
1690 fn unique_build_targets(targets
: &[Target
], package_root
: &Path
) -> Result
<(), String
> {
1691 let mut seen
= HashSet
::new();
1692 for target
in targets
{
1693 if let TargetSourcePath
::Path(path
) = target
.src_path() {
1694 let full
= package_root
.join(path
);
1695 if !seen
.insert(full
.clone()) {
1696 return Err(full
.display().to_string());
1703 impl<P
: ResolveToPath
> TomlDependency
<P
> {
1704 pub(crate) fn to_dependency_split(
1707 source_id
: SourceId
,
1708 nested_paths
: &mut Vec
<PathBuf
>,
1710 warnings
: &mut Vec
<String
>,
1711 platform
: Option
<Platform
>,
1713 features
: &Features
,
1714 kind
: Option
<DepKind
>,
1715 ) -> CargoResult
<Dependency
> {
1719 deps
: &mut Vec
::new(),
1735 cx
: &mut Context
<'_
, '_
>,
1736 kind
: Option
<DepKind
>,
1737 ) -> CargoResult
<Dependency
> {
1739 TomlDependency
::Simple(ref version
) => DetailedTomlDependency
::<P
> {
1740 version
: Some(version
.clone()),
1741 ..Default
::default()
1743 .to_dependency(name
, cx
, kind
),
1744 TomlDependency
::Detailed(ref details
) => details
.to_dependency(name
, cx
, kind
),
1748 fn is_version_specified(&self) -> bool
{
1750 TomlDependency
::Detailed(d
) => d
.version
.is_some(),
1751 TomlDependency
::Simple(..) => true,
1756 impl<P
: ResolveToPath
> DetailedTomlDependency
<P
> {
1760 cx
: &mut Context
<'_
, '_
>,
1761 kind
: Option
<DepKind
>,
1762 ) -> CargoResult
<Dependency
> {
1763 if self.version
.is_none() && self.path
.is_none() && self.git
.is_none() {
1765 "dependency ({}) specified without \
1766 providing a local path, Git repository, or version to use.",
1771 if let Some(version
) = &self.version
{
1772 if version
.contains('
+'
) {
1773 cx
.warnings
.push(format
!(
1774 "version requirement `{}` for dependency `{}` \
1775 includes semver metadata which will be ignored, removing the \
1776 metadata is recommended to avoid confusion",
1777 version
, name_in_toml
1782 if self.git
.is_none() {
1783 let git_only_keys
= [
1784 (&self.branch
, "branch"),
1789 for &(key
, key_name
) in &git_only_keys
{
1792 "key `{}` is ignored for dependency ({}).",
1800 // Early detection of potentially misused feature syntax
1801 // instead of generating a "feature not found" error.
1802 if let Some(features
) = &self.features
{
1803 for feature
in features
{
1804 if feature
.contains('
/'
) {
1806 "feature `{}` in dependency `{}` is not allowed to contain slashes\n\
1807 If you want to enable features of a transitive dependency, \
1808 the direct dependency needs to re-export those features from \
1809 the `[features]` table.",
1814 if feature
.starts_with("dep:") {
1816 "feature `{}` in dependency `{}` is not allowed to use explicit \
1818 If you want to enable an optional dependency, specify the name \
1819 of the optional dependency without the `dep:` prefix, or specify \
1820 a feature from the dependency's `[features]` table that enables \
1821 the optional dependency.",
1829 let new_source_id
= match (
1832 self.registry
.as_ref(),
1833 self.registry_index
.as_ref(),
1835 (Some(_
), _
, Some(_
), _
) | (Some(_
), _
, _
, Some(_
)) => bail
!(
1836 "dependency ({}) specification is ambiguous. \
1837 Only one of `git` or `registry` is allowed.",
1840 (_
, _
, Some(_
), Some(_
)) => bail
!(
1841 "dependency ({}) specification is ambiguous. \
1842 Only one of `registry` or `registry-index` is allowed.",
1845 (Some(git
), maybe_path
, _
, _
) => {
1846 if maybe_path
.is_some() {
1848 "dependency ({}) specification is ambiguous. \
1849 Only one of `git` or `path` is allowed.",
1854 let n_details
= [&self.branch
, &self.tag
, &self.rev
]
1856 .filter(|d
| d
.is_some())
1861 "dependency ({}) specification is ambiguous. \
1862 Only one of `branch`, `tag` or `rev` is allowed.",
1867 let reference
= self
1870 .map(GitReference
::Branch
)
1871 .or_else(|| self.tag
.clone().map(GitReference
::Tag
))
1872 .or_else(|| self.rev
.clone().map(GitReference
::Rev
))
1873 .unwrap_or(GitReference
::DefaultBranch
);
1874 let loc
= git
.into_url()?
;
1876 if let Some(fragment
) = loc
.fragment() {
1878 "URL fragment `#{}` in git URL is ignored for dependency ({}). \
1879 If you were trying to specify a specific git revision, \
1880 use `rev = \"{}\"` in the dependency declaration.",
1881 fragment
, name_in_toml
, fragment
1883 cx
.warnings
.push(msg
)
1886 SourceId
::for_git(&loc
, reference
)?
1888 (None
, Some(path
), _
, _
) => {
1889 let path
= path
.resolve(cx
.config
);
1890 cx
.nested_paths
.push(path
.clone());
1891 // If the source ID for the package we're parsing is a path
1892 // source, then we normalize the path here to get rid of
1893 // components like `..`.
1895 // The purpose of this is to get a canonical ID for the package
1896 // that we're depending on to ensure that builds of this package
1897 // always end up hashing to the same value no matter where it's
1899 if cx
.source_id
.is_path() {
1900 let path
= cx
.root
.join(path
);
1901 let path
= paths
::normalize_path(&path
);
1902 SourceId
::for_path(&path
)?
1907 (None
, None
, Some(registry
), None
) => SourceId
::alt_registry(cx
.config
, registry
)?
,
1908 (None
, None
, None
, Some(registry_index
)) => {
1909 let url
= registry_index
.into_url()?
;
1910 SourceId
::for_registry(&url
)?
1912 (None
, None
, None
, None
) => SourceId
::crates_io(cx
.config
)?
,
1915 let (pkg_name
, explicit_name_in_toml
) = match self.package
{
1916 Some(ref s
) => (&s
[..], Some(name_in_toml
)),
1917 None
=> (name_in_toml
, None
),
1920 let version
= self.version
.as_deref();
1921 let mut dep
= Dependency
::parse(pkg_name
, version
, new_source_id
)?
;
1922 dep
.set_features(self.features
.iter().flatten())
1923 .set_default_features(
1924 self.default_features
1925 .or(self.default_features2
)
1928 .set_optional(self.optional
.unwrap_or(false))
1929 .set_platform(cx
.platform
.clone());
1930 if let Some(registry
) = &self.registry
{
1931 let registry_id
= SourceId
::alt_registry(cx
.config
, registry
)?
;
1932 dep
.set_registry_id(registry_id
);
1934 if let Some(registry_index
) = &self.registry_index
{
1935 let url
= registry_index
.into_url()?
;
1936 let registry_id
= SourceId
::for_registry(&url
)?
;
1937 dep
.set_registry_id(registry_id
);
1940 if let Some(kind
) = kind
{
1943 if let Some(name_in_toml
) = explicit_name_in_toml
{
1944 cx
.features
.require(Feature
::rename_dependency())?
;
1945 dep
.set_explicit_name_in_toml(name_in_toml
);
1948 if let Some(p
) = self.public
{
1949 cx
.features
.require(Feature
::public_dependency())?
;
1951 if dep
.kind() != DepKind
::Normal
{
1952 bail
!("'public' specifier can only be used on regular dependencies, not {:?} dependencies", dep
.kind());
1961 #[derive(Default, Serialize, Deserialize, Debug, Clone)]
1963 name
: Option
<String
>,
1965 // The intention was to only accept `crate-type` here but historical
1966 // versions of Cargo also accepted `crate_type`, so look for both.
1967 #[serde(rename = "crate-type")]
1968 crate_type
: Option
<Vec
<String
>>,
1969 #[serde(rename = "crate_type")]
1970 crate_type2
: Option
<Vec
<String
>>,
1972 path
: Option
<PathValue
>,
1973 // Note that `filename` is used for the cargo-feature `different_binary_name`
1974 filename
: Option
<String
>,
1976 doctest
: Option
<bool
>,
1977 bench
: Option
<bool
>,
1979 plugin
: Option
<bool
>,
1980 #[serde(rename = "proc-macro")]
1981 proc_macro_raw
: Option
<bool
>,
1982 #[serde(rename = "proc_macro")]
1983 proc_macro_raw2
: Option
<bool
>,
1984 harness
: Option
<bool
>,
1985 #[serde(rename = "required-features")]
1986 required_features
: Option
<Vec
<String
>>,
1987 edition
: Option
<String
>,
1991 struct PathValue(PathBuf
);
1993 impl<'de
> de
::Deserialize
<'de
> for PathValue
{
1994 fn deserialize
<D
>(deserializer
: D
) -> Result
<Self, D
::Error
>
1996 D
: de
::Deserializer
<'de
>,
1998 Ok(PathValue(String
::deserialize(deserializer
)?
.into()))
2002 impl ser
::Serialize
for PathValue
{
2003 fn serialize
<S
>(&self, serializer
: S
) -> Result
<S
::Ok
, S
::Error
>
2007 self.0.serialize(serializer
)
2011 /// Corresponds to a `target` entry, but `TomlTarget` is already used.
2012 #[derive(Serialize, Deserialize, Debug)]
2013 struct TomlPlatform
{
2014 dependencies
: Option
<BTreeMap
<String
, TomlDependency
>>,
2015 #[serde(rename = "build-dependencies")]
2016 build_dependencies
: Option
<BTreeMap
<String
, TomlDependency
>>,
2017 #[serde(rename = "build_dependencies")]
2018 build_dependencies2
: Option
<BTreeMap
<String
, TomlDependency
>>,
2019 #[serde(rename = "dev-dependencies")]
2020 dev_dependencies
: Option
<BTreeMap
<String
, TomlDependency
>>,
2021 #[serde(rename = "dev_dependencies")]
2022 dev_dependencies2
: Option
<BTreeMap
<String
, TomlDependency
>>,
2026 fn new() -> TomlTarget
{
2027 TomlTarget
::default()
2030 fn name(&self) -> String
{
2032 Some(ref name
) => name
.clone(),
2033 None
=> panic
!("target name is required"),
2037 fn proc_macro(&self) -> Option
<bool
> {
2038 self.proc_macro_raw
.or(self.proc_macro_raw2
).or_else(|| {
2039 if let Some(types
) = self.crate_types() {
2040 if types
.contains(&"proc-macro".to_string()) {
2048 fn crate_types(&self) -> Option
<&Vec
<String
>> {
2051 .or_else(|| self.crate_type2
.as_ref())
2055 impl fmt
::Debug
for PathValue
{
2056 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{