diff --git a/src/tools/run-make-support/src/external_deps/c_build.rs b/src/tools/run-make-support/src/external_deps/c_build.rs index 86c9c08183197..15e02d04393ce 100644 --- a/src/tools/run-make-support/src/external_deps/c_build.rs +++ b/src/tools/run-make-support/src/external_deps/c_build.rs @@ -2,7 +2,7 @@ use std::path::PathBuf; use super::cygpath::get_windows_path; use crate::artifact_names::{dynamic_lib_name, static_lib_name}; -use crate::external_deps::cc::cc; +use crate::external_deps::cc::{cc, cxx}; use crate::external_deps::llvm::llvm_ar; use crate::path_helpers::path; use crate::targets::{is_darwin, is_msvc, is_windows}; @@ -10,6 +10,7 @@ use crate::targets::{is_darwin, is_msvc, is_windows}; // FIXME(Oneirical): These native build functions should take a Path-based generic. /// Builds a static lib (`.lib` on Windows MSVC and `.a` for the rest) with the given name. +/// Built from a C file. #[track_caller] pub fn build_native_static_lib(lib_name: &str) -> PathBuf { let obj_file = if is_msvc() { format!("{lib_name}") } else { format!("{lib_name}.o") }; @@ -58,3 +59,24 @@ pub fn build_native_dynamic_lib(lib_name: &str) -> PathBuf { } path(lib_path) } + +/// Builds a static lib (`.lib` on Windows MSVC and `.a` for the rest) with the given name. +/// Built from a C++ file. +#[track_caller] +pub fn build_native_static_lib_cxx(lib_name: &str) -> PathBuf { + let obj_file = if is_msvc() { format!("{lib_name}") } else { format!("{lib_name}.o") }; + let src = format!("{lib_name}.cpp"); + let lib_path = static_lib_name(lib_name); + if is_msvc() { + cxx().arg("-EHs").arg("-c").out_exe(&obj_file).input(src).run(); + } else { + cxx().arg("-c").out_exe(&obj_file).input(src).run(); + }; + let obj_file = if is_msvc() { + PathBuf::from(format!("{lib_name}.obj")) + } else { + PathBuf::from(format!("{lib_name}.o")) + }; + llvm_ar().obj_to_ar().output_input(&lib_path, &obj_file).run(); + path(lib_path) +} diff --git a/src/tools/run-make-support/src/external_deps/rustc.rs b/src/tools/run-make-support/src/external_deps/rustc.rs index 71d28dd9675fb..cece58d29566c 100644 --- a/src/tools/run-make-support/src/external_deps/rustc.rs +++ b/src/tools/run-make-support/src/external_deps/rustc.rs @@ -5,6 +5,7 @@ use crate::command::Command; use crate::env::env_var; use crate::path_helpers::cwd; use crate::util::set_host_rpath; +use crate::{is_darwin, is_msvc, is_windows, uname}; /// Construct a new `rustc` invocation. This will automatically set the library /// search path as `-L cwd()`. Use [`bare_rustc`] to avoid this. @@ -314,4 +315,62 @@ impl Rustc { self.cmd.arg(format!("-Clinker-flavor={linker_flavor}")); self } + + /// `EXTRARSCXXFLAGS` + pub fn extra_rs_cxx_flags(&mut self) -> &mut Self { + // Adapted from tools.mk (trimmed): + // + // ```makefile + // ifdef IS_WINDOWS + // ifdef IS_MSVC + // else + // EXTRARSCXXFLAGS := -lstatic:-bundle=stdc++ + // endif + // else + // ifeq ($(UNAME),Darwin) + // EXTRARSCXXFLAGS := -lc++ + // else + // ifeq ($(UNAME),FreeBSD) + // else + // ifeq ($(UNAME),SunOS) + // else + // ifeq ($(UNAME),OpenBSD) + // else + // EXTRARSCXXFLAGS := -lstdc++ + // endif + // endif + // endif + // endif + // endif + // ``` + let flag = if is_windows() { + // So this is a bit hacky: we can't use the DLL version of libstdc++ because + // it pulls in the DLL version of libgcc, which means that we end up with 2 + // instances of the DW2 unwinding implementation. This is a problem on + // i686-pc-windows-gnu because each module (DLL/EXE) needs to register its + // unwind information with the unwinding implementation, and libstdc++'s + // __cxa_throw won't see the unwinding info we registered with our statically + // linked libgcc. + // + // Now, simply statically linking libstdc++ would fix this problem, except + // that it is compiled with the expectation that pthreads is dynamically + // linked as a DLL and will fail to link with a statically linked libpthread. + // + // So we end up with the following hack: we link use static:-bundle to only + // link the parts of libstdc++ that we actually use, which doesn't include + // the dependency on the pthreads DLL. + if is_msvc() { None } else { Some("-lstatic:-bundle=stdc++") } + } else if is_darwin() { + Some("-lc++") + } else { + match &uname()[..] { + "FreeBSD" | "SunOS" | "OpenBSD" => None, + _ => Some("-lstdc++"), + } + }; + if let Some(flag) = flag { + self.cmd.arg(flag); + } + self + } } diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index 97c6bfe86d2bb..63c4c4d88638a 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -44,8 +44,8 @@ pub use wasmparser; pub use external_deps::{c_build, cc, clang, htmldocck, llvm, python, rustc, rustdoc}; // These rely on external dependencies. -pub use c_build::{build_native_dynamic_lib, build_native_static_lib}; pub use cc::{cc, cxx, extra_c_flags, extra_cxx_flags, Cc}; +pub use c_build::{build_native_dynamic_lib, build_native_static_lib, build_native_static_lib_cxx}; pub use clang::{clang, Clang}; pub use htmldocck::htmldocck; pub use llvm::{ diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index a7c8df3e27478..bd4f54d22da7b 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -9,10 +9,7 @@ run-make/dep-info-spaces/Makefile run-make/dep-info/Makefile run-make/emit-to-stdout/Makefile run-make/extern-fn-reachable/Makefile -run-make/foreign-double-unwind/Makefile -run-make/foreign-exceptions/Makefile run-make/incr-add-rust-src-component/Makefile -run-make/issue-36710/Makefile run-make/issue-84395-lto-embed-bitcode/Makefile run-make/issue-88756-default-output/Makefile run-make/jobserver-error/Makefile diff --git a/tests/run-make/issue-36710/foo.cpp b/tests/run-make/cpp-global-destructors/foo.cpp similarity index 100% rename from tests/run-make/issue-36710/foo.cpp rename to tests/run-make/cpp-global-destructors/foo.cpp diff --git a/tests/run-make/issue-36710/foo.rs b/tests/run-make/cpp-global-destructors/foo.rs similarity index 100% rename from tests/run-make/issue-36710/foo.rs rename to tests/run-make/cpp-global-destructors/foo.rs diff --git a/tests/run-make/cpp-global-destructors/rmake.rs b/tests/run-make/cpp-global-destructors/rmake.rs new file mode 100644 index 0000000000000..9bc5c84e10db5 --- /dev/null +++ b/tests/run-make/cpp-global-destructors/rmake.rs @@ -0,0 +1,26 @@ +// Some start files were missed when originally writing the logic to swap in musl start files. +// This caused #36710. After the fix in #50105, this test checks that linking to C++ code +// with global destructors works. +// See https://github.com/rust-lang/rust/pull/50105 + +//@ ignore-cross-compile +// Reason: the compiled binary is executed + +//@ ignore-none +// Reason: no-std is not supported. +//@ ignore-wasm32 +//@ ignore-wasm64 +// Reason: compiling C++ to WASM may cause problems. + +// Neither of these are tested in full CI. +//@ ignore-nvptx64-nvidia-cuda +// Reason: can't find crate "std" +//@ ignore-sgx + +use run_make_support::{build_native_static_lib_cxx, run, rustc}; + +fn main() { + build_native_static_lib_cxx("foo"); + rustc().input("foo.rs").arg("-lfoo").extra_rs_cxx_flags().run(); + run("foo"); +} diff --git a/tests/run-make/foreign-double-unwind/Makefile b/tests/run-make/foreign-double-unwind/Makefile deleted file mode 100644 index b5e52808d2fc5..0000000000000 --- a/tests/run-make/foreign-double-unwind/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# ignore-cross-compile -# needs-unwind -include ../tools.mk - -all: foo - $(call RUN,foo) | $(CGREP) -v unreachable - -foo: foo.rs $(call NATIVE_STATICLIB,foo) - $(RUSTC) $< -lfoo $(EXTRARSCXXFLAGS) - -$(TMPDIR)/libfoo.o: foo.cpp - $(call COMPILE_OBJ_CXX,$@,$<) diff --git a/tests/run-make/foreign-double-unwind/rmake.rs b/tests/run-make/foreign-double-unwind/rmake.rs new file mode 100644 index 0000000000000..9bd3b4c0fea5b --- /dev/null +++ b/tests/run-make/foreign-double-unwind/rmake.rs @@ -0,0 +1,21 @@ +// When using foreign function interface (FFI) with C++, it is possible +// to run into a "double unwind" if either both Rust and C++ run into a panic +// and exception at the same time, or C++ encounters two exceptions. In this case, +// one of the panic unwinds would be leaked and the other would be kept, leading +// to undefined behaviour. After this was fixed in #92911, this test checks that +// the keyword "unreachable" indicative of this bug triggering in this specific context +// does not appear after successfully compiling and executing the program. +// See https://github.com/rust-lang/rust/pull/92911 + +//@ needs-unwind +// Reason: this test exercises panic unwinding +//@ ignore-cross-compile +// Reason: the compiled binary is executed + +use run_make_support::{build_native_static_lib_cxx, run_fail, rustc}; + +fn main() { + build_native_static_lib_cxx("foo"); + rustc().input("foo.rs").arg("-lfoo").extra_rs_cxx_flags().run(); + run_fail("foo").assert_stdout_not_contains("unreachable"); +} diff --git a/tests/run-make/foreign-exceptions/Makefile b/tests/run-make/foreign-exceptions/Makefile deleted file mode 100644 index 56c41b274fb2e..0000000000000 --- a/tests/run-make/foreign-exceptions/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# ignore-cross-compile -# needs-unwind -include ../tools.mk - -all: foo - $(call RUN,foo) - -foo: foo.rs $(call NATIVE_STATICLIB,foo) - $(RUSTC) $< -lfoo $(EXTRARSCXXFLAGS) - -$(TMPDIR)/libfoo.o: foo.cpp - $(call COMPILE_OBJ_CXX,$@,$<) diff --git a/tests/run-make/foreign-exceptions/rmake.rs b/tests/run-make/foreign-exceptions/rmake.rs new file mode 100644 index 0000000000000..929319f049fe9 --- /dev/null +++ b/tests/run-make/foreign-exceptions/rmake.rs @@ -0,0 +1,19 @@ +// This test was created to check that compilation and execution still works +// after the addition of a new feature, in #65646: the ability to unwind panics +// and exceptions back and forth between Rust and C++. This is a basic smoke test, +// this feature being broken in quiet or subtle ways could still result in this test +// passing. +// See https://github.com/rust-lang/rust/pull/65646 + +//@ needs-unwind +// Reason: this test exercises panic unwinding +//@ ignore-cross-compile +// Reason: the compiled binary is executed + +use run_make_support::{build_native_static_lib_cxx, run, rustc}; + +fn main() { + build_native_static_lib_cxx("foo"); + rustc().input("foo.rs").arg("-lfoo").extra_rs_cxx_flags().run(); + run("foo"); +} diff --git a/tests/run-make/issue-36710/Makefile b/tests/run-make/issue-36710/Makefile deleted file mode 100644 index 7b91107a23495..0000000000000 --- a/tests/run-make/issue-36710/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -# ignore-cross-compile -# ignore-none no-std is not supported -# ignore-wasm32 FIXME: don't attempt to compile C++ to WASM -# ignore-wasm64 FIXME: don't attempt to compile C++ to WASM -# ignore-nvptx64-nvidia-cuda FIXME: can't find crate for `std` -# ignore-musl FIXME: this makefile needs teaching how to use a musl toolchain -# (see dist-i586-gnu-i586-i686-musl Dockerfile) -# ignore-sgx - -include ../tools.mk - -all: foo - $(call RUN,foo) - -foo: foo.rs $(call NATIVE_STATICLIB,foo) - $(RUSTC) $< -lfoo $(EXTRARSCXXFLAGS) --target $(TARGET) - -$(TMPDIR)/libfoo.o: foo.cpp - $(call COMPILE_OBJ_CXX,$@,$<)