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