From 0d555de37dfdaed7d5b2f938359bbcdc86906b49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Sat, 25 Apr 2026 02:35:46 +0200 Subject: [PATCH] Allow building LLVM DLL with MSVC --- compiler/rustc_llvm/build.rs | 10 ++++++++ src/bootstrap/src/core/build_steps/llvm.rs | 27 ++++++++++++++++++---- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs index ef262a5b19b08..696a9e3d000e9 100644 --- a/compiler/rustc_llvm/build.rs +++ b/compiler/rustc_llvm/build.rs @@ -287,6 +287,16 @@ fn main() { cfg.flag(&*flag); } + // When linking a shared (dylib) LLVM on MSVC, the LLVM build itself is compiled with + // `/Zc:dllexportInlines-` (see llvm-project's AddLLVM.cmake) so that inline class members + // are *not* dllexport'ed (to stay under the 64k export-symbol limit). The consumer must + // match that flag, otherwise it emits `__imp_` references for those inline members (e.g. + // `Module::setTargetTriple`, `Function::getAttributes`) that the DLL intentionally does not + // export, breaking the link. With the flag, the in-header inline bodies are used directly. + if target.contains("msvc") && tracked_env_var_os("LLVM_LINK_SHARED").is_some() { + cfg.flag("/Zc:dllexportInlines-"); + } + // Remap ci-llvm include paths in debug info for reproducible builds. if let Some(maps) = tracked_env_var_os("RUSTC_DEBUGINFO_MAP") && let Some(maps_str) = maps.to_str() diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index ff96f341966b7..5388832f9ed44 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -24,7 +24,7 @@ use crate::utils::exec::command; use crate::utils::helpers::{ self, exe, get_clang_cl_resource_dir, libdir, t, unhashed_basename, up_to_date, }; -use crate::{CLang, GitRepo, Kind, exit, trace}; +use crate::{CLang, FileType, GitRepo, Kind, exit, trace}; #[derive(Clone)] pub struct LlvmResult { @@ -308,7 +308,10 @@ impl CommandLineStep for Llvm { LlvmBuildStatus::ShouldBuild(m) => m, }; - if builder.llvm_link_shared() && target.is_windows() && !target.is_windows_gnullvm() { + if builder.llvm_link_shared() + && target.is_windows() + && !(target.is_windows_gnullvm() || target.is_msvc()) + { panic!("shared linking to LLVM is not currently supported on {}", target.triple); } @@ -409,6 +412,8 @@ impl CommandLineStep for Llvm { // for the tools. We don't do this on every platform as it doesn't work // equally well everywhere. if builder.llvm_link_shared() { + cfg.define("LLVM_BUILD_LLVM_DYLIB_VIS", "ON"); + cfg.define("LLVM_BUILD_LLVM_DYLIB", "ON"); cfg.define("LLVM_LINK_LLVM_DYLIB", "ON"); } @@ -557,12 +562,13 @@ impl CommandLineStep for Llvm { cfg.build(); - // Helper to find the name of LLVM's shared library on darwin and linux. + // Helper to find the name of LLVM's shared library. let find_llvm_lib_name = |extension| { let major = get_llvm_version_major(builder, &res.host_llvm_config); + let prefix = if target.is_msvc() { "" } else { "lib" }; match &llvm_version_suffix { - Some(version_suffix) => format!("libLLVM-{major}{version_suffix}.{extension}"), - None => format!("libLLVM-{major}.{extension}"), + Some(version_suffix) => format!("{prefix}LLVM-{major}{version_suffix}.{extension}"), + None => format!("{prefix}LLVM-{major}.{extension}"), } }; @@ -578,6 +584,17 @@ impl CommandLineStep for Llvm { } } + // Create the .dll.lib import file which llvm-config incorrectly points to + if builder.llvm_link_shared() && target.is_msvc() { + let lib_name = find_llvm_lib_name(""); + let lib_path = out_dir.join("lib"); + let wanted = lib_path.clone().join(lib_name.clone() + "dll.lib"); + let wrong = lib_path.join(lib_name + "lib"); + if wrong.exists() && !wanted.exists() { + builder.copy_link(&wrong, &wanted, FileType::Regular); + } + } + // When building LLVM as a shared library on linux, it can contain unexpected debuginfo: // some can come from the C++ standard library. Unless we're explicitly requesting LLVM to // be built with debuginfo, strip it away after the fact, to make dist artifacts smaller.