1 use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
2 use std::fmt;
3 use std::marker::PhantomData;
4 use std::path::{Path, PathBuf};
5 use std::rc::Rc;
6 use std::str;
7
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};
13 use serde::de;
14 use serde::ser;
15 use serde::{Deserialize, Serialize};
16 use url::Url;
17
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;
28 use crate::util::{
29 self, config::ConfigRelativePath, validate_package_name, Config, IntoUrl, VersionReqExt,
30 };
31
32 mod targets;
33 use self::targets::targets;
34
35 /// Loads a `Cargo.toml` from a file on disk.
36 ///
37 /// This could result in a real or virtual manifest being returned.
38 ///
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
42 /// canonicalized.
read_manifest( path: &Path, source_id: SourceId, config: &Config, ) -> Result<(EitherManifest, Vec<PathBuf>), ManifestError>43 pub fn read_manifest(
44 path: &Path,
45 source_id: SourceId,
46 config: &Config,
47 ) -> Result<(EitherManifest, Vec<PathBuf>), ManifestError> {
48 trace!(
49 "read_manifest; path={}; source-id={}",
50 path.display(),
51 source_id
52 );
53 let contents = paths::read(path).map_err(|err| ManifestError::new(err, path.into()))?;
54
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()))
58 }
59
do_read_manifest( contents: &str, manifest_file: &Path, source_id: SourceId, config: &Config, ) -> CargoResult<(EitherManifest, Vec<PathBuf>)>60 fn do_read_manifest(
61 contents: &str,
62 manifest_file: &Path,
63 source_id: SourceId,
64 config: &Config,
65 ) -> CargoResult<(EitherManifest, Vec<PathBuf>)> {
66 let package_root = manifest_file.parent().unwrap();
67
68 let toml = {
69 let pretty_filename = manifest_file
70 .strip_prefix(config.cwd())
71 .unwrap_or(manifest_file);
72 parse(contents, pretty_filename, config)?
73 };
74
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") {
78 bail!(
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()
82 );
83 }
84 }
85
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);
90 unused.insert(key);
91 })?;
92 let add_unused = |warnings: &mut Warnings| {
93 for key in unused {
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());
97 }
98 }
99 };
100
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()) {
107 bail!(
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"
111 )
112 }
113 Ok((EitherManifest::Real(manifest), paths))
114 } else {
115 let (mut m, paths) =
116 TomlManifest::to_virtual_manifest(&manifest, source_id, package_root, config)?;
117 add_unused(m.warnings_mut());
118 Ok((EitherManifest::Virtual(m), paths))
119 };
120
121 fn stringify(dst: &mut String, path: &serde_ignored::Path<'_>) {
122 use serde_ignored::Path;
123
124 match *path {
125 Path::Root => {}
126 Path::Seq { parent, index } => {
127 stringify(dst, parent);
128 if !dst.is_empty() {
129 dst.push('.');
130 }
131 dst.push_str(&index.to_string());
132 }
133 Path::Map { parent, ref key } => {
134 stringify(dst, parent);
135 if !dst.is_empty() {
136 dst.push('.');
137 }
138 dst.push_str(key);
139 }
140 Path::Some { parent }
141 | Path::NewtypeVariant { parent }
142 | Path::NewtypeStruct { parent } => stringify(dst, parent),
143 }
144 }
145 }
146
147 /// Attempts to parse a string into a [`toml::Value`]. This is not specific to any
148 /// particular kind of TOML file.
149 ///
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.
parse(toml: &str, _file: &Path, _config: &Config) -> CargoResult<toml::Value>153 pub fn parse(toml: &str, _file: &Path, _config: &Config) -> CargoResult<toml::Value> {
154 // At the moment, no compatibility checks are needed.
155 toml.parse()
156 .map_err(|e| anyhow::Error::from(e).context("could not parse input as TOML"))
157 }
158
159 type TomlLibTarget = TomlTarget;
160 type TomlBinTarget = TomlTarget;
161 type TomlExampleTarget = TomlTarget;
162 type TomlTestTarget = TomlTarget;
163 type TomlBenchTarget = TomlTarget;
164
165 #[derive(Clone, Debug, Serialize)]
166 #[serde(untagged)]
167 pub enum TomlDependency<P = String> {
168 /// In the simple format, only a version is specified, eg.
169 /// `package = "<version>"`
170 Simple(String),
171 /// The simple format is equivalent to a detailed dependency
172 /// specifying only a version, eg.
173 /// `package = { version = "<version>" }`
174 Detailed(DetailedTomlDependency<P>),
175 }
176
177 impl<'de, P: Deserialize<'de>> de::Deserialize<'de> for TomlDependency<P> {
deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: de::Deserializer<'de>,178 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
179 where
180 D: de::Deserializer<'de>,
181 {
182 struct TomlDependencyVisitor<P>(PhantomData<P>);
183
184 impl<'de, P: Deserialize<'de>> de::Visitor<'de> for TomlDependencyVisitor<P> {
185 type Value = TomlDependency<P>;
186
187 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
188 formatter.write_str(
189 "a version string like \"0.9.8\" or a \
190 detailed dependency like { version = \"0.9.8\" }",
191 )
192 }
193
194 fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
195 where
196 E: de::Error,
197 {
198 Ok(TomlDependency::Simple(s.to_owned()))
199 }
200
201 fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
202 where
203 V: de::MapAccess<'de>,
204 {
205 let mvd = de::value::MapAccessDeserializer::new(map);
206 DetailedTomlDependency::deserialize(mvd).map(TomlDependency::Detailed)
207 }
208 }
209
210 deserializer.deserialize_any(TomlDependencyVisitor(PhantomData))
211 }
212 }
213
214 pub trait ResolveToPath {
resolve(&self, config: &Config) -> PathBuf215 fn resolve(&self, config: &Config) -> PathBuf;
216 }
217
218 impl ResolveToPath for String {
resolve(&self, _: &Config) -> PathBuf219 fn resolve(&self, _: &Config) -> PathBuf {
220 self.into()
221 }
222 }
223
224 impl ResolveToPath for ConfigRelativePath {
resolve(&self, c: &Config) -> PathBuf225 fn resolve(&self, c: &Config) -> PathBuf {
226 self.resolve_path(c)
227 }
228 }
229
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.
244 path: Option<P>,
245 git: Option<String>,
246 branch: Option<String>,
247 tag: Option<String>,
248 rev: 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>,
256 }
257
258 // Explicit implementation so we avoid pulling in P: Default
259 impl<P> Default for DetailedTomlDependency<P> {
default() -> Self260 fn default() -> Self {
261 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(),
276 }
277 }
278 }
279
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>>>,
306 }
307
308 #[derive(Deserialize, Serialize, Clone, Debug, Default)]
309 pub struct TomlProfiles(BTreeMap<InternedString, TomlProfile>);
310
311 impl TomlProfiles {
get_all(&self) -> &BTreeMap<InternedString, TomlProfile>312 pub fn get_all(&self) -> &BTreeMap<InternedString, TomlProfile> {
313 &self.0
314 }
315
get(&self, name: &str) -> Option<&TomlProfile>316 pub fn get(&self, name: &str) -> Option<&TomlProfile> {
317 self.0.get(name)
318 }
319
validate(&self, features: &Features, warnings: &mut Vec<String>) -> CargoResult<()>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)?;
323 }
324 Ok(())
325 }
326 }
327
328 #[derive(Clone, Debug, Eq, PartialEq)]
329 pub struct TomlOptLevel(pub String);
330
331 impl<'de> de::Deserialize<'de> for TomlOptLevel {
deserialize<D>(d: D) -> Result<TomlOptLevel, D::Error> where D: de::Deserializer<'de>,332 fn deserialize<D>(d: D) -> Result<TomlOptLevel, D::Error>
333 where
334 D: de::Deserializer<'de>,
335 {
336 struct Visitor;
337
338 impl<'de> de::Visitor<'de> for Visitor {
339 type Value = TomlOptLevel;
340
341 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
342 formatter.write_str("an optimization level")
343 }
344
345 fn visit_i64<E>(self, value: i64) -> Result<TomlOptLevel, E>
346 where
347 E: de::Error,
348 {
349 Ok(TomlOptLevel(value.to_string()))
350 }
351
352 fn visit_str<E>(self, value: &str) -> Result<TomlOptLevel, E>
353 where
354 E: de::Error,
355 {
356 if value == "s" || value == "z" {
357 Ok(TomlOptLevel(value.to_string()))
358 } else {
359 Err(E::custom(format!(
360 "must be `0`, `1`, `2`, `3`, `s` or `z`, \
361 but found the string: \"{}\"",
362 value
363 )))
364 }
365 }
366 }
367
368 d.deserialize_any(Visitor)
369 }
370 }
371
372 impl ser::Serialize for TomlOptLevel {
serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: ser::Serializer,373 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
374 where
375 S: ser::Serializer,
376 {
377 match self.0.parse::<u32>() {
378 Ok(n) => n.serialize(serializer),
379 Err(_) => self.0.serialize(serializer),
380 }
381 }
382 }
383
384 #[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
385 #[serde(untagged, expecting = "expected a boolean or an integer")]
386 pub enum U32OrBool {
387 U32(u32),
388 Bool(bool),
389 }
390
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>>,
412 }
413
414 #[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
415 pub enum ProfilePackageSpec {
416 Spec(PackageIdSpec),
417 All,
418 }
419
420 impl ser::Serialize for ProfilePackageSpec {
serialize<S>(&self, s: S) -> Result<S::Ok, S::Error> where S: ser::Serializer,421 fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
422 where
423 S: ser::Serializer,
424 {
425 match *self {
426 ProfilePackageSpec::Spec(ref spec) => spec.serialize(s),
427 ProfilePackageSpec::All => "*".serialize(s),
428 }
429 }
430 }
431
432 impl<'de> de::Deserialize<'de> for ProfilePackageSpec {
deserialize<D>(d: D) -> Result<ProfilePackageSpec, D::Error> where D: de::Deserializer<'de>,433 fn deserialize<D>(d: D) -> Result<ProfilePackageSpec, D::Error>
434 where
435 D: de::Deserializer<'de>,
436 {
437 let string = String::deserialize(d)?;
438 if string == "*" {
439 Ok(ProfilePackageSpec::All)
440 } else {
441 PackageIdSpec::parse(&string)
442 .map_err(de::Error::custom)
443 .map(ProfilePackageSpec::Spec)
444 }
445 }
446 }
447
448 impl TomlProfile {
validate( &self, name: &str, features: &Features, warnings: &mut Vec<String>, ) -> CargoResult<()>449 pub fn validate(
450 &self,
451 name: &str,
452 features: &Features,
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)?;
458 }
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)?;
463 }
464 }
465
466 // Feature gate definition of named profiles
467 match name {
468 "dev" | "release" | "bench" | "test" | "doc" => {}
469 _ => {
470 features.require(Feature::named_profiles())?;
471 }
472 }
473
474 // Profile name validation
475 Self::validate_name(name)?;
476
477 // Feature gate on uses of keys related to named profiles
478 if self.inherits.is_some() {
479 features.require(Feature::named_profiles())?;
480 }
481
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.
486 bail!(
487 "dir-name=\"{}\" in profile `{}` is not currently allowed, \
488 directory names are tied to the profile name for custom profiles",
489 dir_name,
490 name
491 );
492 }
493
494 // `inherits` validation
495 if matches!(self.inherits.map(|s| s.as_str()), Some("debug")) {
496 bail!(
497 "profile.{}.inherits=\"debug\" should be profile.{}.inherits=\"dev\"",
498 name,
499 name
500 );
501 }
502
503 match name {
504 "doc" => {
505 warnings.push("profile `doc` is deprecated and has no effect".to_string());
506 }
507 "test" | "bench" => {
508 if self.panic.is_some() {
509 warnings.push(format!("`panic` setting is ignored for `{}` profile", name))
510 }
511 }
512 _ => {}
513 }
514
515 if let Some(panic) = &self.panic {
516 if panic != "unwind" && panic != "abort" {
517 bail!(
518 "`panic` setting of `{}` is not a valid setting, \
519 must be `unwind` or `abort`",
520 panic
521 );
522 }
523 }
524
525 if self.strip.is_some() {
526 features.require(Feature::strip())?;
527 }
528
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 != '_') {
532 bail!(
533 "`profile.{}.codegen-backend` setting of `{}` is not a valid backend name.",
534 name,
535 codegen_backend,
536 );
537 }
538 }
539
540 Ok(())
541 }
542
543 /// Validate dir-names and profile names according to RFC 2678.
validate_name(name: &str) -> CargoResult<()>544 pub fn validate_name(name: &str) -> CargoResult<()> {
545 if let Some(ch) = name
546 .chars()
547 .find(|ch| !ch.is_alphanumeric() && *ch != '_' && *ch != '-')
548 {
549 bail!(
550 "invalid character `{}` in profile name `{}`\n\
551 Allowed characters are letters, numbers, underscore, and hyphen.",
552 ch,
553 name
554 );
555 }
556
557 const SEE_DOCS: &str = "See https://doc.rust-lang.org/cargo/reference/profiles.html \
558 for more on configuring profiles.";
559
560 let lower_name = name.to_lowercase();
561 if lower_name == "debug" {
562 bail!(
563 "profile name `{}` is reserved\n\
564 To configure the default development profile, use the name `dev` \
565 as in [profile.dev]\n\
566 {}",
567 name,
568 SEE_DOCS
569 );
570 }
571 if lower_name == "build-override" {
572 bail!(
573 "profile name `{}` is reserved\n\
574 To configure build dependency settings, use [profile.dev.build-override] \
575 and [profile.release.build-override]\n\
576 {}",
577 name,
578 SEE_DOCS
579 );
580 }
581
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
586 // be used.
587 if matches!(
588 lower_name.as_str(),
589 "build"
590 | "check"
591 | "clean"
592 | "config"
593 | "fetch"
594 | "fix"
595 | "install"
596 | "metadata"
597 | "package"
598 | "publish"
599 | "report"
600 | "root"
601 | "run"
602 | "rust"
603 | "rustc"
604 | "rustdoc"
605 | "target"
606 | "tmp"
607 | "uninstall"
608 ) || lower_name.starts_with("cargo")
609 {
610 bail!(
611 "profile name `{}` is reserved\n\
612 Please choose a different name.\n\
613 {}",
614 name,
615 SEE_DOCS
616 );
617 }
618
619 Ok(())
620 }
621
validate_override(&self, which: &str, features: &Features) -> CargoResult<()>622 fn validate_override(&self, which: &str, features: &Features) -> CargoResult<()> {
623 if self.package.is_some() {
624 bail!("package-specific profiles cannot be nested");
625 }
626 if self.build_override.is_some() {
627 bail!("build-override profiles cannot be nested");
628 }
629 if self.panic.is_some() {
630 bail!("`panic` may not be specified in a `{}` profile", which)
631 }
632 if self.lto.is_some() {
633 bail!("`lto` may not be specified in a `{}` profile", which)
634 }
635 if self.rpath.is_some() {
636 bail!("`rpath` may not be specified in a `{}` profile", which)
637 }
638 if self.codegen_backend.is_some() {
639 features.require(Feature::codegen_backend())?;
640 }
641 Ok(())
642 }
643
644 /// Overwrite self's values with the given profile.
merge(&mut self, profile: &TomlProfile)645 pub fn merge(&mut self, profile: &TomlProfile) {
646 if let Some(v) = &profile.opt_level {
647 self.opt_level = Some(v.clone());
648 }
649
650 if let Some(v) = &profile.lto {
651 self.lto = Some(v.clone());
652 }
653
654 if let Some(v) = profile.codegen_backend {
655 self.codegen_backend = Some(v);
656 }
657
658 if let Some(v) = profile.codegen_units {
659 self.codegen_units = Some(v);
660 }
661
662 if let Some(v) = &profile.debug {
663 self.debug = Some(v.clone());
664 }
665
666 if let Some(v) = profile.debug_assertions {
667 self.debug_assertions = Some(v);
668 }
669
670 if let Some(v) = &profile.split_debuginfo {
671 self.split_debuginfo = Some(v.clone());
672 }
673
674 if let Some(v) = profile.rpath {
675 self.rpath = Some(v);
676 }
677
678 if let Some(v) = &profile.panic {
679 self.panic = Some(v.clone());
680 }
681
682 if let Some(v) = profile.overflow_checks {
683 self.overflow_checks = Some(v);
684 }
685
686 if let Some(v) = profile.incremental {
687 self.incremental = Some(v);
688 }
689
690 if let Some(other_package) = &profile.package {
691 match &mut self.package {
692 Some(self_package) => {
693 for (spec, other_pkg_profile) in other_package {
694 match self_package.get_mut(spec) {
695 Some(p) => p.merge(other_pkg_profile),
696 None => {
697 self_package.insert(spec.clone(), other_pkg_profile.clone());
698 }
699 }
700 }
701 }
702 None => self.package = Some(other_package.clone()),
703 }
704 }
705
706 if let Some(other_bo) = &profile.build_override {
707 match &mut self.build_override {
708 Some(self_bo) => self_bo.merge(other_bo),
709 None => self.build_override = Some(other_bo.clone()),
710 }
711 }
712
713 if let Some(v) = &profile.inherits {
714 self.inherits = Some(*v);
715 }
716
717 if let Some(v) = &profile.dir_name {
718 self.dir_name = Some(*v);
719 }
720
721 if let Some(v) = &profile.strip {
722 self.strip = Some(v.clone());
723 }
724 }
725 }
726
727 /// A StringOrVec can be parsed from either a TOML string or array,
728 /// but is always stored as a vector.
729 #[derive(Clone, Debug, Serialize, Eq, PartialEq, PartialOrd, Ord)]
730 pub struct StringOrVec(Vec<String>);
731
732 impl<'de> de::Deserialize<'de> for StringOrVec {
deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: de::Deserializer<'de>,733 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
734 where
735 D: de::Deserializer<'de>,
736 {
737 struct Visitor;
738
739 impl<'de> de::Visitor<'de> for Visitor {
740 type Value = StringOrVec;
741
742 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
743 formatter.write_str("string or list of strings")
744 }
745
746 fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
747 where
748 E: de::Error,
749 {
750 Ok(StringOrVec(vec![s.to_string()]))
751 }
752
753 fn visit_seq<V>(self, v: V) -> Result<Self::Value, V::Error>
754 where
755 V: de::SeqAccess<'de>,
756 {
757 let seq = de::value::SeqAccessDeserializer::new(v);
758 Vec::deserialize(seq).map(StringOrVec)
759 }
760 }
761
762 deserializer.deserialize_any(Visitor)
763 }
764 }
765
766 impl StringOrVec {
iter<'a>(&'a self) -> std::slice::Iter<'a, String>767 pub fn iter<'a>(&'a self) -> std::slice::Iter<'a, String> {
768 self.0.iter()
769 }
770 }
771
772 #[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
773 #[serde(untagged, expecting = "expected a boolean or a string")]
774 pub enum StringOrBool {
775 String(String),
776 Bool(bool),
777 }
778
779 #[derive(PartialEq, Clone, Debug, Serialize)]
780 #[serde(untagged)]
781 pub enum VecStringOrBool {
782 VecString(Vec<String>),
783 Bool(bool),
784 }
785
786 impl<'de> de::Deserialize<'de> for VecStringOrBool {
deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: de::Deserializer<'de>,787 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
788 where
789 D: de::Deserializer<'de>,
790 {
791 struct Visitor;
792
793 impl<'de> de::Visitor<'de> for Visitor {
794 type Value = VecStringOrBool;
795
796 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
797 formatter.write_str("a boolean or vector of strings")
798 }
799
800 fn visit_seq<V>(self, v: V) -> Result<Self::Value, V::Error>
801 where
802 V: de::SeqAccess<'de>,
803 {
804 let seq = de::value::SeqAccessDeserializer::new(v);
805 Vec::deserialize(seq).map(VecStringOrBool::VecString)
806 }
807
808 fn visit_bool<E>(self, b: bool) -> Result<Self::Value, E>
809 where
810 E: de::Error,
811 {
812 Ok(VecStringOrBool::Bool(b))
813 }
814 }
815
816 deserializer.deserialize_any(Visitor)
817 }
818 }
819
version_trim_whitespace<'de, D>(deserializer: D) -> Result<semver::Version, D::Error> where D: de::Deserializer<'de>,820 fn version_trim_whitespace<'de, D>(deserializer: D) -> Result<semver::Version, D::Error>
821 where
822 D: de::Deserializer<'de>,
823 {
824 struct Visitor;
825
826 impl<'de> de::Visitor<'de> for Visitor {
827 type Value = semver::Version;
828
829 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
830 formatter.write_str("SemVer version")
831 }
832
833 fn visit_str<E>(self, string: &str) -> Result<Self::Value, E>
834 where
835 E: de::Error,
836 {
837 string.trim().parse().map_err(de::Error::custom)
838 }
839 }
840
841 deserializer.deserialize_str(Visitor)
842 }
843
844 /// Represents the `package`/`project` sections of a `Cargo.toml`.
845 ///
846 /// Note that the order of the fields matters, since this is the order they
847 /// are serialized to a TOML file. For example, you cannot have values after
848 /// the field `metadata`, since it is a table and values cannot appear after
849 /// tables.
850 #[derive(Deserialize, Serialize, Clone, Debug)]
851 #[serde(rename_all = "kebab-case")]
852 pub struct TomlProject {
853 edition: Option<String>,
854 rust_version: Option<String>,
855 name: InternedString,
856 #[serde(deserialize_with = "version_trim_whitespace")]
857 version: semver::Version,
858 authors: Option<Vec<String>>,
859 build: Option<StringOrBool>,
860 metabuild: Option<StringOrVec>,
861 #[serde(rename = "default-target")]
862 default_target: Option<String>,
863 #[serde(rename = "forced-target")]
864 forced_target: Option<String>,
865 links: Option<String>,
866 exclude: Option<Vec<String>>,
867 include: Option<Vec<String>>,
868 publish: Option<VecStringOrBool>,
869 workspace: Option<String>,
870 im_a_teapot: Option<bool>,
871 autobins: Option<bool>,
872 autoexamples: Option<bool>,
873 autotests: Option<bool>,
874 autobenches: Option<bool>,
875 default_run: Option<String>,
876
877 // Package metadata.
878 description: Option<String>,
879 homepage: Option<String>,
880 documentation: Option<String>,
881 readme: Option<StringOrBool>,
882 keywords: Option<Vec<String>>,
883 categories: Option<Vec<String>>,
884 license: Option<String>,
885 license_file: Option<String>,
886 repository: Option<String>,
887 resolver: Option<String>,
888
889 // Note that this field must come last due to the way toml serialization
890 // works which requires tables to be emitted after all values.
891 metadata: Option<toml::Value>,
892 }
893
894 #[derive(Debug, Deserialize, Serialize)]
895 pub struct TomlWorkspace {
896 members: Option<Vec<String>>,
897 #[serde(rename = "default-members")]
898 default_members: Option<Vec<String>>,
899 exclude: Option<Vec<String>>,
900 resolver: Option<String>,
901
902 // Note that this field must come last due to the way toml serialization
903 // works which requires tables to be emitted after all values.
904 metadata: Option<toml::Value>,
905 }
906
907 impl TomlProject {
to_package_id(&self, source_id: SourceId) -> CargoResult<PackageId>908 pub fn to_package_id(&self, source_id: SourceId) -> CargoResult<PackageId> {
909 PackageId::new(self.name, self.version.clone(), source_id)
910 }
911 }
912
913 struct Context<'a, 'b> {
914 deps: &'a mut Vec<Dependency>,
915 source_id: SourceId,
916 nested_paths: &'a mut Vec<PathBuf>,
917 config: &'b Config,
918 warnings: &'a mut Vec<String>,
919 platform: Option<Platform>,
920 root: &'a Path,
921 features: &'a Features,
922 }
923
924 impl TomlManifest {
925 /// Prepares the manifest for publishing.
926 // - Path and git components of dependency specifications are removed.
927 // - License path is updated to point within the package.
prepare_for_publish( &self, ws: &Workspace<'_>, package_root: &Path, ) -> CargoResult<TomlManifest>928 pub fn prepare_for_publish(
929 &self,
930 ws: &Workspace<'_>,
931 package_root: &Path,
932 ) -> CargoResult<TomlManifest> {
933 let config = ws.config();
934 let mut package = self
935 .package
936 .as_ref()
937 .or_else(|| self.project.as_ref())
938 .unwrap()
939 .clone();
940 package.workspace = None;
941 package.resolver = ws.resolve_behavior().to_manifest();
942 if let Some(license_file) = &package.license_file {
943 let license_path = Path::new(&license_file);
944 let abs_license_path = paths::normalize_path(&package_root.join(license_path));
945 if abs_license_path.strip_prefix(package_root).is_err() {
946 // This path points outside of the package root. `cargo package`
947 // will copy it into the root, so adjust the path to this location.
948 package.license_file = Some(
949 license_path
950 .file_name()
951 .unwrap()
952 .to_str()
953 .unwrap()
954 .to_string(),
955 );
956 }
957 }
958 let all = |_d: &TomlDependency| true;
959 return Ok(TomlManifest {
960 package: Some(package),
961 project: None,
962 profile: self.profile.clone(),
963 lib: self.lib.clone(),
964 bin: self.bin.clone(),
965 example: self.example.clone(),
966 test: self.test.clone(),
967 bench: self.bench.clone(),
968 dependencies: map_deps(config, self.dependencies.as_ref(), all)?,
969 dev_dependencies: map_deps(
970 config,
971 self.dev_dependencies
972 .as_ref()
973 .or_else(|| self.dev_dependencies2.as_ref()),
974 TomlDependency::is_version_specified,
975 )?,
976 dev_dependencies2: None,
977 build_dependencies: map_deps(
978 config,
979 self.build_dependencies
980 .as_ref()
981 .or_else(|| self.build_dependencies2.as_ref()),
982 all,
983 )?,
984 build_dependencies2: None,
985 features: self.features.clone(),
986 target: match self.target.as_ref().map(|target_map| {
987 target_map
988 .iter()
989 .map(|(k, v)| {
990 Ok((
991 k.clone(),
992 TomlPlatform {
993 dependencies: map_deps(config, v.dependencies.as_ref(), all)?,
994 dev_dependencies: map_deps(
995 config,
996 v.dev_dependencies
997 .as_ref()
998 .or_else(|| v.dev_dependencies2.as_ref()),
999 TomlDependency::is_version_specified,
1000 )?,
1001 dev_dependencies2: None,
1002 build_dependencies: map_deps(
1003 config,
1004 v.build_dependencies
1005 .as_ref()
1006 .or_else(|| v.build_dependencies2.as_ref()),
1007 all,
1008 )?,
1009 build_dependencies2: None,
1010 },
1011 ))
1012 })
1013 .collect()
1014 }) {
1015 Some(Ok(v)) => Some(v),
1016 Some(Err(e)) => return Err(e),
1017 None => None,
1018 },
1019 replace: None,
1020 patch: None,
1021 workspace: None,
1022 badges: self.badges.clone(),
1023 cargo_features: self.cargo_features.clone(),
1024 });
1025
1026 fn map_deps(
1027 config: &Config,
1028 deps: Option<&BTreeMap<String, TomlDependency>>,
1029 filter: impl Fn(&TomlDependency) -> bool,
1030 ) -> CargoResult<Option<BTreeMap<String, TomlDependency>>> {
1031 let deps = match deps {
1032 Some(deps) => deps,
1033 None => return Ok(None),
1034 };
1035 let deps = deps
1036 .iter()
1037 .filter(|(_k, v)| filter(v))
1038 .map(|(k, v)| Ok((k.clone(), map_dependency(config, v)?)))
1039 .collect::<CargoResult<BTreeMap<_, _>>>()?;
1040 Ok(Some(deps))
1041 }
1042
1043 fn map_dependency(config: &Config, dep: &TomlDependency) -> CargoResult<TomlDependency> {
1044 match dep {
1045 TomlDependency::Detailed(d) => {
1046 let mut d = d.clone();
1047 // Path dependencies become crates.io deps.
1048 d.path.take();
1049 // Same with git dependencies.
1050 d.git.take();
1051 d.branch.take();
1052 d.tag.take();
1053 d.rev.take();
1054 // registry specifications are elaborated to the index URL
1055 if let Some(registry) = d.registry.take() {
1056 let src = SourceId::alt_registry(config, ®istry)?;
1057 d.registry_index = Some(src.url().to_string());
1058 }
1059 Ok(TomlDependency::Detailed(d))
1060 }
1061 TomlDependency::Simple(s) => Ok(TomlDependency::Detailed(DetailedTomlDependency {
1062 version: Some(s.clone()),
1063 ..Default::default()
1064 })),
1065 }
1066 }
1067 }
1068
to_real_manifest( me: &Rc<TomlManifest>, source_id: SourceId, package_root: &Path, config: &Config, ) -> CargoResult<(Manifest, Vec<PathBuf>)>1069 pub fn to_real_manifest(
1070 me: &Rc<TomlManifest>,
1071 source_id: SourceId,
1072 package_root: &Path,
1073 config: &Config,
1074 ) -> CargoResult<(Manifest, Vec<PathBuf>)> {
1075 let mut nested_paths = vec![];
1076 let mut warnings = vec![];
1077 let mut errors = vec![];
1078
1079 // Parse features first so they will be available when parsing other parts of the TOML.
1080 let empty = Vec::new();
1081 let cargo_features = me.cargo_features.as_ref().unwrap_or(&empty);
1082 let features = Features::new(cargo_features, config, &mut warnings, source_id.is_path())?;
1083
1084 let project = me.project.as_ref().or_else(|| me.package.as_ref());
1085 let project = project.ok_or_else(|| anyhow!("no `package` section found"))?;
1086
1087 let package_name = project.name.trim();
1088 if package_name.is_empty() {
1089 bail!("package name cannot be an empty string")
1090 }
1091
1092 validate_package_name(package_name, "package name", "")?;
1093
1094 let pkgid = project.to_package_id(source_id)?;
1095
1096 let edition = if let Some(ref edition) = project.edition {
1097 features
1098 .require(Feature::edition())
1099 .with_context(|| "editions are unstable")?;
1100 edition
1101 .parse()
1102 .with_context(|| "failed to parse the `edition` key")?
1103 } else {
1104 Edition::Edition2015
1105 };
1106 if edition == Edition::Edition2021 {
1107 features.require(Feature::edition2021())?;
1108 } else if !edition.is_stable() {
1109 // Guard in case someone forgets to add .require()
1110 return Err(util::errors::internal(format!(
1111 "edition {} should be gated",
1112 edition
1113 )));
1114 }
1115
1116 let rust_version = if let Some(rust_version) = &project.rust_version {
1117 let req = match semver::VersionReq::parse(rust_version) {
1118 // Exclude semver operators like `^` and pre-release identifiers
1119 Ok(req) if rust_version.chars().all(|c| c.is_ascii_digit() || c == '.') => req,
1120 _ => bail!("`rust-version` must be a value like \"1.32\""),
1121 };
1122 if let Some(first_version) = edition.first_version() {
1123 let unsupported =
1124 semver::Version::new(first_version.major, first_version.minor - 1, 9999);
1125 if req.matches(&unsupported) {
1126 bail!(
1127 "rust-version {} is older than first version ({}) required by \
1128 the specified edition ({})",
1129 rust_version,
1130 first_version,
1131 edition,
1132 )
1133 }
1134 }
1135 Some(rust_version.clone())
1136 } else {
1137 None
1138 };
1139
1140 if project.metabuild.is_some() {
1141 features.require(Feature::metabuild())?;
1142 }
1143
1144 if project.resolver.is_some()
1145 || me
1146 .workspace
1147 .as_ref()
1148 .map_or(false, |ws| ws.resolver.is_some())
1149 {
1150 features.require(Feature::resolver())?;
1151 }
1152 let resolve_behavior = match (
1153 project.resolver.as_ref(),
1154 me.workspace.as_ref().and_then(|ws| ws.resolver.as_ref()),
1155 ) {
1156 (None, None) => None,
1157 (Some(s), None) | (None, Some(s)) => Some(ResolveBehavior::from_manifest(s)?),
1158 (Some(_), Some(_)) => {
1159 bail!("cannot specify `resolver` field in both `[workspace]` and `[package]`")
1160 }
1161 };
1162
1163 // If we have no lib at all, use the inferred lib, if available.
1164 // If we have a lib with a path, we're done.
1165 // If we have a lib with no path, use the inferred lib or else the package name.
1166 let targets = targets(
1167 &features,
1168 me,
1169 package_name,
1170 package_root,
1171 edition,
1172 &project.build,
1173 &project.metabuild,
1174 &mut warnings,
1175 &mut errors,
1176 )?;
1177
1178 if targets.is_empty() {
1179 debug!("manifest has no build targets");
1180 }
1181
1182 if let Err(e) = unique_build_targets(&targets, package_root) {
1183 warnings.push(format!(
1184 "file found to be present in multiple \
1185 build targets: {}",
1186 e
1187 ));
1188 }
1189
1190 if let Some(links) = &project.links {
1191 if !targets.iter().any(|t| t.is_custom_build()) {
1192 bail!(
1193 "package `{}` specifies that it links to `{}` but does not \
1194 have a custom build script",
1195 pkgid,
1196 links
1197 )
1198 }
1199 }
1200
1201 let mut deps = Vec::new();
1202 let replace;
1203 let patch;
1204
1205 {
1206 let mut cx = Context {
1207 deps: &mut deps,
1208 source_id,
1209 nested_paths: &mut nested_paths,
1210 config,
1211 warnings: &mut warnings,
1212 features: &features,
1213 platform: None,
1214 root: package_root,
1215 };
1216
1217 fn process_dependencies(
1218 cx: &mut Context<'_, '_>,
1219 new_deps: Option<&BTreeMap<String, TomlDependency>>,
1220 kind: Option<DepKind>,
1221 ) -> CargoResult<()> {
1222 let dependencies = match new_deps {
1223 Some(dependencies) => dependencies,
1224 None => return Ok(()),
1225 };
1226 for (n, v) in dependencies.iter() {
1227 let dep = v.to_dependency(n, cx, kind)?;
1228 validate_package_name(dep.name_in_toml().as_str(), "dependency name", "")?;
1229 cx.deps.push(dep);
1230 }
1231
1232 Ok(())
1233 }
1234
1235 // Collect the dependencies.
1236 process_dependencies(&mut cx, me.dependencies.as_ref(), None)?;
1237 let dev_deps = me
1238 .dev_dependencies
1239 .as_ref()
1240 .or_else(|| me.dev_dependencies2.as_ref());
1241 process_dependencies(&mut cx, dev_deps, Some(DepKind::Development))?;
1242 let build_deps = me
1243 .build_dependencies
1244 .as_ref()
1245 .or_else(|| me.build_dependencies2.as_ref());
1246 process_dependencies(&mut cx, build_deps, Some(DepKind::Build))?;
1247
1248 for (name, platform) in me.target.iter().flatten() {
1249 cx.platform = {
1250 let platform: Platform = name.parse()?;
1251 platform.check_cfg_attributes(&mut cx.warnings);
1252 Some(platform)
1253 };
1254 process_dependencies(&mut cx, platform.dependencies.as_ref(), None)?;
1255 let build_deps = platform
1256 .build_dependencies
1257 .as_ref()
1258 .or_else(|| platform.build_dependencies2.as_ref());
1259 process_dependencies(&mut cx, build_deps, Some(DepKind::Build))?;
1260 let dev_deps = platform
1261 .dev_dependencies
1262 .as_ref()
1263 .or_else(|| platform.dev_dependencies2.as_ref());
1264 process_dependencies(&mut cx, dev_deps, Some(DepKind::Development))?;
1265 }
1266
1267 replace = me.replace(&mut cx)?;
1268 patch = me.patch(&mut cx)?;
1269 }
1270
1271 {
1272 let mut names_sources = BTreeMap::new();
1273 for dep in &deps {
1274 let name = dep.name_in_toml();
1275 let prev = names_sources.insert(name.to_string(), dep.source_id());
1276 if prev.is_some() && prev != Some(dep.source_id()) {
1277 bail!(
1278 "Dependency '{}' has different source paths depending on the build \
1279 target. Each dependency must have a single canonical source path \
1280 irrespective of build target.",
1281 name
1282 );
1283 }
1284 }
1285 }
1286
1287 let exclude = project.exclude.clone().unwrap_or_default();
1288 let include = project.include.clone().unwrap_or_default();
1289 let empty_features = BTreeMap::new();
1290
1291 let summary = Summary::new(
1292 config,
1293 pkgid,
1294 deps,
1295 me.features.as_ref().unwrap_or(&empty_features),
1296 project.links.as_deref(),
1297 )?;
1298 let unstable = config.cli_unstable();
1299 summary.unstable_gate(unstable.namespaced_features, unstable.weak_dep_features)?;
1300
1301 let metadata = ManifestMetadata {
1302 description: project.description.clone(),
1303 homepage: project.homepage.clone(),
1304 documentation: project.documentation.clone(),
1305 readme: readme_for_project(package_root, project),
1306 authors: project.authors.clone().unwrap_or_default(),
1307 license: project.license.clone(),
1308 license_file: project.license_file.clone(),
1309 repository: project.repository.clone(),
1310 keywords: project.keywords.clone().unwrap_or_default(),
1311 categories: project.categories.clone().unwrap_or_default(),
1312 badges: me.badges.clone().unwrap_or_default(),
1313 links: project.links.clone(),
1314 };
1315
1316 let workspace_config = match (me.workspace.as_ref(), project.workspace.as_ref()) {
1317 (Some(config), None) => WorkspaceConfig::Root(WorkspaceRootConfig::new(
1318 package_root,
1319 &config.members,
1320 &config.default_members,
1321 &config.exclude,
1322 &config.metadata,
1323 )),
1324 (None, root) => WorkspaceConfig::Member {
1325 root: root.cloned(),
1326 },
1327 (Some(..), Some(..)) => bail!(
1328 "cannot configure both `package.workspace` and \
1329 `[workspace]`, only one can be specified"
1330 ),
1331 };
1332 let profiles = me.profile.clone();
1333 if let Some(profiles) = &profiles {
1334 profiles.validate(&features, &mut warnings)?;
1335 }
1336 let publish = match project.publish {
1337 Some(VecStringOrBool::VecString(ref vecstring)) => Some(vecstring.clone()),
1338 Some(VecStringOrBool::Bool(false)) => Some(vec![]),
1339 None | Some(VecStringOrBool::Bool(true)) => None,
1340 };
1341
1342 if summary.features().contains_key("default-features") {
1343 warnings.push(
1344 "`default-features = [\"..\"]` was found in [features]. \
1345 Did you mean to use `default = [\"..\"]`?"
1346 .to_string(),
1347 )
1348 }
1349
1350 if let Some(run) = &project.default_run {
1351 if !targets
1352 .iter()
1353 .filter(|t| t.is_bin())
1354 .any(|t| t.name() == run)
1355 {
1356 let suggestion =
1357 util::closest_msg(run, targets.iter().filter(|t| t.is_bin()), |t| t.name());
1358 bail!("default-run target `{}` not found{}", run, suggestion);
1359 }
1360 }
1361
1362 let default_kind = project
1363 .default_target
1364 .as_ref()
1365 .map(|t| CompileTarget::new(&*t))
1366 .transpose()?
1367 .map(CompileKind::Target);
1368 let forced_kind = project
1369 .forced_target
1370 .as_ref()
1371 .map(|t| CompileTarget::new(&*t))
1372 .transpose()?
1373 .map(CompileKind::Target);
1374
1375 let custom_metadata = project.metadata.clone();
1376 let mut manifest = Manifest::new(
1377 summary,
1378 default_kind,
1379 forced_kind,
1380 targets,
1381 exclude,
1382 include,
1383 project.links.clone(),
1384 metadata,
1385 custom_metadata,
1386 profiles,
1387 publish,
1388 replace,
1389 patch,
1390 workspace_config,
1391 features,
1392 edition,
1393 rust_version,
1394 project.im_a_teapot,
1395 project.default_run.clone(),
1396 Rc::clone(me),
1397 project.metabuild.clone().map(|sov| sov.0),
1398 resolve_behavior,
1399 );
1400 if project.license_file.is_some() && project.license.is_some() {
1401 manifest.warnings_mut().add_warning(
1402 "only one of `license` or `license-file` is necessary\n\
1403 `license` should be used if the package license can be expressed \
1404 with a standard SPDX expression.\n\
1405 `license-file` should be used if the package uses a non-standard license.\n\
1406 See https://doc.rust-lang.org/cargo/reference/manifest.html#the-license-and-license-file-fields \
1407 for more information."
1408 .to_string(),
1409 );
1410 }
1411 for warning in warnings {
1412 manifest.warnings_mut().add_warning(warning);
1413 }
1414 for error in errors {
1415 manifest.warnings_mut().add_critical_warning(error);
1416 }
1417
1418 manifest.feature_gate()?;
1419
1420 Ok((manifest, nested_paths))
1421 }
1422
to_virtual_manifest( me: &Rc<TomlManifest>, source_id: SourceId, root: &Path, config: &Config, ) -> CargoResult<(VirtualManifest, Vec<PathBuf>)>1423 fn to_virtual_manifest(
1424 me: &Rc<TomlManifest>,
1425 source_id: SourceId,
1426 root: &Path,
1427 config: &Config,
1428 ) -> CargoResult<(VirtualManifest, Vec<PathBuf>)> {
1429 if me.project.is_some() {
1430 bail!("this virtual manifest specifies a [project] section, which is not allowed");
1431 }
1432 if me.package.is_some() {
1433 bail!("this virtual manifest specifies a [package] section, which is not allowed");
1434 }
1435 if me.lib.is_some() {
1436 bail!("this virtual manifest specifies a [lib] section, which is not allowed");
1437 }
1438 if me.bin.is_some() {
1439 bail!("this virtual manifest specifies a [[bin]] section, which is not allowed");
1440 }
1441 if me.example.is_some() {
1442 bail!("this virtual manifest specifies a [[example]] section, which is not allowed");
1443 }
1444 if me.test.is_some() {
1445 bail!("this virtual manifest specifies a [[test]] section, which is not allowed");
1446 }
1447 if me.bench.is_some() {
1448 bail!("this virtual manifest specifies a [[bench]] section, which is not allowed");
1449 }
1450 if me.dependencies.is_some() {
1451 bail!("this virtual manifest specifies a [dependencies] section, which is not allowed");
1452 }
1453 if me.dev_dependencies.is_some() || me.dev_dependencies2.is_some() {
1454 bail!("this virtual manifest specifies a [dev-dependencies] section, which is not allowed");
1455 }
1456 if me.build_dependencies.is_some() || me.build_dependencies2.is_some() {
1457 bail!("this virtual manifest specifies a [build-dependencies] section, which is not allowed");
1458 }
1459 if me.features.is_some() {
1460 bail!("this virtual manifest specifies a [features] section, which is not allowed");
1461 }
1462 if me.target.is_some() {
1463 bail!("this virtual manifest specifies a [target] section, which is not allowed");
1464 }
1465 if me.badges.is_some() {
1466 bail!("this virtual manifest specifies a [badges] section, which is not allowed");
1467 }
1468
1469 let mut nested_paths = Vec::new();
1470 let mut warnings = Vec::new();
1471 let mut deps = Vec::new();
1472 let empty = Vec::new();
1473 let cargo_features = me.cargo_features.as_ref().unwrap_or(&empty);
1474 let features = Features::new(cargo_features, config, &mut warnings, source_id.is_path())?;
1475
1476 let (replace, patch) = {
1477 let mut cx = Context {
1478 deps: &mut deps,
1479 source_id,
1480 nested_paths: &mut nested_paths,
1481 config,
1482 warnings: &mut warnings,
1483 platform: None,
1484 features: &features,
1485 root,
1486 };
1487 (me.replace(&mut cx)?, me.patch(&mut cx)?)
1488 };
1489 let profiles = me.profile.clone();
1490 if let Some(profiles) = &profiles {
1491 profiles.validate(&features, &mut warnings)?;
1492 }
1493 if me
1494 .workspace
1495 .as_ref()
1496 .map_or(false, |ws| ws.resolver.is_some())
1497 {
1498 features.require(Feature::resolver())?;
1499 }
1500 let resolve_behavior = me
1501 .workspace
1502 .as_ref()
1503 .and_then(|ws| ws.resolver.as_deref())
1504 .map(|r| ResolveBehavior::from_manifest(r))
1505 .transpose()?;
1506 let workspace_config = match me.workspace {
1507 Some(ref config) => WorkspaceConfig::Root(WorkspaceRootConfig::new(
1508 root,
1509 &config.members,
1510 &config.default_members,
1511 &config.exclude,
1512 &config.metadata,
1513 )),
1514 None => {
1515 bail!("virtual manifests must be configured with [workspace]");
1516 }
1517 };
1518 Ok((
1519 VirtualManifest::new(
1520 replace,
1521 patch,
1522 workspace_config,
1523 profiles,
1524 features,
1525 resolve_behavior,
1526 ),
1527 nested_paths,
1528 ))
1529 }
1530
replace(&self, cx: &mut Context<'_, '_>) -> CargoResult<Vec<(PackageIdSpec, Dependency)>>1531 fn replace(&self, cx: &mut Context<'_, '_>) -> CargoResult<Vec<(PackageIdSpec, Dependency)>> {
1532 if self.patch.is_some() && self.replace.is_some() {
1533 bail!("cannot specify both [replace] and [patch]");
1534 }
1535 let mut replace = Vec::new();
1536 for (spec, replacement) in self.replace.iter().flatten() {
1537 let mut spec = PackageIdSpec::parse(spec).with_context(|| {
1538 format!(
1539 "replacements must specify a valid semver \
1540 version to replace, but `{}` does not",
1541 spec
1542 )
1543 })?;
1544 if spec.url().is_none() {
1545 spec.set_url(CRATES_IO_INDEX.parse().unwrap());
1546 }
1547
1548 if replacement.is_version_specified() {
1549 bail!(
1550 "replacements cannot specify a version \
1551 requirement, but found one for `{}`",
1552 spec
1553 );
1554 }
1555
1556 let mut dep = replacement.to_dependency(spec.name().as_str(), cx, None)?;
1557 let version = spec.version().ok_or_else(|| {
1558 anyhow!(
1559 "replacements must specify a version \
1560 to replace, but `{}` does not",
1561 spec
1562 )
1563 })?;
1564 dep.set_version_req(VersionReq::exact(version))
1565 .lock_version(version);
1566 replace.push((spec, dep));
1567 }
1568 Ok(replace)
1569 }
1570
patch(&self, cx: &mut Context<'_, '_>) -> CargoResult<HashMap<Url, Vec<Dependency>>>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(),
1576 _ => cx
1577 .config
1578 .get_registry_index(url)
1579 .or_else(|_| url.into_url())
1580 .with_context(|| {
1581 format!("[patch] entry `{}` should be a URL or registry name", url)
1582 })?,
1583 };
1584 patch.insert(
1585 url,
1586 deps.iter()
1587 .map(|(name, dep)| dep.to_dependency(name, cx, None))
1588 .collect::<CargoResult<Vec<_>>>()?,
1589 );
1590 }
1591 Ok(patch)
1592 }
1593
1594 /// Returns the path to the build script if one exists for this crate.
maybe_custom_build( &self, build: &Option<StringOrBool>, package_root: &Path, ) -> Option<PathBuf>1595 fn maybe_custom_build(
1596 &self,
1597 build: &Option<StringOrBool>,
1598 package_root: &Path,
1599 ) -> Option<PathBuf> {
1600 let build_rs = package_root.join("build.rs");
1601 match *build {
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)),
1606 None => {
1607 // If there is a `build.rs` file next to the `Cargo.toml`, assume it is
1608 // a build script.
1609 if build_rs.is_file() {
1610 Some(build_rs)
1611 } else {
1612 None
1613 }
1614 }
1615 }
1616 }
1617
has_profiles(&self) -> bool1618 pub fn has_profiles(&self) -> bool {
1619 self.profile.is_some()
1620 }
1621
features(&self) -> Option<&BTreeMap<InternedString, Vec<InternedString>>>1622 pub fn features(&self) -> Option<&BTreeMap<InternedString, Vec<InternedString>>> {
1623 self.features.as_ref()
1624 }
1625 }
1626
1627 /// Returns the name of the README file for a `TomlProject`.
readme_for_project(package_root: &Path, project: &TomlProject) -> Option<String>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()),
1635 },
1636 }
1637 }
1638
1639 const DEFAULT_README_FILES: [&str; 3] = ["README.md", "README.txt", "README"];
1640
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.
default_readme_from_package_root(package_root: &Path) -> Option<String>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());
1647 }
1648 }
1649
1650 None
1651 }
1652
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.
unique_build_targets(targets: &[Target], package_root: &Path) -> Result<(), String>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());
1662 }
1663 }
1664 }
1665 Ok(())
1666 }
1667
1668 impl<P: ResolveToPath> TomlDependency<P> {
to_dependency_split( &self, name: &str, source_id: SourceId, nested_paths: &mut Vec<PathBuf>, config: &Config, warnings: &mut Vec<String>, platform: Option<Platform>, root: &Path, features: &Features, kind: Option<DepKind>, ) -> CargoResult<Dependency>1669 pub(crate) fn to_dependency_split(
1670 &self,
1671 name: &str,
1672 source_id: SourceId,
1673 nested_paths: &mut Vec<PathBuf>,
1674 config: &Config,
1675 warnings: &mut Vec<String>,
1676 platform: Option<Platform>,
1677 root: &Path,
1678 features: &Features,
1679 kind: Option<DepKind>,
1680 ) -> CargoResult<Dependency> {
1681 self.to_dependency(
1682 name,
1683 &mut Context {
1684 deps: &mut Vec::new(),
1685 source_id,
1686 nested_paths,
1687 config,
1688 warnings,
1689 platform,
1690 root,
1691 features,
1692 },
1693 kind,
1694 )
1695 }
1696
to_dependency( &self, name: &str, cx: &mut Context<'_, '_>, kind: Option<DepKind>, ) -> CargoResult<Dependency>1697 fn to_dependency(
1698 &self,
1699 name: &str,
1700 cx: &mut Context<'_, '_>,
1701 kind: Option<DepKind>,
1702 ) -> CargoResult<Dependency> {
1703 match *self {
1704 TomlDependency::Simple(ref version) => DetailedTomlDependency::<P> {
1705 version: Some(version.clone()),
1706 ..Default::default()
1707 }
1708 .to_dependency(name, cx, kind),
1709 TomlDependency::Detailed(ref details) => details.to_dependency(name, cx, kind),
1710 }
1711 }
1712
is_version_specified(&self) -> bool1713 fn is_version_specified(&self) -> bool {
1714 match self {
1715 TomlDependency::Detailed(d) => d.version.is_some(),
1716 TomlDependency::Simple(..) => true,
1717 }
1718 }
1719 }
1720
1721 impl<P: ResolveToPath> DetailedTomlDependency<P> {
to_dependency( &self, name_in_toml: &str, cx: &mut Context<'_, '_>, kind: Option<DepKind>, ) -> CargoResult<Dependency>1722 fn to_dependency(
1723 &self,
1724 name_in_toml: &str,
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() {
1729 let msg = format!(
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",
1734 name_in_toml
1735 );
1736 cx.warnings.push(msg);
1737 }
1738
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
1746 ));
1747 }
1748 }
1749
1750 if self.git.is_none() {
1751 let git_only_keys = [
1752 (&self.branch, "branch"),
1753 (&self.tag, "tag"),
1754 (&self.rev, "rev"),
1755 ];
1756
1757 for &(key, key_name) in &git_only_keys {
1758 if key.is_some() {
1759 bail!(
1760 "key `{}` is ignored for dependency ({}).",
1761 key_name,
1762 name_in_toml
1763 );
1764 }
1765 }
1766 }
1767
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('/') {
1773 bail!(
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.",
1778 feature,
1779 name_in_toml
1780 );
1781 }
1782 if feature.starts_with("dep:") {
1783 bail!(
1784 "feature `{}` in dependency `{}` is not allowed to use explicit \
1785 `dep:` syntax\n\
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.",
1790 feature,
1791 name_in_toml
1792 );
1793 }
1794 }
1795 }
1796
1797 let new_source_id = match (
1798 self.git.as_ref(),
1799 self.path.as_ref(),
1800 self.registry.as_ref(),
1801 self.registry_index.as_ref(),
1802 ) {
1803 (Some(_), _, Some(_), _) | (Some(_), _, _, Some(_)) => bail!(
1804 "dependency ({}) specification is ambiguous. \
1805 Only one of `git` or `registry` is allowed.",
1806 name_in_toml
1807 ),
1808 (_, _, Some(_), Some(_)) => bail!(
1809 "dependency ({}) specification is ambiguous. \
1810 Only one of `registry` or `registry-index` is allowed.",
1811 name_in_toml
1812 ),
1813 (Some(git), maybe_path, _, _) => {
1814 if maybe_path.is_some() {
1815 bail!(
1816 "dependency ({}) specification is ambiguous. \
1817 Only one of `git` or `path` is allowed.",
1818 name_in_toml
1819 );
1820 }
1821
1822 let n_details = [&self.branch, &self.tag, &self.rev]
1823 .iter()
1824 .filter(|d| d.is_some())
1825 .count();
1826
1827 if n_details > 1 {
1828 bail!(
1829 "dependency ({}) specification is ambiguous. \
1830 Only one of `branch`, `tag` or `rev` is allowed.",
1831 name_in_toml
1832 );
1833 }
1834
1835 let reference = self
1836 .branch
1837 .clone()
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()?;
1843
1844 if let Some(fragment) = loc.fragment() {
1845 let msg = format!(
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
1850 );
1851 cx.warnings.push(msg)
1852 }
1853
1854 SourceId::for_git(&loc, reference)?
1855 }
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 `..`.
1862 //
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
1866 // built from.
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)?
1871 } else {
1872 cx.source_id
1873 }
1874 }
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)?
1879 }
1880 (None, None, None, None) => SourceId::crates_io(cx.config)?,
1881 };
1882
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),
1886 };
1887
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)
1894 .unwrap_or(true),
1895 )
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);
1901 }
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);
1906 }
1907
1908 if let Some(kind) = kind {
1909 dep.set_kind(kind);
1910 }
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);
1914 }
1915
1916 if let Some(p) = self.public {
1917 cx.features.require(Feature::public_dependency())?;
1918
1919 if dep.kind() != DepKind::Normal {
1920 bail!("'public' specifier can only be used on regular dependencies, not {:?} dependencies", dep.kind());
1921 }
1922
1923 dep.set_public(p);
1924 }
1925 Ok(dep)
1926 }
1927 }
1928
1929 #[derive(Default, Serialize, Deserialize, Debug, Clone)]
1930 struct TomlTarget {
1931 name: Option<String>,
1932
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>>,
1939
1940 path: Option<PathValue>,
1941 // Note that `filename` is used for the cargo-feature `different_binary_name`
1942 filename: Option<String>,
1943 test: Option<bool>,
1944 doctest: Option<bool>,
1945 bench: Option<bool>,
1946 doc: 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>,
1956 }
1957
1958 #[derive(Clone)]
1959 struct PathValue(PathBuf);
1960
1961 impl<'de> de::Deserialize<'de> for PathValue {
deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: de::Deserializer<'de>,1962 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1963 where
1964 D: de::Deserializer<'de>,
1965 {
1966 Ok(PathValue(String::deserialize(deserializer)?.into()))
1967 }
1968 }
1969
1970 impl ser::Serialize for PathValue {
serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: ser::Serializer,1971 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1972 where
1973 S: ser::Serializer,
1974 {
1975 self.0.serialize(serializer)
1976 }
1977 }
1978
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>>,
1991 }
1992
1993 impl TomlTarget {
new() -> TomlTarget1994 fn new() -> TomlTarget {
1995 TomlTarget::default()
1996 }
1997
name(&self) -> String1998 fn name(&self) -> String {
1999 match self.name {
2000 Some(ref name) => name.clone(),
2001 None => panic!("target name is required"),
2002 }
2003 }
2004
proc_macro(&self) -> Option<bool>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()) {
2009 return Some(true);
2010 }
2011 }
2012 None
2013 })
2014 }
2015
crate_types(&self) -> Option<&Vec<String>>2016 fn crate_types(&self) -> Option<&Vec<String>> {
2017 self.crate_type
2018 .as_ref()
2019 .or_else(|| self.crate_type2.as_ref())
2020 }
2021 }
2022
2023 impl fmt::Debug for PathValue {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result2024 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2025 self.0.fmt(f)
2026 }
2027 }
2028