From 10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 4 May 2024 14:41:41 +0200 Subject: Merging upstream version 1.70.0+dfsg2. Signed-off-by: Daniel Baumann --- src/tools/cargo/tests/testsuite/standard_lib.rs | 657 ++++++++++++++++++++++++ 1 file changed, 657 insertions(+) create mode 100644 src/tools/cargo/tests/testsuite/standard_lib.rs (limited to 'src/tools/cargo/tests/testsuite/standard_lib.rs') diff --git a/src/tools/cargo/tests/testsuite/standard_lib.rs b/src/tools/cargo/tests/testsuite/standard_lib.rs new file mode 100644 index 000000000..d3be303ea --- /dev/null +++ b/src/tools/cargo/tests/testsuite/standard_lib.rs @@ -0,0 +1,657 @@ +//! Tests for building the standard library (-Zbuild-std). +//! +//! These tests all use a "mock" standard library so that we don't have to +//! rebuild the real one. There is a separate integration test `build-std` +//! which builds the real thing, but that should be avoided if possible. + +use cargo_test_support::registry::{Dependency, Package}; +use cargo_test_support::ProjectBuilder; +use cargo_test_support::{paths, project, rustc_host, Execs}; +use std::path::{Path, PathBuf}; + +struct Setup { + rustc_wrapper: PathBuf, + real_sysroot: String, +} + +fn setup() -> Setup { + // Our mock sysroot requires a few packages from crates.io, so make sure + // they're "published" to crates.io. Also edit their code a bit to make sure + // that they have access to our custom crates with custom apis. + Package::new("registry-dep-using-core", "1.0.0") + .file( + "src/lib.rs", + " + #![no_std] + + #[cfg(feature = \"mockbuild\")] + pub fn custom_api() { + } + + #[cfg(not(feature = \"mockbuild\"))] + pub fn non_sysroot_api() { + core::custom_api(); + } + ", + ) + .add_dep(Dependency::new("rustc-std-workspace-core", "*").optional(true)) + .feature("mockbuild", &["rustc-std-workspace-core"]) + .publish(); + Package::new("registry-dep-using-alloc", "1.0.0") + .file( + "src/lib.rs", + " + #![no_std] + + extern crate alloc; + + #[cfg(feature = \"mockbuild\")] + pub fn custom_api() { + } + + #[cfg(not(feature = \"mockbuild\"))] + pub fn non_sysroot_api() { + core::custom_api(); + alloc::custom_api(); + } + ", + ) + .add_dep(Dependency::new("rustc-std-workspace-core", "*").optional(true)) + .add_dep(Dependency::new("rustc-std-workspace-alloc", "*").optional(true)) + .feature( + "mockbuild", + &["rustc-std-workspace-core", "rustc-std-workspace-alloc"], + ) + .publish(); + Package::new("registry-dep-using-std", "1.0.0") + .file( + "src/lib.rs", + " + #[cfg(feature = \"mockbuild\")] + pub fn custom_api() { + } + + #[cfg(not(feature = \"mockbuild\"))] + pub fn non_sysroot_api() { + std::custom_api(); + } + ", + ) + .add_dep(Dependency::new("rustc-std-workspace-std", "*").optional(true)) + .feature("mockbuild", &["rustc-std-workspace-std"]) + .publish(); + + let p = ProjectBuilder::new(paths::root().join("rustc-wrapper")) + .file( + "src/main.rs", + r#" + use std::process::Command; + use std::env; + fn main() { + let mut args = env::args().skip(1).collect::>(); + + let is_sysroot_crate = env::var_os("RUSTC_BOOTSTRAP").is_some(); + if is_sysroot_crate { + args.push("--sysroot".to_string()); + args.push(env::var("REAL_SYSROOT").unwrap()); + } else if args.iter().any(|arg| arg == "--target") { + // build-std target unit + args.push("--sysroot".to_string()); + args.push("/path/to/nowhere".to_string()); + } else { + // host unit, do not use sysroot + } + + let ret = Command::new(&args[0]).args(&args[1..]).status().unwrap(); + std::process::exit(ret.code().unwrap_or(1)); + } + "#, + ) + .build(); + p.cargo("build").run(); + + Setup { + rustc_wrapper: p.bin("foo"), + real_sysroot: paths::sysroot(), + } +} + +fn enable_build_std(e: &mut Execs, setup: &Setup) { + // First up, force Cargo to use our "mock sysroot" which mimics what + // libstd looks like upstream. + let root = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/testsuite/mock-std"); + e.env("__CARGO_TESTS_ONLY_SRC_ROOT", &root); + + e.masquerade_as_nightly_cargo(&["build-std"]); + + // We do various shenanigans to ensure our "mock sysroot" actually links + // with the real sysroot, so we don't have to actually recompile std for + // each test. Perform all that logic here, namely: + // + // * RUSTC_WRAPPER - uses our shim executable built above to control rustc + // * REAL_SYSROOT - used by the shim executable to swap out to the real + // sysroot temporarily for some compilations + // * RUST{,DOC}FLAGS - an extra `-L` argument to ensure we can always load + // crates from the sysroot, but only indirectly through other crates. + e.env("RUSTC_WRAPPER", &setup.rustc_wrapper); + e.env("REAL_SYSROOT", &setup.real_sysroot); + let libdir = format!("/lib/rustlib/{}/lib", rustc_host()); + e.env( + "RUSTFLAGS", + format!("-Ldependency={}{}", setup.real_sysroot, libdir), + ); + e.env( + "RUSTDOCFLAGS", + format!("-Ldependency={}{}", setup.real_sysroot, libdir), + ); +} + +// Helper methods used in the tests below +trait BuildStd: Sized { + fn build_std(&mut self, setup: &Setup) -> &mut Self; + fn build_std_arg(&mut self, setup: &Setup, arg: &str) -> &mut Self; + fn target_host(&mut self) -> &mut Self; +} + +impl BuildStd for Execs { + fn build_std(&mut self, setup: &Setup) -> &mut Self { + enable_build_std(self, setup); + self.arg("-Zbuild-std"); + self + } + + fn build_std_arg(&mut self, setup: &Setup, arg: &str) -> &mut Self { + enable_build_std(self, setup); + self.arg(format!("-Zbuild-std={}", arg)); + self + } + + fn target_host(&mut self) -> &mut Self { + self.arg("--target").arg(rustc_host()); + self + } +} + +#[cargo_test(build_std_mock)] +fn basic() { + let setup = setup(); + + let p = project() + .file( + "src/main.rs", + " + fn main() { + std::custom_api(); + foo::f(); + } + + #[test] + fn smoke_bin_unit() { + std::custom_api(); + foo::f(); + } + ", + ) + .file( + "src/lib.rs", + " + extern crate alloc; + extern crate proc_macro; + + /// ``` + /// foo::f(); + /// ``` + pub fn f() { + core::custom_api(); + std::custom_api(); + alloc::custom_api(); + proc_macro::custom_api(); + } + + #[test] + fn smoke_lib_unit() { + std::custom_api(); + f(); + } + ", + ) + .file( + "tests/smoke.rs", + " + #[test] + fn smoke_integration() { + std::custom_api(); + foo::f(); + } + ", + ) + .build(); + + p.cargo("check -v").build_std(&setup).target_host().run(); + p.cargo("build").build_std(&setup).target_host().run(); + p.cargo("run").build_std(&setup).target_host().run(); + p.cargo("test").build_std(&setup).target_host().run(); +} + +#[cargo_test(build_std_mock)] +fn simple_lib_std() { + let setup = setup(); + + let p = project().file("src/lib.rs", "").build(); + p.cargo("build -v") + .build_std(&setup) + .target_host() + .with_stderr_contains("[RUNNING] `[..]--crate-name std [..]`") + .run(); + // Check freshness. + p.change_file("src/lib.rs", " "); + p.cargo("build -v") + .build_std(&setup) + .target_host() + .with_stderr_contains("[FRESH] std[..]") + .run(); +} + +#[cargo_test(build_std_mock)] +fn simple_bin_std() { + let setup = setup(); + + let p = project().file("src/main.rs", "fn main() {}").build(); + p.cargo("run -v").build_std(&setup).target_host().run(); +} + +#[cargo_test(build_std_mock)] +fn lib_nostd() { + let setup = setup(); + + let p = project() + .file( + "src/lib.rs", + r#" + #![no_std] + pub fn foo() { + assert_eq!(u8::MIN, 0); + } + "#, + ) + .build(); + p.cargo("build -v --lib") + .build_std_arg(&setup, "core") + .target_host() + .with_stderr_does_not_contain("[..]libstd[..]") + .run(); +} + +#[cargo_test(build_std_mock)] +fn check_core() { + let setup = setup(); + + let p = project() + .file("src/lib.rs", "#![no_std] fn unused_fn() {}") + .build(); + + p.cargo("check -v") + .build_std_arg(&setup, "core") + .target_host() + .with_stderr_contains("[WARNING] [..]unused_fn[..]") + .run(); +} + +#[cargo_test(build_std_mock)] +fn depend_same_as_std() { + let setup = setup(); + + let p = project() + .file( + "src/lib.rs", + r#" + pub fn f() { + registry_dep_using_core::non_sysroot_api(); + registry_dep_using_alloc::non_sysroot_api(); + registry_dep_using_std::non_sysroot_api(); + } + "#, + ) + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [dependencies] + registry-dep-using-core = "1.0" + registry-dep-using-alloc = "1.0" + registry-dep-using-std = "1.0" + "#, + ) + .build(); + + p.cargo("build -v").build_std(&setup).target_host().run(); +} + +#[cargo_test(build_std_mock)] +fn test() { + let setup = setup(); + + let p = project() + .file( + "src/lib.rs", + r#" + #[cfg(test)] + mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } + } + "#, + ) + .build(); + + p.cargo("test -v") + .build_std(&setup) + .target_host() + .with_stdout_contains("test tests::it_works ... ok") + .run(); +} + +#[cargo_test(build_std_mock)] +fn target_proc_macro() { + let setup = setup(); + + let p = project() + .file( + "src/lib.rs", + r#" + extern crate proc_macro; + pub fn f() { + let _ts = proc_macro::TokenStream::new(); + } + "#, + ) + .build(); + + p.cargo("build -v").build_std(&setup).target_host().run(); +} + +#[cargo_test(build_std_mock)] +fn bench() { + let setup = setup(); + + let p = project() + .file( + "src/lib.rs", + r#" + #![feature(test)] + extern crate test; + + #[bench] + fn b1(b: &mut test::Bencher) { + b.iter(|| ()) + } + "#, + ) + .build(); + + p.cargo("bench -v").build_std(&setup).target_host().run(); +} + +#[cargo_test(build_std_mock)] +fn doc() { + let setup = setup(); + + let p = project() + .file( + "src/lib.rs", + r#" + /// Doc + pub fn f() -> Result<(), ()> {Ok(())} + "#, + ) + .build(); + + p.cargo("doc -v").build_std(&setup).target_host().run(); +} + +#[cargo_test(build_std_mock)] +fn check_std() { + let setup = setup(); + + let p = project() + .file( + "src/lib.rs", + " + extern crate core; + extern crate alloc; + extern crate proc_macro; + pub fn f() {} + ", + ) + .file("src/main.rs", "fn main() {}") + .file( + "tests/t1.rs", + r#" + #[test] + fn t1() { + assert_eq!(1, 2); + } + "#, + ) + .build(); + + p.cargo("check -v --all-targets") + .build_std(&setup) + .target_host() + .run(); + p.cargo("check -v --all-targets --profile=test") + .build_std(&setup) + .target_host() + .run(); +} + +#[cargo_test(build_std_mock)] +fn doctest() { + let setup = setup(); + + let p = project() + .file( + "src/lib.rs", + r#" + /// Doc + /// ``` + /// std::custom_api(); + /// ``` + pub fn f() {} + "#, + ) + .build(); + + p.cargo("test --doc -v -Zdoctest-xcompile") + .build_std(&setup) + .with_stdout_contains("test src/lib.rs - f [..] ... ok") + .target_host() + .run(); +} + +#[cargo_test(build_std_mock)] +fn no_implicit_alloc() { + // Demonstrate that alloc is not implicitly in scope. + let setup = setup(); + + let p = project() + .file( + "src/lib.rs", + r#" + pub fn f() { + let _: Vec = alloc::vec::Vec::new(); + } + "#, + ) + .build(); + + p.cargo("build -v") + .build_std(&setup) + .target_host() + .with_stderr_contains("[..]use of undeclared [..]`alloc`") + .with_status(101) + .run(); +} + +#[cargo_test(build_std_mock)] +fn macro_expanded_shadow() { + // This tests a bug caused by the previous use of `--extern` to directly + // load sysroot crates. This necessitated the switch to `--sysroot` to + // retain existing behavior. See + // https://github.com/rust-lang/wg-cargo-std-aware/issues/40 for more + // detail. + let setup = setup(); + + let p = project() + .file( + "src/lib.rs", + r#" + macro_rules! a { + () => (extern crate std as alloc;) + } + a!(); + "#, + ) + .build(); + + p.cargo("build -v").build_std(&setup).target_host().run(); +} + +#[cargo_test(build_std_mock)] +fn ignores_incremental() { + // Incremental is not really needed for std, make sure it is disabled. + // Incremental also tends to have bugs that affect std libraries more than + // any other crate. + let setup = setup(); + + let p = project().file("src/lib.rs", "").build(); + p.cargo("build") + .env("CARGO_INCREMENTAL", "1") + .build_std(&setup) + .target_host() + .run(); + let incremental: Vec<_> = p + .glob(format!("target/{}/debug/incremental/*", rustc_host())) + .map(|e| e.unwrap()) + .collect(); + assert_eq!(incremental.len(), 1); + assert!(incremental[0] + .file_name() + .unwrap() + .to_str() + .unwrap() + .starts_with("foo-")); +} + +#[cargo_test(build_std_mock)] +fn cargo_config_injects_compiler_builtins() { + let setup = setup(); + + let p = project() + .file( + "src/lib.rs", + r#" + #![no_std] + pub fn foo() { + assert_eq!(u8::MIN, 0); + } + "#, + ) + .file( + ".cargo/config.toml", + r#" + [unstable] + build-std = ['core'] + "#, + ) + .build(); + let mut build = p.cargo("build -v --lib"); + enable_build_std(&mut build, &setup); + build + .target_host() + .with_stderr_does_not_contain("[..]libstd[..]") + .run(); +} + +#[cargo_test(build_std_mock)] +fn different_features() { + let setup = setup(); + + let p = project() + .file( + "src/lib.rs", + " + pub fn foo() { + std::conditional_function(); + } + ", + ) + .build(); + p.cargo("build") + .build_std(&setup) + .arg("-Zbuild-std-features=feature1") + .target_host() + .run(); +} + +#[cargo_test(build_std_mock)] +fn no_roots() { + // Checks for a bug where it would panic if there are no roots. + let setup = setup(); + + let p = project().file("tests/t1.rs", "").build(); + p.cargo("build") + .build_std(&setup) + .target_host() + .with_stderr_contains("[FINISHED] [..]") + .run(); +} + +#[cargo_test(build_std_mock)] +fn proc_macro_only() { + // Checks for a bug where it would panic if building a proc-macro only + let setup = setup(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "pm" + version = "0.1.0" + + [lib] + proc-macro = true + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("build") + .build_std(&setup) + .target_host() + .with_stderr_contains("[FINISHED] [..]") + .run(); +} + +#[cargo_test(build_std_mock)] +fn fetch() { + let setup = setup(); + + let p = project().file("src/main.rs", "fn main() {}").build(); + p.cargo("fetch") + .build_std(&setup) + .target_host() + .with_stderr_contains("[DOWNLOADED] [..]") + .run(); + p.cargo("build") + .build_std(&setup) + .target_host() + .with_stderr_does_not_contain("[DOWNLOADED] [..]") + .run(); +} -- cgit v1.2.3