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