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 read_manifest_from_str(&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()))
60 /// Parse an already-loaded `Cargo.toml` as a Cargo manifest.
62 /// This could result in a real or virtual manifest being returned.
64 /// A list of nested paths is also returned, one for each path dependency
65 /// within the manifest. For virtual manifests, these paths can only
66 /// come from patched or replaced dependencies. These paths are not
68 pub fn read_manifest_from_str(
73 ) -> CargoResult
<(EitherManifest
, Vec
<PathBuf
>)> {
74 let package_root
= manifest_file
.parent().unwrap();
77 let pretty_filename
= manifest_file
78 .strip_prefix(config
.cwd())
79 .unwrap_or(manifest_file
);
80 parse(contents
, pretty_filename
, config
)?
83 // Provide a helpful error message for a common user error.
84 if let Some(package
) = toml
.get("package").or_else(|| toml
.get("project")) {
85 if let Some(feats
) = package
.get("cargo-features") {
87 "cargo-features = {} was found in the wrong location: it \
88 should be set at the top of Cargo.toml before any tables",
89 toml
::to_string(feats
).unwrap()
94 let mut unused
= BTreeSet
::new();
95 let manifest
: TomlManifest
= serde_ignored
::deserialize(toml
, |path
| {
96 let mut key
= String
::new();
97 stringify(&mut key
, &path
);
100 let add_unused
= |warnings
: &mut Warnings
| {
102 warnings
.add_warning(format
!("unused manifest key: {}", key
));
103 if key
== "profiles.debug" {
104 warnings
.add_warning("use `[profile.dev]` to configure debug builds".to_string());
109 let manifest
= Rc
::new(manifest
);
110 return if manifest
.project
.is_some() || manifest
.package
.is_some() {
111 let (mut manifest
, paths
) =
112 TomlManifest
::to_real_manifest(&manifest
, source_id
, package_root
, config
)?
;
113 add_unused(manifest
.warnings_mut());
114 if manifest
.targets().iter().all(|t
| t
.is_custom_build()) {
116 "no targets specified in the manifest\n\
117 either src/lib.rs, src/main.rs, a [lib] section, or \
118 [[bin]] section must be present"
121 Ok((EitherManifest
::Real(manifest
), paths
))
124 TomlManifest
::to_virtual_manifest(&manifest
, source_id
, package_root
, config
)?
;
125 add_unused(m
.warnings_mut());
126 Ok((EitherManifest
::Virtual(m
), paths
))
129 fn stringify(dst
: &mut String
, path
: &serde_ignored
::Path
<'_
>) {
130 use serde_ignored
::Path
;
134 Path
::Seq { parent, index }
=> {
135 stringify(dst
, parent
);
139 dst
.push_str(&index
.to_string());
141 Path
::Map { parent, ref key }
=> {
142 stringify(dst
, parent
);
148 Path
::Some { parent }
149 | Path
::NewtypeVariant { parent }
150 | Path
::NewtypeStruct { parent }
=> stringify(dst
, parent
),
155 /// Attempts to parse a string into a [`toml::Value`]. This is not specific to any
156 /// particular kind of TOML file.
158 /// The purpose of this wrapper is to detect invalid TOML which was previously
159 /// accepted and display a warning to the user in that case. The `file` and `config`
160 /// parameters are only used by this fallback path.
161 pub fn parse(toml
: &str, _file
: &Path
, _config
: &Config
) -> CargoResult
<toml
::Value
> {
162 // At the moment, no compatibility checks are needed.
164 .map_err(|e
| anyhow
::Error
::from(e
).context("could not parse input as TOML"))
167 type TomlLibTarget
= TomlTarget
;
168 type TomlBinTarget
= TomlTarget
;
169 type TomlExampleTarget
= TomlTarget
;
170 type TomlTestTarget
= TomlTarget
;
171 type TomlBenchTarget
= TomlTarget
;
173 #[derive(Clone, Debug, Serialize)]
175 pub enum TomlDependency
<P
= String
> {
176 /// In the simple format, only a version is specified, eg.
177 /// `package = "<version>"`
179 /// The simple format is equivalent to a detailed dependency
180 /// specifying only a version, eg.
181 /// `package = { version = "<version>" }`
182 Detailed(DetailedTomlDependency
<P
>),
185 impl<'de
, P
: Deserialize
<'de
>> de
::Deserialize
<'de
> for TomlDependency
<P
> {
186 fn deserialize
<D
>(deserializer
: D
) -> Result
<Self, D
::Error
>
188 D
: de
::Deserializer
<'de
>,
190 struct TomlDependencyVisitor
<P
>(PhantomData
<P
>);
192 impl<'de
, P
: Deserialize
<'de
>> de
::Visitor
<'de
> for TomlDependencyVisitor
<P
> {
193 type Value
= TomlDependency
<P
>;
195 fn expecting(&self, formatter
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
197 "a version string like \"0.9.8\" or a \
198 detailed dependency like { version = \"0.9.8\" }",
202 fn visit_str
<E
>(self, s
: &str) -> Result
<Self::Value
, E
>
206 Ok(TomlDependency
::Simple(s
.to_owned()))
209 fn visit_map
<V
>(self, map
: V
) -> Result
<Self::Value
, V
::Error
>
211 V
: de
::MapAccess
<'de
>,
213 let mvd
= de
::value
::MapAccessDeserializer
::new(map
);
214 DetailedTomlDependency
::deserialize(mvd
).map(TomlDependency
::Detailed
)
218 deserializer
.deserialize_any(TomlDependencyVisitor(PhantomData
))
222 pub trait ResolveToPath
{
223 fn resolve(&self, config
: &Config
) -> PathBuf
;
226 impl ResolveToPath
for String
{
227 fn resolve(&self, _
: &Config
) -> PathBuf
{
232 impl ResolveToPath
for ConfigRelativePath
{
233 fn resolve(&self, c
: &Config
) -> PathBuf
{
238 #[derive(Deserialize, Serialize, Clone, Debug)]
239 #[serde(rename_all = "kebab-case")]
240 pub struct DetailedTomlDependency
<P
= String
> {
241 version
: Option
<String
>,
242 registry
: Option
<String
>,
243 /// The URL of the `registry` field.
244 /// This is an internal implementation detail. When Cargo creates a
245 /// package, it replaces `registry` with `registry-index` so that the
246 /// manifest contains the correct URL. All users won't have the same
247 /// registry names configured, so Cargo can't rely on just the name for
248 /// crates published by other users.
249 registry_index
: Option
<String
>,
250 // `path` is relative to the file it appears in. If that's a `Cargo.toml`, it'll be relative to
251 // that TOML file, and if it's a `.cargo/config` file, it'll be relative to that file.
254 branch
: Option
<String
>,
257 features
: Option
<Vec
<String
>>,
258 optional
: Option
<bool
>,
259 default_features
: Option
<bool
>,
260 #[serde(rename = "default_features")]
261 default_features2
: Option
<bool
>,
262 package
: Option
<String
>,
263 public
: Option
<bool
>,
266 // Explicit implementation so we avoid pulling in P: Default
267 impl<P
> Default
for DetailedTomlDependency
<P
> {
268 fn default() -> Self {
270 version
: Default
::default(),
271 registry
: Default
::default(),
272 registry_index
: Default
::default(),
273 path
: Default
::default(),
274 git
: Default
::default(),
275 branch
: Default
::default(),
276 tag
: Default
::default(),
277 rev
: Default
::default(),
278 features
: Default
::default(),
279 optional
: Default
::default(),
280 default_features
: Default
::default(),
281 default_features2
: Default
::default(),
282 package
: Default
::default(),
283 public
: Default
::default(),
288 /// This type is used to deserialize `Cargo.toml` files.
289 #[derive(Debug, Deserialize, Serialize)]
290 #[serde(rename_all = "kebab-case")]
291 pub struct TomlManifest
{
292 cargo_features
: Option
<Vec
<String
>>,
293 package
: Option
<Box
<TomlProject
>>,
294 project
: Option
<Box
<TomlProject
>>,
295 profile
: Option
<TomlProfiles
>,
296 lib
: Option
<TomlLibTarget
>,
297 bin
: Option
<Vec
<TomlBinTarget
>>,
298 example
: Option
<Vec
<TomlExampleTarget
>>,
299 test
: Option
<Vec
<TomlTestTarget
>>,
300 bench
: Option
<Vec
<TomlTestTarget
>>,
301 dependencies
: Option
<BTreeMap
<String
, TomlDependency
>>,
302 dev_dependencies
: Option
<BTreeMap
<String
, TomlDependency
>>,
303 #[serde(rename = "dev_dependencies")]
304 dev_dependencies2
: Option
<BTreeMap
<String
, TomlDependency
>>,
305 build_dependencies
: Option
<BTreeMap
<String
, TomlDependency
>>,
306 #[serde(rename = "build_dependencies")]
307 build_dependencies2
: Option
<BTreeMap
<String
, TomlDependency
>>,
308 features
: Option
<BTreeMap
<InternedString
, Vec
<InternedString
>>>,
309 target
: Option
<BTreeMap
<String
, TomlPlatform
>>,
310 replace
: Option
<BTreeMap
<String
, TomlDependency
>>,
311 patch
: Option
<BTreeMap
<String
, BTreeMap
<String
, TomlDependency
>>>,
312 workspace
: Option
<TomlWorkspace
>,
313 badges
: Option
<BTreeMap
<String
, BTreeMap
<String
, String
>>>,
316 #[derive(Deserialize, Serialize, Clone, Debug, Default)]
317 pub struct TomlProfiles(BTreeMap
<InternedString
, TomlProfile
>);
320 pub fn get_all(&self) -> &BTreeMap
<InternedString
, TomlProfile
> {
324 pub fn get(&self, name
: &str) -> Option
<&TomlProfile
> {
328 pub fn validate(&self, features
: &Features
, warnings
: &mut Vec
<String
>) -> CargoResult
<()> {
329 for (name
, profile
) in &self.0 {
330 profile
.validate(name
, features
, warnings
)?
;
336 #[derive(Clone, Debug, Eq, PartialEq)]
337 pub struct TomlOptLevel(pub String
);
339 impl<'de
> de
::Deserialize
<'de
> for TomlOptLevel
{
340 fn deserialize
<D
>(d
: D
) -> Result
<TomlOptLevel
, D
::Error
>
342 D
: de
::Deserializer
<'de
>,
346 impl<'de
> de
::Visitor
<'de
> for Visitor
{
347 type Value
= TomlOptLevel
;
349 fn expecting(&self, formatter
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
350 formatter
.write_str("an optimization level")
353 fn visit_i64
<E
>(self, value
: i64) -> Result
<TomlOptLevel
, E
>
357 Ok(TomlOptLevel(value
.to_string()))
360 fn visit_str
<E
>(self, value
: &str) -> Result
<TomlOptLevel
, E
>
364 if value
== "s" || value
== "z" {
365 Ok(TomlOptLevel(value
.to_string()))
367 Err(E
::custom(format
!(
368 "must be `0`, `1`, `2`, `3`, `s` or `z`, \
369 but found the string: \"{}\"",
376 d
.deserialize_any(Visitor
)
380 impl ser
::Serialize
for TomlOptLevel
{
381 fn serialize
<S
>(&self, serializer
: S
) -> Result
<S
::Ok
, S
::Error
>
385 match self.0.parse
::<u32>() {
386 Ok(n
) => n
.serialize(serializer
),
387 Err(_
) => self.0.serialize(serializer
),
392 #[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
393 #[serde(untagged, expecting = "expected a boolean or an integer")]
399 #[derive(Deserialize, Serialize, Clone, Debug, Default, Eq, PartialEq)]
400 #[serde(default, rename_all = "kebab-case")]
401 pub struct TomlProfile
{
402 pub opt_level
: Option
<TomlOptLevel
>,
403 pub lto
: Option
<StringOrBool
>,
404 pub codegen_backend
: Option
<InternedString
>,
405 pub codegen_units
: Option
<u32>,
406 pub debug
: Option
<U32OrBool
>,
407 pub split_debuginfo
: Option
<String
>,
408 pub debug_assertions
: Option
<bool
>,
409 pub rpath
: Option
<bool
>,
410 pub panic
: Option
<String
>,
411 pub overflow_checks
: Option
<bool
>,
412 pub incremental
: Option
<bool
>,
413 pub dir_name
: Option
<InternedString
>,
414 pub inherits
: Option
<InternedString
>,
415 pub strip
: Option
<StringOrBool
>,
416 // These two fields must be last because they are sub-tables, and TOML
417 // requires all non-tables to be listed first.
418 pub package
: Option
<BTreeMap
<ProfilePackageSpec
, TomlProfile
>>,
419 pub build_override
: Option
<Box
<TomlProfile
>>,
422 #[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
423 pub enum ProfilePackageSpec
{
428 impl ser
::Serialize
for ProfilePackageSpec
{
429 fn serialize
<S
>(&self, s
: S
) -> Result
<S
::Ok
, S
::Error
>
434 ProfilePackageSpec
::Spec(ref spec
) => spec
.serialize(s
),
435 ProfilePackageSpec
::All
=> "*".serialize(s
),
440 impl<'de
> de
::Deserialize
<'de
> for ProfilePackageSpec
{
441 fn deserialize
<D
>(d
: D
) -> Result
<ProfilePackageSpec
, D
::Error
>
443 D
: de
::Deserializer
<'de
>,
445 let string
= String
::deserialize(d
)?
;
447 Ok(ProfilePackageSpec
::All
)
449 PackageIdSpec
::parse(&string
)
450 .map_err(de
::Error
::custom
)
451 .map(ProfilePackageSpec
::Spec
)
461 warnings
: &mut Vec
<String
>,
462 ) -> CargoResult
<()> {
463 if let Some(ref profile
) = self.build_override
{
464 features
.require(Feature
::profile_overrides())?
;
465 profile
.validate_override("build-override", features
)?
;
467 if let Some(ref packages
) = self.package
{
468 features
.require(Feature
::profile_overrides())?
;
469 for profile
in packages
.values() {
470 profile
.validate_override("package", features
)?
;
474 // Feature gate definition of named profiles
476 "dev" | "release" | "bench" | "test" | "doc" => {}
478 features
.require(Feature
::named_profiles())?
;
482 // Profile name validation
483 Self::validate_name(name
)?
;
485 // Feature gate on uses of keys related to named profiles
486 if self.inherits
.is_some() {
487 features
.require(Feature
::named_profiles())?
;
490 if let Some(dir_name
) = self.dir_name
{
491 // This is disabled for now, as we would like to stabilize named
492 // profiles without this, and then decide in the future if it is
493 // needed. This helps simplify the UI a little.
495 "dir-name=\"{}\" in profile `{}` is not currently allowed, \
496 directory names are tied to the profile name for custom profiles",
502 // `inherits` validation
503 if matches
!(self.inherits
.map(|s
| s
.as_str()), Some("debug")) {
505 "profile.{}.inherits=\"debug\" should be profile.{}.inherits=\"dev\"",
513 warnings
.push("profile `doc` is deprecated and has no effect".to_string());
515 "test" | "bench" => {
516 if self.panic
.is_some() {
517 warnings
.push(format
!("`panic` setting is ignored for `{}` profile", name
))
523 if let Some(panic
) = &self.panic
{
524 if panic
!= "unwind" && panic
!= "abort" {
526 "`panic` setting of `{}` is not a valid setting, \
527 must be `unwind` or `abort`",
533 if let Some(codegen_backend
) = &self.codegen_backend
{
534 features
.require(Feature
::codegen_backend())?
;
535 if codegen_backend
.contains(|c
: char| !c
.is_ascii_alphanumeric() && c
!= '_'
) {
537 "`profile.{}.codegen-backend` setting of `{}` is not a valid backend name.",
547 /// Validate dir-names and profile names according to RFC 2678.
548 pub fn validate_name(name
: &str) -> CargoResult
<()> {
549 if let Some(ch
) = name
551 .find(|ch
| !ch
.is_alphanumeric() && *ch
!= '_'
&& *ch
!= '
-'
)
554 "invalid character `{}` in profile name `{}`\n\
555 Allowed characters are letters, numbers, underscore, and hyphen.",
561 const SEE_DOCS
: &str = "See https://doc.rust-lang.org/cargo/reference/profiles.html \
562 for more on configuring profiles.";
564 let lower_name
= name
.to_lowercase();
565 if lower_name
== "debug" {
567 "profile name `{}` is reserved\n\
568 To configure the default development profile, use the name `dev` \
569 as in [profile.dev]\n\
575 if lower_name
== "build-override" {
577 "profile name `{}` is reserved\n\
578 To configure build dependency settings, use [profile.dev.build-override] \
579 and [profile.release.build-override]\n\
586 // These are some arbitrary reservations. We have no plans to use
587 // these, but it seems safer to reserve a few just in case we want to
588 // add more built-in profiles in the future. We can also uses special
589 // syntax like cargo:foo if needed. But it is unlikely these will ever
612 ) || lower_name
.starts_with("cargo")
615 "profile name `{}` is reserved\n\
616 Please choose a different name.\n\
626 fn validate_override(&self, which
: &str, features
: &Features
) -> CargoResult
<()> {
627 if self.package
.is_some() {
628 bail
!("package-specific profiles cannot be nested");
630 if self.build_override
.is_some() {
631 bail
!("build-override profiles cannot be nested");
633 if self.panic
.is_some() {
634 bail
!("`panic` may not be specified in a `{}` profile", which
)
636 if self.lto
.is_some() {
637 bail
!("`lto` may not be specified in a `{}` profile", which
)
639 if self.rpath
.is_some() {
640 bail
!("`rpath` may not be specified in a `{}` profile", which
)
642 if self.codegen_backend
.is_some() {
643 features
.require(Feature
::codegen_backend())?
;
648 /// Overwrite self's values with the given profile.
649 pub fn merge(&mut self, profile
: &TomlProfile
) {
650 if let Some(v
) = &profile
.opt_level
{
651 self.opt_level
= Some(v
.clone());
654 if let Some(v
) = &profile
.lto
{
655 self.lto
= Some(v
.clone());
658 if let Some(v
) = profile
.codegen_backend
{
659 self.codegen_backend
= Some(v
);
662 if let Some(v
) = profile
.codegen_units
{
663 self.codegen_units
= Some(v
);
666 if let Some(v
) = &profile
.debug
{
667 self.debug
= Some(v
.clone());
670 if let Some(v
) = profile
.debug_assertions
{
671 self.debug_assertions
= Some(v
);
674 if let Some(v
) = &profile
.split_debuginfo
{
675 self.split_debuginfo
= Some(v
.clone());
678 if let Some(v
) = profile
.rpath
{
679 self.rpath
= Some(v
);
682 if let Some(v
) = &profile
.panic
{
683 self.panic
= Some(v
.clone());
686 if let Some(v
) = profile
.overflow_checks
{
687 self.overflow_checks
= Some(v
);
690 if let Some(v
) = profile
.incremental
{
691 self.incremental
= Some(v
);
694 if let Some(other_package
) = &profile
.package
{
695 match &mut self.package
{
696 Some(self_package
) => {
697 for (spec
, other_pkg_profile
) in other_package
{
698 match self_package
.get_mut(spec
) {
699 Some(p
) => p
.merge(other_pkg_profile
),
701 self_package
.insert(spec
.clone(), other_pkg_profile
.clone());
706 None
=> self.package
= Some(other_package
.clone()),
710 if let Some(other_bo
) = &profile
.build_override
{
711 match &mut self.build_override
{
712 Some(self_bo
) => self_bo
.merge(other_bo
),
713 None
=> self.build_override
= Some(other_bo
.clone()),
717 if let Some(v
) = &profile
.inherits
{
718 self.inherits
= Some(*v
);
721 if let Some(v
) = &profile
.dir_name
{
722 self.dir_name
= Some(*v
);
725 if let Some(v
) = &profile
.strip
{
726 self.strip
= Some(v
.clone());
731 /// A StringOrVec can be parsed from either a TOML string or array,
732 /// but is always stored as a vector.
733 #[derive(Clone, Debug, Serialize, Eq, PartialEq, PartialOrd, Ord)]
734 pub struct StringOrVec(Vec
<String
>);
736 impl<'de
> de
::Deserialize
<'de
> for StringOrVec
{
737 fn deserialize
<D
>(deserializer
: D
) -> Result
<Self, D
::Error
>
739 D
: de
::Deserializer
<'de
>,
743 impl<'de
> de
::Visitor
<'de
> for Visitor
{
744 type Value
= StringOrVec
;
746 fn expecting(&self, formatter
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
747 formatter
.write_str("string or list of strings")
750 fn visit_str
<E
>(self, s
: &str) -> Result
<Self::Value
, E
>
754 Ok(StringOrVec(vec
![s
.to_string()]))
757 fn visit_seq
<V
>(self, v
: V
) -> Result
<Self::Value
, V
::Error
>
759 V
: de
::SeqAccess
<'de
>,
761 let seq
= de
::value
::SeqAccessDeserializer
::new(v
);
762 Vec
::deserialize(seq
).map(StringOrVec
)
766 deserializer
.deserialize_any(Visitor
)
771 pub fn iter
<'a
>(&'a
self) -> std
::slice
::Iter
<'a
, String
> {
776 #[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
777 #[serde(untagged, expecting = "expected a boolean or a string")]
778 pub enum StringOrBool
{
783 #[derive(PartialEq, Clone, Debug, Serialize)]
785 pub enum VecStringOrBool
{
786 VecString(Vec
<String
>),
790 impl<'de
> de
::Deserialize
<'de
> for VecStringOrBool
{
791 fn deserialize
<D
>(deserializer
: D
) -> Result
<Self, D
::Error
>
793 D
: de
::Deserializer
<'de
>,
797 impl<'de
> de
::Visitor
<'de
> for Visitor
{
798 type Value
= VecStringOrBool
;
800 fn expecting(&self, formatter
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
801 formatter
.write_str("a boolean or vector of strings")
804 fn visit_seq
<V
>(self, v
: V
) -> Result
<Self::Value
, V
::Error
>
806 V
: de
::SeqAccess
<'de
>,
808 let seq
= de
::value
::SeqAccessDeserializer
::new(v
);
809 Vec
::deserialize(seq
).map(VecStringOrBool
::VecString
)
812 fn visit_bool
<E
>(self, b
: bool
) -> Result
<Self::Value
, E
>
816 Ok(VecStringOrBool
::Bool(b
))
820 deserializer
.deserialize_any(Visitor
)
824 fn version_trim_whitespace
<'de
, D
>(deserializer
: D
) -> Result
<semver
::Version
, D
::Error
>
826 D
: de
::Deserializer
<'de
>,
830 impl<'de
> de
::Visitor
<'de
> for Visitor
{
831 type Value
= semver
::Version
;
833 fn expecting(&self, formatter
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
834 formatter
.write_str("SemVer version")
837 fn visit_str
<E
>(self, string
: &str) -> Result
<Self::Value
, E
>
841 string
.trim().parse().map_err(de
::Error
::custom
)
845 deserializer
.deserialize_str(Visitor
)
848 /// Represents the `package`/`project` sections of a `Cargo.toml`.
850 /// Note that the order of the fields matters, since this is the order they
851 /// are serialized to a TOML file. For example, you cannot have values after
852 /// the field `metadata`, since it is a table and values cannot appear after
854 #[derive(Deserialize, Serialize, Clone, Debug)]
855 #[serde(rename_all = "kebab-case")]
856 pub struct TomlProject
{
857 edition
: Option
<String
>,
858 rust_version
: Option
<String
>,
859 name
: InternedString
,
860 #[serde(deserialize_with = "version_trim_whitespace")]
861 version
: semver
::Version
,
862 authors
: Option
<Vec
<String
>>,
863 build
: Option
<StringOrBool
>,
864 metabuild
: Option
<StringOrVec
>,
865 #[serde(rename = "default-target")]
866 default_target
: Option
<String
>,
867 #[serde(rename = "forced-target")]
868 forced_target
: Option
<String
>,
869 links
: Option
<String
>,
870 exclude
: Option
<Vec
<String
>>,
871 include
: Option
<Vec
<String
>>,
872 publish
: Option
<VecStringOrBool
>,
873 workspace
: Option
<String
>,
874 im_a_teapot
: Option
<bool
>,
875 autobins
: Option
<bool
>,
876 autoexamples
: Option
<bool
>,
877 autotests
: Option
<bool
>,
878 autobenches
: Option
<bool
>,
879 default_run
: Option
<String
>,
882 description
: Option
<String
>,
883 homepage
: Option
<String
>,
884 documentation
: Option
<String
>,
885 readme
: Option
<StringOrBool
>,
886 keywords
: Option
<Vec
<String
>>,
887 categories
: Option
<Vec
<String
>>,
888 license
: Option
<String
>,
889 license_file
: Option
<String
>,
890 repository
: Option
<String
>,
891 resolver
: Option
<String
>,
893 // Note that this field must come last due to the way toml serialization
894 // works which requires tables to be emitted after all values.
895 metadata
: Option
<toml
::Value
>,
898 #[derive(Debug, Deserialize, Serialize)]
899 pub struct TomlWorkspace
{
900 members
: Option
<Vec
<String
>>,
901 #[serde(rename = "default-members")]
902 default_members
: Option
<Vec
<String
>>,
903 exclude
: Option
<Vec
<String
>>,
904 resolver
: Option
<String
>,
906 // Note that this field must come last due to the way toml serialization
907 // works which requires tables to be emitted after all values.
908 metadata
: Option
<toml
::Value
>,
912 pub fn to_package_id(&self, source_id
: SourceId
) -> CargoResult
<PackageId
> {
913 PackageId
::new(self.name
, self.version
.clone(), source_id
)
917 struct Context
<'a
, 'b
> {
918 deps
: &'a
mut Vec
<Dependency
>,
920 nested_paths
: &'a
mut Vec
<PathBuf
>,
922 warnings
: &'a
mut Vec
<String
>,
923 platform
: Option
<Platform
>,
925 features
: &'a Features
,
929 /// Prepares the manifest for publishing.
930 // - Path and git components of dependency specifications are removed.
931 // - License path is updated to point within the package.
932 pub fn prepare_for_publish(
936 ) -> CargoResult
<TomlManifest
> {
937 let config
= ws
.config();
938 let mut package
= self
941 .or_else(|| self.project
.as_ref())
944 package
.workspace
= None
;
945 package
.resolver
= ws
.resolve_behavior().to_manifest();
946 if let Some(license_file
) = &package
.license_file
{
947 let license_path
= Path
::new(&license_file
);
948 let abs_license_path
= paths
::normalize_path(&package_root
.join(license_path
));
949 if abs_license_path
.strip_prefix(package_root
).is_err() {
950 // This path points outside of the package root. `cargo package`
951 // will copy it into the root, so adjust the path to this location.
952 package
.license_file
= Some(
962 let all
= |_d
: &TomlDependency
| true;
963 return Ok(TomlManifest
{
964 package
: Some(package
),
966 profile
: self.profile
.clone(),
967 lib
: self.lib
.clone(),
968 bin
: self.bin
.clone(),
969 example
: self.example
.clone(),
970 test
: self.test
.clone(),
971 bench
: self.bench
.clone(),
972 dependencies
: map_deps(config
, self.dependencies
.as_ref(), all
)?
,
973 dev_dependencies
: map_deps(
975 self.dev_dependencies
977 .or_else(|| self.dev_dependencies2
.as_ref()),
978 TomlDependency
::is_version_specified
,
980 dev_dependencies2
: None
,
981 build_dependencies
: map_deps(
983 self.build_dependencies
985 .or_else(|| self.build_dependencies2
.as_ref()),
988 build_dependencies2
: None
,
989 features
: self.features
.clone(),
990 target
: match self.target
.as_ref().map(|target_map
| {
997 dependencies
: map_deps(config
, v
.dependencies
.as_ref(), all
)?
,
998 dev_dependencies
: map_deps(
1002 .or_else(|| v
.dev_dependencies2
.as_ref()),
1003 TomlDependency
::is_version_specified
,
1005 dev_dependencies2
: None
,
1006 build_dependencies
: map_deps(
1008 v
.build_dependencies
1010 .or_else(|| v
.build_dependencies2
.as_ref()),
1013 build_dependencies2
: None
,
1019 Some(Ok(v
)) => Some(v
),
1020 Some(Err(e
)) => return Err(e
),
1026 badges
: self.badges
.clone(),
1027 cargo_features
: self.cargo_features
.clone(),
1032 deps
: Option
<&BTreeMap
<String
, TomlDependency
>>,
1033 filter
: impl Fn(&TomlDependency
) -> bool
,
1034 ) -> CargoResult
<Option
<BTreeMap
<String
, TomlDependency
>>> {
1035 let deps
= match deps
{
1037 None
=> return Ok(None
),
1041 .filter(|(_k
, v
)| filter(v
))
1042 .map(|(k
, v
)| Ok((k
.clone(), map_dependency(config
, v
)?
)))
1043 .collect
::<CargoResult
<BTreeMap
<_
, _
>>>()?
;
1047 fn map_dependency(config
: &Config
, dep
: &TomlDependency
) -> CargoResult
<TomlDependency
> {
1049 TomlDependency
::Detailed(d
) => {
1050 let mut d
= d
.clone();
1051 // Path dependencies become crates.io deps.
1053 // Same with git dependencies.
1058 // registry specifications are elaborated to the index URL
1059 if let Some(registry
) = d
.registry
.take() {
1060 let src
= SourceId
::alt_registry(config
, ®istry
)?
;
1061 d
.registry_index
= Some(src
.url().to_string());
1063 Ok(TomlDependency
::Detailed(d
))
1065 TomlDependency
::Simple(s
) => Ok(TomlDependency
::Detailed(DetailedTomlDependency
{
1066 version
: Some(s
.clone()),
1067 ..Default
::default()
1073 pub fn to_real_manifest(
1074 me
: &Rc
<TomlManifest
>,
1075 source_id
: SourceId
,
1076 package_root
: &Path
,
1078 ) -> CargoResult
<(Manifest
, Vec
<PathBuf
>)> {
1079 let mut nested_paths
= vec
![];
1080 let mut warnings
= vec
![];
1081 let mut errors
= vec
![];
1083 // Parse features first so they will be available when parsing other parts of the TOML.
1084 let empty
= Vec
::new();
1085 let cargo_features
= me
.cargo_features
.as_ref().unwrap_or(&empty
);
1086 let features
= Features
::new(cargo_features
, config
, &mut warnings
, source_id
.is_path())?
;
1088 let project
= me
.project
.as_ref().or_else(|| me
.package
.as_ref());
1089 let project
= project
.ok_or_else(|| anyhow
!("no `package` section found"))?
;
1091 let package_name
= project
.name
.trim();
1092 if package_name
.is_empty() {
1093 bail
!("package name cannot be an empty string")
1096 validate_package_name(package_name
, "package name", "")?
;
1098 let pkgid
= project
.to_package_id(source_id
)?
;
1100 let edition
= if let Some(ref edition
) = project
.edition
{
1102 .require(Feature
::edition())
1103 .with_context(|| "editions are unstable")?
;
1106 .with_context(|| "failed to parse the `edition` key")?
1108 Edition
::Edition2015
1110 if edition
== Edition
::Edition2021
{
1111 features
.require(Feature
::edition2021())?
;
1112 } else if !edition
.is_stable() {
1113 // Guard in case someone forgets to add .require()
1114 return Err(util
::errors
::internal(format
!(
1115 "edition {} should be gated",
1120 let rust_version
= if let Some(rust_version
) = &project
.rust_version
{
1121 let req
= match semver
::VersionReq
::parse(rust_version
) {
1122 // Exclude semver operators like `^` and pre-release identifiers
1123 Ok(req
) if rust_version
.chars().all(|c
| c
.is_ascii_digit() || c
== '
.'
) => req
,
1124 _
=> bail
!("`rust-version` must be a value like \"1.32\""),
1126 if let Some(first_version
) = edition
.first_version() {
1128 semver
::Version
::new(first_version
.major
, first_version
.minor
- 1, 9999);
1129 if req
.matches(&unsupported
) {
1131 "rust-version {} is older than first version ({}) required by \
1132 the specified edition ({})",
1139 Some(rust_version
.clone())
1144 if project
.metabuild
.is_some() {
1145 features
.require(Feature
::metabuild())?
;
1148 if project
.resolver
.is_some()
1152 .map_or(false, |ws
| ws
.resolver
.is_some())
1154 features
.require(Feature
::resolver())?
;
1156 let resolve_behavior
= match (
1157 project
.resolver
.as_ref(),
1158 me
.workspace
.as_ref().and_then(|ws
| ws
.resolver
.as_ref()),
1160 (None
, None
) => None
,
1161 (Some(s
), None
) | (None
, Some(s
)) => Some(ResolveBehavior
::from_manifest(s
)?
),
1162 (Some(_
), Some(_
)) => {
1163 bail
!("cannot specify `resolver` field in both `[workspace]` and `[package]`")
1167 // If we have no lib at all, use the inferred lib, if available.
1168 // If we have a lib with a path, we're done.
1169 // If we have a lib with no path, use the inferred lib or else the package name.
1170 let targets
= targets(
1182 if targets
.is_empty() {
1183 debug
!("manifest has no build targets");
1186 if let Err(e
) = unique_build_targets(&targets
, package_root
) {
1187 warnings
.push(format
!(
1188 "file found to be present in multiple \
1194 if let Some(links
) = &project
.links
{
1195 if !targets
.iter().any(|t
| t
.is_custom_build()) {
1197 "package `{}` specifies that it links to `{}` but does not \
1198 have a custom build script",
1205 let mut deps
= Vec
::new();
1210 let mut cx
= Context
{
1213 nested_paths
: &mut nested_paths
,
1215 warnings
: &mut warnings
,
1216 features
: &features
,
1221 fn process_dependencies(
1222 cx
: &mut Context
<'_
, '_
>,
1223 new_deps
: Option
<&BTreeMap
<String
, TomlDependency
>>,
1224 kind
: Option
<DepKind
>,
1225 ) -> CargoResult
<()> {
1226 let dependencies
= match new_deps
{
1227 Some(dependencies
) => dependencies
,
1228 None
=> return Ok(()),
1230 for (n
, v
) in dependencies
.iter() {
1231 let dep
= v
.to_dependency(n
, cx
, kind
)?
;
1232 validate_package_name(dep
.name_in_toml().as_str(), "dependency name", "")?
;
1239 // Collect the dependencies.
1240 process_dependencies(&mut cx
, me
.dependencies
.as_ref(), None
)?
;
1244 .or_else(|| me
.dev_dependencies2
.as_ref());
1245 process_dependencies(&mut cx
, dev_deps
, Some(DepKind
::Development
))?
;
1249 .or_else(|| me
.build_dependencies2
.as_ref());
1250 process_dependencies(&mut cx
, build_deps
, Some(DepKind
::Build
))?
;
1252 for (name
, platform
) in me
.target
.iter().flatten() {
1254 let platform
: Platform
= name
.parse()?
;
1255 platform
.check_cfg_attributes(cx
.warnings
);
1258 process_dependencies(&mut cx
, platform
.dependencies
.as_ref(), None
)?
;
1259 let build_deps
= platform
1262 .or_else(|| platform
.build_dependencies2
.as_ref());
1263 process_dependencies(&mut cx
, build_deps
, Some(DepKind
::Build
))?
;
1264 let dev_deps
= platform
1267 .or_else(|| platform
.dev_dependencies2
.as_ref());
1268 process_dependencies(&mut cx
, dev_deps
, Some(DepKind
::Development
))?
;
1271 replace
= me
.replace(&mut cx
)?
;
1272 patch
= me
.patch(&mut cx
)?
;
1276 let mut names_sources
= BTreeMap
::new();
1278 let name
= dep
.name_in_toml();
1279 let prev
= names_sources
.insert(name
.to_string(), dep
.source_id());
1280 if prev
.is_some() && prev
!= Some(dep
.source_id()) {
1282 "Dependency '{}' has different source paths depending on the build \
1283 target. Each dependency must have a single canonical source path \
1284 irrespective of build target.",
1291 let exclude
= project
.exclude
.clone().unwrap_or_default();
1292 let include
= project
.include
.clone().unwrap_or_default();
1293 let empty_features
= BTreeMap
::new();
1295 let summary
= Summary
::new(
1299 me
.features
.as_ref().unwrap_or(&empty_features
),
1300 project
.links
.as_deref(),
1303 let metadata
= ManifestMetadata
{
1304 description
: project
.description
.clone(),
1305 homepage
: project
.homepage
.clone(),
1306 documentation
: project
.documentation
.clone(),
1307 readme
: readme_for_project(package_root
, project
),
1308 authors
: project
.authors
.clone().unwrap_or_default(),
1309 license
: project
.license
.clone(),
1310 license_file
: project
.license_file
.clone(),
1311 repository
: project
.repository
.clone(),
1312 keywords
: project
.keywords
.clone().unwrap_or_default(),
1313 categories
: project
.categories
.clone().unwrap_or_default(),
1314 badges
: me
.badges
.clone().unwrap_or_default(),
1315 links
: project
.links
.clone(),
1318 let workspace_config
= match (me
.workspace
.as_ref(), project
.workspace
.as_ref()) {
1319 (Some(config
), None
) => WorkspaceConfig
::Root(WorkspaceRootConfig
::new(
1322 &config
.default_members
,
1326 (None
, root
) => WorkspaceConfig
::Member
{
1327 root
: root
.cloned(),
1329 (Some(..), Some(..)) => bail
!(
1330 "cannot configure both `package.workspace` and \
1331 `[workspace]`, only one can be specified"
1334 let profiles
= me
.profile
.clone();
1335 if let Some(profiles
) = &profiles
{
1336 profiles
.validate(&features
, &mut warnings
)?
;
1338 let publish
= match project
.publish
{
1339 Some(VecStringOrBool
::VecString(ref vecstring
)) => Some(vecstring
.clone()),
1340 Some(VecStringOrBool
::Bool(false)) => Some(vec
![]),
1341 None
| Some(VecStringOrBool
::Bool(true)) => None
,
1344 if summary
.features().contains_key("default-features") {
1346 "`default-features = [\"..\"]` was found in [features]. \
1347 Did you mean to use `default = [\"..\"]`?"
1352 if let Some(run
) = &project
.default_run
{
1355 .filter(|t
| t
.is_bin())
1356 .any(|t
| t
.name() == run
)
1359 util
::closest_msg(run
, targets
.iter().filter(|t
| t
.is_bin()), |t
| t
.name());
1360 bail
!("default-run target `{}` not found{}", run
, suggestion
);
1364 let default_kind
= project
1367 .map(|t
| CompileTarget
::new(&*t
))
1369 .map(CompileKind
::Target
);
1370 let forced_kind
= project
1373 .map(|t
| CompileTarget
::new(&*t
))
1375 .map(CompileKind
::Target
);
1377 let custom_metadata
= project
.metadata
.clone();
1378 let mut manifest
= Manifest
::new(
1385 project
.links
.clone(),
1396 project
.im_a_teapot
,
1397 project
.default_run
.clone(),
1399 project
.metabuild
.clone().map(|sov
| sov
.0),
1402 if project
.license_file
.is_some() && project
.license
.is_some() {
1403 manifest
.warnings_mut().add_warning(
1404 "only one of `license` or `license-file` is necessary\n\
1405 `license` should be used if the package license can be expressed \
1406 with a standard SPDX expression.\n\
1407 `license-file` should be used if the package uses a non-standard license.\n\
1408 See https://doc.rust-lang.org/cargo/reference/manifest.html#the-license-and-license-file-fields \
1409 for more information."
1413 for warning
in warnings
{
1414 manifest
.warnings_mut().add_warning(warning
);
1416 for error
in errors
{
1417 manifest
.warnings_mut().add_critical_warning(error
);
1420 manifest
.feature_gate()?
;
1422 Ok((manifest
, nested_paths
))
1425 fn to_virtual_manifest(
1426 me
: &Rc
<TomlManifest
>,
1427 source_id
: SourceId
,
1430 ) -> CargoResult
<(VirtualManifest
, Vec
<PathBuf
>)> {
1431 if me
.project
.is_some() {
1432 bail
!("this virtual manifest specifies a [project] section, which is not allowed");
1434 if me
.package
.is_some() {
1435 bail
!("this virtual manifest specifies a [package] section, which is not allowed");
1437 if me
.lib
.is_some() {
1438 bail
!("this virtual manifest specifies a [lib] section, which is not allowed");
1440 if me
.bin
.is_some() {
1441 bail
!("this virtual manifest specifies a [[bin]] section, which is not allowed");
1443 if me
.example
.is_some() {
1444 bail
!("this virtual manifest specifies a [[example]] section, which is not allowed");
1446 if me
.test
.is_some() {
1447 bail
!("this virtual manifest specifies a [[test]] section, which is not allowed");
1449 if me
.bench
.is_some() {
1450 bail
!("this virtual manifest specifies a [[bench]] section, which is not allowed");
1452 if me
.dependencies
.is_some() {
1453 bail
!("this virtual manifest specifies a [dependencies] section, which is not allowed");
1455 if me
.dev_dependencies
.is_some() || me
.dev_dependencies2
.is_some() {
1456 bail
!("this virtual manifest specifies a [dev-dependencies] section, which is not allowed");
1458 if me
.build_dependencies
.is_some() || me
.build_dependencies2
.is_some() {
1459 bail
!("this virtual manifest specifies a [build-dependencies] section, which is not allowed");
1461 if me
.features
.is_some() {
1462 bail
!("this virtual manifest specifies a [features] section, which is not allowed");
1464 if me
.target
.is_some() {
1465 bail
!("this virtual manifest specifies a [target] section, which is not allowed");
1467 if me
.badges
.is_some() {
1468 bail
!("this virtual manifest specifies a [badges] section, which is not allowed");
1471 let mut nested_paths
= Vec
::new();
1472 let mut warnings
= Vec
::new();
1473 let mut deps
= Vec
::new();
1474 let empty
= Vec
::new();
1475 let cargo_features
= me
.cargo_features
.as_ref().unwrap_or(&empty
);
1476 let features
= Features
::new(cargo_features
, config
, &mut warnings
, source_id
.is_path())?
;
1478 let (replace
, patch
) = {
1479 let mut cx
= Context
{
1482 nested_paths
: &mut nested_paths
,
1484 warnings
: &mut warnings
,
1486 features
: &features
,
1489 (me
.replace(&mut cx
)?
, me
.patch(&mut cx
)?
)
1491 let profiles
= me
.profile
.clone();
1492 if let Some(profiles
) = &profiles
{
1493 profiles
.validate(&features
, &mut warnings
)?
;
1498 .map_or(false, |ws
| ws
.resolver
.is_some())
1500 features
.require(Feature
::resolver())?
;
1502 let resolve_behavior
= me
1505 .and_then(|ws
| ws
.resolver
.as_deref())
1506 .map(|r
| ResolveBehavior
::from_manifest(r
))
1508 let workspace_config
= match me
.workspace
{
1509 Some(ref config
) => WorkspaceConfig
::Root(WorkspaceRootConfig
::new(
1512 &config
.default_members
,
1517 bail
!("virtual manifests must be configured with [workspace]");
1521 VirtualManifest
::new(
1533 fn replace(&self, cx
: &mut Context
<'_
, '_
>) -> CargoResult
<Vec
<(PackageIdSpec
, Dependency
)>> {
1534 if self.patch
.is_some() && self.replace
.is_some() {
1535 bail
!("cannot specify both [replace] and [patch]");
1537 let mut replace
= Vec
::new();
1538 for (spec
, replacement
) in self.replace
.iter().flatten() {
1539 let mut spec
= PackageIdSpec
::parse(spec
).with_context(|| {
1541 "replacements must specify a valid semver \
1542 version to replace, but `{}` does not",
1546 if spec
.url().is_none() {
1547 spec
.set_url(CRATES_IO_INDEX
.parse().unwrap());
1550 if replacement
.is_version_specified() {
1552 "replacements cannot specify a version \
1553 requirement, but found one for `{}`",
1558 let mut dep
= replacement
.to_dependency(spec
.name().as_str(), cx
, None
)?
;
1559 let version
= spec
.version().ok_or_else(|| {
1561 "replacements must specify a version \
1562 to replace, but `{}` does not",
1566 dep
.set_version_req(VersionReq
::exact(version
))
1567 .lock_version(version
);
1568 replace
.push((spec
, dep
));
1573 fn patch(&self, cx
: &mut Context
<'_
, '_
>) -> CargoResult
<HashMap
<Url
, Vec
<Dependency
>>> {
1574 let mut patch
= HashMap
::new();
1575 for (url
, deps
) in self.patch
.iter().flatten() {
1576 let url
= match &url
[..] {
1577 CRATES_IO_REGISTRY
=> CRATES_IO_INDEX
.parse().unwrap(),
1580 .get_registry_index(url
)
1581 .or_else(|_
| url
.into_url())
1583 format
!("[patch] entry `{}` should be a URL or registry name", url
)
1589 .map(|(name
, dep
)| dep
.to_dependency(name
, cx
, None
))
1590 .collect
::<CargoResult
<Vec
<_
>>>()?
,
1596 /// Returns the path to the build script if one exists for this crate.
1597 fn maybe_custom_build(
1599 build
: &Option
<StringOrBool
>,
1600 package_root
: &Path
,
1601 ) -> Option
<PathBuf
> {
1602 let build_rs
= package_root
.join("build.rs");
1604 // Explicitly no build script.
1605 Some(StringOrBool
::Bool(false)) => None
,
1606 Some(StringOrBool
::Bool(true)) => Some(build_rs
),
1607 Some(StringOrBool
::String(ref s
)) => Some(PathBuf
::from(s
)),
1609 // If there is a `build.rs` file next to the `Cargo.toml`, assume it is
1611 if build_rs
.is_file() {
1620 pub fn has_profiles(&self) -> bool
{
1621 self.profile
.is_some()
1624 pub fn features(&self) -> Option
<&BTreeMap
<InternedString
, Vec
<InternedString
>>> {
1625 self.features
.as_ref()
1629 /// Returns the name of the README file for a `TomlProject`.
1630 fn readme_for_project(package_root
: &Path
, project
: &TomlProject
) -> Option
<String
> {
1631 match &project
.readme
{
1632 None
=> default_readme_from_package_root(package_root
),
1633 Some(value
) => match value
{
1634 StringOrBool
::Bool(false) => None
,
1635 StringOrBool
::Bool(true) => Some("README.md".to_string()),
1636 StringOrBool
::String(v
) => Some(v
.clone()),
1641 const DEFAULT_README_FILES
: [&str; 3] = ["README.md", "README.txt", "README"];
1643 /// Checks if a file with any of the default README file names exists in the package root.
1644 /// If so, returns a `String` representing that name.
1645 fn default_readme_from_package_root(package_root
: &Path
) -> Option
<String
> {
1646 for &readme_filename
in DEFAULT_README_FILES
.iter() {
1647 if package_root
.join(readme_filename
).is_file() {
1648 return Some(readme_filename
.to_string());
1655 /// Checks a list of build targets, and ensures the target names are unique within a vector.
1656 /// If not, the name of the offending build target is returned.
1657 fn unique_build_targets(targets
: &[Target
], package_root
: &Path
) -> Result
<(), String
> {
1658 let mut seen
= HashSet
::new();
1659 for target
in targets
{
1660 if let TargetSourcePath
::Path(path
) = target
.src_path() {
1661 let full
= package_root
.join(path
);
1662 if !seen
.insert(full
.clone()) {
1663 return Err(full
.display().to_string());
1670 impl<P
: ResolveToPath
> TomlDependency
<P
> {
1671 pub(crate) fn to_dependency_split(
1674 source_id
: SourceId
,
1675 nested_paths
: &mut Vec
<PathBuf
>,
1677 warnings
: &mut Vec
<String
>,
1678 platform
: Option
<Platform
>,
1680 features
: &Features
,
1681 kind
: Option
<DepKind
>,
1682 ) -> CargoResult
<Dependency
> {
1686 deps
: &mut Vec
::new(),
1702 cx
: &mut Context
<'_
, '_
>,
1703 kind
: Option
<DepKind
>,
1704 ) -> CargoResult
<Dependency
> {
1706 TomlDependency
::Simple(ref version
) => DetailedTomlDependency
::<P
> {
1707 version
: Some(version
.clone()),
1708 ..Default
::default()
1710 .to_dependency(name
, cx
, kind
),
1711 TomlDependency
::Detailed(ref details
) => details
.to_dependency(name
, cx
, kind
),
1715 fn is_version_specified(&self) -> bool
{
1717 TomlDependency
::Detailed(d
) => d
.version
.is_some(),
1718 TomlDependency
::Simple(..) => true,
1723 impl<P
: ResolveToPath
> DetailedTomlDependency
<P
> {
1727 cx
: &mut Context
<'_
, '_
>,
1728 kind
: Option
<DepKind
>,
1729 ) -> CargoResult
<Dependency
> {
1730 if self.version
.is_none() && self.path
.is_none() && self.git
.is_none() {
1732 "dependency ({}) specified without \
1733 providing a local path, Git repository, or \
1734 version to use. This will be considered an \
1735 error in future versions",
1738 cx
.warnings
.push(msg
);
1741 if let Some(version
) = &self.version
{
1742 if version
.contains('
+'
) {
1743 cx
.warnings
.push(format
!(
1744 "version requirement `{}` for dependency `{}` \
1745 includes semver metadata which will be ignored, removing the \
1746 metadata is recommended to avoid confusion",
1747 version
, name_in_toml
1752 if self.git
.is_none() {
1753 let git_only_keys
= [
1754 (&self.branch
, "branch"),
1759 for &(key
, key_name
) in &git_only_keys
{
1762 "key `{}` is ignored for dependency ({}).",
1770 // Early detection of potentially misused feature syntax
1771 // instead of generating a "feature not found" error.
1772 if let Some(features
) = &self.features
{
1773 for feature
in features
{
1774 if feature
.contains('
/'
) {
1776 "feature `{}` in dependency `{}` is not allowed to contain slashes\n\
1777 If you want to enable features of a transitive dependency, \
1778 the direct dependency needs to re-export those features from \
1779 the `[features]` table.",
1784 if feature
.starts_with("dep:") {
1786 "feature `{}` in dependency `{}` is not allowed to use explicit \
1788 If you want to enable an optional dependency, specify the name \
1789 of the optional dependency without the `dep:` prefix, or specify \
1790 a feature from the dependency's `[features]` table that enables \
1791 the optional dependency.",
1799 let new_source_id
= match (
1802 self.registry
.as_ref(),
1803 self.registry_index
.as_ref(),
1805 (Some(_
), _
, Some(_
), _
) | (Some(_
), _
, _
, Some(_
)) => bail
!(
1806 "dependency ({}) specification is ambiguous. \
1807 Only one of `git` or `registry` is allowed.",
1810 (_
, _
, Some(_
), Some(_
)) => bail
!(
1811 "dependency ({}) specification is ambiguous. \
1812 Only one of `registry` or `registry-index` is allowed.",
1815 (Some(git
), maybe_path
, _
, _
) => {
1816 if maybe_path
.is_some() {
1818 "dependency ({}) specification is ambiguous. \
1819 Only one of `git` or `path` is allowed.",
1824 let n_details
= [&self.branch
, &self.tag
, &self.rev
]
1826 .filter(|d
| d
.is_some())
1831 "dependency ({}) specification is ambiguous. \
1832 Only one of `branch`, `tag` or `rev` is allowed.",
1837 let reference
= self
1840 .map(GitReference
::Branch
)
1841 .or_else(|| self.tag
.clone().map(GitReference
::Tag
))
1842 .or_else(|| self.rev
.clone().map(GitReference
::Rev
))
1843 .unwrap_or(GitReference
::DefaultBranch
);
1844 let loc
= git
.into_url()?
;
1846 if let Some(fragment
) = loc
.fragment() {
1848 "URL fragment `#{}` in git URL is ignored for dependency ({}). \
1849 If you were trying to specify a specific git revision, \
1850 use `rev = \"{}\"` in the dependency declaration.",
1851 fragment
, name_in_toml
, fragment
1853 cx
.warnings
.push(msg
)
1856 SourceId
::for_git(&loc
, reference
)?
1858 (None
, Some(path
), _
, _
) => {
1859 let path
= path
.resolve(cx
.config
);
1860 cx
.nested_paths
.push(path
.clone());
1861 // If the source ID for the package we're parsing is a path
1862 // source, then we normalize the path here to get rid of
1863 // components like `..`.
1865 // The purpose of this is to get a canonical ID for the package
1866 // that we're depending on to ensure that builds of this package
1867 // always end up hashing to the same value no matter where it's
1869 if cx
.source_id
.is_path() {
1870 let path
= cx
.root
.join(path
);
1871 let path
= paths
::normalize_path(&path
);
1872 SourceId
::for_path(&path
)?
1877 (None
, None
, Some(registry
), None
) => SourceId
::alt_registry(cx
.config
, registry
)?
,
1878 (None
, None
, None
, Some(registry_index
)) => {
1879 let url
= registry_index
.into_url()?
;
1880 SourceId
::for_registry(&url
)?
1882 (None
, None
, None
, None
) => SourceId
::crates_io(cx
.config
)?
,
1885 let (pkg_name
, explicit_name_in_toml
) = match self.package
{
1886 Some(ref s
) => (&s
[..], Some(name_in_toml
)),
1887 None
=> (name_in_toml
, None
),
1890 let version
= self.version
.as_deref();
1891 let mut dep
= Dependency
::parse(pkg_name
, version
, new_source_id
)?
;
1892 dep
.set_features(self.features
.iter().flatten())
1893 .set_default_features(
1894 self.default_features
1895 .or(self.default_features2
)
1898 .set_optional(self.optional
.unwrap_or(false))
1899 .set_platform(cx
.platform
.clone());
1900 if let Some(registry
) = &self.registry
{
1901 let registry_id
= SourceId
::alt_registry(cx
.config
, registry
)?
;
1902 dep
.set_registry_id(registry_id
);
1904 if let Some(registry_index
) = &self.registry_index
{
1905 let url
= registry_index
.into_url()?
;
1906 let registry_id
= SourceId
::for_registry(&url
)?
;
1907 dep
.set_registry_id(registry_id
);
1910 if let Some(kind
) = kind
{
1913 if let Some(name_in_toml
) = explicit_name_in_toml
{
1914 cx
.features
.require(Feature
::rename_dependency())?
;
1915 dep
.set_explicit_name_in_toml(name_in_toml
);
1918 if let Some(p
) = self.public
{
1919 cx
.features
.require(Feature
::public_dependency())?
;
1921 if dep
.kind() != DepKind
::Normal
{
1922 bail
!("'public' specifier can only be used on regular dependencies, not {:?} dependencies", dep
.kind());
1931 #[derive(Default, Serialize, Deserialize, Debug, Clone)]
1933 name
: Option
<String
>,
1935 // The intention was to only accept `crate-type` here but historical
1936 // versions of Cargo also accepted `crate_type`, so look for both.
1937 #[serde(rename = "crate-type")]
1938 crate_type
: Option
<Vec
<String
>>,
1939 #[serde(rename = "crate_type")]
1940 crate_type2
: Option
<Vec
<String
>>,
1942 path
: Option
<PathValue
>,
1943 // Note that `filename` is used for the cargo-feature `different_binary_name`
1944 filename
: Option
<String
>,
1946 doctest
: Option
<bool
>,
1947 bench
: Option
<bool
>,
1949 plugin
: Option
<bool
>,
1950 #[serde(rename = "proc-macro")]
1951 proc_macro_raw
: Option
<bool
>,
1952 #[serde(rename = "proc_macro")]
1953 proc_macro_raw2
: Option
<bool
>,
1954 harness
: Option
<bool
>,
1955 #[serde(rename = "required-features")]
1956 required_features
: Option
<Vec
<String
>>,
1957 edition
: Option
<String
>,
1961 struct PathValue(PathBuf
);
1963 impl<'de
> de
::Deserialize
<'de
> for PathValue
{
1964 fn deserialize
<D
>(deserializer
: D
) -> Result
<Self, D
::Error
>
1966 D
: de
::Deserializer
<'de
>,
1968 Ok(PathValue(String
::deserialize(deserializer
)?
.into()))
1972 impl ser
::Serialize
for PathValue
{
1973 fn serialize
<S
>(&self, serializer
: S
) -> Result
<S
::Ok
, S
::Error
>
1977 self.0.serialize(serializer
)
1981 /// Corresponds to a `target` entry, but `TomlTarget` is already used.
1982 #[derive(Serialize, Deserialize, Debug)]
1983 struct TomlPlatform
{
1984 dependencies
: Option
<BTreeMap
<String
, TomlDependency
>>,
1985 #[serde(rename = "build-dependencies")]
1986 build_dependencies
: Option
<BTreeMap
<String
, TomlDependency
>>,
1987 #[serde(rename = "build_dependencies")]
1988 build_dependencies2
: Option
<BTreeMap
<String
, TomlDependency
>>,
1989 #[serde(rename = "dev-dependencies")]
1990 dev_dependencies
: Option
<BTreeMap
<String
, TomlDependency
>>,
1991 #[serde(rename = "dev_dependencies")]
1992 dev_dependencies2
: Option
<BTreeMap
<String
, TomlDependency
>>,
1996 fn new() -> TomlTarget
{
1997 TomlTarget
::default()
2000 fn name(&self) -> String
{
2002 Some(ref name
) => name
.clone(),
2003 None
=> panic
!("target name is required"),
2007 fn proc_macro(&self) -> Option
<bool
> {
2008 self.proc_macro_raw
.or(self.proc_macro_raw2
).or_else(|| {
2009 if let Some(types
) = self.crate_types() {
2010 if types
.contains(&"proc-macro".to_string()) {
2018 fn crate_types(&self) -> Option
<&Vec
<String
>> {
2021 .or_else(|| self.crate_type2
.as_ref())
2025 impl fmt
::Debug
for PathValue
{
2026 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{