|
52 | 52 | # workspace builds, there is never a @@ in labels. |
53 | 53 | BZLMOD_ENABLED = "@@" in str(Label("//:unused")) |
54 | 54 |
|
| 55 | +def _detect_gcc_cxx_headers(rctx, sysroot_path, target_system_name): |
| 56 | + """Detect GCC C++ header directories in a sysroot. |
| 57 | +
|
| 58 | + When using libstdc++ with a sysroot, clang needs to know where the GCC C++ |
| 59 | + headers are located. This function auto-detects these paths by scanning |
| 60 | + the sysroot for installed GCC versions. |
| 61 | +
|
| 62 | + Args: |
| 63 | + rctx: Repository context. |
| 64 | + sysroot_path: Path to the sysroot (absolute or relative). |
| 65 | + target_system_name: Target triple (e.g., x86_64-unknown-linux-gnu). |
| 66 | +
|
| 67 | + Returns: |
| 68 | + List of C++ include directory paths relative to the sysroot root. |
| 69 | + """ |
| 70 | + include_dirs = [] |
| 71 | + |
| 72 | + # For non-absolute paths (Bazel labels), we can't inspect the filesystem |
| 73 | + # during repository rule execution. Return empty and let users specify |
| 74 | + # additional include dirs manually via cxx_builtin_include_directories. |
| 75 | + if not _is_absolute_path(sysroot_path): |
| 76 | + return include_dirs |
| 77 | + |
| 78 | + # Extract the GNU target triple from target_system_name |
| 79 | + # e.g., "x86_64-unknown-linux-gnu" -> "x86_64-linux-gnu" |
| 80 | + parts = target_system_name.split("-") |
| 81 | + if len(parts) >= 3: |
| 82 | + # Common GNU triple format: arch-linux-gnu or arch-linux-gnueabihf |
| 83 | + gnu_triple = parts[0] + "-linux-" + parts[-1] |
| 84 | + else: |
| 85 | + gnu_triple = target_system_name |
| 86 | + |
| 87 | + # Check for GCC C++ headers in common locations |
| 88 | + # Modern distros (Debian 10+, Ubuntu 18.04+): /usr/include/c++/<version> |
| 89 | + cxx_include_base = sysroot_path + "/usr/include/c++" |
| 90 | + if rctx.path(cxx_include_base).exists: |
| 91 | + # Find GCC version directories (e.g., "14", "13", "12") |
| 92 | + result = rctx.execute(["ls", cxx_include_base]) |
| 93 | + if result.return_code == 0: |
| 94 | + versions = [v.strip() for v in result.stdout.strip().split("\n") if v.strip()] |
| 95 | + for version in versions: |
| 96 | + # Add main C++ headers |
| 97 | + include_dirs.append("/usr/include/c++/" + version) |
| 98 | + # Add target-specific headers (for multi-arch) |
| 99 | + include_dirs.append("/usr/include/" + gnu_triple + "/c++/" + version) |
| 100 | + # Add backward compatibility headers |
| 101 | + include_dirs.append("/usr/include/c++/" + version + "/backward") |
| 102 | + |
| 103 | + # Also check traditional GCC installation path: /usr/lib/gcc/<triple>/<version>/... |
| 104 | + # This is the layout used by older distros and Chromium sysroots |
| 105 | + gcc_lib_base = sysroot_path + "/usr/lib/gcc/" + gnu_triple |
| 106 | + if rctx.path(gcc_lib_base).exists: |
| 107 | + result = rctx.execute(["ls", gcc_lib_base]) |
| 108 | + if result.return_code == 0: |
| 109 | + versions = [v.strip() for v in result.stdout.strip().split("\n") if v.strip()] |
| 110 | + for version in versions: |
| 111 | + # Traditional GCC include path structure uses relative paths from gcc lib dir |
| 112 | + # e.g., /usr/lib/gcc/x86_64-linux-gnu/6/../../../../include/c++/6 |
| 113 | + # which resolves to /usr/include/c++/6 |
| 114 | + base = "/usr/lib/gcc/" + gnu_triple + "/" + version |
| 115 | + include_dirs.append(base + "/../../../../include/c++/" + version) |
| 116 | + include_dirs.append(base + "/../../../../include/" + gnu_triple + "/c++/" + version) |
| 117 | + include_dirs.append(base + "/../../../../include/c++/" + version + "/backward") |
| 118 | + |
| 119 | + return include_dirs |
| 120 | + |
55 | 121 | def _empty_repository(rctx): |
56 | 122 | rctx.file("BUILD.bazel") |
57 | 123 | rctx.file("toolchains.bzl", """\ |
@@ -382,6 +448,17 @@ def _cc_toolchain_str( |
382 | 448 | _join(sysroot_prefix, "/usr/include"), |
383 | 449 | _join(sysroot_prefix, "/usr/local/include"), |
384 | 450 | ]) |
| 451 | + # Add GCC C++ headers from sysroot when using libstdc++. |
| 452 | + # These paths are needed because clang doesn't automatically add them |
| 453 | + # to the include search path when cross-compiling with a sysroot. |
| 454 | + # See https://github.com/bazel-contrib/toolchains_llvm/issues/533 |
| 455 | + stdlib = _dict_value(toolchain_info.stdlib_dict, target_pair, "builtin-libc++") |
| 456 | + if stdlib in ["stdc++", "dynamic-stdc++"] and sysroot_path: |
| 457 | + # Common GCC C++ header locations in modern distros (Debian/Ubuntu) |
| 458 | + # Pattern: /usr/include/c++/<version> and /usr/include/<triple>/c++/<version> |
| 459 | + gcc_cxx_include_dirs = _detect_gcc_cxx_headers(rctx, sysroot_path, target_system_name) |
| 460 | + for dir in gcc_cxx_include_dirs: |
| 461 | + cxx_builtin_include_directories.append(_join(sysroot_prefix, dir)) |
385 | 462 | elif target_os == "darwin": |
386 | 463 | cxx_builtin_include_directories.extend([ |
387 | 464 | _join(sysroot_prefix, "/usr/include"), |
|
0 commit comments