Skip to content

Commit

Permalink
Update DocC for rules_swift 2.1.0 (#2487)
Browse files Browse the repository at this point in the history
Updates the DocC rules to support rules_swift 2.x which introduced
breaking changes to how Swift symbol graphs are collected.

This PR updates to use the new aspect to extract the symbol graphs. The
public API remains unchanged.

Fixes #2445
  • Loading branch information
luispadron authored Jul 10, 2024
1 parent d68d52a commit 8e33d08
Show file tree
Hide file tree
Showing 9 changed files with 130 additions and 63 deletions.
2 changes: 1 addition & 1 deletion MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ bazel_dep(name = "bazel_skylib", version = "1.3.0")
bazel_dep(name = "platforms", version = "0.0.9")
bazel_dep(
name = "rules_swift",
version = "2.0.0",
version = "2.1.0",
max_compatibility_level = 2,
repo_name = "build_bazel_rules_swift",
)
Expand Down
69 changes: 40 additions & 29 deletions apple/internal/aspects/docc_archive_aspect.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,30 @@
"""Defines aspects for collecting information required to build .docc and .doccarchive files."""

load(
"@build_bazel_rules_swift//swift:swift.bzl",
"SwiftInfo",
"@build_bazel_rules_swift//swift:providers.bzl",
"SwiftSymbolGraphInfo",
)
load(
"@build_bazel_rules_apple//apple:providers.bzl",
"@build_bazel_rules_swift//swift:swift_symbol_graph_aspect.bzl",
"swift_symbol_graph_aspect",
)
load(
"//apple:providers.bzl",
"DocCBundleInfo",
"DocCSymbolGraphsInfo",
)

def _swift_symbol_graph(swift_info):
"""Returns the symbol graph from a SwiftInfo provider or fails if it doesn't exist."""
direct_modules = swift_info.direct_modules
if len(direct_modules) != 1:
return None
module = direct_modules[0]
if not module.swift:
return None
swift_module = module.swift
if not swift_module.symbol_graph:
return None
return swift_module.symbol_graph

def _first_docc_bundle(target, ctx):
def _swift_symbol_graphs(*, swift_symbol_graph_info):
"""Returns a `List` of symbol graph directories from a `SwiftSymbolGraphInfo` provider or fails if it doesn't exist."""
direct_symbol_graphs = swift_symbol_graph_info.direct_symbol_graphs
transitive_symbol_graphs = swift_symbol_graph_info.transitive_symbol_graphs

return [
symbol_graph.symbol_graph_dir
for symbol_graph in (direct_symbol_graphs + transitive_symbol_graphs.to_list())
]

def _first_docc_bundle(*, target, ctx):
"""Returns the first .docc bundle for the target or its deps by looking in it's data."""
docc_bundles = []

Expand All @@ -57,15 +58,20 @@ def _docc_symbol_graphs_aspect_impl(target, ctx):

symbol_graphs = []

if SwiftInfo in target:
symbol_graphs.append(_swift_symbol_graph(target[SwiftInfo]))
if SwiftSymbolGraphInfo in target:
symbol_graphs.extend(
_swift_symbol_graphs(
swift_symbol_graph_info = target[SwiftSymbolGraphInfo],
),
)
elif hasattr(ctx.rule.attr, "deps"):
for dep in ctx.rule.attr.deps:
if SwiftInfo in dep:
symbol_graphs.append(_swift_symbol_graph(dep[SwiftInfo]))

# Filter out None
symbol_graphs = [symbol_graph for symbol_graph in symbol_graphs if symbol_graph]
if SwiftSymbolGraphInfo in dep:
symbol_graphs.extend(
_swift_symbol_graphs(
swift_symbol_graph_info = dep[SwiftSymbolGraphInfo],
),
)

if not symbol_graphs:
return []
Expand All @@ -76,7 +82,10 @@ def _docc_bundle_info_aspect_impl(target, ctx):
"""Creates a DocCBundleInfo provider for targets which have a .docc bundle (or which bundle a target that does)"""

if hasattr(ctx.rule.attr, "data"):
first_docc_bundle = _first_docc_bundle(target, ctx)
first_docc_bundle = _first_docc_bundle(
target = target,
ctx = ctx,
)
if first_docc_bundle:
return [DocCBundleInfo(bundle = first_docc_bundle)]
if hasattr(ctx.rule.attr, "deps"):
Expand All @@ -90,19 +99,21 @@ def _docc_bundle_info_aspect_impl(target, ctx):
docc_bundle_info_aspect = aspect(
implementation = _docc_bundle_info_aspect_impl,
doc = """
Creates or collects the DocCBundleInfo provider for a target or its deps.
Creates or collects the `DocCBundleInfo` provider for a target or its deps.
This aspect works with targets that have a .docc bundle in their data, or which bundle a target that does.
This aspect works with targets that have a `.docc` bundle in their data, or which bundle a target that does.
""",
attr_aspects = ["data", "deps"],
)

docc_symbol_graphs_aspect = aspect(
implementation = _docc_symbol_graphs_aspect_impl,
required_aspect_providers = [SwiftSymbolGraphInfo],
requires = [swift_symbol_graph_aspect],
doc = """
Creates or collects the DocCSymbolGraphsInfo provider for a target or its deps.
Creates or collects the `DocCSymbolGraphsInfo` provider for a target or its deps.
This aspect works with targets that have a SwiftInfo provider, or which bundle a target that does.
This aspect works with targets that have a `SwiftSymbolGraphInfo` provider, or which bundle a target that does.
""",
attr_aspects = ["deps"],
)
47 changes: 28 additions & 19 deletions apple/internal/docc.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def _docc_archive_impl(ctx):
platform = ctx.fragments.apple.single_arch_platform
transform_for_static_hosting = ctx.attr.transform_for_static_hosting
xcode_config = ctx.attr._xcode_config[apple_common.XcodeVersionConfig]
dep = ctx.attr.dep[0] # this isn't actually a list target but transition makes it one.
dep = ctx.attr.dep
symbol_graphs_info = None
docc_bundle_info = None
docc_build_inputs = []
Expand Down Expand Up @@ -155,19 +155,6 @@ def _docc_archive_impl(ctx):
doccarchive_binary_info,
]

def _swift_emit_symbol_graph_transition_impl(settings, _attr):
"""A transition that enables "swift.emit_symbol_graph" feature"""
if "//command_line_option:features" in settings:
return {"//command_line_option:features": settings["//command_line_option:features"] + ["swift.emit_symbol_graph"]}
else:
return {"//command_line_option:features": ["swift.emit_symbol_graph"]}

swift_emit_symbol_graph_transition = transition(
implementation = _swift_emit_symbol_graph_transition_impl,
inputs = ["//command_line_option:features"],
outputs = ["//command_line_option:features"],
)

docc_archive = rule(
implementation = _docc_archive_impl,
fragments = ["apple"],
Expand All @@ -179,7 +166,7 @@ NOTE: At this time Swift is the only supported language for this rule.
Example:
```python
```starlark
load("@build_bazel_rules_apple//apple:docc.bzl", "docc_archive")
docc_archive(
Expand All @@ -198,7 +185,6 @@ docc_archive(
docc_bundle_info_aspect,
docc_symbol_graphs_aspect,
],
cfg = swift_emit_symbol_graph_transition,
providers = [[DocCBundleInfo], [DocCSymbolGraphsInfo]],
),
"default_code_listing_language": attr.string(
Expand All @@ -212,6 +198,19 @@ Must be one of "error", "warning", "information", or "hint"
""",
values = ["error", "warning", "information", "hint"],
),
# TODO: use `attr.bool` once https://github.com/bazelbuild/bazel/issues/22809 is resolved.
"emit_extension_block_symbols": attr.string(
default = "0",
doc = """
Defines if the symbol graph information for `extension` blocks should be
emitted in addition to the default symbol graph information.
This value must be either `"0"` or `"1"`.When the value is `"1"`, the symbol
graph information for `extension` blocks will be emitted in addition to
the default symbol graph information. The default value is `"0"`.
""",
values = ["0", "1"],
),
"enable_inherited_docs": attr.bool(
default = False,
doc = "Inherit documentation for inherited symbols.",
Expand All @@ -235,12 +234,22 @@ Must be one of "error", "warning", "information", or "hint"
"kinds": attr.string_list(
doc = "The kinds of entities to filter generated documentation for.",
),
"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",
],
),
"transform_for_static_hosting": attr.bool(
default = True,
),
"_allowlist_function_transition": attr.label(
default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
),
"_preview_template": attr.label(
allow_single_file = True,
default = "//apple/internal/templates:docc_preview_template",
Expand Down
4 changes: 2 additions & 2 deletions apple/repositories.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,8 @@ def apple_rules_dependencies(ignore_version_differences = False, include_bzlmod_
_maybe(
http_archive,
name = "build_bazel_rules_swift",
sha256 = "32eeb4ef33c708d9c9a4ee0fa8475322ef149dabc81884ddc3b50eb2efff7843",
url = "https://github.com/bazelbuild/rules_swift/releases/download/2.0.0/rules_swift.2.0.0.tar.gz",
sha256 = "8e0c72aa2be5ae44da44521c46e0700df184953e8dbc5d5423222b8cb141c64f",
url = "https://github.com/bazelbuild/rules_swift/releases/download/2.1.0/rules_swift.2.1.0.tar.gz",
ignore_version_differences = ignore_version_differences,
)

Expand Down
11 changes: 7 additions & 4 deletions doc/rules-docc.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ Defines rules for building Apple DocC targets.
## docc_archive

<pre>
docc_archive(<a href="#docc_archive-name">name</a>, <a href="#docc_archive-default_code_listing_language">default_code_listing_language</a>, <a href="#docc_archive-dep">dep</a>, <a href="#docc_archive-diagnostic_level">diagnostic_level</a>, <a href="#docc_archive-enable_inherited_docs">enable_inherited_docs</a>,
<a href="#docc_archive-fallback_bundle_identifier">fallback_bundle_identifier</a>, <a href="#docc_archive-fallback_bundle_version">fallback_bundle_version</a>, <a href="#docc_archive-fallback_display_name">fallback_display_name</a>,
<a href="#docc_archive-hosting_base_path">hosting_base_path</a>, <a href="#docc_archive-kinds">kinds</a>, <a href="#docc_archive-transform_for_static_hosting">transform_for_static_hosting</a>)
docc_archive(<a href="#docc_archive-name">name</a>, <a href="#docc_archive-default_code_listing_language">default_code_listing_language</a>, <a href="#docc_archive-dep">dep</a>, <a href="#docc_archive-diagnostic_level">diagnostic_level</a>,
<a href="#docc_archive-emit_extension_block_symbols">emit_extension_block_symbols</a>, <a href="#docc_archive-enable_inherited_docs">enable_inherited_docs</a>, <a href="#docc_archive-fallback_bundle_identifier">fallback_bundle_identifier</a>,
<a href="#docc_archive-fallback_bundle_version">fallback_bundle_version</a>, <a href="#docc_archive-fallback_display_name">fallback_display_name</a>, <a href="#docc_archive-hosting_base_path">hosting_base_path</a>, <a href="#docc_archive-kinds">kinds</a>,
<a href="#docc_archive-minimum_access_level">minimum_access_level</a>, <a href="#docc_archive-transform_for_static_hosting">transform_for_static_hosting</a>)
</pre>

Builds a .doccarchive for the given dependency.
Expand All @@ -19,7 +20,7 @@ NOTE: At this time Swift is the only supported language for this rule.

Example:

```python
```starlark
load("@build_bazel_rules_apple//apple:docc.bzl", "docc_archive")

docc_archive(
Expand All @@ -40,12 +41,14 @@ docc_archive(
| <a id="docc_archive-default_code_listing_language"></a>default_code_listing_language | A fallback default language for code listings if no value is provided in the documentation bundle's Info.plist file. | String | optional | `""` |
| <a id="docc_archive-dep"></a>dep | - | <a href="https://bazel.build/concepts/labels">Label</a> | optional | `None` |
| <a id="docc_archive-diagnostic_level"></a>diagnostic_level | Filters diagnostics above this level from output This filter level is inclusive. If a level of `information` is specified, diagnostics with a severity up to and including `information` will be printed. Must be one of "error", "warning", "information", or "hint" | String | optional | `""` |
| <a id="docc_archive-emit_extension_block_symbols"></a>emit_extension_block_symbols | Defines if the symbol graph information for `extension` blocks should be emitted in addition to the default symbol graph information.<br><br>This value must be either `"0"` or `"1"`.When the value is `"1"`, the symbol graph information for `extension` blocks will be emitted in addition to the default symbol graph information. The default value is `"0"`. | String | optional | `"0"` |
| <a id="docc_archive-enable_inherited_docs"></a>enable_inherited_docs | Inherit documentation for inherited symbols. | Boolean | optional | `False` |
| <a id="docc_archive-fallback_bundle_identifier"></a>fallback_bundle_identifier | A fallback bundle identifier if no value is provided in the documentation bundle's Info.plist file. | String | required | |
| <a id="docc_archive-fallback_bundle_version"></a>fallback_bundle_version | A fallback bundle version if no value is provided in the documentation bundle's Info.plist file. | String | required | |
| <a id="docc_archive-fallback_display_name"></a>fallback_display_name | A fallback display name if no value is provided in the documentation bundle's Info.plist file. | String | required | |
| <a id="docc_archive-hosting_base_path"></a>hosting_base_path | The base path your documentation website will be hosted at. For example, to deploy your site to 'example.com/my_name/my_project/documentation' instead of 'example.com/documentation', pass '/my_name/my_project' as the base path. | String | optional | `""` |
| <a id="docc_archive-kinds"></a>kinds | The kinds of entities to filter generated documentation for. | List of strings | optional | `[]` |
| <a id="docc_archive-minimum_access_level"></a>minimum_access_level | " 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`. | String | optional | `"public"` |
| <a id="docc_archive-transform_for_static_hosting"></a>transform_for_static_hosting | - | Boolean | optional | `True` |


2 changes: 2 additions & 0 deletions examples/ios/HelloWorldSwift/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,10 @@ docc_archive(
default_code_listing_language = "en",
dep = ":HelloWorldSwift",
diagnostic_level = "information",
emit_extension_block_symbols = "1",
enable_inherited_docs = True,
fallback_bundle_identifier = "com.example.hello-world-swift",
fallback_bundle_version = "1.0.0",
fallback_display_name = "HelloWorldSwift",
minimum_access_level = "internal",
)
51 changes: 46 additions & 5 deletions examples/ios/HelloWorldSwift/Sources/BazelApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,61 @@
import SwiftUI

@main
struct BazelApp: App {
var body: some Scene {
public struct BazelApp: App {
public init() { }

public var body: some Scene {
WindowGroup {
Text("Hello World")
.accessibility(identifier: "HELLO_WORLD")
}
}

/// A foo API to test DooC documentation generation.
/// A public API to test DooC documentation generation.
///
/// Example referencing ``AppDelegate``:
/// Example referencing ``BazelApp``:
///
/// ```swift
/// let appDelegate = AppDelegate()
/// let app = BazelApp()
/// ```
public func foo() { }

/// An internal API to test `minimum_access_level` DooC option.
internal func internalFoo() { }
}

// MARK: - View Extension

extension View {

/// A public API extension on ``View`` to test DooC documentation generation.
public func viewFoo() { }

/// An internal API extension on ``View`` to test `minimum_access_level` DooC option.
internal func internalViewFoo() { }
}

// MARK: - Custom type

/// My Struct
///
/// Example referencing ``MyStruct``:
///
/// ```swift
///
/// let foo = MyStruct()
/// ```
public struct MyStruct {
public init() { }

/// My Struct's foo API to test DooC documentation generation.
/// - Parameters:
/// - bar: A bar parameter.
public func foo(bar: Int) { }

/// An internal foo API to test DooC documentation generation with `minimum_access_level` set to `internal`.
///
/// - Parameters:
/// - bar: A bar parameter.
internal func internalFoo(bar: Int) { }
}
3 changes: 2 additions & 1 deletion test/starlark_tests/docc_tests.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ def docc_test_suite(name):
text_file_not_contains = [],
text_test_file = "$BUNDLE_ROOT/index.html",
text_test_values = [
"<script src=\"/custom/base/path/js/",
"<script defer=\"defer\" src=\"/custom/base/path/js/",
"<link href=\"/custom/base/path/css/",
],
tags = [name],
)
Expand Down
4 changes: 2 additions & 2 deletions test/starlark_tests/targets_under_test/ios/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -4915,7 +4915,7 @@ swift_library(
name = "basic_framework_lib_with_docc_bundle",
srcs = ["//test/starlark_tests/resources:BasicFramework.swift"],
data = ["//test/starlark_tests/resources:basic_docc_bundle_files"],
module_name = "BasicFramework",
module_name = "BasicFrameworkWithDocCBundle",
tags = common.fixture_tags,
visibility = ["//visibility:public"],
)
Expand All @@ -4941,7 +4941,7 @@ docc_archive(
ios_dynamic_framework(
name = "basic_framework_with_docc_bundle",
bundle_id = "com.google.example.framework",
bundle_name = "BasicFramework",
bundle_name = "BasicFrameworkWithDocCBundle",
families = [
"iphone",
"ipad",
Expand Down

0 comments on commit 8e33d08

Please sign in to comment.