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