This is an automated email from the ASF dual-hosted git repository. leginee pushed a commit to branch bazel-migration in repository https://gitbox.apache.org/repos/asf/openoffice.git
commit da6d97753b9dbacd92e5039544392af0fdea15d6 Author: Peter Kovacs <[email protected]> AuthorDate: Thu Jul 2 21:41:20 2026 +0200 switch to LLVM Linker if we build debug builds with pbd files --- build/toolchain/BUILD.bazel | 16 +++-- build/toolchain/windows_cc_toolchain_config.bzl | 34 ++++++++-- build/vs_config_repo.bzl | 88 +++++++++++++++++++++++-- 3 files changed, 123 insertions(+), 15 deletions(-) diff --git a/build/toolchain/BUILD.bazel b/build/toolchain/BUILD.bazel index ab0b8b16cf..5a244540d0 100644 --- a/build/toolchain/BUILD.bazel +++ b/build/toolchain/BUILD.bazel @@ -1,6 +1,6 @@ load("@rules_cc//cc/toolchains:cc_toolchain.bzl", "cc_toolchain") load(":windows_cc_toolchain_config.bzl", "cc_toolchain_config") -load("@vs_config//:paths.bzl", _VS = "VS", _VC = "VC", _SDK = "SDK", _MSVC_TMP = "MSVC_TMP", _LINK_WRAPPER = "LINK_WRAPPER") +load("@vs_config//:paths.bzl", _VS = "VS", _VC = "VC", _SDK = "SDK", _MSVC_TMP = "MSVC_TMP", _LINK_WRAPPER = "LINK_WRAPPER", _PDB_LINK = "PDB_LINK") # VS2008 x86 toolchain for Apache OpenOffice on Windows # Paths are configured via --repo_env=VS_PATH / --repo_env=SDK_PATH in user.bazelrc. @@ -37,10 +37,16 @@ cc_toolchain_config( tool_bin_path = _VC + "\\bin", msvc_cl_path = _VC + "\\bin\\cl.exe", msvc_ml_path = _VC + "\\bin\\ml.exe", - # Native cc_library/cc_binary links go through the mspdbsrv-endpoint wrapper - # (see vs_config_repo.bzl) so debug (/DEBUG) builds run parallel without - # LNK1318. rules_foreign_cc still uses raw link.exe via the "ld" tool_path - # below (external libs build without /DEBUG, so no mspdbsrv contention). + # Link tool selection (see windows_cc_toolchain_config.bzl link_tools): + # - generate_pdb builds (debug .pdb) link with a modern MSVC link.exe + # (auto-discovered under VS_MODERN_PATH), whose mspdbsrv writes PDBs in + # parallel — so /DEBUG links run at full --jobs without the VC9 LNK1318 + # mspdbsrv contention. It reads the VC9 /Z7 objects natively. + # - all other links use VC9 link.exe (via the mspdbsrv-endpoint wrapper), + # so shipped non-PDB binaries are produced by the same linker as before. + # rules_foreign_cc still uses raw link.exe via the "ld" tool_path below + # (external libs build without /DEBUG, so no mspdbsrv contention there). + pdb_link_path = _PDB_LINK, msvc_link_path = _LINK_WRAPPER, msvc_lib_path = _VC + "\\bin\\lib.exe", cxx_builtin_include_directories = [ diff --git a/build/toolchain/windows_cc_toolchain_config.bzl b/build/toolchain/windows_cc_toolchain_config.bzl index 0a26698240..85924bb390 100644 --- a/build/toolchain/windows_cc_toolchain_config.bzl +++ b/build/toolchain/windows_cc_toolchain_config.bzl @@ -146,6 +146,24 @@ def _impl(ctx): ] if _use_msvc_toolchain(ctx): + # Linker tool selection for the executable / dynamic-library link actions. + # When the generate_pdb feature is enabled (debug builds that emit .pdb), + # Bazel runs a modern MSVC link.exe (pdb_link_path, auto-discovered from a + # VS2017+/BuildTools install). The VC9 linker's single mspdbsrv cannot + # write PDBs in parallel (LNK1318 0x6BA; VC9 has no /FS), while a modern + # link.exe reads the VC9 /Z7 objects natively and its mspdbsrv handles + # parallel PDB writes — so debug builds run at full --jobs. (LLVM lld-link + # was rejected: it cannot emit PDBs from VC9-era CodeView.) Every other + # link stays on VC9 link.exe so shipped (non-PDB) binaries are produced by + # the same linker as before. Bazel selects the first tool whose + # with_features set matches the active feature configuration. + link_tools = [ + tool( + path = ctx.attr.pdb_link_path, + with_features = [with_feature_set(features = ["generate_pdb"])], + ), + tool(path = ctx.attr.msvc_link_path), + ] cpp_link_nodeps_dynamic_library_action = action_config( action_name = ACTION_NAMES.cpp_link_nodeps_dynamic_library, implies = [ @@ -162,7 +180,7 @@ def _impl(ctx): "has_configured_linker_path", "def_file", ], - tools = [tool(path = ctx.attr.msvc_link_path)], + tools = link_tools, ) cpp_link_static_library_action = action_config( @@ -256,7 +274,7 @@ def _impl(ctx): "msvc_env", "no_stripping", ], - tools = [tool(path = ctx.attr.msvc_link_path)], + tools = link_tools, ) cpp_link_dynamic_library_action = action_config( @@ -275,7 +293,7 @@ def _impl(ctx): "has_configured_linker_path", "def_file", ], - tools = [tool(path = ctx.attr.msvc_link_path)], + tools = link_tools, ) deps_scanner = "cpp-module-deps-scanner_not_found" @@ -1366,7 +1384,14 @@ def _impl(ctx): flag_sets = [ flag_set( actions = all_link_actions, - flag_groups = [flag_group(flags = ["/DEBUG"])], + # /DEBUG: emit the separate .pdb. + # /SAFESEH:NO: this feature routes links through lld-link (see + # link_tools), which — unlike VC9 link.exe — enforces /safeseh + # on x86 and rejects data-only objects that carry no SEH table + # (e.g. ICU's icudt49_dat.obj). SafeSEH is a ship-time mitigation + # and these are non-distributed debug binaries, so disabling it + # here is harmless and only affects the generate_pdb path. + flag_groups = [flag_group(flags = ["/DEBUG", "/SAFESEH:NO"])], ), ], ) @@ -1825,6 +1850,7 @@ This is only offered as a migration bridge for projects transitioning to rule-ba "msvc_lib_path": attr.string(default = "vc_installation_error.bat"), "msvc_link_path": attr.string(default = "vc_installation_error.bat"), "msvc_ml_path": attr.string(default = "vc_installation_error.bat"), + "pdb_link_path": attr.string(default = "vc_installation_error.bat"), "shorten_virtual_includes": attr.bool(default = False), "supports_parse_showincludes": attr.bool(), "target_libc": attr.string(), diff --git a/build/vs_config_repo.bzl b/build/vs_config_repo.bzl index e1f283af06..df0fb7dbc8 100644 --- a/build/vs_config_repo.bzl +++ b/build/vs_config_repo.bzl @@ -7,9 +7,19 @@ flag value so bazelrc doesn't strip backslashes): build --repo_env="SDK_PATH=C:/Program Files/Microsoft SDKs/Windows/v7.0" build --repo_env="MSVC_TMP=C:/Users/<you>/AppData/Local/Temp" build --repo_env="PERL_PATH=C:/msys64/usr/bin/perl.exe" + build --repo_env="VS_MODERN_PATH=C:/Program Files (x86)/Microsoft Visual Studio/2019/BuildTools" The generated @vs_config//:paths.bzl exposes VS, VC, SDK, MSVC_TMP, -PERL, DEBUG_CRT_DIR, and LINK_WRAPPER. +PERL, DEBUG_CRT_DIR, LINK_WRAPPER, and PDB_LINK. + +PDB_LINK is a modern MSVC link.exe (auto-discovered under VS_MODERN_PATH). +The C++ toolchain routes generate_pdb (debug .pdb) links to it instead of +VC9 link.exe: the VC9 linker's single mspdbsrv cannot write PDBs in parallel +(LNK1318 0x6BA, no /FS), while a modern link.exe reads the VC9 /Z7 objects +natively and its mspdbsrv handles parallel PDB writes — so debug builds run +at full --jobs. (LLVM lld-link was evaluated and rejected: it cannot write +PDBs from VC9-era CodeView — "Stream too short".) Non-PDB links stay on VC9 +link.exe via LINK_WRAPPER, below, so shipped binaries are unchanged. LINK_WRAPPER is a generated batch file that wraps link.exe and gives every link action its own _MSPDBSRV_ENDPOINT_ (derived from the unique per-target @@ -19,28 +29,70 @@ LNK1318 RPC_S_SERVER_UNAVAILABLE (0x6BA), which is the only reason debug builds ever needed --jobs=1. Routing all links through the wrapper lets debug builds run fully parallel. VS2008/VC9 has no /FS flag (added in VS2013), so the per-process endpoint is the only fix available here. + +Endpoint isolation prevents shared-server *corruption*, but each isolated link +must still spawn its own mspdbsrv.exe, and a wide parallel link phase can still +transiently lose the RPC handshake (LNK1318 0x6BA) when many servers start at +once. So the wrapper also retries link.exe on LNK1318 with a linear backoff, +handing each attempt a *fresh* endpoint (…_a1, _a2, …) so a retry never +reconnects to a half-dead server from the previous try. Non-PDB link errors +(unresolved externals, etc.) are detected and returned immediately without +wasting retries. Tunable via LINK_MAX_RETRY / LINK_RETRY_BASE_SEC env vars. """ _DEFAULT_VS = "C:\\Program Files (x86)\\Microsoft Visual Studio 9.0" _DEFAULT_SDK = "C:\\Program Files\\Microsoft SDKs\\Windows\\v7.0" _DEFAULT_TMP = "C:\\Temp" _DEFAULT_PERL = "C:\\msys64\\usr\\bin\\perl.exe" +# Modern MSVC (VS2017+/BuildTools) install root — only used to source a newer +# link.exe for the generate_pdb path (see _find_modern_link / PDB_LINK below). +_DEFAULT_VS_MODERN = "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools" + +def _find_modern_link(rctx, vs_modern): + """Locate the newest VS2017+ x86-target link.exe under a BuildTools/VS root. + + The VC9 link.exe cannot write PDBs in parallel (single mspdbsrv, no /FS, the + LNK1318 bug); LLVM lld-link cannot write PDBs from VC9-era CodeView at all + ("Stream too short"). A modern MSVC link.exe reads the VC9 /Z7 objects natively + and its mspdbsrv handles parallel PDB writes, so the generate_pdb link path + uses it. Returns a placeholder if none is found (only breaks generate_pdb builds). + """ + tools = rctx.path(vs_modern + "\\VC\\Tools\\MSVC") + if not tools.exists: + return "modern_link_not_found.bat" + best = None + for ver in tools.readdir(): + for host in ["Hostx64", "Hostx86"]: + link = ver.get_child("bin").get_child(host).get_child("x86").get_child("link.exe") + if link.exists: + if best == None or ver.basename > best[0]: + best = (ver.basename, str(link)) + break + if best == None: + return "modern_link_not_found.bat" + return best[1] def _vs_config_impl(rctx): vs = rctx.os.environ.get("VS_PATH", _DEFAULT_VS) sdk = rctx.os.environ.get("SDK_PATH", _DEFAULT_SDK) tmp = rctx.os.environ.get("MSVC_TMP", _DEFAULT_TMP) perl = rctx.os.environ.get("PERL_PATH", _DEFAULT_PERL) + vs_modern = rctx.os.environ.get("VS_MODERN_PATH", _DEFAULT_VS_MODERN) vc = vs + "\\VC" + pdb_link = _find_modern_link(rctx, vs_modern) debug_crt = vc + "\\redist\\Debug_NonRedist\\x86\\Microsoft.VC90.DebugCRT" def q(p): return p.replace("\\", "\\\\") # link.exe wrapper: per-action _MSPDBSRV_ENDPOINT_ so concurrent /DEBUG links - # each get a private mspdbsrv.exe instead of sharing one (LNK1318 0x6BA). + # each get a private mspdbsrv.exe instead of sharing one (LNK1318 0x6BA), plus + # a bounded retry-with-backoff on LNK1318 (fresh endpoint per attempt) for the + # residual "server unavailable" that isolation alone can't prevent. # The endpoint is derived from the linker @param-file name (unique per target, # always on the command line); falls back to %RANDOM% if no @file is present. + # Output is captured so we can tell a transient PDB failure (retry) from a real + # link error (return immediately). Dynamic wait grows linearly per attempt. link_exe = vc + "\\bin\\link.exe" bat = "\r\n".join([ "@echo off", @@ -56,9 +108,32 @@ def _vs_config_impl(rctx): "set \"ep=!ep:/=_!\"", "set \"ep=!ep:.=_!\"", "set \"ep=!ep::=_!\"", - "set \"_MSPDBSRV_ENDPOINT_=bzl_!ep!\"", - "\"" + link_exe + "\" %*", - "exit /b !ERRORLEVEL!", + # Tunables (override via --action_env at build time), with sane defaults. + "if not defined LINK_MAX_RETRY set \"LINK_MAX_RETRY=5\"", + "if not defined LINK_RETRY_BASE_SEC set \"LINK_RETRY_BASE_SEC=2\"", + "set \"log=%TEMP%\\bzl_link_%RANDOM%%RANDOM%.log\"", + "set \"n=0\"", + ":retry", + "set /a n+=1", + # Fresh endpoint each attempt so a retry spawns a brand-new mspdbsrv.exe + # instead of reconnecting to a hung/half-dead one from the previous try. + "set \"_MSPDBSRV_ENDPOINT_=bzl_!ep!_a!n!\"", + "\"" + link_exe + "\" %* > \"!log!\" 2>&1", + "set \"rc=!ERRORLEVEL!\"", + "if !rc! equ 0 goto done", + # Only LNK1318 (PDB RPC) is transient; anything else is a real failure. + "findstr /c:\"LNK1318\" \"!log!\" >nul", + "if errorlevel 1 goto done", + "if !n! geq !LINK_MAX_RETRY! goto done", + "set /a wait=!n!*!LINK_RETRY_BASE_SEC!", + "set /a pings=!wait!+1", + "echo [msvc_link] LNK1318 PDB RPC error; retry !n!/!LINK_MAX_RETRY! after !wait!s (endpoint bzl_!ep!_a!n!)>&2", + "ping -n !pings! 127.0.0.1 >nul", + "goto retry", + ":done", + "type \"!log!\"", + "del \"!log!\" >nul 2>&1", + "exit /b !rc!", "", ]) rctx.file("msvc_link.bat", bat) @@ -74,6 +149,7 @@ def _vs_config_impl(rctx): 'PERL = "{}"'.format(q(perl)), 'DEBUG_CRT_DIR = "{}"'.format(q(debug_crt)), 'LINK_WRAPPER = "{}"'.format(q(link_wrapper)), + 'PDB_LINK = "{}"'.format(q(pdb_link)), "", ]) rctx.file("paths.bzl", content) @@ -81,5 +157,5 @@ def _vs_config_impl(rctx): vs_config_repo = repository_rule( implementation = _vs_config_impl, - environ = ["VS_PATH", "SDK_PATH", "MSVC_TMP", "PERL_PATH"], + environ = ["VS_PATH", "SDK_PATH", "MSVC_TMP", "PERL_PATH", "VS_MODERN_PATH"], )
