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