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