Clang support for Objective-C symbol graph generation

Hey everyone! I landed preliminary support for symbol graph generation in Clang for C/Objective-C projects in the Swift 5.7 Development toolchain. Symbol graph is the JSON file format emitted by the Swift compiler and consumed by documentation tools like DocC that describes the APIs exposed by a project along with the associated documentation comments. More details about symbol graph can be found at https://github.com/apple/swift-docc-symbolkit.

Trying out Objective-C symbol graph generation

To try out this preliminary support you will need to download a Swift 5.7 Development Snapshot toolchain released after May 18th 2022 as available at https://www.swift.org/download/. I have also provided a sample Objective-C framework available here. To simply see the end result you can just invoke the ./preview-docs script provided in the sample code.

If you are interested in the details here is a sample Clang invocation to generate symbol graph for a project called MyProject:

xcrun --toolchain swift clang                        \ # Alternatively just use the path to clang in the toolchain
     -extract-api                                    \
     --product-name=MyProject                        \ # Name of the project
     -o .build/symbol-graphs/MyProject.symbols.json  \ # Output path
     -isysroot $SDK_PATH                             \ # Path to the SDK as returned by `xcrun --show-sdk-path` 
     -F $SDK_PATH/System/Library/Frameworks          \ # System frameworks
     $HEADER_SEARCH_ARGS                             \ # Additional header search arguments needed by your project
     -x objective-c-header                           \
     MyProject/MyProject.h                           \ # Umbrella header if your project has one
     MyProject/A.h                                   \ # List of public headers in your project
     MyProject/B.h

This command would generate the symbol graph saved as MyProject.symbols.json in the build/symbol-graphs directory.

You can then generate a DocC archive or preview the docs in the usual way and making sure you provide the directory in which the symbol graph for your project was generated using the --additional-symbol-graph-dir argument. For example to preview the docs for MyProject you can use the following command:

xcrun --toolchain swift docc preview MyProject/MyProject.docc      \
    --fallback-display-name MyProject                              \
    --fallback-bundle-identifier org.swift.MyProject               \
    --fallback-bundle-version 0.0.1                                \
    --additional-symbol-graph-dir ./build/symbol-graphs

This command will start a local web server on port 8000. You can preview the documentation for your project by accessing http://localhost:8000/documentation/MyProject in your web browser.

SwiftPM integration

This is just the beginning of documentation tooling support for Objective-C in the Swift ecosystem. Now that it is technically possible to generate symbol graph files for Objective-C projects using open source tooling, I am looking at refining the end user experience. The aim is to provide seamless integration with existing Swift Package Manager documentation plugins (including the Swift-DocC Plugin) by enhancing the PackageManager.getSymbolGraph(for:options:) method introduced with SE-0332 to also return symbol graphs for Objective-C targets.

12 Likes

This is super exciting :D How difficult might it be to get something like this wired into a build system like bazel? From poking around in https://github.com/apple/llvm-project/pull/4442, it looks like the main thing that would need to be wired up is the new -extract-api flag and then the output aggregated and provided to the docc command?

Yes that's right, it's built for being integrated with build systems! Assuming you have a capable toolchain, you would need to create a clang invocation for your documentation target that passes clang the -extract-api flag as well as the --product-name flag and all your documentation target's public headers starting with the umbrella header as outlined above. Clang does all the hard work of aggregating all the APIs present in the provided headers. You would then pass the directory containing the symbol graph to the docc command.
If you are interested in generating documentation for a target that contains both Swift and Objective-C code you would just need to generate the symbol graph generated by swiftc in the same place as the one generated by clang and it should just work.

1 Like

Awesome; thank you!

Hi, I download the project from this link .
Then I try this clang invocation

➜  SampleObjCFramework clang  \                                     
-extract-api \
--product-name=SampleObjCFramework \
-o ./build/symbol-graphs/SampleObjCFramework.symbols.json \
-F $SDK_PATH/System/Library/Frameworks \
$HEADER_SEARCH_ARGS \
-x objective-c-header \
SampleObjCFramework/ImmutableRectangle.h \
SampleObjCFramework/SampleObjCFramework.h \
SampleObjCFramework/ShapeCollection.h \
SampleObjCFramework/Triangle.h \
SampleObjCFramework/TwoDimensionalShape.h

It throws a Error

<extract-api-includes>:1:9: fatal error: 'SampleObjCFramework/ImmutableRectangle.h' file not found
#import "SampleObjCFramework/ImmutableRectangle.h"
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.

This file is still in the appropriate location.

Here is my clang version

Apple clang version 14.0.0 (clang-1400.0.29.202)
Target: arm64-apple-darwin22.4.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin