diff --git a/crates/cargo-util-schemas/manifest.schema.json b/crates/cargo-util-schemas/manifest.schema.json index 2024fea8411..c584ad25570 100644 --- a/crates/cargo-util-schemas/manifest.schema.json +++ b/crates/cargo-util-schemas/manifest.schema.json @@ -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", diff --git a/crates/cargo-util-schemas/src/manifest/mod.rs b/crates/cargo-util-schemas/src/manifest/mod.rs index b28aa38fb60..194a5b29564 100644 --- a/crates/cargo-util-schemas/src/manifest/mod.rs +++ b/crates/cargo-util-schemas/src/manifest/mod.rs @@ -182,7 +182,7 @@ pub struct TomlPackage { pub name: Option, pub version: Option, pub authors: Option, - pub build: Option, + pub build: Option, pub metabuild: Option, pub default_target: Option, pub forced_target: Option, @@ -254,12 +254,13 @@ impl TomlPackage { self.authors.as_ref().map(|v| v.normalized()).transpose() } - pub fn normalized_build(&self) -> Result, 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, 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), +} + +impl<'de> Deserialize<'de> for TomlPackageBuild { + fn deserialize(deserializer: D) -> Result + 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))] diff --git a/src/cargo/core/features.rs b/src/cargo/core/features.rs index a79695e872f..a0a1a9371f9 100644 --- a/src/cargo/core/features.rs +++ b/src/cargo/core/features.rs @@ -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. diff --git a/src/cargo/ops/vendor.rs b/src/cargo/ops/vendor.rs index ce1f688d182..a0067e79c3b 100644 --- a/src/cargo/ops/vendor.rs +++ b/src/cargo/ops/vendor.rs @@ -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 { diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index 83d10678368..b93ec97fd22 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -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> { 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 diff --git a/src/cargo/util/toml/targets.rs b/src/cargo/util/toml/targets.rs index e2f61d5513a..e809188ef96 100644 --- a/src/cargo/util/toml/targets.rs +++ b/src/cargo/util/toml/targets.rs @@ -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 { +pub fn normalize_build( + build: Option<&TomlPackageBuild>, + package_root: &Path, +) -> CargoResult> { 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(TomlPackageBuild::Auto(true)) => { + Ok(Some(TomlPackageBuild::SingleScript(BUILD_RS.to_owned()))) } - Some(StringOrBool::Bool(true)) => Some(StringOrBool::String(BUILD_RS.to_owned())), + Some(TomlPackageBuild::MultipleScript(_scripts)) => Ok(build.cloned()), } } diff --git a/src/doc/src/reference/unstable.md b/src/doc/src/reference/unstable.md index d9022d761d2..54fe74e11c4 100644 --- a/src/doc/src/reference/unstable.md +++ b/src/doc/src/reference/unstable.md @@ -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) diff --git a/tests/testsuite/bad_config.rs b/tests/testsuite/bad_config.rs index 8e7316dd246..baced0c9517 100644 --- a/tests/testsuite/bad_config.rs +++ b/tests/testsuite/bad_config.rs @@ -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 diff --git a/tests/testsuite/build_scripts_multiple.rs b/tests/testsuite/build_scripts_multiple.rs new file mode 100644 index 00000000000..4f81b8f421f --- /dev/null +++ b/tests/testsuite/build_scripts_multiple.rs @@ -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(); +} diff --git a/tests/testsuite/main.rs b/tests/testsuite/main.rs index 038953dc425..d841cd38544 100644 --- a/tests/testsuite/main.rs +++ b/tests/testsuite/main.rs @@ -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; diff --git a/tests/testsuite/package.rs b/tests/testsuite/package.rs index 65fd4b7714c..4d70f5bd74d 100644 --- a/tests/testsuite/package.rs +++ b/tests/testsuite/package.rs @@ -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)