From 152db2760cbd21b6c4fcd2c22ee1dcb422fb9965 Mon Sep 17 00:00:00 2001
From: Oneirical <manchot@videotron.ca>
Date: Mon, 29 Jul 2024 12:03:14 -0400
Subject: [PATCH] rewrite cross-lang-lto to rmake

---
 .../src/external_deps/llvm.rs                 |  36 ++++++
 src/tools/run-make-support/src/lib.rs         |   4 +-
 .../tidy/src/allowed_run_make_makefiles.txt   |   1 -
 tests/run-make/cross-lang-lto/Makefile        |  57 ---------
 tests/run-make/cross-lang-lto/rmake.rs        | 110 ++++++++++++++++++
 5 files changed, 148 insertions(+), 60 deletions(-)
 delete mode 100644 tests/run-make/cross-lang-lto/Makefile
 create mode 100644 tests/run-make/cross-lang-lto/rmake.rs

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 259bb6159461b..c06cefa91a924 100644
--- a/src/tools/run-make-support/src/external_deps/llvm.rs
+++ b/src/tools/run-make-support/src/external_deps/llvm.rs
@@ -42,6 +42,12 @@ pub fn llvm_nm() -> LlvmNm {
     LlvmNm::new()
 }
 
+/// Construct a new `llvm-bcanalyzer` invocation. This assumes that `llvm-bcanalyzer` is available
+/// at `$LLVM_BIN_DIR/llvm-bcanalyzer`.
+pub fn llvm_bcanalyzer() -> LlvmBcanalyzer {
+    LlvmBcanalyzer::new()
+}
+
 /// A `llvm-readobj` invocation builder.
 #[derive(Debug)]
 #[must_use]
@@ -84,12 +90,20 @@ pub struct LlvmNm {
     cmd: Command,
 }
 
+/// A `llvm-bcanalyzer` invocation builder.
+#[derive(Debug)]
+#[must_use]
+pub struct LlvmBcanalyzer {
+    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);
+crate::macros::impl_common_helpers!(LlvmBcanalyzer);
 
 /// Generate the path to the bin directory of LLVM.
 #[must_use]
@@ -250,6 +264,12 @@ impl LlvmAr {
         self
     }
 
+    /// Extract archive members back to files.
+    pub fn extract(&mut self) -> &mut Self {
+        self.cmd.arg("x");
+        self
+    }
+
     /// Provide an output, then an input file. Bundled in one function, as llvm-ar has
     /// no "--output"-style flag.
     pub fn output_input(&mut self, out: impl AsRef<Path>, input: impl AsRef<Path>) -> &mut Self {
@@ -274,3 +294,19 @@ impl LlvmNm {
         self
     }
 }
+
+impl LlvmBcanalyzer {
+    /// Construct a new `llvm-bcanalyzer` invocation. This assumes that `llvm-bcanalyzer` is available
+    /// at `$LLVM_BIN_DIR/llvm-bcanalyzer`.
+    pub fn new() -> Self {
+        let llvm_bcanalyzer = llvm_bin_dir().join("llvm-bcanalyzer");
+        let cmd = Command::new(llvm_bcanalyzer);
+        Self { cmd }
+    }
+
+    /// Provide an input file.
+    pub fn input<P: AsRef<Path>>(&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 f28f2a120a44b..2c9c3e175fe3f 100644
--- a/src/tools/run-make-support/src/lib.rs
+++ b/src/tools/run-make-support/src/lib.rs
@@ -48,8 +48,8 @@ pub use cc::{cc, cxx, extra_c_flags, extra_cxx_flags, Cc};
 pub use clang::{clang, Clang};
 pub use htmldocck::htmldocck;
 pub use llvm::{
-    llvm_ar, llvm_filecheck, llvm_nm, llvm_objdump, llvm_profdata, llvm_readobj, LlvmAr,
-    LlvmFilecheck, LlvmNm, LlvmObjdump, LlvmProfdata, LlvmReadobj,
+    llvm_ar, llvm_bcanalyzer, llvm_filecheck, llvm_nm, llvm_objdump, llvm_profdata, llvm_readobj,
+    LlvmAr, LlvmBcanalyzer, LlvmFilecheck, LlvmNm, LlvmObjdump, LlvmProfdata, LlvmReadobj,
 };
 pub use python::python_command;
 pub use rustc::{aux_build, bare_rustc, rustc, Rustc};
diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt
index 84436e96767a2..9e1c520a2c00a 100644
--- a/src/tools/tidy/src/allowed_run_make_makefiles.txt
+++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt
@@ -4,7 +4,6 @@ run-make/cdylib-dylib-linkage/Makefile
 run-make/cross-lang-lto-clang/Makefile
 run-make/cross-lang-lto-pgo-smoketest/Makefile
 run-make/cross-lang-lto-upstream-rlibs/Makefile
-run-make/cross-lang-lto/Makefile
 run-make/dep-info-doesnt-run-much/Makefile
 run-make/dep-info-spaces/Makefile
 run-make/dep-info/Makefile
diff --git a/tests/run-make/cross-lang-lto/Makefile b/tests/run-make/cross-lang-lto/Makefile
deleted file mode 100644
index 92058f952a9fc..0000000000000
--- a/tests/run-make/cross-lang-lto/Makefile
+++ /dev/null
@@ -1,57 +0,0 @@
-
-include ../tools.mk
-
-# ignore windows due to libLLVM being present in PATH and the PATH and library path being the same
-# (so fixing it is harder). See #57765 for context
-ifndef IS_WINDOWS
-
-# This test makes sure that the object files we generate are actually
-# LLVM bitcode files (as used by linker LTO plugins) when compiling with
-# -Clinker-plugin-lto.
-
-# this only succeeds for bitcode files
-ASSERT_IS_BITCODE_OBJ=("$(LLVM_BIN_DIR)"/llvm-bcanalyzer $(1))
-EXTRACT_OBJS=(cd $(TMPDIR); rm -f ./*.o; "$(LLVM_BIN_DIR)"/llvm-ar x $(1))
-
-BUILD_LIB=$(RUSTC) lib.rs -Copt-level=2 -Clinker-plugin-lto -Ccodegen-units=1
-BUILD_EXE=$(RUSTC) main.rs -Copt-level=2 -Clinker-plugin-lto -Ccodegen-units=1 --emit=obj
-
-all: staticlib staticlib-fat-lto staticlib-thin-lto rlib exe cdylib rdylib
-
-staticlib: lib.rs
-	$(BUILD_LIB) --crate-type=staticlib -o $(TMPDIR)/liblib.a
-	$(call EXTRACT_OBJS, liblib.a)
-	for file in $(TMPDIR)/liblib.*.rcgu.o; do $(call ASSERT_IS_BITCODE_OBJ, $$file); done
-
-staticlib-fat-lto: lib.rs
-	$(BUILD_LIB) --crate-type=staticlib -o $(TMPDIR)/liblib-fat-lto.a -Clto=fat
-	$(call EXTRACT_OBJS, liblib-fat-lto.a)
-	for file in $(TMPDIR)/liblib-fat-lto.*.rcgu.o; do $(call ASSERT_IS_BITCODE_OBJ, $$file); done
-
-staticlib-thin-lto: lib.rs
-	$(BUILD_LIB) --crate-type=staticlib -o $(TMPDIR)/liblib-thin-lto.a -Clto=thin
-	$(call EXTRACT_OBJS, liblib-thin-lto.a)
-	for file in $(TMPDIR)/liblib-thin-lto.*.rcgu.o; do $(call ASSERT_IS_BITCODE_OBJ, $$file); done
-
-rlib: lib.rs
-	$(BUILD_LIB) --crate-type=rlib -o $(TMPDIR)/liblib.rlib
-	$(call EXTRACT_OBJS, liblib.rlib)
-	for file in $(TMPDIR)/liblib.*.rcgu.o; do $(call ASSERT_IS_BITCODE_OBJ, $$file); done
-
-cdylib: lib.rs
-	$(BUILD_LIB) --crate-type=cdylib --emit=obj -o $(TMPDIR)/cdylib.o
-	$(call ASSERT_IS_BITCODE_OBJ, $(TMPDIR)/cdylib.o)
-
-rdylib: lib.rs
-	$(BUILD_LIB) --crate-type=dylib --emit=obj -o $(TMPDIR)/rdylib.o
-	$(call ASSERT_IS_BITCODE_OBJ, $(TMPDIR)/rdylib.o)
-
-exe: lib.rs
-	$(BUILD_EXE) -o $(TMPDIR)/exe.o
-	$(call ASSERT_IS_BITCODE_OBJ, $(TMPDIR)/exe.o)
-
-else
-
-all:
-
-endif
diff --git a/tests/run-make/cross-lang-lto/rmake.rs b/tests/run-make/cross-lang-lto/rmake.rs
new file mode 100644
index 0000000000000..dc376b561e43e
--- /dev/null
+++ b/tests/run-make/cross-lang-lto/rmake.rs
@@ -0,0 +1,110 @@
+// This test checks that the object files we generate are actually
+// LLVM bitcode files (as used by linker LTO plugins) when compiling with
+// -Clinker-plugin-lto.
+// See https://github.com/rust-lang/rust/pull/50000
+
+#![feature(path_file_prefix)]
+
+use std::path::PathBuf;
+
+use run_make_support::{
+    cwd, has_extension, has_prefix, llvm_ar, llvm_bcanalyzer, path, rfs, rust_lib_name, rustc,
+    shallow_find_files, static_lib_name,
+};
+
+fn main() {
+    check_bitcode(LibBuild {
+        source: path("lib.rs"),
+        crate_type: Some("staticlib"),
+        output: path(static_lib_name("liblib")),
+        lto: None,
+        emit_obj: false,
+    });
+    check_bitcode(LibBuild {
+        source: path("lib.rs"),
+        crate_type: Some("staticlib"),
+        output: path(static_lib_name("liblib-fat-lto")),
+        lto: Some("fat"),
+        emit_obj: false,
+    });
+    check_bitcode(LibBuild {
+        source: path("lib.rs"),
+        crate_type: Some("staticlib"),
+        output: path(static_lib_name("liblib-thin-lto")),
+        lto: Some("thin"),
+        emit_obj: false,
+    });
+    check_bitcode(LibBuild {
+        source: path("lib.rs"),
+        crate_type: Some("rlib"),
+        output: path(rust_lib_name("liblib")),
+        lto: None,
+        emit_obj: false,
+    });
+    check_bitcode(LibBuild {
+        source: path("lib.rs"),
+        crate_type: Some("cdylib"),
+        output: path("cdylib.o"),
+        lto: None,
+        emit_obj: true,
+    });
+    check_bitcode(LibBuild {
+        source: path("lib.rs"),
+        crate_type: Some("dylib"),
+        output: path("rdylib.o"),
+        lto: None,
+        emit_obj: true,
+    });
+    check_bitcode(LibBuild {
+        source: path("main.rs"),
+        crate_type: None,
+        output: path("exe.o"),
+        lto: None,
+        emit_obj: true,
+    });
+}
+
+#[track_caller]
+fn check_bitcode(instructions: LibBuild) {
+    let mut rustc = rustc();
+    rustc
+        .input(instructions.source)
+        .output(&instructions.output)
+        .opt_level("2")
+        .codegen_units(1)
+        .arg("-Clinker-plugin-lto");
+    if instructions.emit_obj {
+        rustc.emit("obj");
+    }
+    if let Some(crate_type) = instructions.crate_type {
+        rustc.crate_type(crate_type);
+    }
+    if let Some(lto) = instructions.lto {
+        rustc.arg(format!("-Clto={lto}"));
+    }
+    rustc.run();
+
+    if instructions.output.extension().unwrap() != "o" {
+        // Remove all potential leftover object files, then turn the output into an object file.
+        for object in shallow_find_files(cwd(), |path| has_extension(path, "o")) {
+            rfs::remove_file(object);
+        }
+        llvm_ar().extract().arg(&instructions.output).run();
+    }
+
+    for object in shallow_find_files(cwd(), |path| {
+        has_prefix(path, instructions.output.file_prefix().unwrap().to_str().unwrap())
+            && has_extension(path, "o")
+    }) {
+        // All generated object files should be LLVM bitcode files - this will fail otherwise.
+        llvm_bcanalyzer().input(object).run();
+    }
+}
+
+struct LibBuild {
+    source: PathBuf,
+    crate_type: Option<&'static str>,
+    output: PathBuf,
+    lto: Option<&'static str>,
+    emit_obj: bool,
+}