mirror of
https://github.com/rust-lang/cargo.git
synced 2025-09-28 11:20:36 +00:00
feat(toml): Parse support for multiple build scripts (#15630)
Hi Everyone! This is PR for the manifest parsing of the first milestone of [GSoC Project : Build Script Delegation](https://summerofcode.withgoogle.com/programs/2025/projects/nUt4PdAA) ### What does this PR try to resolve? Currently, just a single build script is allowed for each package. This PR will allow users to create and use multiple build scripts, and is backward compatible with single script as well as boolean values. **Motivation :** This will help users to maintain separate smaller and cleaner build scripts instead of one large build script. This is also necessary for build script delegation. **Open questions:** - What should the build script target names be? - Currently they use the file stem of the build script which could run into conflicts **Known Issues:** - This is just parsing support, and currently, only the first build script of the array is actually executed. ### How to test and review this PR? There is a feature gate `multiple-build-scripts` that can be passed via `cargo-features` in `Cargo.toml`. So, you have to add ```toml cargo-features = ["multiple-build-scripts"] ``` Preferably on the top of the `Cargo.toml` and use nightly toolchain to use the feature **This PR is ready to be reviewed and merged**
This commit is contained in:
commit
f94409aec8
@ -269,7 +269,7 @@
|
||||
"build": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/$defs/StringOrBool"
|
||||
"$ref": "#/$defs/TomlPackageBuild"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
@ -540,13 +540,22 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"StringOrBool": {
|
||||
"TomlPackageBuild": {
|
||||
"anyOf": [
|
||||
{
|
||||
"description": "If build scripts are disabled or enabled.\n If true, `build.rs` in the root folder will be the build script.",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"description": "Path of Build Script if there's just one script.",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
"description": "Vector of paths if multiple build script are to be used.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -596,6 +605,16 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"StringOrBool": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
},
|
||||
"TomlValue": true,
|
||||
"TomlTarget": {
|
||||
"type": "object",
|
||||
|
@ -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>,
|
||||
@ -254,12 +254,13 @@ impl TomlPackage {
|
||||
self.authors.as_ref().map(|v| v.normalized()).transpose()
|
||||
}
|
||||
|
||||
pub fn normalized_build(&self) -> Result<Option<&String>, UnresolvedError> {
|
||||
let readme = self.build.as_ref().ok_or(UnresolvedError)?;
|
||||
match readme {
|
||||
StringOrBool::Bool(false) => Ok(None),
|
||||
StringOrBool::Bool(true) => Err(UnresolvedError),
|
||||
StringOrBool::String(value) => Ok(Some(value)),
|
||||
pub fn normalized_build(&self) -> Result<Option<&[String]>, UnresolvedError> {
|
||||
let build = self.build.as_ref().ok_or(UnresolvedError)?;
|
||||
match build {
|
||||
TomlPackageBuild::Auto(false) => Ok(None),
|
||||
TomlPackageBuild::Auto(true) => Err(UnresolvedError),
|
||||
TomlPackageBuild::SingleScript(value) => Ok(Some(std::slice::from_ref(value))),
|
||||
TomlPackageBuild::MultipleScript(scripts) => Ok(Some(scripts)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1702,6 +1703,34 @@ 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),
|
||||
|
||||
/// Vector of paths if multiple build script are to be used.
|
||||
MultipleScript(Vec<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())))
|
||||
.seq(|value| value.deserialize().map(TomlPackageBuild::MultipleScript))
|
||||
.deserialize(deserializer)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Debug, Serialize)]
|
||||
#[serde(untagged)]
|
||||
#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
|
||||
|
@ -577,6 +577,9 @@ features! {
|
||||
|
||||
/// Allows use of editions that are not yet stable.
|
||||
(unstable, unstable_editions, "", "reference/unstable.html#unstable-editions"),
|
||||
|
||||
/// Allows use of multiple build scripts.
|
||||
(unstable, multiple_build_scripts, "", "reference/unstable.html#multiple-build-scripts"),
|
||||
}
|
||||
|
||||
/// Status and metadata for a single unstable feature.
|
||||
|
@ -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,24 +514,31 @@ 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 {
|
||||
let path = paths::normalize_path(Path::new(path));
|
||||
let included = packaged_files.contains(&path);
|
||||
let build = if included {
|
||||
let path = path
|
||||
.into_os_string()
|
||||
.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)
|
||||
} 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)
|
||||
};
|
||||
package.build = Some(build);
|
||||
// Validates if build script file is included in package. If not, warn and ignore.
|
||||
if let Some(custom_build_scripts) = package.normalized_build().expect("previously normalized") {
|
||||
let mut included_scripts = Vec::new();
|
||||
for script in custom_build_scripts {
|
||||
let path = paths::normalize_path(Path::new(script));
|
||||
let included = packaged_files.contains(&path);
|
||||
if included {
|
||||
let path = path
|
||||
.into_os_string()
|
||||
.into_string()
|
||||
.map_err(|_err| anyhow::format_err!("non-UTF8 `package.build`"))?;
|
||||
let path = crate::util::toml::normalize_path_string_sep(path);
|
||||
included_scripts.push(path);
|
||||
} else {
|
||||
gctx.shell().warn(format!(
|
||||
"ignoring `package.build` entry `{}` as it is not included in the published package",
|
||||
path.display()
|
||||
))?;
|
||||
}
|
||||
}
|
||||
package.build = Some(match included_scripts.len() {
|
||||
0 => TomlPackageBuild::Auto(false),
|
||||
1 => TomlPackageBuild::SingleScript(included_scripts[0].clone()),
|
||||
_ => TomlPackageBuild::MultipleScript(included_scripts),
|
||||
});
|
||||
}
|
||||
|
||||
let lib = if let Some(target) = &me.lib {
|
||||
|
@ -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;
|
||||
@ -344,6 +344,7 @@ fn normalize_toml(
|
||||
is_embedded,
|
||||
gctx,
|
||||
&inherit,
|
||||
features,
|
||||
)?;
|
||||
let package_name = &normalized_package
|
||||
.normalized_name()
|
||||
@ -607,6 +608,7 @@ fn normalize_package_toml<'a>(
|
||||
is_embedded: bool,
|
||||
gctx: &GlobalContext,
|
||||
inherit: &dyn Fn() -> CargoResult<&'a InheritableFields>,
|
||||
features: &Features,
|
||||
) -> CargoResult<Box<manifest::TomlPackage>> {
|
||||
let package_root = manifest_file.parent().unwrap();
|
||||
|
||||
@ -670,9 +672,12 @@ 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)
|
||||
if let Some(TomlPackageBuild::MultipleScript(_)) = original_package.build {
|
||||
features.require(Feature::multiple_build_scripts())?;
|
||||
}
|
||||
targets::normalize_build(original_package.build.as_ref(), package_root)?
|
||||
};
|
||||
let metabuild = original_package.metabuild.clone();
|
||||
let default_target = original_package.default_target.clone();
|
||||
@ -2885,24 +2890,32 @@ fn prepare_toml_for_publish(
|
||||
|
||||
let mut package = me.package().unwrap().clone();
|
||||
package.workspace = None;
|
||||
if let Some(StringOrBool::String(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 {
|
||||
let path = path
|
||||
.into_os_string()
|
||||
.into_string()
|
||||
.map_err(|_err| anyhow::format_err!("non-UTF8 `package.build`"))?;
|
||||
let path = normalize_path_string_sep(path);
|
||||
StringOrBool::String(path)
|
||||
} else {
|
||||
ws.gctx().shell().warn(format!(
|
||||
"ignoring `package.build` as `{}` is not included in the published package",
|
||||
path.display()
|
||||
))?;
|
||||
StringOrBool::Bool(false)
|
||||
};
|
||||
package.build = Some(build);
|
||||
// Validates if build script file is included in package. If not, warn and ignore.
|
||||
if let Some(custom_build_scripts) = package.normalized_build().expect("previously normalized") {
|
||||
let mut included_scripts = Vec::new();
|
||||
for script in custom_build_scripts {
|
||||
let path = Path::new(script).to_path_buf();
|
||||
let included = packaged_files.map(|i| i.contains(&path)).unwrap_or(true);
|
||||
if included {
|
||||
let path = path
|
||||
.into_os_string()
|
||||
.into_string()
|
||||
.map_err(|_err| anyhow::format_err!("non-UTF8 `package.build`"))?;
|
||||
let path = normalize_path_string_sep(path);
|
||||
included_scripts.push(path);
|
||||
} else {
|
||||
ws.gctx().shell().warn(format!(
|
||||
"ignoring `package.build` entry `{}` as it is not included in the published package",
|
||||
path.display()
|
||||
))?;
|
||||
}
|
||||
}
|
||||
|
||||
package.build = Some(match included_scripts.len() {
|
||||
0 => TomlPackageBuild::Auto(false),
|
||||
1 => TomlPackageBuild::SingleScript(included_scripts[0].clone()),
|
||||
_ => TomlPackageBuild::MultipleScript(included_scripts),
|
||||
});
|
||||
}
|
||||
let current_resolver = package
|
||||
.resolver
|
||||
|
@ -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;
|
||||
@ -105,19 +105,21 @@ pub(super) fn to_targets(
|
||||
if metabuild.is_some() {
|
||||
anyhow::bail!("cannot specify both `metabuild` and `build`");
|
||||
}
|
||||
let custom_build = Path::new(custom_build);
|
||||
let name = format!(
|
||||
"build-script-{}",
|
||||
custom_build
|
||||
.file_stem()
|
||||
.and_then(|s| s.to_str())
|
||||
.unwrap_or("")
|
||||
);
|
||||
targets.push(Target::custom_build_target(
|
||||
&name,
|
||||
package_root.join(custom_build),
|
||||
edition,
|
||||
));
|
||||
for script in custom_build {
|
||||
let script_path = Path::new(script);
|
||||
let name = format!(
|
||||
"build-script-{}",
|
||||
script_path
|
||||
.file_stem()
|
||||
.and_then(|s| s.to_str())
|
||||
.unwrap_or("")
|
||||
);
|
||||
targets.push(Target::custom_build_target(
|
||||
&name,
|
||||
package_root.join(script_path),
|
||||
edition,
|
||||
));
|
||||
}
|
||||
}
|
||||
if let Some(metabuild) = metabuild {
|
||||
// Verify names match available build deps.
|
||||
@ -1076,7 +1078,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,
|
||||
) -> CargoResult<Option<TomlPackageBuild>> {
|
||||
const BUILD_RS: &str = "build.rs";
|
||||
match build {
|
||||
None => {
|
||||
@ -1084,21 +1089,24 @@ 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()))
|
||||
Ok(Some(TomlPackageBuild::SingleScript(BUILD_RS.to_owned())))
|
||||
} else {
|
||||
Some(StringOrBool::Bool(false))
|
||||
Ok(Some(TomlPackageBuild::Auto(false)))
|
||||
}
|
||||
}
|
||||
// Explicitly no build script.
|
||||
Some(StringOrBool::Bool(false)) => build.cloned(),
|
||||
Some(StringOrBool::String(build_file)) => {
|
||||
Some(TomlPackageBuild::Auto(false)) => Ok(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))
|
||||
Ok(Some(TomlPackageBuild::SingleScript(build)))
|
||||
}
|
||||
Some(StringOrBool::Bool(true)) => Some(StringOrBool::String(BUILD_RS.to_owned())),
|
||||
Some(TomlPackageBuild::Auto(true)) => {
|
||||
Ok(Some(TomlPackageBuild::SingleScript(BUILD_RS.to_owned())))
|
||||
}
|
||||
Some(TomlPackageBuild::MultipleScript(_scripts)) => Ok(build.cloned()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,6 +66,7 @@ Each new feature described below should explain how to use it.
|
||||
* [-Z allow-features](#allow-features) --- Provides a way to restrict which unstable features are used.
|
||||
* Build scripts and linking
|
||||
* [Metabuild](#metabuild) --- Provides declarative build scripts.
|
||||
* [Multiple Build Scripts](#multiple-build-scripts) --- Allows use of multiple build scripts.
|
||||
* Resolver and features
|
||||
* [no-index-update](#no-index-update) --- Prevents cargo from updating the index cache.
|
||||
* [avoid-dev-deps](#avoid-dev-deps) --- Prevents the resolver from including dev-dependencies during resolution.
|
||||
@ -332,6 +333,24 @@ extra-info = "qwerty"
|
||||
Metabuild packages should have a public function called `metabuild` that
|
||||
performs the same actions as a regular `build.rs` script would perform.
|
||||
|
||||
## Multiple Build Scripts
|
||||
* Tracking Issue: [#14903](https://github.com/rust-lang/cargo/issues/14903)
|
||||
* Original Pull Request: [#15630](https://github.com/rust-lang/cargo/pull/15630)
|
||||
|
||||
Multiple Build Scripts feature allows you to have multiple build scripts in your package.
|
||||
|
||||
Include `cargo-features` at the top of `Cargo.toml` and add `multiple-build-scripts` to enable feature.
|
||||
Add the paths of the build scripts as an array in `package.build`. For example:
|
||||
|
||||
```toml
|
||||
cargo-features = ["multiple-build-scripts"]
|
||||
|
||||
[package]
|
||||
name = "mypackage"
|
||||
version = "0.0.1"
|
||||
build = ["foo.rs", "bar.rs"]
|
||||
```
|
||||
|
||||
## public-dependency
|
||||
* Tracking Issue: [#44663](https://github.com/rust-lang/rust/issues/44663)
|
||||
|
||||
|
@ -2733,7 +2733,7 @@ fn bad_opt_level() {
|
||||
p.cargo("check")
|
||||
.with_status(101)
|
||||
.with_stderr_data(str![[r#"
|
||||
[ERROR] invalid type: integer `3`, expected a boolean or string
|
||||
[ERROR] invalid type: integer `3`, expected a boolean, string or array
|
||||
--> Cargo.toml:7:25
|
||||
|
|
||||
7 | build = 3
|
||||
|
469
tests/testsuite/build_scripts_multiple.rs
Normal file
469
tests/testsuite/build_scripts_multiple.rs
Normal file
@ -0,0 +1,469 @@
|
||||
//! Tests for multiple build scripts feature.
|
||||
|
||||
use cargo_test_support::compare::assert_e2e;
|
||||
use cargo_test_support::git;
|
||||
use cargo_test_support::prelude::*;
|
||||
use cargo_test_support::publish::validate_crate_contents;
|
||||
use cargo_test_support::str;
|
||||
use cargo_test_support::{project, Project};
|
||||
use std::fs::File;
|
||||
|
||||
#[cargo_test]
|
||||
fn build_without_feature_enabled_aborts_with_error() {
|
||||
let p = project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
build = ["build1.rs", "build2.rs"]
|
||||
"#,
|
||||
)
|
||||
.file("src/main.rs", "fn main() {}")
|
||||
.file("build1.rs", "fn main() {}")
|
||||
.file("build2.rs", "fn main() {}")
|
||||
.build();
|
||||
p.cargo("check")
|
||||
.masquerade_as_nightly_cargo(&["multiple-build-scripts"])
|
||||
.with_status(101)
|
||||
.with_stderr_data(str![[r#"
|
||||
[ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml`
|
||||
|
||||
Caused by:
|
||||
feature `multiple-build-scripts` is required
|
||||
|
||||
The package requires the Cargo feature called `multiple-build-scripts`, but that feature is not stabilized in this version of Cargo ([..]).
|
||||
Consider adding `cargo-features = ["multiple-build-scripts"]` to the top of Cargo.toml (above the [package] table) to tell Cargo you are opting in to use this unstable feature.
|
||||
See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#multiple-build-scripts for more information about the status of this feature.
|
||||
|
||||
"#]])
|
||||
.run();
|
||||
}
|
||||
|
||||
fn basic_empty_project() -> Project {
|
||||
project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
cargo-features = ["multiple-build-scripts"]
|
||||
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
build = ["build1.rs", "build2.rs"]
|
||||
"#,
|
||||
)
|
||||
.file("src/main.rs", "fn main() {}")
|
||||
.file("build1.rs", "fn main() {}")
|
||||
.file("build2.rs", "fn main() {}")
|
||||
.build()
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn empty_multiple_build_script_project() {
|
||||
let p = basic_empty_project();
|
||||
p.cargo("check")
|
||||
.masquerade_as_nightly_cargo(&["multiple-build-scripts"])
|
||||
.with_status(0)
|
||||
.with_stderr_data(str![[r#"
|
||||
[COMPILING] foo v0.1.0 ([ROOT]/foo)
|
||||
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
|
||||
|
||||
"#]])
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn multiple_build_scripts_metadata() {
|
||||
let p = basic_empty_project();
|
||||
p.cargo("metadata --format-version=1")
|
||||
.masquerade_as_nightly_cargo(&["multiple-build-scripts"])
|
||||
.with_status(0)
|
||||
.with_stderr_data("")
|
||||
.with_stdout_data(
|
||||
str![[r#"
|
||||
{
|
||||
"metadata": null,
|
||||
"packages": [
|
||||
{
|
||||
"authors": [],
|
||||
"categories": [],
|
||||
"default_run": null,
|
||||
"dependencies": [],
|
||||
"description": null,
|
||||
"documentation": null,
|
||||
"edition": "2024",
|
||||
"features": {},
|
||||
"homepage": null,
|
||||
"id": "path+[ROOTURL]/foo#0.1.0",
|
||||
"keywords": [],
|
||||
"license": null,
|
||||
"license_file": null,
|
||||
"links": null,
|
||||
"manifest_path": "[ROOT]/foo/Cargo.toml",
|
||||
"metadata": null,
|
||||
"name": "foo",
|
||||
"publish": null,
|
||||
"readme": null,
|
||||
"repository": null,
|
||||
"rust_version": null,
|
||||
"source": null,
|
||||
"targets": [
|
||||
{
|
||||
"crate_types": [
|
||||
"bin"
|
||||
],
|
||||
"doc": true,
|
||||
"doctest": false,
|
||||
"edition": "2024",
|
||||
"kind": [
|
||||
"bin"
|
||||
],
|
||||
"name": "foo",
|
||||
"src_path": "[ROOT]/foo/src/main.rs",
|
||||
"test": true
|
||||
},
|
||||
{
|
||||
"crate_types": [
|
||||
"bin"
|
||||
],
|
||||
"doc": false,
|
||||
"doctest": false,
|
||||
"edition": "2024",
|
||||
"kind": [
|
||||
"custom-build"
|
||||
],
|
||||
"name": "build-script-build1",
|
||||
"src_path": "[ROOT]/foo/build1.rs",
|
||||
"test": false
|
||||
},
|
||||
{
|
||||
"crate_types": [
|
||||
"bin"
|
||||
],
|
||||
"doc": false,
|
||||
"doctest": false,
|
||||
"edition": "2024",
|
||||
"kind": [
|
||||
"custom-build"
|
||||
],
|
||||
"name": "build-script-build2",
|
||||
"src_path": "[ROOT]/foo/build2.rs",
|
||||
"test": false
|
||||
}
|
||||
],
|
||||
"version": "0.1.0"
|
||||
}
|
||||
],
|
||||
"resolve": {
|
||||
"nodes": [
|
||||
{
|
||||
"dependencies": [],
|
||||
"deps": [],
|
||||
"features": [],
|
||||
"id": "path+[ROOTURL]/foo#0.1.0"
|
||||
}
|
||||
],
|
||||
"root": "path+[ROOTURL]/foo#0.1.0"
|
||||
},
|
||||
"target_directory": "[ROOT]/foo/target",
|
||||
"version": 1,
|
||||
"workspace_default_members": [
|
||||
"path+[ROOTURL]/foo#0.1.0"
|
||||
],
|
||||
"workspace_members": [
|
||||
"path+[ROOTURL]/foo#0.1.0"
|
||||
],
|
||||
"workspace_root": "[ROOT]/foo"
|
||||
}
|
||||
"#]]
|
||||
.is_json(),
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn verify_package_multiple_build_scripts() {
|
||||
let p = project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
cargo-features = ["multiple-build-scripts"]
|
||||
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
license = "MIT"
|
||||
description = "foo"
|
||||
documentation = "docs.rs/foo"
|
||||
authors = []
|
||||
|
||||
build = ["build1.rs", "build2.rs"]
|
||||
include = [ "src/main.rs", "build1.rs" ]
|
||||
"#,
|
||||
)
|
||||
.file("src/main.rs", "fn main() {}")
|
||||
.file("build1.rs", "fn main() {}")
|
||||
.file("build2.rs", "fn main() {}")
|
||||
.build();
|
||||
|
||||
p.cargo("package")
|
||||
.masquerade_as_nightly_cargo(&["multiple-build-scripts"])
|
||||
.with_status(0)
|
||||
.with_stderr_data(str![[r#"
|
||||
[PACKAGING] foo v0.1.0 ([ROOT]/foo)
|
||||
[WARNING] ignoring `package.build` entry `build2.rs` as it is not included in the published package
|
||||
[PACKAGED] 5 files, [FILE_SIZE]B ([FILE_SIZE]B compressed)
|
||||
[VERIFYING] foo v0.1.0 ([ROOT]/foo)
|
||||
[COMPILING] foo v0.1.0 ([ROOT]/foo/target/package/foo-0.1.0)
|
||||
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
|
||||
|
||||
"#]])
|
||||
.run();
|
||||
|
||||
let f = File::open(&p.root().join("target/package/foo-0.1.0.crate")).unwrap();
|
||||
validate_crate_contents(
|
||||
f,
|
||||
"foo-0.1.0.crate",
|
||||
&[
|
||||
"Cargo.toml",
|
||||
"Cargo.toml.orig",
|
||||
"src/main.rs",
|
||||
"build1.rs",
|
||||
"Cargo.lock",
|
||||
],
|
||||
[(
|
||||
"Cargo.toml",
|
||||
str![[r##"
|
||||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies.
|
||||
#
|
||||
# If you are reading this file be aware that the original Cargo.toml
|
||||
# will likely look very different (and much more reasonable).
|
||||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
cargo-features = ["multiple-build-scripts"]
|
||||
|
||||
[package]
|
||||
edition = "2024"
|
||||
name = "foo"
|
||||
version = "0.1.0"
|
||||
authors = []
|
||||
build = "build1.rs"
|
||||
include = [
|
||||
"src/main.rs",
|
||||
"build1.rs",
|
||||
]
|
||||
autolib = false
|
||||
autobins = false
|
||||
autoexamples = false
|
||||
autotests = false
|
||||
autobenches = false
|
||||
description = "foo"
|
||||
documentation = "docs.rs/foo"
|
||||
readme = false
|
||||
license = "MIT"
|
||||
|
||||
[[bin]]
|
||||
name = "foo"
|
||||
path = "src/main.rs"
|
||||
|
||||
"##]],
|
||||
)],
|
||||
);
|
||||
}
|
||||
|
||||
fn add_git_vendor_config(p: &Project, git_project: &Project) {
|
||||
p.change_file(
|
||||
".cargo/config.toml",
|
||||
&format!(
|
||||
r#"
|
||||
[source."git+{url}"]
|
||||
git = "{url}"
|
||||
replace-with = 'vendor'
|
||||
|
||||
[source.vendor]
|
||||
directory = 'vendor'
|
||||
"#,
|
||||
url = git_project.url()
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn verify_vendor_multiple_build_scripts() {
|
||||
let git_project = git::new("dep", |project| {
|
||||
project
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
cargo-features = ["multiple-build-scripts"]
|
||||
|
||||
[package]
|
||||
name = "dep"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
license = "MIT"
|
||||
description = "dependency of foo"
|
||||
documentation = "docs.rs/dep"
|
||||
authors = []
|
||||
|
||||
build = ["build1.rs", "build2.rs"]
|
||||
include = [ "src/main.rs", "build1.rs" ]
|
||||
"#,
|
||||
)
|
||||
.file("src/main.rs", "fn main() {}")
|
||||
.file("build1.rs", "fn main() {}")
|
||||
.file("build2.rs", "fn main() {}")
|
||||
});
|
||||
|
||||
let p = project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
&format!(
|
||||
r#"
|
||||
cargo-features = ["multiple-build-scripts"]
|
||||
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies.dep]
|
||||
git = '{}'
|
||||
"#,
|
||||
git_project.url()
|
||||
),
|
||||
)
|
||||
.file("src/main.rs", "fn main() {}")
|
||||
.build();
|
||||
|
||||
p.cargo("vendor --respect-source-config")
|
||||
.masquerade_as_nightly_cargo(&["multiple-build-scripts"])
|
||||
.with_status(0)
|
||||
.with_stderr_data(str![[r#"
|
||||
[UPDATING] git repository `[ROOTURL]/dep`
|
||||
[LOCKING] 1 package to latest [..] compatible version
|
||||
Vendoring dep v0.1.0 ([ROOTURL]/dep#[..]) ([ROOT]/home/.cargo/git/checkouts/dep-[HASH]/[..]) to vendor/dep
|
||||
[WARNING] ignoring `package.build` entry `build2.rs` as it is not included in the published package
|
||||
To use vendored sources, add this to your .cargo/config.toml for this project:
|
||||
|
||||
|
||||
"#]])
|
||||
.run();
|
||||
add_git_vendor_config(&p, &git_project);
|
||||
|
||||
assert_e2e().eq(
|
||||
p.read_file("vendor/dep/Cargo.toml"),
|
||||
str![[r##"
|
||||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies.
|
||||
#
|
||||
# If you are reading this file be aware that the original Cargo.toml
|
||||
# will likely look very different (and much more reasonable).
|
||||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
cargo-features = ["multiple-build-scripts"]
|
||||
|
||||
[package]
|
||||
edition = "2024"
|
||||
name = "dep"
|
||||
version = "0.1.0"
|
||||
authors = []
|
||||
build = "build1.rs"
|
||||
include = [
|
||||
"src/main.rs",
|
||||
"build1.rs",
|
||||
]
|
||||
autolib = false
|
||||
autobins = false
|
||||
autoexamples = false
|
||||
autotests = false
|
||||
autobenches = false
|
||||
description = "dependency of foo"
|
||||
documentation = "docs.rs/dep"
|
||||
readme = false
|
||||
license = "MIT"
|
||||
|
||||
[[bin]]
|
||||
name = "dep"
|
||||
path = "src/main.rs"
|
||||
|
||||
"##]],
|
||||
);
|
||||
|
||||
p.cargo("check")
|
||||
.masquerade_as_nightly_cargo(&["multiple-build-scripts"])
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn rerun_untracks_other_files() {
|
||||
let p = project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
"#,
|
||||
)
|
||||
.file("src/main.rs", "fn main() {}")
|
||||
.file(
|
||||
"build.rs",
|
||||
r#"
|
||||
fn main() {
|
||||
foo();
|
||||
bar();
|
||||
}
|
||||
fn foo() {
|
||||
let _path = "assets/foo.txt";
|
||||
}
|
||||
fn bar() {
|
||||
let path = "assets/bar.txt";
|
||||
println!("cargo::rerun-if-changed={path}");
|
||||
}"#,
|
||||
)
|
||||
.file("assets/foo.txt", "foo")
|
||||
.file("assets/bar.txt", "bar")
|
||||
.build();
|
||||
p.cargo("build").run();
|
||||
|
||||
// Editing foo.txt won't recompile, leading to unnoticed changes
|
||||
|
||||
p.change_file("assets/foo.txt", "foo updated");
|
||||
p.cargo("build -v")
|
||||
.with_stderr_data(str![[r#"
|
||||
[FRESH] foo v0.1.0 ([ROOT]/foo)
|
||||
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
|
||||
|
||||
"#]])
|
||||
.run();
|
||||
|
||||
// Editing bar.txt will recompile
|
||||
|
||||
p.change_file("assets/bar.txt", "bar updated");
|
||||
p.cargo("build -v")
|
||||
.with_stderr_data(str![[r#"
|
||||
[DIRTY] foo v0.1.0 ([ROOT]/foo): the file `assets/bar.txt` has changed ([TIME_DIFF_AFTER_LAST_BUILD])
|
||||
[COMPILING] foo v0.1.0 ([ROOT]/foo)
|
||||
[RUNNING] `[ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build`
|
||||
[RUNNING] `rustc --crate-name foo --edition=2024 src/main.rs [..] --crate-type bin [..]
|
||||
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
|
||||
|
||||
"#]])
|
||||
.run();
|
||||
}
|
@ -16,6 +16,7 @@ mod build_plan;
|
||||
mod build_script;
|
||||
mod build_script_env;
|
||||
mod build_script_extra_link_arg;
|
||||
mod build_scripts_multiple;
|
||||
mod cache_lock;
|
||||
mod cache_messages;
|
||||
mod cargo;
|
||||
|
@ -3856,7 +3856,7 @@ fn normalize_case() {
|
||||
[WARNING] manifest has no documentation, homepage or repository.
|
||||
See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info.
|
||||
[PACKAGING] foo v0.0.1 ([ROOT]/foo)
|
||||
[WARNING] ignoring `package.build` as `build.rs` is not included in the published package
|
||||
[WARNING] ignoring `package.build` entry `build.rs` as it is not included in the published package
|
||||
[WARNING] ignoring binary `foo` as `src/main.rs` is not included in the published package
|
||||
[WARNING] ignoring example `ExampleFoo` as `examples/ExampleFoo.rs` is not included in the published package
|
||||
[WARNING] ignoring test `ExplicitPath` as `tests/ExplicitPath.rs` is not included in the published package
|
||||
@ -3885,7 +3885,7 @@ src/lib.rs
|
||||
[WARNING] manifest has no documentation, homepage or repository.
|
||||
See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info.
|
||||
[PACKAGING] foo v0.0.1 ([ROOT]/foo)
|
||||
[WARNING] ignoring `package.build` as `build.rs` is not included in the published package
|
||||
[WARNING] ignoring `package.build` entry `build.rs` as it is not included in the published package
|
||||
[WARNING] ignoring binary `foo` as `src/main.rs` is not included in the published package
|
||||
[WARNING] ignoring example `ExampleFoo` as `examples/ExampleFoo.rs` is not included in the published package
|
||||
[WARNING] ignoring test `ExplicitPath` as `tests/ExplicitPath.rs` is not included in the published package
|
||||
@ -4486,7 +4486,7 @@ fn discovery_inferred_build_rs_excluded() {
|
||||
.with_stdout_data("")
|
||||
.with_stderr_data(str![[r#"
|
||||
[PACKAGING] foo v0.0.1 ([ROOT]/foo)
|
||||
[WARNING] ignoring `package.build` as `build.rs` is not included in the published package
|
||||
[WARNING] ignoring `package.build` entry `build.rs` as it is not included in the published package
|
||||
[PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed)
|
||||
[VERIFYING] foo v0.0.1 ([ROOT]/foo)
|
||||
[COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1)
|
||||
@ -4654,7 +4654,7 @@ fn discovery_explicit_build_rs_excluded() {
|
||||
.with_stdout_data("")
|
||||
.with_stderr_data(str![[r#"
|
||||
[PACKAGING] foo v0.0.1 ([ROOT]/foo)
|
||||
[WARNING] ignoring `package.build` as `build.rs` is not included in the published package
|
||||
[WARNING] ignoring `package.build` entry `build.rs` as it is not included in the published package
|
||||
[PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed)
|
||||
[VERIFYING] foo v0.0.1 ([ROOT]/foo)
|
||||
[COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1)
|
||||
|
Loading…
x
Reference in New Issue
Block a user