diff --git a/src/tools/run-make-support/src/assertion_helpers.rs b/src/tools/run-make-support/src/assertion_helpers.rs index 4b5b349431d..6d256fc594d 100644 --- a/src/tools/run-make-support/src/assertion_helpers.rs +++ b/src/tools/run-make-support/src/assertion_helpers.rs @@ -3,7 +3,7 @@ use std::panic; use std::path::Path; -use crate::fs; +use crate::{fs, regex}; /// Assert that `actual` is equal to `expected`. #[track_caller] @@ -47,6 +47,36 @@ pub fn assert_not_contains, N: AsRef>(haystack: H, needle: N) } } +/// Assert that `haystack` contains the regex pattern `needle`. +#[track_caller] +pub fn assert_contains_regex, N: AsRef>(haystack: H, needle: N) { + let haystack = haystack.as_ref(); + let needle = needle.as_ref(); + let re = regex::Regex::new(needle).unwrap(); + if !re.is_match(haystack) { + eprintln!("=== HAYSTACK ==="); + eprintln!("{}", haystack); + eprintln!("=== NEEDLE ==="); + eprintln!("{}", needle); + panic!("needle was not found in haystack"); + } +} + +/// Assert that `haystack` does not contain the regex pattern `needle`. +#[track_caller] +pub fn assert_not_contains_regex, N: AsRef>(haystack: H, needle: N) { + let haystack = haystack.as_ref(); + let needle = needle.as_ref(); + let re = regex::Regex::new(needle).unwrap(); + if re.is_match(haystack) { + eprintln!("=== HAYSTACK ==="); + eprintln!("{}", haystack); + eprintln!("=== NEEDLE ==="); + eprintln!("{}", needle); + panic!("needle was unexpectedly found in haystack"); + } +} + /// Assert that all files in `dir1` exist and have the same content in `dir2` pub fn assert_dirs_are_equal(dir1: impl AsRef, dir2: impl AsRef) { let dir2 = dir2.as_ref(); diff --git a/src/tools/run-make-support/src/command.rs b/src/tools/run-make-support/src/command.rs index fb94ff996f0..dd7c0e32cf1 100644 --- a/src/tools/run-make-support/src/command.rs +++ b/src/tools/run-make-support/src/command.rs @@ -7,7 +7,10 @@ use build_helper::drop_bomb::DropBomb; use crate::util::handle_failed_output; -use crate::{assert_contains, assert_equals, assert_not_contains}; +use crate::{ + assert_contains, assert_contains_regex, assert_equals, assert_not_contains, + assert_not_contains_regex, +}; /// This is a custom command wrapper that simplifies working with commands and makes it easier to /// ensure that we check the exit status of executed processes. @@ -191,6 +194,13 @@ pub fn assert_stdout_not_contains>(&self, unexpected: S) -> &Self self } + /// Checks that `stdout` does not contain the regex pattern `unexpected`. + #[track_caller] + pub fn assert_stdout_not_contains_regex>(&self, unexpected: S) -> &Self { + assert_not_contains_regex(&self.stdout_utf8(), unexpected); + self + } + /// Checks that `stdout` contains `expected`. #[track_caller] pub fn assert_stdout_contains>(&self, expected: S) -> &Self { @@ -198,6 +208,13 @@ pub fn assert_stdout_contains>(&self, expected: S) -> &Self { self } + /// Checks that `stdout` contains the regex pattern `expected`. + #[track_caller] + pub fn assert_stdout_contains_regex>(&self, expected: S) -> &Self { + assert_contains_regex(&self.stdout_utf8(), expected); + self + } + /// Checks that trimmed `stderr` matches trimmed `expected`. #[track_caller] pub fn assert_stderr_equals>(&self, expected: S) -> &Self { @@ -212,6 +229,13 @@ pub fn assert_stderr_contains>(&self, expected: S) -> &Self { self } + /// Checks that `stderr` contains the regex pattern `expected`. + #[track_caller] + pub fn assert_stderr_contains_regex>(&self, expected: S) -> &Self { + assert_contains_regex(&self.stderr_utf8(), expected); + self + } + /// Checks that `stderr` does not contain `unexpected`. #[track_caller] pub fn assert_stderr_not_contains>(&self, unexpected: S) -> &Self { @@ -219,6 +243,13 @@ pub fn assert_stderr_not_contains>(&self, unexpected: S) -> &Self self } + /// Checks that `stderr` does not contain the regex pattern `unexpected`. + #[track_caller] + pub fn assert_stderr_not_contains_regex>(&self, unexpected: S) -> &Self { + assert_not_contains_regex(&self.stdout_utf8(), unexpected); + self + } + #[track_caller] pub fn assert_exit_code(&self, code: i32) -> &Self { assert!(self.output.status.code() == Some(code)); diff --git a/src/tools/run-make-support/src/external_deps/llvm.rs b/src/tools/run-make-support/src/external_deps/llvm.rs index b116bd08e3a..259bb615946 100644 --- a/src/tools/run-make-support/src/external_deps/llvm.rs +++ b/src/tools/run-make-support/src/external_deps/llvm.rs @@ -36,6 +36,12 @@ pub fn llvm_ar() -> LlvmAr { LlvmAr::new() } +/// Construct a new `llvm-nm` invocation. This assumes that `llvm-nm` is available +/// at `$LLVM_BIN_DIR/llvm-nm`. +pub fn llvm_nm() -> LlvmNm { + LlvmNm::new() +} + /// A `llvm-readobj` invocation builder. #[derive(Debug)] #[must_use] @@ -71,11 +77,19 @@ pub struct LlvmAr { cmd: Command, } +/// A `llvm-nm` invocation builder. +#[derive(Debug)] +#[must_use] +pub struct LlvmNm { + cmd: Command, +} + crate::macros::impl_common_helpers!(LlvmReadobj); crate::macros::impl_common_helpers!(LlvmProfdata); crate::macros::impl_common_helpers!(LlvmFilecheck); crate::macros::impl_common_helpers!(LlvmObjdump); crate::macros::impl_common_helpers!(LlvmAr); +crate::macros::impl_common_helpers!(LlvmNm); /// Generate the path to the bin directory of LLVM. #[must_use] @@ -244,3 +258,19 @@ pub fn output_input(&mut self, out: impl AsRef, input: impl AsRef) - self } } + +impl LlvmNm { + /// Construct a new `llvm-nm` invocation. This assumes that `llvm-nm` is available + /// at `$LLVM_BIN_DIR/llvm-nm`. + pub fn new() -> Self { + let llvm_nm = llvm_bin_dir().join("llvm-nm"); + let cmd = Command::new(llvm_nm); + Self { cmd } + } + + /// Provide an input file. + pub fn input>(&mut self, path: P) -> &mut Self { + self.cmd.arg(path.as_ref()); + self + } +} diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index 085120764b4..f28f2a120a4 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -48,8 +48,8 @@ pub mod rfs { pub use clang::{clang, Clang}; pub use htmldocck::htmldocck; pub use llvm::{ - llvm_ar, llvm_filecheck, llvm_objdump, llvm_profdata, llvm_readobj, LlvmAr, LlvmFilecheck, - LlvmObjdump, LlvmProfdata, LlvmReadobj, + llvm_ar, llvm_filecheck, llvm_nm, llvm_objdump, llvm_profdata, llvm_readobj, LlvmAr, + LlvmFilecheck, LlvmNm, LlvmObjdump, LlvmProfdata, LlvmReadobj, }; pub use python::python_command; pub use rustc::{aux_build, bare_rustc, rustc, Rustc}; @@ -84,7 +84,8 @@ pub mod rfs { pub use scoped_run::{run_in_tmpdir, test_while_readonly}; pub use assertion_helpers::{ - assert_contains, assert_dirs_are_equal, assert_equals, assert_not_contains, + assert_contains, assert_contains_regex, assert_dirs_are_equal, assert_equals, + assert_not_contains, assert_not_contains_regex, }; pub use string::{ diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index a84b89ff4a1..84436e96767 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -31,9 +31,7 @@ run-make/long-linker-command-lines/Makefile run-make/macos-deployment-target/Makefile run-make/min-global-align/Makefile run-make/native-link-modifier-bundle/Makefile -run-make/native-link-modifier-whole-archive/Makefile run-make/no-alloc-shim/Makefile -run-make/no-builtins-attribute/Makefile run-make/pdb-buildinfo-cl-cmd/Makefile run-make/pgo-gen-lto/Makefile run-make/pgo-indirect-call-promotion/Makefile @@ -45,7 +43,6 @@ run-make/redundant-libs/Makefile run-make/remap-path-prefix-dwarf/Makefile run-make/reproducible-build-2/Makefile run-make/reproducible-build/Makefile -run-make/rlib-format-packed-bundled-libs-2/Makefile run-make/rlib-format-packed-bundled-libs/Makefile run-make/simd-ffi/Makefile run-make/split-debuginfo/Makefile diff --git a/tests/run-make/native-link-modifier-whole-archive/Makefile b/tests/run-make/native-link-modifier-whole-archive/Makefile deleted file mode 100644 index 5eb7a416f91..00000000000 --- a/tests/run-make/native-link-modifier-whole-archive/Makefile +++ /dev/null @@ -1,52 +0,0 @@ -# ignore-cross-compile -- compiling C++ code does not work well when cross-compiling - -# This test case makes sure that native libraries are linked with appropriate semantics -# when the `[+-]bundle,[+-]whole-archive` modifiers are applied to them. -# -# The test works by checking that the resulting executables produce the expected output, -# part of which is emitted by otherwise unreferenced C code. If +whole-archive didn't work -# that code would never make it into the final executable and we'd thus be missing some -# of the output. - -include ../tools.mk - -all: $(TMPDIR)/$(call BIN,directly_linked) \ - $(TMPDIR)/$(call BIN,directly_linked_test_plus_whole_archive) \ - $(TMPDIR)/$(call BIN,directly_linked_test_minus_whole_archive) \ - $(TMPDIR)/$(call BIN,indirectly_linked) \ - $(TMPDIR)/$(call BIN,indirectly_linked_via_attr) - $(call RUN,directly_linked) | $(CGREP) 'static-initializer.directly_linked.' - $(call RUN,directly_linked_test_plus_whole_archive) --nocapture | $(CGREP) 'static-initializer.' - $(call RUN,directly_linked_test_minus_whole_archive) --nocapture | $(CGREP) -v 'static-initializer.' - $(call RUN,indirectly_linked) | $(CGREP) 'static-initializer.indirectly_linked.' - $(call RUN,indirectly_linked_via_attr) | $(CGREP) 'static-initializer.native_lib_in_src.' - -# Native lib linked directly into executable -$(TMPDIR)/$(call BIN,directly_linked): $(call NATIVE_STATICLIB,c_static_lib_with_constructor) - $(RUSTC) directly_linked.rs -l static:+whole-archive=c_static_lib_with_constructor - -# Native lib linked into test executable, +whole-archive -$(TMPDIR)/$(call BIN,directly_linked_test_plus_whole_archive): $(call NATIVE_STATICLIB,c_static_lib_with_constructor) - $(RUSTC) directly_linked_test_plus_whole_archive.rs --test -l static:+whole-archive=c_static_lib_with_constructor -# Native lib linked into test executable, -whole-archive -$(TMPDIR)/$(call BIN,directly_linked_test_minus_whole_archive): $(call NATIVE_STATICLIB,c_static_lib_with_constructor) - $(RUSTC) directly_linked_test_minus_whole_archive.rs --test -l static:-whole-archive=c_static_lib_with_constructor - -# Native lib linked into RLIB via `-l static:-bundle,+whole-archive`, RLIB linked into executable -$(TMPDIR)/$(call BIN,indirectly_linked): $(TMPDIR)/librlib_with_cmdline_native_lib.rlib - $(RUSTC) indirectly_linked.rs - -# Native lib linked into RLIB via #[link] attribute, RLIB linked into executable -$(TMPDIR)/$(call BIN,indirectly_linked_via_attr): $(TMPDIR)/libnative_lib_in_src.rlib - $(RUSTC) indirectly_linked_via_attr.rs - -# Native lib linked into rlib with via commandline -$(TMPDIR)/librlib_with_cmdline_native_lib.rlib: $(call NATIVE_STATICLIB,c_static_lib_with_constructor) - $(RUSTC) rlib_with_cmdline_native_lib.rs --crate-type=rlib -l static:-bundle,+whole-archive=c_static_lib_with_constructor - -# Native lib linked into rlib via `#[link()]` attribute on extern block. -$(TMPDIR)/libnative_lib_in_src.rlib: $(call NATIVE_STATICLIB,c_static_lib_with_constructor) - $(RUSTC) native_lib_in_src.rs --crate-type=rlib - -$(TMPDIR)/libc_static_lib_with_constructor.o: c_static_lib_with_constructor.cpp - $(call COMPILE_OBJ_CXX,$@,$<) diff --git a/tests/run-make/native-link-modifier-whole-archive/rmake.rs b/tests/run-make/native-link-modifier-whole-archive/rmake.rs new file mode 100644 index 00000000000..b8b814043e5 --- /dev/null +++ b/tests/run-make/native-link-modifier-whole-archive/rmake.rs @@ -0,0 +1,86 @@ +// This test case makes sure that native libraries are linked with appropriate semantics +// when the `[+-]bundle,[+-]whole-archive` modifiers are applied to them. +// The test works by checking that the resulting executables produce the expected output, +// part of which is emitted by otherwise unreferenced C code. If +whole-archive didn't work +// that code would never make it into the final executable and we'd thus be missing some +// of the output. +// See https://github.com/rust-lang/rust/issues/88085 + +//@ ignore-cross-compile +// Reason: compiling C++ code does not work well when cross-compiling +// plus, the compiled binary is executed + +use run_make_support::{cxx, is_msvc, llvm_ar, run, run_with_args, rustc, static_lib_name}; + +fn main() { + let mut cxx = cxx(); + if is_msvc() { + cxx.arg("-EHs"); + } + cxx.input("c_static_lib_with_constructor.cpp") + .arg("-c") + .out_exe("libc_static_lib_with_constructor") + .run(); + + let mut llvm_ar = llvm_ar(); + llvm_ar.obj_to_ar(); + if is_msvc() { + llvm_ar + .output_input( + static_lib_name("c_static_lib_with_constructor"), + "libc_static_lib_with_constructor.obj", + ) + .run(); + } else { + llvm_ar + .output_input( + static_lib_name("c_static_lib_with_constructor"), + "libc_static_lib_with_constructor", + ) + .run(); + } + + // Native lib linked directly into executable + rustc() + .input("directly_linked.rs") + .arg("-lstatic:+whole-archive=c_static_lib_with_constructor") + .run(); + + // Native lib linked into test executable, +whole-archive + rustc() + .input("directly_linked_test_plus_whole_archive.rs") + .arg("--test") + .arg("-lstatic:+whole-archive=c_static_lib_with_constructor") + .run(); + + // Native lib linked into test executable, -whole-archive + rustc() + .input("directly_linked_test_minus_whole_archive.rs") + .arg("--test") + .arg("-lstatic:-whole-archive=c_static_lib_with_constructor") + .run(); + + // Native lib linked into rlib with via commandline + rustc() + .input("rlib_with_cmdline_native_lib.rs") + .crate_type("rlib") + .arg("-lstatic:-bundle,+whole-archive=c_static_lib_with_constructor") + .run(); + // Native lib linked into RLIB via `-l static:-bundle,+whole-archive` + // RLIB linked into executable + rustc().input("indirectly_linked.rs").run(); + + // Native lib linked into rlib via `#[link()]` attribute on extern block. + rustc().input("native_lib_in_src.rs").crate_type("rlib").run(); + // Native lib linked into RLIB via #[link] attribute, RLIB linked into executable + rustc().input("indirectly_linked_via_attr.rs").run(); + + run("directly_linked").assert_stdout_contains("static-initializer.directly_linked."); + run_with_args("directly_linked_test_plus_whole_archive", &["--nocapture"]) + .assert_stdout_contains("static-initializer."); + run_with_args("directly_linked_test_minus_whole_archive", &["--nocapture"]) + .assert_stdout_not_contains("static-initializer."); + run("indirectly_linked").assert_stdout_contains("static-initializer.indirectly_linked."); + run("indirectly_linked_via_attr") + .assert_stdout_contains("static-initializer.native_lib_in_src."); +} diff --git a/tests/run-make/no-builtins-attribute/Makefile b/tests/run-make/no-builtins-attribute/Makefile deleted file mode 100644 index 0ce95facaea..00000000000 --- a/tests/run-make/no-builtins-attribute/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -include ../tools.mk - -# We want to check if `no-builtins` is also added to the function declarations in the used crate. - -all: - $(RUSTC) no_builtins.rs --emit=link - $(RUSTC) main.rs --emit=llvm-ir - - cat "$(TMPDIR)"/main.ll | "$(LLVM_FILECHECK)" filecheck.main.txt diff --git a/tests/run-make/no-builtins-attribute/rmake.rs b/tests/run-make/no-builtins-attribute/rmake.rs new file mode 100644 index 00000000000..1e15b0c03f1 --- /dev/null +++ b/tests/run-make/no-builtins-attribute/rmake.rs @@ -0,0 +1,13 @@ +// `no_builtins` is an attribute related to LLVM's optimizations. In order to ensure that it has an +// effect on link-time optimizations (LTO), it should be added to function declarations in a crate. +// This test uses the `llvm-filecheck` tool to determine that this attribute is successfully +// being added to these function declarations. +// See https://github.com/rust-lang/rust/pull/113716 + +use run_make_support::{llvm_filecheck, rfs, rustc}; + +fn main() { + rustc().input("no_builtins.rs").emit("link").run(); + rustc().input("main.rs").emit("llvm-ir").run(); + llvm_filecheck().patterns("filecheck.main.txt").stdin(rfs::read("main.ll")).run(); +} diff --git a/tests/run-make/rlib-format-packed-bundled-libs-2/Makefile b/tests/run-make/rlib-format-packed-bundled-libs-2/Makefile deleted file mode 100644 index d2a740b06b3..00000000000 --- a/tests/run-make/rlib-format-packed-bundled-libs-2/Makefile +++ /dev/null @@ -1,27 +0,0 @@ -include ../tools.mk - -# ignore-cross-compile - -# Make sure -Zpacked_bundled_libs is compatible with verbatim. - -# We're using the llvm-nm instead of the system nm to ensure it is compatible -# with the LLVM bitcode generated by rustc. -# Except on Windows where piping/IO redirection under MSYS2 is wonky with llvm-nm. -ifndef IS_WINDOWS -NM = "$(LLVM_BIN_DIR)"/llvm-nm -else -NM = nm -endif - -all: - # Build strange-named dep. - $(RUSTC) native_dep.rs --crate-type=staticlib -o $(TMPDIR)/native_dep.ext - - $(RUSTC) rust_dep.rs --crate-type=rlib -Zpacked_bundled_libs - $(NM) $(TMPDIR)/librust_dep.rlib | $(CGREP) -e "U.*native_f1" - $(AR) t $(TMPDIR)/librust_dep.rlib | $(CGREP) "native_dep.ext" - - # Make sure compiler doesn't use files, that it shouldn't know about. - rm $(TMPDIR)/native_dep.ext - - $(RUSTC) main.rs --extern rust_dep=$(TMPDIR)/librust_dep.rlib -Zpacked_bundled_libs diff --git a/tests/run-make/rlib-format-packed-bundled-libs-2/rmake.rs b/tests/run-make/rlib-format-packed-bundled-libs-2/rmake.rs new file mode 100644 index 00000000000..5a1460963b6 --- /dev/null +++ b/tests/run-make/rlib-format-packed-bundled-libs-2/rmake.rs @@ -0,0 +1,28 @@ +// `-Z packed_bundled_libs` is an unstable rustc flag that makes the compiler +// only require a native library and no supplementary object files to compile. +// This test simply checks that this flag can be passed alongside verbatim syntax +// in rustc flags without a compilation failure or the removal of expected symbols. +// See https://github.com/rust-lang/rust/pull/100101 + +use run_make_support::{llvm_ar, llvm_nm, rfs, rust_lib_name, rustc}; + +fn main() { + // Build a strangely named dependency. + rustc().input("native_dep.rs").crate_type("staticlib").output("native_dep.ext").run(); + + rustc().input("rust_dep.rs").crate_type("rlib").arg("-Zpacked_bundled_libs").run(); + llvm_nm().input(rust_lib_name("rust_dep")).run().assert_stdout_contains_regex("U.*native_f1"); + llvm_ar() + .arg("t") + .arg(rust_lib_name("rust_dep")) + .run() + .assert_stdout_contains("native_dep.ext"); + + // Ensure the compiler does not use files it should not be aware of. + rfs::remove_file("native_dep.ext"); + rustc() + .input("main.rs") + .extern_("rust_dep", rust_lib_name("rust_dep")) + .arg("-Zpacked_bundled_libs") + .run(); +}