Skip to content

Commit c5755fa

Browse files
committed
check host's libstdc++ version when using ci llvm
Signed-off-by: onur-ozkan <work@onurozkan.dev>
1 parent 3dc4fff commit c5755fa

File tree

2 files changed

+96
-4
lines changed

2 files changed

+96
-4
lines changed

src/bootstrap/src/core/build_steps/tool.rs

+54
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use crate::core::builder::{Builder, Cargo as CargoCommand, RunConfig, ShouldRun,
1010
use crate::core::config::TargetSelection;
1111
use crate::utils::channel::GitInfo;
1212
use crate::utils::exec::BootstrapCommand;
13+
use crate::utils::helpers::output;
1314
use crate::utils::helpers::{add_dylib_path, exe, t};
1415
use crate::Compiler;
1516
use crate::Mode;
@@ -804,6 +805,59 @@ impl Step for LlvmBitcodeLinker {
804805
}
805806
}
806807

808+
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
809+
pub struct LibcxxVersionTool {
810+
pub target: TargetSelection,
811+
}
812+
813+
#[derive(Debug, Clone)]
814+
pub enum LibcxxVersion {
815+
Gnu(usize),
816+
#[allow(dead_code)]
817+
Llvm(usize),
818+
}
819+
820+
impl Step for LibcxxVersionTool {
821+
type Output = LibcxxVersion;
822+
const DEFAULT: bool = false;
823+
const ONLY_HOSTS: bool = true;
824+
825+
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
826+
run.never()
827+
}
828+
829+
fn run(self, builder: &Builder<'_>) -> LibcxxVersion {
830+
let out_dir = builder.out.join(self.target.to_string()).join("libcxx-version");
831+
let _ = fs::remove_dir_all(&out_dir);
832+
t!(fs::create_dir_all(&out_dir));
833+
834+
let compiler = builder.cxx(self.target).unwrap();
835+
let mut cmd = Command::new(compiler);
836+
837+
let executable = out_dir.join("libcxx-version");
838+
cmd.arg("-o").arg(&executable).arg(builder.src.join("src/tools/libcxx-version/main.cpp"));
839+
840+
builder.run_cmd(&mut cmd);
841+
842+
if !executable.exists() {
843+
panic!("Something went wrong. {} is not present", executable.display());
844+
}
845+
846+
let version_output = output(&mut Command::new(executable));
847+
848+
let version_str = version_output.split_once("version:").unwrap().1;
849+
let version = version_str.trim().parse::<usize>().unwrap();
850+
851+
if version_output.starts_with("libstdc++") {
852+
LibcxxVersion::Gnu(version)
853+
} else if version_output.starts_with("libc++") {
854+
LibcxxVersion::Llvm(version)
855+
} else {
856+
panic!("Coudln't recognize the standard library version.");
857+
}
858+
}
859+
}
860+
807861
macro_rules! tool_extended {
808862
(($sel:ident, $builder:ident),
809863
$($name:ident,

src/bootstrap/src/core/sanity.rs

+42-4
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ use std::path::PathBuf;
1616
use std::process::Command;
1717
use walkdir::WalkDir;
1818

19-
use crate::builder::Kind;
19+
use crate::builder::{Builder, Kind};
20+
use crate::core::build_steps::tool;
2021
use crate::core::config::Target;
2122
use crate::utils::helpers::output;
2223
use crate::Build;
@@ -35,6 +36,10 @@ const STAGE0_MISSING_TARGETS: &[&str] = &[
3536
// just a dummy comment so the list doesn't get onelined
3637
];
3738

39+
/// Minimum version threshold for libstdc++ required when using prebuilt LLVM
40+
/// from CI (with`llvm.download-ci-llvm` option).
41+
const LIBSTDCXX_MIN_VERSION_THRESHOLD: usize = 8;
42+
3843
impl Finder {
3944
pub fn new() -> Self {
4045
Self { cache: HashMap::new(), path: env::var_os("PATH").unwrap_or_default() }
@@ -99,6 +104,35 @@ pub fn check(build: &mut Build) {
99104
cmd_finder.must_have("git");
100105
}
101106

107+
// Ensure that a compatible version of libstdc++ is available on the system when using `llvm.download-ci-llvm`.
108+
if !build.config.dry_run() && !build.build.is_msvc() && build.config.llvm_from_ci {
109+
let builder = Builder::new(build);
110+
let libcxx_version = builder.ensure(tool::LibcxxVersionTool { target: build.build });
111+
112+
match libcxx_version {
113+
tool::LibcxxVersion::Gnu(version) => {
114+
if LIBSTDCXX_MIN_VERSION_THRESHOLD > version {
115+
eprintln!(
116+
"\nYour system's libstdc++ version is too old for the `llvm.download-ci-llvm` option."
117+
);
118+
eprintln!("Current version detected: '{}'", version);
119+
eprintln!("Minimum required version: '{}'", LIBSTDCXX_MIN_VERSION_THRESHOLD);
120+
eprintln!(
121+
"Consider upgrading libstdc++ or disabling the `llvm.download-ci-llvm` option."
122+
);
123+
crate::exit!(1);
124+
}
125+
}
126+
tool::LibcxxVersion::Llvm(_) => {
127+
eprintln!(
128+
"\nYour system is using libc++, which is incompatible with the `llvm.download-ci-llvm` option."
129+
);
130+
eprintln!("Disable `llvm.download-ci-llvm` or switch to libstdc++.");
131+
crate::exit!(1);
132+
}
133+
}
134+
}
135+
102136
// We need cmake, but only if we're actually building LLVM or sanitizers.
103137
let building_llvm = build
104138
.hosts
@@ -199,11 +233,15 @@ than building it.
199233
if !["A-A", "B-B", "C-C"].contains(&target_str.as_str()) {
200234
let mut has_target = false;
201235

202-
let missing_targets_hashset: HashSet<_> = STAGE0_MISSING_TARGETS.iter().map(|t| t.to_string()).collect();
203-
let duplicated_targets: Vec<_> = stage0_supported_target_list.intersection(&missing_targets_hashset).collect();
236+
let missing_targets_hashset: HashSet<_> =
237+
STAGE0_MISSING_TARGETS.iter().map(|t| t.to_string()).collect();
238+
let duplicated_targets: Vec<_> =
239+
stage0_supported_target_list.intersection(&missing_targets_hashset).collect();
204240

205241
if !duplicated_targets.is_empty() {
206-
println!("Following targets supported from the stage0 compiler, please remove them from STAGE0_MISSING_TARGETS list.");
242+
println!(
243+
"Following targets supported from the stage0 compiler, please remove them from STAGE0_MISSING_TARGETS list."
244+
);
207245
for duplicated_target in duplicated_targets {
208246
println!(" {duplicated_target}");
209247
}

0 commit comments

Comments
 (0)