Skip to content

Commit 124f745

Browse files
allevatoswiple-rules-gardener
authored andcommitted
Create a new swift_common.compile API to replace the existing two compilation APIs (compile_as_library and compile_as_objects).
This is the first of a multi-CL process to get rid of those two functions and simplify the overall APIs. The major goals here are: * The APIs shouldn't return providers; rules should construct those from structured outputs. This creates a bit of duplication in the short-term, but there are other plans to reduce the providers currently being used (SwiftClangModuleInfo can go away, and ObjcProvider will go away with the Objective-C -> C++ sandwich migration.) * Linking of libraries should be a separate call; swift_common shouldn't be involved in that. * Make it easier to explore different compilation models, like separating object and module compilation. RELNOTES: Introduce `swift_common.compile`, which will eventually replace `swift_common.compile_as_{library,objects}`. PiperOrigin-RevId: 246203838
1 parent e780192 commit 124f745

File tree

6 files changed

+397
-91
lines changed

6 files changed

+397
-91
lines changed

swift/internal/api.bzl

Lines changed: 260 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ load(
3535
"run_toolchain_shell_action",
3636
"run_toolchain_swift_action",
3737
)
38-
load(":archiving.bzl", "register_static_archive_action")
3938
load(":attrs.bzl", "swift_common_rule_attrs")
4039
load(
4140
":compiling.bzl",
@@ -69,6 +68,7 @@ load(
6968
"SWIFT_FEATURE_USE_GLOBAL_MODULE_CACHE",
7069
"SWIFT_FEATURE_USE_RESPONSE_FILES",
7170
)
71+
load(":linking.bzl", "register_libraries_to_link")
7272
load(
7373
":providers.bzl",
7474
"SwiftClangModuleInfo",
@@ -413,6 +413,254 @@ def _cc_feature_configuration(feature_configuration):
413413
"""
414414
return feature_configuration.cc_feature_configuration
415415

416+
def _compile(
417+
actions,
418+
feature_configuration,
419+
module_name,
420+
srcs,
421+
swift_toolchain,
422+
target_name,
423+
additional_inputs = [],
424+
bin_dir = None,
425+
copts = [],
426+
defines = [],
427+
deps = [],
428+
genfiles_dir = None):
429+
"""Compiles a Swift module.
430+
431+
Args:
432+
actions: The context's `actions` object.
433+
feature_configuration: A feature configuration obtained from
434+
`swift_common.configure_features`.
435+
module_name: The name of the Swift module being compiled. This must be present and valid;
436+
use `swift_common.derive_module_name` to generate a default from the target's label if
437+
needed.
438+
srcs: The Swift source files to compile.
439+
swift_toolchain: The `SwiftToolchainInfo` provider of the toolchain.
440+
target_name: The name of the target for which the code is being compiled, which is used to
441+
determine unique file paths for the outputs.
442+
additional_inputs: A list of `File`s representing additional input files that need to be
443+
passed to the Swift compile action because they are referenced by compiler flags.
444+
bin_dir: The Bazel `*-bin` directory root. If provided, its path is used to store the cache
445+
for modules precompiled by Swift's ClangImporter.
446+
copts: A list of compiler flags that apply to the target being built. These flags, along
447+
with those from Bazel's Swift configuration fragment (i.e., `--swiftcopt` command line
448+
flags) are scanned to determine whether whole module optimization is being requested,
449+
which affects the nature of the output files.
450+
defines: Symbols that should be defined by passing `-D` to the compiler.
451+
deps: Dependencies of the target being compiled. These targets must propagate one of the
452+
following providers: `CcInfo`, `SwiftClangModuleInfo`, `SwiftInfo`, or
453+
`apple_common.Objc`.
454+
genfiles_dir: The Bazel `*-genfiles` directory root. If provided, its path is added to
455+
ClangImporter's header search paths for compatibility with Bazel's C++ and Objective-C
456+
rules which support inclusions of generated headers from that location.
457+
458+
Returns:
459+
A `struct` containing the following fields:
460+
461+
* `generated_header`: A `File` representing the Objective-C header that was generated for
462+
the compiled module. If no header was generated, this field will be None.
463+
* `generated_header_module_map`: A `File` representing the module map that was generated
464+
to correspond to the generated Objective-C header. If no module map was generated, this
465+
field will be None.
466+
* `indexstore`: A `File` representing the directory that contains the index store data
467+
generated by the compiler if index-while-building is enabled. May be None if no
468+
indexing was requested.
469+
* `linker_flags`: A list of strings representing additional flags that should be passed
470+
to the linker when linking these objects into a binary. If there are none, this field
471+
will always be an empty list, never None.
472+
* `linker_inputs`: A list of `File`s representing additional input files (such as those
473+
referenced in `linker_flags`) that need to be available to the link action when linking
474+
these objects into a binary. If there are none, this field will always be an empty
475+
list, never None.
476+
* `object_files`: A list of `.o` files that were produced by the compiler.
477+
* `swiftdoc`: The `.swiftdoc` file that was produced by the compiler.
478+
* `swiftmodule`: The `.swiftmodule` file that was produced by the compiler.
479+
"""
480+
481+
# Force threaded mode for WMO builds, using the same number of cores that is
482+
# on a Mac Pro for historical reasons.
483+
# TODO(b/32571265): Generalize this based on platform and core count when an
484+
# API to obtain this is available.
485+
if _is_wmo(copts + swift_toolchain.command_line_copts, feature_configuration):
486+
# We intentionally don't use `+=` or `extend` here to ensure that a
487+
# copy is made instead of extending the original.
488+
copts = copts + ["-num-threads", "12"]
489+
490+
compile_reqs = declare_compile_outputs(
491+
actions = actions,
492+
copts = copts + swift_toolchain.command_line_copts,
493+
srcs = srcs,
494+
target_name = target_name,
495+
index_while_building = swift_common.is_enabled(
496+
feature_configuration = feature_configuration,
497+
feature_name = SWIFT_FEATURE_INDEX_WHILE_BUILDING,
498+
),
499+
)
500+
output_objects = compile_reqs.output_objects
501+
502+
swiftmodule = derived_files.swiftmodule(actions, module_name = module_name)
503+
swiftdoc = derived_files.swiftdoc(actions, module_name = module_name)
504+
505+
wrapper_args = actions.args()
506+
if _is_enabled(
507+
feature_configuration = feature_configuration,
508+
feature_name = SWIFT_FEATURE_USE_GLOBAL_MODULE_CACHE,
509+
):
510+
# If bin_dir is not provided, then we don't pass any special flags to the compiler,
511+
# letting it decide where the cache should live. This is usually somewhere in the system
512+
# temporary directory.
513+
if bin_dir:
514+
wrapper_args.add("-module-cache-path", _global_module_cache_path(bin_dir))
515+
else:
516+
wrapper_args.add("-Xwrapped-swift=-ephemeral-module-cache")
517+
518+
if _is_enabled(
519+
feature_configuration = feature_configuration,
520+
feature_name = SWIFT_FEATURE_DEBUG_PREFIX_MAP,
521+
):
522+
wrapper_args.add("-Xwrapped-swift=-debug-prefix-pwd-is-dot")
523+
524+
compile_args = actions.args()
525+
if _is_enabled(
526+
feature_configuration = feature_configuration,
527+
feature_name = SWIFT_FEATURE_USE_RESPONSE_FILES,
528+
):
529+
compile_args.use_param_file("@%s", use_always = True)
530+
531+
compile_args.add("-emit-object")
532+
compile_args.add_all(compile_reqs.args)
533+
compile_args.add("-emit-module-path")
534+
compile_args.add(swiftmodule)
535+
536+
# Add any command line arguments that do *not* have to do with emitting outputs.
537+
basic_inputs = _swiftc_command_line_and_inputs(
538+
# TODO(allevato): Make this argument a list of files instead.
539+
additional_input_depsets = [depset(additional_inputs)],
540+
args = compile_args,
541+
copts = copts,
542+
defines = defines,
543+
deps = deps,
544+
feature_configuration = feature_configuration,
545+
genfiles_dir = genfiles_dir,
546+
module_name = module_name,
547+
srcs = srcs,
548+
toolchain = swift_toolchain,
549+
)
550+
551+
additional_outputs = []
552+
553+
# If the toolchain supports Objective-C interop, generate a Swift header for this library so
554+
# that it can be included by Objective-C code that depends on it.
555+
generates_header = not _is_enabled(
556+
feature_configuration = feature_configuration,
557+
feature_name = SWIFT_FEATURE_NO_GENERATED_HEADER,
558+
)
559+
if generates_header and swift_toolchain.supports_objc_interop:
560+
generated_header = derived_files.objc_header(actions = actions, target_name = target_name)
561+
compile_args.add("-emit-objc-header-path")
562+
compile_args.add(generated_header)
563+
additional_outputs.append(generated_header)
564+
565+
# Create a module map for the generated header file. This ensures that inclusions of it are
566+
# treated modularly, not textually.
567+
#
568+
# Caveat: Generated module maps are incompatible with the hack that some folks are using to
569+
# support mixed Objective-C and Swift modules. This trap door lets them escape the module
570+
# redefinition error, with the caveat that certain import scenarios could lead to incorrect
571+
# behavior because a header can be imported textually instead of modularly.
572+
if not _is_enabled(
573+
feature_configuration = feature_configuration,
574+
feature_name = SWIFT_FEATURE_NO_GENERATED_MODULE_MAP,
575+
):
576+
generated_module_map = derived_files.module_map(
577+
actions = actions,
578+
target_name = target_name,
579+
)
580+
write_objc_header_module_map(
581+
actions = actions,
582+
module_name = module_name,
583+
objc_header = generated_header,
584+
output = generated_module_map,
585+
)
586+
else:
587+
generated_module_map = None
588+
else:
589+
generated_header = None
590+
generated_module_map = None
591+
592+
all_inputs = depset(
593+
transitive = [basic_inputs, depset(direct = compile_reqs.compile_inputs)],
594+
)
595+
compile_outputs = ([swiftmodule, swiftdoc] + output_objects +
596+
compile_reqs.other_outputs) + additional_outputs
597+
598+
if swift_toolchain.swift_worker:
599+
execution_requirements = {"supports-workers": "1"}
600+
tools = [swift_toolchain.swift_worker]
601+
else:
602+
execution_requirements = {}
603+
tools = []
604+
605+
run_toolchain_swift_action(
606+
actions = actions,
607+
arguments = [wrapper_args, compile_args],
608+
execution_requirements = execution_requirements,
609+
inputs = all_inputs,
610+
mnemonic = "SwiftCompile",
611+
outputs = compile_outputs,
612+
progress_message = "Compiling Swift module {}".format(module_name),
613+
swift_tool = "swiftc",
614+
toolchain = swift_toolchain,
615+
tools = tools,
616+
)
617+
618+
linker_flags = []
619+
linker_inputs = []
620+
621+
# Ensure that the .swiftmodule file is embedded in the final library or binary
622+
# for debugging purposes.
623+
if _is_debugging(feature_configuration = feature_configuration):
624+
module_embed_results = ensure_swiftmodule_is_embedded(
625+
actions = actions,
626+
swiftmodule = swiftmodule,
627+
target_name = target_name,
628+
toolchain = swift_toolchain,
629+
)
630+
linker_flags.extend(module_embed_results.linker_flags)
631+
linker_inputs.extend(module_embed_results.linker_inputs)
632+
output_objects.extend(module_embed_results.objects_to_link)
633+
634+
# Invoke an autolink-extract action for toolchains that require it.
635+
if swift_common.is_enabled(
636+
feature_configuration = feature_configuration,
637+
feature_name = SWIFT_FEATURE_AUTOLINK_EXTRACT,
638+
):
639+
autolink_file = derived_files.autolink_flags(
640+
actions,
641+
target_name = target_name,
642+
)
643+
register_autolink_extract_action(
644+
actions = actions,
645+
module_name = module_name,
646+
objects = output_objects,
647+
output = autolink_file,
648+
toolchain = swift_toolchain,
649+
)
650+
linker_flags.append("@{}".format(autolink_file.path))
651+
linker_inputs.append(autolink_file)
652+
653+
return struct(
654+
generated_header = generated_header,
655+
generated_module_map = generated_module_map,
656+
indexstore = compile_reqs.indexstore,
657+
linker_flags = linker_flags,
658+
linker_inputs = linker_inputs,
659+
object_files = output_objects,
660+
swiftdoc = swiftdoc,
661+
swiftmodule = swiftmodule,
662+
)
663+
416664
def _compile_as_objects(
417665
actions,
418666
arguments,
@@ -430,6 +678,8 @@ def _compile_as_objects(
430678
genfiles_dir = None):
431679
"""Compiles Swift source files into object files (and optionally a module).
432680
681+
NOTE: This API is deprecated. Use `swift_common.compile` instead.
682+
433683
Args:
434684
actions: The context's `actions` object.
435685
arguments: A list of `Args` objects that provide additional arguments to the
@@ -641,6 +891,8 @@ def _compile_as_library(
641891
linkopts = []):
642892
"""Compiles Swift source files into static and/or shared libraries.
643893
894+
NOTE: This API is deprecated. Use `swift_common.compile` instead.
895+
644896
This is a high-level API that wraps the compilation and library creation steps
645897
based on the provided input arguments, and is likely suitable for most common
646898
purposes.
@@ -713,11 +965,6 @@ def _compile_as_library(
713965

714966
if not library_name:
715967
library_name = label.name
716-
out_archive = derived_files.static_archive(
717-
actions,
718-
alwayslink = alwayslink,
719-
link_name = library_name,
720-
)
721968

722969
# Register the compilation actions to get an object file (.o) for the Swift
723970
# code, along with its swiftmodule and swiftdoc.
@@ -800,17 +1047,19 @@ def _compile_as_library(
8001047
)
8011048

8021049
# Create an archive that contains the compiled .o files.
803-
register_static_archive_action(
1050+
library_to_link = register_libraries_to_link(
8041051
actions = actions,
1052+
alwayslink = alwayslink,
8051053
cc_feature_configuration = _cc_feature_configuration(
8061054
feature_configuration = feature_configuration,
8071055
),
808-
mnemonic = "SwiftArchive",
1056+
is_dynamic = False,
1057+
is_static = True,
1058+
library_name = library_name,
8091059
objects = compile_results.output_objects,
810-
output = out_archive,
811-
progress_message = "Linking {}".format(out_archive.short_path),
8121060
swift_toolchain = toolchain,
8131061
)
1062+
out_archive = library_to_link.pic_static_library
8141063

8151064
# TODO(b/130741225): Move this logic out of the API and have the rules themselves manipulate
8161065
# providers.
@@ -1265,6 +1514,7 @@ def _toolchain_attrs(toolchain_attr_name = "_toolchain"):
12651514
swift_common = struct(
12661515
cc_feature_configuration = _cc_feature_configuration,
12671516
compilation_attrs = _compilation_attrs,
1517+
compile = _compile,
12681518
compile_as_library = _compile_as_library,
12691519
compile_as_objects = _compile_as_objects,
12701520
configure_features = _configure_features,

0 commit comments

Comments
 (0)