Fork StringOrBool into new TomlPackageBuild type and update manifest.schema.json

This commit is contained in:
Naman Garg 2025-06-07 04:14:44 +05:30
parent 79730837cb
commit 92b00aefc3
No known key found for this signature in database
5 changed files with 69 additions and 25 deletions

View File

@ -269,7 +269,7 @@
"build": {
"anyOf": [
{
"$ref": "#/$defs/StringOrBool"
"$ref": "#/$defs/TomlPackageBuild"
},
{
"type": "null"
@ -540,13 +540,15 @@
}
]
},
"StringOrBool": {
"TomlPackageBuild": {
"anyOf": [
{
"type": "string"
"description": "If build scripts are disabled or enabled.\n If true, `build.rs` in the root folder will be the build script.",
"type": "boolean"
},
{
"type": "boolean"
"description": "Path of Build Script if there's just one script.",
"type": "string"
}
]
},
@ -596,6 +598,16 @@
}
]
},
"StringOrBool": {
"anyOf": [
{
"type": "string"
},
{
"type": "boolean"
}
]
},
"TomlValue": true,
"TomlTarget": {
"type": "object",

View File

@ -182,7 +182,7 @@ pub struct TomlPackage {
pub name: Option<PackageName>,
pub version: Option<InheritableSemverVersion>,
pub authors: Option<InheritableVecString>,
pub build: Option<StringOrBool>,
pub build: Option<TomlPackageBuild>,
pub metabuild: Option<StringOrVec>,
pub default_target: Option<String>,
pub forced_target: Option<String>,
@ -257,9 +257,9 @@ impl TomlPackage {
pub fn normalized_build(&self) -> Result<Option<&String>, UnresolvedError> {
let build = self.build.as_ref().ok_or(UnresolvedError)?;
match build {
StringOrBool::Bool(false) => Ok(None),
StringOrBool::Bool(true) => Err(UnresolvedError),
StringOrBool::String(value) => Ok(Some(value)),
TomlPackageBuild::Auto(false) => Ok(None),
TomlPackageBuild::Auto(true) => Err(UnresolvedError),
TomlPackageBuild::SingleScript(value) => Ok(Some(value)),
}
}
@ -1702,6 +1702,30 @@ impl<'de> Deserialize<'de> for StringOrBool {
}
}
#[derive(Clone, Debug, Serialize, Eq, PartialEq)]
#[serde(untagged)]
#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
pub enum TomlPackageBuild {
/// If build scripts are disabled or enabled.
/// If true, `build.rs` in the root folder will be the build script.
Auto(bool),
/// Path of Build Script if there's just one script.
SingleScript(String),
}
impl<'de> Deserialize<'de> for TomlPackageBuild {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
UntaggedEnumVisitor::new()
.bool(|b| Ok(TomlPackageBuild::Auto(b)))
.string(|s| Ok(TomlPackageBuild::SingleScript(s.to_owned())))
.deserialize(deserializer)
}
}
#[derive(PartialEq, Clone, Debug, Serialize)]
#[serde(untagged)]
#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]

View File

@ -12,6 +12,7 @@ use crate::util::{try_canonicalize, CargoResult, GlobalContext};
use anyhow::{bail, Context as _};
use cargo_util::{paths, Sha256};
use cargo_util_schemas::core::SourceKind;
use cargo_util_schemas::manifest::TomlPackageBuild;
use serde::Serialize;
use walkdir::WalkDir;
@ -513,7 +514,8 @@ fn prepare_toml_for_vendor(
.package
.as_mut()
.expect("venedored manifests must have packages");
if let Some(cargo_util_schemas::manifest::StringOrBool::String(path)) = &package.build {
// Validates if build script file exists. If not, warn and ignore.
if let Some(TomlPackageBuild::SingleScript(path)) = &package.build {
let path = paths::normalize_path(Path::new(path));
let included = packaged_files.contains(&path);
let build = if included {
@ -522,13 +524,13 @@ fn prepare_toml_for_vendor(
.into_string()
.map_err(|_err| anyhow::format_err!("non-UTF8 `package.build`"))?;
let path = crate::util::toml::normalize_path_string_sep(path);
cargo_util_schemas::manifest::StringOrBool::String(path)
TomlPackageBuild::SingleScript(path)
} else {
gctx.shell().warn(format!(
"ignoring `package.build` as `{}` is not included in the published package",
path.display()
))?;
cargo_util_schemas::manifest::StringOrBool::Bool(false)
TomlPackageBuild::Auto(false)
};
package.build = Some(build);
}

View File

@ -13,7 +13,7 @@ use cargo_platform::Platform;
use cargo_util::paths;
use cargo_util_schemas::manifest::{
self, PackageName, PathBaseName, TomlDependency, TomlDetailedDependency, TomlManifest,
TomlWorkspace,
TomlPackageBuild, TomlWorkspace,
};
use cargo_util_schemas::manifest::{RustVersion, StringOrBool};
use itertools::Itertools;
@ -670,7 +670,7 @@ fn normalize_package_toml<'a>(
.transpose()?
.map(manifest::InheritableField::Value);
let build = if is_embedded {
Some(StringOrBool::Bool(false))
Some(TomlPackageBuild::Auto(false))
} else {
targets::normalize_build(original_package.build.as_ref(), package_root)
};
@ -2885,7 +2885,8 @@ fn prepare_toml_for_publish(
let mut package = me.package().unwrap().clone();
package.workspace = None;
if let Some(StringOrBool::String(path)) = &package.build {
// Validates if build script file exists. If not, warn and ignore.
if let Some(TomlPackageBuild::SingleScript(path)) = &package.build {
let path = Path::new(path).to_path_buf();
let included = packaged_files.map(|i| i.contains(&path)).unwrap_or(true);
let build = if included {
@ -2894,13 +2895,13 @@ fn prepare_toml_for_publish(
.into_string()
.map_err(|_err| anyhow::format_err!("non-UTF8 `package.build`"))?;
let path = normalize_path_string_sep(path);
StringOrBool::String(path)
TomlPackageBuild::SingleScript(path)
} else {
ws.gctx().shell().warn(format!(
"ignoring `package.build` as `{}` is not included in the published package",
path.display()
))?;
StringOrBool::Bool(false)
TomlPackageBuild::Auto(false)
};
package.build = Some(build);
}

View File

@ -17,8 +17,8 @@ use std::path::{Path, PathBuf};
use anyhow::Context as _;
use cargo_util::paths;
use cargo_util_schemas::manifest::{
PathValue, StringOrBool, StringOrVec, TomlBenchTarget, TomlBinTarget, TomlExampleTarget,
TomlLibTarget, TomlManifest, TomlTarget, TomlTestTarget,
PathValue, StringOrVec, TomlBenchTarget, TomlBinTarget, TomlExampleTarget, TomlLibTarget,
TomlManifest, TomlPackageBuild, TomlTarget, TomlTestTarget,
};
use crate::core::compiler::rustdoc::RustdocScrapeExamples;
@ -1076,7 +1076,10 @@ Cargo doesn't know which to use because multiple target files found at `{}` and
/// Returns the path to the build script if one exists for this crate.
#[tracing::instrument(skip_all)]
pub fn normalize_build(build: Option<&StringOrBool>, package_root: &Path) -> Option<StringOrBool> {
pub fn normalize_build(
build: Option<&TomlPackageBuild>,
package_root: &Path,
) -> Option<TomlPackageBuild> {
const BUILD_RS: &str = "build.rs";
match build {
None => {
@ -1084,21 +1087,23 @@ pub fn normalize_build(build: Option<&StringOrBool>, package_root: &Path) -> Opt
// a build script.
let build_rs = package_root.join(BUILD_RS);
if build_rs.is_file() {
Some(StringOrBool::String(BUILD_RS.to_owned()))
Some(TomlPackageBuild::SingleScript(BUILD_RS.to_owned()))
} else {
Some(StringOrBool::Bool(false))
Some(TomlPackageBuild::Auto(false))
}
}
// Explicitly no build script.
Some(StringOrBool::Bool(false)) => build.cloned(),
Some(StringOrBool::String(build_file)) => {
Some(TomlPackageBuild::Auto(false)) => build.cloned(),
Some(TomlPackageBuild::SingleScript(build_file)) => {
let build_file = paths::normalize_path(Path::new(build_file));
let build = build_file.into_os_string().into_string().expect(
"`build_file` started as a String and `normalize_path` shouldn't have changed that",
);
Some(StringOrBool::String(build))
Some(TomlPackageBuild::SingleScript(build))
}
Some(TomlPackageBuild::Auto(true)) => {
Some(TomlPackageBuild::SingleScript(BUILD_RS.to_owned()))
}
Some(StringOrBool::Bool(true)) => Some(StringOrBool::String(BUILD_RS.to_owned())),
}
}