diff --git a/crates/cargo-util-schemas/manifest.schema.json b/crates/cargo-util-schemas/manifest.schema.json index 5bc43e49d37..bbf85455913 100644 --- a/crates/cargo-util-schemas/manifest.schema.json +++ b/crates/cargo-util-schemas/manifest.schema.json @@ -241,7 +241,10 @@ ] }, "name": { - "type": "string" + "type": [ + "string", + "null" + ] }, "version": { "anyOf": [ @@ -475,10 +478,7 @@ } ] } - }, - "required": [ - "name" - ] + } }, "InheritableField_for_string": { "description": "An enum that allows for inheriting keys from a workspace in a Cargo.toml.", diff --git a/crates/cargo-util-schemas/src/manifest/mod.rs b/crates/cargo-util-schemas/src/manifest/mod.rs index 906ff3d431f..563b7d329c7 100644 --- a/crates/cargo-util-schemas/src/manifest/mod.rs +++ b/crates/cargo-util-schemas/src/manifest/mod.rs @@ -171,15 +171,15 @@ pub struct InheritablePackage { /// are serialized to a TOML file. For example, you cannot have values after /// the field `metadata`, since it is a table and values cannot appear after /// tables. -#[derive(Deserialize, Serialize, Clone, Debug)] +#[derive(Deserialize, Serialize, Clone, Debug, Default)] #[serde(rename_all = "kebab-case")] #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub struct TomlPackage { pub edition: Option, #[cfg_attr(feature = "unstable-schema", schemars(with = "Option"))] pub rust_version: Option, - #[cfg_attr(feature = "unstable-schema", schemars(with = "String"))] - pub name: PackageName, + #[cfg_attr(feature = "unstable-schema", schemars(with = "Option"))] + pub name: Option, pub version: Option, pub authors: Option, pub build: Option, @@ -226,43 +226,15 @@ pub struct TomlPackage { impl TomlPackage { pub fn new(name: PackageName) -> Self { Self { - name, - - edition: None, - rust_version: None, - version: None, - authors: None, - build: None, - metabuild: None, - default_target: None, - forced_target: None, - links: None, - exclude: None, - include: None, - publish: None, - workspace: None, - im_a_teapot: None, - autolib: None, - autobins: None, - autoexamples: None, - autotests: None, - autobenches: None, - default_run: None, - description: None, - homepage: None, - documentation: None, - readme: None, - keywords: None, - categories: None, - license: None, - license_file: None, - repository: None, - resolver: None, - metadata: None, - _invalid_cargo_features: None, + name: Some(name), + ..Default::default() } } + pub fn normalized_name(&self) -> Result<&PackageName, UnresolvedError> { + self.name.as_ref().ok_or(UnresolvedError) + } + pub fn normalized_edition(&self) -> Result, UnresolvedError> { self.edition.as_ref().map(|v| v.normalized()).transpose() } diff --git a/src/cargo/ops/vendor.rs b/src/cargo/ops/vendor.rs index c6c76f624da..44e2a2addd2 100644 --- a/src/cargo/ops/vendor.rs +++ b/src/cargo/ops/vendor.rs @@ -434,6 +434,7 @@ fn prepare_for_vendor( workspace_config, source_id, me.manifest_path(), + me.manifest().is_embedded(), gctx, &mut warnings, &mut errors, diff --git a/src/cargo/util/toml/embedded.rs b/src/cargo/util/toml/embedded.rs index 686fcb77a22..e151c4adde2 100644 --- a/src/cargo/util/toml/embedded.rs +++ b/src/cargo/util/toml/embedded.rs @@ -6,16 +6,6 @@ use crate::util::restricted_names; use crate::CargoResult; use crate::GlobalContext; -const DEFAULT_EDITION: crate::core::features::Edition = - crate::core::features::Edition::LATEST_STABLE; -const AUTO_FIELDS: &[&str] = &[ - "autolib", - "autobins", - "autoexamples", - "autotests", - "autobenches", -]; - pub(super) fn expand_manifest( content: &str, path: &std::path::Path, @@ -64,49 +54,23 @@ pub(super) fn expand_manifest( } cargo_util::paths::write_if_changed(&hacked_path, hacked_source)?; - let manifest = expand_manifest_(&frontmatter, &hacked_path, gctx) - .with_context(|| format!("failed to parse manifest at {}", path.display()))?; + let manifest = inject_bin_path(&frontmatter, &hacked_path) + .with_context(|| format!("failed to parse manifest at `{}`", path.display()))?; let manifest = toml::to_string_pretty(&manifest)?; Ok(manifest) } else { let frontmatter = ""; - let manifest = expand_manifest_(frontmatter, path, gctx) - .with_context(|| format!("failed to parse manifest at {}", path.display()))?; + let manifest = inject_bin_path(frontmatter, path) + .with_context(|| format!("failed to parse manifest at `{}`", path.display()))?; let manifest = toml::to_string_pretty(&manifest)?; Ok(manifest) } } -fn expand_manifest_( - manifest: &str, - path: &std::path::Path, - gctx: &GlobalContext, -) -> CargoResult { +/// HACK: Add a `[[bin]]` table to the `original_toml` +fn inject_bin_path(manifest: &str, path: &std::path::Path) -> CargoResult { let mut manifest: toml::Table = toml::from_str(&manifest)?; - for key in ["workspace", "lib", "bin", "example", "test", "bench"] { - if manifest.contains_key(key) { - anyhow::bail!("`{key}` is not allowed in embedded manifests") - } - } - - // Prevent looking for a workspace by `read_manifest_from_str` - manifest.insert("workspace".to_owned(), toml::Table::new().into()); - - let package = manifest - .entry("package".to_owned()) - .or_insert_with(|| toml::Table::new().into()) - .as_table_mut() - .ok_or_else(|| anyhow::format_err!("`package` must be a table"))?; - for key in ["workspace", "build", "links"] - .iter() - .chain(AUTO_FIELDS.iter()) - { - if package.contains_key(*key) { - anyhow::bail!("`package.{key}` is not allowed in embedded manifests") - } - } - // HACK: Using an absolute path while `hacked_path` is in use let bin_path = path.to_string_lossy().into_owned(); let file_stem = path .file_stem() @@ -114,38 +78,22 @@ fn expand_manifest_( .to_string_lossy(); let name = sanitize_name(file_stem.as_ref()); let bin_name = name.clone(); - package - .entry("name".to_owned()) - .or_insert(toml::Value::String(name)); - package.entry("edition".to_owned()).or_insert_with(|| { - let _ = gctx.shell().warn(format_args!( - "`package.edition` is unspecified, defaulting to `{}`", - DEFAULT_EDITION - )); - toml::Value::String(DEFAULT_EDITION.to_string()) - }); - package - .entry("build".to_owned()) - .or_insert_with(|| toml::Value::Boolean(false)); - for field in AUTO_FIELDS { - package - .entry(field.to_owned()) - .or_insert_with(|| toml::Value::Boolean(false)); - } let mut bin = toml::Table::new(); bin.insert("name".to_owned(), toml::Value::String(bin_name)); bin.insert("path".to_owned(), toml::Value::String(bin_path)); - manifest.insert( - "bin".to_owned(), - toml::Value::Array(vec![toml::Value::Table(bin)]), - ); + manifest + .entry("bin") + .or_insert_with(|| Vec::::new().into()) + .as_array_mut() + .ok_or_else(|| anyhow::format_err!("`bin` must be an array"))? + .push(toml::Value::Table(bin)); Ok(manifest) } /// Ensure the package name matches the validation from `ops::cargo_new::check_name` -fn sanitize_name(name: &str) -> String { +pub fn sanitize_name(name: &str) -> String { let placeholder = if name.contains('_') { '_' } else { @@ -565,18 +513,6 @@ fn main() {} name = "test-" path = "/home/me/test.rs" -[package] -autobenches = false -autobins = false -autoexamples = false -autolib = false -autotests = false -build = false -edition = "2024" -name = "test-" - -[workspace] - "#]] ); } @@ -600,18 +536,6 @@ path = [..] [dependencies] time = "0.1.25" -[package] -autobenches = false -autobins = false -autoexamples = false -autolib = false -autotests = false -build = false -edition = "2024" -name = "test-" - -[workspace] - "#]] ); } diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index 4eed082eb69..e66c5a00aa4 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -1,4 +1,5 @@ use annotate_snippets::{Level, Snippet}; +use std::borrow::Cow; use std::collections::{BTreeMap, BTreeSet, HashMap}; use std::ffi::OsStr; use std::path::{Path, PathBuf}; @@ -66,8 +67,9 @@ pub fn read_manifest( let mut warnings = Default::default(); let mut errors = Default::default(); - let contents = - read_toml_string(path, gctx).map_err(|err| ManifestError::new(err, path.into()))?; + let is_embedded = is_embedded(path); + let contents = read_toml_string(path, is_embedded, gctx) + .map_err(|err| ManifestError::new(err, path.into()))?; let document = parse_document(&contents).map_err(|e| emit_diagnostic(e.into(), &contents, path, gctx))?; let original_toml = deserialize_toml(&document) @@ -89,6 +91,7 @@ pub fn read_manifest( &features, &workspace_config, path, + is_embedded, gctx, &mut warnings, &mut errors, @@ -104,12 +107,14 @@ pub fn read_manifest( workspace_config, source_id, path, + is_embedded, gctx, &mut warnings, &mut errors, ) .map(EitherManifest::Real) } else if normalized_toml.workspace.is_some() { + assert!(!is_embedded); to_virtual_manifest( contents, document, @@ -146,9 +151,9 @@ pub fn read_manifest( } #[tracing::instrument(skip_all)] -fn read_toml_string(path: &Path, gctx: &GlobalContext) -> CargoResult { +fn read_toml_string(path: &Path, is_embedded: bool, gctx: &GlobalContext) -> CargoResult { let mut contents = paths::read(path)?; - if is_embedded(path) { + if is_embedded { if !gctx.cli_unstable().script { anyhow::bail!("parsing `{}` requires `-Zscript`", path.display()); } @@ -271,6 +276,7 @@ fn normalize_toml( features: &Features, workspace_config: &WorkspaceConfig, manifest_file: &Path, + is_embedded: bool, gctx: &GlobalContext, warnings: &mut Vec, errors: &mut Vec, @@ -302,7 +308,10 @@ fn normalize_toml( build_dependencies2: None, target: None, lints: None, - workspace: original_toml.workspace.clone(), + workspace: original_toml.workspace.clone().or_else(|| { + // Prevent looking for a workspace by `read_manifest_from_str` + is_embedded.then(manifest::TomlWorkspace::default) + }), profile: original_toml.profile.clone(), patch: normalize_patch( gctx, @@ -314,10 +323,24 @@ fn normalize_toml( _unused_keys: Default::default(), }; - if let Some(original_package) = original_toml.package() { - let package_name = &original_package.name; - - let normalized_package = normalize_package_toml(original_package, package_root, &inherit)?; + if let Some(original_package) = original_toml.package().map(Cow::Borrowed).or_else(|| { + if is_embedded { + Some(Cow::Owned(Box::new(manifest::TomlPackage::default()))) + } else { + None + } + }) { + let normalized_package = normalize_package_toml( + &original_package, + manifest_file, + is_embedded, + gctx, + &inherit, + )?; + let package_name = &normalized_package + .normalized_name() + .expect("previously normalized") + .clone(); let edition = normalized_package .normalized_edition() .expect("previously normalized") @@ -328,20 +351,21 @@ fn normalize_toml( normalized_toml.features = normalize_features(original_toml.features.as_ref())?; + let auto_embedded = is_embedded.then_some(false); normalized_toml.lib = targets::normalize_lib( original_toml.lib.as_ref(), package_root, - &original_package.name, + package_name, edition, - original_package.autolib, + original_package.autolib.or(auto_embedded), warnings, )?; normalized_toml.bin = Some(targets::normalize_bins( original_toml.bin.as_ref(), package_root, - &original_package.name, + package_name, edition, - original_package.autobins, + original_package.autobins.or(auto_embedded), warnings, errors, normalized_toml.lib.is_some(), @@ -350,7 +374,7 @@ fn normalize_toml( original_toml.example.as_ref(), package_root, edition, - original_package.autoexamples, + original_package.autoexamples.or(auto_embedded), warnings, errors, )?); @@ -358,7 +382,7 @@ fn normalize_toml( original_toml.test.as_ref(), package_root, edition, - original_package.autotests, + original_package.autotests.or(auto_embedded), warnings, errors, )?); @@ -366,7 +390,7 @@ fn normalize_toml( original_toml.bench.as_ref(), package_root, edition, - original_package.autobenches, + original_package.autobenches.or(auto_embedded), warnings, errors, )?); @@ -542,135 +566,208 @@ fn normalize_patch<'a>( #[tracing::instrument(skip_all)] fn normalize_package_toml<'a>( original_package: &manifest::TomlPackage, - package_root: &Path, + manifest_file: &Path, + is_embedded: bool, + gctx: &GlobalContext, inherit: &dyn Fn() -> CargoResult<&'a InheritableFields>, ) -> CargoResult> { - let normalized_package = manifest::TomlPackage { - edition: original_package - .edition - .clone() - .map(|value| field_inherit_with(value, "edition", || inherit()?.edition())) - .transpose()? - .map(manifest::InheritableField::Value), - rust_version: original_package - .rust_version - .clone() - .map(|value| field_inherit_with(value, "rust-version", || inherit()?.rust_version())) - .transpose()? - .map(manifest::InheritableField::Value), - name: original_package.name.clone(), - version: original_package - .version - .clone() - .map(|value| field_inherit_with(value, "version", || inherit()?.version())) - .transpose()? - .map(manifest::InheritableField::Value), - authors: original_package - .authors - .clone() - .map(|value| field_inherit_with(value, "authors", || inherit()?.authors())) - .transpose()? - .map(manifest::InheritableField::Value), - build: targets::normalize_build(original_package.build.as_ref(), package_root), - metabuild: original_package.metabuild.clone(), - default_target: original_package.default_target.clone(), - forced_target: original_package.forced_target.clone(), - links: original_package.links.clone(), - exclude: original_package - .exclude - .clone() - .map(|value| field_inherit_with(value, "exclude", || inherit()?.exclude())) - .transpose()? - .map(manifest::InheritableField::Value), - include: original_package - .include - .clone() - .map(|value| field_inherit_with(value, "include", || inherit()?.include())) - .transpose()? - .map(manifest::InheritableField::Value), - publish: original_package - .publish - .clone() - .map(|value| field_inherit_with(value, "publish", || inherit()?.publish())) - .transpose()? - .map(manifest::InheritableField::Value), - workspace: original_package.workspace.clone(), - im_a_teapot: original_package.im_a_teapot.clone(), - autolib: Some(false), - autobins: Some(false), - autoexamples: Some(false), - autotests: Some(false), - autobenches: Some(false), - default_run: original_package.default_run.clone(), - description: original_package - .description - .clone() - .map(|value| field_inherit_with(value, "description", || inherit()?.description())) - .transpose()? - .map(manifest::InheritableField::Value), - homepage: original_package - .homepage - .clone() - .map(|value| field_inherit_with(value, "homepage", || inherit()?.homepage())) - .transpose()? - .map(manifest::InheritableField::Value), - documentation: original_package - .documentation - .clone() - .map(|value| field_inherit_with(value, "documentation", || inherit()?.documentation())) - .transpose()? - .map(manifest::InheritableField::Value), - readme: normalize_package_readme( - package_root, - original_package - .readme - .clone() - .map(|value| { - field_inherit_with(value, "readme", || inherit()?.readme(package_root)) - }) - .transpose()? - .as_ref(), - ) - .map(|s| manifest::InheritableField::Value(StringOrBool::String(s))) - .or(Some(manifest::InheritableField::Value(StringOrBool::Bool( - false, - )))), - keywords: original_package - .keywords - .clone() - .map(|value| field_inherit_with(value, "keywords", || inherit()?.keywords())) - .transpose()? - .map(manifest::InheritableField::Value), - categories: original_package - .categories - .clone() - .map(|value| field_inherit_with(value, "categories", || inherit()?.categories())) - .transpose()? - .map(manifest::InheritableField::Value), - license: original_package - .license - .clone() - .map(|value| field_inherit_with(value, "license", || inherit()?.license())) - .transpose()? - .map(manifest::InheritableField::Value), - license_file: original_package - .license_file + let package_root = manifest_file.parent().unwrap(); + + let edition = original_package + .edition + .clone() + .map(|value| field_inherit_with(value, "edition", || inherit()?.edition())) + .transpose()? + .map(manifest::InheritableField::Value) + .or_else(|| { + if is_embedded { + const DEFAULT_EDITION: crate::core::features::Edition = + crate::core::features::Edition::LATEST_STABLE; + let _ = gctx.shell().warn(format_args!( + "`package.edition` is unspecified, defaulting to `{}`", + DEFAULT_EDITION + )); + Some(manifest::InheritableField::Value( + DEFAULT_EDITION.to_string(), + )) + } else { + None + } + }); + let rust_version = original_package + .rust_version + .clone() + .map(|value| field_inherit_with(value, "rust-version", || inherit()?.rust_version())) + .transpose()? + .map(manifest::InheritableField::Value); + let name = Some( + original_package + .name .clone() - .map(|value| { - field_inherit_with(value, "license-file", || { - inherit()?.license_file(package_root) - }) + .or_else(|| { + if is_embedded { + let file_stem = manifest_file + .file_stem() + .expect("file name enforced previously") + .to_string_lossy(); + let name = embedded::sanitize_name(file_stem.as_ref()); + let name = + manifest::PackageName::new(name).expect("sanitize made the name valid"); + Some(name) + } else { + None + } }) - .transpose()? - .map(manifest::InheritableField::Value), - repository: original_package - .repository + .ok_or_else(|| anyhow::format_err!("missing field `package.name`"))?, + ); + let version = original_package + .version + .clone() + .map(|value| field_inherit_with(value, "version", || inherit()?.version())) + .transpose()? + .map(manifest::InheritableField::Value); + let authors = original_package + .authors + .clone() + .map(|value| field_inherit_with(value, "authors", || inherit()?.authors())) + .transpose()? + .map(manifest::InheritableField::Value); + let build = if is_embedded { + Some(StringOrBool::Bool(false)) + } else { + targets::normalize_build(original_package.build.as_ref(), package_root) + }; + let metabuild = original_package.metabuild.clone(); + let default_target = original_package.default_target.clone(); + let forced_target = original_package.forced_target.clone(); + let links = original_package.links.clone(); + let exclude = original_package + .exclude + .clone() + .map(|value| field_inherit_with(value, "exclude", || inherit()?.exclude())) + .transpose()? + .map(manifest::InheritableField::Value); + let include = original_package + .include + .clone() + .map(|value| field_inherit_with(value, "include", || inherit()?.include())) + .transpose()? + .map(manifest::InheritableField::Value); + let publish = original_package + .publish + .clone() + .map(|value| field_inherit_with(value, "publish", || inherit()?.publish())) + .transpose()? + .map(manifest::InheritableField::Value); + let workspace = original_package.workspace.clone(); + let im_a_teapot = original_package.im_a_teapot.clone(); + let autolib = Some(false); + let autobins = Some(false); + let autoexamples = Some(false); + let autotests = Some(false); + let autobenches = Some(false); + let default_run = original_package.default_run.clone(); + let description = original_package + .description + .clone() + .map(|value| field_inherit_with(value, "description", || inherit()?.description())) + .transpose()? + .map(manifest::InheritableField::Value); + let homepage = original_package + .homepage + .clone() + .map(|value| field_inherit_with(value, "homepage", || inherit()?.homepage())) + .transpose()? + .map(manifest::InheritableField::Value); + let documentation = original_package + .documentation + .clone() + .map(|value| field_inherit_with(value, "documentation", || inherit()?.documentation())) + .transpose()? + .map(manifest::InheritableField::Value); + let readme = normalize_package_readme( + package_root, + original_package + .readme .clone() - .map(|value| field_inherit_with(value, "repository", || inherit()?.repository())) + .map(|value| field_inherit_with(value, "readme", || inherit()?.readme(package_root))) .transpose()? - .map(manifest::InheritableField::Value), - resolver: original_package.resolver.clone(), - metadata: original_package.metadata.clone(), + .as_ref(), + ) + .map(|s| manifest::InheritableField::Value(StringOrBool::String(s))) + .or(Some(manifest::InheritableField::Value(StringOrBool::Bool( + false, + )))); + let keywords = original_package + .keywords + .clone() + .map(|value| field_inherit_with(value, "keywords", || inherit()?.keywords())) + .transpose()? + .map(manifest::InheritableField::Value); + let categories = original_package + .categories + .clone() + .map(|value| field_inherit_with(value, "categories", || inherit()?.categories())) + .transpose()? + .map(manifest::InheritableField::Value); + let license = original_package + .license + .clone() + .map(|value| field_inherit_with(value, "license", || inherit()?.license())) + .transpose()? + .map(manifest::InheritableField::Value); + let license_file = original_package + .license_file + .clone() + .map(|value| { + field_inherit_with(value, "license-file", || { + inherit()?.license_file(package_root) + }) + }) + .transpose()? + .map(manifest::InheritableField::Value); + let repository = original_package + .repository + .clone() + .map(|value| field_inherit_with(value, "repository", || inherit()?.repository())) + .transpose()? + .map(manifest::InheritableField::Value); + let resolver = original_package.resolver.clone(); + let metadata = original_package.metadata.clone(); + + let normalized_package = manifest::TomlPackage { + edition, + rust_version, + name, + version, + authors, + build, + metabuild, + default_target, + forced_target, + links, + exclude, + include, + publish, + workspace, + im_a_teapot, + autolib, + autobins, + autoexamples, + autotests, + autobenches, + default_run, + description, + homepage, + documentation, + readme, + keywords, + categories, + license, + license_file, + repository, + resolver, + metadata, _invalid_cargo_features: Default::default(), }; @@ -1122,11 +1219,11 @@ pub fn to_real_manifest( workspace_config: WorkspaceConfig, source_id: SourceId, manifest_file: &Path, + is_embedded: bool, gctx: &GlobalContext, warnings: &mut Vec, _errors: &mut Vec, ) -> CargoResult { - let embedded = is_embedded(manifest_file); let package_root = manifest_file.parent().unwrap(); if !package_root.is_dir() { bail!( @@ -1135,18 +1232,15 @@ pub fn to_real_manifest( ); }; - let original_package = original_toml + let normalized_package = normalized_toml .package() - .ok_or_else(|| anyhow::format_err!("no `package` section found"))?; - - let package_name = &original_package.name; + .expect("previously verified to have a `[package]`"); + let package_name = normalized_package + .normalized_name() + .expect("previously normalized"); if package_name.contains(':') { features.require(Feature::open_namespaces())?; } - - let normalized_package = normalized_toml - .package() - .expect("previously verified to have a `[package]`"); let rust_version = normalized_package .normalized_rust_version() .expect("previously normalized") @@ -1239,6 +1333,89 @@ pub fn to_real_manifest( features.require(Feature::metabuild())?; } + if is_embedded { + let invalid_fields = [ + ("`workspace`", original_toml.workspace.is_some()), + ("`lib`", original_toml.lib.is_some()), + ( + "`bin`", + original_toml.bin.as_ref().map(|b| b.len()).unwrap_or(0) != 1, + ), + ("`example`", original_toml.example.is_some()), + ("`test`", original_toml.test.is_some()), + ("`bench`", original_toml.bench.is_some()), + ( + "`package.workspace`", + original_toml + .package() + .map(|p| p.workspace.is_some()) + .unwrap_or(false), + ), + ( + "`package.build`", + original_toml + .package() + .map(|p| p.build.is_some()) + .unwrap_or(false), + ), + ( + "`package.links`", + original_toml + .package() + .map(|p| p.links.is_some()) + .unwrap_or(false), + ), + ( + "`package.autolib`", + original_toml + .package() + .map(|p| p.autolib.is_some()) + .unwrap_or(false), + ), + ( + "`package.autobins`", + original_toml + .package() + .map(|p| p.autobins.is_some()) + .unwrap_or(false), + ), + ( + "`package.autoexamples`", + original_toml + .package() + .map(|p| p.autoexamples.is_some()) + .unwrap_or(false), + ), + ( + "`package.autotests`", + original_toml + .package() + .map(|p| p.autotests.is_some()) + .unwrap_or(false), + ), + ( + "`package.autobenches`", + original_toml + .package() + .map(|p| p.autobenches.is_some()) + .unwrap_or(false), + ), + ]; + let invalid_fields = invalid_fields + .into_iter() + .filter_map(|(name, invalid)| invalid.then_some(name)) + .collect::>(); + if !invalid_fields.is_empty() { + let fields = invalid_fields.join(", "); + let are = if invalid_fields.len() == 1 { + "is" + } else { + "are" + }; + anyhow::bail!("{fields} {are} not allowed in embedded manifests") + } + } + let resolve_behavior = match ( normalized_package.resolver.as_ref(), normalized_toml @@ -1479,7 +1656,7 @@ pub fn to_real_manifest( } let pkgid = PackageId::new( - normalized_package.name.as_str().into(), + package_name.as_str().into(), version .cloned() .unwrap_or_else(|| semver::Version::new(0, 0, 0)), @@ -1598,7 +1775,7 @@ pub fn to_real_manifest( metabuild, resolve_behavior, rustflags, - embedded, + is_embedded, ); if manifest .normalized_toml() @@ -2672,6 +2849,7 @@ pub fn prepare_for_publish( workspace_config, source_id, me.manifest_path(), + me.manifest().is_embedded(), gctx, &mut warnings, &mut errors, diff --git a/src/cargo/util/toml/targets.rs b/src/cargo/util/toml/targets.rs index 3fe018433b6..9c71b4d766d 100644 --- a/src/cargo/util/toml/targets.rs +++ b/src/cargo/util/toml/targets.rs @@ -117,7 +117,7 @@ pub(super) fn to_targets( targets.push(Target::metabuild_target(&format!( "metabuild-{}", - package.name + package.normalized_name().expect("previously normalized") ))); } diff --git a/tests/testsuite/bad_config.rs b/tests/testsuite/bad_config.rs index 57b84a17f90..8e7316dd246 100644 --- a/tests/testsuite/bad_config.rs +++ b/tests/testsuite/bad_config.rs @@ -470,6 +470,29 @@ expected `}` .run(); } +#[cargo_test] +fn cargo_toml_missing_package_name() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + "#, + ) + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr_data(str![[r#" +[ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` + +Caused by: + missing field `package.name` + +"#]]) + .run(); +} + #[cargo_test] fn duplicate_binary_names() { let p = project() diff --git a/tests/testsuite/script.rs b/tests/testsuite/script.rs index 0c0e19b16d7..5c6a46bb271 100644 --- a/tests/testsuite/script.rs +++ b/tests/testsuite/script.rs @@ -842,6 +842,406 @@ Hello world! .run(); } +#[cargo_test] +fn disallow_explicit_workspace() { + let p = cargo_test_support::project() + .file( + "script.rs", + r#" +---- +package.edition = "2021" + +[workspace] +---- + +fn main() {} +"#, + ) + .build(); + + p.cargo("-Zscript -v script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_status(101) + .with_stderr_data(str![[r#" +[ERROR] failed to parse manifest at `[ROOT]/foo/script.rs` + +Caused by: + `workspace` is not allowed in embedded manifests + +"#]]) + .run(); +} + +#[cargo_test] +fn disallow_explicit_lib() { + let p = cargo_test_support::project() + .file( + "script.rs", + r#" +---- +package.edition = "2021" + +[lib] +name = "script" +path = "script.rs" +---- + +fn main() {} +"#, + ) + .build(); + + p.cargo("-Zscript -v script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_status(101) + .with_stderr_data(str![[r#" +[ERROR] failed to parse manifest at `[ROOT]/foo/script.rs` + +Caused by: + `lib` is not allowed in embedded manifests + +"#]]) + .run(); +} + +#[cargo_test] +fn disallow_explicit_bin() { + let p = cargo_test_support::project() + .file( + "script.rs", + r#" +---- +package.edition = "2021" + +[[bin]] +name = "script" +path = "script.rs" +---- + +fn main() {} +"#, + ) + .build(); + + p.cargo("-Zscript -v script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_status(101) + .with_stderr_data(str![[r#" +[ERROR] failed to parse manifest at `[ROOT]/foo/script.rs` + +Caused by: + `bin` is not allowed in embedded manifests + +"#]]) + .run(); +} + +#[cargo_test] +fn disallow_explicit_example() { + let p = cargo_test_support::project() + .file( + "script.rs", + r#" +---- +package.edition = "2021" + +[[example]] +name = "script" +path = "script.rs" +---- + +fn main() {} +"#, + ) + .build(); + + p.cargo("-Zscript -v script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_status(101) + .with_stderr_data(str![[r#" +[ERROR] failed to parse manifest at `[ROOT]/foo/script.rs` + +Caused by: + `example` is not allowed in embedded manifests + +"#]]) + .run(); +} + +#[cargo_test] +fn disallow_explicit_test() { + let p = cargo_test_support::project() + .file( + "script.rs", + r#" +---- +package.edition = "2021" + +[[test]] +name = "script" +path = "script.rs" +---- + +fn main() {} +"#, + ) + .build(); + + p.cargo("-Zscript -v script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_status(101) + .with_stderr_data(str![[r#" +[ERROR] failed to parse manifest at `[ROOT]/foo/script.rs` + +Caused by: + `test` is not allowed in embedded manifests + +"#]]) + .run(); +} + +#[cargo_test] +fn disallow_explicit_bench() { + let p = cargo_test_support::project() + .file( + "script.rs", + r#" +---- +package.edition = "2021" + +[[bench]] +name = "script" +path = "script.rs" +---- + +fn main() {} +"#, + ) + .build(); + + p.cargo("-Zscript -v script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_status(101) + .with_stderr_data(str![[r#" +[ERROR] failed to parse manifest at `[ROOT]/foo/script.rs` + +Caused by: + `bench` is not allowed in embedded manifests + +"#]]) + .run(); +} + +#[cargo_test] +fn disallow_explicit_package_build() { + let p = cargo_test_support::project() + .file( + "script.rs", + r#" +---- +[package] +edition = "2021" +build = "script.rs" +---- + +fn main() {} +"#, + ) + .build(); + + p.cargo("-Zscript -v script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_status(101) + .with_stderr_data(str![[r#" +[ERROR] failed to parse manifest at `[ROOT]/foo/script.rs` + +Caused by: + `package.build` is not allowed in embedded manifests + +"#]]) + .run(); +} + +#[cargo_test] +fn disallow_explicit_package_links() { + let p = cargo_test_support::project() + .file( + "script.rs", + r#" +---- +[package] +edition = "2021" +links = "script" +---- + +fn main() {} +"#, + ) + .build(); + + p.cargo("-Zscript -v script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_status(101) + .with_stderr_data(str![[r#" +[ERROR] failed to parse manifest at `[ROOT]/foo/script.rs` + +Caused by: + `package.links` is not allowed in embedded manifests + +"#]]) + .run(); +} + +#[cargo_test] +fn disallow_explicit_package_autolib() { + let p = cargo_test_support::project() + .file( + "script.rs", + r#" +---- +[package] +edition = "2021" +autolib = true +---- + +fn main() {} +"#, + ) + .build(); + + p.cargo("-Zscript -v script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_status(101) + .with_stderr_data(str![[r#" +[ERROR] failed to parse manifest at `[ROOT]/foo/script.rs` + +Caused by: + `package.autolib` is not allowed in embedded manifests + +"#]]) + .run(); +} + +#[cargo_test] +fn disallow_explicit_package_autobins() { + let p = cargo_test_support::project() + .file( + "script.rs", + r#" +---- +[package] +edition = "2021" +autobins = true +---- + +fn main() {} +"#, + ) + .build(); + + p.cargo("-Zscript -v script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_status(101) + .with_stderr_data(str![[r#" +[ERROR] failed to parse manifest at `[ROOT]/foo/script.rs` + +Caused by: + `package.autobins` is not allowed in embedded manifests + +"#]]) + .run(); +} + +#[cargo_test] +fn disallow_explicit_package_autoexamples() { + let p = cargo_test_support::project() + .file( + "script.rs", + r#" +---- +[package] +edition = "2021" +autoexamples = true +---- + +fn main() {} +"#, + ) + .build(); + + p.cargo("-Zscript -v script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_status(101) + .with_stderr_data(str![[r#" +[ERROR] failed to parse manifest at `[ROOT]/foo/script.rs` + +Caused by: + `package.autoexamples` is not allowed in embedded manifests + +"#]]) + .run(); +} + +#[cargo_test] +fn disallow_explicit_package_autotests() { + let p = cargo_test_support::project() + .file( + "script.rs", + r#" +---- +[package] +edition = "2021" +autotests = true +---- + +fn main() {} +"#, + ) + .build(); + + p.cargo("-Zscript -v script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_status(101) + .with_stderr_data(str![[r#" +[ERROR] failed to parse manifest at `[ROOT]/foo/script.rs` + +Caused by: + `package.autotests` is not allowed in embedded manifests + +"#]]) + .run(); +} + +#[cargo_test] +fn disallow_explicit_package_autobenches() { + let p = cargo_test_support::project() + .file( + "script.rs", + r#" +---- +[package] +edition = "2021" +autobenches = true +---- + +fn main() {} +"#, + ) + .build(); + + p.cargo("-Zscript -v script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_status(101) + .with_stderr_data(str![[r#" +[ERROR] failed to parse manifest at `[ROOT]/foo/script.rs` + +Caused by: + `package.autobenches` is not allowed in embedded manifests + +"#]]) + .run(); +} + #[cargo_test(nightly, reason = "edition2024 hasn't hit stable yet")] fn implicit_target_dir() { let script = ECHO_SCRIPT;