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