summaryrefslogtreecommitdiffstats
path: root/src/tools/cargo/tests/testsuite/standard_lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/cargo/tests/testsuite/standard_lib.rs')
-rw-r--r--src/tools/cargo/tests/testsuite/standard_lib.rs657
1 files changed, 657 insertions, 0 deletions
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::<Vec<_>>();
+
+ 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<i32> = 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();
+}