From 255bee177797cd5c35729acf02c6a43b5dd1f73d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?=
 <jieyouxu@outlook.com>
Date: Wed, 10 Apr 2024 15:21:16 +0000
Subject: [PATCH] run-make-support: add clang and llvm-readobj command wrappers

---
 src/tools/run-make-support/src/clang.rs       | 73 +++++++++++++++++++
 src/tools/run-make-support/src/lib.rs         |  4 +
 .../run-make-support/src/llvm_readobj.rs      | 44 +++++++++++
 3 files changed, 121 insertions(+)
 create mode 100644 src/tools/run-make-support/src/clang.rs
 create mode 100644 src/tools/run-make-support/src/llvm_readobj.rs

diff --git a/src/tools/run-make-support/src/clang.rs b/src/tools/run-make-support/src/clang.rs
new file mode 100644
index 00000000000..ed9f8383dc3
--- /dev/null
+++ b/src/tools/run-make-support/src/clang.rs
@@ -0,0 +1,73 @@
+use std::env;
+use std::path::Path;
+use std::process::Command;
+
+use crate::{bin_name, handle_failed_output, tmp_dir};
+
+/// Construct a new `clang` invocation. `clang` is not always available for all targets.
+pub fn clang() -> Clang {
+    Clang::new()
+}
+
+/// A `clang` invocation builder.
+#[derive(Debug)]
+pub struct Clang {
+    cmd: Command,
+}
+
+crate::impl_common_helpers!(Clang);
+
+impl Clang {
+    /// Construct a new `clang` invocation. `clang` is not always available for all targets.
+    pub fn new() -> Self {
+        let clang =
+            env::var("CLANG").expect("`CLANG` not specified, but this is required to find `clang`");
+        let cmd = Command::new(clang);
+        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
+    }
+
+    /// Specify the name of the executable. The executable will be placed under `$TMPDIR`, and the
+    /// extension will be determined by [`bin_name`].
+    pub fn out_exe(&mut self, name: &str) -> &mut Self {
+        self.cmd.arg("-o");
+        self.cmd.arg(tmp_dir().join(bin_name(name)));
+        self
+    }
+
+    /// Specify which target triple clang should target.
+    pub fn target(&mut self, target_triple: &str) -> &mut Self {
+        self.cmd.arg("-target");
+        self.cmd.arg(target_triple);
+        self
+    }
+
+    /// Pass `-nostdlib` to disable linking the C standard library.
+    pub fn no_stdlib(&mut self) -> &mut Self {
+        self.cmd.arg("-nostdlib");
+        self
+    }
+
+    /// Specify architecture.
+    pub fn arch(&mut self, arch: &str) -> &mut Self {
+        self.cmd.arg(format!("-march={arch}"));
+        self
+    }
+
+    /// Specify LTO settings.
+    pub fn lto(&mut self, lto: &str) -> &mut Self {
+        self.cmd.arg(format!("-flto={lto}"));
+        self
+    }
+
+    /// Specify which ld to use.
+    pub fn use_ld(&mut self, ld: &str) -> &mut Self {
+        self.cmd.arg(format!("-fuse-ld={ld}"));
+        self
+    }
+}
diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs
index 9a4fdff5d15..e723e824ed6 100644
--- a/src/tools/run-make-support/src/lib.rs
+++ b/src/tools/run-make-support/src/lib.rs
@@ -4,6 +4,8 @@
 //! as `object` or `wasmparser`, they can be re-exported and be made available through this library.
 
 pub mod cc;
+pub mod clang;
+pub mod llvm_readobj;
 pub mod run;
 pub mod rustc;
 pub mod rustdoc;
@@ -17,6 +19,8 @@ pub use regex;
 pub use wasmparser;
 
 pub use cc::{cc, extra_c_flags, extra_cxx_flags, Cc};
+pub use clang::{clang, Clang};
+pub use llvm_readobj::{llvm_readobj, LlvmReadobj};
 pub use run::{run, run_fail};
 pub use rustc::{aux_build, rustc, Rustc};
 pub use rustdoc::{bare_rustdoc, rustdoc, Rustdoc};
diff --git a/src/tools/run-make-support/src/llvm_readobj.rs b/src/tools/run-make-support/src/llvm_readobj.rs
new file mode 100644
index 00000000000..32ea07e932e
--- /dev/null
+++ b/src/tools/run-make-support/src/llvm_readobj.rs
@@ -0,0 +1,44 @@
+use std::env;
+use std::path::{Path, PathBuf};
+use std::process::Command;
+
+use crate::handle_failed_output;
+
+/// Construct a new `llvm-readobj` invocation. This assumes that `llvm-readobj` is available
+/// at `$LLVM_BIN_DIR/llvm-readobj`.
+pub fn llvm_readobj() -> LlvmReadobj {
+    LlvmReadobj::new()
+}
+
+/// A `llvm-readobj` invocation builder.
+#[derive(Debug)]
+pub struct LlvmReadobj {
+    cmd: Command,
+}
+
+crate::impl_common_helpers!(LlvmReadobj);
+
+impl LlvmReadobj {
+    /// Construct a new `llvm-readobj` invocation. This assumes that `llvm-readobj` is available
+    /// at `$LLVM_BIN_DIR/llvm-readobj`.
+    pub fn new() -> Self {
+        let llvm_bin_dir = env::var("LLVM_BIN_DIR")
+            .expect("`LLVM_BIN_DIR` not specified, but this is required to find `llvm-readobj`");
+        let llvm_bin_dir = PathBuf::from(llvm_bin_dir);
+        let llvm_readobj = llvm_bin_dir.join("llvm-readobj");
+        let cmd = Command::new(llvm_readobj);
+        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
+    }
+
+    /// Pass `--file-header` to display file headers.
+    pub fn file_header(&mut self) -> &mut Self {
+        self.cmd.arg("--file-header");
+        self
+    }
+}