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