cargo/util/toml/
mod.rs

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