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