Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Bazel 5 #156

Merged
merged 4 commits into from
Jan 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 59 additions & 2 deletions refresh.template.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,35 @@ def _print_header_finding_warning_once():
_print_header_finding_warning_once.has_logged = False


@functools.lru_cache
def _get_bazel_version():
"""Gets the Bazel version as a tuple of (major, minor, patch).

The rolling release and the release candidate are treated as the LTS release.
E.g. both 7.0.0-pre.XXXXXXXX.X and 7.0.0rc1 are treated as 7.0.0.
If the version can't be determined, returns (0, 0, 0).
"""
bazel_version_process = subprocess.run(
['bazel', 'version'],
capture_output=True,
encoding=locale.getpreferredencoding(),
check=True, # Should always succeed.
)

version = ''
for line in bazel_version_process.stdout.splitlines():
line = line.strip()
if line.startswith('Build label: '):
version = line[len('Build label: '):]

match = re.search(r'^(\d+)\.(\d+)\.(\d+)', version)
if not match:
log_warning(f">>> Failed to get Bazel version.\nPlease file an issue with the following log:\n", bazel_version_process.stdout)
return (0, 0, 0)

return tuple(int(match.group(i)) for i in range(1, 4))


@functools.lru_cache
def _get_bazel_cached_action_keys():
"""Gets the set of actionKeys cached in bazel-out."""
Expand Down Expand Up @@ -727,6 +756,19 @@ def _apple_platform_patch(compile_args: list[str]):
return compile_args


def _get_sysroot(args: typing.List[str]):
"""Get path to sysroot from command line arguments."""
for idx, arg in enumerate(args):
if arg == '--sysroot' or arg == '-isysroot':
if idx + 1 < len(args):
return pathlib.PurePath(args[idx + 1])
elif arg.startswith('--sysroot='):
return pathlib.PurePath(arg[len('--sysroot='):])
elif arg.startswith('-isysroot'):
return pathlib.PurePath(arg[len('-isysroot'):])
return None


def _emscripten_platform_patch(compile_action):
"""De-Bazel the command into something clangd can parse.

Expand All @@ -737,13 +779,24 @@ def _emscripten_platform_patch(compile_action):
return compile_action.arguments

workspace_absolute = pathlib.PurePath(os.environ["BUILD_WORKSPACE_DIRECTORY"])
sysroot = _get_sysroot(compile_action.arguments)
assert sysroot, f'Emscripten sysroot not detected in CMD: {compile_action.arguments}'

def get_workspace_root(path_from_execroot: pathlib.PurePath):
if path_from_execroot.parts[0] != 'external':
return pathlib.PurePath('.')
return pathlib.PurePath('external') / path_from_execroot.parts[1]

environment = compile_action.environmentVariables.copy()
environment['EXT_BUILD_ROOT'] = str(workspace_absolute)
environment['EMCC_SKIP_SANITY_CHECK'] = '1'
environment['EM_COMPILER_WRAPPER'] = str(pathlib.PurePath({print_args_executable}))
if 'PATH' not in environment:
environment['PATH'] = os.environ['PATH']
if 'EM_BIN_PATH' not in environment:
environment['EM_BIN_PATH'] = str(get_workspace_root(sysroot))
if 'EM_CONFIG_PATH' not in environment:
environment['EM_CONFIG_PATH'] = str(get_workspace_root(emcc_driver) / 'emscripten_toolchain' / 'emscripten_config')

# We run the emcc process with the environment variable EM_COMPILER_WRAPPER to intercept the command line arguments passed to `clang`.
emcc_process = subprocess.run(
Expand Down Expand Up @@ -1143,13 +1196,17 @@ def _get_commands(target: str, flags: str):
# That's all well and good, but param files would prevent us from seeing compile actions before the param files had been generated by compilation.
# Since clangd has no such length limit, we'll disable param files for our aquery run.
'--features=-compiler_param_file',
'--host_features=-compiler_param_file',
# Disable layering_check during, because it causes large-scale dependence on generated module map files that prevent header extraction before their generation
# For more context, see https://github.com/hedronvision/bazel-compile-commands-extractor/issues/83
# If https://github.com/clangd/clangd/issues/123 is resolved and we're not doing header extraction, we could try removing this, checking that there aren't erroneous red squigglies squigglies before the module maps are generated.
# If Bazel starts supporting modules (https://github.com/bazelbuild/bazel/issues/4005), we'll probably need to make changes that subsume this.
'--features=-layering_check',
] + additional_flags
]

if _get_bazel_version() >= (6, 1, 0):
aquery_args += ['--host_features=-compiler_param_file']

aquery_args += additional_flags

aquery_process = subprocess.run(
aquery_args,
Expand Down
2 changes: 1 addition & 1 deletion refresh_compile_commands.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def refresh_compile_commands(

# Make any package-relative labels absolute
targets = {
target if target.startswith("/") or target.startswith("@") else "@{}//{}:{}".format(native.repository_name(), native.package_name(), target.removeprefix(":")): flags for target, flags in targets.items()
target if target.startswith("/") or target.startswith("@") else "{}//{}:{}".format(native.repository_name(), native.package_name(), target.removeprefix(":")): flags for target, flags in targets.items()
}

# Generate the core, runnable python script from refresh.template.py
Expand Down