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