Skip to content

Commit 373701b

Browse files
committed
refresh.py: basic windows support
1 parent 1d21dc3 commit 373701b

File tree

1 file changed

+84
-13
lines changed

1 file changed

+84
-13
lines changed

refresh.template.py

Lines changed: 84 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import json
2424
import os
2525
import pathlib
26+
import platform
2627
import re
2728
import shlex
2829
import subprocess
@@ -41,19 +42,7 @@
4142
# Implementation: skip source files we've already seen in _get_files, shortcutting a bunch of slow preprocessor runs in _get_headers and output. We'd need a threadsafe set, or one set per thread, because header finding is already multithreaded for speed (same magnitudespeed win as single-threaded set).
4243
# Anticipated speedup: ~2x (30s to 15s.)
4344

44-
45-
def _get_headers(compile_args: typing.List[str], source_path_for_sanity_check: typing.Optional[str] = None):
46-
"""Gets the headers used by a particular compile command.
47-
48-
Relatively slow. Requires running the C preprocessor.
49-
"""
50-
# Hacky, but hopefully this is a temporary workaround for the clangd issue mentioned in the caller (https://github.com/clangd/clangd/issues/123)
51-
# Runs a modified version of the compile command to piggyback on the compiler's preprocessing and header searching.
52-
# Flags reference here: https://clang.llvm.org/docs/ClangCommandLineReference.html
53-
54-
# As an alternative approach, you might consider trying to get the headers by inspecing the Middlemen actions in the aquery output, but I don't see a way to get just the ones actually #included--or an easy way to get the system headers--without invoking the preprocessor's header search logic.
55-
# For more on this, see https://github.com/hedronvision/bazel-compile-commands-extractor/issues/5#issuecomment-1031148373
56-
45+
def _get_headers_gcc(compile_args: typing.List[str], source_path_for_sanity_check: typing.Optional[str] = None):
5746
# Strip out existing dependency file generation that could interfere with ours.
5847
# Clang on Apple doesn't let later flags override earlier ones, unfortunately.
5948
# These flags are prefixed with M for "make", because that's their output format.
@@ -94,6 +83,88 @@ def _get_headers(compile_args: typing.List[str], source_path_for_sanity_check: t
9483
return headers
9584

9685

86+
def _get_headers_msvc(compile_args: typing.List[str]):
87+
# We don't want to compile, but only pre-process and show all the included header files. We don't want to have the preprocessed results written to a file, so we get them printed to `stderr`.
88+
header_cmd = list(compile_args) + ["/showIncludes", "/EP"]
89+
90+
# cl.exe expects `INCLUDE` (and normally `LIB` but that is irrelevant here) to be set so that it can find the system headers. We have no idea what the correct paths are. So in case these variables are not set, we need to infer them somehow. These variables might not be set if the user has a custom `cc_toolchain()` configured with `bazel` and does not rely on having sourced vcvarsall.bat.
91+
environ = dict(os.environ)
92+
if "INCLUDE" not in environ:
93+
# We assume the base MSVC install based based on the `cl.exe` path in our command.
94+
base = pathlib.Path(header_cmd[0]).parent.parent.parent.parent
95+
# And we find the newest Windows 10 kits version that exists.
96+
windows_kits = sorted( pathlib.Path("C:\\Program Files (x86)\\Windows Kits\\10\\include").glob("*"))[-1]
97+
environ["INCLUDE"] = os.pathsep.join(
98+
(
99+
os.fspath(base / "ATLMFC/include"),
100+
os.fspath(base / "include"),
101+
os.fspath(windows_kits / "ucrt"),
102+
os.fspath(windows_kits / "shared"),
103+
os.fspath(windows_kits / "um"),
104+
os.fspath(windows_kits / "winrt"),
105+
os.fspath(windows_kits / "cppwinrt"),
106+
)
107+
)
108+
109+
header_search_process = subprocess.run(
110+
header_cmd,
111+
cwd=os.environ["BUILD_WORKSPACE_DIRECTORY"],
112+
stderr=subprocess.PIPE,
113+
stdout=subprocess.DEVNULL,
114+
env=environ,
115+
encoding="utf-8",
116+
check=False, # We explicitly ignore errors and carry on.
117+
)
118+
119+
headers = list()
120+
121+
# Based on the locale, `cl.exe` will emit different marker strings. See also https://github.com/ninja-build/ninja/issues/613#issuecomment-885185024 and https://github.com/bazelbuild/bazel/pull/7966.
122+
include_marker = (
123+
"注意: 包含檔案:", # Chinese - Taiwan
124+
"Poznámka: Včetně souboru:", # Czech
125+
"Hinweis: Einlesen der Datei:", # German - Germany
126+
"Note: including file:", # English - United States
127+
"Remarque : inclusion du fichier : ", # French - France
128+
"Nota: file incluso ", # Italian - Italy
129+
"メモ: インクルード ファイル: ", # Japanese
130+
"참고: 포함 파일:", # Korean
131+
"Uwaga: w tym pliku: ", # Polish
132+
"Observação: incluindo arquivo:", # Portuguese - Brazil
133+
"Примечание: включение файла: ", # Russian
134+
"Not: eklenen dosya: ", # Turkish
135+
"注意: 包含文件: ", # Chinese - People's Republic of China
136+
"Nota: inclusión del archivo:", # Spanish - Spain (Modern Sort)
137+
)
138+
139+
for line in header_search_process.stderr.splitlines():
140+
for marker in include_marker:
141+
if line.startswith(marker):
142+
headers.append(line[len(marker):].strip())
143+
break
144+
else:
145+
print(line, file=sys.stderr)
146+
147+
return headers
148+
149+
150+
def _get_headers(compile_args: typing.List[str], source_path_for_sanity_check: typing.Optional[str] = None):
151+
"""Gets the headers used by a particular compile command.
152+
153+
Relatively slow. Requires running the C preprocessor.
154+
"""
155+
# Hacky, but hopefully this is a temporary workaround for the clangd issue mentioned in the caller (https://github.com/clangd/clangd/issues/123)
156+
# Runs a modified version of the compile command to piggyback on the compiler's preprocessing and header searching.
157+
# Flags reference here: https://clang.llvm.org/docs/ClangCommandLineReference.html
158+
159+
# As an alternative approach, you might consider trying to get the headers by inspecing the Middlemen actions in the aquery output, but I don't see a way to get just the ones actually #included--or an easy way to get the system headers--without invoking the preprocessor's header search logic.
160+
# For more on this, see https://github.com/hedronvision/bazel-compile-commands-extractor/issues/5#issuecomment-1031148373
161+
162+
# Lets hope this catches cl.exe from the MSVC toolchain and also a clang cl.exe.
163+
if compile_args[0].endswith("cl.exe"):
164+
return _get_headers_msvc(compile_args)
165+
return _get_headers_gcc(compile_args, source_path_for_sanity_check)
166+
167+
97168
def _get_files(compile_args: typing.List[str]):
98169
"""Gets the ([source files], [header files]) clangd should be told the command applies to."""
99170
source_files = [arg for arg in compile_args if arg.endswith(_get_files.source_extensions)]

0 commit comments

Comments
 (0)