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