From be812fddc356dcb8636b0b40d1ba79108fd46a49 Mon Sep 17 00:00:00 2001 From: Luis Padron Date: Tue, 4 Jul 2023 00:56:28 -0400 Subject: [PATCH] Add support for Apple .doccarchive --- apple/BUILD | 6 + apple/docc.bzl | 22 ++ apple/internal/BUILD | 14 + apple/internal/aspects/BUILD | 12 + .../internal/aspects/docc_archive_aspect.bzl | 108 ++++++++ apple/internal/docc.bzl | 244 ++++++++++++++++++ apple/internal/templates/BUILD | 9 + .../templates/docc_preview.template.sh | 38 +++ apple/providers.bzl | 14 + doc/BUILD.bazel | 1 + doc/README.md | 8 +- doc/providers.md | 36 +++ doc/rules-docc.md | 51 ++++ examples/ios/HelloWorldSwift/BUILD | 16 +- .../Resources/HelloWorldSwift.docc/README.md | 7 + .../HelloWorldSwift/Sources/AppDelegate.swift | 19 +- test/starlark_tests/BUILD | 3 + test/starlark_tests/docc_tests.bzl | 67 +++++ test/starlark_tests/resources/BUILD | 5 + .../resources/basic.docc/README.md | 3 + .../targets_under_test/ios/BUILD | 51 ++++ .../verifier_scripts/archive_contents_test.sh | 3 +- 22 files changed, 731 insertions(+), 6 deletions(-) create mode 100644 apple/docc.bzl create mode 100644 apple/internal/aspects/docc_archive_aspect.bzl create mode 100644 apple/internal/docc.bzl create mode 100644 apple/internal/templates/docc_preview.template.sh create mode 100755 doc/rules-docc.md create mode 100644 examples/ios/HelloWorldSwift/Resources/HelloWorldSwift.docc/README.md create mode 100644 test/starlark_tests/docc_tests.bzl create mode 100644 test/starlark_tests/resources/basic.docc/README.md diff --git a/apple/BUILD b/apple/BUILD index 269be651b6..2781648324 100644 --- a/apple/BUILD +++ b/apple/BUILD @@ -227,6 +227,12 @@ bzl_library( deps = ["//apple/internal:xcarchive"], ) +bzl_library( + name = "docc", + srcs = ["docc.bzl"], + deps = ["//apple/internal:docc"], +) + cc_toolchain_forwarder( name = "default_cc_toolchain_forwarder", ) diff --git a/apple/docc.bzl b/apple/docc.bzl new file mode 100644 index 0000000000..338f072187 --- /dev/null +++ b/apple/docc.bzl @@ -0,0 +1,22 @@ +# Copyright 2023 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. + +"""Defines rules for building Apple DocC targets.""" + +load( + "@build_bazel_rules_apple//apple/internal:docc.bzl", + _docc_archive = "docc_archive", +) + +docc_archive = _docc_archive diff --git a/apple/internal/BUILD b/apple/internal/BUILD index 4343420b93..ac2f663445 100644 --- a/apple/internal/BUILD +++ b/apple/internal/BUILD @@ -760,6 +760,20 @@ bzl_library( ], ) +bzl_library( + name = "docc", + srcs = ["docc.bzl"], + visibility = [ + "//apple:__subpackages__", + ], + deps = [ + "//apple:providers", + "//apple/internal/aspects:docc_archive_aspect", + "@bazel_skylib//lib:dicts", + "@build_bazel_apple_support//lib:apple_support", + ], +) + # Consumed by bazel tests. filegroup( name = "for_bazel_tests", diff --git a/apple/internal/aspects/BUILD b/apple/internal/aspects/BUILD index 511d971b3a..dcda2b61e7 100644 --- a/apple/internal/aspects/BUILD +++ b/apple/internal/aspects/BUILD @@ -76,6 +76,18 @@ bzl_library( ], ) +bzl_library( + name = "docc_archive_aspect", + srcs = ["docc_archive_aspect.bzl"], + visibility = [ + "//apple/internal:__pkg__", + ], + deps = [ + "//apple:providers", + "@build_bazel_rules_swift//swift", + ], +) + # Consumed by bazel tests. filegroup( name = "for_bazel_tests", diff --git a/apple/internal/aspects/docc_archive_aspect.bzl b/apple/internal/aspects/docc_archive_aspect.bzl new file mode 100644 index 0000000000..cfeaa0ff7d --- /dev/null +++ b/apple/internal/aspects/docc_archive_aspect.bzl @@ -0,0 +1,108 @@ +# Copyright 2023 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. + +"""Defines aspects for collecting information required to build .docc and .doccarchive files.""" + +load( + "@build_bazel_rules_swift//swift:swift.bzl", + "SwiftInfo", +) +load( + "@build_bazel_rules_apple//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): + """Returns the first .docc bundle for the target or its deps by looking in it's data.""" + docc_bundles = [] + + # Find the path to the .docc directory if it exists. + for data_target in ctx.rule.attr.data: + for file in data_target.files.to_list(): + if file.extension == "docc": + docc_bundles.append(file) + + if len(docc_bundles) > 1: + fail("Expected target %s to have at most one .docc bundle in its data" % target.label) + + return docc_bundles[0] if docc_bundles else None + +def _docc_symbol_graphs_aspect_impl(target, ctx): + """Creates a DocCSymbolGraphsInfo provider for targets which have a SwiftInfo provider (or which bundle a target that does).""" + + symbol_graphs = [] + + if SwiftInfo in target: + symbol_graphs.append(_swift_symbol_graph(target[SwiftInfo])) + 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 not symbol_graphs: + return [] + + return [DocCSymbolGraphsInfo(symbol_graphs = symbol_graphs)] + +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) + if first_docc_bundle: + return [DocCBundleInfo(bundle = first_docc_bundle)] + elif hasattr(ctx.rule.attr, "deps"): + # If this target has "deps", try to find a DocCBundleInfo provider in its deps. + for dep in ctx.rule.attr.deps: + if DocCBundleInfo in dep: + return dep[DocCBundleInfo] + + return [] + +docc_bundle_info_aspect = aspect( + implementation = _docc_bundle_info_aspect_impl, + doc = """ + 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. + """, + attr_aspects = ["data", "deps"], +) + +docc_symbol_graphs_aspect = aspect( + implementation = _docc_symbol_graphs_aspect_impl, + doc = """ + 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. + """, + attr_aspects = ["deps"], +) diff --git a/apple/internal/docc.bzl b/apple/internal/docc.bzl new file mode 100644 index 0000000000..06f91a913a --- /dev/null +++ b/apple/internal/docc.bzl @@ -0,0 +1,244 @@ +# Copyright 2023 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. + +"""Defines rules for building Apple DocC targets.""" + +load( + "@build_bazel_apple_support//lib:apple_support.bzl", + "apple_support", +) +load( + "@bazel_skylib//lib:dicts.bzl", + "dicts", +) +load( + "@build_bazel_rules_apple//apple:providers.bzl", + "DocCBundleInfo", + "DocCSymbolGraphsInfo", +) +load( + "@build_bazel_rules_apple//apple/internal:providers.bzl", + "new_applebinaryinfo", +) +load( + "@build_bazel_rules_apple//apple/internal/aspects:docc_archive_aspect.bzl", + "docc_bundle_info_aspect", + "docc_symbol_graphs_aspect", +) + +def _docc_archive_impl(ctx): + """Builds a .doccarchive for the given module. + """ + + default_code_listing_language = ctx.attr.default_code_listing_language + diagnostic_level = ctx.attr.diagnostic_level + enable_inherited_docs = ctx.attr.enable_inherited_docs + execution_requirements = {} + fallback_bundle_identifier = ctx.attr.fallback_bundle_identifier + fallback_bundle_version = ctx.attr.fallback_bundle_version + fallback_display_name = ctx.attr.fallback_display_name + kinds = ctx.attr.kinds + 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. + symbol_graphs_info = None + docc_bundle_info = None + docc_build_inputs = [] + + if DocCSymbolGraphsInfo in dep: + symbol_graphs_info = dep[DocCSymbolGraphsInfo] + if DocCBundleInfo in dep: + docc_bundle_info = dep[DocCBundleInfo] + + if not symbol_graphs_info and not docc_bundle_info: + fail("At least one of DocCSymbolGraphsInfo or DocCBundleInfo must be provided for target %s" % ctx.attr.name) + + if ctx.attr.name.endswith(".doccarchive"): + doccarchive_dir = ctx.actions.declare_directory(ctx.attr.name) + else: + doccarchive_dir = ctx.actions.declare_directory("%s.doccarchive" % ctx.attr.name) + + # Command and required arguments + arguments = ctx.actions.args() + arguments.add("docc") + arguments.add("convert") + arguments.add("--index") + arguments.add("--fallback-display-name", fallback_display_name) + arguments.add("--fallback-bundle-identifier", fallback_bundle_identifier) + arguments.add("--fallback-bundle-version", fallback_bundle_version) + arguments.add("--output-dir", doccarchive_dir.path) + + # Optional agruments + if default_code_listing_language: + arguments.add("--default-code-listing-language", default_code_listing_language) + if diagnostic_level: + arguments.add("--diagnostic-level", diagnostic_level) + if enable_inherited_docs: + arguments.add("--enable-inherited-docs") + if kinds: + arguments.add_all("--kind", kinds) + if transform_for_static_hosting: + arguments.add("--transform-for-static-hosting") + + # Add symbol graphs + if symbol_graphs_info: + arguments.add_all("--additional-symbol-graph-dir", symbol_graphs_info.symbol_graphs, expand_directories = False) + docc_build_inputs.extend(symbol_graphs_info.symbol_graphs) + + # The .docc bundle (if provided, only one is allowed) + if docc_bundle_info: + arguments.add(docc_bundle_info.bundle) + + # TODO: no-sandbox seems to be required when running docc convert with a .docc bundle provided + # in the sandbox the tool is unable to open the .docc bundle. + execution_requirements["no-sandbox"] = "1" + docc_build_inputs.append(docc_bundle_info.bundle) + + apple_support.run( + ctx, + inputs = depset(docc_build_inputs), + outputs = [doccarchive_dir], + mnemonic = "DocCConvert", + executable = "/usr/bin/xcrun", + arguments = [arguments], + progress_message = "Converting .doccarchive for %{label}", + execution_requirements = execution_requirements, + ) + + # Create an executable shell script that runs `docc preview` on the .doccarchive. + preview_script = ctx.actions.declare_file("%s_preview.sh" % ctx.attr.name) + ctx.actions.expand_template( + output = preview_script, + template = ctx.file._preview_template, + substitutions = { + "{docc_bundle}": docc_bundle_info.bundle.path if docc_bundle_info else "", + "{fallback_bundle_identifier}": fallback_bundle_identifier, + "{fallback_bundle_version}": str(fallback_bundle_version), + "{fallback_display_name}": fallback_display_name, + "{platform}": platform.name_in_plist, + "{sdk_version}": str(xcode_config.sdk_version_for_platform(platform)), + "{symbol_graph_dirs}": ",".join([f.path for f in symbol_graphs_info.symbol_graphs]) if symbol_graphs_info else "", + "{target_name}": ctx.attr.name, + "{xcode_version}": str(xcode_config.xcode_version()), + }, + is_executable = True, + ) + + # Limiting the contents of AppleBinaryInfo to what is necessary for testing and validation. + doccarchive_binary_info = new_applebinaryinfo( + binary = doccarchive_dir, + infoplist = None, + product_type = None, + ) + + return [ + DefaultInfo( + files = depset([doccarchive_dir]), + executable = preview_script, + runfiles = ctx.runfiles(files = [preview_script] + docc_build_inputs), + ), + 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"], + doc = """ +Builds a .doccarchive for the given dependency. +The target created by this rule can also be `run` to preview the generated documentation in Xcode. + +NOTE: At this time Swift is the only supported language for this rule. + +Example: + +```python +load("@build_bazel_rules_apple//apple:docc.bzl", "docc_archive") + +docc_archive( + name = "Lib.doccarchive", + dep = ":Lib", + fallback_bundle_identifier = "com.example.lib", + fallback_bundle_version = "1.0.0", + fallback_display_name = "Lib", +) +```""", + attrs = dicts.add( + apple_support.action_required_attrs(), + { + "dep": attr.label( + aspects = [ + docc_bundle_info_aspect, + docc_symbol_graphs_aspect, + ], + cfg = swift_emit_symbol_graph_transition, + providers = [[DocCBundleInfo], [DocCSymbolGraphsInfo]], + ), + "default_code_listing_language": attr.string( + doc = "A fallback default language for code listings if no value is provided in the documentation bundle's Info.plist file.", + ), + "diagnostic_level": attr.string( + doc = """ +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" + """, + values = ["error", "warning", "information", "hint"], + ), + "enable_inherited_docs": attr.bool( + default = False, + doc = "Inherit documentation for inherited symbols.", + ), + "fallback_bundle_identifier": attr.string( + doc = "A fallback bundle identifier if no value is provided in the documentation bundle's Info.plist file.", + mandatory = True, + ), + "fallback_bundle_version": attr.string( + doc = "A fallback bundle version if no value is provided in the documentation bundle's Info.plist file.", + mandatory = True, + ), + "fallback_display_name": attr.string( + doc = "A fallback display name if no value is provided in the documentation bundle's Info.plist file.", + mandatory = True, + ), + "kinds": attr.string_list( + doc = "The kinds of entities to filter generated documentation for.", + ), + "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", + ), + }, + ), + executable = True, +) diff --git a/apple/internal/templates/BUILD b/apple/internal/templates/BUILD index 2f6697ef08..a3e1781fd4 100644 --- a/apple/internal/templates/BUILD +++ b/apple/internal/templates/BUILD @@ -27,6 +27,15 @@ filegroup( visibility = ["//visibility:public"], ) +filegroup( + name = "docc_preview_template", + srcs = ["docc_preview.template.sh"], + # Used by the rule implementations, so it needs to be public; but + # should be considered an implementation detail of the rules and + # not used by other things. + visibility = ["//visibility:public"], +) + # Consumed by bazel tests. filegroup( name = "for_bazel_tests", diff --git a/apple/internal/templates/docc_preview.template.sh b/apple/internal/templates/docc_preview.template.sh new file mode 100644 index 0000000000..f8a64bda60 --- /dev/null +++ b/apple/internal/templates/docc_preview.template.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +# Copyright 2023 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. + +set -euo pipefail + +if [ ! -d "{docc_bundle}" ]; then + echo "ERROR: Expected a .docc directory bundle for target: {target_name}" + echo "Previewing requires a .docc bundle to be provided in the target's resources." + exit 1 +fi + +cd "$BUILD_WORKSPACE_DIRECTORY" + +env -i \ + APPLE_SDK_PLATFORM="{platform}" \ + APPLE_SDK_VERSION_OVERRIDE="{sdk_version}" \ + XCODE_VERSION_OVERRIDE="{xcode_version}" \ + /usr/bin/xcrun docc preview \ + --index \ + --fallback-display-name "{fallback_display_name}" \ + --fallback-bundle-identifier "{fallback_bundle_identifier}" \ + --fallback-bundle-version "{fallback_bundle_version}" \ + --additional-symbol-graph-dir "{symbol_graph_dirs}" \ + --output-dir "$(mktemp -d)" \ + "{docc_bundle}" diff --git a/apple/providers.bzl b/apple/providers.bzl index 8ccef6363c..1f0bbabb71 100644 --- a/apple/providers.bzl +++ b/apple/providers.bzl @@ -215,3 +215,17 @@ that requirement. """, fields = {}, ) + +DocCBundleInfo = provider( + doc = "Provides general information about a .docc bundle.", + fields = { + "bundle": "the path to the .docc bundle", + }, +) + +DocCSymbolGraphsInfo = provider( + doc = "Provides the symbol graphs required to archive a .docc bundle.", + fields = { + "symbol_graphs": "the paths to the symbol graphs", + }, +) diff --git a/doc/BUILD.bazel b/doc/BUILD.bazel index d82c51e12b..1df88a1eb9 100644 --- a/doc/BUILD.bazel +++ b/doc/BUILD.bazel @@ -8,6 +8,7 @@ _PLAIN_DOC_SRCS = [ _RULES_DOC_SRCS = [ "apple", + "docc", "dtrace", "ios.doc", "macos.doc", diff --git a/doc/README.md b/doc/README.md index 0e872c47dc..bd680faacc 100644 --- a/doc/README.md +++ b/doc/README.md @@ -106,7 +106,13 @@ below. - General + General + + @build_bazel_rules_apple//apple:docc.bzl + + docc_archive + + @build_bazel_rules_apple//apple:versioning.bzl apple_bundle_version
diff --git a/doc/providers.md b/doc/providers.md index c612f550e9..93b457a0c8 100644 --- a/doc/providers.md +++ b/doc/providers.md @@ -453,6 +453,42 @@ requirement. + + +## DocCBundleInfo + +
+DocCBundleInfo(bundle)
+
+ +Provides general information about a .docc bundle. + +**FIELDS** + + +| Name | Description | +| :------------- | :------------- | +| bundle | the path to the .docc bundle | + + + + +## DocCSymbolGraphsInfo + +
+DocCSymbolGraphsInfo(symbol_graphs)
+
+ +Provides the symbol graphs required to archive a .docc bundle. + +**FIELDS** + + +| Name | Description | +| :------------- | :------------- | +| symbol_graphs | the paths to the symbol graphs | + + ## IosAppClipBundleInfo diff --git a/doc/rules-docc.md b/doc/rules-docc.md new file mode 100755 index 0000000000..544eb9b621 --- /dev/null +++ b/doc/rules-docc.md @@ -0,0 +1,51 @@ + + +Defines rules for building Apple DocC targets. + + + +## docc_archive + +
+docc_archive(name, default_code_listing_language, dep, diagnostic_level, enable_inherited_docs,
+             fallback_bundle_identifier, fallback_bundle_version, fallback_display_name, kinds,
+             transform_for_static_hosting)
+
+ + +Builds a .doccarchive for the given dependency. +The target created by this rule can also be `run` to preview the generated documentation in Xcode. + +NOTE: At this time Swift is the only supported language for this rule. + +Example: + +```python +load("@build_bazel_rules_apple//apple:docc.bzl", "docc_archive") + +docc_archive( + name = "Lib.doccarchive", + dep = ":Lib", + fallback_bundle_identifier = "com.example.lib", + fallback_bundle_version = "1.0.0", + fallback_display_name = "Lib", +) +``` + +**ATTRIBUTES** + + +| Name | Description | Type | Mandatory | Default | +| :------------- | :------------- | :------------- | :------------- | :------------- | +| name | A unique name for this target. | Name | required | | +| 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 | "" | +| dep | - | Label | optional | None | +| 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 | "" | +| enable_inherited_docs | Inherit documentation for inherited symbols. | Boolean | optional | False | +| fallback_bundle_identifier | A fallback bundle identifier if no value is provided in the documentation bundle's Info.plist file. | String | required | | +| fallback_bundle_version | A fallback bundle version if no value is provided in the documentation bundle's Info.plist file. | String | required | | +| fallback_display_name | A fallback display name if no value is provided in the documentation bundle's Info.plist file. | String | required | | +| kinds | The kinds of entities to filter generated documentation for. | List of strings | optional | [] | +| transform_for_static_hosting | - | Boolean | optional | True | + + diff --git a/examples/ios/HelloWorldSwift/BUILD b/examples/ios/HelloWorldSwift/BUILD index ab222d9629..9c486b15d0 100644 --- a/examples/ios/HelloWorldSwift/BUILD +++ b/examples/ios/HelloWorldSwift/BUILD @@ -1,6 +1,7 @@ load("@bazel_skylib//rules:build_test.bzl", "build_test") -load("//apple:ios.bzl", "ios_application", "ios_ui_test", "ios_unit_test") load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") +load("//apple:docc.bzl", "docc_archive") +load("//apple:ios.bzl", "ios_application", "ios_ui_test", "ios_unit_test") licenses(["notice"]) @@ -10,6 +11,7 @@ swift_library( "Sources/AppDelegate.swift", ], data = [ + "Resources/HelloWorldSwift.docc", "Resources/Main.storyboard", ], module_name = "Sources", @@ -89,3 +91,15 @@ ios_ui_test( test_host = ":HelloWorldSwift", deps = [":UITests"], ) + +# Example using docc_archive to generate documentation +docc_archive( + name = "HelloWorldSwift.doccarchive", + default_code_listing_language = "en", + dep = ":HelloWorldSwift", + diagnostic_level = "information", + enable_inherited_docs = True, + fallback_bundle_identifier = "com.example.hello-world-swift", + fallback_bundle_version = "1.0.0", + fallback_display_name = "HelloWorldSwift", +) diff --git a/examples/ios/HelloWorldSwift/Resources/HelloWorldSwift.docc/README.md b/examples/ios/HelloWorldSwift/Resources/HelloWorldSwift.docc/README.md new file mode 100644 index 0000000000..8a61c4d913 --- /dev/null +++ b/examples/ios/HelloWorldSwift/Resources/HelloWorldSwift.docc/README.md @@ -0,0 +1,7 @@ +# HelloWorldSwift + +This is an example of a Swift Bazel package using `.docc` documentation. + +```swift +let foo = AppDelegate.foo() +``` diff --git a/examples/ios/HelloWorldSwift/Sources/AppDelegate.swift b/examples/ios/HelloWorldSwift/Sources/AppDelegate.swift index edf89f6689..6dc25a5a8b 100644 --- a/examples/ios/HelloWorldSwift/Sources/AppDelegate.swift +++ b/examples/ios/HelloWorldSwift/Sources/AppDelegate.swift @@ -21,15 +21,28 @@ extension UIApplication { } #endif +/// The ``AppDelegate`` for the application. +/// This class is the entry point for the application and is responsible for the lifecycle of it. @UIApplicationMain -class AppDelegate: NSObject, UIApplicationDelegate { +public class AppDelegate: NSObject, UIApplicationDelegate { - var window: UIWindow? + /// The window of the application. + public var window: UIWindow? - func application( + /// The start of the application. + public func application( _ application: UIApplication, didFinishLaunchingWithOptions: [UIApplication.LaunchOptionsKey : Any]? ) -> Bool { return true } + + /// A foo API to test DooC documentation generation. + /// + /// Example referencing ``AppDelegate``: + /// + /// ```swift + /// let appDelegate = AppDelegate() + /// ``` + public func foo() { } } diff --git a/test/starlark_tests/BUILD b/test/starlark_tests/BUILD index fb296421c8..d5da115ebb 100644 --- a/test/starlark_tests/BUILD +++ b/test/starlark_tests/BUILD @@ -56,6 +56,7 @@ load(":watchos_single_target_ui_test_tests.bzl", "watchos_single_target_ui_test_ load(":watchos_ui_test_tests.bzl", "watchos_ui_test_test_suite") load(":watchos_unit_test_tests.bzl", "watchos_unit_test_test_suite") load(":xcarchive_tests.bzl", "xcarchive_test_suite") +load(":docc_tests.bzl", "docc_test_suite") licenses(["notice"]) @@ -176,6 +177,8 @@ watchos_unit_test_test_suite(name = "watchos_unit_test") xcarchive_test_suite(name = "xcarchive") +docc_test_suite(name = "docc") + test_suite(name = "all_tests") bzl_library( diff --git a/test/starlark_tests/docc_tests.bzl b/test/starlark_tests/docc_tests.bzl new file mode 100644 index 0000000000..3a8c43c9a3 --- /dev/null +++ b/test/starlark_tests/docc_tests.bzl @@ -0,0 +1,67 @@ +# Copyright 2023 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. + +"""docc Starlark tests.""" + +load( + "//test/starlark_tests/rules:common_verification_tests.bzl", + "archive_contents_test", +) + +def docc_test_suite(name): + """Test suite for docc rules. + + Args: + name: the base name to be used in things created by this macro + """ + + # Verify doccarchive bundle is created for Swift iOS app. + archive_contents_test( + name = "{}_contains_doccarchive_when_ios_swift_app".format(name), + build_type = "simulator", + target_under_test = "//test/starlark_tests/targets_under_test/ios:app_with_swift_dep.doccarchive", + contains = [ + "$BUNDLE_ROOT/index.html", + ], + text_file_not_contains = [ + "$BUNDLE_ROOT/documentation/basicframework/readme/index.html", # only included with a .docc bundle in data + ], + text_test_file = "$BUNDLE_ROOT/metadata.json", + text_test_values = [ + "{\"bundleDisplayName\":\"app_with_swift_dep\",\"bundleIdentifier\":\"com.google.example\",\"schemaVersion\":{\"major\":0,\"minor\":1,\"patch\":0}}", + ], + tags = [name], + ) + + # Verify doccarchive bundle is created for Swift iOS framework which includes a .docc bundle. + archive_contents_test( + name = "{}_contains_doccarchive_with_docc_bundle_when_ios_framework".format(name), + build_type = "simulator", + target_under_test = "//test/starlark_tests/targets_under_test/ios:basic_framework_with_docc_bundle.doccarchive", + contains = [ + "$BUNDLE_ROOT/index.html", + "$BUNDLE_ROOT/documentation/basicframework/readme/index.html", + ], + text_file_not_contains = [], + text_test_file = "$BUNDLE_ROOT/metadata.json", + text_test_values = [ + "{\"bundleDisplayName\":\"BasicFramework\",\"bundleIdentifier\":\"com.google.example.framework\",\"schemaVersion\":{\"major\":0,\"minor\":1,\"patch\":0}}", + ], + tags = [name], + ) + + native.test_suite( + name = name, + tags = [name], + ) diff --git a/test/starlark_tests/resources/BUILD b/test/starlark_tests/resources/BUILD index c21efc8d3b..4cfcf84e77 100644 --- a/test/starlark_tests/resources/BUILD +++ b/test/starlark_tests/resources/BUILD @@ -511,6 +511,11 @@ filegroup( srcs = glob(["basic.bundle/**"]), ) +filegroup( + name = "basic_docc_bundle_files", + srcs = ["basic.docc"], +) + filegroup( name = "localized_storyboards_ios", srcs = glob(["*.lproj/*.storyboard"]), diff --git a/test/starlark_tests/resources/basic.docc/README.md b/test/starlark_tests/resources/basic.docc/README.md new file mode 100644 index 0000000000..23e10d98b7 --- /dev/null +++ b/test/starlark_tests/resources/basic.docc/README.md @@ -0,0 +1,3 @@ +# Basic DocC Bundle + +A basic DocC bundle with a single article. diff --git a/test/starlark_tests/targets_under_test/ios/BUILD b/test/starlark_tests/targets_under_test/ios/BUILD index a37870ff5a..8b72a2fca3 100644 --- a/test/starlark_tests/targets_under_test/ios/BUILD +++ b/test/starlark_tests/targets_under_test/ios/BUILD @@ -36,6 +36,10 @@ load( "//test/starlark_tests:common.bzl", "common", ) +load( + "//apple:docc.bzl", + "docc_archive", +) load( "//apple:xcarchive.bzl", "xcarchive", @@ -627,6 +631,15 @@ ios_application( ], ) +docc_archive( + name = "app_with_swift_dep.doccarchive", + dep = ":app_with_swift_dep", + fallback_bundle_identifier = "com.google.example", + fallback_bundle_version = "1.0.0", + fallback_display_name = "app_with_swift_dep", + tags = common.fixture_tags, +) + ios_static_framework( name = "swift_static_framework", bundle_name = "swift_framework_lib", @@ -4785,3 +4798,41 @@ ios_application( "//test/starlark_tests/resources:objc_main_lib", ], ) + +# --------------------------------------------------------------------------------------- +# Targets for testing DocC support. + +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", + tags = common.fixture_tags, + visibility = ["//visibility:public"], +) + +ios_dynamic_framework( + name = "basic_framework_with_docc_bundle", + bundle_id = "com.google.example.framework", + bundle_name = "BasicFramework", + families = [ + "iphone", + "ipad", + ], + infoplists = [ + "//test/starlark_tests/resources:Info.plist", + ], + minimum_os_version = common.min_os_ios.baseline, + tags = common.fixture_tags, + deps = [ + ":basic_framework_lib_with_docc_bundle", + ], +) + +docc_archive( + name = "basic_framework_with_docc_bundle.doccarchive", + dep = ":basic_framework_with_docc_bundle", + fallback_bundle_identifier = "com.google.example.framework", + fallback_bundle_version = "1.0", + fallback_display_name = "BasicFramework", +) diff --git a/test/starlark_tests/verifier_scripts/archive_contents_test.sh b/test/starlark_tests/verifier_scripts/archive_contents_test.sh index 3feed35ce1..d104d4faa5 100644 --- a/test/starlark_tests/verifier_scripts/archive_contents_test.sh +++ b/test/starlark_tests/verifier_scripts/archive_contents_test.sh @@ -123,7 +123,8 @@ if [[ -n "${TEXT_TEST_FILE-}" ]]; then "contents of text file at \"$path\"" fi done - for test_regexp in "${TEXT_FILE_NOT_CONTAINS[@]}" + text_file_not_contains=${TEXT_FILE_NOT_CONTAINS:-()} + for test_regexp in "${text_file_not_contains[@]}" do something_tested=true if grep -q "$test_regexp" "$path"