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