1use annotate_snippets::{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_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 use embedded::ScriptSource;
44
45pub fn is_embedded(path: &Path) -> bool {
47 let ext = path.extension();
48 (ext == Some(OsStr::new("rs")) ||
49 ext.is_none())
51 && path.is_file()
52}
53
54#[tracing::instrument(skip(gctx))]
63pub fn read_manifest(
64 path: &Path,
65 source_id: SourceId,
66 gctx: &GlobalContext,
67) -> CargoResult<EitherManifest> {
68 let mut warnings = Default::default();
69 let mut errors = Default::default();
70
71 let is_embedded = is_embedded(path);
72 let contents = read_toml_string(path, is_embedded, gctx)
73 .map_err(|err| ManifestError::new(err, path.into()))?;
74 let document =
75 parse_document(&contents).map_err(|e| emit_diagnostic(e.into(), &contents, path, gctx))?;
76 let original_toml = deserialize_toml(&document)
77 .map_err(|e| emit_diagnostic(e.into(), &contents, path, gctx))?;
78
79 let mut manifest = (|| {
80 let empty = Vec::new();
81 let cargo_features = original_toml.cargo_features.as_ref().unwrap_or(&empty);
82 let features = Features::new(cargo_features, gctx, &mut warnings, source_id.is_path())?;
83 let workspace_config =
84 to_workspace_config(&original_toml, path, is_embedded, gctx, &mut warnings)?;
85 if let WorkspaceConfig::Root(ws_root_config) = &workspace_config {
86 let package_root = path.parent().unwrap();
87 gctx.ws_roots
88 .borrow_mut()
89 .insert(package_root.to_owned(), ws_root_config.clone());
90 }
91 let normalized_toml = normalize_toml(
92 &original_toml,
93 &features,
94 &workspace_config,
95 path,
96 is_embedded,
97 gctx,
98 &mut warnings,
99 &mut errors,
100 )?;
101
102 if normalized_toml.package().is_some() {
103 to_real_manifest(
104 contents,
105 document,
106 original_toml,
107 normalized_toml,
108 features,
109 workspace_config,
110 source_id,
111 path,
112 is_embedded,
113 gctx,
114 &mut warnings,
115 &mut errors,
116 )
117 .map(EitherManifest::Real)
118 } else if normalized_toml.workspace.is_some() {
119 assert!(!is_embedded);
120 to_virtual_manifest(
121 contents,
122 document,
123 original_toml,
124 normalized_toml,
125 features,
126 workspace_config,
127 source_id,
128 path,
129 gctx,
130 &mut warnings,
131 &mut errors,
132 )
133 .map(EitherManifest::Virtual)
134 } else {
135 anyhow::bail!("manifest is missing either a `[package]` or a `[workspace]`")
136 }
137 })()
138 .map_err(|err| {
139 ManifestError::new(
140 err.context(format!("failed to parse manifest at `{}`", path.display())),
141 path.into(),
142 )
143 })?;
144
145 for warning in warnings {
146 manifest.warnings_mut().add_warning(warning);
147 }
148 for error in errors {
149 manifest.warnings_mut().add_critical_warning(error);
150 }
151
152 Ok(manifest)
153}
154
155#[tracing::instrument(skip_all)]
156fn read_toml_string(path: &Path, is_embedded: bool, gctx: &GlobalContext) -> CargoResult<String> {
157 let mut contents = paths::read(path)?;
158 if is_embedded {
159 if !gctx.cli_unstable().script {
160 anyhow::bail!("parsing `{}` requires `-Zscript`", path.display());
161 }
162 contents = embedded::expand_manifest(&contents)?;
163 }
164 Ok(contents)
165}
166
167#[tracing::instrument(skip_all)]
168fn parse_document(
169 contents: &str,
170) -> Result<toml::Spanned<toml::de::DeTable<'static>>, toml::de::Error> {
171 let mut table = toml::de::DeTable::parse(contents)?;
172 table.get_mut().make_owned();
173 let table = unsafe {
176 std::mem::transmute::<
177 toml::Spanned<toml::de::DeTable<'_>>,
178 toml::Spanned<toml::de::DeTable<'static>>,
179 >(table)
180 };
181 Ok(table)
182}
183
184#[tracing::instrument(skip_all)]
185fn deserialize_toml(
186 document: &toml::Spanned<toml::de::DeTable<'static>>,
187) -> Result<manifest::TomlManifest, toml::de::Error> {
188 let mut unused = BTreeSet::new();
189 let deserializer = toml::de::Deserializer::from(document.clone());
190 let mut document: manifest::TomlManifest = serde_ignored::deserialize(deserializer, |path| {
191 let mut key = String::new();
192 stringify(&mut key, &path);
193 unused.insert(key);
194 })?;
195 document._unused_keys = unused;
196 Ok(document)
197}
198
199fn stringify(dst: &mut String, path: &serde_ignored::Path<'_>) {
200 use serde_ignored::Path;
201
202 match *path {
203 Path::Root => {}
204 Path::Seq { parent, index } => {
205 stringify(dst, parent);
206 if !dst.is_empty() {
207 dst.push('.');
208 }
209 dst.push_str(&index.to_string());
210 }
211 Path::Map { parent, ref key } => {
212 stringify(dst, parent);
213 if !dst.is_empty() {
214 dst.push('.');
215 }
216 dst.push_str(key);
217 }
218 Path::Some { parent }
219 | Path::NewtypeVariant { parent }
220 | Path::NewtypeStruct { parent } => stringify(dst, parent),
221 }
222}
223
224fn to_workspace_config(
225 original_toml: &manifest::TomlManifest,
226 manifest_file: &Path,
227 is_embedded: bool,
228 gctx: &GlobalContext,
229 warnings: &mut Vec<String>,
230) -> CargoResult<WorkspaceConfig> {
231 if is_embedded {
232 let ws_root_config = to_workspace_root_config(&TomlWorkspace::default(), manifest_file);
233 return Ok(WorkspaceConfig::Root(ws_root_config));
234 }
235 let workspace_config = match (
236 original_toml.workspace.as_ref(),
237 original_toml.package().and_then(|p| p.workspace.as_ref()),
238 ) {
239 (Some(toml_config), None) => {
240 verify_lints(toml_config.lints.as_ref(), gctx, warnings)?;
241 if let Some(ws_deps) = &toml_config.dependencies {
242 for (name, dep) in ws_deps {
243 if dep.is_optional() {
244 bail!("{name} is optional, but workspace dependencies cannot be optional",);
245 }
246 if dep.is_public() {
247 bail!("{name} is public, but workspace dependencies cannot be public",);
248 }
249 }
250
251 for (name, dep) in ws_deps {
252 unused_dep_keys(name, "workspace.dependencies", dep.unused_keys(), warnings);
253 }
254 }
255 let ws_root_config = to_workspace_root_config(toml_config, manifest_file);
256 WorkspaceConfig::Root(ws_root_config)
257 }
258 (None, root) => WorkspaceConfig::Member {
259 root: root.cloned(),
260 },
261 (Some(..), Some(..)) => bail!(
262 "cannot configure both `package.workspace` and \
263 `[workspace]`, only one can be specified"
264 ),
265 };
266 Ok(workspace_config)
267}
268
269fn to_workspace_root_config(
270 normalized_toml: &manifest::TomlWorkspace,
271 manifest_file: &Path,
272) -> WorkspaceRootConfig {
273 let package_root = manifest_file.parent().unwrap();
274 let inheritable = InheritableFields {
275 package: normalized_toml.package.clone(),
276 dependencies: normalized_toml.dependencies.clone(),
277 lints: normalized_toml.lints.clone(),
278 _ws_root: package_root.to_owned(),
279 };
280 let ws_root_config = WorkspaceRootConfig::new(
281 package_root,
282 &normalized_toml.members,
283 &normalized_toml.default_members,
284 &normalized_toml.exclude,
285 &Some(inheritable),
286 &normalized_toml.metadata,
287 );
288 ws_root_config
289}
290
291#[tracing::instrument(skip_all)]
293fn normalize_toml(
294 original_toml: &manifest::TomlManifest,
295 features: &Features,
296 workspace_config: &WorkspaceConfig,
297 manifest_file: &Path,
298 is_embedded: bool,
299 gctx: &GlobalContext,
300 warnings: &mut Vec<String>,
301 errors: &mut Vec<String>,
302) -> CargoResult<manifest::TomlManifest> {
303 let package_root = manifest_file.parent().unwrap();
304
305 let inherit_cell: LazyCell<InheritableFields> = LazyCell::new();
306 let inherit = || {
307 inherit_cell
308 .try_borrow_with(|| load_inheritable_fields(gctx, manifest_file, &workspace_config))
309 };
310 let workspace_root = || inherit().map(|fields| fields.ws_root().as_path());
311
312 let mut normalized_toml = manifest::TomlManifest {
313 cargo_features: original_toml.cargo_features.clone(),
314 package: None,
315 project: None,
316 badges: None,
317 features: None,
318 lib: None,
319 bin: None,
320 example: None,
321 test: None,
322 bench: None,
323 dependencies: None,
324 dev_dependencies: None,
325 dev_dependencies2: None,
326 build_dependencies: None,
327 build_dependencies2: None,
328 target: None,
329 lints: None,
330 hints: None,
331 workspace: original_toml.workspace.clone().or_else(|| {
332 is_embedded.then(manifest::TomlWorkspace::default)
334 }),
335 profile: original_toml.profile.clone(),
336 patch: normalize_patch(
337 gctx,
338 original_toml.patch.as_ref(),
339 &workspace_root,
340 features,
341 )?,
342 replace: original_toml.replace.clone(),
343 _unused_keys: Default::default(),
344 };
345
346 if let Some(original_package) = original_toml.package().map(Cow::Borrowed).or_else(|| {
347 if is_embedded {
348 Some(Cow::Owned(Box::new(manifest::TomlPackage::default())))
349 } else {
350 None
351 }
352 }) {
353 let normalized_package = normalize_package_toml(
354 &original_package,
355 manifest_file,
356 is_embedded,
357 gctx,
358 &inherit,
359 features,
360 )?;
361 let package_name = &normalized_package
362 .normalized_name()
363 .expect("previously normalized")
364 .clone();
365 let edition = normalized_package
366 .normalized_edition()
367 .expect("previously normalized")
368 .map_or(Edition::default(), |e| {
369 Edition::from_str(&e).unwrap_or_default()
370 });
371 normalized_toml.package = Some(normalized_package);
372
373 normalized_toml.features = normalize_features(original_toml.features.as_ref())?;
374
375 let auto_embedded = is_embedded.then_some(false);
376 normalized_toml.lib = targets::normalize_lib(
377 original_toml.lib.as_ref(),
378 package_root,
379 package_name,
380 edition,
381 original_package.autolib.or(auto_embedded),
382 warnings,
383 )?;
384 let original_toml_bin = if is_embedded {
385 let manifest_file_stem = manifest_file
386 .file_stem()
387 .expect("file name enforced previously");
388 let name = embedded::sanitize_name(manifest_file_stem.to_string_lossy().as_ref());
389 let manifest_file_name = manifest_file
390 .file_name()
391 .expect("file name enforced previously");
392 let path = PathBuf::from(manifest_file_name);
393 Cow::Owned(Some(vec![manifest::TomlBinTarget {
394 name: Some(name),
395 crate_type: None,
396 crate_type2: None,
397 path: Some(manifest::PathValue(path)),
398 filename: None,
399 test: None,
400 doctest: None,
401 bench: None,
402 doc: None,
403 doc_scrape_examples: None,
404 proc_macro: None,
405 proc_macro2: None,
406 harness: None,
407 required_features: None,
408 edition: None,
409 }]))
410 } else {
411 Cow::Borrowed(&original_toml.bin)
412 };
413 normalized_toml.bin = Some(targets::normalize_bins(
414 original_toml_bin.as_ref().as_ref(),
415 package_root,
416 package_name,
417 edition,
418 original_package.autobins.or(auto_embedded),
419 warnings,
420 errors,
421 normalized_toml.lib.is_some(),
422 )?);
423 normalized_toml.example = Some(targets::normalize_examples(
424 original_toml.example.as_ref(),
425 package_root,
426 edition,
427 original_package.autoexamples.or(auto_embedded),
428 warnings,
429 errors,
430 )?);
431 normalized_toml.test = Some(targets::normalize_tests(
432 original_toml.test.as_ref(),
433 package_root,
434 edition,
435 original_package.autotests.or(auto_embedded),
436 warnings,
437 errors,
438 )?);
439 normalized_toml.bench = Some(targets::normalize_benches(
440 original_toml.bench.as_ref(),
441 package_root,
442 edition,
443 original_package.autobenches.or(auto_embedded),
444 warnings,
445 errors,
446 )?);
447
448 normalized_toml.dependencies = normalize_dependencies(
449 gctx,
450 edition,
451 &features,
452 original_toml.dependencies.as_ref(),
453 None,
454 &inherit,
455 &workspace_root,
456 package_root,
457 warnings,
458 )?;
459 deprecated_underscore(
460 &original_toml.dev_dependencies2,
461 &original_toml.dev_dependencies,
462 "dev-dependencies",
463 package_name,
464 "package",
465 edition,
466 warnings,
467 )?;
468 normalized_toml.dev_dependencies = normalize_dependencies(
469 gctx,
470 edition,
471 &features,
472 original_toml.dev_dependencies(),
473 Some(DepKind::Development),
474 &inherit,
475 &workspace_root,
476 package_root,
477 warnings,
478 )?;
479 deprecated_underscore(
480 &original_toml.build_dependencies2,
481 &original_toml.build_dependencies,
482 "build-dependencies",
483 package_name,
484 "package",
485 edition,
486 warnings,
487 )?;
488 normalized_toml.build_dependencies = normalize_dependencies(
489 gctx,
490 edition,
491 &features,
492 original_toml.build_dependencies(),
493 Some(DepKind::Build),
494 &inherit,
495 &workspace_root,
496 package_root,
497 warnings,
498 )?;
499 let mut normalized_target = BTreeMap::new();
500 for (name, platform) in original_toml.target.iter().flatten() {
501 let normalized_dependencies = normalize_dependencies(
502 gctx,
503 edition,
504 &features,
505 platform.dependencies.as_ref(),
506 None,
507 &inherit,
508 &workspace_root,
509 package_root,
510 warnings,
511 )?;
512 deprecated_underscore(
513 &platform.dev_dependencies2,
514 &platform.dev_dependencies,
515 "dev-dependencies",
516 name,
517 "platform target",
518 edition,
519 warnings,
520 )?;
521 let normalized_dev_dependencies = normalize_dependencies(
522 gctx,
523 edition,
524 &features,
525 platform.dev_dependencies(),
526 Some(DepKind::Development),
527 &inherit,
528 &workspace_root,
529 package_root,
530 warnings,
531 )?;
532 deprecated_underscore(
533 &platform.build_dependencies2,
534 &platform.build_dependencies,
535 "build-dependencies",
536 name,
537 "platform target",
538 edition,
539 warnings,
540 )?;
541 let normalized_build_dependencies = normalize_dependencies(
542 gctx,
543 edition,
544 &features,
545 platform.build_dependencies(),
546 Some(DepKind::Build),
547 &inherit,
548 &workspace_root,
549 package_root,
550 warnings,
551 )?;
552 normalized_target.insert(
553 name.clone(),
554 manifest::TomlPlatform {
555 dependencies: normalized_dependencies,
556 build_dependencies: normalized_build_dependencies,
557 build_dependencies2: None,
558 dev_dependencies: normalized_dev_dependencies,
559 dev_dependencies2: None,
560 },
561 );
562 }
563 normalized_toml.target = (!normalized_target.is_empty()).then_some(normalized_target);
564
565 let normalized_lints = original_toml
566 .lints
567 .clone()
568 .map(|value| lints_inherit_with(value, || inherit()?.lints()))
569 .transpose()?;
570 normalized_toml.lints = normalized_lints.map(|lints| manifest::InheritableLints {
571 workspace: false,
572 lints,
573 });
574
575 normalized_toml.hints = original_toml.hints.clone();
576
577 normalized_toml.badges = original_toml.badges.clone();
578 } else {
579 if let Some(field) = original_toml.requires_package().next() {
580 bail!("this virtual manifest specifies a `{field}` section, which is not allowed");
581 }
582 }
583
584 Ok(normalized_toml)
585}
586
587fn normalize_patch<'a>(
588 gctx: &GlobalContext,
589 original_patch: Option<&BTreeMap<String, BTreeMap<PackageName, TomlDependency>>>,
590 workspace_root: &dyn Fn() -> CargoResult<&'a Path>,
591 features: &Features,
592) -> CargoResult<Option<BTreeMap<String, BTreeMap<PackageName, TomlDependency>>>> {
593 if let Some(patch) = original_patch {
594 let mut normalized_patch = BTreeMap::new();
595 for (name, packages) in patch {
596 let mut normalized_packages = BTreeMap::new();
597 for (pkg, dep) in packages {
598 let dep = if let TomlDependency::Detailed(dep) = dep {
599 let mut dep = dep.clone();
600 normalize_path_dependency(gctx, &mut dep, workspace_root, features)
601 .with_context(|| {
602 format!("resolving path for patch of ({pkg}) for source ({name})")
603 })?;
604 TomlDependency::Detailed(dep)
605 } else {
606 dep.clone()
607 };
608 normalized_packages.insert(pkg.clone(), dep);
609 }
610 normalized_patch.insert(name.clone(), normalized_packages);
611 }
612 Ok(Some(normalized_patch))
613 } else {
614 Ok(None)
615 }
616}
617
618#[tracing::instrument(skip_all)]
619fn normalize_package_toml<'a>(
620 original_package: &manifest::TomlPackage,
621 manifest_file: &Path,
622 is_embedded: bool,
623 gctx: &GlobalContext,
624 inherit: &dyn Fn() -> CargoResult<&'a InheritableFields>,
625 features: &Features,
626) -> CargoResult<Box<manifest::TomlPackage>> {
627 let package_root = manifest_file.parent().unwrap();
628
629 let edition = original_package
630 .edition
631 .clone()
632 .map(|value| field_inherit_with(value, "edition", || inherit()?.edition()))
633 .transpose()?
634 .map(manifest::InheritableField::Value)
635 .or_else(|| {
636 if is_embedded {
637 const DEFAULT_EDITION: crate::core::features::Edition =
638 crate::core::features::Edition::LATEST_STABLE;
639 let _ = gctx.shell().warn(format_args!(
640 "`package.edition` is unspecified, defaulting to `{}`",
641 DEFAULT_EDITION
642 ));
643 Some(manifest::InheritableField::Value(
644 DEFAULT_EDITION.to_string(),
645 ))
646 } else {
647 None
648 }
649 });
650 let rust_version = original_package
651 .rust_version
652 .clone()
653 .map(|value| field_inherit_with(value, "rust-version", || inherit()?.rust_version()))
654 .transpose()?
655 .map(manifest::InheritableField::Value);
656 let name = Some(
657 original_package
658 .name
659 .clone()
660 .or_else(|| {
661 if is_embedded {
662 let file_stem = manifest_file
663 .file_stem()
664 .expect("file name enforced previously")
665 .to_string_lossy();
666 let name = embedded::sanitize_name(file_stem.as_ref());
667 let name =
668 manifest::PackageName::new(name).expect("sanitize made the name valid");
669 Some(name)
670 } else {
671 None
672 }
673 })
674 .ok_or_else(|| anyhow::format_err!("missing field `package.name`"))?,
675 );
676 let version = original_package
677 .version
678 .clone()
679 .map(|value| field_inherit_with(value, "version", || inherit()?.version()))
680 .transpose()?
681 .map(manifest::InheritableField::Value);
682 let authors = original_package
683 .authors
684 .clone()
685 .map(|value| field_inherit_with(value, "authors", || inherit()?.authors()))
686 .transpose()?
687 .map(manifest::InheritableField::Value);
688 let build = if is_embedded {
689 Some(TomlPackageBuild::Auto(false))
690 } else {
691 if let Some(TomlPackageBuild::MultipleScript(_)) = original_package.build {
692 features.require(Feature::multiple_build_scripts())?;
693 }
694 targets::normalize_build(original_package.build.as_ref(), package_root)?
695 };
696 let metabuild = original_package.metabuild.clone();
697 let default_target = original_package.default_target.clone();
698 let forced_target = original_package.forced_target.clone();
699 let links = original_package.links.clone();
700 let exclude = original_package
701 .exclude
702 .clone()
703 .map(|value| field_inherit_with(value, "exclude", || inherit()?.exclude()))
704 .transpose()?
705 .map(manifest::InheritableField::Value);
706 let include = original_package
707 .include
708 .clone()
709 .map(|value| field_inherit_with(value, "include", || inherit()?.include()))
710 .transpose()?
711 .map(manifest::InheritableField::Value);
712 let publish = original_package
713 .publish
714 .clone()
715 .map(|value| field_inherit_with(value, "publish", || inherit()?.publish()))
716 .transpose()?
717 .map(manifest::InheritableField::Value);
718 let workspace = original_package.workspace.clone();
719 let im_a_teapot = original_package.im_a_teapot.clone();
720 let autolib = Some(false);
721 let autobins = Some(false);
722 let autoexamples = Some(false);
723 let autotests = Some(false);
724 let autobenches = Some(false);
725 let default_run = original_package.default_run.clone();
726 let description = original_package
727 .description
728 .clone()
729 .map(|value| field_inherit_with(value, "description", || inherit()?.description()))
730 .transpose()?
731 .map(manifest::InheritableField::Value);
732 let homepage = original_package
733 .homepage
734 .clone()
735 .map(|value| field_inherit_with(value, "homepage", || inherit()?.homepage()))
736 .transpose()?
737 .map(manifest::InheritableField::Value);
738 let documentation = original_package
739 .documentation
740 .clone()
741 .map(|value| field_inherit_with(value, "documentation", || inherit()?.documentation()))
742 .transpose()?
743 .map(manifest::InheritableField::Value);
744 let readme = normalize_package_readme(
745 package_root,
746 original_package
747 .readme
748 .clone()
749 .map(|value| field_inherit_with(value, "readme", || inherit()?.readme(package_root)))
750 .transpose()?
751 .as_ref(),
752 )
753 .map(|s| manifest::InheritableField::Value(StringOrBool::String(s)))
754 .or(Some(manifest::InheritableField::Value(StringOrBool::Bool(
755 false,
756 ))));
757 let keywords = original_package
758 .keywords
759 .clone()
760 .map(|value| field_inherit_with(value, "keywords", || inherit()?.keywords()))
761 .transpose()?
762 .map(manifest::InheritableField::Value);
763 let categories = original_package
764 .categories
765 .clone()
766 .map(|value| field_inherit_with(value, "categories", || inherit()?.categories()))
767 .transpose()?
768 .map(manifest::InheritableField::Value);
769 let license = original_package
770 .license
771 .clone()
772 .map(|value| field_inherit_with(value, "license", || inherit()?.license()))
773 .transpose()?
774 .map(manifest::InheritableField::Value);
775 let license_file = original_package
776 .license_file
777 .clone()
778 .map(|value| {
779 field_inherit_with(value, "license-file", || {
780 inherit()?.license_file(package_root)
781 })
782 })
783 .transpose()?
784 .map(manifest::InheritableField::Value);
785 let repository = original_package
786 .repository
787 .clone()
788 .map(|value| field_inherit_with(value, "repository", || inherit()?.repository()))
789 .transpose()?
790 .map(manifest::InheritableField::Value);
791 let resolver = original_package.resolver.clone();
792 let metadata = original_package.metadata.clone();
793
794 let normalized_package = manifest::TomlPackage {
795 edition,
796 rust_version,
797 name,
798 version,
799 authors,
800 build,
801 metabuild,
802 default_target,
803 forced_target,
804 links,
805 exclude,
806 include,
807 publish,
808 workspace,
809 im_a_teapot,
810 autolib,
811 autobins,
812 autoexamples,
813 autotests,
814 autobenches,
815 default_run,
816 description,
817 homepage,
818 documentation,
819 readme,
820 keywords,
821 categories,
822 license,
823 license_file,
824 repository,
825 resolver,
826 metadata,
827 _invalid_cargo_features: Default::default(),
828 };
829
830 Ok(Box::new(normalized_package))
831}
832
833fn normalize_package_readme(
835 package_root: &Path,
836 readme: Option<&manifest::StringOrBool>,
837) -> Option<String> {
838 match &readme {
839 None => default_readme_from_package_root(package_root),
840 Some(value) => match value {
841 manifest::StringOrBool::Bool(false) => None,
842 manifest::StringOrBool::Bool(true) => Some("README.md".to_string()),
843 manifest::StringOrBool::String(v) => Some(v.clone()),
844 },
845 }
846}
847
848const DEFAULT_README_FILES: [&str; 3] = ["README.md", "README.txt", "README"];
849
850fn default_readme_from_package_root(package_root: &Path) -> Option<String> {
853 for &readme_filename in DEFAULT_README_FILES.iter() {
854 if package_root.join(readme_filename).is_file() {
855 return Some(readme_filename.to_string());
856 }
857 }
858
859 None
860}
861
862#[tracing::instrument(skip_all)]
863fn normalize_features(
864 original_features: Option<&BTreeMap<manifest::FeatureName, Vec<String>>>,
865) -> CargoResult<Option<BTreeMap<manifest::FeatureName, Vec<String>>>> {
866 let Some(normalized_features) = original_features.cloned() else {
867 return Ok(None);
868 };
869
870 Ok(Some(normalized_features))
871}
872
873#[tracing::instrument(skip_all)]
874fn normalize_dependencies<'a>(
875 gctx: &GlobalContext,
876 edition: Edition,
877 features: &Features,
878 orig_deps: Option<&BTreeMap<manifest::PackageName, manifest::InheritableDependency>>,
879 kind: Option<DepKind>,
880 inherit: &dyn Fn() -> CargoResult<&'a InheritableFields>,
881 workspace_root: &dyn Fn() -> CargoResult<&'a Path>,
882 package_root: &Path,
883 warnings: &mut Vec<String>,
884) -> CargoResult<Option<BTreeMap<manifest::PackageName, manifest::InheritableDependency>>> {
885 let Some(dependencies) = orig_deps else {
886 return Ok(None);
887 };
888
889 let mut deps = BTreeMap::new();
890 for (name_in_toml, v) in dependencies.iter() {
891 let mut resolved = dependency_inherit_with(
892 v.clone(),
893 name_in_toml,
894 inherit,
895 package_root,
896 edition,
897 warnings,
898 )?;
899 if let manifest::TomlDependency::Detailed(ref mut d) = resolved {
900 deprecated_underscore(
901 &d.default_features2,
902 &d.default_features,
903 "default-features",
904 name_in_toml,
905 "dependency",
906 edition,
907 warnings,
908 )?;
909 if d.public.is_some() {
910 let with_public_feature = features.require(Feature::public_dependency()).is_ok();
911 let with_z_public = gctx.cli_unstable().public_dependency;
912 if matches!(kind, None) {
913 if !with_public_feature && !with_z_public {
914 d.public = None;
915 warnings.push(format!(
916 "ignoring `public` on dependency {name_in_toml}, pass `-Zpublic-dependency` to enable support for it"
917 ))
918 }
919 } else {
920 let kind_name = match kind {
921 Some(k) => k.kind_table(),
922 None => "dependencies",
923 };
924 let hint = format!(
925 "'public' specifier can only be used on regular dependencies, not {kind_name}",
926 );
927 if with_public_feature || with_z_public {
928 bail!(hint)
929 } else {
930 warnings.push(hint);
932 d.public = None;
933 }
934 }
935 }
936 normalize_path_dependency(gctx, d, workspace_root, features)
937 .with_context(|| format!("resolving path dependency {name_in_toml}"))?;
938 }
939
940 deps.insert(
941 name_in_toml.clone(),
942 manifest::InheritableDependency::Value(resolved.clone()),
943 );
944 }
945 Ok(Some(deps))
946}
947
948fn normalize_path_dependency<'a>(
949 gctx: &GlobalContext,
950 detailed_dep: &mut TomlDetailedDependency,
951 workspace_root: &dyn Fn() -> CargoResult<&'a Path>,
952 features: &Features,
953) -> CargoResult<()> {
954 if let Some(base) = detailed_dep.base.take() {
955 if let Some(path) = detailed_dep.path.as_mut() {
956 let new_path = lookup_path_base(&base, gctx, workspace_root, features)?.join(&path);
957 *path = new_path.to_str().unwrap().to_string();
958 } else {
959 bail!("`base` can only be used with path dependencies");
960 }
961 }
962 Ok(())
963}
964
965fn load_inheritable_fields(
966 gctx: &GlobalContext,
967 normalized_path: &Path,
968 workspace_config: &WorkspaceConfig,
969) -> CargoResult<InheritableFields> {
970 match workspace_config {
971 WorkspaceConfig::Root(root) => Ok(root.inheritable().clone()),
972 WorkspaceConfig::Member {
973 root: Some(path_to_root),
974 } => {
975 let path = normalized_path
976 .parent()
977 .unwrap()
978 .join(path_to_root)
979 .join("Cargo.toml");
980 let root_path = paths::normalize_path(&path);
981 inheritable_from_path(gctx, root_path)
982 }
983 WorkspaceConfig::Member { root: None } => {
984 match find_workspace_root(&normalized_path, gctx)? {
985 Some(path_to_root) => inheritable_from_path(gctx, path_to_root),
986 None => Err(anyhow!("failed to find a workspace root")),
987 }
988 }
989 }
990}
991
992fn inheritable_from_path(
993 gctx: &GlobalContext,
994 workspace_path: PathBuf,
995) -> CargoResult<InheritableFields> {
996 let workspace_path_root = workspace_path.parent().unwrap();
998
999 if let Some(ws_root) = gctx.ws_roots.borrow().get(workspace_path_root) {
1002 return Ok(ws_root.inheritable().clone());
1003 };
1004
1005 let source_id = SourceId::for_manifest_path(&workspace_path)?;
1006 let man = read_manifest(&workspace_path, source_id, gctx)?;
1007 match man.workspace_config() {
1008 WorkspaceConfig::Root(root) => {
1009 gctx.ws_roots
1010 .borrow_mut()
1011 .insert(workspace_path, root.clone());
1012 Ok(root.inheritable().clone())
1013 }
1014 _ => bail!(
1015 "root of a workspace inferred but wasn't a root: {}",
1016 workspace_path.display()
1017 ),
1018 }
1019}
1020
1021macro_rules! package_field_getter {
1023 ( $(($key:literal, $field:ident -> $ret:ty),)* ) => (
1024 $(
1025 #[doc = concat!("Gets the field `workspace.package.", $key, "`.")]
1026 fn $field(&self) -> CargoResult<$ret> {
1027 let Some(val) = self.package.as_ref().and_then(|p| p.$field.as_ref()) else {
1028 bail!("`workspace.package.{}` was not defined", $key);
1029 };
1030 Ok(val.clone())
1031 }
1032 )*
1033 )
1034}
1035
1036#[derive(Clone, Debug, Default)]
1038pub struct InheritableFields {
1039 package: Option<manifest::InheritablePackage>,
1040 dependencies: Option<BTreeMap<manifest::PackageName, manifest::TomlDependency>>,
1041 lints: Option<manifest::TomlLints>,
1042
1043 _ws_root: PathBuf,
1045}
1046
1047impl InheritableFields {
1048 package_field_getter! {
1049 ("authors", authors -> Vec<String>),
1051 ("categories", categories -> Vec<String>),
1052 ("description", description -> String),
1053 ("documentation", documentation -> String),
1054 ("edition", edition -> String),
1055 ("exclude", exclude -> Vec<String>),
1056 ("homepage", homepage -> String),
1057 ("include", include -> Vec<String>),
1058 ("keywords", keywords -> Vec<String>),
1059 ("license", license -> String),
1060 ("publish", publish -> manifest::VecStringOrBool),
1061 ("repository", repository -> String),
1062 ("rust-version", rust_version -> RustVersion),
1063 ("version", version -> semver::Version),
1064 }
1065
1066 fn get_dependency(
1068 &self,
1069 name: &str,
1070 package_root: &Path,
1071 ) -> CargoResult<manifest::TomlDependency> {
1072 let Some(deps) = &self.dependencies else {
1073 bail!("`workspace.dependencies` was not defined");
1074 };
1075 let Some(dep) = deps.get(name) else {
1076 bail!("`dependency.{name}` was not found in `workspace.dependencies`");
1077 };
1078 let mut dep = dep.clone();
1079 if let manifest::TomlDependency::Detailed(detailed) = &mut dep {
1080 if detailed.base.is_none() {
1081 if let Some(rel_path) = &detailed.path {
1084 detailed.path = Some(resolve_relative_path(
1085 name,
1086 self.ws_root(),
1087 package_root,
1088 rel_path,
1089 )?);
1090 }
1091 }
1092 }
1093 Ok(dep)
1094 }
1095
1096 pub fn lints(&self) -> CargoResult<manifest::TomlLints> {
1098 let Some(val) = &self.lints else {
1099 bail!("`workspace.lints` was not defined");
1100 };
1101 Ok(val.clone())
1102 }
1103
1104 fn license_file(&self, package_root: &Path) -> CargoResult<String> {
1106 let Some(license_file) = self.package.as_ref().and_then(|p| p.license_file.as_ref()) else {
1107 bail!("`workspace.package.license-file` was not defined");
1108 };
1109 resolve_relative_path("license-file", &self._ws_root, package_root, license_file)
1110 }
1111
1112 fn readme(&self, package_root: &Path) -> CargoResult<manifest::StringOrBool> {
1114 let Some(readme) = normalize_package_readme(
1115 self._ws_root.as_path(),
1116 self.package.as_ref().and_then(|p| p.readme.as_ref()),
1117 ) else {
1118 bail!("`workspace.package.readme` was not defined");
1119 };
1120 resolve_relative_path("readme", &self._ws_root, package_root, &readme)
1121 .map(manifest::StringOrBool::String)
1122 }
1123
1124 fn ws_root(&self) -> &PathBuf {
1125 &self._ws_root
1126 }
1127}
1128
1129fn field_inherit_with<'a, T>(
1130 field: manifest::InheritableField<T>,
1131 label: &str,
1132 get_ws_inheritable: impl FnOnce() -> CargoResult<T>,
1133) -> CargoResult<T> {
1134 match field {
1135 manifest::InheritableField::Value(value) => Ok(value),
1136 manifest::InheritableField::Inherit(_) => get_ws_inheritable().with_context(|| {
1137 format!(
1138 "error inheriting `{label}` from workspace root manifest's `workspace.package.{label}`",
1139 )
1140 }),
1141 }
1142}
1143
1144fn lints_inherit_with(
1145 lints: manifest::InheritableLints,
1146 get_ws_inheritable: impl FnOnce() -> CargoResult<manifest::TomlLints>,
1147) -> CargoResult<manifest::TomlLints> {
1148 if lints.workspace {
1149 if !lints.lints.is_empty() {
1150 anyhow::bail!(
1151 "cannot override `workspace.lints` in `lints`, either remove the overrides or `lints.workspace = true` and manually specify the lints"
1152 );
1153 }
1154 get_ws_inheritable().with_context(
1155 || "error inheriting `lints` from workspace root manifest's `workspace.lints`",
1156 )
1157 } else {
1158 Ok(lints.lints)
1159 }
1160}
1161
1162fn dependency_inherit_with<'a>(
1163 dependency: manifest::InheritableDependency,
1164 name: &str,
1165 inherit: &dyn Fn() -> CargoResult<&'a InheritableFields>,
1166 package_root: &Path,
1167 edition: Edition,
1168 warnings: &mut Vec<String>,
1169) -> CargoResult<manifest::TomlDependency> {
1170 match dependency {
1171 manifest::InheritableDependency::Value(value) => Ok(value),
1172 manifest::InheritableDependency::Inherit(w) => {
1173 inner_dependency_inherit_with(w, name, inherit, package_root, edition, warnings).with_context(|| {
1174 format!(
1175 "error inheriting `{name}` from workspace root manifest's `workspace.dependencies.{name}`",
1176 )
1177 })
1178 }
1179 }
1180}
1181
1182fn inner_dependency_inherit_with<'a>(
1183 pkg_dep: manifest::TomlInheritedDependency,
1184 name: &str,
1185 inherit: &dyn Fn() -> CargoResult<&'a InheritableFields>,
1186 package_root: &Path,
1187 edition: Edition,
1188 warnings: &mut Vec<String>,
1189) -> CargoResult<manifest::TomlDependency> {
1190 let ws_dep = inherit()?.get_dependency(name, package_root)?;
1191 let mut merged_dep = match ws_dep {
1192 manifest::TomlDependency::Simple(ws_version) => manifest::TomlDetailedDependency {
1193 version: Some(ws_version),
1194 ..Default::default()
1195 },
1196 manifest::TomlDependency::Detailed(ws_dep) => ws_dep.clone(),
1197 };
1198 let manifest::TomlInheritedDependency {
1199 workspace: _,
1200
1201 features,
1202 optional,
1203 default_features,
1204 default_features2,
1205 public,
1206
1207 _unused_keys: _,
1208 } = &pkg_dep;
1209 let default_features = default_features.or(*default_features2);
1210
1211 match (default_features, merged_dep.default_features()) {
1212 (Some(true), Some(false)) => {
1216 merged_dep.default_features = Some(true);
1217 }
1218 (Some(false), Some(true)) => {
1222 deprecated_ws_default_features(name, Some(true), edition, warnings)?;
1223 }
1224 (Some(false), None) => {
1227 deprecated_ws_default_features(name, None, edition, warnings)?;
1228 }
1229 _ => {}
1230 }
1231 merged_dep.features = match (merged_dep.features.clone(), features.clone()) {
1232 (Some(dep_feat), Some(inherit_feat)) => Some(
1233 dep_feat
1234 .into_iter()
1235 .chain(inherit_feat)
1236 .collect::<Vec<String>>(),
1237 ),
1238 (Some(dep_fet), None) => Some(dep_fet),
1239 (None, Some(inherit_feat)) => Some(inherit_feat),
1240 (None, None) => None,
1241 };
1242 merged_dep.optional = *optional;
1243 merged_dep.public = *public;
1244 Ok(manifest::TomlDependency::Detailed(merged_dep))
1245}
1246
1247fn deprecated_ws_default_features(
1248 label: &str,
1249 ws_def_feat: Option<bool>,
1250 edition: Edition,
1251 warnings: &mut Vec<String>,
1252) -> CargoResult<()> {
1253 let ws_def_feat = match ws_def_feat {
1254 Some(true) => "true",
1255 Some(false) => "false",
1256 None => "not specified",
1257 };
1258 if Edition::Edition2024 <= edition {
1259 anyhow::bail!("`default-features = false` cannot override workspace's `default-features`");
1260 } else {
1261 warnings.push(format!(
1262 "`default-features` is ignored for {label}, since `default-features` was \
1263 {ws_def_feat} for `workspace.dependencies.{label}`, \
1264 this could become a hard error in the future"
1265 ));
1266 }
1267 Ok(())
1268}
1269
1270#[tracing::instrument(skip_all)]
1271pub fn to_real_manifest(
1272 contents: String,
1273 document: toml::Spanned<toml::de::DeTable<'static>>,
1274 original_toml: manifest::TomlManifest,
1275 normalized_toml: manifest::TomlManifest,
1276 features: Features,
1277 workspace_config: WorkspaceConfig,
1278 source_id: SourceId,
1279 manifest_file: &Path,
1280 is_embedded: bool,
1281 gctx: &GlobalContext,
1282 warnings: &mut Vec<String>,
1283 _errors: &mut Vec<String>,
1284) -> CargoResult<Manifest> {
1285 let package_root = manifest_file.parent().unwrap();
1286 if !package_root.is_dir() {
1287 bail!(
1288 "package root '{}' is not a directory",
1289 package_root.display()
1290 );
1291 };
1292
1293 let normalized_package = normalized_toml
1294 .package()
1295 .expect("previously verified to have a `[package]`");
1296 let package_name = normalized_package
1297 .normalized_name()
1298 .expect("previously normalized");
1299 if package_name.contains(':') {
1300 features.require(Feature::open_namespaces())?;
1301 }
1302 let rust_version = normalized_package
1303 .normalized_rust_version()
1304 .expect("previously normalized")
1305 .cloned();
1306
1307 let edition = if let Some(edition) = normalized_package
1308 .normalized_edition()
1309 .expect("previously normalized")
1310 {
1311 let edition: Edition = edition
1312 .parse()
1313 .context("failed to parse the `edition` key")?;
1314 if let Some(pkg_msrv) = &rust_version {
1315 if let Some(edition_msrv) = edition.first_version() {
1316 let edition_msrv = RustVersion::try_from(edition_msrv).unwrap();
1317 if !edition_msrv.is_compatible_with(pkg_msrv.as_partial()) {
1318 bail!(
1319 "rust-version {} is older than first version ({}) required by \
1320 the specified edition ({})",
1321 pkg_msrv,
1322 edition_msrv,
1323 edition,
1324 )
1325 }
1326 }
1327 }
1328 edition
1329 } else {
1330 let msrv_edition = if let Some(pkg_msrv) = &rust_version {
1331 Edition::ALL
1332 .iter()
1333 .filter(|e| {
1334 e.first_version()
1335 .map(|e| {
1336 let e = RustVersion::try_from(e).unwrap();
1337 e.is_compatible_with(pkg_msrv.as_partial())
1338 })
1339 .unwrap_or_default()
1340 })
1341 .max()
1342 .copied()
1343 } else {
1344 None
1345 }
1346 .unwrap_or_default();
1347 let default_edition = Edition::default();
1348 let latest_edition = Edition::LATEST_STABLE;
1349
1350 if msrv_edition != default_edition || rust_version.is_none() {
1355 let tip = if msrv_edition == latest_edition || rust_version.is_none() {
1356 format!(" while the latest is {latest_edition}")
1357 } else {
1358 format!(" while {msrv_edition} is compatible with `rust-version`")
1359 };
1360 warnings.push(format!(
1361 "no edition set: defaulting to the {default_edition} edition{tip}",
1362 ));
1363 }
1364 default_edition
1365 };
1366 if !edition.is_stable() {
1367 features.require(Feature::unstable_editions())?;
1368 }
1369
1370 if original_toml.project.is_some() {
1371 if Edition::Edition2024 <= edition {
1372 anyhow::bail!(
1373 "`[project]` is not supported as of the 2024 Edition, please use `[package]`"
1374 );
1375 } else {
1376 warnings.push(format!("`[project]` is deprecated in favor of `[package]`"));
1377 }
1378 }
1379
1380 if normalized_package.metabuild.is_some() {
1381 features.require(Feature::metabuild())?;
1382 }
1383
1384 if is_embedded {
1385 let invalid_fields = [
1386 ("`workspace`", original_toml.workspace.is_some()),
1387 ("`lib`", original_toml.lib.is_some()),
1388 ("`bin`", original_toml.bin.is_some()),
1389 ("`example`", original_toml.example.is_some()),
1390 ("`test`", original_toml.test.is_some()),
1391 ("`bench`", original_toml.bench.is_some()),
1392 (
1393 "`package.workspace`",
1394 original_toml
1395 .package()
1396 .map(|p| p.workspace.is_some())
1397 .unwrap_or(false),
1398 ),
1399 (
1400 "`package.build`",
1401 original_toml
1402 .package()
1403 .map(|p| p.build.is_some())
1404 .unwrap_or(false),
1405 ),
1406 (
1407 "`package.links`",
1408 original_toml
1409 .package()
1410 .map(|p| p.links.is_some())
1411 .unwrap_or(false),
1412 ),
1413 (
1414 "`package.autolib`",
1415 original_toml
1416 .package()
1417 .map(|p| p.autolib.is_some())
1418 .unwrap_or(false),
1419 ),
1420 (
1421 "`package.autobins`",
1422 original_toml
1423 .package()
1424 .map(|p| p.autobins.is_some())
1425 .unwrap_or(false),
1426 ),
1427 (
1428 "`package.autoexamples`",
1429 original_toml
1430 .package()
1431 .map(|p| p.autoexamples.is_some())
1432 .unwrap_or(false),
1433 ),
1434 (
1435 "`package.autotests`",
1436 original_toml
1437 .package()
1438 .map(|p| p.autotests.is_some())
1439 .unwrap_or(false),
1440 ),
1441 (
1442 "`package.autobenches`",
1443 original_toml
1444 .package()
1445 .map(|p| p.autobenches.is_some())
1446 .unwrap_or(false),
1447 ),
1448 ];
1449 let invalid_fields = invalid_fields
1450 .into_iter()
1451 .filter_map(|(name, invalid)| invalid.then_some(name))
1452 .collect::<Vec<_>>();
1453 if !invalid_fields.is_empty() {
1454 let fields = invalid_fields.join(", ");
1455 let are = if invalid_fields.len() == 1 {
1456 "is"
1457 } else {
1458 "are"
1459 };
1460 anyhow::bail!("{fields} {are} not allowed in embedded manifests")
1461 }
1462 }
1463
1464 let resolve_behavior = match (
1465 normalized_package.resolver.as_ref(),
1466 normalized_toml
1467 .workspace
1468 .as_ref()
1469 .and_then(|ws| ws.resolver.as_ref()),
1470 ) {
1471 (None, None) => None,
1472 (Some(s), None) | (None, Some(s)) => Some(ResolveBehavior::from_manifest(s)?),
1473 (Some(_), Some(_)) => {
1474 bail!("cannot specify `resolver` field in both `[workspace]` and `[package]`")
1475 }
1476 };
1477
1478 let targets = to_targets(
1482 &features,
1483 &original_toml,
1484 &normalized_toml,
1485 package_root,
1486 edition,
1487 &normalized_package.metabuild,
1488 warnings,
1489 )?;
1490
1491 if targets.iter().all(|t| t.is_custom_build()) {
1492 bail!(
1493 "no targets specified in the manifest\n\
1494 either src/lib.rs, src/main.rs, a [lib] section, or \
1495 [[bin]] section must be present"
1496 )
1497 }
1498
1499 if let Err(conflict_targets) = unique_build_targets(&targets, package_root) {
1500 conflict_targets
1501 .iter()
1502 .for_each(|(target_path, conflicts)| {
1503 warnings.push(format!(
1504 "file `{}` found to be present in multiple \
1505 build targets:\n{}",
1506 target_path.display(),
1507 conflicts
1508 .iter()
1509 .map(|t| format!(" * `{}` target `{}`", t.kind().description(), t.name(),))
1510 .join("\n")
1511 ));
1512 })
1513 }
1514
1515 if let Some(links) = &normalized_package.links {
1516 if !targets.iter().any(|t| t.is_custom_build()) {
1517 bail!(
1518 "package specifies that it links to `{links}` but does not have a custom build script"
1519 )
1520 }
1521 }
1522
1523 validate_dependencies(original_toml.dependencies.as_ref(), None, None, warnings)?;
1524 validate_dependencies(
1525 original_toml.dev_dependencies(),
1526 None,
1527 Some(DepKind::Development),
1528 warnings,
1529 )?;
1530 validate_dependencies(
1531 original_toml.build_dependencies(),
1532 None,
1533 Some(DepKind::Build),
1534 warnings,
1535 )?;
1536 for (name, platform) in original_toml.target.iter().flatten() {
1537 let platform_kind: Platform = name.parse()?;
1538 platform_kind.check_cfg_attributes(warnings);
1539 platform_kind.check_cfg_keywords(warnings, manifest_file);
1540 let platform_kind = Some(platform_kind);
1541 validate_dependencies(
1542 platform.dependencies.as_ref(),
1543 platform_kind.as_ref(),
1544 None,
1545 warnings,
1546 )?;
1547 validate_dependencies(
1548 platform.build_dependencies(),
1549 platform_kind.as_ref(),
1550 Some(DepKind::Build),
1551 warnings,
1552 )?;
1553 validate_dependencies(
1554 platform.dev_dependencies(),
1555 platform_kind.as_ref(),
1556 Some(DepKind::Development),
1557 warnings,
1558 )?;
1559 }
1560
1561 let mut deps = Vec::new();
1563 let mut manifest_ctx = ManifestContext {
1564 deps: &mut deps,
1565 source_id,
1566 gctx,
1567 warnings,
1568 platform: None,
1569 root: package_root,
1570 };
1571 gather_dependencies(
1572 &mut manifest_ctx,
1573 normalized_toml.dependencies.as_ref(),
1574 None,
1575 )?;
1576 gather_dependencies(
1577 &mut manifest_ctx,
1578 normalized_toml.dev_dependencies(),
1579 Some(DepKind::Development),
1580 )?;
1581 gather_dependencies(
1582 &mut manifest_ctx,
1583 normalized_toml.build_dependencies(),
1584 Some(DepKind::Build),
1585 )?;
1586 for (name, platform) in normalized_toml.target.iter().flatten() {
1587 manifest_ctx.platform = Some(name.parse()?);
1588 gather_dependencies(&mut manifest_ctx, platform.dependencies.as_ref(), None)?;
1589 gather_dependencies(
1590 &mut manifest_ctx,
1591 platform.build_dependencies(),
1592 Some(DepKind::Build),
1593 )?;
1594 gather_dependencies(
1595 &mut manifest_ctx,
1596 platform.dev_dependencies(),
1597 Some(DepKind::Development),
1598 )?;
1599 }
1600 let replace = replace(&normalized_toml, &mut manifest_ctx)?;
1601 let patch = patch(&normalized_toml, &mut manifest_ctx)?;
1602
1603 {
1604 let mut names_sources = BTreeMap::new();
1605 for dep in &deps {
1606 let name = dep.name_in_toml();
1607 let prev = names_sources.insert(name, dep.source_id());
1608 if prev.is_some() && prev != Some(dep.source_id()) {
1609 bail!(
1610 "Dependency '{}' has different source paths depending on the build \
1611 target. Each dependency must have a single canonical source path \
1612 irrespective of build target.",
1613 name
1614 );
1615 }
1616 }
1617 }
1618
1619 verify_lints(
1620 normalized_toml
1621 .normalized_lints()
1622 .expect("previously normalized"),
1623 gctx,
1624 warnings,
1625 )?;
1626 let default = manifest::TomlLints::default();
1627 let rustflags = lints_to_rustflags(
1628 normalized_toml
1629 .normalized_lints()
1630 .expect("previously normalized")
1631 .unwrap_or(&default),
1632 )?;
1633
1634 let hints = normalized_toml.hints.clone();
1635
1636 let metadata = ManifestMetadata {
1637 description: normalized_package
1638 .normalized_description()
1639 .expect("previously normalized")
1640 .cloned(),
1641 homepage: normalized_package
1642 .normalized_homepage()
1643 .expect("previously normalized")
1644 .cloned(),
1645 documentation: normalized_package
1646 .normalized_documentation()
1647 .expect("previously normalized")
1648 .cloned(),
1649 readme: normalized_package
1650 .normalized_readme()
1651 .expect("previously normalized")
1652 .cloned(),
1653 authors: normalized_package
1654 .normalized_authors()
1655 .expect("previously normalized")
1656 .cloned()
1657 .unwrap_or_default(),
1658 license: normalized_package
1659 .normalized_license()
1660 .expect("previously normalized")
1661 .cloned(),
1662 license_file: normalized_package
1663 .normalized_license_file()
1664 .expect("previously normalized")
1665 .cloned(),
1666 repository: normalized_package
1667 .normalized_repository()
1668 .expect("previously normalized")
1669 .cloned(),
1670 keywords: normalized_package
1671 .normalized_keywords()
1672 .expect("previously normalized")
1673 .cloned()
1674 .unwrap_or_default(),
1675 categories: normalized_package
1676 .normalized_categories()
1677 .expect("previously normalized")
1678 .cloned()
1679 .unwrap_or_default(),
1680 badges: normalized_toml.badges.clone().unwrap_or_default(),
1681 links: normalized_package.links.clone(),
1682 rust_version: rust_version.clone(),
1683 };
1684
1685 if let Some(profiles) = &normalized_toml.profile {
1686 let cli_unstable = gctx.cli_unstable();
1687 validate_profiles(profiles, cli_unstable, &features, warnings)?;
1688 }
1689
1690 let version = normalized_package
1691 .normalized_version()
1692 .expect("previously normalized");
1693 let publish = match normalized_package
1694 .normalized_publish()
1695 .expect("previously normalized")
1696 {
1697 Some(manifest::VecStringOrBool::VecString(vecstring)) => Some(vecstring.clone()),
1698 Some(manifest::VecStringOrBool::Bool(false)) => Some(vec![]),
1699 Some(manifest::VecStringOrBool::Bool(true)) => None,
1700 None => version.is_none().then_some(vec![]),
1701 };
1702
1703 if version.is_none() && publish != Some(vec![]) {
1704 bail!("`package.publish` requires `package.version` be specified");
1705 }
1706
1707 let pkgid = PackageId::new(
1708 package_name.as_str().into(),
1709 version
1710 .cloned()
1711 .unwrap_or_else(|| semver::Version::new(0, 0, 0)),
1712 source_id,
1713 );
1714 let summary = {
1715 let summary = Summary::new(
1716 pkgid,
1717 deps,
1718 &normalized_toml
1719 .features
1720 .as_ref()
1721 .unwrap_or(&Default::default())
1722 .iter()
1723 .map(|(k, v)| {
1724 (
1725 k.to_string().into(),
1726 v.iter().map(InternedString::from).collect(),
1727 )
1728 })
1729 .collect(),
1730 normalized_package.links.as_deref(),
1731 rust_version.clone(),
1732 );
1733 if let Err(ref err) = summary {
1736 if let Some(missing_dep) = err.downcast_ref::<MissingDependencyError>() {
1737 missing_dep_diagnostic(
1738 missing_dep,
1739 &original_toml,
1740 &document,
1741 &contents,
1742 manifest_file,
1743 gctx,
1744 )?;
1745 }
1746 }
1747 summary?
1748 };
1749
1750 if summary.features().contains_key("default-features") {
1751 warnings.push(
1752 "`default-features = [\"..\"]` was found in [features]. \
1753 Did you mean to use `default = [\"..\"]`?"
1754 .to_string(),
1755 )
1756 }
1757
1758 if let Some(run) = &normalized_package.default_run {
1759 if !targets
1760 .iter()
1761 .filter(|t| t.is_bin())
1762 .any(|t| t.name() == run)
1763 {
1764 let suggestion = util::closest_msg(
1765 run,
1766 targets.iter().filter(|t| t.is_bin()),
1767 |t| t.name(),
1768 "target",
1769 );
1770 bail!("default-run target `{}` not found{}", run, suggestion);
1771 }
1772 }
1773
1774 let default_kind = normalized_package
1775 .default_target
1776 .as_ref()
1777 .map(|t| CompileTarget::new(&*t))
1778 .transpose()?
1779 .map(CompileKind::Target);
1780 let forced_kind = normalized_package
1781 .forced_target
1782 .as_ref()
1783 .map(|t| CompileTarget::new(&*t))
1784 .transpose()?
1785 .map(CompileKind::Target);
1786 let include = normalized_package
1787 .normalized_include()
1788 .expect("previously normalized")
1789 .cloned()
1790 .unwrap_or_default();
1791 let exclude = normalized_package
1792 .normalized_exclude()
1793 .expect("previously normalized")
1794 .cloned()
1795 .unwrap_or_default();
1796 let links = normalized_package.links.clone();
1797 let custom_metadata = normalized_package.metadata.clone();
1798 let im_a_teapot = normalized_package.im_a_teapot;
1799 let default_run = normalized_package.default_run.clone();
1800 let metabuild = normalized_package.metabuild.clone().map(|sov| sov.0);
1801 let manifest = Manifest::new(
1802 Rc::new(contents),
1803 Rc::new(document),
1804 Rc::new(original_toml),
1805 Rc::new(normalized_toml),
1806 summary,
1807 default_kind,
1808 forced_kind,
1809 targets,
1810 exclude,
1811 include,
1812 links,
1813 metadata,
1814 custom_metadata,
1815 publish,
1816 replace,
1817 patch,
1818 workspace_config,
1819 features,
1820 edition,
1821 rust_version,
1822 im_a_teapot,
1823 default_run,
1824 metabuild,
1825 resolve_behavior,
1826 rustflags,
1827 hints,
1828 is_embedded,
1829 );
1830 if manifest
1831 .normalized_toml()
1832 .package()
1833 .unwrap()
1834 .license_file
1835 .is_some()
1836 && manifest
1837 .normalized_toml()
1838 .package()
1839 .unwrap()
1840 .license
1841 .is_some()
1842 {
1843 warnings.push(
1844 "only one of `license` or `license-file` is necessary\n\
1845 `license` should be used if the package license can be expressed \
1846 with a standard SPDX expression.\n\
1847 `license-file` should be used if the package uses a non-standard license.\n\
1848 See https://doc.rust-lang.org/cargo/reference/manifest.html#the-license-and-license-file-fields \
1849 for more information."
1850 .to_owned(),
1851 );
1852 }
1853 warn_on_unused(&manifest.original_toml()._unused_keys, warnings);
1854
1855 manifest.feature_gate()?;
1856
1857 Ok(manifest)
1858}
1859
1860fn missing_dep_diagnostic(
1861 missing_dep: &MissingDependencyError,
1862 orig_toml: &TomlManifest,
1863 document: &toml::Spanned<toml::de::DeTable<'static>>,
1864 contents: &str,
1865 manifest_file: &Path,
1866 gctx: &GlobalContext,
1867) -> CargoResult<()> {
1868 let dep_name = missing_dep.dep_name;
1869 let manifest_path = rel_cwd_manifest_path(manifest_file, gctx);
1870 let feature_value_span =
1871 get_span(&document, &["features", missing_dep.feature.as_str()], true).unwrap();
1872
1873 let title = format!(
1874 "feature `{}` includes `{}`, but `{}` is not a dependency",
1875 missing_dep.feature, missing_dep.feature_value, &dep_name
1876 );
1877 let help = format!("enable the dependency with `dep:{dep_name}`");
1878 let info_label = format!(
1879 "`{}` is an unused optional dependency since no feature enables it",
1880 &dep_name
1881 );
1882 let message = Level::Error.title(&title);
1883 let snippet = Snippet::source(&contents)
1884 .origin(&manifest_path)
1885 .fold(true)
1886 .annotation(Level::Error.span(feature_value_span.start..feature_value_span.end));
1887 let message = if missing_dep.weak_optional {
1888 let mut orig_deps = vec![
1889 (
1890 orig_toml.dependencies.as_ref(),
1891 vec![DepKind::Normal.kind_table()],
1892 ),
1893 (
1894 orig_toml.build_dependencies.as_ref(),
1895 vec![DepKind::Build.kind_table()],
1896 ),
1897 ];
1898 for (name, platform) in orig_toml.target.iter().flatten() {
1899 orig_deps.push((
1900 platform.dependencies.as_ref(),
1901 vec!["target", name, DepKind::Normal.kind_table()],
1902 ));
1903 orig_deps.push((
1904 platform.build_dependencies.as_ref(),
1905 vec!["target", name, DepKind::Normal.kind_table()],
1906 ));
1907 }
1908
1909 if let Some((_, toml_path)) = orig_deps.iter().find(|(deps, _)| {
1910 if let Some(deps) = deps {
1911 deps.keys().any(|p| *p.as_str() == *dep_name)
1912 } else {
1913 false
1914 }
1915 }) {
1916 let toml_path = toml_path
1917 .iter()
1918 .map(|s| *s)
1919 .chain(std::iter::once(dep_name.as_str()))
1920 .collect::<Vec<_>>();
1921 let dep_span = get_span(&document, &toml_path, false).unwrap();
1922
1923 message
1924 .snippet(snippet.annotation(Level::Warning.span(dep_span).label(&info_label)))
1925 .footer(Level::Help.title(&help))
1926 } else {
1927 message.snippet(snippet)
1928 }
1929 } else {
1930 message.snippet(snippet)
1931 };
1932
1933 if let Err(err) = gctx.shell().print_message(message) {
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_diagnostic(
2781 e: toml::de::Error,
2782 contents: &str,
2783 manifest_file: &Path,
2784 gctx: &GlobalContext,
2785) -> anyhow::Error {
2786 let Some(span) = e.span() else {
2787 return e.into();
2788 };
2789
2790 let manifest_path = diff_paths(manifest_file, gctx.cwd())
2792 .unwrap_or_else(|| manifest_file.to_path_buf())
2793 .display()
2794 .to_string();
2795 let message = Level::Error.title(e.message()).snippet(
2796 Snippet::source(contents)
2797 .origin(&manifest_path)
2798 .fold(true)
2799 .annotation(Level::Error.span(span)),
2800 );
2801 if let Err(err) = gctx.shell().print_message(message) {
2802 return err.into();
2803 }
2804 return AlreadyPrintedError::new(e.into()).into();
2805}
2806
2807fn deprecated_underscore<T>(
2809 old: &Option<T>,
2810 new: &Option<T>,
2811 new_path: &str,
2812 name: &str,
2813 kind: &str,
2814 edition: Edition,
2815 warnings: &mut Vec<String>,
2816) -> CargoResult<()> {
2817 let old_path = new_path.replace("-", "_");
2818 if old.is_some() && Edition::Edition2024 <= edition {
2819 anyhow::bail!(
2820 "`{old_path}` is unsupported as of the 2024 edition; instead use `{new_path}`\n(in the `{name}` {kind})"
2821 );
2822 } else if old.is_some() && new.is_some() {
2823 warnings.push(format!(
2824 "`{old_path}` is redundant with `{new_path}`, preferring `{new_path}` in the `{name}` {kind}"
2825 ))
2826 } else if old.is_some() {
2827 warnings.push(format!(
2828 "`{old_path}` is deprecated in favor of `{new_path}` and will not work in the 2024 edition\n(in the `{name}` {kind})"
2829 ))
2830 }
2831 Ok(())
2832}
2833
2834fn warn_on_unused(unused: &BTreeSet<String>, warnings: &mut Vec<String>) {
2835 for key in unused {
2836 warnings.push(format!("unused manifest key: {}", key));
2837 if key == "profiles.debug" {
2838 warnings.push("use `[profile.dev]` to configure debug builds".to_string());
2839 }
2840 }
2841}
2842
2843fn unused_dep_keys(
2844 dep_name: &str,
2845 kind: &str,
2846 unused_keys: Vec<String>,
2847 warnings: &mut Vec<String>,
2848) {
2849 for unused in unused_keys {
2850 let key = format!("unused manifest key: {kind}.{dep_name}.{unused}");
2851 warnings.push(key);
2852 }
2853}
2854
2855pub fn prepare_for_publish(
2857 me: &Package,
2858 ws: &Workspace<'_>,
2859 packaged_files: Option<&[PathBuf]>,
2860) -> CargoResult<Package> {
2861 let contents = me.manifest().contents();
2862 let document = me.manifest().document();
2863 let original_toml = prepare_toml_for_publish(
2864 me.manifest().normalized_toml(),
2865 ws,
2866 me.root(),
2867 packaged_files,
2868 )?;
2869 let normalized_toml = original_toml.clone();
2870 let features = me.manifest().unstable_features().clone();
2871 let workspace_config = me.manifest().workspace_config().clone();
2872 let source_id = me.package_id().source_id();
2873 let mut warnings = Default::default();
2874 let mut errors = Default::default();
2875 let gctx = ws.gctx();
2876 let manifest = to_real_manifest(
2877 contents.to_owned(),
2878 document.clone(),
2879 original_toml,
2880 normalized_toml,
2881 features,
2882 workspace_config,
2883 source_id,
2884 me.manifest_path(),
2885 me.manifest().is_embedded(),
2886 gctx,
2887 &mut warnings,
2888 &mut errors,
2889 )?;
2890 let new_pkg = Package::new(manifest, me.manifest_path());
2891 Ok(new_pkg)
2892}
2893
2894fn prepare_toml_for_publish(
2898 me: &manifest::TomlManifest,
2899 ws: &Workspace<'_>,
2900 package_root: &Path,
2901 packaged_files: Option<&[PathBuf]>,
2902) -> CargoResult<manifest::TomlManifest> {
2903 let gctx = ws.gctx();
2904
2905 if me
2906 .cargo_features
2907 .iter()
2908 .flat_map(|f| f.iter())
2909 .any(|f| f == "open-namespaces")
2910 {
2911 anyhow::bail!("cannot publish with `open-namespaces`")
2912 }
2913
2914 let mut package = me.package().unwrap().clone();
2915 package.workspace = None;
2916 if let Some(custom_build_scripts) = package.normalized_build().expect("previously normalized") {
2918 let mut included_scripts = Vec::new();
2919 for script in custom_build_scripts {
2920 let path = Path::new(script).to_path_buf();
2921 let included = packaged_files.map(|i| i.contains(&path)).unwrap_or(true);
2922 if included {
2923 let path = path
2924 .into_os_string()
2925 .into_string()
2926 .map_err(|_err| anyhow::format_err!("non-UTF8 `package.build`"))?;
2927 let path = normalize_path_string_sep(path);
2928 included_scripts.push(path);
2929 } else {
2930 ws.gctx().shell().warn(format!(
2931 "ignoring `package.build` entry `{}` as it is not included in the published package",
2932 path.display()
2933 ))?;
2934 }
2935 }
2936
2937 package.build = Some(match included_scripts.len() {
2938 0 => TomlPackageBuild::Auto(false),
2939 1 => TomlPackageBuild::SingleScript(included_scripts[0].clone()),
2940 _ => TomlPackageBuild::MultipleScript(included_scripts),
2941 });
2942 }
2943 let current_resolver = package
2944 .resolver
2945 .as_ref()
2946 .map(|r| ResolveBehavior::from_manifest(r))
2947 .unwrap_or_else(|| {
2948 package
2949 .edition
2950 .as_ref()
2951 .and_then(|e| e.as_value())
2952 .map(|e| Edition::from_str(e))
2953 .unwrap_or(Ok(Edition::Edition2015))
2954 .map(|e| e.default_resolve_behavior())
2955 })?;
2956 if ws.resolve_behavior() != current_resolver {
2957 package.resolver = Some(ws.resolve_behavior().to_manifest());
2962 }
2963 if let Some(license_file) = &package.license_file {
2964 let license_file = license_file
2965 .as_value()
2966 .context("license file should have been resolved before `prepare_for_publish()`")?;
2967 let license_path = Path::new(&license_file);
2968 let abs_license_path = paths::normalize_path(&package_root.join(license_path));
2969 if let Ok(license_file) = abs_license_path.strip_prefix(package_root) {
2970 package.license_file = Some(manifest::InheritableField::Value(
2971 normalize_path_string_sep(
2972 license_file
2973 .to_str()
2974 .ok_or_else(|| anyhow::format_err!("non-UTF8 `package.license-file`"))?
2975 .to_owned(),
2976 ),
2977 ));
2978 } else {
2979 package.license_file = Some(manifest::InheritableField::Value(
2982 license_path
2983 .file_name()
2984 .unwrap()
2985 .to_str()
2986 .unwrap()
2987 .to_string(),
2988 ));
2989 }
2990 }
2991
2992 if let Some(readme) = &package.readme {
2993 let readme = readme
2994 .as_value()
2995 .context("readme should have been resolved before `prepare_for_publish()`")?;
2996 match readme {
2997 manifest::StringOrBool::String(readme) => {
2998 let readme_path = Path::new(&readme);
2999 let abs_readme_path = paths::normalize_path(&package_root.join(readme_path));
3000 if let Ok(readme_path) = abs_readme_path.strip_prefix(package_root) {
3001 package.readme = Some(manifest::InheritableField::Value(StringOrBool::String(
3002 normalize_path_string_sep(
3003 readme_path
3004 .to_str()
3005 .ok_or_else(|| {
3006 anyhow::format_err!("non-UTF8 `package.license-file`")
3007 })?
3008 .to_owned(),
3009 ),
3010 )));
3011 } else {
3012 package.readme = Some(manifest::InheritableField::Value(
3015 manifest::StringOrBool::String(
3016 readme_path
3017 .file_name()
3018 .unwrap()
3019 .to_str()
3020 .unwrap()
3021 .to_string(),
3022 ),
3023 ));
3024 }
3025 }
3026 manifest::StringOrBool::Bool(_) => {}
3027 }
3028 }
3029
3030 let lib = if let Some(target) = &me.lib {
3031 prepare_target_for_publish(target, packaged_files, "library", ws.gctx())?
3032 } else {
3033 None
3034 };
3035 let bin = prepare_targets_for_publish(me.bin.as_ref(), packaged_files, "binary", ws.gctx())?;
3036 let example =
3037 prepare_targets_for_publish(me.example.as_ref(), packaged_files, "example", ws.gctx())?;
3038 let test = prepare_targets_for_publish(me.test.as_ref(), packaged_files, "test", ws.gctx())?;
3039 let bench =
3040 prepare_targets_for_publish(me.bench.as_ref(), packaged_files, "benchmark", ws.gctx())?;
3041
3042 let all = |_d: &manifest::TomlDependency| true;
3043 let mut manifest = manifest::TomlManifest {
3044 cargo_features: me.cargo_features.clone(),
3045 package: Some(package),
3046 project: None,
3047 badges: me.badges.clone(),
3048 features: me.features.clone(),
3049 lib,
3050 bin,
3051 example,
3052 test,
3053 bench,
3054 dependencies: map_deps(gctx, me.dependencies.as_ref(), all)?,
3055 dev_dependencies: map_deps(
3056 gctx,
3057 me.dev_dependencies(),
3058 manifest::TomlDependency::is_version_specified,
3059 )?,
3060 dev_dependencies2: None,
3061 build_dependencies: map_deps(gctx, me.build_dependencies(), all)?,
3062 build_dependencies2: None,
3063 target: match me.target.as_ref().map(|target_map| {
3064 target_map
3065 .iter()
3066 .map(|(k, v)| {
3067 Ok((
3068 k.clone(),
3069 manifest::TomlPlatform {
3070 dependencies: map_deps(gctx, v.dependencies.as_ref(), all)?,
3071 dev_dependencies: map_deps(
3072 gctx,
3073 v.dev_dependencies(),
3074 manifest::TomlDependency::is_version_specified,
3075 )?,
3076 dev_dependencies2: None,
3077 build_dependencies: map_deps(gctx, v.build_dependencies(), all)?,
3078 build_dependencies2: None,
3079 },
3080 ))
3081 })
3082 .collect()
3083 }) {
3084 Some(Ok(v)) => Some(v),
3085 Some(Err(e)) => return Err(e),
3086 None => None,
3087 },
3088 lints: me.lints.clone(),
3089 hints: me.hints.clone(),
3090 workspace: None,
3091 profile: me.profile.clone(),
3092 patch: None,
3093 replace: None,
3094 _unused_keys: Default::default(),
3095 };
3096 strip_features(&mut manifest);
3097 return Ok(manifest);
3098
3099 fn strip_features(manifest: &mut TomlManifest) {
3100 fn insert_dep_name(
3101 dep_name_set: &mut BTreeSet<manifest::PackageName>,
3102 deps: Option<&BTreeMap<manifest::PackageName, manifest::InheritableDependency>>,
3103 ) {
3104 let Some(deps) = deps else {
3105 return;
3106 };
3107 deps.iter().for_each(|(k, _v)| {
3108 dep_name_set.insert(k.clone());
3109 });
3110 }
3111 let mut dep_name_set = BTreeSet::new();
3112 insert_dep_name(&mut dep_name_set, manifest.dependencies.as_ref());
3113 insert_dep_name(&mut dep_name_set, manifest.dev_dependencies());
3114 insert_dep_name(&mut dep_name_set, manifest.build_dependencies());
3115 if let Some(target_map) = manifest.target.as_ref() {
3116 target_map.iter().for_each(|(_k, v)| {
3117 insert_dep_name(&mut dep_name_set, v.dependencies.as_ref());
3118 insert_dep_name(&mut dep_name_set, v.dev_dependencies());
3119 insert_dep_name(&mut dep_name_set, v.build_dependencies());
3120 });
3121 }
3122 let features = manifest.features.as_mut();
3123
3124 let Some(features) = features else {
3125 return;
3126 };
3127
3128 features.values_mut().for_each(|feature_deps| {
3129 feature_deps.retain(|feature_dep| {
3130 let feature_value = FeatureValue::new(feature_dep.into());
3131 match feature_value {
3132 FeatureValue::Dep { dep_name } | FeatureValue::DepFeature { dep_name, .. } => {
3133 let k = &manifest::PackageName::new(dep_name.to_string()).unwrap();
3134 dep_name_set.contains(k)
3135 }
3136 _ => true,
3137 }
3138 });
3139 });
3140 }
3141
3142 fn map_deps(
3143 gctx: &GlobalContext,
3144 deps: Option<&BTreeMap<manifest::PackageName, manifest::InheritableDependency>>,
3145 filter: impl Fn(&manifest::TomlDependency) -> bool,
3146 ) -> CargoResult<Option<BTreeMap<manifest::PackageName, manifest::InheritableDependency>>> {
3147 let Some(deps) = deps else {
3148 return Ok(None);
3149 };
3150 let deps = deps
3151 .iter()
3152 .filter(|(_k, v)| {
3153 if let manifest::InheritableDependency::Value(def) = v {
3154 filter(def)
3155 } else {
3156 false
3157 }
3158 })
3159 .map(|(k, v)| Ok((k.clone(), map_dependency(gctx, v)?)))
3160 .collect::<CargoResult<BTreeMap<_, _>>>()?;
3161 Ok(Some(deps))
3162 }
3163
3164 fn map_dependency(
3165 gctx: &GlobalContext,
3166 dep: &manifest::InheritableDependency,
3167 ) -> CargoResult<manifest::InheritableDependency> {
3168 let dep = match dep {
3169 manifest::InheritableDependency::Value(manifest::TomlDependency::Detailed(d)) => {
3170 let mut d = d.clone();
3171 d.path.take();
3173 d.base.take();
3174 d.git.take();
3176 d.branch.take();
3177 d.tag.take();
3178 d.rev.take();
3179 if let Some(registry) = d.registry.take() {
3181 d.registry_index = Some(gctx.get_registry_index(®istry)?.to_string());
3182 }
3183 Ok(d)
3184 }
3185 manifest::InheritableDependency::Value(manifest::TomlDependency::Simple(s)) => {
3186 Ok(manifest::TomlDetailedDependency {
3187 version: Some(s.clone()),
3188 ..Default::default()
3189 })
3190 }
3191 _ => unreachable!(),
3192 };
3193 dep.map(manifest::TomlDependency::Detailed)
3194 .map(manifest::InheritableDependency::Value)
3195 }
3196}
3197
3198pub fn prepare_targets_for_publish(
3199 targets: Option<&Vec<manifest::TomlTarget>>,
3200 packaged_files: Option<&[PathBuf]>,
3201 context: &str,
3202 gctx: &GlobalContext,
3203) -> CargoResult<Option<Vec<manifest::TomlTarget>>> {
3204 let Some(targets) = targets else {
3205 return Ok(None);
3206 };
3207
3208 let mut prepared = Vec::with_capacity(targets.len());
3209 for target in targets {
3210 let Some(target) = prepare_target_for_publish(target, packaged_files, context, gctx)?
3211 else {
3212 continue;
3213 };
3214 prepared.push(target);
3215 }
3216
3217 if prepared.is_empty() {
3218 Ok(None)
3219 } else {
3220 Ok(Some(prepared))
3221 }
3222}
3223
3224pub fn prepare_target_for_publish(
3225 target: &manifest::TomlTarget,
3226 packaged_files: Option<&[PathBuf]>,
3227 context: &str,
3228 gctx: &GlobalContext,
3229) -> CargoResult<Option<manifest::TomlTarget>> {
3230 let path = target.path.as_ref().expect("previously normalized");
3231 let path = &path.0;
3232 if let Some(packaged_files) = packaged_files {
3233 if !packaged_files.contains(&path) {
3234 let name = target.name.as_ref().expect("previously normalized");
3235 gctx.shell().warn(format!(
3236 "ignoring {context} `{name}` as `{}` is not included in the published package",
3237 path.display()
3238 ))?;
3239 return Ok(None);
3240 }
3241 }
3242
3243 let mut target = target.clone();
3244 let path = normalize_path_sep(path.to_path_buf(), context)?;
3245 target.path = Some(manifest::PathValue(path.into()));
3246
3247 Ok(Some(target))
3248}
3249
3250fn normalize_path_sep(path: PathBuf, context: &str) -> CargoResult<PathBuf> {
3251 let path = path
3252 .into_os_string()
3253 .into_string()
3254 .map_err(|_err| anyhow::format_err!("non-UTF8 path for {context}"))?;
3255 let path = normalize_path_string_sep(path);
3256 Ok(path.into())
3257}
3258
3259pub fn normalize_path_string_sep(path: String) -> String {
3260 if std::path::MAIN_SEPARATOR != '/' {
3261 path.replace(std::path::MAIN_SEPARATOR, "/")
3262 } else {
3263 path
3264 }
3265}