diff --git a/CHANGELOG.md b/CHANGELOG.md index 24c115254..a57d50c8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ * Support using pre-generated symbolgraph files in Swift symbolgraph mode. [Nathan Wong](https://github.com/esteluk) +* Issue a warning on some combinations of Objective-C flags. + [John Fairhurst](https://github.com/johnfairh) + [#900](https://github.com/realm/jazzy/issues/900) + ##### Bug Fixes * In Swift symbolgraph mode, stop including extensions to types that are beneath diff --git a/ObjectiveC.md b/ObjectiveC.md new file mode 100644 index 000000000..3527ced01 --- /dev/null +++ b/ObjectiveC.md @@ -0,0 +1,208 @@ +This document is about Jazzy's Objective-C documentation generation. + +It's intended for users who are having problems after trying to follow the +examples in the [README](README.md#objective-c). It gives some solutions to +common problems and explains how the system works to help users work through +uncommon problems. + +* [How it works](#how-objective-c-docs-generation-works) +* Common problems: + * [Apple SDK include failure](#problem-apple-sdk-importinclude-failure) + * [Non-SDK include failure](#problem-non-sdk-include-failure) + * [Argument list too long](#problem-argument-list-too-long-e2big-and-more) + * [Enum cases with wrong doc comment](#problem-enum-cases-have-the-wrong-doc-comment) + * [Swift API versions missing](#problem-swift-api-versions-are-all-missing) + * [Swift API versions use `Any`](#problem-swift-api-versions-have-any-instead-of-type-name) + * [Structural `NS_SWIFT_NAME` not working](#problem-structural-ns_swift_name-not-working) + +# How Objective-C docs generation works + +Jazzy uses `libclang` to generate docs for Objective-C projects. You can think +of this as running some parts of the `clang` compiler against a header file. +Jazzy refers to this header file as the _umbrella header_ but it does not have +to be the umbrella header from a Clang module: it's just the header file that +includes everything to be documented. + +This means there are two problems to solve: +1. What `clang` flags are required; and +2. How to pass them to the tools. + +Jazzy has two modes here: a smart mode that covers 90% of projects and a direct mode where the user provides all the flags. + +> *Important*: Jazzy does _not_ use any Objective-C build settings from your + Xcode project or `Package.swift`. If your project needs special settings + such as `#define`s then you need to repeat those in the Jazzy invocation. + +## Direct mode + +Passing a basic set of `clang` flags looks like this: + +```shell +jazzy ... + --objc + --build-tool-arguments + --objc,MyProject/MyProject.h,--,-x,objective-c, + -isysroot,$(xcrun --show-sdk-path), + -I,$(pwd), + -fmodules +``` +The `--build-tool-arguments` are arguments to `sourcekitten`. Everything after +the `--` are the `clang` flags that will be used with the header file given +before the `--`. + +You can try these flags outside of Jazzy's environment: +```shell +clang -c -x objective-c -isysroot $(xcrun --show-sdk-path) -I $(pwd) MyProject/MyProject.h -fmodules +``` +(The `-c` stops `clang` from trying to link an executable.) + +This is a good method of experimenting with compiler flags to get a working +build without getting bogged down in the Jazzy and SourceKitten layers. + +## Smart mode + +The smart mode takes the variable parts of the basic set of flags and maps +them from Jazzy flags: +```shell +jazzy ... + --objc + --umbrella-header MyProject/MyProject.h + --framework-root $(pwd) + [--sdk ] +``` + +The `--umbrella-header` maps directly to the file passed to `sourcekitten`. + +The `--framework-root` is used for the `-I` include path, as well as every +directory beneath it, recursively. So if your project structure looks like: +``` +MyProject/ + Sources/ + Main/ + Extension/ +``` +... and you pass `--framework-root MyProject`, then the `-I` flags passed to +`clang` are `-I MyProject -I MyProject/Sources -I MyProject/Sources/Main -I +MyProject/Sources/Extension`. This feature helps some projects resolve +`#include` directives. + +Finally the `--sdk` option is passed through instead of the default `macosx` to +find the SDK. + +## Mixing modes + +Do not mix modes. For example do not set both `--umbrella-header` and +`--build-tool-arguments`. Jazzy does not flag this as an error for +historical compatibility reasons, but the results are at best confusing. + +# Problem: Apple SDK import/include failure + +For example `fatal error: module 'UIKit' not found`. + +This means Jazzy is using the wrong SDK: the default is for macOS which does +not have `UIKit`. Use `--sdk iphonesimulator`. + +# Problem: Non-SDK include failure + +For example `fatal error: 'MyModule/SomeHeader.h' file not found`. + +This means `clang` is not being passed the right flags to resolve a `#include` / +`#import`. + +Start by finding the header file in the filesystem. You might be able to fix +the problem just by adding extra `-I ` flags. + +Usually though the problem is that Xcode has done something clever that needs +to be worked around or replicated. + +Xcode uses technology including Clang header maps to let files be found using +lines like `#import ` even when there is no such +filesystem directory. + +To make the Jazzy build work you need to make these `#include`s work. The +solution depends on your project structure. Some suggestions in rough order +of complexity: +* Use symlinks to create an equivalent directory tree. For example if + `Header.h` is inside `Sources/include` then symlink that directory to + `ModuleName` and pass `-I $(pwd)`. + +* Copy/link your header files into a throwaway directory tree that matches + the required structure and is used just for building docs. + +* Create a 'docs header file' separate to the framework's regular umbrella + header that contains only filesystem-correct `#import`s. + +* If you are happy to build the framework project before generating docs and + all the problematic paths have the form `ModuleName/PublicHeader.h` then + have `clang` resolve those includes to the built framework by passing + `-F `. + +* If you are happy to build the project before generating docs then you may + be able to use the header maps Xcode has generated. Check the build log in + Xcode to find them and the invocation syntax. + +* Manually construct an equivalent header map. This is complex not least + because Apple does not make tools or proper documentation available. + [An open-source starting point](https://milen.me/writings/swift-module-maps-vfs-overlays-header-maps/). + +# Problem: Argument list too long `E2BIG` (and more...) + +For example ``...open4.rb:49:in `exec': Argument list too long - [...]/bin/sourcekitten (Errno::E2BIG)`` + +Can also manifest as 'generally weird' errors such as `sourcekitten` just +crashing and `fatal error: could not build module 'Foundation'`. + +This means `--framework-root` is trying to add too many include directories: +there are too many subdirectories of its parameter. If you cannot change this +to something more specific that works then you need to use Jazzy's +[direct mode](#direct-mode) to pass in the correct directories. + +# Problem: Enum cases have the wrong doc comment + +If you write an enum case with a doc comment followed by an enum case without +a doc comment, then both get the same doc comment. + +This seems to be a bug in `libclang`. The only workaround is to add the missing +doc comment. + +# Problem: Swift API versions are all missing + +This usually means the `clang` flags are malformed in a way that is ignored by +`libclang` but not by the Swift Objective-C importer. + +One easy way to accidentally do this is passing `-I` without a path, for +example `--build-tool-flags ...,-I,-I,Headers`,.... + +This also sometimes happens if you are frequently switching back and forth +between some Swift / Xcode versions -- it's a bug somewhere in the Apple tools. +The bad state goes away with time / reboot. + +# Problem: Swift API versions have `Any` instead of type name + +Jazzy finds the Swift version of an Objective-C API using the SourceKit +`source.request.editor.open.interface.header` request on the header file that +contains the declaration, passing in the same `clang` flags used for the +umbrella header. [See the code](https://github.com/jpsim/SourceKitten/blob/bed112c313ca8c3c149f8cb84069f1c080e86a7e/Source/SourceKittenFramework/Clang%2BSourceKitten.swift#L202). + +This means that each header file needs to compile standalone, without +any implicit dependencies. For example: +``` + MyModule.h // umbrella, includes MyClass.h then Utils.h + MyClass.h // @interface MyClass ... @end + Utils.h // void my_function( MyClass * myClass); +``` +Here, `Utils.h` has an implicit dependency on `MyClass.h` that is normally +satisfied by the include order of `MyModule.h`. One fix that allows `Utils.h` +to compile standalone is to add `@class MyClass;`. + +# Problem: Structural `NS_SWIFT_NAME` not working + +The `NS_SWIFT_NAME` macro is mostly used to give an Objective-C API a +different name in Swift. There are no known problems with this part. + +The macro can also be used to change the 'structure' of an API, for example +make a C global function appear as a member function in Swift, or make a C +class appear as a nested type in Swift. + +Jazzy doesn't understand or communicate these structural changes: you'll need +to explain it in doc comments. diff --git a/README.md b/README.md index cc0589825..1b854964d 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ Here are some resources with tutorials and examples, starting with the most mode * Erica Sadun's [Swift header documentation in Xcode 7](https://ericasadun.com/2015/06/14/swift-header-documentation-in-xcode-7/) post and her [book on Swift Documentation Markup](https://itunes.apple.com/us/book/swift-documentation-markup/id1049010423). For Objective-C documentation the same keywords are supported, but note that the format -is slightly different. In Swift you would write `- returns:`, but in Objective-C you write `@return`. See Apple's [*HeaderDoc User Guide*](https://developer.apple.com/legacy/library/documentation/DeveloperTools/Conceptual/HeaderDoc/tags/tags.html) for more details. **Note: `jazzy` currently does not support _all_ Objective-C keywords listed in this document, only @param, @return, @warning, @see, and @note.** +is slightly different. In Swift you would write `- returns:`, but in Objective-C you write `@return`. See Apple's [*HeaderDoc User Guide*](https://developer.apple.com/legacy/library/documentation/DeveloperTools/Conceptual/HeaderDoc/tags/tags.html) for more details. **Note: `jazzy` currently does not support _all_ Objective-C keywords listed in this document, only @param, @return, @warning, @see, @note, @code, @endcode, and @c.** Jazzy can also generate cross-references within your documentation. A symbol name in backticks generates a link, for example: @@ -139,20 +139,36 @@ jazzy \ ### Objective-C -To generate documentation for Objective-C headers, you must pass the following -parameters to jazzy: - +To generate documentation for a simple Objective-C project, you must pass the +following parameters: * `--objc` * `--umbrella-header ...` * `--framework-root ...` -* `--sdk [iphone|watch|appletv][os|simulator]|macosx` (optional, default value + +...and optionally: +* `--sdk [iphone|watch|appletv][os|simulator]|macosx` (default value of `macosx`) -* `--hide-declarations [objc|swift]` (optional, hides the selected language - declarations) +* `--hide-declarations [objc|swift]` (hides the selected language declarations) -##### Example +For example, this is how the `AFNetworking` docs are generated: + +```shell +jazzy \ + --objc \ + --author AFNetworking \ + --author_url http://afnetworking.com \ + --source-host github \ + --source-host-url https://github.com/AFNetworking/AFNetworking \ + --source-host-files-url https://github.com/AFNetworking/AFNetworking/tree/2.6.2 \ + --module-version 2.6.2 \ + --umbrella-header AFNetworking/AFNetworking.h \ + --framework-root . \ + --module AFNetworking +``` -This is how Realm Objective-C docs are generated: +For a more complicated Objective-C project, instead use `--build-tool-arguments` +to pass arbitrary compiler flags. For example, this is how Realm Objective-C +docs are generated: ```shell jazzy \ @@ -171,21 +187,8 @@ jazzy \ --head "$(cat docs/custom_head.html)" ``` -This is how the AFNetworking docs are generated: - -```shell -jazzy \ - --objc \ - --author AFNetworking \ - --author_url http://afnetworking.com \ - --source-host github \ - --source-host-url https://github.com/AFNetworking/AFNetworking \ - --source-host-files-url https://github.com/AFNetworking/AFNetworking/tree/2.6.2 \ - --module-version 2.6.2 \ - --umbrella-header AFNetworking/AFNetworking.h \ - --framework-root . \ - --module AFNetworking -``` +See [the Objective-C docs](ObjectiveC.md) for more information and some tips +on troubleshooting. ### Mixed Objective-C / Swift @@ -416,6 +419,10 @@ Check the `--min-acl` setting -- see [above](#controlling-what-is-documented). environment variable to point to the Xcode you want before running Jazzy without the `--swift-version` flag. +### Objective-C + +See [this document](ObjectiveC.md). + ### Installation Problems **Can't find header files / clang** diff --git a/lib/jazzy/config.rb b/lib/jazzy/config.rb index 759c2e776..a000c236b 100644 --- a/lib/jazzy/config.rb +++ b/lib/jazzy/config.rb @@ -597,6 +597,13 @@ def validate warning 'Option `source_host` is set but has no effect without either '\ '`source_host_url` or `source_host_files_url`.' end + + if objc_mode && + build_tool_arguments_configured && + (framework_root_configured || umbrella_header_configured) + warning 'Option `build_tool_arguments` is set: values passed to '\ + '`framework_root` or `umbrella_header` may be ignored.' + end end # rubocop:enable Metrics/MethodLength