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 // At the moment, no compatibility checks are needed.
156 .map_err(|e
| anyhow
::Error
::from(e
).context("could not parse input as TOML"))
159 type TomlLibTarget
= TomlTarget
;
160 type TomlBinTarget
= TomlTarget
;
161 type TomlExampleTarget
= TomlTarget
;
162 type TomlTestTarget
= TomlTarget
;
163 type TomlBenchTarget
= TomlTarget
;
165 #[derive(Clone, Debug, Serialize)]
167 pub enum TomlDependency
<P
= String
> {
168 /// In the simple format, only a version is specified, eg.
169 /// `package = "<version>"`
171 /// The simple format is equivalent to a detailed dependency
172 /// specifying only a version, eg.
173 /// `package = { version = "<version>" }`
174 Detailed(DetailedTomlDependency
<P
>),
177 impl<'de
, P
: Deserialize
<'de
>> de
::Deserialize
<'de
> for TomlDependency
<P
> {
178 fn deserialize
<D
>(deserializer
: D
) -> Result
<Self, D
::Error
>
180 D
: de
::Deserializer
<'de
>,
182 struct TomlDependencyVisitor
<P
>(PhantomData
<P
>);
184 impl<'de
, P
: Deserialize
<'de
>> de
::Visitor
<'de
> for TomlDependencyVisitor
<P
> {
185 type Value
= TomlDependency
<P
>;
187 fn expecting(&self, formatter
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
189 "a version string like \"0.9.8\" or a \
190 detailed dependency like { version = \"0.9.8\" }",
194 fn visit_str
<E
>(self, s
: &str) -> Result
<Self::Value
, E
>
198 Ok(TomlDependency
::Simple(s
.to_owned()))
201 fn visit_map
<V
>(self, map
: V
) -> Result
<Self::Value
, V
::Error
>
203 V
: de
::MapAccess
<'de
>,
205 let mvd
= de
::value
::MapAccessDeserializer
::new(map
);
206 DetailedTomlDependency
::deserialize(mvd
).map(TomlDependency
::Detailed
)
210 deserializer
.deserialize_any(TomlDependencyVisitor(PhantomData
))
214 pub trait ResolveToPath
{
215 fn resolve(&self, config
: &Config
) -> PathBuf
;
218 impl ResolveToPath
for String
{
219 fn resolve(&self, _
: &Config
) -> PathBuf
{
224 impl ResolveToPath
for ConfigRelativePath
{
225 fn resolve(&self, c
: &Config
) -> PathBuf
{
230 #[derive(Deserialize, Serialize, Clone, Debug)]
231 #[serde(rename_all = "kebab-case")]
232 pub struct DetailedTomlDependency
<P
= String
> {
233 version
: Option
<String
>,
234 registry
: Option
<String
>,
235 /// The URL of the `registry` field.
236 /// This is an internal implementation detail. When Cargo creates a
237 /// package, it replaces `registry` with `registry-index` so that the
238 /// manifest contains the correct URL. All users won't have the same
239 /// registry names configured, so Cargo can't rely on just the name for
240 /// crates published by other users.
241 registry_index
: Option
<String
>,
242 // `path` is relative to the file it appears in. If that's a `Cargo.toml`, it'll be relative to
243 // that TOML file, and if it's a `.cargo/config` file, it'll be relative to that file.
246 branch
: Option
<String
>,
249 features
: Option
<Vec
<String
>>,
250 optional
: Option
<bool
>,
251 default_features
: Option
<bool
>,
252 #[serde(rename = "default_features")]
253 default_features2
: Option
<bool
>,
254 package
: Option
<String
>,
255 public
: Option
<bool
>,
258 // Explicit implementation so we avoid pulling in P: Default
259 impl<P
> Default
for DetailedTomlDependency
<P
> {
260 fn default() -> Self {
262 version
: Default
::default(),
263 registry
: Default
::default(),
264 registry_index
: Default
::default(),
265 path
: Default
::default(),
266 git
: Default
::default(),
267 branch
: Default
::default(),
268 tag
: Default
::default(),
269 rev
: Default
::default(),
270 features
: Default
::default(),
271 optional
: Default
::default(),
272 default_features
: Default
::default(),
273 default_features2
: Default
::default(),
274 package
: Default
::default(),
275 public
: Default
::default(),
280 /// This type is used to deserialize `Cargo.toml` files.
281 #[derive(Debug, Deserialize, Serialize)]
282 #[serde(rename_all = "kebab-case")]
283 pub struct TomlManifest
{
284 cargo_features
: Option
<Vec
<String
>>,
285 package
: Option
<Box
<TomlProject
>>,
286 project
: Option
<Box
<TomlProject
>>,
287 profile
: Option
<TomlProfiles
>,
288 lib
: Option
<TomlLibTarget
>,
289 bin
: Option
<Vec
<TomlBinTarget
>>,
290 example
: Option
<Vec
<TomlExampleTarget
>>,
291 test
: Option
<Vec
<TomlTestTarget
>>,
292 bench
: Option
<Vec
<TomlTestTarget
>>,
293 dependencies
: Option
<BTreeMap
<String
, TomlDependency
>>,
294 dev_dependencies
: Option
<BTreeMap
<String
, TomlDependency
>>,
295 #[serde(rename = "dev_dependencies")]
296 dev_dependencies2
: Option
<BTreeMap
<String
, TomlDependency
>>,
297 build_dependencies
: Option
<BTreeMap
<String
, TomlDependency
>>,
298 #[serde(rename = "build_dependencies")]
299 build_dependencies2
: Option
<BTreeMap
<String
, TomlDependency
>>,
300 features
: Option
<BTreeMap
<InternedString
, Vec
<InternedString
>>>,
301 target
: Option
<BTreeMap
<String
, TomlPlatform
>>,
302 replace
: Option
<BTreeMap
<String
, TomlDependency
>>,
303 patch
: Option
<BTreeMap
<String
, BTreeMap
<String
, TomlDependency
>>>,
304 workspace
: Option
<TomlWorkspace
>,
305 badges
: Option
<BTreeMap
<String
, BTreeMap
<String
, String
>>>,
308 #[derive(Deserialize, Serialize, Clone, Debug, Default)]
309 pub struct TomlProfiles(BTreeMap
<InternedString
, TomlProfile
>);
312 pub fn get_all(&self) -> &BTreeMap
<InternedString
, TomlProfile
> {
316 pub fn get(&self, name
: &str) -> Option
<&TomlProfile
> {
320 pub fn validate(&self, features
: &Features
, warnings
: &mut Vec
<String
>) -> CargoResult
<()> {
321 for (name
, profile
) in &self.0 {
322 profile
.validate(name
, features
, warnings
)?
;
328 #[derive(Clone, Debug, Eq, PartialEq)]
329 pub struct TomlOptLevel(pub String
);
331 impl<'de
> de
::Deserialize
<'de
> for TomlOptLevel
{
332 fn deserialize
<D
>(d
: D
) -> Result
<TomlOptLevel
, D
::Error
>
334 D
: de
::Deserializer
<'de
>,
338 impl<'de
> de
::Visitor
<'de
> for Visitor
{
339 type Value
= TomlOptLevel
;
341 fn expecting(&self, formatter
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
342 formatter
.write_str("an optimization level")
345 fn visit_i64
<E
>(self, value
: i64) -> Result
<TomlOptLevel
, E
>
349 Ok(TomlOptLevel(value
.to_string()))
352 fn visit_str
<E
>(self, value
: &str) -> Result
<TomlOptLevel
, E
>
356 if value
== "s" || value
== "z" {
357 Ok(TomlOptLevel(value
.to_string()))
359 Err(E
::custom(format
!(
360 "must be `0`, `1`, `2`, `3`, `s` or `z`, \
361 but found the string: \"{}\"",
368 d
.deserialize_any(Visitor
)
372 impl ser
::Serialize
for TomlOptLevel
{
373 fn serialize
<S
>(&self, serializer
: S
) -> Result
<S
::Ok
, S
::Error
>
377 match self.0.parse
::<u32>() {
378 Ok(n
) => n
.serialize(serializer
),
379 Err(_
) => self.0.serialize(serializer
),
384 #[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
385 #[serde(untagged, expecting = "expected a boolean or an integer")]
391 #[derive(Deserialize, Serialize, Clone, Debug, Default, Eq, PartialEq)]
392 #[serde(default, rename_all = "kebab-case")]
393 pub struct TomlProfile
{
394 pub opt_level
: Option
<TomlOptLevel
>,
395 pub lto
: Option
<StringOrBool
>,
396 pub codegen_backend
: Option
<InternedString
>,
397 pub codegen_units
: Option
<u32>,
398 pub debug
: Option
<U32OrBool
>,
399 pub split_debuginfo
: Option
<String
>,
400 pub debug_assertions
: Option
<bool
>,
401 pub rpath
: Option
<bool
>,
402 pub panic
: Option
<String
>,
403 pub overflow_checks
: Option
<bool
>,
404 pub incremental
: Option
<bool
>,
405 pub dir_name
: Option
<InternedString
>,
406 pub inherits
: Option
<InternedString
>,
407 pub strip
: Option
<StringOrBool
>,
408 // These two fields must be last because they are sub-tables, and TOML
409 // requires all non-tables to be listed first.
410 pub package
: Option
<BTreeMap
<ProfilePackageSpec
, TomlProfile
>>,
411 pub build_override
: Option
<Box
<TomlProfile
>>,
414 #[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
415 pub enum ProfilePackageSpec
{
420 impl ser
::Serialize
for ProfilePackageSpec
{
421 fn serialize
<S
>(&self, s
: S
) -> Result
<S
::Ok
, S
::Error
>
426 ProfilePackageSpec
::Spec(ref spec
) => spec
.serialize(s
),
427 ProfilePackageSpec
::All
=> "*".serialize(s
),
432 impl<'de
> de
::Deserialize
<'de
> for ProfilePackageSpec
{
433 fn deserialize
<D
>(d
: D
) -> Result
<ProfilePackageSpec
, D
::Error
>
435 D
: de
::Deserializer
<'de
>,
437 let string
= String
::deserialize(d
)?
;
439 Ok(ProfilePackageSpec
::All
)
441 PackageIdSpec
::parse(&string
)
442 .map_err(de
::Error
::custom
)
443 .map(ProfilePackageSpec
::Spec
)
453 warnings
: &mut Vec
<String
>,
454 ) -> CargoResult
<()> {
455 if let Some(ref profile
) = self.build_override
{
456 features
.require(Feature
::profile_overrides())?
;
457 profile
.validate_override("build-override", features
)?
;
459 if let Some(ref packages
) = self.package
{
460 features
.require(Feature
::profile_overrides())?
;
461 for profile
in packages
.values() {
462 profile
.validate_override("package", features
)?
;
466 // Feature gate definition of named profiles
468 "dev" | "release" | "bench" | "test" | "doc" => {}
470 features
.require(Feature
::named_profiles())?
;
474 // Profile name validation
475 Self::validate_name(name
)?
;
477 // Feature gate on uses of keys related to named profiles
478 if self.inherits
.is_some() {
479 features
.require(Feature
::named_profiles())?
;
482 if let Some(dir_name
) = self.dir_name
{
483 // This is disabled for now, as we would like to stabilize named
484 // profiles without this, and then decide in the future if it is
485 // needed. This helps simplify the UI a little.
487 "dir-name=\"{}\" in profile `{}` is not currently allowed, \
488 directory names are tied to the profile name for custom profiles",
494 // `inherits` validation
495 if matches
!(self.inherits
.map(|s
| s
.as_str()), Some("debug")) {
497 "profile.{}.inherits=\"debug\" should be profile.{}.inherits=\"dev\"",
505 warnings
.push("profile `doc` is deprecated and has no effect".to_string());
507 "test" | "bench" => {
508 if self.panic
.is_some() {
509 warnings
.push(format
!("`panic` setting is ignored for `{}` profile", name
))
515 if let Some(panic
) = &self.panic
{
516 if panic
!= "unwind" && panic
!= "abort" {
518 "`panic` setting of `{}` is not a valid setting, \
519 must be `unwind` or `abort`",
525 if self.strip
.is_some() {
526 features
.require(Feature
::strip())?
;
529 if let Some(codegen_backend
) = &self.codegen_backend
{
530 features
.require(Feature
::codegen_backend())?
;
531 if codegen_backend
.contains(|c
: char| !c
.is_ascii_alphanumeric() && c
!= '_'
) {
533 "`profile.{}.codegen-backend` setting of `{}` is not a valid backend name.",
543 /// Validate dir-names and profile names according to RFC 2678.
544 pub fn validate_name(name
: &str) -> CargoResult
<()> {
545 if let Some(ch
) = name
547 .find(|ch
| !ch
.is_alphanumeric() && *ch
!= '_'
&& *ch
!= '
-'
)
550 "invalid character `{}` in profile name `{}`\n\
551 Allowed characters are letters, numbers, underscore, and hyphen.",
557 const SEE_DOCS
: &str = "See https://doc.rust-lang.org/cargo/reference/profiles.html \
558 for more on configuring profiles.";
560 let lower_name
= name
.to_lowercase();
561 if lower_name
== "debug" {
563 "profile name `{}` is reserved\n\
564 To configure the default development profile, use the name `dev` \
565 as in [profile.dev]\n\
571 if lower_name
== "build-override" {
573 "profile name `{}` is reserved\n\
574 To configure build dependency settings, use [profile.dev.build-override] \
575 and [profile.release.build-override]\n\
582 // These are some arbitrary reservations. We have no plans to use
583 // these, but it seems safer to reserve a few just in case we want to
584 // add more built-in profiles in the future. We can also uses special
585 // syntax like cargo:foo if needed. But it is unlikely these will ever
607 ) || lower_name
.starts_with("cargo")
610 "profile name `{}` is reserved\n\
611 Please choose a different name.\n\
621 fn validate_override(&self, which
: &str, features
: &Features
) -> CargoResult
<()> {
622 if self.package
.is_some() {
623 bail
!("package-specific profiles cannot be nested");
625 if self.build_override
.is_some() {
626 bail
!("build-override profiles cannot be nested");
628 if self.panic
.is_some() {
629 bail
!("`panic` may not be specified in a `{}` profile", which
)
631 if self.lto
.is_some() {
632 bail
!("`lto` may not be specified in a `{}` profile", which
)
634 if self.rpath
.is_some() {
635 bail
!("`rpath` may not be specified in a `{}` profile", which
)
637 if self.codegen_backend
.is_some() {
638 features
.require(Feature
::codegen_backend())?
;
643 /// Overwrite self's values with the given profile.
644 pub fn merge(&mut self, profile
: &TomlProfile
) {
645 if let Some(v
) = &profile
.opt_level
{
646 self.opt_level
= Some(v
.clone());
649 if let Some(v
) = &profile
.lto
{
650 self.lto
= Some(v
.clone());
653 if let Some(v
) = profile
.codegen_backend
{
654 self.codegen_backend
= Some(v
);
657 if let Some(v
) = profile
.codegen_units
{
658 self.codegen_units
= Some(v
);
661 if let Some(v
) = &profile
.debug
{
662 self.debug
= Some(v
.clone());
665 if let Some(v
) = profile
.debug_assertions
{
666 self.debug_assertions
= Some(v
);
669 if let Some(v
) = &profile
.split_debuginfo
{
670 self.split_debuginfo
= Some(v
.clone());
673 if let Some(v
) = profile
.rpath
{
674 self.rpath
= Some(v
);
677 if let Some(v
) = &profile
.panic
{
678 self.panic
= Some(v
.clone());
681 if let Some(v
) = profile
.overflow_checks
{
682 self.overflow_checks
= Some(v
);
685 if let Some(v
) = profile
.incremental
{
686 self.incremental
= Some(v
);
689 if let Some(other_package
) = &profile
.package
{
690 match &mut self.package
{
691 Some(self_package
) => {
692 for (spec
, other_pkg_profile
) in other_package
{
693 match self_package
.get_mut(spec
) {
694 Some(p
) => p
.merge(other_pkg_profile
),
696 self_package
.insert(spec
.clone(), other_pkg_profile
.clone());
701 None
=> self.package
= Some(other_package
.clone()),
705 if let Some(other_bo
) = &profile
.build_override
{
706 match &mut self.build_override
{
707 Some(self_bo
) => self_bo
.merge(other_bo
),
708 None
=> self.build_override
= Some(other_bo
.clone()),
712 if let Some(v
) = &profile
.inherits
{
713 self.inherits
= Some(*v
);
716 if let Some(v
) = &profile
.dir_name
{
717 self.dir_name
= Some(*v
);
720 if let Some(v
) = &profile
.strip
{
721 self.strip
= Some(v
.clone());
726 /// A StringOrVec can be parsed from either a TOML string or array,
727 /// but is always stored as a vector.
728 #[derive(Clone, Debug, Serialize, Eq, PartialEq, PartialOrd, Ord)]
729 pub struct StringOrVec(Vec
<String
>);
731 impl<'de
> de
::Deserialize
<'de
> for StringOrVec
{
732 fn deserialize
<D
>(deserializer
: D
) -> Result
<Self, D
::Error
>
734 D
: de
::Deserializer
<'de
>,
738 impl<'de
> de
::Visitor
<'de
> for Visitor
{
739 type Value
= StringOrVec
;
741 fn expecting(&self, formatter
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
742 formatter
.write_str("string or list of strings")
745 fn visit_str
<E
>(self, s
: &str) -> Result
<Self::Value
, E
>
749 Ok(StringOrVec(vec
![s
.to_string()]))
752 fn visit_seq
<V
>(self, v
: V
) -> Result
<Self::Value
, V
::Error
>
754 V
: de
::SeqAccess
<'de
>,
756 let seq
= de
::value
::SeqAccessDeserializer
::new(v
);
757 Vec
::deserialize(seq
).map(StringOrVec
)
761 deserializer
.deserialize_any(Visitor
)
766 pub fn iter
<'a
>(&'a
self) -> std
::slice
::Iter
<'a
, String
> {
771 #[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
772 #[serde(untagged, expecting = "expected a boolean or a string")]
773 pub enum StringOrBool
{
778 #[derive(PartialEq, Clone, Debug, Serialize)]
780 pub enum VecStringOrBool
{
781 VecString(Vec
<String
>),
785 impl<'de
> de
::Deserialize
<'de
> for VecStringOrBool
{
786 fn deserialize
<D
>(deserializer
: D
) -> Result
<Self, D
::Error
>
788 D
: de
::Deserializer
<'de
>,
792 impl<'de
> de
::Visitor
<'de
> for Visitor
{
793 type Value
= VecStringOrBool
;
795 fn expecting(&self, formatter
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
796 formatter
.write_str("a boolean or vector of strings")
799 fn visit_seq
<V
>(self, v
: V
) -> Result
<Self::Value
, V
::Error
>
801 V
: de
::SeqAccess
<'de
>,
803 let seq
= de
::value
::SeqAccessDeserializer
::new(v
);
804 Vec
::deserialize(seq
).map(VecStringOrBool
::VecString
)
807 fn visit_bool
<E
>(self, b
: bool
) -> Result
<Self::Value
, E
>
811 Ok(VecStringOrBool
::Bool(b
))
815 deserializer
.deserialize_any(Visitor
)
819 fn version_trim_whitespace
<'de
, D
>(deserializer
: D
) -> Result
<semver
::Version
, D
::Error
>
821 D
: de
::Deserializer
<'de
>,
825 impl<'de
> de
::Visitor
<'de
> for Visitor
{
826 type Value
= semver
::Version
;
828 fn expecting(&self, formatter
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
829 formatter
.write_str("SemVer version")
832 fn visit_str
<E
>(self, string
: &str) -> Result
<Self::Value
, E
>
836 string
.trim().parse().map_err(de
::Error
::custom
)
840 deserializer
.deserialize_str(Visitor
)
843 /// Represents the `package`/`project` sections of a `Cargo.toml`.
845 /// Note that the order of the fields matters, since this is the order they
846 /// are serialized to a TOML file. For example, you cannot have values after
847 /// the field `metadata`, since it is a table and values cannot appear after
849 #[derive(Deserialize, Serialize, Clone, Debug)]
850 #[serde(rename_all = "kebab-case")]
851 pub struct TomlProject
{
852 edition
: Option
<String
>,
853 rust_version
: Option
<String
>,
854 name
: InternedString
,
855 #[serde(deserialize_with = "version_trim_whitespace")]
856 version
: semver
::Version
,
857 authors
: Option
<Vec
<String
>>,
858 build
: Option
<StringOrBool
>,
859 metabuild
: Option
<StringOrVec
>,
860 #[serde(rename = "default-target")]
861 default_target
: Option
<String
>,
862 #[serde(rename = "forced-target")]
863 forced_target
: Option
<String
>,
864 links
: Option
<String
>,
865 exclude
: Option
<Vec
<String
>>,
866 include
: Option
<Vec
<String
>>,
867 publish
: Option
<VecStringOrBool
>,
868 workspace
: Option
<String
>,
869 im_a_teapot
: Option
<bool
>,
870 autobins
: Option
<bool
>,
871 autoexamples
: Option
<bool
>,
872 autotests
: Option
<bool
>,
873 autobenches
: Option
<bool
>,
874 default_run
: Option
<String
>,
877 description
: Option
<String
>,
878 homepage
: Option
<String
>,
879 documentation
: Option
<String
>,
880 readme
: Option
<StringOrBool
>,
881 keywords
: Option
<Vec
<String
>>,
882 categories
: Option
<Vec
<String
>>,
883 license
: Option
<String
>,
884 license_file
: Option
<String
>,
885 repository
: Option
<String
>,
886 resolver
: Option
<String
>,
888 // Note that this field must come last due to the way toml serialization
889 // works which requires tables to be emitted after all values.
890 metadata
: Option
<toml
::Value
>,
893 #[derive(Debug, Deserialize, Serialize)]
894 pub struct TomlWorkspace
{
895 members
: Option
<Vec
<String
>>,
896 #[serde(rename = "default-members")]
897 default_members
: Option
<Vec
<String
>>,
898 exclude
: Option
<Vec
<String
>>,
899 resolver
: Option
<String
>,
901 // Note that this field must come last due to the way toml serialization
902 // works which requires tables to be emitted after all values.
903 metadata
: Option
<toml
::Value
>,
907 pub fn to_package_id(&self, source_id
: SourceId
) -> CargoResult
<PackageId
> {
908 PackageId
::new(self.name
, self.version
.clone(), source_id
)
912 struct Context
<'a
, 'b
> {
913 deps
: &'a
mut Vec
<Dependency
>,
915 nested_paths
: &'a
mut Vec
<PathBuf
>,
917 warnings
: &'a
mut Vec
<String
>,
918 platform
: Option
<Platform
>,
920 features
: &'a Features
,
924 /// Prepares the manifest for publishing.
925 // - Path and git components of dependency specifications are removed.
926 // - License path is updated to point within the package.
927 pub fn prepare_for_publish(
931 ) -> CargoResult
<TomlManifest
> {
932 let config
= ws
.config();
933 let mut package
= self
936 .or_else(|| self.project
.as_ref())
939 package
.workspace
= None
;
940 package
.resolver
= ws
.resolve_behavior().to_manifest();
941 if let Some(license_file
) = &package
.license_file
{
942 let license_path
= Path
::new(&license_file
);
943 let abs_license_path
= paths
::normalize_path(&package_root
.join(license_path
));
944 if abs_license_path
.strip_prefix(package_root
).is_err() {
945 // This path points outside of the package root. `cargo package`
946 // will copy it into the root, so adjust the path to this location.
947 package
.license_file
= Some(
957 let all
= |_d
: &TomlDependency
| true;
958 return Ok(TomlManifest
{
959 package
: Some(package
),
961 profile
: self.profile
.clone(),
962 lib
: self.lib
.clone(),
963 bin
: self.bin
.clone(),
964 example
: self.example
.clone(),
965 test
: self.test
.clone(),
966 bench
: self.bench
.clone(),
967 dependencies
: map_deps(config
, self.dependencies
.as_ref(), all
)?
,
968 dev_dependencies
: map_deps(
970 self.dev_dependencies
972 .or_else(|| self.dev_dependencies2
.as_ref()),
973 TomlDependency
::is_version_specified
,
975 dev_dependencies2
: None
,
976 build_dependencies
: map_deps(
978 self.build_dependencies
980 .or_else(|| self.build_dependencies2
.as_ref()),
983 build_dependencies2
: None
,
984 features
: self.features
.clone(),
985 target
: match self.target
.as_ref().map(|target_map
| {
992 dependencies
: map_deps(config
, v
.dependencies
.as_ref(), all
)?
,
993 dev_dependencies
: map_deps(
997 .or_else(|| v
.dev_dependencies2
.as_ref()),
998 TomlDependency
::is_version_specified
,
1000 dev_dependencies2
: None
,
1001 build_dependencies
: map_deps(
1003 v
.build_dependencies
1005 .or_else(|| v
.build_dependencies2
.as_ref()),
1008 build_dependencies2
: None
,
1014 Some(Ok(v
)) => Some(v
),
1015 Some(Err(e
)) => return Err(e
),
1021 badges
: self.badges
.clone(),
1022 cargo_features
: self.cargo_features
.clone(),
1027 deps
: Option
<&BTreeMap
<String
, TomlDependency
>>,
1028 filter
: impl Fn(&TomlDependency
) -> bool
,
1029 ) -> CargoResult
<Option
<BTreeMap
<String
, TomlDependency
>>> {
1030 let deps
= match deps
{
1032 None
=> return Ok(None
),
1036 .filter(|(_k
, v
)| filter(v
))
1037 .map(|(k
, v
)| Ok((k
.clone(), map_dependency(config
, v
)?
)))
1038 .collect
::<CargoResult
<BTreeMap
<_
, _
>>>()?
;
1042 fn map_dependency(config
: &Config
, dep
: &TomlDependency
) -> CargoResult
<TomlDependency
> {
1044 TomlDependency
::Detailed(d
) => {
1045 let mut d
= d
.clone();
1046 // Path dependencies become crates.io deps.
1048 // Same with git dependencies.
1053 // registry specifications are elaborated to the index URL
1054 if let Some(registry
) = d
.registry
.take() {
1055 let src
= SourceId
::alt_registry(config
, ®istry
)?
;
1056 d
.registry_index
= Some(src
.url().to_string());
1058 Ok(TomlDependency
::Detailed(d
))
1060 TomlDependency
::Simple(s
) => Ok(TomlDependency
::Detailed(DetailedTomlDependency
{
1061 version
: Some(s
.clone()),
1062 ..Default
::default()
1068 pub fn to_real_manifest(
1069 me
: &Rc
<TomlManifest
>,
1070 source_id
: SourceId
,
1071 package_root
: &Path
,
1073 ) -> CargoResult
<(Manifest
, Vec
<PathBuf
>)> {
1074 let mut nested_paths
= vec
![];
1075 let mut warnings
= vec
![];
1076 let mut errors
= vec
![];
1078 // Parse features first so they will be available when parsing other parts of the TOML.
1079 let empty
= Vec
::new();
1080 let cargo_features
= me
.cargo_features
.as_ref().unwrap_or(&empty
);
1081 let features
= Features
::new(cargo_features
, config
, &mut warnings
, source_id
.is_path())?
;
1083 let project
= me
.project
.as_ref().or_else(|| me
.package
.as_ref());
1084 let project
= project
.ok_or_else(|| anyhow
!("no `package` section found"))?
;
1086 let package_name
= project
.name
.trim();
1087 if package_name
.is_empty() {
1088 bail
!("package name cannot be an empty string")
1091 validate_package_name(package_name
, "package name", "")?
;
1093 let pkgid
= project
.to_package_id(source_id
)?
;
1095 let edition
= if let Some(ref edition
) = project
.edition
{
1097 .require(Feature
::edition())
1098 .with_context(|| "editions are unstable")?
;
1101 .with_context(|| "failed to parse the `edition` key")?
1103 Edition
::Edition2015
1105 if edition
== Edition
::Edition2021
{
1106 features
.require(Feature
::edition2021())?
;
1107 } else if !edition
.is_stable() {
1108 // Guard in case someone forgets to add .require()
1109 return Err(util
::errors
::internal(format
!(
1110 "edition {} should be gated",
1115 let rust_version
= if let Some(rust_version
) = &project
.rust_version
{
1116 let req
= match semver
::VersionReq
::parse(rust_version
) {
1117 // Exclude semver operators like `^` and pre-release identifiers
1118 Ok(req
) if rust_version
.chars().all(|c
| c
.is_ascii_digit() || c
== '
.'
) => req
,
1119 _
=> bail
!("`rust-version` must be a value like \"1.32\""),
1121 if let Some(first_version
) = edition
.first_version() {
1123 semver
::Version
::new(first_version
.major
, first_version
.minor
- 1, 9999);
1124 if req
.matches(&unsupported
) {
1126 "rust-version {} is older than first version ({}) required by \
1127 the specified edition ({})",
1134 Some(rust_version
.clone())
1139 if project
.metabuild
.is_some() {
1140 features
.require(Feature
::metabuild())?
;
1143 if project
.resolver
.is_some()
1147 .map_or(false, |ws
| ws
.resolver
.is_some())
1149 features
.require(Feature
::resolver())?
;
1151 let resolve_behavior
= match (
1152 project
.resolver
.as_ref(),
1153 me
.workspace
.as_ref().and_then(|ws
| ws
.resolver
.as_ref()),
1155 (None
, None
) => None
,
1156 (Some(s
), None
) | (None
, Some(s
)) => Some(ResolveBehavior
::from_manifest(s
)?
),
1157 (Some(_
), Some(_
)) => {
1158 bail
!("cannot specify `resolver` field in both `[workspace]` and `[package]`")
1162 // If we have no lib at all, use the inferred lib, if available.
1163 // If we have a lib with a path, we're done.
1164 // If we have a lib with no path, use the inferred lib or else the package name.
1165 let targets
= targets(
1177 if targets
.is_empty() {
1178 debug
!("manifest has no build targets");
1181 if let Err(e
) = unique_build_targets(&targets
, package_root
) {
1182 warnings
.push(format
!(
1183 "file found to be present in multiple \
1189 if let Some(links
) = &project
.links
{
1190 if !targets
.iter().any(|t
| t
.is_custom_build()) {
1192 "package `{}` specifies that it links to `{}` but does not \
1193 have a custom build script",
1200 let mut deps
= Vec
::new();
1205 let mut cx
= Context
{
1208 nested_paths
: &mut nested_paths
,
1210 warnings
: &mut warnings
,
1211 features
: &features
,
1216 fn process_dependencies(
1217 cx
: &mut Context
<'_
, '_
>,
1218 new_deps
: Option
<&BTreeMap
<String
, TomlDependency
>>,
1219 kind
: Option
<DepKind
>,
1220 ) -> CargoResult
<()> {
1221 let dependencies
= match new_deps
{
1222 Some(dependencies
) => dependencies
,
1223 None
=> return Ok(()),
1225 for (n
, v
) in dependencies
.iter() {
1226 let dep
= v
.to_dependency(n
, cx
, kind
)?
;
1227 validate_package_name(dep
.name_in_toml().as_str(), "dependency name", "")?
;
1234 // Collect the dependencies.
1235 process_dependencies(&mut cx
, me
.dependencies
.as_ref(), None
)?
;
1239 .or_else(|| me
.dev_dependencies2
.as_ref());
1240 process_dependencies(&mut cx
, dev_deps
, Some(DepKind
::Development
))?
;
1244 .or_else(|| me
.build_dependencies2
.as_ref());
1245 process_dependencies(&mut cx
, build_deps
, Some(DepKind
::Build
))?
;
1247 for (name
, platform
) in me
.target
.iter().flatten() {
1249 let platform
: Platform
= name
.parse()?
;
1250 platform
.check_cfg_attributes(&mut cx
.warnings
);
1253 process_dependencies(&mut cx
, platform
.dependencies
.as_ref(), None
)?
;
1254 let build_deps
= platform
1257 .or_else(|| platform
.build_dependencies2
.as_ref());
1258 process_dependencies(&mut cx
, build_deps
, Some(DepKind
::Build
))?
;
1259 let dev_deps
= platform
1262 .or_else(|| platform
.dev_dependencies2
.as_ref());
1263 process_dependencies(&mut cx
, dev_deps
, Some(DepKind
::Development
))?
;
1266 replace
= me
.replace(&mut cx
)?
;
1267 patch
= me
.patch(&mut cx
)?
;
1271 let mut names_sources
= BTreeMap
::new();
1273 let name
= dep
.name_in_toml();
1274 let prev
= names_sources
.insert(name
.to_string(), dep
.source_id());
1275 if prev
.is_some() && prev
!= Some(dep
.source_id()) {
1277 "Dependency '{}' has different source paths depending on the build \
1278 target. Each dependency must have a single canonical source path \
1279 irrespective of build target.",
1286 let exclude
= project
.exclude
.clone().unwrap_or_default();
1287 let include
= project
.include
.clone().unwrap_or_default();
1288 let empty_features
= BTreeMap
::new();
1290 let summary
= Summary
::new(
1294 me
.features
.as_ref().unwrap_or(&empty_features
),
1295 project
.links
.as_deref(),
1297 let unstable
= config
.cli_unstable();
1298 summary
.unstable_gate(unstable
.namespaced_features
, unstable
.weak_dep_features
)?
;
1300 let metadata
= ManifestMetadata
{
1301 description
: project
.description
.clone(),
1302 homepage
: project
.homepage
.clone(),
1303 documentation
: project
.documentation
.clone(),
1304 readme
: readme_for_project(package_root
, project
),
1305 authors
: project
.authors
.clone().unwrap_or_default(),
1306 license
: project
.license
.clone(),
1307 license_file
: project
.license_file
.clone(),
1308 repository
: project
.repository
.clone(),
1309 keywords
: project
.keywords
.clone().unwrap_or_default(),
1310 categories
: project
.categories
.clone().unwrap_or_default(),
1311 badges
: me
.badges
.clone().unwrap_or_default(),
1312 links
: project
.links
.clone(),
1315 let workspace_config
= match (me
.workspace
.as_ref(), project
.workspace
.as_ref()) {
1316 (Some(config
), None
) => WorkspaceConfig
::Root(WorkspaceRootConfig
::new(
1319 &config
.default_members
,
1323 (None
, root
) => WorkspaceConfig
::Member
{
1324 root
: root
.cloned(),
1326 (Some(..), Some(..)) => bail
!(
1327 "cannot configure both `package.workspace` and \
1328 `[workspace]`, only one can be specified"
1331 let profiles
= me
.profile
.clone();
1332 if let Some(profiles
) = &profiles
{
1333 profiles
.validate(&features
, &mut warnings
)?
;
1335 let publish
= match project
.publish
{
1336 Some(VecStringOrBool
::VecString(ref vecstring
)) => Some(vecstring
.clone()),
1337 Some(VecStringOrBool
::Bool(false)) => Some(vec
![]),
1338 None
| Some(VecStringOrBool
::Bool(true)) => None
,
1341 if summary
.features().contains_key("default-features") {
1343 "`default-features = [\"..\"]` was found in [features]. \
1344 Did you mean to use `default = [\"..\"]`?"
1349 if let Some(run
) = &project
.default_run
{
1352 .filter(|t
| t
.is_bin())
1353 .any(|t
| t
.name() == run
)
1356 util
::closest_msg(run
, targets
.iter().filter(|t
| t
.is_bin()), |t
| t
.name());
1357 bail
!("default-run target `{}` not found{}", run
, suggestion
);
1361 let default_kind
= project
1364 .map(|t
| CompileTarget
::new(&*t
))
1366 .map(CompileKind
::Target
);
1367 let forced_kind
= project
1370 .map(|t
| CompileTarget
::new(&*t
))
1372 .map(CompileKind
::Target
);
1374 let custom_metadata
= project
.metadata
.clone();
1375 let mut manifest
= Manifest
::new(
1382 project
.links
.clone(),
1393 project
.im_a_teapot
,
1394 project
.default_run
.clone(),
1396 project
.metabuild
.clone().map(|sov
| sov
.0),
1399 if project
.license_file
.is_some() && project
.license
.is_some() {
1400 manifest
.warnings_mut().add_warning(
1401 "only one of `license` or `license-file` is necessary\n\
1402 `license` should be used if the package license can be expressed \
1403 with a standard SPDX expression.\n\
1404 `license-file` should be used if the package uses a non-standard license.\n\
1405 See https://doc.rust-lang.org/cargo/reference/manifest.html#the-license-and-license-file-fields \
1406 for more information."
1410 for warning
in warnings
{
1411 manifest
.warnings_mut().add_warning(warning
);
1413 for error
in errors
{
1414 manifest
.warnings_mut().add_critical_warning(error
);
1417 manifest
.feature_gate()?
;
1419 Ok((manifest
, nested_paths
))
1422 fn to_virtual_manifest(
1423 me
: &Rc
<TomlManifest
>,
1424 source_id
: SourceId
,
1427 ) -> CargoResult
<(VirtualManifest
, Vec
<PathBuf
>)> {
1428 if me
.project
.is_some() {
1429 bail
!("this virtual manifest specifies a [project] section, which is not allowed");
1431 if me
.package
.is_some() {
1432 bail
!("this virtual manifest specifies a [package] section, which is not allowed");
1434 if me
.lib
.is_some() {
1435 bail
!("this virtual manifest specifies a [lib] section, which is not allowed");
1437 if me
.bin
.is_some() {
1438 bail
!("this virtual manifest specifies a [[bin]] section, which is not allowed");
1440 if me
.example
.is_some() {
1441 bail
!("this virtual manifest specifies a [[example]] section, which is not allowed");
1443 if me
.test
.is_some() {
1444 bail
!("this virtual manifest specifies a [[test]] section, which is not allowed");
1446 if me
.bench
.is_some() {
1447 bail
!("this virtual manifest specifies a [[bench]] section, which is not allowed");
1449 if me
.dependencies
.is_some() {
1450 bail
!("this virtual manifest specifies a [dependencies] section, which is not allowed");
1452 if me
.dev_dependencies
.is_some() || me
.dev_dependencies2
.is_some() {
1453 bail
!("this virtual manifest specifies a [dev-dependencies] section, which is not allowed");
1455 if me
.build_dependencies
.is_some() || me
.build_dependencies2
.is_some() {
1456 bail
!("this virtual manifest specifies a [build-dependencies] section, which is not allowed");
1458 if me
.features
.is_some() {
1459 bail
!("this virtual manifest specifies a [features] section, which is not allowed");
1461 if me
.target
.is_some() {
1462 bail
!("this virtual manifest specifies a [target] section, which is not allowed");
1464 if me
.badges
.is_some() {
1465 bail
!("this virtual manifest specifies a [badges] section, which is not allowed");
1468 let mut nested_paths
= Vec
::new();
1469 let mut warnings
= Vec
::new();
1470 let mut deps
= Vec
::new();
1471 let empty
= Vec
::new();
1472 let cargo_features
= me
.cargo_features
.as_ref().unwrap_or(&empty
);
1473 let features
= Features
::new(cargo_features
, config
, &mut warnings
, source_id
.is_path())?
;
1475 let (replace
, patch
) = {
1476 let mut cx
= Context
{
1479 nested_paths
: &mut nested_paths
,
1481 warnings
: &mut warnings
,
1483 features
: &features
,
1486 (me
.replace(&mut cx
)?
, me
.patch(&mut cx
)?
)
1488 let profiles
= me
.profile
.clone();
1489 if let Some(profiles
) = &profiles
{
1490 profiles
.validate(&features
, &mut warnings
)?
;
1495 .map_or(false, |ws
| ws
.resolver
.is_some())
1497 features
.require(Feature
::resolver())?
;
1499 let resolve_behavior
= me
1502 .and_then(|ws
| ws
.resolver
.as_deref())
1503 .map(|r
| ResolveBehavior
::from_manifest(r
))
1505 let workspace_config
= match me
.workspace
{
1506 Some(ref config
) => WorkspaceConfig
::Root(WorkspaceRootConfig
::new(
1509 &config
.default_members
,
1514 bail
!("virtual manifests must be configured with [workspace]");
1518 VirtualManifest
::new(
1530 fn replace(&self, cx
: &mut Context
<'_
, '_
>) -> CargoResult
<Vec
<(PackageIdSpec
, Dependency
)>> {
1531 if self.patch
.is_some() && self.replace
.is_some() {
1532 bail
!("cannot specify both [replace] and [patch]");
1534 let mut replace
= Vec
::new();
1535 for (spec
, replacement
) in self.replace
.iter().flatten() {
1536 let mut spec
= PackageIdSpec
::parse(spec
).with_context(|| {
1538 "replacements must specify a valid semver \
1539 version to replace, but `{}` does not",
1543 if spec
.url().is_none() {
1544 spec
.set_url(CRATES_IO_INDEX
.parse().unwrap());
1547 if replacement
.is_version_specified() {
1549 "replacements cannot specify a version \
1550 requirement, but found one for `{}`",
1555 let mut dep
= replacement
.to_dependency(spec
.name().as_str(), cx
, None
)?
;
1557 let version
= spec
.version().ok_or_else(|| {
1559 "replacements must specify a version \
1560 to replace, but `{}` does not",
1564 dep
.set_version_req(VersionReq
::exact(version
));
1566 replace
.push((spec
, dep
));
1571 fn patch(&self, cx
: &mut Context
<'_
, '_
>) -> CargoResult
<HashMap
<Url
, Vec
<Dependency
>>> {
1572 let mut patch
= HashMap
::new();
1573 for (url
, deps
) in self.patch
.iter().flatten() {
1574 let url
= match &url
[..] {
1575 CRATES_IO_REGISTRY
=> CRATES_IO_INDEX
.parse().unwrap(),
1578 .get_registry_index(url
)
1579 .or_else(|_
| url
.into_url())
1581 format
!("[patch] entry `{}` should be a URL or registry name", url
)
1587 .map(|(name
, dep
)| dep
.to_dependency(name
, cx
, None
))
1588 .collect
::<CargoResult
<Vec
<_
>>>()?
,
1594 /// Returns the path to the build script if one exists for this crate.
1595 fn maybe_custom_build(
1597 build
: &Option
<StringOrBool
>,
1598 package_root
: &Path
,
1599 ) -> Option
<PathBuf
> {
1600 let build_rs
= package_root
.join("build.rs");
1602 // Explicitly no build script.
1603 Some(StringOrBool
::Bool(false)) => None
,
1604 Some(StringOrBool
::Bool(true)) => Some(build_rs
),
1605 Some(StringOrBool
::String(ref s
)) => Some(PathBuf
::from(s
)),
1607 // If there is a `build.rs` file next to the `Cargo.toml`, assume it is
1609 if build_rs
.is_file() {
1618 pub fn has_profiles(&self) -> bool
{
1619 self.profile
.is_some()
1622 pub fn features(&self) -> Option
<&BTreeMap
<InternedString
, Vec
<InternedString
>>> {
1623 self.features
.as_ref()
1627 /// Returns the name of the README file for a `TomlProject`.
1628 fn readme_for_project(package_root
: &Path
, project
: &TomlProject
) -> Option
<String
> {
1629 match &project
.readme
{
1630 None
=> default_readme_from_package_root(package_root
),
1631 Some(value
) => match value
{
1632 StringOrBool
::Bool(false) => None
,
1633 StringOrBool
::Bool(true) => Some("README.md".to_string()),
1634 StringOrBool
::String(v
) => Some(v
.clone()),
1639 const DEFAULT_README_FILES
: [&str; 3] = ["README.md", "README.txt", "README"];
1641 /// Checks if a file with any of the default README file names exists in the package root.
1642 /// If so, returns a `String` representing that name.
1643 fn default_readme_from_package_root(package_root
: &Path
) -> Option
<String
> {
1644 for &readme_filename
in DEFAULT_README_FILES
.iter() {
1645 if package_root
.join(readme_filename
).is_file() {
1646 return Some(readme_filename
.to_string());
1653 /// Checks a list of build targets, and ensures the target names are unique within a vector.
1654 /// If not, the name of the offending build target is returned.
1655 fn unique_build_targets(targets
: &[Target
], package_root
: &Path
) -> Result
<(), String
> {
1656 let mut seen
= HashSet
::new();
1657 for target
in targets
{
1658 if let TargetSourcePath
::Path(path
) = target
.src_path() {
1659 let full
= package_root
.join(path
);
1660 if !seen
.insert(full
.clone()) {
1661 return Err(full
.display().to_string());
1668 impl<P
: ResolveToPath
> TomlDependency
<P
> {
1669 pub(crate) fn to_dependency_split(
1672 source_id
: SourceId
,
1673 nested_paths
: &mut Vec
<PathBuf
>,
1675 warnings
: &mut Vec
<String
>,
1676 platform
: Option
<Platform
>,
1678 features
: &Features
,
1679 kind
: Option
<DepKind
>,
1680 ) -> CargoResult
<Dependency
> {
1684 deps
: &mut Vec
::new(),
1700 cx
: &mut Context
<'_
, '_
>,
1701 kind
: Option
<DepKind
>,
1702 ) -> CargoResult
<Dependency
> {
1704 TomlDependency
::Simple(ref version
) => DetailedTomlDependency
::<P
> {
1705 version
: Some(version
.clone()),
1706 ..Default
::default()
1708 .to_dependency(name
, cx
, kind
),
1709 TomlDependency
::Detailed(ref details
) => details
.to_dependency(name
, cx
, kind
),
1713 fn is_version_specified(&self) -> bool
{
1715 TomlDependency
::Detailed(d
) => d
.version
.is_some(),
1716 TomlDependency
::Simple(..) => true,
1721 impl<P
: ResolveToPath
> DetailedTomlDependency
<P
> {
1725 cx
: &mut Context
<'_
, '_
>,
1726 kind
: Option
<DepKind
>,
1727 ) -> CargoResult
<Dependency
> {
1728 if self.version
.is_none() && self.path
.is_none() && self.git
.is_none() {
1730 "dependency ({}) specified without \
1731 providing a local path, Git repository, or \
1732 version to use. This will be considered an \
1733 error in future versions",
1736 cx
.warnings
.push(msg
);
1739 if let Some(version
) = &self.version
{
1740 if version
.contains('
+'
) {
1741 cx
.warnings
.push(format
!(
1742 "version requirement `{}` for dependency `{}` \
1743 includes semver metadata which will be ignored, removing the \
1744 metadata is recommended to avoid confusion",
1745 version
, name_in_toml
1750 if self.git
.is_none() {
1751 let git_only_keys
= [
1752 (&self.branch
, "branch"),
1757 for &(key
, key_name
) in &git_only_keys
{
1760 "key `{}` is ignored for dependency ({}).",
1768 // Early detection of potentially misused feature syntax
1769 // instead of generating a "feature not found" error.
1770 if let Some(features
) = &self.features
{
1771 for feature
in features
{
1772 if feature
.contains('
/'
) {
1774 "feature `{}` in dependency `{}` is not allowed to contain slashes\n\
1775 If you want to enable features of a transitive dependency, \
1776 the direct dependency needs to re-export those features from \
1777 the `[features]` table.",
1782 if feature
.starts_with("dep:") {
1784 "feature `{}` in dependency `{}` is not allowed to use explicit \
1786 If you want to enable an optional dependency, specify the name \
1787 of the optional dependency without the `dep:` prefix, or specify \
1788 a feature from the dependency's `[features]` table that enables \
1789 the optional dependency.",
1797 let new_source_id
= match (
1800 self.registry
.as_ref(),
1801 self.registry_index
.as_ref(),
1803 (Some(_
), _
, Some(_
), _
) | (Some(_
), _
, _
, Some(_
)) => bail
!(
1804 "dependency ({}) specification is ambiguous. \
1805 Only one of `git` or `registry` is allowed.",
1808 (_
, _
, Some(_
), Some(_
)) => bail
!(
1809 "dependency ({}) specification is ambiguous. \
1810 Only one of `registry` or `registry-index` is allowed.",
1813 (Some(git
), maybe_path
, _
, _
) => {
1814 if maybe_path
.is_some() {
1816 "dependency ({}) specification is ambiguous. \
1817 Only one of `git` or `path` is allowed.",
1822 let n_details
= [&self.branch
, &self.tag
, &self.rev
]
1824 .filter(|d
| d
.is_some())
1829 "dependency ({}) specification is ambiguous. \
1830 Only one of `branch`, `tag` or `rev` is allowed.",
1835 let reference
= self
1838 .map(GitReference
::Branch
)
1839 .or_else(|| self.tag
.clone().map(GitReference
::Tag
))
1840 .or_else(|| self.rev
.clone().map(GitReference
::Rev
))
1841 .unwrap_or(GitReference
::DefaultBranch
);
1842 let loc
= git
.into_url()?
;
1844 if let Some(fragment
) = loc
.fragment() {
1846 "URL fragment `#{}` in git URL is ignored for dependency ({}). \
1847 If you were trying to specify a specific git revision, \
1848 use `rev = \"{}\"` in the dependency declaration.",
1849 fragment
, name_in_toml
, fragment
1851 cx
.warnings
.push(msg
)
1854 SourceId
::for_git(&loc
, reference
)?
1856 (None
, Some(path
), _
, _
) => {
1857 let path
= path
.resolve(cx
.config
);
1858 cx
.nested_paths
.push(path
.clone());
1859 // If the source ID for the package we're parsing is a path
1860 // source, then we normalize the path here to get rid of
1861 // components like `..`.
1863 // The purpose of this is to get a canonical ID for the package
1864 // that we're depending on to ensure that builds of this package
1865 // always end up hashing to the same value no matter where it's
1867 if cx
.source_id
.is_path() {
1868 let path
= cx
.root
.join(path
);
1869 let path
= paths
::normalize_path(&path
);
1870 SourceId
::for_path(&path
)?
1875 (None
, None
, Some(registry
), None
) => SourceId
::alt_registry(cx
.config
, registry
)?
,
1876 (None
, None
, None
, Some(registry_index
)) => {
1877 let url
= registry_index
.into_url()?
;
1878 SourceId
::for_registry(&url
)?
1880 (None
, None
, None
, None
) => SourceId
::crates_io(cx
.config
)?
,
1883 let (pkg_name
, explicit_name_in_toml
) = match self.package
{
1884 Some(ref s
) => (&s
[..], Some(name_in_toml
)),
1885 None
=> (name_in_toml
, None
),
1888 let version
= self.version
.as_deref();
1889 let mut dep
= Dependency
::parse(pkg_name
, version
, new_source_id
)?
;
1890 dep
.set_features(self.features
.iter().flatten())
1891 .set_default_features(
1892 self.default_features
1893 .or(self.default_features2
)
1896 .set_optional(self.optional
.unwrap_or(false))
1897 .set_platform(cx
.platform
.clone());
1898 if let Some(registry
) = &self.registry
{
1899 let registry_id
= SourceId
::alt_registry(cx
.config
, registry
)?
;
1900 dep
.set_registry_id(registry_id
);
1902 if let Some(registry_index
) = &self.registry_index
{
1903 let url
= registry_index
.into_url()?
;
1904 let registry_id
= SourceId
::for_registry(&url
)?
;
1905 dep
.set_registry_id(registry_id
);
1908 if let Some(kind
) = kind
{
1911 if let Some(name_in_toml
) = explicit_name_in_toml
{
1912 cx
.features
.require(Feature
::rename_dependency())?
;
1913 dep
.set_explicit_name_in_toml(name_in_toml
);
1916 if let Some(p
) = self.public
{
1917 cx
.features
.require(Feature
::public_dependency())?
;
1919 if dep
.kind() != DepKind
::Normal
{
1920 bail
!("'public' specifier can only be used on regular dependencies, not {:?} dependencies", dep
.kind());
1929 #[derive(Default, Serialize, Deserialize, Debug, Clone)]
1931 name
: Option
<String
>,
1933 // The intention was to only accept `crate-type` here but historical
1934 // versions of Cargo also accepted `crate_type`, so look for both.
1935 #[serde(rename = "crate-type")]
1936 crate_type
: Option
<Vec
<String
>>,
1937 #[serde(rename = "crate_type")]
1938 crate_type2
: Option
<Vec
<String
>>,
1940 path
: Option
<PathValue
>,
1941 // Note that `filename` is used for the cargo-feature `different_binary_name`
1942 filename
: Option
<String
>,
1944 doctest
: Option
<bool
>,
1945 bench
: Option
<bool
>,
1947 plugin
: Option
<bool
>,
1948 #[serde(rename = "proc-macro")]
1949 proc_macro_raw
: Option
<bool
>,
1950 #[serde(rename = "proc_macro")]
1951 proc_macro_raw2
: Option
<bool
>,
1952 harness
: Option
<bool
>,
1953 #[serde(rename = "required-features")]
1954 required_features
: Option
<Vec
<String
>>,
1955 edition
: Option
<String
>,
1959 struct PathValue(PathBuf
);
1961 impl<'de
> de
::Deserialize
<'de
> for PathValue
{
1962 fn deserialize
<D
>(deserializer
: D
) -> Result
<Self, D
::Error
>
1964 D
: de
::Deserializer
<'de
>,
1966 Ok(PathValue(String
::deserialize(deserializer
)?
.into()))
1970 impl ser
::Serialize
for PathValue
{
1971 fn serialize
<S
>(&self, serializer
: S
) -> Result
<S
::Ok
, S
::Error
>
1975 self.0.serialize(serializer
)
1979 /// Corresponds to a `target` entry, but `TomlTarget` is already used.
1980 #[derive(Serialize, Deserialize, Debug)]
1981 struct TomlPlatform
{
1982 dependencies
: Option
<BTreeMap
<String
, TomlDependency
>>,
1983 #[serde(rename = "build-dependencies")]
1984 build_dependencies
: Option
<BTreeMap
<String
, TomlDependency
>>,
1985 #[serde(rename = "build_dependencies")]
1986 build_dependencies2
: Option
<BTreeMap
<String
, TomlDependency
>>,
1987 #[serde(rename = "dev-dependencies")]
1988 dev_dependencies
: Option
<BTreeMap
<String
, TomlDependency
>>,
1989 #[serde(rename = "dev_dependencies")]
1990 dev_dependencies2
: Option
<BTreeMap
<String
, TomlDependency
>>,
1994 fn new() -> TomlTarget
{
1995 TomlTarget
::default()
1998 fn name(&self) -> String
{
2000 Some(ref name
) => name
.clone(),
2001 None
=> panic
!("target name is required"),
2005 fn proc_macro(&self) -> Option
<bool
> {
2006 self.proc_macro_raw
.or(self.proc_macro_raw2
).or_else(|| {
2007 if let Some(types
) = self.crate_types() {
2008 if types
.contains(&"proc-macro".to_string()) {
2016 fn crate_types(&self) -> Option
<&Vec
<String
>> {
2019 .or_else(|| self.crate_type2
.as_ref())
2023 impl fmt
::Debug
for PathValue
{
2024 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{