Skip to content

Commit a98130e

Browse files
authored
Unrolled build for rust-lang#137012
Rollup merge of rust-lang#137012 - Shourya742:2025-02-14-doc-and-unit-test-cc-detect, r=onur-ozkan add docs and ut for bootstrap util cc-detect This PR adds doc and unit test for bootstrap utils/cc-detect module
2 parents 5bc6231 + f6c911a commit a98130e

File tree

2 files changed

+285
-3
lines changed

2 files changed

+285
-3
lines changed

src/bootstrap/src/utils/cc_detect.rs

+31-3
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use crate::core::config::TargetSelection;
2929
use crate::utils::exec::{BootstrapCommand, command};
3030
use crate::{Build, CLang, GitRepo};
3131

32+
/// Finds archiver tool for the given target if possible.
3233
/// FIXME(onur-ozkan): This logic should be replaced by calling into the `cc` crate.
3334
fn cc2ar(cc: &Path, target: TargetSelection, default_ar: PathBuf) -> Option<PathBuf> {
3435
if let Some(ar) = env::var_os(format!("AR_{}", target.triple.replace('-', "_"))) {
@@ -58,6 +59,7 @@ fn cc2ar(cc: &Path, target: TargetSelection, default_ar: PathBuf) -> Option<Path
5859
}
5960
}
6061

62+
/// Creates and configures a new [`cc::Build`] instance for the given target.
6163
fn new_cc_build(build: &Build, target: TargetSelection) -> cc::Build {
6264
let mut cfg = cc::Build::new();
6365
cfg.cargo_metadata(false)
@@ -84,6 +86,12 @@ fn new_cc_build(build: &Build, target: TargetSelection) -> cc::Build {
8486
cfg
8587
}
8688

89+
/// Probes for C and C++ compilers and configures the corresponding entries in the [`Build`]
90+
/// structure.
91+
///
92+
/// This function determines which targets need a C compiler (and, if needed, a C++ compiler)
93+
/// by combining the primary build target, host targets, and any additional targets. For
94+
/// each target, it calls [`find_target`] to configure the necessary compiler tools.
8795
pub fn find(build: &Build) {
8896
let targets: HashSet<_> = match build.config.cmd {
8997
// We don't need to check cross targets for these commands.
@@ -112,6 +120,11 @@ pub fn find(build: &Build) {
112120
}
113121
}
114122

123+
/// Probes and configures the C and C++ compilers for a single target.
124+
///
125+
/// This function uses both user-specified configuration (from `config.toml`) and auto-detection
126+
/// logic to determine the correct C/C++ compilers for the target. It also determines the appropriate
127+
/// archiver (`ar`) and sets up additional compilation flags (both handled and unhandled).
115128
pub fn find_target(build: &Build, target: TargetSelection) {
116129
let mut cfg = new_cc_build(build, target);
117130
let config = build.config.target_config.get(&target);
@@ -172,6 +185,8 @@ pub fn find_target(build: &Build, target: TargetSelection) {
172185
}
173186
}
174187

188+
/// Determines the default compiler for a given target and language when not explicitly
189+
/// configured in `config.toml`.
175190
fn default_compiler(
176191
cfg: &mut cc::Build,
177192
compiler: Language,
@@ -248,6 +263,12 @@ fn default_compiler(
248263
}
249264
}
250265

266+
/// Constructs the path to the Android NDK compiler for the given target triple and language.
267+
///
268+
/// This helper function transform the target triple by converting certain architecture names
269+
/// (for example, translating "arm" to "arm7a"), appends the minimum API level (hardcoded as "21"
270+
/// for NDK r26d), and then constructs the full path based on the provided NDK directory and host
271+
/// platform.
251272
pub(crate) fn ndk_compiler(compiler: Language, triple: &str, ndk: &Path) -> PathBuf {
252273
let mut triple_iter = triple.split('-');
253274
let triple_translated = if let Some(arch) = triple_iter.next() {
@@ -277,7 +298,11 @@ pub(crate) fn ndk_compiler(compiler: Language, triple: &str, ndk: &Path) -> Path
277298
ndk.join("toolchains").join("llvm").join("prebuilt").join(host_tag).join("bin").join(compiler)
278299
}
279300

280-
/// The target programming language for a native compiler.
301+
/// Representing the target programming language for a native compiler.
302+
///
303+
/// This enum is used to indicate whether a particular compiler is intended for C or C++.
304+
/// It also provides helper methods for obtaining the standard executable names for GCC and
305+
/// clang-based compilers.
281306
#[derive(PartialEq)]
282307
pub(crate) enum Language {
283308
/// The compiler is targeting C.
@@ -287,19 +312,22 @@ pub(crate) enum Language {
287312
}
288313

289314
impl Language {
290-
/// Obtains the name of a compiler in the GCC collection.
315+
/// Returns the executable name for a GCC compiler corresponding to this language.
291316
fn gcc(self) -> &'static str {
292317
match self {
293318
Language::C => "gcc",
294319
Language::CPlusPlus => "g++",
295320
}
296321
}
297322

298-
/// Obtains the name of a compiler in the clang suite.
323+
/// Returns the executable name for a clang-based compiler corresponding to this language.
299324
fn clang(self) -> &'static str {
300325
match self {
301326
Language::C => "clang",
302327
Language::CPlusPlus => "clang++",
303328
}
304329
}
305330
}
331+
332+
#[cfg(test)]
333+
mod tests;
+254
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
use std::path::{Path, PathBuf};
2+
use std::{env, iter};
3+
4+
use super::*;
5+
use crate::core::config::{Target, TargetSelection};
6+
use crate::{Build, Config, Flags};
7+
8+
#[test]
9+
fn test_cc2ar_env_specific() {
10+
let triple = "x86_64-unknown-linux-gnu";
11+
let key = "AR_x86_64_unknown_linux_gnu";
12+
env::set_var(key, "custom-ar");
13+
let target = TargetSelection::from_user(triple);
14+
let cc = Path::new("/usr/bin/clang");
15+
let default_ar = PathBuf::from("default-ar");
16+
let result = cc2ar(cc, target, default_ar);
17+
env::remove_var(key);
18+
assert_eq!(result, Some(PathBuf::from("custom-ar")));
19+
}
20+
21+
#[test]
22+
fn test_cc2ar_musl() {
23+
let triple = "x86_64-unknown-linux-musl";
24+
env::remove_var("AR_x86_64_unknown_linux_musl");
25+
env::remove_var("AR");
26+
let target = TargetSelection::from_user(triple);
27+
let cc = Path::new("/usr/bin/clang");
28+
let default_ar = PathBuf::from("default-ar");
29+
let result = cc2ar(cc, target, default_ar);
30+
assert_eq!(result, Some(PathBuf::from("ar")));
31+
}
32+
33+
#[test]
34+
fn test_cc2ar_openbsd() {
35+
let triple = "x86_64-unknown-openbsd";
36+
env::remove_var("AR_x86_64_unknown_openbsd");
37+
env::remove_var("AR");
38+
let target = TargetSelection::from_user(triple);
39+
let cc = Path::new("/usr/bin/cc");
40+
let default_ar = PathBuf::from("default-ar");
41+
let result = cc2ar(cc, target, default_ar);
42+
assert_eq!(result, Some(PathBuf::from("ar")));
43+
}
44+
45+
#[test]
46+
fn test_cc2ar_vxworks() {
47+
let triple = "armv7-wrs-vxworks";
48+
env::remove_var("AR_armv7_wrs_vxworks");
49+
env::remove_var("AR");
50+
let target = TargetSelection::from_user(triple);
51+
let cc = Path::new("/usr/bin/clang");
52+
let default_ar = PathBuf::from("default-ar");
53+
let result = cc2ar(cc, target, default_ar);
54+
assert_eq!(result, Some(PathBuf::from("wr-ar")));
55+
}
56+
57+
#[test]
58+
fn test_cc2ar_nto_i586() {
59+
let triple = "i586-unknown-nto-something";
60+
env::remove_var("AR_i586_unknown_nto_something");
61+
env::remove_var("AR");
62+
let target = TargetSelection::from_user(triple);
63+
let cc = Path::new("/usr/bin/clang");
64+
let default_ar = PathBuf::from("default-ar");
65+
let result = cc2ar(cc, target, default_ar);
66+
assert_eq!(result, Some(PathBuf::from("ntox86-ar")));
67+
}
68+
69+
#[test]
70+
fn test_cc2ar_nto_aarch64() {
71+
let triple = "aarch64-unknown-nto-something";
72+
env::remove_var("AR_aarch64_unknown_nto_something");
73+
env::remove_var("AR");
74+
let target = TargetSelection::from_user(triple);
75+
let cc = Path::new("/usr/bin/clang");
76+
let default_ar = PathBuf::from("default-ar");
77+
let result = cc2ar(cc, target, default_ar);
78+
assert_eq!(result, Some(PathBuf::from("ntoaarch64-ar")));
79+
}
80+
81+
#[test]
82+
fn test_cc2ar_nto_x86_64() {
83+
let triple = "x86_64-unknown-nto-something";
84+
env::remove_var("AR_x86_64_unknown_nto_something");
85+
env::remove_var("AR");
86+
let target = TargetSelection::from_user(triple);
87+
let cc = Path::new("/usr/bin/clang");
88+
let default_ar = PathBuf::from("default-ar");
89+
let result = cc2ar(cc, target, default_ar);
90+
assert_eq!(result, Some(PathBuf::from("ntox86_64-ar")));
91+
}
92+
93+
#[test]
94+
#[should_panic(expected = "Unknown architecture, cannot determine archiver for Neutrino QNX")]
95+
fn test_cc2ar_nto_unknown() {
96+
let triple = "powerpc-unknown-nto-something";
97+
env::remove_var("AR_powerpc_unknown_nto_something");
98+
env::remove_var("AR");
99+
let target = TargetSelection::from_user(triple);
100+
let cc = Path::new("/usr/bin/clang");
101+
let default_ar = PathBuf::from("default-ar");
102+
let _ = cc2ar(cc, target, default_ar);
103+
}
104+
105+
#[test]
106+
fn test_ndk_compiler_c() {
107+
let ndk_path = PathBuf::from("/ndk");
108+
let target_triple = "arm-unknown-linux-android";
109+
let expected_triple_translated = "armv7a-unknown-linux-android";
110+
let expected_compiler = format!("{}21-{}", expected_triple_translated, Language::C.clang());
111+
let host_tag = if cfg!(target_os = "macos") {
112+
"darwin-x86_64"
113+
} else if cfg!(target_os = "windows") {
114+
"windows-x86_64"
115+
} else {
116+
"linux-x86_64"
117+
};
118+
let expected_path = ndk_path
119+
.join("toolchains")
120+
.join("llvm")
121+
.join("prebuilt")
122+
.join(host_tag)
123+
.join("bin")
124+
.join(&expected_compiler);
125+
let result = ndk_compiler(Language::C, target_triple, &ndk_path);
126+
assert_eq!(result, expected_path);
127+
}
128+
129+
#[test]
130+
fn test_ndk_compiler_cpp() {
131+
let ndk_path = PathBuf::from("/ndk");
132+
let target_triple = "arm-unknown-linux-android";
133+
let expected_triple_translated = "armv7a-unknown-linux-android";
134+
let expected_compiler =
135+
format!("{}21-{}", expected_triple_translated, Language::CPlusPlus.clang());
136+
let host_tag = if cfg!(target_os = "macos") {
137+
"darwin-x86_64"
138+
} else if cfg!(target_os = "windows") {
139+
"windows-x86_64"
140+
} else {
141+
"linux-x86_64"
142+
};
143+
let expected_path = ndk_path
144+
.join("toolchains")
145+
.join("llvm")
146+
.join("prebuilt")
147+
.join(host_tag)
148+
.join("bin")
149+
.join(&expected_compiler);
150+
let result = ndk_compiler(Language::CPlusPlus, target_triple, &ndk_path);
151+
assert_eq!(result, expected_path);
152+
}
153+
154+
#[test]
155+
fn test_language_gcc() {
156+
assert_eq!(Language::C.gcc(), "gcc");
157+
assert_eq!(Language::CPlusPlus.gcc(), "g++");
158+
}
159+
160+
#[test]
161+
fn test_language_clang() {
162+
assert_eq!(Language::C.clang(), "clang");
163+
assert_eq!(Language::CPlusPlus.clang(), "clang++");
164+
}
165+
166+
#[test]
167+
fn test_new_cc_build() {
168+
let build = Build::new(Config { ..Config::parse(Flags::parse(&["check".to_owned()])) });
169+
let target = TargetSelection::from_user("x86_64-unknown-linux-gnu");
170+
let cfg = new_cc_build(&build, target.clone());
171+
let compiler = cfg.get_compiler();
172+
assert!(!compiler.path().to_str().unwrap().is_empty(), "Compiler path should not be empty");
173+
}
174+
175+
#[test]
176+
fn test_default_compiler_wasi() {
177+
let build = Build::new(Config { ..Config::parse(Flags::parse(&["check".to_owned()])) });
178+
let target = TargetSelection::from_user("wasm32-wasi");
179+
let wasi_sdk = PathBuf::from("/wasi-sdk");
180+
env::set_var("WASI_SDK_PATH", &wasi_sdk);
181+
let mut cfg = cc::Build::new();
182+
if let Some(result) = default_compiler(&mut cfg, Language::C, target.clone(), &build) {
183+
let expected = {
184+
let compiler = format!("{}-clang", target.triple);
185+
wasi_sdk.join("bin").join(compiler)
186+
};
187+
assert_eq!(result, expected);
188+
} else {
189+
panic!(
190+
"default_compiler should return a compiler path for wasi target when WASI_SDK_PATH is set"
191+
);
192+
}
193+
env::remove_var("WASI_SDK_PATH");
194+
}
195+
196+
#[test]
197+
fn test_default_compiler_fallback() {
198+
let build = Build::new(Config { ..Config::parse(Flags::parse(&["check".to_owned()])) });
199+
let target = TargetSelection::from_user("x86_64-unknown-linux-gnu");
200+
let mut cfg = cc::Build::new();
201+
let result = default_compiler(&mut cfg, Language::C, target, &build);
202+
assert!(result.is_none(), "default_compiler should return None for generic targets");
203+
}
204+
205+
#[test]
206+
fn test_find_target_with_config() {
207+
let mut build = Build::new(Config { ..Config::parse(Flags::parse(&["check".to_owned()])) });
208+
let target = TargetSelection::from_user("x86_64-unknown-linux-gnu");
209+
let mut target_config = Target::default();
210+
target_config.cc = Some(PathBuf::from("dummy-cc"));
211+
target_config.cxx = Some(PathBuf::from("dummy-cxx"));
212+
target_config.ar = Some(PathBuf::from("dummy-ar"));
213+
target_config.ranlib = Some(PathBuf::from("dummy-ranlib"));
214+
build.config.target_config.insert(target.clone(), target_config);
215+
find_target(&build, target.clone());
216+
let binding = build.cc.borrow();
217+
let cc_tool = binding.get(&target).unwrap();
218+
assert_eq!(cc_tool.path(), &PathBuf::from("dummy-cc"));
219+
let binding = build.cxx.borrow();
220+
let cxx_tool = binding.get(&target).unwrap();
221+
assert_eq!(cxx_tool.path(), &PathBuf::from("dummy-cxx"));
222+
let binding = build.ar.borrow();
223+
let ar = binding.get(&target).unwrap();
224+
assert_eq!(ar, &PathBuf::from("dummy-ar"));
225+
let binding = build.ranlib.borrow();
226+
let ranlib = binding.get(&target).unwrap();
227+
assert_eq!(ranlib, &PathBuf::from("dummy-ranlib"));
228+
}
229+
230+
#[test]
231+
fn test_find_target_without_config() {
232+
let mut build = Build::new(Config { ..Config::parse(Flags::parse(&["check".to_owned()])) });
233+
let target = TargetSelection::from_user("x86_64-unknown-linux-gnu");
234+
build.config.target_config.clear();
235+
find_target(&build, target.clone());
236+
assert!(build.cc.borrow().contains_key(&target));
237+
if !target.triple.contains("vxworks") {
238+
assert!(build.cxx.borrow().contains_key(&target));
239+
}
240+
assert!(build.ar.borrow().contains_key(&target));
241+
}
242+
243+
#[test]
244+
fn test_find() {
245+
let mut build = Build::new(Config { ..Config::parse(Flags::parse(&["check".to_owned()])) });
246+
let target1 = TargetSelection::from_user("x86_64-unknown-linux-gnu");
247+
let target2 = TargetSelection::from_user("arm-linux-androideabi");
248+
build.targets.push(target1.clone());
249+
build.hosts.push(target2.clone());
250+
find(&build);
251+
for t in build.hosts.iter().chain(build.targets.iter()).chain(iter::once(&build.build)) {
252+
assert!(build.cc.borrow().contains_key(t), "CC not set for target {}", t.triple);
253+
}
254+
}

0 commit comments

Comments
 (0)