Auto merge of #106000 - nikic:lld-build, r=Mark-Simulacrum
Make LLD build forward-compatible with LLVM 16 Switch to using the cmake module instead of llvm-config. I believe this also removes the need for llvm-config-wrapper.
This commit is contained in:
commit
e5e4eef02d
7 changed files with 58 additions and 99 deletions
|
|
@ -24,9 +24,18 @@ use crate::util::get_clang_cl_resource_dir;
|
|||
use crate::util::{self, exe, output, t, up_to_date};
|
||||
use crate::{CLang, GitRepo};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LlvmResult {
|
||||
/// Path to llvm-config binary.
|
||||
/// NB: This is always the host llvm-config!
|
||||
pub llvm_config: PathBuf,
|
||||
/// Path to LLVM cmake directory for the target.
|
||||
pub llvm_cmake_dir: PathBuf,
|
||||
}
|
||||
|
||||
pub struct Meta {
|
||||
stamp: HashStamp,
|
||||
build_llvm_config: PathBuf,
|
||||
res: LlvmResult,
|
||||
out_dir: PathBuf,
|
||||
root: String,
|
||||
}
|
||||
|
|
@ -64,7 +73,7 @@ impl LdFlags {
|
|||
pub fn prebuilt_llvm_config(
|
||||
builder: &Builder<'_>,
|
||||
target: TargetSelection,
|
||||
) -> Result<PathBuf, Meta> {
|
||||
) -> Result<LlvmResult, Meta> {
|
||||
builder.config.maybe_download_ci_llvm();
|
||||
|
||||
// If we're using a custom LLVM bail out here, but we can only use a
|
||||
|
|
@ -72,7 +81,14 @@ pub fn prebuilt_llvm_config(
|
|||
if let Some(config) = builder.config.target_config.get(&target) {
|
||||
if let Some(ref s) = config.llvm_config {
|
||||
check_llvm_version(builder, s);
|
||||
return Ok(s.to_path_buf());
|
||||
let llvm_config = s.to_path_buf();
|
||||
let mut llvm_cmake_dir = llvm_config.clone();
|
||||
llvm_cmake_dir.pop();
|
||||
llvm_cmake_dir.pop();
|
||||
llvm_cmake_dir.push("lib");
|
||||
llvm_cmake_dir.push("cmake");
|
||||
llvm_cmake_dir.push("llvm");
|
||||
return Ok(LlvmResult { llvm_config, llvm_cmake_dir });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -84,8 +100,9 @@ pub fn prebuilt_llvm_config(
|
|||
llvm_config_ret_dir.push("build");
|
||||
}
|
||||
llvm_config_ret_dir.push("bin");
|
||||
|
||||
let build_llvm_config = llvm_config_ret_dir.join(exe("llvm-config", builder.config.build));
|
||||
let llvm_cmake_dir = out_dir.join("lib/cmake/llvm");
|
||||
let res = LlvmResult { llvm_config: build_llvm_config, llvm_cmake_dir };
|
||||
|
||||
let stamp = out_dir.join("llvm-finished-building");
|
||||
let stamp = HashStamp::new(stamp, builder.in_tree_llvm_info.sha());
|
||||
|
|
@ -96,7 +113,7 @@ pub fn prebuilt_llvm_config(
|
|||
Using a potentially stale build of LLVM; \
|
||||
This may not behave well.",
|
||||
);
|
||||
return Ok(build_llvm_config);
|
||||
return Ok(res);
|
||||
}
|
||||
|
||||
if stamp.is_done() {
|
||||
|
|
@ -110,10 +127,10 @@ pub fn prebuilt_llvm_config(
|
|||
stamp.path.display()
|
||||
));
|
||||
}
|
||||
return Ok(build_llvm_config);
|
||||
return Ok(res);
|
||||
}
|
||||
|
||||
Err(Meta { stamp, build_llvm_config, out_dir, root: root.into() })
|
||||
Err(Meta { stamp, res, out_dir, root: root.into() })
|
||||
}
|
||||
|
||||
/// This retrieves the LLVM sha we *want* to use, according to git history.
|
||||
|
|
@ -223,7 +240,7 @@ pub struct Llvm {
|
|||
}
|
||||
|
||||
impl Step for Llvm {
|
||||
type Output = PathBuf; // path to llvm-config
|
||||
type Output = LlvmResult;
|
||||
|
||||
const ONLY_HOSTS: bool = true;
|
||||
|
||||
|
|
@ -236,7 +253,7 @@ impl Step for Llvm {
|
|||
}
|
||||
|
||||
/// Compile LLVM for `target`.
|
||||
fn run(self, builder: &Builder<'_>) -> PathBuf {
|
||||
fn run(self, builder: &Builder<'_>) -> LlvmResult {
|
||||
let target = self.target;
|
||||
let target_native = if self.target.starts_with("riscv") {
|
||||
// RISC-V target triples in Rust is not named the same as C compiler target triples.
|
||||
|
|
@ -252,11 +269,10 @@ impl Step for Llvm {
|
|||
target.to_string()
|
||||
};
|
||||
|
||||
let Meta { stamp, build_llvm_config, out_dir, root } =
|
||||
match prebuilt_llvm_config(builder, target) {
|
||||
Ok(p) => return p,
|
||||
Err(m) => m,
|
||||
};
|
||||
let Meta { stamp, res, out_dir, root } = match prebuilt_llvm_config(builder, target) {
|
||||
Ok(p) => return p,
|
||||
Err(m) => m,
|
||||
};
|
||||
|
||||
builder.update_submodule(&Path::new("src").join("llvm-project"));
|
||||
if builder.llvm_link_shared() && target.contains("windows") {
|
||||
|
|
@ -430,7 +446,8 @@ impl Step for Llvm {
|
|||
|
||||
// https://llvm.org/docs/HowToCrossCompileLLVM.html
|
||||
if target != builder.config.build {
|
||||
let llvm_config = builder.ensure(Llvm { target: builder.config.build });
|
||||
let LlvmResult { llvm_config, .. } =
|
||||
builder.ensure(Llvm { target: builder.config.build });
|
||||
if !builder.config.dry_run() {
|
||||
let llvm_bindir = output(Command::new(&llvm_config).arg("--bindir"));
|
||||
let host_bin = Path::new(llvm_bindir.trim());
|
||||
|
|
@ -480,7 +497,7 @@ impl Step for Llvm {
|
|||
// tools and libs on all platforms.
|
||||
|
||||
if builder.config.dry_run() {
|
||||
return build_llvm_config;
|
||||
return res;
|
||||
}
|
||||
|
||||
cfg.build();
|
||||
|
|
@ -490,7 +507,7 @@ impl Step for Llvm {
|
|||
// for a versioned path like libLLVM-14.dylib. Manually create a symbolic
|
||||
// link to make llvm-config happy.
|
||||
if builder.llvm_link_shared() && target.contains("apple-darwin") {
|
||||
let mut cmd = Command::new(&build_llvm_config);
|
||||
let mut cmd = Command::new(&res.llvm_config);
|
||||
let version = output(cmd.arg("--version"));
|
||||
let major = version.split('.').next().unwrap();
|
||||
let lib_name = match llvm_version_suffix {
|
||||
|
|
@ -509,18 +526,18 @@ impl Step for Llvm {
|
|||
// LLVM after a configuration change, so to rebuild it the build files have to be removed,
|
||||
// which will also remove these modified files.
|
||||
if builder.config.llvm_bolt_profile_generate {
|
||||
instrument_with_bolt_inplace(&get_built_llvm_lib_path(&build_llvm_config));
|
||||
instrument_with_bolt_inplace(&get_built_llvm_lib_path(&res.llvm_config));
|
||||
}
|
||||
if let Some(path) = &builder.config.llvm_bolt_profile_use {
|
||||
optimize_library_with_bolt_inplace(
|
||||
&get_built_llvm_lib_path(&build_llvm_config),
|
||||
&get_built_llvm_lib_path(&res.llvm_config),
|
||||
&Path::new(path),
|
||||
);
|
||||
}
|
||||
|
||||
t!(stamp.write());
|
||||
|
||||
build_llvm_config
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -806,7 +823,8 @@ impl Step for Lld {
|
|||
}
|
||||
let target = self.target;
|
||||
|
||||
let llvm_config = builder.ensure(Llvm { target: self.target });
|
||||
let LlvmResult { llvm_config, llvm_cmake_dir } =
|
||||
builder.ensure(Llvm { target: self.target });
|
||||
|
||||
let out_dir = builder.lld_out(target);
|
||||
let done_stamp = out_dir.join("lld-finished-building");
|
||||
|
|
@ -837,22 +855,6 @@ impl Step for Lld {
|
|||
configure_cmake(builder, target, &mut cfg, true, ldflags);
|
||||
configure_llvm(builder, target, &mut cfg);
|
||||
|
||||
// This is an awful, awful hack. Discovered when we migrated to using
|
||||
// clang-cl to compile LLVM/LLD it turns out that LLD, when built out of
|
||||
// tree, will execute `llvm-config --cmakedir` and then tell CMake about
|
||||
// that directory for later processing. Unfortunately if this path has
|
||||
// forward slashes in it (which it basically always does on Windows)
|
||||
// then CMake will hit a syntax error later on as... something isn't
|
||||
// escaped it seems?
|
||||
//
|
||||
// Instead of attempting to fix this problem in upstream CMake and/or
|
||||
// LLVM/LLD we just hack around it here. This thin wrapper will take the
|
||||
// output from llvm-config and replace all instances of `\` with `/` to
|
||||
// ensure we don't hit the same bugs with escaping. It means that you
|
||||
// can't build on a system where your paths require `\` on Windows, but
|
||||
// there's probably a lot of reasons you can't do that other than this.
|
||||
let llvm_config_shim = env::current_exe().unwrap().with_file_name("llvm-config-wrapper");
|
||||
|
||||
// Re-use the same flags as llvm to control the level of debug information
|
||||
// generated for lld.
|
||||
let profile = match (builder.config.llvm_optimize, builder.config.llvm_release_debuginfo) {
|
||||
|
|
@ -863,36 +865,17 @@ impl Step for Lld {
|
|||
|
||||
cfg.out_dir(&out_dir)
|
||||
.profile(profile)
|
||||
.env("LLVM_CONFIG_REAL", &llvm_config)
|
||||
.define("LLVM_CONFIG_PATH", llvm_config_shim)
|
||||
.define("LLVM_CMAKE_DIR", llvm_cmake_dir)
|
||||
.define("LLVM_INCLUDE_TESTS", "OFF");
|
||||
|
||||
// While we're using this horrible workaround to shim the execution of
|
||||
// llvm-config, let's just pile on more. I can't seem to figure out how
|
||||
// to build LLD as a standalone project and also cross-compile it at the
|
||||
// same time. It wants a natively executable `llvm-config` to learn
|
||||
// about LLVM, but then it learns about all the host configuration of
|
||||
// LLVM and tries to link to host LLVM libraries.
|
||||
//
|
||||
// To work around that we tell our shim to replace anything with the
|
||||
// build target with the actual target instead. This'll break parts of
|
||||
// LLD though which try to execute host tools, such as llvm-tblgen, so
|
||||
// we specifically tell it where to find those. This is likely super
|
||||
// brittle and will break over time. If anyone knows better how to
|
||||
// cross-compile LLD it would be much appreciated to fix this!
|
||||
if target != builder.config.build {
|
||||
cfg.env("LLVM_CONFIG_SHIM_REPLACE", &builder.config.build.triple)
|
||||
.env("LLVM_CONFIG_SHIM_REPLACE_WITH", &target.triple)
|
||||
.define(
|
||||
"LLVM_TABLEGEN_EXE",
|
||||
llvm_config.with_file_name("llvm-tblgen").with_extension(EXE_EXTENSION),
|
||||
);
|
||||
// Use the host llvm-tblgen binary.
|
||||
cfg.define(
|
||||
"LLVM_TABLEGEN_EXE",
|
||||
llvm_config.with_file_name("llvm-tblgen").with_extension(EXE_EXTENSION),
|
||||
);
|
||||
}
|
||||
|
||||
// Explicitly set C++ standard, because upstream doesn't do so
|
||||
// for standalone builds.
|
||||
cfg.define("CMAKE_CXX_STANDARD", "14");
|
||||
|
||||
cfg.build();
|
||||
|
||||
t!(File::create(&done_stamp));
|
||||
|
|
@ -994,7 +977,7 @@ impl Step for Sanitizers {
|
|||
return runtimes;
|
||||
}
|
||||
|
||||
let llvm_config = builder.ensure(Llvm { target: builder.config.build });
|
||||
let LlvmResult { llvm_config, .. } = builder.ensure(Llvm { target: builder.config.build });
|
||||
if builder.config.dry_run() {
|
||||
return runtimes;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue