Skip to content

Commit e8f1965

Browse files
committed
Auto merge of #127760 - jieyouxu:rmake-support-reorganize, r=<try>
Reorganize the `run-make-support` library The `run_make_support` library has a kitchen sink `lib.rs` that make discovery/learning very difficult. Let's try to improve that by breaking up `lib.rs` into smaller more organized modules. This is a precursor to improving the documentation and learnability of the `run_make_support` library. ### Changes - Breakup `lib.rs` into smaller modules according to functionality - Rename `recursive_diff` -> `assert_recursive_eq` - Minor doc improvements / fixes in a few places (I have a follow-up documentation PR planned) This PR is best reviewed commit-by-commit. r? `@Kobzol` (or Mark, or T-compiler or T-bootstrap) try-job: x86_64-msvc try-job: aarch64-apple try-job: test-various try-job: armhf-gnu try-job: dist-x86_64-linux
2 parents adeb79d + 4769fc9 commit e8f1965

22 files changed

+804
-628
lines changed

src/tools/run-make-support/src/ar.rs

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
use std::fs;
2+
use std::path::Path;
3+
4+
/// Archive utility.
5+
///
6+
/// # Notes
7+
///
8+
/// This *currently* uses the [ar][rust-ar] crate, but this is subject to changes. We may need to
9+
/// use `llvm-ar`, and if that is the case, this should be moved under `external_deps`.
10+
///
11+
/// [rust-ar]: https://github.com/mdsteele/rust-ar
12+
#[track_caller]
13+
pub fn ar(inputs: &[impl AsRef<Path>], output_path: impl AsRef<Path>) {
14+
let output = fs::File::create(&output_path).expect(&format!(
15+
"the file in path `{}` could not be created",
16+
output_path.as_ref().display()
17+
));
18+
let mut builder = ar::Builder::new(output);
19+
for input in inputs {
20+
builder.append_path(input).unwrap();
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
//! A collection of helpers to construct artifact names, such as names of dynamic or static
2+
//! librarys which are target-dependent.
3+
4+
use crate::targets::{is_darwin, is_msvc, is_windows};
5+
6+
/// Construct the static library name based on the target.
7+
#[must_use]
8+
pub fn static_lib_name(name: &str) -> String {
9+
// See tools.mk (irrelevant lines omitted):
10+
//
11+
// ```makefile
12+
// ifeq ($(UNAME),Darwin)
13+
// STATICLIB = $(TMPDIR)/lib$(1).a
14+
// else
15+
// ifdef IS_WINDOWS
16+
// ifdef IS_MSVC
17+
// STATICLIB = $(TMPDIR)/$(1).lib
18+
// else
19+
// STATICLIB = $(TMPDIR)/lib$(1).a
20+
// endif
21+
// else
22+
// STATICLIB = $(TMPDIR)/lib$(1).a
23+
// endif
24+
// endif
25+
// ```
26+
assert!(!name.contains(char::is_whitespace), "static library name cannot contain whitespace");
27+
28+
if is_msvc() { format!("{name}.lib") } else { format!("lib{name}.a") }
29+
}
30+
31+
/// Construct the dynamic library name based on the target.
32+
#[must_use]
33+
pub fn dynamic_lib_name(name: &str) -> String {
34+
// See tools.mk (irrelevant lines omitted):
35+
//
36+
// ```makefile
37+
// ifeq ($(UNAME),Darwin)
38+
// DYLIB = $(TMPDIR)/lib$(1).dylib
39+
// else
40+
// ifdef IS_WINDOWS
41+
// DYLIB = $(TMPDIR)/$(1).dll
42+
// else
43+
// DYLIB = $(TMPDIR)/lib$(1).so
44+
// endif
45+
// endif
46+
// ```
47+
assert!(!name.contains(char::is_whitespace), "dynamic library name cannot contain whitespace");
48+
49+
let extension = dynamic_lib_extension();
50+
if is_darwin() {
51+
format!("lib{name}.{extension}")
52+
} else if is_windows() {
53+
format!("{name}.{extension}")
54+
} else {
55+
format!("lib{name}.{extension}")
56+
}
57+
}
58+
59+
/// Construct the dynamic library extension based on the target.
60+
#[must_use]
61+
pub fn dynamic_lib_extension() -> &'static str {
62+
if is_darwin() {
63+
"dylib"
64+
} else if is_windows() {
65+
"dll"
66+
} else {
67+
"so"
68+
}
69+
}
70+
71+
/// Construct the name of a rust library (rlib).
72+
#[must_use]
73+
pub fn rust_lib_name(name: &str) -> String {
74+
format!("lib{name}.rlib")
75+
}
76+
77+
/// Construct the binary (executable) name based on the target.
78+
#[must_use]
79+
pub fn bin_name(name: &str) -> String {
80+
if is_windows() { format!("{name}.exe") } else { name.to_string() }
81+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
//! Collection of assertions and assertion-related helpers.
2+
3+
use std::panic;
4+
use std::path::{Path, PathBuf};
5+
6+
use crate::fs_helpers;
7+
use crate::fs_wrapper;
8+
use crate::path_helpers::cwd;
9+
10+
/// Browse the directory `path` non-recursively and return all files which respect the parameters
11+
/// outlined by `closure`.
12+
#[track_caller]
13+
pub fn shallow_find_files<P: AsRef<Path>, F: Fn(&PathBuf) -> bool>(
14+
path: P,
15+
filter: F,
16+
) -> Vec<PathBuf> {
17+
let mut matching_files = Vec::new();
18+
for entry in fs_wrapper::read_dir(path) {
19+
let entry = entry.expect("failed to read directory entry.");
20+
let path = entry.path();
21+
22+
if path.is_file() && filter(&path) {
23+
matching_files.push(path);
24+
}
25+
}
26+
matching_files
27+
}
28+
29+
/// Returns true if the filename at `path` starts with `prefix`.
30+
pub fn has_prefix<P: AsRef<Path>>(path: P, prefix: &str) -> bool {
31+
path.as_ref().file_name().is_some_and(|name| name.to_str().unwrap().starts_with(prefix))
32+
}
33+
34+
/// Returns true if the filename at `path` has the extension `extension`.
35+
pub fn has_extension<P: AsRef<Path>>(path: P, extension: &str) -> bool {
36+
path.as_ref().extension().is_some_and(|ext| ext == extension)
37+
}
38+
39+
/// Returns true if the filename at `path` does not contain `expected`.
40+
pub fn not_contains<P: AsRef<Path>>(path: P, expected: &str) -> bool {
41+
!path.as_ref().file_name().is_some_and(|name| name.to_str().unwrap().contains(expected))
42+
}
43+
44+
/// Returns true if the filename at `path` is not in `expected`.
45+
pub fn filename_not_in_denylist<P: AsRef<Path>, V: AsRef<[String]>>(path: P, expected: V) -> bool {
46+
let expected = expected.as_ref();
47+
path.as_ref()
48+
.file_name()
49+
.is_some_and(|name| !expected.contains(&name.to_str().unwrap().to_owned()))
50+
}
51+
52+
/// Returns true if the filename at `path` ends with `suffix`.
53+
pub fn has_suffix<P: AsRef<Path>>(path: P, suffix: &str) -> bool {
54+
path.as_ref().file_name().is_some_and(|name| name.to_str().unwrap().ends_with(suffix))
55+
}
56+
57+
/// Gathers all files in the current working directory that have the extension `ext`, and counts
58+
/// the number of lines within that contain a match with the regex pattern `re`.
59+
pub fn count_regex_matches_in_files_with_extension(re: &regex::Regex, ext: &str) -> usize {
60+
let fetched_files = shallow_find_files(cwd(), |path| has_extension(path, ext));
61+
62+
let mut count = 0;
63+
for file in fetched_files {
64+
let content = fs_wrapper::read_to_string(file);
65+
count += content.lines().filter(|line| re.is_match(&line)).count();
66+
}
67+
68+
count
69+
}
70+
71+
/// Read the contents of a file that cannot simply be read by
72+
/// [`read_to_string`][crate::fs_wrapper::read_to_string], due to invalid UTF-8 data, then assert
73+
/// that it contains `expected`.
74+
#[track_caller]
75+
pub fn invalid_utf8_contains<P: AsRef<Path>, S: AsRef<str>>(path: P, expected: S) {
76+
let buffer = fs_wrapper::read(path.as_ref());
77+
let expected = expected.as_ref();
78+
if !String::from_utf8_lossy(&buffer).contains(expected) {
79+
eprintln!("=== FILE CONTENTS (LOSSY) ===");
80+
eprintln!("{}", String::from_utf8_lossy(&buffer));
81+
eprintln!("=== SPECIFIED TEXT ===");
82+
eprintln!("{}", expected);
83+
panic!("specified text was not found in file");
84+
}
85+
}
86+
87+
/// Read the contents of a file that cannot simply be read by
88+
/// [`read_to_string`][crate::fs_wrapper::read_to_string], due to invalid UTF-8 data, then assert
89+
/// that it does not contain `expected`.
90+
#[track_caller]
91+
pub fn invalid_utf8_not_contains<P: AsRef<Path>, S: AsRef<str>>(path: P, expected: S) {
92+
let buffer = fs_wrapper::read(path.as_ref());
93+
let expected = expected.as_ref();
94+
if String::from_utf8_lossy(&buffer).contains(expected) {
95+
eprintln!("=== FILE CONTENTS (LOSSY) ===");
96+
eprintln!("{}", String::from_utf8_lossy(&buffer));
97+
eprintln!("=== SPECIFIED TEXT ===");
98+
eprintln!("{}", expected);
99+
panic!("specified text was unexpectedly found in file");
100+
}
101+
}
102+
103+
/// Assert that `actual` is equal to `expected`.
104+
#[track_caller]
105+
pub fn assert_equals<A: AsRef<str>, E: AsRef<str>>(actual: A, expected: E) {
106+
let actual = actual.as_ref();
107+
let expected = expected.as_ref();
108+
if actual != expected {
109+
eprintln!("=== ACTUAL TEXT ===");
110+
eprintln!("{}", actual);
111+
eprintln!("=== EXPECTED ===");
112+
eprintln!("{}", expected);
113+
panic!("expected text was not found in actual text");
114+
}
115+
}
116+
117+
/// Assert that `haystack` contains `needle`.
118+
#[track_caller]
119+
pub fn assert_contains<H: AsRef<str>, N: AsRef<str>>(haystack: H, needle: N) {
120+
let haystack = haystack.as_ref();
121+
let needle = needle.as_ref();
122+
if !haystack.contains(needle) {
123+
eprintln!("=== HAYSTACK ===");
124+
eprintln!("{}", haystack);
125+
eprintln!("=== NEEDLE ===");
126+
eprintln!("{}", needle);
127+
panic!("needle was not found in haystack");
128+
}
129+
}
130+
131+
/// Assert that `haystack` does not contain `needle`.
132+
#[track_caller]
133+
pub fn assert_not_contains<H: AsRef<str>, N: AsRef<str>>(haystack: H, needle: N) {
134+
let haystack = haystack.as_ref();
135+
let needle = needle.as_ref();
136+
if haystack.contains(needle) {
137+
eprintln!("=== HAYSTACK ===");
138+
eprintln!("{}", haystack);
139+
eprintln!("=== NEEDLE ===");
140+
eprintln!("{}", needle);
141+
panic!("needle was unexpectedly found in haystack");
142+
}
143+
}
144+
145+
/// Assert that all files in `dir1` exist and have the same content in `dir2`
146+
pub fn assert_recursive_eq(dir1: impl AsRef<Path>, dir2: impl AsRef<Path>) {
147+
let dir2 = dir2.as_ref();
148+
fs_helpers::read_dir(dir1, |entry_path| {
149+
let entry_name = entry_path.file_name().unwrap();
150+
if entry_path.is_dir() {
151+
assert_recursive_eq(&entry_path, &dir2.join(entry_name));
152+
} else {
153+
let path2 = dir2.join(entry_name);
154+
let file1 = fs_wrapper::read(&entry_path);
155+
let file2 = fs_wrapper::read(&path2);
156+
157+
// We don't use `assert_eq!` because they are `Vec<u8>`, so not great for display.
158+
// Why not using String? Because there might be minified files or even potentially
159+
// binary ones, so that would display useless output.
160+
assert!(
161+
file1 == file2,
162+
"`{}` and `{}` have different content",
163+
entry_path.display(),
164+
path2.display(),
165+
);
166+
}
167+
});
168+
}

src/tools/run-make-support/src/command.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ use std::panic;
55
use std::path::Path;
66
use std::process::{Command as StdCommand, ExitStatus, Output, Stdio};
77

8-
use crate::{assert_contains, assert_equals, assert_not_contains, handle_failed_output};
8+
use crate::util::handle_failed_output;
9+
use crate::{assert_contains, assert_equals, assert_not_contains};
10+
911
use build_helper::drop_bomb::DropBomb;
1012

1113
/// This is a custom command wrapper that simplifies working with commands and makes it easier to
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
use std::env;
2+
use std::ffi::OsString;
3+
4+
#[track_caller]
5+
#[must_use]
6+
pub fn env_var(name: &str) -> String {
7+
match env::var(name) {
8+
Ok(v) => v,
9+
Err(err) => panic!("failed to retrieve environment variable {name:?}: {err:?}"),
10+
}
11+
}
12+
13+
#[track_caller]
14+
#[must_use]
15+
pub fn env_var_os(name: &str) -> OsString {
16+
match env::var_os(name) {
17+
Some(v) => v,
18+
None => panic!("failed to retrieve environment variable {name:?}"),
19+
}
20+
}

src/tools/run-make-support/src/cc.rs src/tools/run-make-support/src/external_deps/cc.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ pub struct Cc {
2020
cmd: Command,
2121
}
2222

23-
crate::impl_common_helpers!(Cc);
23+
crate::macros::impl_common_helpers!(Cc);
2424

2525
impl Cc {
2626
/// Construct a new platform-specific C compiler invocation.

src/tools/run-make-support/src/clang.rs src/tools/run-make-support/src/external_deps/clang.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ pub struct Clang {
1616
cmd: Command,
1717
}
1818

19-
crate::impl_common_helpers!(Clang);
19+
crate::macros::impl_common_helpers!(Clang);
2020

2121
impl Clang {
2222
/// Construct a new `clang` invocation. `clang` is not always available for all targets.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
use crate::command::Command;
2+
use crate::source_root;
3+
4+
use super::python::python_command;
5+
6+
/// `htmldocck` is a python script which is used for rustdoc test suites, it is assumed to be
7+
/// available at `$SOURCE_ROOT/src/etc/htmldocck.py`.
8+
#[track_caller]
9+
#[must_use]
10+
pub fn htmldocck() -> Command {
11+
let mut python = python_command();
12+
python.arg(source_root().join("src/etc/htmldocck.py"));
13+
python
14+
}

src/tools/run-make-support/src/llvm.rs src/tools/run-make-support/src/external_deps/llvm.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::path::{Path, PathBuf};
22

3-
use crate::{env_var, Command};
3+
use crate::command::Command;
4+
use crate::env_checked::env_var;
45

56
/// Construct a new `llvm-readobj` invocation with the `GNU` output style.
67
/// This assumes that `llvm-readobj` is available at `$LLVM_BIN_DIR/llvm-readobj`.
@@ -57,10 +58,10 @@ pub struct LlvmObjdump {
5758
cmd: Command,
5859
}
5960

60-
crate::impl_common_helpers!(LlvmReadobj);
61-
crate::impl_common_helpers!(LlvmProfdata);
62-
crate::impl_common_helpers!(LlvmFilecheck);
63-
crate::impl_common_helpers!(LlvmObjdump);
61+
crate::macros::impl_common_helpers!(LlvmReadobj);
62+
crate::macros::impl_common_helpers!(LlvmProfdata);
63+
crate::macros::impl_common_helpers!(LlvmFilecheck);
64+
crate::macros::impl_common_helpers!(LlvmObjdump);
6465

6566
/// Generate the path to the bin directory of LLVM.
6667
#[must_use]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//! This module contains external tool dependencies that we assume are available in the environment,
2+
//! such as `cc` or `python`.
3+
//!
4+
//! # Notes
5+
//!
6+
//! - This is not the *only* place where external dependencies are assumed or referenced. For
7+
//! example, see [`cygpath_windows`][crate::path_helpers::cygpath_windows].
8+
9+
pub mod cc;
10+
pub mod clang;
11+
pub mod htmldocck;
12+
pub mod llvm;
13+
pub mod python;
14+
pub mod rustc;
15+
pub mod rustdoc;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
use crate::command::Command;
2+
use crate::env_checked::env_var;
3+
4+
/// Obtain path of python as provided by the `PYTHON` environment variable. It is up to the caller
5+
/// to document and check if the python version is compatible with its intended usage.
6+
#[track_caller]
7+
#[must_use]
8+
pub fn python_command() -> Command {
9+
let python_path = env_var("PYTHON");
10+
Command::new(python_path)
11+
}

0 commit comments

Comments
 (0)