Skip to content

Commit 5b2a22d

Browse files
committed
Swift support
1 parent a426081 commit 5b2a22d

File tree

1 file changed

+44
-11
lines changed

1 file changed

+44
-11
lines changed

refresh.template.py

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -553,11 +553,17 @@ def _get_headers(compile_action, source_path: str):
553553
def _get_files(compile_action):
554554
"""Gets the ({source files}, {header files}) clangd should be told the command applies to."""
555555

556+
# If we've got swift action just return sources
557+
if compile_action.mnemonic == 'SwiftCompile':
558+
source_files = set(filter(lambda arg: arg.endswith(_get_files.swift_source_extension), compile_action.arguments))
559+
return source_files, set()
560+
556561
# Getting the source file is a little trickier than it might seem.
557562

558563
# First, we do the obvious thing: Filter args to those that look like source files.
559-
source_file_candidates = [arg for arg in compile_action.arguments if not arg.startswith('-') and arg.endswith(_get_files.source_extensions)]
564+
source_file_candidates = [arg for arg in compile_action.arguments if not arg.startswith('-') and arg.endswith(_get_files.c_family_source_extensions)]
560565
assert source_file_candidates, f"No source files found in compile args: {compile_action.arguments}.\nPlease file an issue with this information!"
566+
561567
source_file = source_file_candidates[0]
562568

563569
# If we've got multiple candidates for source files, apply heuristics based on how Bazel tends to format commands.
@@ -584,7 +590,7 @@ def _get_files(compile_action):
584590
source_index = compile_action.arguments.index('/c') + 1
585591

586592
source_file = compile_action.arguments[source_index]
587-
assert source_file.endswith(_get_files.source_extensions), f"Source file candidate, {source_file}, seems to be wrong.\nSelected from {compile_action.arguments}.\nPlease file an issue with this information!"
593+
assert source_file.endswith(_get_files.c_family_source_extensions), f"Source file candidate, {source_file}, seems to be wrong.\nSelected from {compile_action.arguments}.\nPlease file an issue with this information!"
588594

589595
# Warn gently about missing files
590596
if not os.path.isfile(source_file):
@@ -640,7 +646,8 @@ def _get_files(compile_action):
640646
_get_files.openclxx_source_extensions = ('.clcpp',)
641647
_get_files.assembly_source_extensions = ('.s', '.asm')
642648
_get_files.assembly_needing_c_preprocessor_source_extensions = ('.S',)
643-
_get_files.source_extensions = _get_files.c_source_extensions + _get_files.cpp_source_extensions + _get_files.objc_source_extensions + _get_files.objcpp_source_extensions + _get_files.cuda_source_extensions + _get_files.opencl_source_extensions + _get_files.openclxx_source_extensions + _get_files.assembly_source_extensions + _get_files.assembly_needing_c_preprocessor_source_extensions
649+
_get_files.swift_source_extension = '.swift'
650+
_get_files.c_family_source_extensions = _get_files.c_source_extensions + _get_files.cpp_source_extensions + _get_files.objc_source_extensions + _get_files.objcpp_source_extensions + _get_files.cuda_source_extensions + _get_files.opencl_source_extensions + _get_files.openclxx_source_extensions + _get_files.assembly_source_extensions + _get_files.assembly_needing_c_preprocessor_source_extensions
644651
_get_files.extensions_to_language_args = { # Note that clangd fails on the --language or -ObjC or -ObjC++ forms. See https://github.com/clangd/clangd/issues/1173#issuecomment-1226847416
645652
_get_files.c_source_extensions: '-xc',
646653
_get_files.cpp_source_extensions: '-xc++',
@@ -673,17 +680,25 @@ def _get_apple_SDKROOT(SDK_name: str):
673680
# Traditionally stored in SDKROOT environment variable, but not provided by Bazel. See https://github.com/bazelbuild/bazel/issues/12852
674681

675682

676-
def _get_apple_platform(compile_args: typing.List[str]):
683+
def _get_apple_platform(compile_action):
677684
"""Figure out which Apple platform a command is for.
678685
679686
Is the name used by Xcode in the SDK files, not the marketing name.
680687
e.g. iPhoneOS, not iOS.
681688
"""
682689
# A bit gross, but Bazel specifies the platform name in one of the include paths, so we mine it from there.
690+
compile_args = compile_action.arguments
683691
for arg in compile_args:
684692
match = re.search('/Platforms/([a-zA-Z]+).platform/Developer/', arg)
685693
if match:
686694
return match.group(1)
695+
if getattr(compile_action, 'environmentVariables', None):
696+
match = next(
697+
filter(lambda x: x.key == "APPLE_SDK_PLATFORM", compile_action.environmentVariables),
698+
None
699+
)
700+
if match:
701+
return match.value
687702
return None
688703

689704

@@ -695,18 +710,21 @@ def _get_apple_DEVELOPER_DIR():
695710
# Traditionally stored in DEVELOPER_DIR environment variable, but not provided by Bazel. See https://github.com/bazelbuild/bazel/issues/12852
696711

697712

698-
def _apple_platform_patch(compile_args: typing.List[str]):
713+
def _apple_platform_patch(compile_action):
699714
"""De-Bazel the command into something clangd can parse.
700715
701716
This function has fixes specific to Apple platforms, but you should call it on all platforms. It'll determine whether the fixes should be applied or not.
702717
"""
703718
# Bazel internal environment variable fragment that distinguishes Apple platforms that need unwrapping.
704719
# Note that this occurs in the Xcode-installed wrapper, but not the CommandLineTools wrapper, which works fine as is.
720+
compile_args = compile_action.arguments
705721
if any('__BAZEL_XCODE_' in arg for arg in compile_args):
706722
# Undo Bazel's Apple platform compiler wrapping.
707723
# Bazel wraps the compiler as `external/local_config_cc/wrapped_clang` and exports that wrapped compiler in the proto. However, we need a clang call that clangd can introspect. (See notes in "how clangd uses compile_commands.json" in ImplementationReadme.md for more.)
708724
# Removing the wrapper is also important because Bazel's Xcode (but not CommandLineTools) wrapper crashes if you don't specify particular environment variables (replaced below). We'd need the wrapper to be invokable by clangd's --query-driver if we didn't remove the wrapper.
709-
compile_args[0] = 'clang'
725+
726+
if compile_action.mnemonic != 'SwiftCompile':
727+
compile_args[0] = 'clang'
710728

711729
# We have to manually substitute out Bazel's macros so clang can parse the command
712730
# Code this mirrors is in https://github.com/bazelbuild/bazel/blob/master/tools/osx/crosstool/wrapped_clang.cc
@@ -715,13 +733,27 @@ def _apple_platform_patch(compile_args: typing.List[str]):
715733
# We also have to manually figure out the values of SDKROOT and DEVELOPER_DIR, since they're missing from the environment variables Bazel provides.
716734
# Filed Bazel issue about the missing environment variables: https://github.com/bazelbuild/bazel/issues/12852
717735
compile_args = [arg.replace('__BAZEL_XCODE_DEVELOPER_DIR__', _get_apple_DEVELOPER_DIR()) for arg in compile_args]
718-
apple_platform = _get_apple_platform(compile_args)
736+
apple_platform = _get_apple_platform(compile_action)
719737
assert apple_platform, f"Apple platform not detected in CMD: {compile_args}"
720738
compile_args = [arg.replace('__BAZEL_XCODE_SDKROOT__', _get_apple_SDKROOT(apple_platform)) for arg in compile_args]
721739

722740
return compile_args
723741

724742

743+
def _swift_patch(compile_args: typing.List[str]):
744+
745+
# rules_swift add a worker for wrapping if enable --persistent_worker flag (https://bazel.build/remote/persistent)
746+
# https://github.com/bazelbuild/rules_swift/blob/master/swift/internal/actions.bzl#L236
747+
# We need to remove it (build_bazel_rules_swift/tools/worker/worker)
748+
while len(compile_args) > 0 and (not 'swiftc' in compile_args[0]):
749+
compile_args.pop(0)
750+
compile_args[0] = 'swiftc'
751+
752+
# Remove -Xwrapped-swift introduced by rules_swift
753+
compile_args = [arg for arg in compile_args if not arg.startswith('-Xwrapped-swift')]
754+
755+
return compile_args
756+
725757
def _all_platform_patch(compile_args: typing.List[str]):
726758
"""Apply de-Bazeling fixes to the compile command that are shared across target platforms."""
727759
# clangd writes module cache files to the wrong place
@@ -758,14 +790,15 @@ def _all_platform_patch(compile_args: typing.List[str]):
758790
return compile_args
759791

760792

761-
def _get_cpp_command_for_files(compile_action):
793+
def _get_command_for_files(compile_action):
762794
"""Reformat compile_action into a compile command clangd can understand.
763795
764796
Undo Bazel-isms and figures out which files clangd should apply the command to.
765797
"""
766798
# Patch command by platform
767799
compile_action.arguments = _all_platform_patch(compile_action.arguments)
768-
compile_action.arguments = _apple_platform_patch(compile_action.arguments)
800+
compile_action.arguments = _apple_platform_patch(compile_action)
801+
compile_action.arguments = _swift_patch(compile_action.arguments)
769802
# Android and Linux and grailbio LLVM toolchains: Fine as is; no special patching needed.
770803

771804
source_files, header_files = _get_files(compile_action)
@@ -803,7 +836,7 @@ def _amend_action_as_external(action):
803836
with concurrent.futures.ThreadPoolExecutor(
804837
max_workers=min(32, (os.cpu_count() or 1) + 4) # Backport. Default in MIN_PY=3.8. See "using very large resources implicitly on many-core machines" in https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.ThreadPoolExecutor
805838
) as threadpool:
806-
outputs = threadpool.map(_get_cpp_command_for_files, aquery_output.actions)
839+
outputs = threadpool.map(_get_command_for_files, aquery_output.actions)
807840

808841
# Yield as compile_commands.json entries
809842
header_files_already_written = set()
@@ -863,7 +896,7 @@ def _get_commands(target: str, flags: str):
863896
# Aquery docs if you need em: https://docs.bazel.build/versions/master/aquery.html
864897
# Aquery output proto reference: https://github.com/bazelbuild/bazel/blob/master/src/main/protobuf/analysis_v2.proto
865898
# One bummer, not described in the docs, is that aquery filters over *all* actions for a given target, rather than just those that would be run by a build to produce a given output. This mostly isn't a problem, but can sometimes surface extra, unnecessary, misconfigured actions. Chris has emailed the authors to discuss and filed an issue so anyone reading this could track it: https://github.com/bazelbuild/bazel/issues/14156.
866-
f"mnemonic('(Objc|Cpp)Compile',{target_statment})",
899+
f"mnemonic('(Objc|Cpp|Swift)Compile',{target_statment})",
867900
# We switched to jsonproto instead of proto because of https://github.com/bazelbuild/bazel/issues/13404. We could change back when fixed--reverting most of the commit that added this line and tweaking the build file to depend on the target in that issue. That said, it's kinda nice to be free of the dependency, unless (OPTIMNOTE) jsonproto becomes a performance bottleneck compated to binary protos.
868901
'--output=jsonproto',
869902
# We'll disable artifact output for efficiency, since it's large and we don't use them. Small win timewise, but dramatically less json output from aquery.

0 commit comments

Comments
 (0)