Skip to content

Commit

Permalink
Add support for symbol graph extraction.
Browse files Browse the repository at this point in the history
This change adds the following APIs:

*   An aspect, `swift_symbol_graph_aspect`, which can traverse a dependency graph and extract symbol graphs from Swift modules that it finds. The results are propagated in a `SwiftSymbolGraphInfo` provider.
*   A rule, `swift_extract_symbol_graph`, which applies the above aspect to one or more targets and combines the results into a single directory.
*   A low-level Starlark API, `swift_common.extract_symbol_graph`, which creates the action to invoke `swift-symbolgraph-extract` on a single `.swiftmodule`.

PiperOrigin-RevId: 430517714
  • Loading branch information
allevato authored and swiple-rules-gardener committed Feb 23, 2022
1 parent 56bac6a commit d41cdb9
Show file tree
Hide file tree
Showing 17 changed files with 805 additions and 5 deletions.
3 changes: 3 additions & 0 deletions swift/internal/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ bzl_library(
name = "rules",
srcs = [
"swift_binary_test.bzl",
"swift_extract_symbol_graph.bzl",
"swift_feature_allowlist.bzl",
"swift_grpc_library.bzl",
"swift_import.bzl",
Expand Down Expand Up @@ -55,7 +56,9 @@ bzl_library(
"proto_gen_utils.bzl",
"providers.bzl",
"swift_clang_module_aspect.bzl",
"swift_symbol_graph_aspect.bzl",
"swift_usage_aspect.bzl",
"symbol_graph_extracting.bzl",
"target_triples.bzl",
"toolchain_config.bzl",
"utils.bzl",
Expand Down
5 changes: 5 additions & 0 deletions swift/internal/actions.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ swift_action_names = struct(
# Precompiles an explicit module for a C/Objective-C module map and its
# headers, emitting a `.pcm` file.
PRECOMPILE_C_MODULE = "SwiftPrecompileCModule",

# Extracts a JSON-formatted symbol graph from a module, which can be used as
# an input to documentation generating tools like `docc` or analyzed with
# other tooling.
SYMBOL_GRAPH_EXTRACT = "SwiftSymbolGraphExtract",
)

def _apply_configurator(configurator, prerequisites, args):
Expand Down
24 changes: 20 additions & 4 deletions swift/internal/compiling.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,7 @@ def compile_action_configs(
actions = [
swift_action_names.COMPILE,
swift_action_names.PRECOMPILE_C_MODULE,
swift_action_names.SYMBOL_GRAPH_EXTRACT,
],
configurators = [
swift_toolchain_config.add_arg("-Xcc", "-Xclang"),
Expand All @@ -438,13 +439,17 @@ def compile_action_configs(
# Configure how implicit modules are handled--either using the module
# cache, or disabled completely when using explicit modules.
swift_toolchain_config.action_config(
actions = [swift_action_names.COMPILE],
actions = [
swift_action_names.COMPILE,
],
configurators = [_global_module_cache_configurator],
features = [SWIFT_FEATURE_USE_GLOBAL_MODULE_CACHE],
not_features = [SWIFT_FEATURE_USE_C_MODULES],
),
swift_toolchain_config.action_config(
actions = [swift_action_names.COMPILE],
actions = [
swift_action_names.COMPILE,
],
configurators = [
swift_toolchain_config.add_arg(
"-Xwrapped-swift=-ephemeral-module-cache",
Expand All @@ -462,6 +467,7 @@ def compile_action_configs(
actions = [
swift_action_names.COMPILE,
swift_action_names.PRECOMPILE_C_MODULE,
swift_action_names.SYMBOL_GRAPH_EXTRACT,
],
configurators = [
swift_toolchain_config.add_arg(
Expand Down Expand Up @@ -550,7 +556,10 @@ def compile_action_configs(
#### Search paths for Swift module dependencies
action_configs.extend([
swift_toolchain_config.action_config(
actions = [swift_action_names.COMPILE],
actions = [
swift_action_names.COMPILE,
swift_action_names.SYMBOL_GRAPH_EXTRACT,
],
configurators = [_dependencies_swiftmodules_configurator],
not_features = [SWIFT_FEATURE_VFSOVERLAY],
),
Expand All @@ -566,7 +575,10 @@ def compile_action_configs(
#### Search paths for framework dependencies
action_configs.extend([
swift_toolchain_config.action_config(
actions = [swift_action_names.COMPILE],
actions = [
swift_action_names.COMPILE,
swift_action_names.SYMBOL_GRAPH_EXTRACT,
],
configurators = [
lambda prereqs, args: _framework_search_paths_configurator(
prereqs,
Expand Down Expand Up @@ -594,6 +606,7 @@ def compile_action_configs(
actions = [
swift_action_names.COMPILE,
swift_action_names.PRECOMPILE_C_MODULE,
swift_action_names.SYMBOL_GRAPH_EXTRACT,
],
configurators = [
_clang_search_paths_configurator,
Expand All @@ -607,6 +620,7 @@ def compile_action_configs(
actions = [
swift_action_names.COMPILE,
swift_action_names.PRECOMPILE_C_MODULE,
swift_action_names.SYMBOL_GRAPH_EXTRACT,
],
configurators = [_dependencies_clang_modules_configurator],
features = [SWIFT_FEATURE_USE_C_MODULES],
Expand All @@ -615,6 +629,7 @@ def compile_action_configs(
actions = [
swift_action_names.COMPILE,
swift_action_names.PRECOMPILE_C_MODULE,
swift_action_names.SYMBOL_GRAPH_EXTRACT,
],
configurators = [_dependencies_clang_modulemaps_configurator],
not_features = [SWIFT_FEATURE_USE_C_MODULES],
Expand Down Expand Up @@ -694,6 +709,7 @@ def compile_action_configs(
actions = [
swift_action_names.COMPILE,
swift_action_names.PRECOMPILE_C_MODULE,
swift_action_names.SYMBOL_GRAPH_EXTRACT,
],
configurators = [_module_name_configurator],
),
Expand Down
13 changes: 13 additions & 0 deletions swift/internal/derived_files.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,18 @@ def _swiftsourceinfo(actions, module_name):
"""
return actions.declare_file("{}.swiftsourceinfo".format(module_name))

def _symbol_graph_directory(actions, target_name):
"""Declares a directory for symbol graphs extracted from a Swift module.
Args:
actions: The context's actions object.
target_name: The name of the target being built.
Returns:
The declared `File`.
"""
return actions.declare_directory("{}.symbolgraphs".format(target_name))

def _vfsoverlay(actions, target_name):
"""Declares a file for the VFS overlay for a compilation action.
Expand Down Expand Up @@ -309,6 +321,7 @@ derived_files = struct(
swiftinterface = _swiftinterface,
swiftmodule = _swiftmodule,
swiftsourceinfo = _swiftsourceinfo,
symbol_graph_directory = _symbol_graph_directory,
vfsoverlay = _vfsoverlay,
whole_module_object_file = _whole_module_object_file,
xctest_bundle = _xctest_bundle,
Expand Down
23 changes: 23 additions & 0 deletions swift/internal/providers.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,29 @@ from the `.proto` files.
},
)

SwiftSymbolGraphInfo = provider(
doc = "Propagates extracted symbol graph files from Swift modules.",
fields = {
"direct_symbol_graphs": """\
`List` of `struct`s representing the symbol graphs extracted from the target
that propagated this provider. This list will be empty if propagated by a
non-Swift target (although its `transitive_symbol_graphs` may be non-empty if it
has Swift dependencies).
Each `struct` has the following fields:
* `module_name`: A string denoting the name of the Swift module.
* `symbol_graph_dir`: A directory-type `File` containing one or more
`.symbols.json` files representing the symbol graph(s) for the module.
""",
"transitive_symbol_graphs": """\
`Depset` of `struct`s representing the symbol graphs extracted from the target
that propagated this provider and all of its Swift dependencies. Each `struct`
has the same fields as documented in `direct_symbol_graphs`.
""",
},
)

SwiftToolchainInfo = provider(
doc = """
Propagates information about a Swift toolchain to compilation and linking rules
Expand Down
2 changes: 2 additions & 0 deletions swift/internal/swift_common.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ load(
"create_swift_module",
)
load(":swift_clang_module_aspect.bzl", "create_swift_interop_info")
load(":symbol_graph_extracting.bzl", "extract_symbol_graph")

# The exported `swift_common` module, which defines the public API for directly
# invoking actions that compile Swift code from other rules.
Expand All @@ -65,6 +66,7 @@ swift_common = struct(
create_swift_interop_info = create_swift_interop_info,
create_swift_module = create_swift_module,
derive_module_name = derive_module_name,
extract_symbol_graph = extract_symbol_graph,
is_enabled = is_feature_enabled,
library_rule_attrs = swift_library_rule_attrs,
precompile_clang_module = precompile_clang_module,
Expand Down
125 changes: 125 additions & 0 deletions swift/internal/swift_extract_symbol_graph.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# Copyright 2022 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Implementation of the `swift_extract_module_graph` rule."""

load(":derived_files.bzl", "derived_files")
load(":providers.bzl", "SwiftInfo", "SwiftSymbolGraphInfo")
load(":swift_symbol_graph_aspect.bzl", "swift_symbol_graph_aspect")

def _swift_extract_symbol_graph_impl(ctx):
actions = ctx.actions

output_dir = derived_files.symbol_graph_directory(
actions = actions,
target_name = ctx.label.name,
)

args = actions.args()
args.add(output_dir.path)

seen_modules = {}
inputs = []

for target in ctx.attr.targets:
if SwiftSymbolGraphInfo in target:
symbol_graph_info = target[SwiftSymbolGraphInfo]
for symbol_graph in symbol_graph_info.direct_symbol_graphs:
if symbol_graph.module_name in seen_modules:
fail("Module '{}' was provided by multiple targets.".format(
symbol_graph.module_name,
))

seen_modules[symbol_graph.module_name] = True

# Expand the directory's files into the argument list so that
# they are individually copied into output directory (resulting
# in a flat layout).
args.add_all(
[symbol_graph.symbol_graph_dir],
expand_directories = True,
)
inputs.append(symbol_graph.symbol_graph_dir)

actions.run_shell(
arguments = [args],
command = """\
set -e
output_dir="$1"
mkdir -p "${output_dir}"
shift
for symbol_file in "$@"; do
cp "${symbol_file}" "${output_dir}/$(basename ${symbol_file})"
done
""",
inputs = inputs,
mnemonic = "SwiftCollectSymbolGraphs",
outputs = [output_dir],
)

return [DefaultInfo(files = depset([output_dir]))]

swift_extract_symbol_graph = rule(
attrs = {
"minimum_access_level": attr.string(
default = "public",
doc = """\
The minimum access level of the declarations that should be emitted in the
symbol graphs.
This value must be either `fileprivate`, `internal`, `private`, or `public`. The
default value is `public`.
""",
values = [
"fileprivate",
"internal",
"private",
"public",
],
),
"targets": attr.label_list(
allow_empty = False,
aspects = [swift_symbol_graph_aspect],
doc = """\
One or more Swift targets from which to extract symbol graphs.
""",
mandatory = True,
providers = [[SwiftInfo]],
),
},
doc = """\
Extracts symbol graphs from one or more Swift targets.
The output of this rule is a single directory named
`${TARGET_NAME}.symbolgraphs` that contains the symbol graph JSON files for the
Swift modules directly compiled by all the targets listed in the `targets`
attribute (but not their transitive dependencies). Therefore, for each module
the directory will contain:
* One or more files named `${MODULE_NAME}.symbols.json` containing the symbol
graphs for non-`extension` declarations in each module.
* Optionally, one or more files named
`${MODULE_NAME}@${EXTENDED_MODULE}.symbols.json` containing the symbol
graphs for declarations in each module that extend types from other modules.
This rule can be used as a simple interface to extract symbol graphs for later
processing by other Bazel rules (for example, a `genrule` that operates on the
resulting JSON files). For more complex workflows, we recommend writing a custom
rule that applies `swift_symbol_graph_aspect` to the targets of interest and
registers other Starlark actions that read the symbol graphs based on the
`SwiftSymbolGraphInfo` providers attached to those targets. The implementation
of this rule can serve as a guide for implementing such a rule.
""",
implementation = _swift_extract_symbol_graph_impl,
)
Loading

1 comment on commit d41cdb9

@keith
Copy link
Member

@keith keith commented on d41cdb9 Mar 3, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.