1use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
2use std::fmt;
3use std::fs;
4use std::path::{Path, PathBuf};
5use std::rc::Rc;
6use std::str;
7
8use anyhow::{anyhow, bail};
9use cargo_platform::Platform;
10use log::{debug, trace};
11use semver::{self, VersionReq};
12use serde::de;
13use serde::ser;
14use serde::{Deserialize, Serialize};
15use url::Url;
16
17use crate::core::dependency::DepKind;
18use crate::core::manifest::{LibKind, ManifestMetadata, TargetSourcePath, Warnings};
19use crate::core::{Dependency, InternedString, Manifest, PackageId, Summary, Target};
20use crate::core::{Edition, EitherManifest, Feature, Features, VirtualManifest};
21use crate::core::{GitReference, PackageIdSpec, SourceId, WorkspaceConfig, WorkspaceRootConfig};
22use crate::sources::{CRATES_IO_INDEX, CRATES_IO_REGISTRY};
23use crate::util::errors::{CargoResult, CargoResultExt, ManifestError};
24use crate::util::{self, paths, validate_package_name, Config, IntoUrl};
25
26mod targets;
27use self::targets::targets;
28
29pub fn read_manifest(
30 path: &Path,
31 source_id: SourceId,
32 config: &Config,
33) -> Result<(EitherManifest, Vec<PathBuf>), ManifestError> {
34 trace!(
35 "read_manifest; path={}; source-id={}",
36 path.display(),
37 source_id
38 );
39 let contents = paths::read(path).map_err(|err| ManifestError::new(err, path.into()))?;
40
41 do_read_manifest(&contents, path, source_id, config)
42 .chain_err(|| format!("failed to parse manifest at `{}`", path.display()))
43 .map_err(|err| ManifestError::new(err, path.into()))
44}
45
46fn do_read_manifest(
47 contents: &str,
48 manifest_file: &Path,
49 source_id: SourceId,
50 config: &Config,
51) -> CargoResult<(EitherManifest, Vec<PathBuf>)> {
52 let package_root = manifest_file.parent().unwrap();
53
54 let toml = {
55 let pretty_filename = manifest_file
56 .strip_prefix(config.cwd())
57 .unwrap_or(manifest_file);
58 parse(contents, pretty_filename, config)?
59 };
60
61 let mut unused = BTreeSet::new();
62 let manifest: TomlManifest = serde_ignored::deserialize(toml, |path| {
63 let mut key = String::new();
64 stringify(&mut key, &path);
65 unused.insert(key);
66 })?;
67 let add_unused = |warnings: &mut Warnings| {
68 for key in unused {
69 warnings.add_warning(format!("unused manifest key: {}", key));
70 if key == "profiles.debug" {
71 warnings.add_warning("use `[profile.dev]` to configure debug builds".to_string());
72 }
73 }
74 };
75
76 let manifest = Rc::new(manifest);
77 return if manifest.project.is_some() || manifest.package.is_some() {
78 let (mut manifest, paths) =
79 TomlManifest::to_real_manifest(&manifest, source_id, package_root, config)?;
80 add_unused(manifest.warnings_mut());
81 if !manifest.targets().iter().any(|t| !t.is_custom_build()) {
82 bail!(
83 "no targets specified in the manifest\n \
84 either src/lib.rs, src/main.rs, a [lib] section, or \
85 [[bin]] section must be present"
86 )
87 }
88 Ok((EitherManifest::Real(manifest), paths))
89 } else {
90 let (mut m, paths) =
91 TomlManifest::to_virtual_manifest(&manifest, source_id, package_root, config)?;
92 add_unused(m.warnings_mut());
93 Ok((EitherManifest::Virtual(m), paths))
94 };
95
96 fn stringify(dst: &mut String, path: &serde_ignored::Path<'_>) {
97 use serde_ignored::Path;
98
99 match *path {
100 Path::Root => {}
101 Path::Seq { parent, index } => {
102 stringify(dst, parent);
103 if !dst.is_empty() {
104 dst.push('.');
105 }
106 dst.push_str(&index.to_string());
107 }
108 Path::Map { parent, ref key } => {
109 stringify(dst, parent);
110 if !dst.is_empty() {
111 dst.push('.');
112 }
113 dst.push_str(key);
114 }
115 Path::Some { parent }
116 | Path::NewtypeVariant { parent }
117 | Path::NewtypeStruct { parent } => stringify(dst, parent),
118 }
119 }
120}
121
122pub fn parse(toml: &str, file: &Path, config: &Config) -> CargoResult<toml::Value> {
123 let first_error = match toml.parse() {
124 Ok(ret) => return Ok(ret),
125 Err(e) => e,
126 };
127
128 let mut second_parser = toml::de::Deserializer::new(toml);
129 second_parser.set_require_newline_after_table(false);
130 if let Ok(ret) = toml::Value::deserialize(&mut second_parser) {
131 let msg = format!(
132 "\
133TOML file found which contains invalid syntax and will soon not parse
134at `{}`.
135
136The TOML spec requires newlines after table definitions (e.g., `[a] b = 1` is
137invalid), but this file has a table header which does not have a newline after
138it. A newline needs to be added and this warning will soon become a hard error
139in the future.",
140 file.display()
141 );
142 config.shell().warn(&msg)?;
143 return Ok(ret);
144 }
145
146 let mut third_parser = toml::de::Deserializer::new(toml);
147 third_parser.set_allow_duplicate_after_longer_table(true);
148 if let Ok(ret) = toml::Value::deserialize(&mut third_parser) {
149 let msg = format!(
150 "\
151TOML file found which contains invalid syntax and will soon not parse
152at `{}`.
153
154The TOML spec requires that each table header is defined at most once, but
155historical versions of Cargo have erroneously accepted this file. The table
156definitions will need to be merged together with one table header to proceed,
157and this will become a hard error in the future.",
158 file.display()
159 );
160 config.shell().warn(&msg)?;
161 return Ok(ret);
162 }
163
164 let first_error = anyhow::Error::from(first_error);
165 Err(first_error.context("could not parse input as TOML").into())
166}
167
168type TomlLibTarget = TomlTarget;
169type TomlBinTarget = TomlTarget;
170type TomlExampleTarget = TomlTarget;
171type TomlTestTarget = TomlTarget;
172type TomlBenchTarget = TomlTarget;
173
174#[derive(Clone, Debug, Serialize)]
175#[serde(untagged)]
176pub enum TomlDependency {
177 Simple(String),
178 Detailed(DetailedTomlDependency),
179}
180
181impl<'de> de::Deserialize<'de> for TomlDependency {
182 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
183 where
184 D: de::Deserializer<'de>,
185 {
186 struct TomlDependencyVisitor;
187
188 impl<'de> de::Visitor<'de> for TomlDependencyVisitor {
189 type Value = TomlDependency;
190
191 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
192 formatter.write_str(
193 "a version string like \"0.9.8\" or a \
194 detailed dependency like { version = \"0.9.8\" }",
195 )
196 }
197
198 fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
199 where
200 E: de::Error,
201 {
202 Ok(TomlDependency::Simple(s.to_owned()))
203 }
204
205 fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
206 where
207 V: de::MapAccess<'de>,
208 {
209 let mvd = de::value::MapAccessDeserializer::new(map);
210 DetailedTomlDependency::deserialize(mvd).map(TomlDependency::Detailed)
211 }
212 }
213
214 deserializer.deserialize_any(TomlDependencyVisitor)
215 }
216}
217
218#[derive(Deserialize, Serialize, Clone, Debug, Default)]
219#[serde(rename_all = "kebab-case")]
220pub struct DetailedTomlDependency {
221 version: Option<String>,
222 registry: Option<String>,
223 registry_index: Option<String>,
230 path: Option<String>,
231 git: Option<String>,
232 branch: Option<String>,
233 tag: Option<String>,
234 rev: Option<String>,
235 features: Option<Vec<String>>,
236 optional: Option<bool>,
237 default_features: Option<bool>,
238 #[serde(rename = "default_features")]
239 default_features2: Option<bool>,
240 package: Option<String>,
241 public: Option<bool>,
242}
243
244#[derive(Debug, Deserialize, Serialize)]
245#[serde(rename_all = "kebab-case")]
246pub struct TomlManifest {
247 cargo_features: Option<Vec<String>>,
248 package: Option<Box<TomlProject>>,
249 project: Option<Box<TomlProject>>,
250 profile: Option<TomlProfiles>,
251 lib: Option<TomlLibTarget>,
252 bin: Option<Vec<TomlBinTarget>>,
253 example: Option<Vec<TomlExampleTarget>>,
254 test: Option<Vec<TomlTestTarget>>,
255 bench: Option<Vec<TomlTestTarget>>,
256 dependencies: Option<BTreeMap<String, TomlDependency>>,
257 dev_dependencies: Option<BTreeMap<String, TomlDependency>>,
258 #[serde(rename = "dev_dependencies")]
259 dev_dependencies2: Option<BTreeMap<String, TomlDependency>>,
260 build_dependencies: Option<BTreeMap<String, TomlDependency>>,
261 #[serde(rename = "build_dependencies")]
262 build_dependencies2: Option<BTreeMap<String, TomlDependency>>,
263 features: Option<BTreeMap<String, Vec<String>>>,
264 target: Option<BTreeMap<String, TomlPlatform>>,
265 replace: Option<BTreeMap<String, TomlDependency>>,
266 patch: Option<BTreeMap<String, BTreeMap<String, TomlDependency>>>,
267 workspace: Option<TomlWorkspace>,
268 badges: Option<BTreeMap<String, BTreeMap<String, String>>>,
269}
270
271#[derive(Deserialize, Serialize, Clone, Debug, Default)]
272pub struct TomlProfiles(BTreeMap<InternedString, TomlProfile>);
273
274impl TomlProfiles {
275 pub fn get_all(&self) -> &BTreeMap<InternedString, TomlProfile> {
276 &self.0
277 }
278
279 pub fn get(&self, name: &str) -> Option<&TomlProfile> {
280 self.0.get(name)
281 }
282
283 pub fn validate(&self, features: &Features, warnings: &mut Vec<String>) -> CargoResult<()> {
284 for (name, profile) in &self.0 {
285 profile.validate(name, features, warnings)?;
286 }
287 Ok(())
288 }
289}
290
291#[derive(Clone, Debug, Eq, PartialEq)]
292pub struct TomlOptLevel(pub String);
293
294impl<'de> de::Deserialize<'de> for TomlOptLevel {
295 fn deserialize<D>(d: D) -> Result<TomlOptLevel, D::Error>
296 where
297 D: de::Deserializer<'de>,
298 {
299 struct Visitor;
300
301 impl<'de> de::Visitor<'de> for Visitor {
302 type Value = TomlOptLevel;
303
304 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
305 formatter.write_str("an optimization level")
306 }
307
308 fn visit_i64<E>(self, value: i64) -> Result<TomlOptLevel, E>
309 where
310 E: de::Error,
311 {
312 Ok(TomlOptLevel(value.to_string()))
313 }
314
315 fn visit_str<E>(self, value: &str) -> Result<TomlOptLevel, E>
316 where
317 E: de::Error,
318 {
319 if value == "s" || value == "z" {
320 Ok(TomlOptLevel(value.to_string()))
321 } else {
322 Err(E::custom(format!(
323 "must be an integer, `z`, or `s`, \
324 but found: {}",
325 value
326 )))
327 }
328 }
329 }
330
331 d.deserialize_any(Visitor)
332 }
333}
334
335impl ser::Serialize for TomlOptLevel {
336 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
337 where
338 S: ser::Serializer,
339 {
340 match self.0.parse::<u32>() {
341 Ok(n) => n.serialize(serializer),
342 Err(_) => self.0.serialize(serializer),
343 }
344 }
345}
346
347#[derive(Clone, Debug, Serialize, Eq, PartialEq)]
348#[serde(untagged)]
349pub enum U32OrBool {
350 U32(u32),
351 Bool(bool),
352}
353
354impl<'de> de::Deserialize<'de> for U32OrBool {
355 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
356 where
357 D: de::Deserializer<'de>,
358 {
359 struct Visitor;
360
361 impl<'de> de::Visitor<'de> for Visitor {
362 type Value = U32OrBool;
363
364 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
365 formatter.write_str("a boolean or an integer")
366 }
367
368 fn visit_bool<E>(self, b: bool) -> Result<Self::Value, E>
369 where
370 E: de::Error,
371 {
372 Ok(U32OrBool::Bool(b))
373 }
374
375 fn visit_i64<E>(self, u: i64) -> Result<Self::Value, E>
376 where
377 E: de::Error,
378 {
379 Ok(U32OrBool::U32(u as u32))
380 }
381
382 fn visit_u64<E>(self, u: u64) -> Result<Self::Value, E>
383 where
384 E: de::Error,
385 {
386 Ok(U32OrBool::U32(u as u32))
387 }
388 }
389
390 deserializer.deserialize_any(Visitor)
391 }
392}
393
394#[derive(Deserialize, Serialize, Clone, Debug, Default, Eq, PartialEq)]
395#[serde(rename_all = "kebab-case")]
396pub struct TomlProfile {
397 pub opt_level: Option<TomlOptLevel>,
398 pub lto: Option<StringOrBool>,
399 pub codegen_units: Option<u32>,
400 pub debug: Option<U32OrBool>,
401 pub debug_assertions: Option<bool>,
402 pub rpath: Option<bool>,
403 pub panic: Option<String>,
404 pub overflow_checks: Option<bool>,
405 pub incremental: Option<bool>,
406 pub package: Option<BTreeMap<ProfilePackageSpec, TomlProfile>>,
407 pub build_override: Option<Box<TomlProfile>>,
408 pub dir_name: Option<InternedString>,
409 pub inherits: Option<InternedString>,
410}
411
412#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
413pub enum ProfilePackageSpec {
414 Spec(PackageIdSpec),
415 All,
416}
417
418impl ser::Serialize for ProfilePackageSpec {
419 fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
420 where
421 S: ser::Serializer,
422 {
423 match *self {
424 ProfilePackageSpec::Spec(ref spec) => spec.serialize(s),
425 ProfilePackageSpec::All => "*".serialize(s),
426 }
427 }
428}
429
430impl<'de> de::Deserialize<'de> for ProfilePackageSpec {
431 fn deserialize<D>(d: D) -> Result<ProfilePackageSpec, D::Error>
432 where
433 D: de::Deserializer<'de>,
434 {
435 let string = String::deserialize(d)?;
436 if string == "*" {
437 Ok(ProfilePackageSpec::All)
438 } else {
439 PackageIdSpec::parse(&string)
440 .map_err(de::Error::custom)
441 .map(ProfilePackageSpec::Spec)
442 }
443 }
444}
445
446impl TomlProfile {
447 pub fn validate(
448 &self,
449 name: &str,
450 features: &Features,
451 warnings: &mut Vec<String>,
452 ) -> CargoResult<()> {
453 if name == "debug" {
454 warnings.push("use `[profile.dev]` to configure debug builds".to_string());
455 }
456
457 if let Some(ref profile) = self.build_override {
458 features.require(Feature::profile_overrides())?;
459 profile.validate_override("build-override")?;
460 }
461 if let Some(ref packages) = self.package {
462 features.require(Feature::profile_overrides())?;
463 for profile in packages.values() {
464 profile.validate_override("package")?;
465 }
466 }
467
468 match name {
470 "dev" | "release" | "bench" | "test" | "doc" => {}
471 _ => {
472 features.require(Feature::named_profiles())?;
473 }
474 }
475
476 Self::validate_name(name, "profile name")?;
478
479 if self.inherits.is_some() {
481 features.require(Feature::named_profiles())?;
482 }
483
484 if self.dir_name.is_some() {
485 features.require(Feature::named_profiles())?;
486 }
487
488 match &self.dir_name {
490 None => {}
491 Some(dir_name) => {
492 Self::validate_name(dir_name, "dir-name")?;
493 }
494 }
495
496 match &self.inherits {
498 None => {}
499 Some(inherits) => {
500 Self::validate_name(inherits, "inherits")?;
501 }
502 }
503
504 match name {
505 "doc" => {
506 warnings.push("profile `doc` is deprecated and has no effect".to_string());
507 }
508 "test" | "bench" => {
509 if self.panic.is_some() {
510 warnings.push(format!("`panic` setting is ignored for `{}` profile", name))
511 }
512 }
513 _ => {}
514 }
515
516 if let Some(panic) = &self.panic {
517 if panic != "unwind" && panic != "abort" {
518 bail!(
519 "`panic` setting of `{}` is not a valid setting,\
520 must be `unwind` or `abort`",
521 panic
522 );
523 }
524 }
525 Ok(())
526 }
527
528 pub fn validate_name(name: &str, what: &str) -> CargoResult<()> {
530 if let Some(ch) = name
531 .chars()
532 .find(|ch| !ch.is_alphanumeric() && *ch != '_' && *ch != '-')
533 {
534 bail!("Invalid character `{}` in {}: `{}`", ch, what, name);
535 }
536
537 match name {
538 "package" | "build" => {
539 bail!("Invalid {}: `{}`", what, name);
540 }
541 "debug" if what == "profile" => {
542 if what == "profile name" {
543 } else {
545 bail!("Invalid {}: `{}`", what, name);
546 }
547 }
548 "doc" if what == "dir-name" => {
549 bail!("Invalid {}: `{}`", what, name);
550 }
551 _ => {}
552 }
553
554 Ok(())
555 }
556
557 fn validate_override(&self, which: &str) -> CargoResult<()> {
558 if self.package.is_some() {
559 bail!("package-specific profiles cannot be nested");
560 }
561 if self.build_override.is_some() {
562 bail!("build-override profiles cannot be nested");
563 }
564 if self.panic.is_some() {
565 bail!("`panic` may not be specified in a `{}` profile", which)
566 }
567 if self.lto.is_some() {
568 bail!("`lto` may not be specified in a `{}` profile", which)
569 }
570 if self.rpath.is_some() {
571 bail!("`rpath` may not be specified in a `{}` profile", which)
572 }
573 Ok(())
574 }
575
576 pub fn merge(&mut self, profile: &TomlProfile) {
578 if let Some(v) = &profile.opt_level {
579 self.opt_level = Some(v.clone());
580 }
581
582 if let Some(v) = &profile.lto {
583 self.lto = Some(v.clone());
584 }
585
586 if let Some(v) = profile.codegen_units {
587 self.codegen_units = Some(v);
588 }
589
590 if let Some(v) = &profile.debug {
591 self.debug = Some(v.clone());
592 }
593
594 if let Some(v) = profile.debug_assertions {
595 self.debug_assertions = Some(v);
596 }
597
598 if let Some(v) = profile.rpath {
599 self.rpath = Some(v);
600 }
601
602 if let Some(v) = &profile.panic {
603 self.panic = Some(v.clone());
604 }
605
606 if let Some(v) = profile.overflow_checks {
607 self.overflow_checks = Some(v);
608 }
609
610 if let Some(v) = profile.incremental {
611 self.incremental = Some(v);
612 }
613
614 if let Some(other_package) = &profile.package {
615 match &mut self.package {
616 Some(self_package) => {
617 for (spec, other_pkg_profile) in other_package {
618 match self_package.get_mut(spec) {
619 Some(p) => p.merge(other_pkg_profile),
620 None => {
621 self_package.insert(spec.clone(), other_pkg_profile.clone());
622 }
623 }
624 }
625 }
626 None => self.package = Some(other_package.clone()),
627 }
628 }
629
630 if let Some(other_bo) = &profile.build_override {
631 match &mut self.build_override {
632 Some(self_bo) => self_bo.merge(other_bo),
633 None => self.build_override = Some(other_bo.clone()),
634 }
635 }
636
637 if let Some(v) = &profile.inherits {
638 self.inherits = Some(*v);
639 }
640
641 if let Some(v) = &profile.dir_name {
642 self.dir_name = Some(*v);
643 }
644 }
645}
646
647#[derive(Clone, Debug, Serialize, Eq, PartialEq)]
648pub struct StringOrVec(Vec<String>);
649
650impl<'de> de::Deserialize<'de> for StringOrVec {
651 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
652 where
653 D: de::Deserializer<'de>,
654 {
655 struct Visitor;
656
657 impl<'de> de::Visitor<'de> for Visitor {
658 type Value = StringOrVec;
659
660 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
661 formatter.write_str("string or list of strings")
662 }
663
664 fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
665 where
666 E: de::Error,
667 {
668 Ok(StringOrVec(vec![s.to_string()]))
669 }
670
671 fn visit_seq<V>(self, v: V) -> Result<Self::Value, V::Error>
672 where
673 V: de::SeqAccess<'de>,
674 {
675 let seq = de::value::SeqAccessDeserializer::new(v);
676 Vec::deserialize(seq).map(StringOrVec)
677 }
678 }
679
680 deserializer.deserialize_any(Visitor)
681 }
682}
683
684#[derive(Clone, Debug, Serialize, Eq, PartialEq)]
685#[serde(untagged)]
686pub enum StringOrBool {
687 String(String),
688 Bool(bool),
689}
690
691impl<'de> de::Deserialize<'de> for StringOrBool {
692 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
693 where
694 D: de::Deserializer<'de>,
695 {
696 struct Visitor;
697
698 impl<'de> de::Visitor<'de> for Visitor {
699 type Value = StringOrBool;
700
701 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
702 formatter.write_str("a boolean or a string")
703 }
704
705 fn visit_bool<E>(self, b: bool) -> Result<Self::Value, E>
706 where
707 E: de::Error,
708 {
709 Ok(StringOrBool::Bool(b))
710 }
711
712 fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
713 where
714 E: de::Error,
715 {
716 Ok(StringOrBool::String(s.to_string()))
717 }
718 }
719
720 deserializer.deserialize_any(Visitor)
721 }
722}
723
724#[derive(PartialEq, Clone, Debug, Serialize)]
725#[serde(untagged)]
726pub enum VecStringOrBool {
727 VecString(Vec<String>),
728 Bool(bool),
729}
730
731impl<'de> de::Deserialize<'de> for VecStringOrBool {
732 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
733 where
734 D: de::Deserializer<'de>,
735 {
736 struct Visitor;
737
738 impl<'de> de::Visitor<'de> for Visitor {
739 type Value = VecStringOrBool;
740
741 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
742 formatter.write_str("a boolean or vector of strings")
743 }
744
745 fn visit_seq<V>(self, v: V) -> Result<Self::Value, V::Error>
746 where
747 V: de::SeqAccess<'de>,
748 {
749 let seq = de::value::SeqAccessDeserializer::new(v);
750 Vec::deserialize(seq).map(VecStringOrBool::VecString)
751 }
752
753 fn visit_bool<E>(self, b: bool) -> Result<Self::Value, E>
754 where
755 E: de::Error,
756 {
757 Ok(VecStringOrBool::Bool(b))
758 }
759 }
760
761 deserializer.deserialize_any(Visitor)
762 }
763}
764
765#[derive(Deserialize, Serialize, Clone, Debug)]
772pub struct TomlProject {
773 edition: Option<String>,
774 name: InternedString,
775 version: semver::Version,
776 authors: Option<Vec<String>>,
777 build: Option<StringOrBool>,
778 metabuild: Option<StringOrVec>,
779 links: Option<String>,
780 exclude: Option<Vec<String>>,
781 include: Option<Vec<String>>,
782 publish: Option<VecStringOrBool>,
783 #[serde(rename = "publish-lockfile")]
784 publish_lockfile: Option<bool>,
785 workspace: Option<String>,
786 #[serde(rename = "im-a-teapot")]
787 im_a_teapot: Option<bool>,
788 autobins: Option<bool>,
789 autoexamples: Option<bool>,
790 autotests: Option<bool>,
791 autobenches: Option<bool>,
792 #[serde(rename = "namespaced-features")]
793 namespaced_features: Option<bool>,
794 #[serde(rename = "default-run")]
795 default_run: Option<String>,
796
797 description: Option<String>,
799 homepage: Option<String>,
800 documentation: Option<String>,
801 readme: Option<String>,
802 keywords: Option<Vec<String>>,
803 categories: Option<Vec<String>>,
804 license: Option<String>,
805 #[serde(rename = "license-file")]
806 license_file: Option<String>,
807 repository: Option<String>,
808 metadata: Option<toml::Value>,
809}
810
811#[derive(Debug, Deserialize, Serialize)]
812pub struct TomlWorkspace {
813 members: Option<Vec<String>>,
814 #[serde(rename = "default-members")]
815 default_members: Option<Vec<String>>,
816 exclude: Option<Vec<String>>,
817}
818
819impl TomlProject {
820 pub fn to_package_id(&self, source_id: SourceId) -> CargoResult<PackageId> {
821 PackageId::new(self.name, self.version.clone(), source_id)
822 }
823}
824
825struct Context<'a, 'b> {
826 pkgid: Option<PackageId>,
827 deps: &'a mut Vec<Dependency>,
828 source_id: SourceId,
829 nested_paths: &'a mut Vec<PathBuf>,
830 config: &'b Config,
831 warnings: &'a mut Vec<String>,
832 platform: Option<Platform>,
833 root: &'a Path,
834 features: &'a Features,
835}
836
837impl TomlManifest {
838 pub fn prepare_for_publish(
839 &self,
840 config: &Config,
841 package_root: &Path,
842 ) -> CargoResult<TomlManifest> {
843 let mut package = self
844 .package
845 .as_ref()
846 .or_else(|| self.project.as_ref())
847 .unwrap()
848 .clone();
849 package.workspace = None;
850 if let Some(license_file) = &package.license_file {
851 let license_path = Path::new(&license_file);
852 let abs_license_path = paths::normalize_path(&package_root.join(license_path));
853 if abs_license_path.strip_prefix(package_root).is_err() {
854 package.license_file = Some(
857 license_path
858 .file_name()
859 .unwrap()
860 .to_str()
861 .unwrap()
862 .to_string(),
863 );
864 }
865 }
866 let all = |_d: &TomlDependency| true;
867 return Ok(TomlManifest {
868 package: Some(package),
869 project: None,
870 profile: self.profile.clone(),
871 lib: self.lib.clone(),
872 bin: self.bin.clone(),
873 example: self.example.clone(),
874 test: self.test.clone(),
875 bench: self.bench.clone(),
876 dependencies: map_deps(config, self.dependencies.as_ref(), all)?,
877 dev_dependencies: map_deps(
878 config,
879 self.dev_dependencies
880 .as_ref()
881 .or_else(|| self.dev_dependencies2.as_ref()),
882 TomlDependency::is_version_specified,
883 )?,
884 dev_dependencies2: None,
885 build_dependencies: map_deps(
886 config,
887 self.build_dependencies
888 .as_ref()
889 .or_else(|| self.build_dependencies2.as_ref()),
890 all,
891 )?,
892 build_dependencies2: None,
893 features: self.features.clone(),
894 target: match self.target.as_ref().map(|target_map| {
895 target_map
896 .iter()
897 .map(|(k, v)| {
898 Ok((
899 k.clone(),
900 TomlPlatform {
901 dependencies: map_deps(config, v.dependencies.as_ref(), all)?,
902 dev_dependencies: map_deps(
903 config,
904 v.dev_dependencies
905 .as_ref()
906 .or_else(|| v.dev_dependencies2.as_ref()),
907 TomlDependency::is_version_specified,
908 )?,
909 dev_dependencies2: None,
910 build_dependencies: map_deps(
911 config,
912 v.build_dependencies
913 .as_ref()
914 .or_else(|| v.build_dependencies2.as_ref()),
915 all,
916 )?,
917 build_dependencies2: None,
918 },
919 ))
920 })
921 .collect()
922 }) {
923 Some(Ok(v)) => Some(v),
924 Some(Err(e)) => return Err(e),
925 None => None,
926 },
927 replace: None,
928 patch: None,
929 workspace: None,
930 badges: self.badges.clone(),
931 cargo_features: self.cargo_features.clone(),
932 });
933
934 fn map_deps(
935 config: &Config,
936 deps: Option<&BTreeMap<String, TomlDependency>>,
937 filter: impl Fn(&TomlDependency) -> bool,
938 ) -> CargoResult<Option<BTreeMap<String, TomlDependency>>> {
939 let deps = match deps {
940 Some(deps) => deps,
941 None => return Ok(None),
942 };
943 let deps = deps
944 .iter()
945 .filter(|(_k, v)| filter(v))
946 .map(|(k, v)| Ok((k.clone(), map_dependency(config, v)?)))
947 .collect::<CargoResult<BTreeMap<_, _>>>()?;
948 Ok(Some(deps))
949 }
950
951 fn map_dependency(config: &Config, dep: &TomlDependency) -> CargoResult<TomlDependency> {
952 match dep {
953 TomlDependency::Detailed(d) => {
954 let mut d = d.clone();
955 d.path.take();
957 d.git.take();
959 d.branch.take();
960 d.tag.take();
961 d.rev.take();
962 if let Some(registry) = d.registry.take() {
964 let src = SourceId::alt_registry(config, ®istry)?;
965 d.registry_index = Some(src.url().to_string());
966 }
967 Ok(TomlDependency::Detailed(d))
968 }
969 TomlDependency::Simple(s) => Ok(TomlDependency::Detailed(DetailedTomlDependency {
970 version: Some(s.clone()),
971 ..Default::default()
972 })),
973 }
974 }
975 }
976
977 pub fn to_real_manifest(
978 me: &Rc<TomlManifest>,
979 source_id: SourceId,
980 package_root: &Path,
981 config: &Config,
982 ) -> CargoResult<(Manifest, Vec<PathBuf>)> {
983 let mut nested_paths = vec![];
984 let mut warnings = vec![];
985 let mut errors = vec![];
986
987 let empty = Vec::new();
989 let cargo_features = me.cargo_features.as_ref().unwrap_or(&empty);
990 let features = Features::new(cargo_features, &mut warnings)?;
991
992 let project = me.project.as_ref().or_else(|| me.package.as_ref());
993 let project = project.ok_or_else(|| anyhow!("no `package` section found"))?;
994
995 let package_name = project.name.trim();
996 if package_name.is_empty() {
997 bail!("package name cannot be an empty string")
998 }
999
1000 validate_package_name(package_name, "package name", "")?;
1001
1002 let pkgid = project.to_package_id(source_id)?;
1003
1004 let edition = if let Some(ref edition) = project.edition {
1005 features
1006 .require(Feature::edition())
1007 .chain_err(|| "editions are unstable")?;
1008 edition
1009 .parse()
1010 .chain_err(|| "failed to parse the `edition` key")?
1011 } else {
1012 Edition::Edition2015
1013 };
1014
1015 if project.metabuild.is_some() {
1016 features.require(Feature::metabuild())?;
1017 }
1018
1019 let targets = targets(
1023 &features,
1024 me,
1025 package_name,
1026 package_root,
1027 edition,
1028 &project.build,
1029 &project.metabuild,
1030 &mut warnings,
1031 &mut errors,
1032 )?;
1033
1034 if targets.is_empty() {
1035 debug!("manifest has no build targets");
1036 }
1037
1038 if let Err(e) = unique_build_targets(&targets, package_root) {
1039 warnings.push(format!(
1040 "file found to be present in multiple \
1041 build targets: {}",
1042 e
1043 ));
1044 }
1045
1046 if let Some(links) = &project.links {
1047 if !targets.iter().any(|t| t.is_custom_build()) {
1048 bail!(
1049 "package `{}` specifies that it links to `{}` but does not \
1050 have a custom build script",
1051 pkgid,
1052 links
1053 )
1054 }
1055 }
1056
1057 let mut deps = Vec::new();
1058 let replace;
1059 let patch;
1060
1061 {
1062 let mut cx = Context {
1063 pkgid: Some(pkgid),
1064 deps: &mut deps,
1065 source_id,
1066 nested_paths: &mut nested_paths,
1067 config,
1068 warnings: &mut warnings,
1069 features: &features,
1070 platform: None,
1071 root: package_root,
1072 };
1073
1074 fn process_dependencies(
1075 cx: &mut Context<'_, '_>,
1076 new_deps: Option<&BTreeMap<String, TomlDependency>>,
1077 kind: Option<DepKind>,
1078 ) -> CargoResult<()> {
1079 let dependencies = match new_deps {
1080 Some(dependencies) => dependencies,
1081 None => return Ok(()),
1082 };
1083 for (n, v) in dependencies.iter() {
1084 let dep = v.to_dependency(n, cx, kind)?;
1085 cx.deps.push(dep);
1086 }
1087
1088 Ok(())
1089 }
1090
1091 process_dependencies(&mut cx, me.dependencies.as_ref(), None)?;
1093 let dev_deps = me
1094 .dev_dependencies
1095 .as_ref()
1096 .or_else(|| me.dev_dependencies2.as_ref());
1097 process_dependencies(&mut cx, dev_deps, Some(DepKind::Development))?;
1098 let build_deps = me
1099 .build_dependencies
1100 .as_ref()
1101 .or_else(|| me.build_dependencies2.as_ref());
1102 process_dependencies(&mut cx, build_deps, Some(DepKind::Build))?;
1103
1104 for (name, platform) in me.target.iter().flatten() {
1105 cx.platform = {
1106 let platform: Platform = name.parse()?;
1107 platform.check_cfg_attributes(&mut cx.warnings);
1108 Some(platform)
1109 };
1110 process_dependencies(&mut cx, platform.dependencies.as_ref(), None)?;
1111 let build_deps = platform
1112 .build_dependencies
1113 .as_ref()
1114 .or_else(|| platform.build_dependencies2.as_ref());
1115 process_dependencies(&mut cx, build_deps, Some(DepKind::Build))?;
1116 let dev_deps = platform
1117 .dev_dependencies
1118 .as_ref()
1119 .or_else(|| platform.dev_dependencies2.as_ref());
1120 process_dependencies(&mut cx, dev_deps, Some(DepKind::Development))?;
1121 }
1122
1123 replace = me.replace(&mut cx)?;
1124 patch = me.patch(&mut cx)?;
1125 }
1126
1127 {
1128 let mut names_sources = BTreeMap::new();
1129 for dep in &deps {
1130 let name = dep.name_in_toml();
1131 let prev = names_sources.insert(name.to_string(), dep.source_id());
1132 if prev.is_some() && prev != Some(dep.source_id()) {
1133 bail!(
1134 "Dependency '{}' has different source paths depending on the build \
1135 target. Each dependency must have a single canonical source path \
1136 irrespective of build target.",
1137 name
1138 );
1139 }
1140 }
1141 }
1142
1143 let exclude = project.exclude.clone().unwrap_or_default();
1144 let include = project.include.clone().unwrap_or_default();
1145 if project.namespaced_features.is_some() {
1146 features.require(Feature::namespaced_features())?;
1147 }
1148
1149 let summary_features = me
1150 .features
1151 .as_ref()
1152 .map(|x| {
1153 x.iter()
1154 .map(|(k, v)| (k.as_str(), v.iter().collect()))
1155 .collect()
1156 })
1157 .unwrap_or_else(BTreeMap::new);
1158 let summary = Summary::new(
1159 pkgid,
1160 deps,
1161 &summary_features,
1162 project.links.as_deref(),
1163 project.namespaced_features.unwrap_or(false),
1164 )?;
1165 let metadata = ManifestMetadata {
1166 description: project.description.clone(),
1167 homepage: project.homepage.clone(),
1168 documentation: project.documentation.clone(),
1169 readme: project.readme.clone(),
1170 authors: project.authors.clone().unwrap_or_default(),
1171 license: project.license.clone(),
1172 license_file: project.license_file.clone(),
1173 repository: project.repository.clone(),
1174 keywords: project.keywords.clone().unwrap_or_default(),
1175 categories: project.categories.clone().unwrap_or_default(),
1176 badges: me.badges.clone().unwrap_or_default(),
1177 links: project.links.clone(),
1178 };
1179
1180 let workspace_config = match (me.workspace.as_ref(), project.workspace.as_ref()) {
1181 (Some(config), None) => WorkspaceConfig::Root(WorkspaceRootConfig::new(
1182 package_root,
1183 &config.members,
1184 &config.default_members,
1185 &config.exclude,
1186 )),
1187 (None, root) => WorkspaceConfig::Member {
1188 root: root.cloned(),
1189 },
1190 (Some(..), Some(..)) => bail!(
1191 "cannot configure both `package.workspace` and \
1192 `[workspace]`, only one can be specified"
1193 ),
1194 };
1195 let profiles = me.profile.clone();
1196 if let Some(profiles) = &profiles {
1197 profiles.validate(&features, &mut warnings)?;
1198 }
1199 let publish = match project.publish {
1200 Some(VecStringOrBool::VecString(ref vecstring)) => Some(vecstring.clone()),
1201 Some(VecStringOrBool::Bool(false)) => Some(vec![]),
1202 None | Some(VecStringOrBool::Bool(true)) => None,
1203 };
1204
1205 let publish_lockfile = match project.publish_lockfile {
1206 Some(b) => {
1207 features.require(Feature::publish_lockfile())?;
1208 warnings.push(
1209 "The `publish-lockfile` feature is deprecated and currently \
1210 has no effect. It may be removed in a future version."
1211 .to_string(),
1212 );
1213 b
1214 }
1215 None => features.is_enabled(Feature::publish_lockfile()),
1216 };
1217
1218 if summary.features().contains_key("default-features") {
1219 warnings.push(
1220 "`default-features = [\"..\"]` was found in [features]. \
1221 Did you mean to use `default = [\"..\"]`?"
1222 .to_string(),
1223 )
1224 }
1225
1226 if let Some(run) = &project.default_run {
1227 if !targets
1228 .iter()
1229 .filter(|t| t.is_bin())
1230 .any(|t| t.name() == run)
1231 {
1232 let suggestion =
1233 util::closest_msg(run, targets.iter().filter(|t| t.is_bin()), |t| t.name());
1234 bail!("default-run target `{}` not found{}", run, suggestion);
1235 }
1236 }
1237
1238 let custom_metadata = project.metadata.clone();
1239 let mut manifest = Manifest::new(
1240 summary,
1241 targets,
1242 exclude,
1243 include,
1244 project.links.clone(),
1245 metadata,
1246 custom_metadata,
1247 profiles,
1248 publish,
1249 publish_lockfile,
1250 replace,
1251 patch,
1252 workspace_config,
1253 features,
1254 edition,
1255 project.im_a_teapot,
1256 project.default_run.clone(),
1257 Rc::clone(me),
1258 project.metabuild.clone().map(|sov| sov.0),
1259 );
1260 if project.license_file.is_some() && project.license.is_some() {
1261 manifest.warnings_mut().add_warning(
1262 "only one of `license` or \
1263 `license-file` is necessary"
1264 .to_string(),
1265 );
1266 }
1267 for warning in warnings {
1268 manifest.warnings_mut().add_warning(warning);
1269 }
1270 for error in errors {
1271 manifest.warnings_mut().add_critical_warning(error);
1272 }
1273
1274 manifest.feature_gate()?;
1275
1276 Ok((manifest, nested_paths))
1277 }
1278
1279 fn to_virtual_manifest(
1280 me: &Rc<TomlManifest>,
1281 source_id: SourceId,
1282 root: &Path,
1283 config: &Config,
1284 ) -> CargoResult<(VirtualManifest, Vec<PathBuf>)> {
1285 if me.project.is_some() {
1286 bail!("virtual manifests do not define [project]");
1287 }
1288 if me.package.is_some() {
1289 bail!("virtual manifests do not define [package]");
1290 }
1291 if me.lib.is_some() {
1292 bail!("virtual manifests do not specify [lib]");
1293 }
1294 if me.bin.is_some() {
1295 bail!("virtual manifests do not specify [[bin]]");
1296 }
1297 if me.example.is_some() {
1298 bail!("virtual manifests do not specify [[example]]");
1299 }
1300 if me.test.is_some() {
1301 bail!("virtual manifests do not specify [[test]]");
1302 }
1303 if me.bench.is_some() {
1304 bail!("virtual manifests do not specify [[bench]]");
1305 }
1306 if me.dependencies.is_some() {
1307 bail!("virtual manifests do not specify [dependencies]");
1308 }
1309 if me.dev_dependencies.is_some() || me.dev_dependencies2.is_some() {
1310 bail!("virtual manifests do not specify [dev-dependencies]");
1311 }
1312 if me.build_dependencies.is_some() || me.build_dependencies2.is_some() {
1313 bail!("virtual manifests do not specify [build-dependencies]");
1314 }
1315 if me.features.is_some() {
1316 bail!("virtual manifests do not specify [features]");
1317 }
1318 if me.target.is_some() {
1319 bail!("virtual manifests do not specify [target]");
1320 }
1321 if me.badges.is_some() {
1322 bail!("virtual manifests do not specify [badges]");
1323 }
1324
1325 let mut nested_paths = Vec::new();
1326 let mut warnings = Vec::new();
1327 let mut deps = Vec::new();
1328 let empty = Vec::new();
1329 let cargo_features = me.cargo_features.as_ref().unwrap_or(&empty);
1330 let features = Features::new(cargo_features, &mut warnings)?;
1331
1332 let (replace, patch) = {
1333 let mut cx = Context {
1334 pkgid: None,
1335 deps: &mut deps,
1336 source_id,
1337 nested_paths: &mut nested_paths,
1338 config,
1339 warnings: &mut warnings,
1340 platform: None,
1341 features: &features,
1342 root,
1343 };
1344 (me.replace(&mut cx)?, me.patch(&mut cx)?)
1345 };
1346 let profiles = me.profile.clone();
1347 if let Some(profiles) = &profiles {
1348 profiles.validate(&features, &mut warnings)?;
1349 }
1350 let workspace_config = match me.workspace {
1351 Some(ref config) => WorkspaceConfig::Root(WorkspaceRootConfig::new(
1352 root,
1353 &config.members,
1354 &config.default_members,
1355 &config.exclude,
1356 )),
1357 None => {
1358 bail!("virtual manifests must be configured with [workspace]");
1359 }
1360 };
1361 Ok((
1362 VirtualManifest::new(replace, patch, workspace_config, profiles, features),
1363 nested_paths,
1364 ))
1365 }
1366
1367 fn replace(&self, cx: &mut Context<'_, '_>) -> CargoResult<Vec<(PackageIdSpec, Dependency)>> {
1368 if self.patch.is_some() && self.replace.is_some() {
1369 bail!("cannot specify both [replace] and [patch]");
1370 }
1371 let mut replace = Vec::new();
1372 for (spec, replacement) in self.replace.iter().flatten() {
1373 let mut spec = PackageIdSpec::parse(spec).chain_err(|| {
1374 format!(
1375 "replacements must specify a valid semver \
1376 version to replace, but `{}` does not",
1377 spec
1378 )
1379 })?;
1380 if spec.url().is_none() {
1381 spec.set_url(CRATES_IO_INDEX.parse().unwrap());
1382 }
1383
1384 if replacement.is_version_specified() {
1385 bail!(
1386 "replacements cannot specify a version \
1387 requirement, but found one for `{}`",
1388 spec
1389 );
1390 }
1391
1392 let mut dep = replacement.to_dependency(spec.name().as_str(), cx, None)?;
1393 {
1394 let version = spec.version().ok_or_else(|| {
1395 anyhow!(
1396 "replacements must specify a version \
1397 to replace, but `{}` does not",
1398 spec
1399 )
1400 })?;
1401 dep.set_version_req(VersionReq::exact(version));
1402 }
1403 replace.push((spec, dep));
1404 }
1405 Ok(replace)
1406 }
1407
1408 fn patch(&self, cx: &mut Context<'_, '_>) -> CargoResult<HashMap<Url, Vec<Dependency>>> {
1409 let mut patch = HashMap::new();
1410 for (url, deps) in self.patch.iter().flatten() {
1411 let url = match &url[..] {
1412 CRATES_IO_REGISTRY => CRATES_IO_INDEX.parse().unwrap(),
1413 _ => cx
1414 .config
1415 .get_registry_index(url)
1416 .or_else(|_| url.into_url())
1417 .chain_err(|| {
1418 format!("[patch] entry `{}` should be a URL or registry name", url)
1419 })?,
1420 };
1421 patch.insert(
1422 url,
1423 deps.iter()
1424 .map(|(name, dep)| dep.to_dependency(name, cx, None))
1425 .collect::<CargoResult<Vec<_>>>()?,
1426 );
1427 }
1428 Ok(patch)
1429 }
1430
1431 fn maybe_custom_build(
1432 &self,
1433 build: &Option<StringOrBool>,
1434 package_root: &Path,
1435 ) -> Option<PathBuf> {
1436 let build_rs = package_root.join("build.rs");
1437 match *build {
1438 Some(StringOrBool::Bool(false)) => None,
1440 Some(StringOrBool::Bool(true)) => Some(build_rs),
1441 Some(StringOrBool::String(ref s)) => Some(PathBuf::from(s)),
1442 None => {
1443 match fs::metadata(&build_rs) {
1444 Ok(ref e) if e.is_file() => Some(build_rs),
1447 Ok(_) | Err(_) => None,
1448 }
1449 }
1450 }
1451 }
1452
1453 pub fn has_profiles(&self) -> bool {
1454 self.profile.is_some()
1455 }
1456}
1457
1458fn unique_build_targets(targets: &[Target], package_root: &Path) -> Result<(), String> {
1461 let mut seen = HashSet::new();
1462 for target in targets {
1463 if let TargetSourcePath::Path(path) = target.src_path() {
1464 let full = package_root.join(path);
1465 if !seen.insert(full.clone()) {
1466 return Err(full.display().to_string());
1467 }
1468 }
1469 }
1470 Ok(())
1471}
1472
1473impl TomlDependency {
1474 fn to_dependency(
1475 &self,
1476 name: &str,
1477 cx: &mut Context<'_, '_>,
1478 kind: Option<DepKind>,
1479 ) -> CargoResult<Dependency> {
1480 match *self {
1481 TomlDependency::Simple(ref version) => DetailedTomlDependency {
1482 version: Some(version.clone()),
1483 ..Default::default()
1484 }
1485 .to_dependency(name, cx, kind),
1486 TomlDependency::Detailed(ref details) => details.to_dependency(name, cx, kind),
1487 }
1488 }
1489
1490 fn is_version_specified(&self) -> bool {
1491 match self {
1492 TomlDependency::Detailed(d) => d.version.is_some(),
1493 TomlDependency::Simple(..) => true,
1494 }
1495 }
1496}
1497
1498impl DetailedTomlDependency {
1499 fn to_dependency(
1500 &self,
1501 name_in_toml: &str,
1502 cx: &mut Context<'_, '_>,
1503 kind: Option<DepKind>,
1504 ) -> CargoResult<Dependency> {
1505 if self.version.is_none() && self.path.is_none() && self.git.is_none() {
1506 let msg = format!(
1507 "dependency ({}) specified without \
1508 providing a local path, Git repository, or \
1509 version to use. This will be considered an \
1510 error in future versions",
1511 name_in_toml
1512 );
1513 cx.warnings.push(msg);
1514 }
1515
1516 if let Some(version) = &self.version {
1517 if version.contains('+') {
1518 cx.warnings.push(format!(
1519 "version requirement `{}` for dependency `{}` \
1520 includes semver metadata which will be ignored, removing the \
1521 metadata is recommended to avoid confusion",
1522 version, name_in_toml
1523 ));
1524 }
1525 }
1526
1527 if self.git.is_none() {
1528 let git_only_keys = [
1529 (&self.branch, "branch"),
1530 (&self.tag, "tag"),
1531 (&self.rev, "rev"),
1532 ];
1533
1534 for &(key, key_name) in &git_only_keys {
1535 if key.is_some() {
1536 let msg = format!(
1537 "key `{}` is ignored for dependency ({}). \
1538 This will be considered an error in future versions",
1539 key_name, name_in_toml
1540 );
1541 cx.warnings.push(msg)
1542 }
1543 }
1544 }
1545
1546 let new_source_id = match (
1547 self.git.as_ref(),
1548 self.path.as_ref(),
1549 self.registry.as_ref(),
1550 self.registry_index.as_ref(),
1551 ) {
1552 (Some(_), _, Some(_), _) | (Some(_), _, _, Some(_)) => bail!(
1553 "dependency ({}) specification is ambiguous. \
1554 Only one of `git` or `registry` is allowed.",
1555 name_in_toml
1556 ),
1557 (_, _, Some(_), Some(_)) => bail!(
1558 "dependency ({}) specification is ambiguous. \
1559 Only one of `registry` or `registry-index` is allowed.",
1560 name_in_toml
1561 ),
1562 (Some(git), maybe_path, _, _) => {
1563 if maybe_path.is_some() {
1564 let msg = format!(
1565 "dependency ({}) specification is ambiguous. \
1566 Only one of `git` or `path` is allowed. \
1567 This will be considered an error in future versions",
1568 name_in_toml
1569 );
1570 cx.warnings.push(msg)
1571 }
1572
1573 let n_details = [&self.branch, &self.tag, &self.rev]
1574 .iter()
1575 .filter(|d| d.is_some())
1576 .count();
1577
1578 if n_details > 1 {
1579 let msg = format!(
1580 "dependency ({}) specification is ambiguous. \
1581 Only one of `branch`, `tag` or `rev` is allowed. \
1582 This will be considered an error in future versions",
1583 name_in_toml
1584 );
1585 cx.warnings.push(msg)
1586 }
1587
1588 let reference = self
1589 .branch
1590 .clone()
1591 .map(GitReference::Branch)
1592 .or_else(|| self.tag.clone().map(GitReference::Tag))
1593 .or_else(|| self.rev.clone().map(GitReference::Rev))
1594 .unwrap_or_else(|| GitReference::Branch("master".to_string()));
1595 let loc = git.into_url()?;
1596 SourceId::for_git(&loc, reference)?
1597 }
1598 (None, Some(path), _, _) => {
1599 cx.nested_paths.push(PathBuf::from(path));
1600 if cx.source_id.is_path() {
1609 let path = cx.root.join(path);
1610 let path = util::normalize_path(&path);
1611 SourceId::for_path(&path)?
1612 } else {
1613 cx.source_id
1614 }
1615 }
1616 (None, None, Some(registry), None) => SourceId::alt_registry(cx.config, registry)?,
1617 (None, None, None, Some(registry_index)) => {
1618 let url = registry_index.into_url()?;
1619 SourceId::for_registry(&url)?
1620 }
1621 (None, None, None, None) => SourceId::crates_io(cx.config)?,
1622 };
1623
1624 let (pkg_name, explicit_name_in_toml) = match self.package {
1625 Some(ref s) => (&s[..], Some(name_in_toml)),
1626 None => (name_in_toml, None),
1627 };
1628
1629 let version = self.version.as_deref();
1630 let mut dep = match cx.pkgid {
1631 Some(id) => Dependency::parse(pkg_name, version, new_source_id, id, cx.config)?,
1632 None => Dependency::parse_no_deprecated(pkg_name, version, new_source_id)?,
1633 };
1634 dep.set_features(self.features.iter().flatten())
1635 .set_default_features(
1636 self.default_features
1637 .or(self.default_features2)
1638 .unwrap_or(true),
1639 )
1640 .set_optional(self.optional.unwrap_or(false))
1641 .set_platform(cx.platform.clone());
1642 if let Some(registry) = &self.registry {
1643 let registry_id = SourceId::alt_registry(cx.config, registry)?;
1644 dep.set_registry_id(registry_id);
1645 }
1646 if let Some(registry_index) = &self.registry_index {
1647 let url = registry_index.into_url()?;
1648 let registry_id = SourceId::for_registry(&url)?;
1649 dep.set_registry_id(registry_id);
1650 }
1651
1652 if let Some(kind) = kind {
1653 dep.set_kind(kind);
1654 }
1655 if let Some(name_in_toml) = explicit_name_in_toml {
1656 cx.features.require(Feature::rename_dependency())?;
1657 dep.set_explicit_name_in_toml(name_in_toml);
1658 }
1659
1660 if let Some(p) = self.public {
1661 cx.features.require(Feature::public_dependency())?;
1662
1663 if dep.kind() != DepKind::Normal {
1664 bail!("'public' specifier can only be used on regular dependencies, not {:?} dependencies", dep.kind());
1665 }
1666
1667 dep.set_public(p);
1668 }
1669 Ok(dep)
1670 }
1671}
1672
1673#[derive(Default, Serialize, Deserialize, Debug, Clone)]
1674struct TomlTarget {
1675 name: Option<String>,
1676
1677 #[serde(rename = "crate-type")]
1680 crate_type: Option<Vec<String>>,
1681 #[serde(rename = "crate_type")]
1682 crate_type2: Option<Vec<String>>,
1683
1684 path: Option<PathValue>,
1685 test: Option<bool>,
1686 doctest: Option<bool>,
1687 bench: Option<bool>,
1688 doc: Option<bool>,
1689 plugin: Option<bool>,
1690 #[serde(rename = "proc-macro")]
1691 proc_macro: Option<bool>,
1692 #[serde(rename = "proc_macro")]
1693 proc_macro2: Option<bool>,
1694 harness: Option<bool>,
1695 #[serde(rename = "required-features")]
1696 required_features: Option<Vec<String>>,
1697 edition: Option<String>,
1698}
1699
1700#[derive(Clone)]
1701struct PathValue(PathBuf);
1702
1703impl<'de> de::Deserialize<'de> for PathValue {
1704 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1705 where
1706 D: de::Deserializer<'de>,
1707 {
1708 Ok(PathValue(String::deserialize(deserializer)?.into()))
1709 }
1710}
1711
1712impl ser::Serialize for PathValue {
1713 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1714 where
1715 S: ser::Serializer,
1716 {
1717 self.0.serialize(serializer)
1718 }
1719}
1720
1721#[derive(Serialize, Deserialize, Debug)]
1723struct TomlPlatform {
1724 dependencies: Option<BTreeMap<String, TomlDependency>>,
1725 #[serde(rename = "build-dependencies")]
1726 build_dependencies: Option<BTreeMap<String, TomlDependency>>,
1727 #[serde(rename = "build_dependencies")]
1728 build_dependencies2: Option<BTreeMap<String, TomlDependency>>,
1729 #[serde(rename = "dev-dependencies")]
1730 dev_dependencies: Option<BTreeMap<String, TomlDependency>>,
1731 #[serde(rename = "dev_dependencies")]
1732 dev_dependencies2: Option<BTreeMap<String, TomlDependency>>,
1733}
1734
1735impl TomlTarget {
1736 fn new() -> TomlTarget {
1737 TomlTarget::default()
1738 }
1739
1740 fn name(&self) -> String {
1741 match self.name {
1742 Some(ref name) => name.clone(),
1743 None => panic!("target name is required"),
1744 }
1745 }
1746
1747 fn proc_macro(&self) -> Option<bool> {
1748 self.proc_macro.or(self.proc_macro2).or_else(|| {
1749 if let Some(types) = self.crate_types() {
1750 if types.contains(&"proc-macro".to_string()) {
1751 return Some(true);
1752 }
1753 }
1754 None
1755 })
1756 }
1757
1758 fn crate_types(&self) -> Option<&Vec<String>> {
1759 self.crate_type
1760 .as_ref()
1761 .or_else(|| self.crate_type2.as_ref())
1762 }
1763}
1764
1765impl fmt::Debug for PathValue {
1766 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1767 self.0.fmt(f)
1768 }
1769}