1use crate::util::data_structures::HashMap;
2use cargo_util_terminal::report::{AnnotationKind, Group, Level, Snippet};
3use std::borrow::Cow;
4use std::cell::OnceCell;
5use std::collections::{BTreeMap, BTreeSet};
6use std::ffi::OsStr;
7use std::path::{Path, PathBuf};
8use std::rc::Rc;
9use std::str::{self, FromStr};
10use std::sync::Arc;
11
12use crate::AlreadyPrintedError;
13use crate::core::summary::MissingDependencyError;
14use anyhow::{Context as _, anyhow, bail};
15use cargo_platform::Platform;
16use cargo_util::paths;
17use cargo_util_schemas::manifest::{
18 self, PackageName, PathBaseName, TomlDependency, TomlDetailedDependency, TomlManifest,
19 TomlPackageBuild, TomlWorkspace,
20};
21use cargo_util_schemas::manifest::{RustVersion, StringOrBool};
22use itertools::Itertools;
23use pathdiff::diff_paths;
24use url::Url;
25
26use crate::core::compiler::{CompileKind, CompileTarget};
27use crate::core::dependency::{Artifact, ArtifactTarget, DepKind};
28use crate::core::manifest::{ManifestMetadata, TargetSourcePath};
29use crate::core::resolver::ResolveBehavior;
30use crate::core::{
31 CliUnstable, FeatureValue, Patch, PatchLocation, find_workspace_root, resolve_relative_path,
32};
33use crate::core::{Dependency, Manifest, Package, PackageId, Summary, Target};
34use crate::core::{Edition, EitherManifest, Feature, Features, VirtualManifest, Workspace};
35use crate::core::{GitReference, PackageIdSpec, SourceId, WorkspaceConfig, WorkspaceRootConfig};
36use crate::diagnostics::cwd_rel_path;
37use crate::diagnostics::get_key_value_span;
38use crate::sources::{CRATES_IO_INDEX, CRATES_IO_REGISTRY};
39use crate::util::errors::{CargoResult, ManifestError};
40use crate::util::interning::InternedString;
41use crate::util::{
42 self, GlobalContext, IntoUrl, OnceExt, OptVersionReq, context::ConfigRelativePath,
43 context::TOP_LEVEL_CONFIG_KEYS,
44};
45
46mod embedded;
47mod targets;
48
49use self::targets::to_targets;
50
51pub fn is_embedded(path: &Path) -> bool {
53 let ext = path.extension();
54 ext == Some(OsStr::new("rs")) || ext.is_none()
55}
56
57#[tracing::instrument(skip(gctx))]
66pub fn read_manifest(
67 path: &Path,
68 source_id: SourceId,
69 gctx: &GlobalContext,
70) -> CargoResult<EitherManifest> {
71 let mut warnings = Default::default();
72 let mut errors = Default::default();
73
74 let is_embedded = is_embedded(path);
75 let contents = read_toml_string(path, is_embedded, gctx)?;
76 let document = parse_document(&contents)
77 .map_err(|e| emit_toml_diagnostic(e.into(), &contents, path, gctx))?;
78 let original_toml = deserialize_toml(&document)
79 .map_err(|e| emit_toml_diagnostic(e.into(), &contents, path, gctx))?;
80
81 let document = make_document_owned(document);
82
83 let mut manifest = (|| {
84 let empty = Vec::new();
85 let cargo_features = original_toml.cargo_features.as_ref().unwrap_or(&empty);
86 let features = Features::new(cargo_features, gctx, &mut warnings, source_id.is_path())?;
87 let workspace_config =
88 to_workspace_config(&original_toml, path, is_embedded, gctx, &mut warnings)?;
89 if let WorkspaceConfig::Root(ws_root_config) = &workspace_config {
90 let package_root = path.parent().unwrap();
91 gctx.ws_roots()
92 .insert(package_root.to_owned(), ws_root_config.clone());
93 }
94 let normalized_toml = normalize_toml(
95 &original_toml,
96 &features,
97 &workspace_config,
98 path,
99 is_embedded,
100 gctx,
101 &mut warnings,
102 &mut errors,
103 )?;
104
105 if normalized_toml.package().is_some() {
106 to_real_manifest(
107 Some(contents),
108 Some(document),
109 original_toml,
110 normalized_toml,
111 features,
112 workspace_config,
113 source_id,
114 path,
115 is_embedded,
116 gctx,
117 &mut warnings,
118 &mut errors,
119 )
120 .map(EitherManifest::Real)
121 } else if normalized_toml.workspace.is_some() {
122 assert!(!is_embedded);
123 to_virtual_manifest(
124 Some(contents),
125 Some(document),
126 original_toml,
127 normalized_toml,
128 features,
129 workspace_config,
130 source_id,
131 path,
132 gctx,
133 &mut warnings,
134 &mut errors,
135 )
136 .map(EitherManifest::Virtual)
137 } else {
138 anyhow::bail!("manifest is missing either a `[package]` or a `[workspace]`")
139 }
140 })()
141 .map_err(|err| {
142 ManifestError::new(
143 err.context(format!("failed to parse manifest at `{}`", path.display())),
144 path.into(),
145 )
146 })?;
147
148 for warning in warnings {
149 manifest.warnings_mut().add_warning(warning);
150 }
151 for error in errors {
152 manifest.warnings_mut().add_critical_warning(error);
153 }
154
155 Ok(manifest)
156}
157
158fn make_document_owned(
161 mut document: toml::Spanned<toml::de::DeTable<'_>>,
162) -> toml::Spanned<toml::de::DeTable<'static>> {
163 document.get_mut().make_owned();
164 unsafe {
167 std::mem::transmute::<
168 toml::Spanned<toml::de::DeTable<'_>>,
169 toml::Spanned<toml::de::DeTable<'static>>,
170 >(document)
171 }
172}
173
174#[tracing::instrument(skip_all)]
175fn read_toml_string(path: &Path, is_embedded: bool, gctx: &GlobalContext) -> CargoResult<String> {
176 let mut contents = paths::read(path).map_err(|err| ManifestError::new(err, path.into()))?;
177 if is_embedded {
178 if !gctx.cli_unstable().script {
179 anyhow::bail!("parsing `{}` requires `-Zscript`", path.display());
180 }
181 contents = embedded::expand_manifest(&contents)
182 .map_err(|e| emit_frontmatter_diagnostic(e, &contents, path, gctx))?;
183 }
184 Ok(contents)
185}
186
187#[tracing::instrument(skip_all)]
188fn parse_document(contents: &str) -> Result<toml::Spanned<toml::de::DeTable<'_>>, toml::de::Error> {
189 toml::de::DeTable::parse(&contents)
190}
191
192#[tracing::instrument(skip_all)]
193fn deserialize_toml(
194 document: &toml::Spanned<toml::de::DeTable<'_>>,
195) -> Result<manifest::TomlManifest, toml::de::Error> {
196 let mut unused = BTreeSet::new();
197 let deserializer = toml::de::Deserializer::from(document.clone());
198 let mut document: manifest::TomlManifest = serde_ignored::deserialize(deserializer, |path| {
199 let mut key = String::new();
200 stringify(&mut key, &path);
201 unused.insert(key);
202 })?;
203 document._unused_keys = unused;
204 Ok(document)
205}
206
207fn stringify(dst: &mut String, path: &serde_ignored::Path<'_>) {
208 use serde_ignored::Path;
209
210 match *path {
211 Path::Root => {}
212 Path::Seq { parent, index } => {
213 stringify(dst, parent);
214 if !dst.is_empty() {
215 dst.push('.');
216 }
217 dst.push_str(&index.to_string());
218 }
219 Path::Map { parent, ref key } => {
220 stringify(dst, parent);
221 if !dst.is_empty() {
222 dst.push('.');
223 }
224 dst.push_str(key);
225 }
226 Path::Some { parent }
227 | Path::NewtypeVariant { parent }
228 | Path::NewtypeStruct { parent } => stringify(dst, parent),
229 }
230}
231
232fn to_workspace_config(
233 original_toml: &manifest::TomlManifest,
234 manifest_file: &Path,
235 is_embedded: bool,
236 gctx: &GlobalContext,
237 warnings: &mut Vec<String>,
238) -> CargoResult<WorkspaceConfig> {
239 if is_embedded {
240 let ws_root_config = to_workspace_root_config(&TomlWorkspace::default(), manifest_file);
241 return Ok(WorkspaceConfig::Root(ws_root_config));
242 }
243 let workspace_config = match (
244 original_toml.workspace.as_ref(),
245 original_toml.package().and_then(|p| p.workspace.as_ref()),
246 ) {
247 (Some(toml_config), None) => {
248 verify_lints(toml_config.lints.as_ref(), gctx, warnings)?;
249 if let Some(ws_deps) = &toml_config.dependencies {
250 for (name, dep) in ws_deps {
251 if dep.is_optional() {
252 bail!("{name} is optional, but workspace dependencies cannot be optional",);
253 }
254 if dep.is_public() {
255 bail!("{name} is public, but workspace dependencies cannot be public",);
256 }
257 }
258
259 for (name, dep) in ws_deps {
260 unused_dep_keys(name, "workspace.dependencies", dep.unused_keys(), warnings);
261 }
262 }
263 let ws_root_config = to_workspace_root_config(toml_config, manifest_file);
264 WorkspaceConfig::Root(ws_root_config)
265 }
266 (None, root) => WorkspaceConfig::Member {
267 root: root.cloned(),
268 },
269 (Some(..), Some(..)) => bail!(
270 "cannot configure both `package.workspace` and \
271 `[workspace]`, only one can be specified"
272 ),
273 };
274 Ok(workspace_config)
275}
276
277fn to_workspace_root_config(
278 normalized_toml: &manifest::TomlWorkspace,
279 manifest_file: &Path,
280) -> WorkspaceRootConfig {
281 let package_root = manifest_file.parent().unwrap();
282 let inheritable = InheritableFields {
283 package: normalized_toml.package.clone(),
284 dependencies: normalized_toml.dependencies.clone(),
285 lints: normalized_toml.lints.clone(),
286 _ws_root: package_root.to_owned(),
287 };
288 let ws_root_config = WorkspaceRootConfig::new(
289 package_root,
290 &normalized_toml.members,
291 &normalized_toml.default_members,
292 &normalized_toml.exclude,
293 &Some(inheritable),
294 &normalized_toml.metadata,
295 );
296 ws_root_config
297}
298
299#[tracing::instrument(skip_all)]
301fn normalize_toml(
302 original_toml: &manifest::TomlManifest,
303 features: &Features,
304 workspace_config: &WorkspaceConfig,
305 manifest_file: &Path,
306 is_embedded: bool,
307 gctx: &GlobalContext,
308 warnings: &mut Vec<String>,
309 errors: &mut Vec<String>,
310) -> CargoResult<manifest::TomlManifest> {
311 let package_root = manifest_file.parent().unwrap();
312
313 let inherit_cell: OnceCell<InheritableFields> = OnceCell::new();
314 let inherit = || {
315 inherit_cell
316 .try_borrow_with(|| load_inheritable_fields(gctx, manifest_file, &workspace_config))
317 };
318 let workspace_root = || inherit().map(|fields| fields.ws_root().as_path());
319
320 let mut normalized_toml = manifest::TomlManifest {
321 cargo_features: original_toml.cargo_features.clone(),
322 package: None,
323 project: None,
324 badges: None,
325 features: None,
326 lib: None,
327 bin: None,
328 example: None,
329 test: None,
330 bench: None,
331 dependencies: None,
332 dev_dependencies: None,
333 dev_dependencies2: None,
334 build_dependencies: None,
335 build_dependencies2: None,
336 target: None,
337 lints: None,
338 hints: None,
339 workspace: original_toml.workspace.clone().or_else(|| {
340 is_embedded.then(manifest::TomlWorkspace::default)
342 }),
343 profile: original_toml.profile.clone(),
344 patch: normalize_patch(
345 gctx,
346 original_toml.patch.as_ref(),
347 &workspace_root,
348 features,
349 )?,
350 replace: original_toml.replace.clone(),
351 _unused_keys: Default::default(),
352 };
353
354 if let Some(original_package) = original_toml.package().map(Cow::Borrowed).or_else(|| {
355 if is_embedded {
356 Some(Cow::Owned(Box::new(manifest::TomlPackage::default())))
357 } else {
358 None
359 }
360 }) {
361 let normalized_package = normalize_package_toml(
362 &original_package,
363 manifest_file,
364 is_embedded,
365 gctx,
366 &inherit,
367 features,
368 )?;
369 let package_name = &normalized_package
370 .normalized_name()
371 .expect("previously normalized")
372 .clone();
373 let edition = normalized_package
374 .normalized_edition()
375 .expect("previously normalized")
376 .map_or(Edition::default(), |e| {
377 Edition::from_str(&e).unwrap_or_default()
378 });
379 normalized_toml.package = Some(normalized_package);
380
381 normalized_toml.features = normalize_features(original_toml.features.as_ref())?;
382
383 let auto_embedded = is_embedded.then_some(false);
384 normalized_toml.lib = targets::normalize_lib(
385 original_toml.lib.as_ref(),
386 package_root,
387 package_name,
388 edition,
389 original_package.autolib.or(auto_embedded),
390 warnings,
391 )?;
392 let original_toml_bin = if is_embedded {
393 let name = package_name.as_ref().to_owned();
394 let manifest_file_name = manifest_file
395 .file_name()
396 .expect("file name enforced previously");
397 let path = PathBuf::from(manifest_file_name);
398 Cow::Owned(Some(vec![manifest::TomlBinTarget {
399 name: Some(name),
400 crate_type: None,
401 crate_type2: None,
402 path: Some(manifest::PathValue(path)),
403 filename: None,
404 test: None,
405 doctest: None,
406 bench: None,
407 doc: None,
408 doc_scrape_examples: None,
409 proc_macro: None,
410 proc_macro2: None,
411 harness: None,
412 required_features: None,
413 edition: None,
414 }]))
415 } else {
416 Cow::Borrowed(&original_toml.bin)
417 };
418 normalized_toml.bin = Some(targets::normalize_bins(
419 original_toml_bin.as_ref().as_ref(),
420 package_root,
421 package_name,
422 edition,
423 original_package.autobins.or(auto_embedded),
424 warnings,
425 errors,
426 normalized_toml.lib.is_some(),
427 )?);
428 normalized_toml.example = Some(targets::normalize_examples(
429 original_toml.example.as_ref(),
430 package_root,
431 edition,
432 original_package.autoexamples.or(auto_embedded),
433 warnings,
434 errors,
435 )?);
436 normalized_toml.test = Some(targets::normalize_tests(
437 original_toml.test.as_ref(),
438 package_root,
439 edition,
440 original_package.autotests.or(auto_embedded),
441 warnings,
442 errors,
443 )?);
444 normalized_toml.bench = Some(targets::normalize_benches(
445 original_toml.bench.as_ref(),
446 package_root,
447 edition,
448 original_package.autobenches.or(auto_embedded),
449 warnings,
450 errors,
451 )?);
452
453 normalized_toml.dependencies = normalize_dependencies(
454 gctx,
455 edition,
456 &features,
457 original_toml.dependencies.as_ref(),
458 DepKind::Normal,
459 &inherit,
460 &workspace_root,
461 package_root,
462 warnings,
463 )?;
464 deprecated_underscore(
465 &original_toml.dev_dependencies2,
466 &original_toml.dev_dependencies,
467 "dev-dependencies",
468 package_name,
469 "package",
470 edition,
471 warnings,
472 )?;
473 normalized_toml.dev_dependencies = normalize_dependencies(
474 gctx,
475 edition,
476 &features,
477 original_toml.dev_dependencies(),
478 DepKind::Development,
479 &inherit,
480 &workspace_root,
481 package_root,
482 warnings,
483 )?;
484 deprecated_underscore(
485 &original_toml.build_dependencies2,
486 &original_toml.build_dependencies,
487 "build-dependencies",
488 package_name,
489 "package",
490 edition,
491 warnings,
492 )?;
493 normalized_toml.build_dependencies = normalize_dependencies(
494 gctx,
495 edition,
496 &features,
497 original_toml.build_dependencies(),
498 DepKind::Build,
499 &inherit,
500 &workspace_root,
501 package_root,
502 warnings,
503 )?;
504 let mut normalized_target = BTreeMap::new();
505 for (name, platform) in original_toml.target.iter().flatten() {
506 let normalized_dependencies = normalize_dependencies(
507 gctx,
508 edition,
509 &features,
510 platform.dependencies.as_ref(),
511 DepKind::Normal,
512 &inherit,
513 &workspace_root,
514 package_root,
515 warnings,
516 )?;
517 deprecated_underscore(
518 &platform.dev_dependencies2,
519 &platform.dev_dependencies,
520 "dev-dependencies",
521 name,
522 "platform target",
523 edition,
524 warnings,
525 )?;
526 let normalized_dev_dependencies = normalize_dependencies(
527 gctx,
528 edition,
529 &features,
530 platform.dev_dependencies(),
531 DepKind::Development,
532 &inherit,
533 &workspace_root,
534 package_root,
535 warnings,
536 )?;
537 deprecated_underscore(
538 &platform.build_dependencies2,
539 &platform.build_dependencies,
540 "build-dependencies",
541 name,
542 "platform target",
543 edition,
544 warnings,
545 )?;
546 let normalized_build_dependencies = normalize_dependencies(
547 gctx,
548 edition,
549 &features,
550 platform.build_dependencies(),
551 DepKind::Build,
552 &inherit,
553 &workspace_root,
554 package_root,
555 warnings,
556 )?;
557 normalized_target.insert(
558 name.clone(),
559 manifest::TomlPlatform {
560 dependencies: normalized_dependencies,
561 build_dependencies: normalized_build_dependencies,
562 build_dependencies2: None,
563 dev_dependencies: normalized_dev_dependencies,
564 dev_dependencies2: None,
565 },
566 );
567 }
568 normalized_toml.target = (!normalized_target.is_empty()).then_some(normalized_target);
569
570 let normalized_lints = original_toml
571 .lints
572 .clone()
573 .map(|value| lints_inherit_with(value, || inherit()?.lints()))
574 .transpose()?;
575 normalized_toml.lints = normalized_lints.map(|lints| manifest::InheritableLints {
576 workspace: false,
577 lints,
578 });
579
580 normalized_toml.hints = original_toml.hints.clone();
581
582 normalized_toml.badges = original_toml.badges.clone();
583 } else {
584 if let Some(field) = original_toml.requires_package().next() {
585 bail!("this virtual manifest specifies a `{field}` section, which is not allowed");
586 }
587 }
588
589 Ok(normalized_toml)
590}
591
592fn normalize_patch<'a>(
593 gctx: &GlobalContext,
594 original_patch: Option<&BTreeMap<String, BTreeMap<PackageName, TomlDependency>>>,
595 workspace_root: &dyn Fn() -> CargoResult<&'a Path>,
596 features: &Features,
597) -> CargoResult<Option<BTreeMap<String, BTreeMap<PackageName, TomlDependency>>>> {
598 if let Some(patch) = original_patch {
599 let mut normalized_patch = BTreeMap::new();
600 for (name, packages) in patch {
601 let mut normalized_packages = BTreeMap::new();
602 for (pkg, dep) in packages {
603 let dep = if let TomlDependency::Detailed(dep) = dep {
604 let mut dep = dep.clone();
605 normalize_path_dependency(gctx, &mut dep, workspace_root, features)
606 .with_context(|| {
607 format!("resolving path for patch of ({pkg}) for source ({name})")
608 })?;
609 TomlDependency::Detailed(dep)
610 } else {
611 dep.clone()
612 };
613 normalized_packages.insert(pkg.clone(), dep);
614 }
615 normalized_patch.insert(name.clone(), normalized_packages);
616 }
617 Ok(Some(normalized_patch))
618 } else {
619 Ok(None)
620 }
621}
622
623#[tracing::instrument(skip_all)]
624fn normalize_package_toml<'a>(
625 original_package: &manifest::TomlPackage,
626 manifest_file: &Path,
627 is_embedded: bool,
628 gctx: &GlobalContext,
629 inherit: &dyn Fn() -> CargoResult<&'a InheritableFields>,
630 features: &Features,
631) -> CargoResult<Box<manifest::TomlPackage>> {
632 let package_root = manifest_file.parent().unwrap();
633
634 let edition = original_package
635 .edition
636 .clone()
637 .map(|value| field_inherit_with(value, "edition", || inherit()?.edition()))
638 .transpose()?
639 .map(manifest::InheritableField::Value)
640 .or_else(|| {
641 if is_embedded {
642 const DEFAULT_EDITION: crate::core::features::Edition =
643 crate::core::features::Edition::LATEST_STABLE;
644 let mut report = vec![Group::with_title(Level::WARNING.secondary_title(format!(
645 "`package.edition` is unspecified, defaulting to the latest edition (currently `{DEFAULT_EDITION}`)"
646 )))];
647 if !matches!(gctx.shell().verbosity(), cargo_util_terminal::Verbosity::Quiet) {
648 report.push(Group::with_title(Level::HELP.secondary_title(format!(
649 "to pin the edition, run `cargo fix --manifest-path {}`", manifest_file.display()
650 ))));
651 }
652 let _ = gctx.shell().print_report(&report, true);
653 Some(manifest::InheritableField::Value(
654 DEFAULT_EDITION.to_string(),
655 ))
656 } else {
657 None
658 }
659 });
660 let rust_version = original_package
661 .rust_version
662 .clone()
663 .map(|value| field_inherit_with(value, "rust-version", || inherit()?.rust_version()))
664 .transpose()?
665 .map(manifest::InheritableField::Value);
666 let name = Some(
667 original_package
668 .name
669 .clone()
670 .or_else(|| {
671 if is_embedded {
672 let file_stem = manifest_file
673 .file_stem()
674 .expect("file name enforced previously")
675 .to_string_lossy();
676 let name = embedded::sanitize_name(file_stem.as_ref());
677 let name =
678 manifest::PackageName::new(name).expect("sanitize made the name valid");
679 Some(name)
680 } else {
681 None
682 }
683 })
684 .ok_or_else(|| anyhow::format_err!("missing field `package.name`"))?,
685 );
686 let version = original_package
687 .version
688 .clone()
689 .map(|value| field_inherit_with(value, "version", || inherit()?.version()))
690 .transpose()?
691 .map(manifest::InheritableField::Value);
692 let authors = original_package
693 .authors
694 .clone()
695 .map(|value| field_inherit_with(value, "authors", || inherit()?.authors()))
696 .transpose()?
697 .map(manifest::InheritableField::Value);
698 let build = if is_embedded {
699 Some(TomlPackageBuild::Auto(false))
700 } else {
701 if let Some(TomlPackageBuild::MultipleScript(_)) = original_package.build {
702 features.require(Feature::multiple_build_scripts())?;
703 }
704 targets::normalize_build(original_package.build.as_ref(), package_root)?
705 };
706 let metabuild = original_package.metabuild.clone();
707 let default_target = original_package.default_target.clone();
708 let forced_target = original_package.forced_target.clone();
709 let links = original_package.links.clone();
710 let exclude = original_package
711 .exclude
712 .clone()
713 .map(|value| field_inherit_with(value, "exclude", || inherit()?.exclude()))
714 .transpose()?
715 .map(manifest::InheritableField::Value);
716 let include = original_package
717 .include
718 .clone()
719 .map(|value| field_inherit_with(value, "include", || inherit()?.include()))
720 .transpose()?
721 .map(manifest::InheritableField::Value);
722 let publish = original_package
723 .publish
724 .clone()
725 .map(|value| field_inherit_with(value, "publish", || inherit()?.publish()))
726 .transpose()?
727 .map(manifest::InheritableField::Value);
728 let workspace = original_package.workspace.clone();
729 let im_a_teapot = original_package.im_a_teapot.clone();
730 let autolib = Some(false);
731 let autobins = Some(false);
732 let autoexamples = Some(false);
733 let autotests = Some(false);
734 let autobenches = Some(false);
735 let default_run = original_package.default_run.clone();
736 let description = original_package
737 .description
738 .clone()
739 .map(|value| field_inherit_with(value, "description", || inherit()?.description()))
740 .transpose()?
741 .map(manifest::InheritableField::Value);
742 let homepage = original_package
743 .homepage
744 .clone()
745 .map(|value| field_inherit_with(value, "homepage", || inherit()?.homepage()))
746 .transpose()?
747 .map(manifest::InheritableField::Value);
748 let documentation = original_package
749 .documentation
750 .clone()
751 .map(|value| field_inherit_with(value, "documentation", || inherit()?.documentation()))
752 .transpose()?
753 .map(manifest::InheritableField::Value);
754 let readme = normalize_package_readme(
755 package_root,
756 original_package
757 .readme
758 .clone()
759 .map(|value| field_inherit_with(value, "readme", || inherit()?.readme(package_root)))
760 .transpose()?
761 .as_ref(),
762 )
763 .map(|s| manifest::InheritableField::Value(StringOrBool::String(s)))
764 .or(Some(manifest::InheritableField::Value(StringOrBool::Bool(
765 false,
766 ))));
767 let keywords = original_package
768 .keywords
769 .clone()
770 .map(|value| field_inherit_with(value, "keywords", || inherit()?.keywords()))
771 .transpose()?
772 .map(manifest::InheritableField::Value);
773 let categories = original_package
774 .categories
775 .clone()
776 .map(|value| field_inherit_with(value, "categories", || inherit()?.categories()))
777 .transpose()?
778 .map(manifest::InheritableField::Value);
779 let license = original_package
780 .license
781 .clone()
782 .map(|value| field_inherit_with(value, "license", || inherit()?.license()))
783 .transpose()?
784 .map(manifest::InheritableField::Value);
785 let license_file = original_package
786 .license_file
787 .clone()
788 .map(|value| {
789 field_inherit_with(value, "license-file", || {
790 inherit()?.license_file(package_root)
791 })
792 })
793 .transpose()?
794 .map(manifest::InheritableField::Value);
795 let repository = original_package
796 .repository
797 .clone()
798 .map(|value| field_inherit_with(value, "repository", || inherit()?.repository()))
799 .transpose()?
800 .map(manifest::InheritableField::Value);
801 let resolver = original_package.resolver.clone();
802 let metadata = original_package.metadata.clone();
803
804 let normalized_package = manifest::TomlPackage {
805 edition,
806 rust_version,
807 name,
808 version,
809 authors,
810 build,
811 metabuild,
812 default_target,
813 forced_target,
814 links,
815 exclude,
816 include,
817 publish,
818 workspace,
819 im_a_teapot,
820 autolib,
821 autobins,
822 autoexamples,
823 autotests,
824 autobenches,
825 default_run,
826 description,
827 homepage,
828 documentation,
829 readme,
830 keywords,
831 categories,
832 license,
833 license_file,
834 repository,
835 resolver,
836 metadata,
837 _invalid_cargo_features: Default::default(),
838 };
839
840 Ok(Box::new(normalized_package))
841}
842
843fn normalize_package_readme(
845 package_root: &Path,
846 readme: Option<&manifest::StringOrBool>,
847) -> Option<String> {
848 match &readme {
849 None => default_readme_from_package_root(package_root),
850 Some(value) => match value {
851 manifest::StringOrBool::Bool(false) => None,
852 manifest::StringOrBool::Bool(true) => Some("README.md".to_string()),
853 manifest::StringOrBool::String(v) => Some(v.clone()),
854 },
855 }
856}
857
858pub const DEFAULT_README_FILES: [&str; 3] = ["README.md", "README.txt", "README"];
859
860fn default_readme_from_package_root(package_root: &Path) -> Option<String> {
863 for &readme_filename in DEFAULT_README_FILES.iter() {
864 if package_root.join(readme_filename).is_file() {
865 return Some(readme_filename.to_string());
866 }
867 }
868
869 None
870}
871
872#[tracing::instrument(skip_all)]
873fn normalize_features(
874 original_features: Option<&BTreeMap<manifest::FeatureName, Vec<String>>>,
875) -> CargoResult<Option<BTreeMap<manifest::FeatureName, Vec<String>>>> {
876 let Some(normalized_features) = original_features.cloned() else {
877 return Ok(None);
878 };
879
880 Ok(Some(normalized_features))
881}
882
883#[tracing::instrument(skip_all)]
884fn normalize_dependencies<'a>(
885 gctx: &GlobalContext,
886 edition: Edition,
887 features: &Features,
888 orig_deps: Option<&BTreeMap<manifest::PackageName, manifest::InheritableDependency>>,
889 kind: DepKind,
890 inherit: &dyn Fn() -> CargoResult<&'a InheritableFields>,
891 workspace_root: &dyn Fn() -> CargoResult<&'a Path>,
892 package_root: &Path,
893 warnings: &mut Vec<String>,
894) -> CargoResult<Option<BTreeMap<manifest::PackageName, manifest::InheritableDependency>>> {
895 let Some(dependencies) = orig_deps else {
896 return Ok(None);
897 };
898
899 let mut deps = BTreeMap::new();
900 for (name_in_toml, v) in dependencies.iter() {
901 let mut resolved = dependency_inherit_with(
902 v.clone(),
903 name_in_toml,
904 inherit,
905 package_root,
906 edition,
907 warnings,
908 )?;
909 if let manifest::TomlDependency::Detailed(ref mut d) = resolved {
910 deprecated_underscore(
911 &d.default_features2,
912 &d.default_features,
913 "default-features",
914 name_in_toml,
915 "dependency",
916 edition,
917 warnings,
918 )?;
919 if d.public.is_some() {
920 let with_public_feature = features.require(Feature::public_dependency()).is_ok();
921 let with_z_public = gctx.cli_unstable().public_dependency;
922 match kind {
923 DepKind::Normal => {
924 if !with_public_feature && !with_z_public {
925 d.public = None;
926 warnings.push(format!(
927 "ignoring `public` on dependency {name_in_toml}, pass `-Zpublic-dependency` to enable support for it"
928 ));
929 }
930 }
931 DepKind::Development | DepKind::Build => {
932 let kind_name = kind.kind_table();
933 let hint = format!(
934 "'public' specifier can only be used on regular dependencies, not {kind_name}",
935 );
936 if with_public_feature || with_z_public {
937 bail!(hint)
938 } else {
939 warnings.push(hint);
941 d.public = None;
942 }
943 }
944 }
945 }
946 normalize_path_dependency(gctx, d, workspace_root, features)
947 .with_context(|| format!("resolving path dependency {name_in_toml}"))?;
948 }
949
950 deps.insert(
951 name_in_toml.clone(),
952 manifest::InheritableDependency::Value(resolved.clone()),
953 );
954 }
955 Ok(Some(deps))
956}
957
958fn normalize_path_dependency<'a>(
959 gctx: &GlobalContext,
960 detailed_dep: &mut TomlDetailedDependency,
961 workspace_root: &dyn Fn() -> CargoResult<&'a Path>,
962 features: &Features,
963) -> CargoResult<()> {
964 if let Some(base) = detailed_dep.base.take() {
965 if let Some(path) = detailed_dep.path.as_mut() {
966 let new_path = lookup_path_base(&base, gctx, workspace_root, features)?.join(&path);
967 *path = new_path.to_str().unwrap().to_string();
968 } else {
969 bail!("`base` can only be used with path dependencies");
970 }
971 }
972 Ok(())
973}
974
975fn load_inheritable_fields(
976 gctx: &GlobalContext,
977 normalized_path: &Path,
978 workspace_config: &WorkspaceConfig,
979) -> CargoResult<InheritableFields> {
980 match workspace_config {
981 WorkspaceConfig::Root(root) => Ok(root.inheritable().clone()),
982 WorkspaceConfig::Member {
983 root: Some(path_to_root),
984 } => {
985 let path = normalized_path
986 .parent()
987 .unwrap()
988 .join(path_to_root)
989 .join("Cargo.toml");
990 let root_path = paths::normalize_path(&path);
991 inheritable_from_path(gctx, root_path)
992 }
993 WorkspaceConfig::Member { root: None } => {
994 match find_workspace_root(&normalized_path, gctx)? {
995 Some(path_to_root) => inheritable_from_path(gctx, path_to_root),
996 None => Err(anyhow!("failed to find a workspace root")),
997 }
998 }
999 }
1000}
1001
1002fn inheritable_from_path(
1003 gctx: &GlobalContext,
1004 workspace_path: PathBuf,
1005) -> CargoResult<InheritableFields> {
1006 let workspace_path_root = workspace_path.parent().unwrap();
1008
1009 if let Some(ws_root) = gctx.ws_roots().get(workspace_path_root) {
1012 return Ok(ws_root.inheritable().clone());
1013 };
1014
1015 let source_id = SourceId::for_manifest_path(&workspace_path)?;
1016 let man = read_manifest(&workspace_path, source_id, gctx)?;
1017 match man.workspace_config() {
1018 WorkspaceConfig::Root(root) => {
1019 gctx.ws_roots().insert(workspace_path, root.clone());
1020 Ok(root.inheritable().clone())
1021 }
1022 _ => bail!(
1023 "root of a workspace inferred but wasn't a root: {}",
1024 workspace_path.display()
1025 ),
1026 }
1027}
1028
1029macro_rules! package_field_getter {
1031 ( $(($key:literal, $field:ident -> $ret:ty),)* ) => (
1032 $(
1033 #[doc = concat!("Gets the field `workspace.package.", $key, "`.")]
1034 fn $field(&self) -> CargoResult<$ret> {
1035 let Some(val) = self.package.as_ref().and_then(|p| p.$field.as_ref()) else {
1036 bail!("`workspace.package.{}` was not defined", $key);
1037 };
1038 Ok(val.clone())
1039 }
1040 )*
1041 )
1042}
1043
1044#[derive(Clone, Debug, Default)]
1046pub struct InheritableFields {
1047 package: Option<manifest::InheritablePackage>,
1048 dependencies: Option<BTreeMap<manifest::PackageName, manifest::TomlDependency>>,
1049 lints: Option<manifest::TomlLints>,
1050
1051 _ws_root: PathBuf,
1053}
1054
1055impl InheritableFields {
1056 package_field_getter! {
1057 ("authors", authors -> Vec<String>),
1059 ("categories", categories -> Vec<String>),
1060 ("description", description -> String),
1061 ("documentation", documentation -> String),
1062 ("edition", edition -> String),
1063 ("exclude", exclude -> Vec<String>),
1064 ("homepage", homepage -> String),
1065 ("include", include -> Vec<String>),
1066 ("keywords", keywords -> Vec<String>),
1067 ("license", license -> String),
1068 ("publish", publish -> manifest::VecStringOrBool),
1069 ("repository", repository -> String),
1070 ("rust-version", rust_version -> RustVersion),
1071 ("version", version -> semver::Version),
1072 }
1073
1074 fn get_dependency(
1076 &self,
1077 name: &str,
1078 package_root: &Path,
1079 ) -> CargoResult<manifest::TomlDependency> {
1080 let Some(deps) = &self.dependencies else {
1081 bail!("`workspace.dependencies` was not defined");
1082 };
1083 let Some(dep) = deps.get(name) else {
1084 bail!("`dependency.{name}` was not found in `workspace.dependencies`");
1085 };
1086 let mut dep = dep.clone();
1087 if let manifest::TomlDependency::Detailed(detailed) = &mut dep {
1088 if detailed.base.is_none() {
1089 if let Some(rel_path) = &detailed.path {
1092 detailed.path = Some(resolve_relative_path(
1093 name,
1094 self.ws_root(),
1095 package_root,
1096 rel_path,
1097 )?);
1098 }
1099 }
1100 }
1101 Ok(dep)
1102 }
1103
1104 pub fn lints(&self) -> CargoResult<manifest::TomlLints> {
1106 let Some(val) = &self.lints else {
1107 bail!("`workspace.lints` was not defined");
1108 };
1109 Ok(val.clone())
1110 }
1111
1112 fn license_file(&self, package_root: &Path) -> CargoResult<String> {
1114 let Some(license_file) = self.package.as_ref().and_then(|p| p.license_file.as_ref()) else {
1115 bail!("`workspace.package.license-file` was not defined");
1116 };
1117 resolve_relative_path("license-file", &self._ws_root, package_root, license_file)
1118 }
1119
1120 fn readme(&self, package_root: &Path) -> CargoResult<manifest::StringOrBool> {
1122 let Some(readme) = normalize_package_readme(
1123 self._ws_root.as_path(),
1124 self.package.as_ref().and_then(|p| p.readme.as_ref()),
1125 ) else {
1126 bail!("`workspace.package.readme` was not defined");
1127 };
1128 resolve_relative_path("readme", &self._ws_root, package_root, &readme)
1129 .map(manifest::StringOrBool::String)
1130 }
1131
1132 fn ws_root(&self) -> &PathBuf {
1133 &self._ws_root
1134 }
1135}
1136
1137fn field_inherit_with<'a, T>(
1138 field: manifest::InheritableField<T>,
1139 label: &str,
1140 get_ws_inheritable: impl FnOnce() -> CargoResult<T>,
1141) -> CargoResult<T> {
1142 match field {
1143 manifest::InheritableField::Value(value) => Ok(value),
1144 manifest::InheritableField::Inherit(_) => get_ws_inheritable().with_context(|| {
1145 format!(
1146 "error inheriting `{label}` from workspace root manifest's `workspace.package.{label}`",
1147 )
1148 }),
1149 }
1150}
1151
1152fn lints_inherit_with(
1153 lints: manifest::InheritableLints,
1154 get_ws_inheritable: impl FnOnce() -> CargoResult<manifest::TomlLints>,
1155) -> CargoResult<manifest::TomlLints> {
1156 if lints.workspace {
1157 if !lints.lints.is_empty() {
1158 anyhow::bail!(
1159 "cannot override `workspace.lints` in `lints`, either remove the overrides or `lints.workspace = true` and manually specify the lints"
1160 );
1161 }
1162 get_ws_inheritable().with_context(
1163 || "error inheriting `lints` from workspace root manifest's `workspace.lints`",
1164 )
1165 } else {
1166 Ok(lints.lints)
1167 }
1168}
1169
1170fn dependency_inherit_with<'a>(
1171 dependency: manifest::InheritableDependency,
1172 name: &str,
1173 inherit: &dyn Fn() -> CargoResult<&'a InheritableFields>,
1174 package_root: &Path,
1175 edition: Edition,
1176 warnings: &mut Vec<String>,
1177) -> CargoResult<manifest::TomlDependency> {
1178 match dependency {
1179 manifest::InheritableDependency::Value(value) => Ok(value),
1180 manifest::InheritableDependency::Inherit(w) => {
1181 inner_dependency_inherit_with(w, name, inherit, package_root, edition, warnings).with_context(|| {
1182 format!(
1183 "error inheriting `{name}` from workspace root manifest's `workspace.dependencies.{name}`",
1184 )
1185 })
1186 }
1187 }
1188}
1189
1190fn inner_dependency_inherit_with<'a>(
1191 pkg_dep: manifest::TomlInheritedDependency,
1192 name: &str,
1193 inherit: &dyn Fn() -> CargoResult<&'a InheritableFields>,
1194 package_root: &Path,
1195 edition: Edition,
1196 warnings: &mut Vec<String>,
1197) -> CargoResult<manifest::TomlDependency> {
1198 let ws_dep = inherit()?.get_dependency(name, package_root)?;
1199 let mut merged_dep = match ws_dep {
1200 manifest::TomlDependency::Simple(ws_version) => manifest::TomlDetailedDependency {
1201 version: Some(ws_version),
1202 ..Default::default()
1203 },
1204 manifest::TomlDependency::Detailed(ws_dep) => ws_dep.clone(),
1205 };
1206 let manifest::TomlInheritedDependency {
1207 workspace: _,
1208
1209 features,
1210 optional,
1211 default_features,
1212 default_features2,
1213 public,
1214
1215 _unused_keys: _,
1216 } = &pkg_dep;
1217 let default_features = default_features.or(*default_features2);
1218
1219 match (default_features, merged_dep.default_features()) {
1220 (Some(true), Some(false)) => {
1224 merged_dep.default_features = Some(true);
1225 }
1226 (Some(false), Some(true)) => {
1230 deprecated_ws_default_features(name, Some(true), edition, warnings)?;
1231 }
1232 (Some(false), None) => {
1235 deprecated_ws_default_features(name, None, edition, warnings)?;
1236 }
1237 _ => {}
1238 }
1239 merged_dep.features = match (merged_dep.features.clone(), features.clone()) {
1240 (Some(dep_feat), Some(inherit_feat)) => Some(
1241 dep_feat
1242 .into_iter()
1243 .chain(inherit_feat)
1244 .collect::<Vec<String>>(),
1245 ),
1246 (Some(dep_fet), None) => Some(dep_fet),
1247 (None, Some(inherit_feat)) => Some(inherit_feat),
1248 (None, None) => None,
1249 };
1250 merged_dep.optional = *optional;
1251 merged_dep.public = *public;
1252 Ok(manifest::TomlDependency::Detailed(merged_dep))
1253}
1254
1255fn deprecated_ws_default_features(
1256 label: &str,
1257 ws_def_feat: Option<bool>,
1258 edition: Edition,
1259 warnings: &mut Vec<String>,
1260) -> CargoResult<()> {
1261 let ws_def_feat = match ws_def_feat {
1262 Some(true) => "true",
1263 Some(false) => "false",
1264 None => "not specified",
1265 };
1266 if Edition::Edition2024 <= edition {
1267 anyhow::bail!("`default-features = false` cannot override workspace's `default-features`");
1268 } else {
1269 warnings.push(format!(
1270 "`default-features` is ignored for {label}, since `default-features` was \
1271 {ws_def_feat} for `workspace.dependencies.{label}`, \
1272 this could become a hard error in the future"
1273 ));
1274 }
1275 Ok(())
1276}
1277
1278#[tracing::instrument(skip_all)]
1279pub fn to_real_manifest(
1280 contents: Option<String>,
1281 document: Option<toml::Spanned<toml::de::DeTable<'static>>>,
1282 original_toml: manifest::TomlManifest,
1283 normalized_toml: manifest::TomlManifest,
1284 features: Features,
1285 workspace_config: WorkspaceConfig,
1286 source_id: SourceId,
1287 manifest_file: &Path,
1288 is_embedded: bool,
1289 gctx: &GlobalContext,
1290 warnings: &mut Vec<String>,
1291 _errors: &mut Vec<String>,
1292) -> CargoResult<Manifest> {
1293 let package_root = manifest_file.parent().unwrap();
1294 if !package_root.is_dir() {
1295 bail!(
1296 "package root '{}' is not a directory",
1297 package_root.display()
1298 );
1299 };
1300
1301 let normalized_package = normalized_toml
1302 .package()
1303 .expect("previously verified to have a `[package]`");
1304 let package_name = normalized_package
1305 .normalized_name()
1306 .expect("previously normalized");
1307 if package_name.contains(':') {
1308 features.require(Feature::open_namespaces())?;
1309 }
1310 let rust_version = normalized_package
1311 .normalized_rust_version()
1312 .expect("previously normalized")
1313 .cloned();
1314
1315 let edition = if let Some(edition) = normalized_package
1316 .normalized_edition()
1317 .expect("previously normalized")
1318 {
1319 let edition: Edition = edition
1320 .parse()
1321 .context("failed to parse the `edition` key")?;
1322 if let Some(pkg_msrv) = &rust_version {
1323 if let Some(edition_msrv) = edition.first_version() {
1324 let edition_msrv = RustVersion::try_from(edition_msrv).unwrap();
1325 if !edition_msrv.is_compatible_with(&pkg_msrv.to_partial()) {
1326 bail!(
1327 "rust-version {} is incompatible with the version ({}) required by \
1328 the specified edition ({})",
1329 pkg_msrv,
1330 edition_msrv,
1331 edition,
1332 )
1333 }
1334 }
1335 }
1336 edition
1337 } else {
1338 let msrv_edition = if let Some(pkg_msrv) = &rust_version {
1339 Edition::ALL
1340 .iter()
1341 .filter(|e| {
1342 e.first_version()
1343 .map(|e| {
1344 let e = RustVersion::try_from(e).unwrap();
1345 e.is_compatible_with(&pkg_msrv.to_partial())
1346 })
1347 .unwrap_or_default()
1348 })
1349 .max()
1350 .copied()
1351 } else {
1352 None
1353 }
1354 .unwrap_or_default();
1355 let default_edition = Edition::default();
1356 let latest_edition = Edition::LATEST_STABLE;
1357
1358 if msrv_edition != default_edition || rust_version.is_none() {
1363 let tip = if msrv_edition == latest_edition || rust_version.is_none() {
1364 format!(" while the latest is `{latest_edition}`")
1365 } else {
1366 format!(" while {msrv_edition} is compatible with `rust-version`")
1367 };
1368 warnings.push(format!(
1369 "`package.edition` is unspecified, defaulting to `{default_edition}`{tip}"
1370 ));
1371 }
1372 default_edition
1373 };
1374 if !edition.is_stable() {
1375 let version = normalized_package
1376 .normalized_version()
1377 .expect("previously normalized")
1378 .map(|v| format!("@{v}"))
1379 .unwrap_or_default();
1380 let hint = rust_version
1381 .as_ref()
1382 .map(|rv| format!("help: {package_name}{version} requires rust {rv}"));
1383 features.require_with_hint(Feature::unstable_editions(), hint.as_deref())?;
1384 }
1385
1386 if original_toml.project.is_some() {
1387 if Edition::Edition2024 <= edition {
1388 anyhow::bail!(
1389 "`[project]` is not supported as of the 2024 Edition, please use `[package]`"
1390 );
1391 } else {
1392 warnings.push(format!("`[project]` is deprecated in favor of `[package]`"));
1393 }
1394 }
1395
1396 if normalized_package.metabuild.is_some() {
1397 features.require(Feature::metabuild())?;
1398 }
1399
1400 if is_embedded {
1401 let manifest::TomlManifest {
1402 cargo_features: _,
1403 package: _,
1404 project: _,
1405 badges: _,
1406 features: _,
1407 lib,
1408 bin,
1409 example,
1410 test,
1411 bench,
1412 dependencies: _,
1413 dev_dependencies: _,
1414 dev_dependencies2: _,
1415 build_dependencies,
1416 build_dependencies2,
1417 target: _,
1418 lints: _,
1419 hints: _,
1420 workspace,
1421 profile: _,
1422 patch: _,
1423 replace: _,
1424 _unused_keys: _,
1425 } = &original_toml;
1426 let mut invalid_fields = vec![
1427 ("`workspace`", workspace.is_some()),
1428 ("`lib`", lib.is_some()),
1429 ("`bin`", bin.is_some()),
1430 ("`example`", example.is_some()),
1431 ("`test`", test.is_some()),
1432 ("`bench`", bench.is_some()),
1433 ("`build-dependencies`", build_dependencies.is_some()),
1434 ("`build_dependencies`", build_dependencies2.is_some()),
1435 ];
1436 if let Some(package) = original_toml.package() {
1437 let manifest::TomlPackage {
1438 edition: _,
1439 rust_version: _,
1440 name: _,
1441 version: _,
1442 authors: _,
1443 build,
1444 metabuild,
1445 default_target: _,
1446 forced_target: _,
1447 links,
1448 exclude: _,
1449 include: _,
1450 publish: _,
1451 workspace,
1452 im_a_teapot: _,
1453 autolib,
1454 autobins,
1455 autoexamples,
1456 autotests,
1457 autobenches,
1458 default_run,
1459 description: _,
1460 homepage: _,
1461 documentation: _,
1462 readme: _,
1463 keywords: _,
1464 categories: _,
1465 license: _,
1466 license_file: _,
1467 repository: _,
1468 resolver: _,
1469 metadata: _,
1470 _invalid_cargo_features: _,
1471 } = package.as_ref();
1472 invalid_fields.extend([
1473 ("`package.workspace`", workspace.is_some()),
1474 ("`package.build`", build.is_some()),
1475 ("`package.metabuild`", metabuild.is_some()),
1476 ("`package.links`", links.is_some()),
1477 ("`package.autolib`", autolib.is_some()),
1478 ("`package.autobins`", autobins.is_some()),
1479 ("`package.autoexamples`", autoexamples.is_some()),
1480 ("`package.autotests`", autotests.is_some()),
1481 ("`package.autobenches`", autobenches.is_some()),
1482 ("`package.default-run`", default_run.is_some()),
1483 ]);
1484 }
1485 let invalid_fields = invalid_fields
1486 .into_iter()
1487 .filter_map(|(name, invalid)| invalid.then_some(name))
1488 .collect::<Vec<_>>();
1489 if !invalid_fields.is_empty() {
1490 let fields = invalid_fields.join(", ");
1491 let are = if invalid_fields.len() == 1 {
1492 "is"
1493 } else {
1494 "are"
1495 };
1496 anyhow::bail!("{fields} {are} not allowed in embedded manifests")
1497 }
1498 }
1499
1500 let resolve_behavior = match (
1501 normalized_package.resolver.as_ref(),
1502 normalized_toml
1503 .workspace
1504 .as_ref()
1505 .and_then(|ws| ws.resolver.as_ref()),
1506 ) {
1507 (None, None) => None,
1508 (Some(s), None) | (None, Some(s)) => Some(ResolveBehavior::from_manifest(s)?),
1509 (Some(_), Some(_)) => {
1510 bail!("cannot specify `resolver` field in both `[workspace]` and `[package]`")
1511 }
1512 };
1513
1514 let targets = to_targets(
1518 &features,
1519 &original_toml,
1520 &normalized_toml,
1521 package_root,
1522 edition,
1523 &normalized_package.metabuild,
1524 warnings,
1525 )?;
1526
1527 if targets.iter().all(|t| t.is_custom_build()) {
1528 bail!(
1529 "no targets specified in the manifest\n\
1530 either src/lib.rs, src/main.rs, a [lib] section, or \
1531 [[bin]] section must be present"
1532 )
1533 }
1534
1535 if let Err(conflict_targets) = unique_build_targets(&targets, package_root) {
1536 conflict_targets
1537 .iter()
1538 .for_each(|(target_path, conflicts)| {
1539 warnings.push(format!(
1540 "file `{}` found to be present in multiple \
1541 build targets:\n{}",
1542 target_path.display(),
1543 conflicts
1544 .iter()
1545 .map(|t| format!(" * `{}` target `{}`", t.kind().description(), t.name(),))
1546 .join("\n")
1547 ));
1548 })
1549 }
1550
1551 if let Some(links) = &normalized_package.links {
1552 if !targets.iter().any(|t| t.is_custom_build()) {
1553 bail!(
1554 "package specifies that it links to `{links}` but does not have a custom build script"
1555 )
1556 }
1557 }
1558
1559 validate_dependencies(original_toml.dependencies.as_ref(), None, None, warnings)?;
1560 validate_dependencies(
1561 original_toml.dev_dependencies(),
1562 None,
1563 Some(DepKind::Development),
1564 warnings,
1565 )?;
1566 validate_dependencies(
1567 original_toml.build_dependencies(),
1568 None,
1569 Some(DepKind::Build),
1570 warnings,
1571 )?;
1572 for (name, platform) in original_toml.target.iter().flatten() {
1573 let platform_kind: Platform = name.parse()?;
1574 platform_kind.check_cfg_attributes(warnings);
1575 platform_kind.check_cfg_keywords(warnings, manifest_file);
1576 let platform_kind = Some(platform_kind);
1577 validate_dependencies(
1578 platform.dependencies.as_ref(),
1579 platform_kind.as_ref(),
1580 None,
1581 warnings,
1582 )?;
1583 validate_dependencies(
1584 platform.build_dependencies(),
1585 platform_kind.as_ref(),
1586 Some(DepKind::Build),
1587 warnings,
1588 )?;
1589 validate_dependencies(
1590 platform.dev_dependencies(),
1591 platform_kind.as_ref(),
1592 Some(DepKind::Development),
1593 warnings,
1594 )?;
1595 }
1596
1597 let mut deps = Vec::new();
1599 let mut manifest_ctx = ManifestContext {
1600 deps: &mut deps,
1601 source_id,
1602 gctx,
1603 warnings,
1604 platform: None,
1605 file: manifest_file,
1606 };
1607 gather_dependencies(
1608 &mut manifest_ctx,
1609 normalized_toml.dependencies.as_ref(),
1610 None,
1611 )?;
1612 gather_dependencies(
1613 &mut manifest_ctx,
1614 normalized_toml.dev_dependencies(),
1615 Some(DepKind::Development),
1616 )?;
1617 gather_dependencies(
1618 &mut manifest_ctx,
1619 normalized_toml.build_dependencies(),
1620 Some(DepKind::Build),
1621 )?;
1622 for (name, platform) in normalized_toml.target.iter().flatten() {
1623 manifest_ctx.platform = Some(name.parse()?);
1624 gather_dependencies(&mut manifest_ctx, platform.dependencies.as_ref(), None)?;
1625 gather_dependencies(
1626 &mut manifest_ctx,
1627 platform.build_dependencies(),
1628 Some(DepKind::Build),
1629 )?;
1630 gather_dependencies(
1631 &mut manifest_ctx,
1632 platform.dev_dependencies(),
1633 Some(DepKind::Development),
1634 )?;
1635 }
1636 let replace = replace(&normalized_toml, &mut manifest_ctx)?;
1637 let patch = patch(&normalized_toml, &mut manifest_ctx)?;
1638
1639 {
1640 let mut names_sources = BTreeMap::new();
1641 for dep in &deps {
1642 let name = dep.name_in_toml();
1643 let prev = names_sources.insert(name, dep.source_id());
1644 if prev.is_some() && prev != Some(dep.source_id()) {
1645 bail!(
1646 "Dependency '{}' has different source paths depending on the build \
1647 target. Each dependency must have a single canonical source path \
1648 irrespective of build target.",
1649 name
1650 );
1651 }
1652 }
1653 }
1654
1655 verify_lints(
1656 normalized_toml
1657 .normalized_lints()
1658 .expect("previously normalized"),
1659 gctx,
1660 warnings,
1661 )?;
1662 let default = manifest::TomlLints::default();
1663 let rustflags = lints_to_rustflags(
1664 normalized_toml
1665 .normalized_lints()
1666 .expect("previously normalized")
1667 .unwrap_or(&default),
1668 )?;
1669
1670 let hints = normalized_toml.hints.clone();
1671
1672 let metadata = ManifestMetadata {
1673 description: normalized_package
1674 .normalized_description()
1675 .expect("previously normalized")
1676 .cloned(),
1677 homepage: normalized_package
1678 .normalized_homepage()
1679 .expect("previously normalized")
1680 .cloned(),
1681 documentation: normalized_package
1682 .normalized_documentation()
1683 .expect("previously normalized")
1684 .cloned(),
1685 readme: normalized_package
1686 .normalized_readme()
1687 .expect("previously normalized")
1688 .cloned(),
1689 authors: normalized_package
1690 .normalized_authors()
1691 .expect("previously normalized")
1692 .cloned()
1693 .unwrap_or_default(),
1694 license: normalized_package
1695 .normalized_license()
1696 .expect("previously normalized")
1697 .cloned(),
1698 license_file: normalized_package
1699 .normalized_license_file()
1700 .expect("previously normalized")
1701 .cloned(),
1702 repository: normalized_package
1703 .normalized_repository()
1704 .expect("previously normalized")
1705 .cloned(),
1706 keywords: normalized_package
1707 .normalized_keywords()
1708 .expect("previously normalized")
1709 .cloned()
1710 .unwrap_or_default(),
1711 categories: normalized_package
1712 .normalized_categories()
1713 .expect("previously normalized")
1714 .cloned()
1715 .unwrap_or_default(),
1716 badges: normalized_toml.badges.clone().unwrap_or_default(),
1717 links: normalized_package.links.clone(),
1718 rust_version: rust_version.clone(),
1719 };
1720
1721 if let Some(profiles) = &normalized_toml.profile {
1722 let cli_unstable = gctx.cli_unstable();
1723 validate_profiles(profiles, cli_unstable, &features, warnings)?;
1724 }
1725
1726 let version = normalized_package
1727 .normalized_version()
1728 .expect("previously normalized");
1729 let publish = match normalized_package
1730 .normalized_publish()
1731 .expect("previously normalized")
1732 {
1733 Some(manifest::VecStringOrBool::VecString(vecstring)) => Some(vecstring.clone()),
1734 Some(manifest::VecStringOrBool::Bool(false)) => Some(vec![]),
1735 Some(manifest::VecStringOrBool::Bool(true)) => None,
1736 None => version.is_none().then_some(vec![]),
1737 };
1738
1739 if version.is_none() && publish != Some(vec![]) {
1740 bail!("`package.publish` requires `package.version` be specified");
1741 }
1742
1743 let pkgid = PackageId::new(
1744 package_name.as_str().into(),
1745 version
1746 .cloned()
1747 .unwrap_or_else(|| semver::Version::new(0, 0, 0)),
1748 source_id,
1749 );
1750 let summary = {
1751 let summary = Summary::new(
1752 pkgid,
1753 deps,
1754 &normalized_toml
1755 .features
1756 .as_ref()
1757 .unwrap_or(&Default::default())
1758 .iter()
1759 .map(|(k, v)| {
1760 (
1761 k.to_string().into(),
1762 v.iter().map(InternedString::from).collect(),
1763 )
1764 })
1765 .collect(),
1766 normalized_package.links.as_deref(),
1767 rust_version.clone(),
1768 );
1769 if let Err(ref err) = summary {
1772 if let Some(missing_dep) = err.downcast_ref::<MissingDependencyError>() {
1773 missing_dep_diagnostic(
1774 missing_dep,
1775 &original_toml,
1776 document.as_ref(),
1777 contents.as_deref(),
1778 manifest_file,
1779 gctx,
1780 )?;
1781 }
1782 }
1783 summary?
1784 };
1785
1786 if summary.features().contains_key("default-features") {
1787 warnings.push(
1788 "`[features]` defines a feature named `default-features`
1789note: only a feature named `default` will be enabled by default"
1790 .to_string(),
1791 )
1792 }
1793
1794 if let Some(run) = &normalized_package.default_run {
1795 if !targets
1796 .iter()
1797 .filter(|t| t.is_bin())
1798 .any(|t| t.name() == run)
1799 {
1800 let suggestion = util::closest_msg(
1801 run,
1802 targets.iter().filter(|t| t.is_bin()),
1803 |t| t.name(),
1804 "target",
1805 );
1806 bail!("default-run target `{}` not found{}", run, suggestion);
1807 }
1808 }
1809
1810 let default_kind = normalized_package
1811 .default_target
1812 .as_ref()
1813 .map(|t| CompileTarget::new(&*t, gctx.cli_unstable().json_target_spec))
1814 .transpose()?
1815 .map(CompileKind::Target);
1816 let forced_kind = normalized_package
1817 .forced_target
1818 .as_ref()
1819 .map(|t| CompileTarget::new(&*t, gctx.cli_unstable().json_target_spec))
1820 .transpose()?
1821 .map(CompileKind::Target);
1822 let include = normalized_package
1823 .normalized_include()
1824 .expect("previously normalized")
1825 .cloned()
1826 .unwrap_or_default();
1827 let exclude = normalized_package
1828 .normalized_exclude()
1829 .expect("previously normalized")
1830 .cloned()
1831 .unwrap_or_default();
1832 let links = normalized_package.links.clone();
1833 let custom_metadata = normalized_package.metadata.clone();
1834 let im_a_teapot = normalized_package.im_a_teapot;
1835 let default_run = normalized_package.default_run.clone();
1836 let metabuild = normalized_package.metabuild.clone().map(|sov| sov.0);
1837 let manifest = Manifest::new(
1838 contents.map(Rc::new),
1839 document.map(Arc::new),
1840 Some(Rc::new(original_toml)),
1841 Rc::new(normalized_toml),
1842 summary,
1843 default_kind,
1844 forced_kind,
1845 targets,
1846 exclude,
1847 include,
1848 links,
1849 metadata,
1850 custom_metadata,
1851 publish,
1852 replace,
1853 patch,
1854 workspace_config,
1855 features,
1856 edition,
1857 rust_version,
1858 im_a_teapot,
1859 default_run,
1860 metabuild,
1861 resolve_behavior,
1862 rustflags,
1863 hints,
1864 is_embedded,
1865 );
1866 if manifest
1867 .normalized_toml()
1868 .package()
1869 .unwrap()
1870 .license_file
1871 .is_some()
1872 && manifest
1873 .normalized_toml()
1874 .package()
1875 .unwrap()
1876 .license
1877 .is_some()
1878 {
1879 warnings.push(
1880 "only one of `license` or `license-file` is necessary\n\
1881 `license` should be used if the package license can be expressed \
1882 with a standard SPDX expression.\n\
1883 `license-file` should be used if the package uses a non-standard license.\n\
1884 See https://doc.rust-lang.org/cargo/reference/manifest.html#the-license-and-license-file-fields \
1885 for more information."
1886 .to_owned(),
1887 );
1888 }
1889 if let Some(original_toml) = manifest.original_toml() {
1890 warn_on_unused(&original_toml._unused_keys, warnings);
1891 }
1892
1893 manifest.feature_gate()?;
1894
1895 Ok(manifest)
1896}
1897
1898fn missing_dep_diagnostic(
1899 missing_dep: &MissingDependencyError,
1900 orig_toml: &TomlManifest,
1901 document: Option<&toml::Spanned<toml::de::DeTable<'static>>>,
1902 contents: Option<&str>,
1903 manifest_file: &Path,
1904 gctx: &GlobalContext,
1905) -> CargoResult<()> {
1906 let dep_name = missing_dep.dep_name;
1907 let manifest_path = cwd_rel_path(manifest_file, gctx);
1908
1909 let title = format!(
1910 "feature `{}` includes `{}`, but `{}` is not a dependency",
1911 missing_dep.feature, missing_dep.feature_value, &dep_name
1912 );
1913 let help = format!("enable the dependency with `dep:{dep_name}`");
1914 let info_label = format!(
1915 "`{}` is an unused optional dependency since no feature enables it",
1916 &dep_name
1917 );
1918 let group = Group::with_title(Level::ERROR.primary_title(&title));
1919 let group =
1920 if let Some(contents) = contents
1921 && let Some(document) = document
1922 {
1923 let feature_span =
1924 get_key_value_span(&document, &["features", missing_dep.feature.as_str()]).unwrap();
1925
1926 let snippet = Snippet::source(contents)
1927 .path(manifest_path)
1928 .annotation(AnnotationKind::Primary.span(feature_span.value));
1929
1930 if missing_dep.weak_optional {
1931 let mut orig_deps = vec![
1932 (
1933 orig_toml.dependencies.as_ref(),
1934 vec![DepKind::Normal.kind_table()],
1935 ),
1936 (
1937 orig_toml.build_dependencies.as_ref(),
1938 vec![DepKind::Build.kind_table()],
1939 ),
1940 ];
1941 for (name, platform) in orig_toml.target.iter().flatten() {
1942 orig_deps.push((
1943 platform.dependencies.as_ref(),
1944 vec!["target", name, DepKind::Normal.kind_table()],
1945 ));
1946 orig_deps.push((
1947 platform.build_dependencies.as_ref(),
1948 vec!["target", name, DepKind::Normal.kind_table()],
1949 ));
1950 }
1951
1952 if let Some((_, toml_path)) = orig_deps.iter().find(|(deps, _)| {
1953 if let Some(deps) = deps {
1954 deps.keys().any(|p| *p.as_str() == *dep_name)
1955 } else {
1956 false
1957 }
1958 }) {
1959 let toml_path = toml_path
1960 .iter()
1961 .map(|s| *s)
1962 .chain(std::iter::once(dep_name.as_str()))
1963 .collect::<Vec<_>>();
1964 let dep_span = get_key_value_span(&document, &toml_path).unwrap();
1965
1966 group
1967 .element(snippet.annotation(
1968 AnnotationKind::Context.span(dep_span.key).label(info_label),
1969 ))
1970 .element(Level::HELP.message(help))
1971 } else {
1972 group.element(snippet)
1973 }
1974 } else {
1975 group.element(snippet)
1976 }
1977 } else {
1978 group
1979 };
1980
1981 if let Err(err) = gctx.shell().print_report(&[group], true) {
1982 return Err(err.into());
1983 }
1984 Err(AlreadyPrintedError::new(anyhow!("").into()).into())
1985}
1986
1987fn to_virtual_manifest(
1988 contents: Option<String>,
1989 document: Option<toml::Spanned<toml::de::DeTable<'static>>>,
1990 original_toml: manifest::TomlManifest,
1991 normalized_toml: manifest::TomlManifest,
1992 features: Features,
1993 workspace_config: WorkspaceConfig,
1994 source_id: SourceId,
1995 manifest_file: &Path,
1996 gctx: &GlobalContext,
1997 warnings: &mut Vec<String>,
1998 _errors: &mut Vec<String>,
1999) -> CargoResult<VirtualManifest> {
2000 let mut deps = Vec::new();
2001 let (replace, patch) = {
2002 let mut manifest_ctx = ManifestContext {
2003 deps: &mut deps,
2004 source_id,
2005 gctx,
2006 warnings,
2007 platform: None,
2008 file: manifest_file,
2009 };
2010 (
2011 replace(&normalized_toml, &mut manifest_ctx)?,
2012 patch(&normalized_toml, &mut manifest_ctx)?,
2013 )
2014 };
2015 if let Some(profiles) = &normalized_toml.profile {
2016 validate_profiles(profiles, gctx.cli_unstable(), &features, warnings)?;
2017 }
2018 let resolve_behavior = normalized_toml
2019 .workspace
2020 .as_ref()
2021 .and_then(|ws| ws.resolver.as_deref())
2022 .map(|r| ResolveBehavior::from_manifest(r))
2023 .transpose()?;
2024 if let WorkspaceConfig::Member { .. } = &workspace_config {
2025 bail!("virtual manifests must be configured with [workspace]");
2026 }
2027 let manifest = VirtualManifest::new(
2028 contents.map(Rc::new),
2029 document.map(Rc::new),
2030 Some(Rc::new(original_toml)),
2031 Rc::new(normalized_toml),
2032 replace,
2033 patch,
2034 workspace_config,
2035 features,
2036 resolve_behavior,
2037 );
2038
2039 if let Some(original_toml) = manifest.original_toml() {
2040 warn_on_unused(&original_toml._unused_keys, warnings);
2041 }
2042
2043 Ok(manifest)
2044}
2045
2046#[tracing::instrument(skip_all)]
2047fn validate_dependencies(
2048 original_deps: Option<&BTreeMap<manifest::PackageName, manifest::InheritableDependency>>,
2049 platform: Option<&Platform>,
2050 kind: Option<DepKind>,
2051 warnings: &mut Vec<String>,
2052) -> CargoResult<()> {
2053 let Some(dependencies) = original_deps else {
2054 return Ok(());
2055 };
2056
2057 for (name_in_toml, v) in dependencies.iter() {
2058 let kind_name = match kind {
2059 Some(k) => k.kind_table(),
2060 None => "dependencies",
2061 };
2062 let table_in_toml = if let Some(platform) = platform {
2063 format!("target.{platform}.{kind_name}")
2064 } else {
2065 kind_name.to_string()
2066 };
2067 unused_dep_keys(name_in_toml, &table_in_toml, v.unused_keys(), warnings);
2068 }
2069 Ok(())
2070}
2071
2072struct ManifestContext<'a, 'b> {
2073 deps: &'a mut Vec<Dependency>,
2074 source_id: SourceId,
2075 gctx: &'b GlobalContext,
2076 warnings: &'a mut Vec<String>,
2077 platform: Option<Platform>,
2078 file: &'a Path,
2079}
2080
2081#[tracing::instrument(skip_all)]
2082fn gather_dependencies(
2083 manifest_ctx: &mut ManifestContext<'_, '_>,
2084 normalized_deps: Option<&BTreeMap<manifest::PackageName, manifest::InheritableDependency>>,
2085 kind: Option<DepKind>,
2086) -> CargoResult<()> {
2087 let Some(dependencies) = normalized_deps else {
2088 return Ok(());
2089 };
2090
2091 for (n, v) in dependencies.iter() {
2092 let resolved = v.normalized().expect("previously normalized");
2093 let dep = dep_to_dependency(&resolved, n, manifest_ctx, kind)?;
2094 manifest_ctx.deps.push(dep);
2095 }
2096 Ok(())
2097}
2098
2099fn replace(
2100 me: &manifest::TomlManifest,
2101 manifest_ctx: &mut ManifestContext<'_, '_>,
2102) -> CargoResult<Vec<(PackageIdSpec, Dependency)>> {
2103 if me.patch.is_some() && me.replace.is_some() {
2104 bail!("cannot specify both [replace] and [patch]");
2105 }
2106 let mut replace = Vec::new();
2107 for (spec, replacement) in me.replace.iter().flatten() {
2108 let mut spec = PackageIdSpec::parse(spec).with_context(|| {
2109 format!(
2110 "replacements must specify a valid semver \
2111 version to replace, but `{}` does not",
2112 spec
2113 )
2114 })?;
2115 if spec.url().is_none() {
2116 spec.set_url(CRATES_IO_INDEX.parse().unwrap());
2117 }
2118
2119 if replacement.is_version_specified() {
2120 bail!(
2121 "replacements cannot specify a version \
2122 requirement, but found one for `{}`",
2123 spec
2124 );
2125 }
2126
2127 let mut dep = dep_to_dependency(replacement, spec.name(), manifest_ctx, None)?;
2128 let version = spec.version().ok_or_else(|| {
2129 anyhow!(
2130 "replacements must specify a version \
2131 to replace, but `{}` does not",
2132 spec
2133 )
2134 })?;
2135 unused_dep_keys(
2136 dep.name_in_toml().as_str(),
2137 "replace",
2138 replacement.unused_keys(),
2139 &mut manifest_ctx.warnings,
2140 );
2141 dep.set_version_req(OptVersionReq::exact(&version));
2142 replace.push((spec, dep));
2143 }
2144 Ok(replace)
2145}
2146
2147fn patch(
2148 me: &TomlManifest,
2149 manifest_ctx: &mut ManifestContext<'_, '_>,
2150) -> CargoResult<HashMap<Url, Vec<Patch>>> {
2151 let mut patch = HashMap::default();
2152 for (toml_url, deps) in me.patch.iter().flatten() {
2153 let url = match &toml_url[..] {
2154 CRATES_IO_REGISTRY => CRATES_IO_INDEX.parse().unwrap(),
2155 _ => manifest_ctx
2156 .gctx
2157 .get_registry_index(toml_url)
2158 .or_else(|_| toml_url.into_url())
2159 .with_context(|| {
2160 format!(
2161 "[patch] entry `{}` should be a URL or registry name{}",
2162 toml_url,
2163 if toml_url == "crates" {
2164 "\nFor crates.io, use [patch.crates-io] (with a dash)"
2165 } else {
2166 ""
2167 }
2168 )
2169 })?,
2170 };
2171 patch.insert(
2172 url,
2173 deps.iter()
2174 .map(|(name, dep)| {
2175 unused_dep_keys(
2176 name,
2177 &format!("patch.{toml_url}",),
2178 dep.unused_keys(),
2179 &mut manifest_ctx.warnings,
2180 );
2181
2182 let dep = dep_to_dependency(dep, name, manifest_ctx, None)?;
2183 let loc = PatchLocation::Manifest(manifest_ctx.file.to_path_buf());
2184 Ok(Patch { dep, loc })
2185 })
2186 .collect::<CargoResult<Vec<_>>>()?,
2187 );
2188 }
2189 Ok(patch)
2190}
2191
2192pub(crate) fn config_patch_to_dependency<P: ResolveToPath + Clone>(
2194 config_patch: &manifest::TomlDependency<P>,
2195 name: &str,
2196 source_id: SourceId,
2197 gctx: &GlobalContext,
2198 warnings: &mut Vec<String>,
2199) -> CargoResult<Dependency> {
2200 let manifest_ctx = &mut ManifestContext {
2201 deps: &mut Vec::new(),
2202 source_id,
2203 gctx,
2204 warnings,
2205 platform: None,
2206 file: Path::new("unused"),
2208 };
2209 dep_to_dependency(config_patch, name, manifest_ctx, None)
2210}
2211
2212fn dep_to_dependency<P: ResolveToPath + Clone>(
2213 orig: &manifest::TomlDependency<P>,
2214 name_in_toml: &str,
2215 manifest_ctx: &mut ManifestContext<'_, '_>,
2216 kind: Option<DepKind>,
2217) -> CargoResult<Dependency> {
2218 let orig = match orig {
2219 manifest::TomlDependency::Simple(version) => &manifest::TomlDetailedDependency::<P> {
2220 version: Some(version.clone()),
2221 ..Default::default()
2222 },
2223 manifest::TomlDependency::Detailed(details) => details,
2224 };
2225
2226 if orig.version.is_none() && orig.path.is_none() && orig.git.is_none() {
2227 anyhow::bail!(
2228 "dependency ({name_in_toml}) specified without \
2229 providing a local path, Git repository, version, or \
2230 workspace dependency to use"
2231 );
2232 }
2233
2234 if let Some(version) = &orig.version {
2235 if version.contains('+') {
2236 manifest_ctx.warnings.push(format!(
2237 "version requirement `{}` for dependency `{}` \
2238 includes semver metadata which will be ignored, removing the \
2239 metadata is recommended to avoid confusion",
2240 version, name_in_toml
2241 ));
2242 }
2243 }
2244
2245 if orig.git.is_none() {
2246 let git_only_keys = [
2247 (&orig.branch, "branch"),
2248 (&orig.tag, "tag"),
2249 (&orig.rev, "rev"),
2250 ];
2251
2252 for &(key, key_name) in &git_only_keys {
2253 if key.is_some() {
2254 bail!(
2255 "key `{}` is ignored for dependency ({}).",
2256 key_name,
2257 name_in_toml
2258 );
2259 }
2260 }
2261 }
2262
2263 if let Some(features) = &orig.features {
2266 for feature in features {
2267 if feature.contains('/') {
2268 bail!(
2269 "feature `{}` in dependency `{}` is not allowed to contain slashes\n\
2270 If you want to enable features of a transitive dependency, \
2271 the direct dependency needs to re-export those features from \
2272 the `[features]` table.",
2273 feature,
2274 name_in_toml
2275 );
2276 }
2277 if feature.starts_with("dep:") {
2278 bail!(
2279 "feature `{}` in dependency `{}` is not allowed to use explicit \
2280 `dep:` syntax\n\
2281 If you want to enable an optional dependency, specify the name \
2282 of the optional dependency without the `dep:` prefix, or specify \
2283 a feature from the dependency's `[features]` table that enables \
2284 the optional dependency.",
2285 feature,
2286 name_in_toml
2287 );
2288 }
2289 }
2290 }
2291
2292 let new_source_id = to_dependency_source_id(orig, name_in_toml, manifest_ctx)?;
2293
2294 let (pkg_name, explicit_name_in_toml) = match orig.package {
2295 Some(ref s) => (&s[..], Some(name_in_toml)),
2296 None => (name_in_toml, None),
2297 };
2298
2299 let version = orig.version.as_deref();
2300 let mut dep = Dependency::parse(pkg_name, version, new_source_id)?;
2301 dep.set_features(orig.features.iter().flatten())
2302 .set_default_features(orig.default_features().unwrap_or(true))
2303 .set_optional(orig.optional.unwrap_or(false))
2304 .set_platform(manifest_ctx.platform.clone());
2305 if let Some(registry) = &orig.registry {
2306 let registry_id = SourceId::alt_registry(manifest_ctx.gctx, registry)?;
2307 dep.set_registry_id(registry_id);
2308 }
2309 if let Some(registry_index) = &orig.registry_index {
2310 let url = registry_index.into_url()?;
2311 let registry_id = SourceId::for_registry(&url)?;
2312 dep.set_registry_id(registry_id);
2313 }
2314
2315 if let Some(kind) = kind {
2316 dep.set_kind(kind);
2317 }
2318 if let Some(name_in_toml) = explicit_name_in_toml {
2319 dep.set_explicit_name_in_toml(name_in_toml);
2320 }
2321
2322 if let Some(p) = orig.public {
2323 dep.set_public(p);
2324 }
2325
2326 if let (Some(artifact), is_lib, target) = (
2327 orig.artifact.as_ref(),
2328 orig.lib.unwrap_or(false),
2329 orig.target.as_deref(),
2330 ) {
2331 if manifest_ctx.gctx.cli_unstable().bindeps {
2332 let artifact = Artifact::parse(
2333 &artifact.0,
2334 is_lib,
2335 target,
2336 manifest_ctx.gctx.cli_unstable().json_target_spec,
2337 )?;
2338 if dep.kind() != DepKind::Build
2339 && artifact.target() == Some(ArtifactTarget::BuildDependencyAssumeTarget)
2340 {
2341 bail!(
2342 r#"`target = "target"` in normal- or dev-dependencies has no effect ({})"#,
2343 name_in_toml
2344 );
2345 }
2346 dep.set_artifact(artifact)
2347 } else {
2348 bail!("`artifact = …` requires `-Z bindeps` ({})", name_in_toml);
2349 }
2350 } else if orig.lib.is_some() || orig.target.is_some() {
2351 for (is_set, specifier) in [
2352 (orig.lib.is_some(), "lib"),
2353 (orig.target.is_some(), "target"),
2354 ] {
2355 if !is_set {
2356 continue;
2357 }
2358 bail!(
2359 "'{}' specifier cannot be used without an 'artifact = …' value ({})",
2360 specifier,
2361 name_in_toml
2362 )
2363 }
2364 }
2365 Ok(dep)
2366}
2367
2368fn to_dependency_source_id<P: ResolveToPath + Clone>(
2369 orig: &manifest::TomlDetailedDependency<P>,
2370 name_in_toml: &str,
2371 manifest_ctx: &mut ManifestContext<'_, '_>,
2372) -> CargoResult<SourceId> {
2373 match (
2374 orig.git.as_ref(),
2375 orig.path.as_ref(),
2376 orig.registry.as_deref(),
2377 orig.registry_index.as_ref(),
2378 ) {
2379 (Some(_git), Some(_path), _, _) => {
2380 bail!(
2381 "dependency ({name_in_toml}) specification is ambiguous. \
2382 Only one of `git` or `path` is allowed.",
2383 );
2384 }
2385 (_, _, Some(_registry), Some(_registry_index)) => bail!(
2386 "dependency ({name_in_toml}) specification is ambiguous. \
2387 Only one of `registry` or `registry-index` is allowed.",
2388 ),
2389 (Some(git), None, _, _) => {
2390 let n_details = [&orig.branch, &orig.tag, &orig.rev]
2391 .iter()
2392 .filter(|d| d.is_some())
2393 .count();
2394
2395 if n_details > 1 {
2396 bail!(
2397 "dependency ({name_in_toml}) specification is ambiguous. \
2398 Only one of `branch`, `tag` or `rev` is allowed.",
2399 );
2400 }
2401
2402 let reference = orig
2403 .branch
2404 .clone()
2405 .map(GitReference::Branch)
2406 .or_else(|| orig.tag.clone().map(GitReference::Tag))
2407 .or_else(|| orig.rev.clone().map(GitReference::Rev))
2408 .unwrap_or(GitReference::DefaultBranch);
2409 let loc = git.into_url()?;
2410
2411 if let Some(fragment) = loc.fragment() {
2412 let msg = format!(
2413 "URL fragment `#{fragment}` in git URL is ignored for dependency ({name_in_toml}). \
2414 If you were trying to specify a specific git revision, \
2415 use `rev = \"{fragment}\"` in the dependency declaration.",
2416 );
2417 manifest_ctx.warnings.push(msg);
2418 }
2419
2420 SourceId::for_git(&loc, reference)
2421 }
2422 (None, Some(path), _, _) => {
2423 let path = path.resolve(manifest_ctx.gctx);
2424 if manifest_ctx.source_id.is_path() {
2433 let path = manifest_ctx.file.parent().unwrap().join(path);
2434 let path = paths::normalize_path(&path);
2435 SourceId::for_path(&path)
2436 } else {
2437 Ok(manifest_ctx.source_id)
2438 }
2439 }
2440 (None, None, Some(registry), None) => SourceId::alt_registry(manifest_ctx.gctx, registry),
2441 (None, None, None, Some(registry_index)) => {
2442 let url = registry_index.into_url()?;
2443 SourceId::for_registry(&url)
2444 }
2445 (None, None, None, None) => SourceId::crates_io(manifest_ctx.gctx),
2446 }
2447}
2448
2449pub(crate) fn lookup_path_base<'a>(
2450 base: &PathBaseName,
2451 gctx: &GlobalContext,
2452 workspace_root: &dyn Fn() -> CargoResult<&'a Path>,
2453 features: &Features,
2454) -> CargoResult<PathBuf> {
2455 features.require(Feature::path_bases())?;
2456
2457 let base_key = format!("path-bases.{base}");
2460
2461 if let Some(path_bases) = gctx.get::<Option<ConfigRelativePath>>(&base_key)? {
2463 Ok(path_bases.resolve_path(gctx))
2464 } else {
2465 match base.as_str() {
2467 "workspace" => Ok(workspace_root()?.to_path_buf()),
2468 _ => bail!(
2469 "path base `{base}` is undefined. \
2470 You must add an entry for `{base}` in the Cargo configuration [path-bases] table."
2471 ),
2472 }
2473 }
2474}
2475
2476pub trait ResolveToPath {
2477 fn resolve(&self, gctx: &GlobalContext) -> PathBuf;
2478}
2479
2480impl ResolveToPath for String {
2481 fn resolve(&self, _: &GlobalContext) -> PathBuf {
2482 self.into()
2483 }
2484}
2485
2486impl ResolveToPath for ConfigRelativePath {
2487 fn resolve(&self, gctx: &GlobalContext) -> PathBuf {
2488 self.resolve_path(gctx)
2489 }
2490}
2491
2492#[tracing::instrument(skip_all)]
2495fn unique_build_targets(
2496 targets: &[Target],
2497 package_root: &Path,
2498) -> Result<(), HashMap<PathBuf, Vec<Target>>> {
2499 let mut source_targets = HashMap::<_, Vec<_>>::default();
2500 for target in targets {
2501 if let TargetSourcePath::Path(path) = target.src_path() {
2502 let full = package_root.join(path);
2503 source_targets.entry(full).or_default().push(target.clone());
2504 }
2505 }
2506
2507 let conflict_targets = source_targets
2508 .into_iter()
2509 .filter(|(_, targets)| targets.len() > 1)
2510 .collect::<HashMap<_, _>>();
2511
2512 if !conflict_targets.is_empty() {
2513 return Err(conflict_targets);
2514 }
2515
2516 Ok(())
2517}
2518
2519fn validate_profiles(
2524 profiles: &manifest::TomlProfiles,
2525 cli_unstable: &CliUnstable,
2526 features: &Features,
2527 warnings: &mut Vec<String>,
2528) -> CargoResult<()> {
2529 for (name, profile) in &profiles.0 {
2530 validate_profile(profile, name, cli_unstable, features, warnings)?;
2531 }
2532 Ok(())
2533}
2534
2535pub fn validate_profile(
2537 root: &manifest::TomlProfile,
2538 name: &str,
2539 cli_unstable: &CliUnstable,
2540 features: &Features,
2541 warnings: &mut Vec<String>,
2542) -> CargoResult<()> {
2543 validate_profile_layer(root, cli_unstable, features)?;
2544 if let Some(ref profile) = root.build_override {
2545 validate_profile_override(profile, "build-override")?;
2546 validate_profile_layer(profile, cli_unstable, features)?;
2547 }
2548 if let Some(ref packages) = root.package {
2549 for profile in packages.values() {
2550 validate_profile_override(profile, "package")?;
2551 validate_profile_layer(profile, cli_unstable, features)?;
2552 }
2553 }
2554
2555 if let Some(dir_name) = &root.dir_name {
2556 bail!(
2560 "dir-name=\"{}\" in profile `{}` is not currently allowed, \
2561 directory names are tied to the profile name for custom profiles",
2562 dir_name,
2563 name
2564 );
2565 }
2566
2567 if matches!(root.inherits.as_deref(), Some("debug")) {
2569 bail!(
2570 "profile.{}.inherits=\"debug\" should be profile.{}.inherits=\"dev\"",
2571 name,
2572 name
2573 );
2574 }
2575
2576 match name {
2577 "doc" => {
2578 warnings.push("profile `doc` is deprecated and has no effect".to_string());
2579 }
2580 "test" | "bench" => {
2581 if root.panic.is_some() {
2582 warnings.push(format!("`panic` setting is ignored for `{}` profile", name))
2583 }
2584 }
2585 _ => {}
2586 }
2587
2588 if let Some(panic) = &root.panic {
2589 if panic != "unwind" && panic != "abort" && panic != "immediate-abort" {
2590 bail!(
2591 "`panic` setting of `{}` is not a valid setting, \
2592 must be `unwind`, `abort`, or `immediate-abort`.",
2593 panic
2594 );
2595 }
2596 }
2597
2598 if let Some(manifest::StringOrBool::String(arg)) = &root.lto {
2599 if arg == "true" || arg == "false" {
2600 bail!(
2601 "`lto` setting of string `\"{arg}\"` for `{name}` profile is not \
2602 a valid setting, must be a boolean (`true`/`false`) or a string \
2603 (`\"thin\"`/`\"fat\"`/`\"off\"`) or omitted.",
2604 );
2605 }
2606 }
2607
2608 Ok(())
2609}
2610
2611fn validate_profile_layer(
2615 profile: &manifest::TomlProfile,
2616 cli_unstable: &CliUnstable,
2617 features: &Features,
2618) -> CargoResult<()> {
2619 if profile.codegen_backend.is_some() {
2620 match (
2621 features.require(Feature::codegen_backend()),
2622 cli_unstable.codegen_backend,
2623 ) {
2624 (Err(e), false) => return Err(e),
2625 _ => {}
2626 }
2627 }
2628 if profile.rustflags.is_some() {
2629 match (
2630 features.require(Feature::profile_rustflags()),
2631 cli_unstable.profile_rustflags,
2632 ) {
2633 (Err(e), false) => return Err(e),
2634 _ => {}
2635 }
2636 }
2637 if profile.trim_paths.is_some() {
2638 match (
2639 features.require(Feature::trim_paths()),
2640 cli_unstable.trim_paths,
2641 ) {
2642 (Err(e), false) => return Err(e),
2643 _ => {}
2644 }
2645 }
2646 if profile.panic.as_deref() == Some("immediate-abort") {
2647 match (
2648 features.require(Feature::panic_immediate_abort()),
2649 cli_unstable.panic_immediate_abort,
2650 ) {
2651 (Err(e), false) => return Err(e),
2652 _ => {}
2653 }
2654 }
2655 Ok(())
2656}
2657
2658fn validate_profile_override(profile: &manifest::TomlProfile, which: &str) -> CargoResult<()> {
2660 if profile.package.is_some() {
2661 bail!("package-specific profiles cannot be nested");
2662 }
2663 if profile.build_override.is_some() {
2664 bail!("build-override profiles cannot be nested");
2665 }
2666 if profile.panic.is_some() {
2667 bail!("`panic` may not be specified in a `{}` profile", which)
2668 }
2669 if profile.lto.is_some() {
2670 bail!("`lto` may not be specified in a `{}` profile", which)
2671 }
2672 if profile.rpath.is_some() {
2673 bail!("`rpath` may not be specified in a `{}` profile", which)
2674 }
2675 Ok(())
2676}
2677
2678fn verify_lints(
2679 lints: Option<&manifest::TomlLints>,
2680 gctx: &GlobalContext,
2681 warnings: &mut Vec<String>,
2682) -> CargoResult<()> {
2683 let Some(lints) = lints else {
2684 return Ok(());
2685 };
2686
2687 for (tool, lints) in lints {
2688 let supported = ["cargo", "clippy", "rust", "rustdoc"];
2689 if !supported.contains(&tool.as_str()) {
2690 let message = format!(
2691 "unrecognized lint tool `lints.{tool}`, specifying unrecognized tools may break in the future.
2692supported tools: {}",
2693 supported.join(", "),
2694 );
2695 warnings.push(message);
2696 continue;
2697 }
2698 if tool == "cargo" && !gctx.cli_unstable().cargo_lints {
2699 warn_for_cargo_lint_feature(gctx, warnings);
2700 }
2701 for (name, config) in lints {
2702 if let Some((prefix, suffix)) = name.split_once("::") {
2703 if tool == prefix {
2704 anyhow::bail!(
2705 "`lints.{tool}.{name}` is not valid lint name; try `lints.{prefix}.{suffix}`"
2706 )
2707 } else if tool == "rust" && supported.contains(&prefix) {
2708 anyhow::bail!(
2709 "`lints.{tool}.{name}` is not valid lint name; try `lints.{prefix}.{suffix}`"
2710 )
2711 } else {
2712 anyhow::bail!("`lints.{tool}.{name}` is not a valid lint name")
2713 }
2714 } else if let Some(config) = config.config() {
2715 for config_name in config.keys() {
2716 let expected = EXPECTED_LINT_CONFIG.contains(&(tool, name, config_name));
2719 if !expected {
2720 let message =
2721 format!("unused manifest key: `lints.{tool}.{name}.{config_name}`");
2722 warnings.push(message);
2723 }
2724 }
2725 }
2726 }
2727 }
2728
2729 Ok(())
2730}
2731
2732static EXPECTED_LINT_CONFIG: &[(&str, &str, &str)] = &[
2733 ("rust", "unexpected_cfgs", "check-cfg"),
2735];
2736
2737fn warn_for_cargo_lint_feature(gctx: &GlobalContext, warnings: &mut Vec<String>) {
2738 use std::fmt::Write as _;
2739
2740 let key_name = "lints.cargo";
2741 let feature_name = "cargo-lints";
2742
2743 let mut message = String::new();
2744
2745 let _ = write!(
2746 message,
2747 "unused manifest key `{key_name}` (may be supported in a future version)"
2748 );
2749 if gctx.nightly_features_allowed {
2750 let _ = write!(
2751 message,
2752 "
2753
2754consider passing `-Z{feature_name}` to enable this feature."
2755 );
2756 } else {
2757 let _ = write!(
2758 message,
2759 "
2760
2761this Cargo does not support nightly features, but if you
2762switch to nightly channel you can pass
2763`-Z{feature_name}` to enable this feature.",
2764 );
2765 }
2766 warnings.push(message);
2767}
2768
2769fn lints_to_rustflags(lints: &manifest::TomlLints) -> CargoResult<Vec<String>> {
2770 let mut rustflags = lints
2771 .iter()
2772 .filter(|(tool, _)| tool != &"cargo")
2774 .flat_map(|(tool, lints)| {
2775 lints.iter().map(move |(name, config)| {
2776 let flag = match config.level() {
2777 manifest::TomlLintLevel::Forbid => "--forbid",
2778 manifest::TomlLintLevel::Deny => "--deny",
2779 manifest::TomlLintLevel::Warn => "--warn",
2780 manifest::TomlLintLevel::Allow => "--allow",
2781 };
2782
2783 let option = if tool == "rust" {
2784 format!("{flag}={name}")
2785 } else {
2786 format!("{flag}={tool}::{name}")
2787 };
2788 (
2789 config.priority(),
2790 std::cmp::Reverse(name),
2793 option,
2794 )
2795 })
2796 })
2797 .collect::<Vec<_>>();
2798 rustflags.sort();
2799
2800 let mut rustflags: Vec<_> = rustflags.into_iter().map(|(_, _, option)| option).collect();
2801
2802 if let Some(rust_lints) = lints.get("rust") {
2804 if let Some(unexpected_cfgs) = rust_lints.get("unexpected_cfgs") {
2805 if let Some(config) = unexpected_cfgs.config() {
2806 if let Some(check_cfg) = config.get("check-cfg") {
2807 if let Ok(check_cfgs) = toml::Value::try_into::<Vec<String>>(check_cfg.clone())
2808 {
2809 for check_cfg in check_cfgs {
2810 rustflags.push("--check-cfg".to_string());
2811 rustflags.push(check_cfg);
2812 }
2813 } else {
2815 bail!("`lints.rust.unexpected_cfgs.check-cfg` must be a list of string");
2816 }
2817 }
2818 }
2819 }
2820 }
2821
2822 Ok(rustflags)
2823}
2824
2825fn emit_frontmatter_diagnostic(
2826 e: crate::util::frontmatter::FrontmatterError,
2827 contents: &str,
2828 manifest_file: &Path,
2829 gctx: &GlobalContext,
2830) -> anyhow::Error {
2831 let primary_span = e.primary_span();
2832
2833 let manifest_path = diff_paths(manifest_file, gctx.cwd())
2835 .unwrap_or_else(|| manifest_file.to_path_buf())
2836 .display()
2837 .to_string();
2838 let group = Group::with_title(Level::ERROR.primary_title(e.message())).element(
2839 Snippet::source(contents)
2840 .path(manifest_path)
2841 .annotation(AnnotationKind::Primary.span(primary_span))
2842 .annotations(
2843 e.visible_spans()
2844 .iter()
2845 .map(|s| AnnotationKind::Visible.span(s.clone())),
2846 ),
2847 );
2848
2849 if let Err(err) = gctx.shell().print_report(&[group], true) {
2850 return err.into();
2851 }
2852 return AlreadyPrintedError::new(e.into()).into();
2853}
2854
2855fn emit_toml_diagnostic(
2856 e: toml::de::Error,
2857 contents: &str,
2858 manifest_file: &Path,
2859 gctx: &GlobalContext,
2860) -> anyhow::Error {
2861 let Some(span) = e.span() else {
2862 return e.into();
2863 };
2864
2865 let manifest_path = diff_paths(manifest_file, gctx.cwd())
2867 .unwrap_or_else(|| manifest_file.to_path_buf())
2868 .display()
2869 .to_string();
2870 let group = Group::with_title(Level::ERROR.primary_title(e.message())).element(
2871 Snippet::source(contents)
2872 .path(manifest_path)
2873 .annotation(AnnotationKind::Primary.span(span)),
2874 );
2875
2876 if let Err(err) = gctx.shell().print_report(&[group], true) {
2877 return err.into();
2878 }
2879 return AlreadyPrintedError::new(e.into()).into();
2880}
2881
2882fn deprecated_underscore<T>(
2884 old: &Option<T>,
2885 new: &Option<T>,
2886 new_path: &str,
2887 name: &str,
2888 kind: &str,
2889 edition: Edition,
2890 warnings: &mut Vec<String>,
2891) -> CargoResult<()> {
2892 let old_path = new_path.replace("-", "_");
2893 if old.is_some() && Edition::Edition2024 <= edition {
2894 anyhow::bail!(
2895 "`{old_path}` is unsupported as of the 2024 edition; instead use `{new_path}`\n(in the `{name}` {kind})"
2896 );
2897 } else if old.is_some() && new.is_some() {
2898 warnings.push(format!(
2899 "`{old_path}` is redundant with `{new_path}`, preferring `{new_path}` in the `{name}` {kind}"
2900 ))
2901 } else if old.is_some() {
2902 warnings.push(format!(
2903 "`{old_path}` is deprecated in favor of `{new_path}` and will not work in the 2024 edition\n(in the `{name}` {kind})"
2904 ))
2905 }
2906 Ok(())
2907}
2908
2909fn warn_on_unused(unused: &BTreeSet<String>, warnings: &mut Vec<String>) {
2910 use std::fmt::Write as _;
2911
2912 for key in unused {
2913 let mut message = format!("unused manifest key: {}", key);
2914 if TOP_LEVEL_CONFIG_KEYS.iter().any(|c| c == key) {
2915 write!(
2916 &mut message,
2917 "\nhelp: {key} is a valid .cargo/config.toml key"
2918 )
2919 .unwrap();
2920 }
2921 warnings.push(message);
2922 }
2923}
2924
2925fn unused_dep_keys(
2926 dep_name: &str,
2927 kind: &str,
2928 unused_keys: Vec<String>,
2929 warnings: &mut Vec<String>,
2930) {
2931 for unused in unused_keys {
2932 let key = format!("unused manifest key: {kind}.{dep_name}.{unused}");
2933 warnings.push(key);
2934 }
2935}
2936
2937pub fn prepare_for_publish(
2939 me: &Package,
2940 ws: &Workspace<'_>,
2941 packaged_files: Option<&[PathBuf]>,
2942) -> CargoResult<Package> {
2943 let contents = me.manifest().contents();
2944 let document = me.manifest().document();
2945 let original_toml = prepare_toml_for_publish(
2946 me.manifest().normalized_toml(),
2947 ws,
2948 me.root(),
2949 packaged_files,
2950 )?;
2951 let normalized_toml = original_toml.clone();
2952 let features = me.manifest().unstable_features().clone();
2953 let workspace_config = me.manifest().workspace_config().clone();
2954 let source_id = me.package_id().source_id();
2955 let mut warnings = Default::default();
2956 let mut errors = Default::default();
2957 let gctx = ws.gctx();
2958 let manifest = to_real_manifest(
2959 contents.map(|c| c.to_owned()),
2960 document.cloned(),
2961 original_toml,
2962 normalized_toml,
2963 features,
2964 workspace_config,
2965 source_id,
2966 me.manifest_path(),
2967 me.manifest().is_embedded(),
2968 gctx,
2969 &mut warnings,
2970 &mut errors,
2971 )?;
2972 let new_pkg = Package::new(manifest, me.manifest_path());
2973 Ok(new_pkg)
2974}
2975
2976fn prepare_toml_for_publish(
2980 me: &manifest::TomlManifest,
2981 ws: &Workspace<'_>,
2982 package_root: &Path,
2983 packaged_files: Option<&[PathBuf]>,
2984) -> CargoResult<manifest::TomlManifest> {
2985 let gctx = ws.gctx();
2986
2987 if me
2988 .cargo_features
2989 .iter()
2990 .flat_map(|f| f.iter())
2991 .any(|f| f == "open-namespaces")
2992 {
2993 anyhow::bail!("cannot publish with `open-namespaces`")
2994 }
2995
2996 let mut package = me.package().unwrap().clone();
2997 package.workspace = None;
2998 if let Some(custom_build_scripts) = package.normalized_build().expect("previously normalized") {
3000 let mut included_scripts = Vec::new();
3001 for script in custom_build_scripts {
3002 let path = Path::new(script).to_path_buf();
3003 let included = packaged_files.map(|i| i.contains(&path)).unwrap_or(true);
3004 if included {
3005 let path = path
3006 .into_os_string()
3007 .into_string()
3008 .map_err(|_err| anyhow::format_err!("non-UTF8 `package.build`"))?;
3009 let path = normalize_path_string_sep(path);
3010 included_scripts.push(path);
3011 } else {
3012 ws.gctx().shell().warn(format!(
3013 "ignoring `package.build` entry `{}` as it is not included in the published package",
3014 path.display()
3015 ))?;
3016 }
3017 }
3018
3019 package.build = Some(match included_scripts.len() {
3020 0 => TomlPackageBuild::Auto(false),
3021 1 => TomlPackageBuild::SingleScript(included_scripts[0].clone()),
3022 _ => TomlPackageBuild::MultipleScript(included_scripts),
3023 });
3024 }
3025 let current_resolver = package
3026 .resolver
3027 .as_ref()
3028 .map(|r| ResolveBehavior::from_manifest(r))
3029 .unwrap_or_else(|| {
3030 package
3031 .edition
3032 .as_ref()
3033 .and_then(|e| e.as_value())
3034 .map(|e| Edition::from_str(e))
3035 .unwrap_or(Ok(Edition::Edition2015))
3036 .map(|e| e.default_resolve_behavior())
3037 })?;
3038 if ws.resolve_behavior() != current_resolver {
3039 package.resolver = Some(ws.resolve_behavior().to_manifest());
3044 }
3045 if let Some(license_file) = &package.license_file {
3046 let license_file = license_file
3047 .as_value()
3048 .context("license file should have been resolved before `prepare_for_publish()`")?;
3049 let license_path = Path::new(&license_file);
3050 let abs_license_path = paths::normalize_path(&package_root.join(license_path));
3051 if let Ok(license_file) = abs_license_path.strip_prefix(package_root) {
3052 package.license_file = Some(manifest::InheritableField::Value(
3053 normalize_path_string_sep(
3054 license_file
3055 .to_str()
3056 .ok_or_else(|| anyhow::format_err!("non-UTF8 `package.license-file`"))?
3057 .to_owned(),
3058 ),
3059 ));
3060 } else {
3061 package.license_file = Some(manifest::InheritableField::Value(
3064 license_path
3065 .file_name()
3066 .unwrap()
3067 .to_str()
3068 .unwrap()
3069 .to_string(),
3070 ));
3071 }
3072 }
3073
3074 if let Some(readme) = &package.readme {
3075 let readme = readme
3076 .as_value()
3077 .context("readme should have been resolved before `prepare_for_publish()`")?;
3078 match readme {
3079 manifest::StringOrBool::String(readme) => {
3080 let readme_path = Path::new(&readme);
3081 let abs_readme_path = paths::normalize_path(&package_root.join(readme_path));
3082 if let Ok(readme_path) = abs_readme_path.strip_prefix(package_root) {
3083 package.readme = Some(manifest::InheritableField::Value(StringOrBool::String(
3084 normalize_path_string_sep(
3085 readme_path
3086 .to_str()
3087 .ok_or_else(|| {
3088 anyhow::format_err!("non-UTF8 `package.license-file`")
3089 })?
3090 .to_owned(),
3091 ),
3092 )));
3093 } else {
3094 package.readme = Some(manifest::InheritableField::Value(
3097 manifest::StringOrBool::String(
3098 readme_path
3099 .file_name()
3100 .unwrap()
3101 .to_str()
3102 .unwrap()
3103 .to_string(),
3104 ),
3105 ));
3106 }
3107 }
3108 manifest::StringOrBool::Bool(_) => {}
3109 }
3110 }
3111
3112 let lib = if let Some(target) = &me.lib {
3113 prepare_target_for_publish(target, packaged_files, "library", ws.gctx())?
3114 } else {
3115 None
3116 };
3117 let bin = prepare_targets_for_publish(me.bin.as_ref(), packaged_files, "binary", ws.gctx())?;
3118 let example =
3119 prepare_targets_for_publish(me.example.as_ref(), packaged_files, "example", ws.gctx())?;
3120 let test = prepare_targets_for_publish(me.test.as_ref(), packaged_files, "test", ws.gctx())?;
3121 let bench =
3122 prepare_targets_for_publish(me.bench.as_ref(), packaged_files, "benchmark", ws.gctx())?;
3123
3124 let all = |_d: &manifest::TomlDependency| true;
3125 let mut manifest = manifest::TomlManifest {
3126 cargo_features: me.cargo_features.clone(),
3127 package: Some(package),
3128 project: None,
3129 badges: me.badges.clone(),
3130 features: me.features.clone(),
3131 lib,
3132 bin,
3133 example,
3134 test,
3135 bench,
3136 dependencies: map_deps(gctx, me.dependencies.as_ref(), all)?,
3137 dev_dependencies: map_deps(
3138 gctx,
3139 me.dev_dependencies(),
3140 manifest::TomlDependency::is_version_specified,
3141 )?,
3142 dev_dependencies2: None,
3143 build_dependencies: map_deps(gctx, me.build_dependencies(), all)?,
3144 build_dependencies2: None,
3145 target: match me.target.as_ref().map(|target_map| {
3146 target_map
3147 .iter()
3148 .map(|(k, v)| {
3149 Ok((
3150 k.clone(),
3151 manifest::TomlPlatform {
3152 dependencies: map_deps(gctx, v.dependencies.as_ref(), all)?,
3153 dev_dependencies: map_deps(
3154 gctx,
3155 v.dev_dependencies(),
3156 manifest::TomlDependency::is_version_specified,
3157 )?,
3158 dev_dependencies2: None,
3159 build_dependencies: map_deps(gctx, v.build_dependencies(), all)?,
3160 build_dependencies2: None,
3161 },
3162 ))
3163 })
3164 .collect()
3165 }) {
3166 Some(Ok(v)) => Some(v),
3167 Some(Err(e)) => return Err(e),
3168 None => None,
3169 },
3170 lints: me.lints.clone(),
3171 hints: me.hints.clone(),
3172 workspace: None,
3173 profile: me.profile.clone(),
3174 patch: None,
3175 replace: None,
3176 _unused_keys: Default::default(),
3177 };
3178 strip_features(&mut manifest);
3179 return Ok(manifest);
3180
3181 fn strip_features(manifest: &mut TomlManifest) {
3182 fn insert_dep_name(
3183 dep_name_set: &mut BTreeSet<manifest::PackageName>,
3184 deps: Option<&BTreeMap<manifest::PackageName, manifest::InheritableDependency>>,
3185 ) {
3186 let Some(deps) = deps else {
3187 return;
3188 };
3189 deps.iter().for_each(|(k, _v)| {
3190 dep_name_set.insert(k.clone());
3191 });
3192 }
3193 let mut dep_name_set = BTreeSet::new();
3194 insert_dep_name(&mut dep_name_set, manifest.dependencies.as_ref());
3195 insert_dep_name(&mut dep_name_set, manifest.dev_dependencies());
3196 insert_dep_name(&mut dep_name_set, manifest.build_dependencies());
3197 if let Some(target_map) = manifest.target.as_ref() {
3198 target_map.iter().for_each(|(_k, v)| {
3199 insert_dep_name(&mut dep_name_set, v.dependencies.as_ref());
3200 insert_dep_name(&mut dep_name_set, v.dev_dependencies());
3201 insert_dep_name(&mut dep_name_set, v.build_dependencies());
3202 });
3203 }
3204 let features = manifest.features.as_mut();
3205
3206 let Some(features) = features else {
3207 return;
3208 };
3209
3210 features.values_mut().for_each(|feature_deps| {
3211 feature_deps.retain(|feature_dep| {
3212 let feature_value = FeatureValue::new(feature_dep.into());
3213 match feature_value {
3214 FeatureValue::Dep { dep_name } | FeatureValue::DepFeature { dep_name, .. } => {
3215 let k = &manifest::PackageName::new(dep_name.to_string()).unwrap();
3216 dep_name_set.contains(k)
3217 }
3218 _ => true,
3219 }
3220 });
3221 });
3222 }
3223
3224 fn map_deps(
3225 gctx: &GlobalContext,
3226 deps: Option<&BTreeMap<manifest::PackageName, manifest::InheritableDependency>>,
3227 filter: impl Fn(&manifest::TomlDependency) -> bool,
3228 ) -> CargoResult<Option<BTreeMap<manifest::PackageName, manifest::InheritableDependency>>> {
3229 let Some(deps) = deps else {
3230 return Ok(None);
3231 };
3232 let deps = deps
3233 .iter()
3234 .filter(|(_k, v)| {
3235 if let manifest::InheritableDependency::Value(def) = v {
3236 filter(def)
3237 } else {
3238 false
3239 }
3240 })
3241 .map(|(k, v)| Ok((k.clone(), map_dependency(gctx, v)?)))
3242 .collect::<CargoResult<BTreeMap<_, _>>>()?;
3243 Ok(Some(deps))
3244 }
3245
3246 fn map_dependency(
3247 gctx: &GlobalContext,
3248 dep: &manifest::InheritableDependency,
3249 ) -> CargoResult<manifest::InheritableDependency> {
3250 let dep = match dep {
3251 manifest::InheritableDependency::Value(manifest::TomlDependency::Detailed(d)) => {
3252 let mut d = d.clone();
3253 d.path.take();
3255 d.base.take();
3256 d.git.take();
3258 d.branch.take();
3259 d.tag.take();
3260 d.rev.take();
3261 if let Some(registry) = d.registry.take() {
3263 d.registry_index = Some(gctx.get_registry_index(®istry)?.to_string());
3264 }
3265 Ok(d)
3266 }
3267 manifest::InheritableDependency::Value(manifest::TomlDependency::Simple(s)) => {
3268 Ok(manifest::TomlDetailedDependency {
3269 version: Some(s.clone()),
3270 ..Default::default()
3271 })
3272 }
3273 _ => unreachable!(),
3274 };
3275 dep.map(manifest::TomlDependency::Detailed)
3276 .map(manifest::InheritableDependency::Value)
3277 }
3278}
3279
3280pub fn prepare_targets_for_publish(
3281 targets: Option<&Vec<manifest::TomlTarget>>,
3282 packaged_files: Option<&[PathBuf]>,
3283 context: &str,
3284 gctx: &GlobalContext,
3285) -> CargoResult<Option<Vec<manifest::TomlTarget>>> {
3286 let Some(targets) = targets else {
3287 return Ok(None);
3288 };
3289
3290 let mut prepared = Vec::with_capacity(targets.len());
3291 for target in targets {
3292 let Some(target) = prepare_target_for_publish(target, packaged_files, context, gctx)?
3293 else {
3294 continue;
3295 };
3296 prepared.push(target);
3297 }
3298
3299 if prepared.is_empty() {
3300 Ok(None)
3301 } else {
3302 Ok(Some(prepared))
3303 }
3304}
3305
3306pub fn prepare_target_for_publish(
3307 target: &manifest::TomlTarget,
3308 packaged_files: Option<&[PathBuf]>,
3309 context: &str,
3310 gctx: &GlobalContext,
3311) -> CargoResult<Option<manifest::TomlTarget>> {
3312 let path = target.path.as_ref().expect("previously normalized");
3313 let path = &path.0;
3314 if let Some(packaged_files) = packaged_files {
3315 if !packaged_files.contains(&path) {
3316 let name = target.name.as_ref().expect("previously normalized");
3317 gctx.shell().warn(format!(
3318 "ignoring {context} `{name}` as `{}` is not included in the published package",
3319 path.display()
3320 ))?;
3321 return Ok(None);
3322 }
3323 }
3324
3325 let mut target = target.clone();
3326 let path = normalize_path_sep(path.to_path_buf(), context)?;
3327 target.path = Some(manifest::PathValue(path.into()));
3328
3329 Ok(Some(target))
3330}
3331
3332fn normalize_path_sep(path: PathBuf, context: &str) -> CargoResult<PathBuf> {
3333 let path = path
3334 .into_os_string()
3335 .into_string()
3336 .map_err(|_err| anyhow::format_err!("non-UTF8 path for {context}"))?;
3337 let path = normalize_path_string_sep(path);
3338 Ok(path.into())
3339}
3340
3341pub fn normalize_path_string_sep(path: String) -> String {
3342 if std::path::MAIN_SEPARATOR != '/' {
3343 path.replace(std::path::MAIN_SEPARATOR, "/")
3344 } else {
3345 path
3346 }
3347}