cargo/util/toml/
mod.rs

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