Skip to content

Commit 0752acf

Browse files
[SR-15434] Add support for custom (non-framework) module kinds (#33)
* Add option to provide a default module kind Resolves rdar://83448323 and SR-15434. * Update DocC's user documentation with custom module kind In this context, DocC is a "Tool" not a "Framework".
1 parent f1543f0 commit 0752acf

File tree

9 files changed

+260
-1
lines changed

9 files changed

+260
-1
lines changed

Sources/DocCDocumentation/DocCDocumentation.docc/Info.plist

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,7 @@
2222
<string>0.1.0</string>
2323
<key>CFBundleVersion</key>
2424
<string>0.1.0</string>
25+
<key>CDDefaultModuleKind</key>
26+
<string>Tool</string>
2527
</dict>
2628
</plist>

Sources/SwiftDocC/Infrastructure/DocumentationContext.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1096,13 +1096,17 @@ public class DocumentationContext: DocumentationContextDataProviderDelegate {
10961096

10971097
// Create a module symbol
10981098
let moduleIdentifier = SymbolGraph.Symbol.Identifier(precise: moduleName, interfaceLanguage: SourceLanguage.swift.id)
1099+
1100+
// Use the default module kind for this bundle if one was provided,
1101+
// otherwise fall back to 'Framework'
1102+
let moduleKindDisplayName = bundle.info.defaultModuleKind ?? "Framework"
10991103
let moduleSymbol = SymbolGraph.Symbol(
11001104
identifier: moduleIdentifier,
11011105
names: SymbolGraph.Symbol.Names(title: moduleName, navigator: nil, subHeading: nil, prose: nil),
11021106
pathComponents: [moduleName],
11031107
docComment: nil,
11041108
accessLevel: SymbolGraph.Symbol.AccessControl(rawValue: "public"),
1105-
kind: SymbolGraph.Symbol.Kind(parsedIdentifier: .module, displayName: "Framework"),
1109+
kind: SymbolGraph.Symbol.Kind(parsedIdentifier: .module, displayName: moduleKindDisplayName),
11061110
mixins: [:])
11071111
let moduleSymbolReference = SymbolReference(moduleName, interfaceLanguage: .swift, symbol: moduleSymbol)
11081112
moduleReference = ResolvedTopicReference(symbolReference: moduleSymbolReference, moduleName: moduleName, bundle: bundle)

Sources/SwiftDocC/Infrastructure/Workspace/DocumentationBundle+Info.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ extension DocumentationBundle {
3030
/// The default availability for the various modules in the bundle.
3131
public var defaultAvailability: DefaultAvailability?
3232

33+
/// The default kind for the various modules in the bundle.
34+
public var defaultModuleKind: String?
35+
3336
/// The keys that must be present in an Info.plist file in order for doc compilation to proceed.
3437
static let requiredKeys: Set<CodingKeys> = [.displayName, .identifier, .version]
3538

@@ -39,6 +42,7 @@ extension DocumentationBundle {
3942
case version = "CFBundleVersion"
4043
case defaultCodeListingLanguage = "CDDefaultCodeListingLanguage"
4144
case defaultAvailability = "CDAppleDefaultAvailability"
45+
case defaultModuleKind = "CDDefaultModuleKind"
4246

4347
var argumentName: String? {
4448
switch self {
@@ -50,6 +54,8 @@ extension DocumentationBundle {
5054
return "--fallback-bundle-version"
5155
case .defaultCodeListingLanguage:
5256
return "--default-code-listing-language"
57+
case .defaultModuleKind:
58+
return "--fallback-default-module-kind"
5359
case .defaultAvailability:
5460
return nil
5561
}
@@ -161,6 +167,7 @@ extension DocumentationBundle {
161167
// Finally, decode the optional keys if they're present.
162168

163169
self.defaultCodeListingLanguage = try decodeOrFallbackIfPresent(String.self, with: .defaultCodeListingLanguage)
170+
self.defaultModuleKind = try decodeOrFallbackIfPresent(String.self, with: .defaultModuleKind)
164171
self.defaultAvailability = try decodeOrFallbackIfPresent(DefaultAvailability.self, with: .defaultAvailability)
165172
}
166173

@@ -169,12 +176,14 @@ extension DocumentationBundle {
169176
identifier: String,
170177
version: Version,
171178
defaultCodeListingLanguage: String? = nil,
179+
defaultModuleKind: String? = nil,
172180
defaultAvailability: DefaultAvailability? = nil
173181
) {
174182
self.displayName = displayName
175183
self.identifier = identifier
176184
self.version = version
177185
self.defaultCodeListingLanguage = defaultCodeListingLanguage
186+
self.defaultModuleKind = defaultModuleKind
178187
self.defaultAvailability = defaultAvailability
179188
}
180189
}
@@ -198,6 +207,7 @@ extension BundleDiscoveryOptions {
198207
fallbackIdentifier: String? = nil,
199208
fallbackVersion: String? = nil,
200209
fallbackDefaultCodeListingLanguage: String? = nil,
210+
fallbackDefaultModuleKind: String? = nil,
201211
fallbackDefaultAvailability: DefaultAvailability? = nil,
202212
additionalSymbolGraphFiles: [URL] = []
203213
) {
@@ -220,6 +230,8 @@ extension BundleDiscoveryOptions {
220230
value = fallbackDefaultCodeListingLanguage
221231
case .defaultAvailability:
222232
value = fallbackDefaultAvailability
233+
case .defaultModuleKind:
234+
value = fallbackDefaultModuleKind
223235
}
224236

225237
guard let unwrappedValue = value else {

Sources/SwiftDocCUtilities/ArgumentParsing/ActionExtensions/ConvertAction+CommandInitialization.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ extension ConvertAction {
4949
fallbackIdentifier: convert.fallbackBundleIdentifier,
5050
fallbackVersion: convert.fallbackBundleVersion,
5151
fallbackDefaultCodeListingLanguage: convert.defaultCodeListingLanguage,
52+
fallbackDefaultModuleKind: convert.fallbackDefaultModuleKind,
5253
additionalSymbolGraphFiles: additionalSymbolGraphFiles
5354
)
5455

Sources/SwiftDocCUtilities/ArgumentParsing/Subcommands/Convert.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,14 @@ extension Docc {
155155
help: "A fallback default language for code listings if no value is provided in the documentation bundle's Info.plist file."
156156
)
157157
public var defaultCodeListingLanguage: String?
158+
159+
@Option(
160+
help: """
161+
A fallback default module kind if no value is provided \
162+
in the documentation bundle's Info.plist file.
163+
"""
164+
)
165+
public var fallbackDefaultModuleKind: String?
158166

159167
/// A user-provided location where the convert action writes the built documentation.
160168
@Option(

Tests/SwiftDocCTests/Infrastructure/DocumentationBundleInfoTests.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ class DocumentationBundleInfoTests: XCTestCase {
159159
</dict>
160160
<key>CDDefaultCodeListingLanguage</key>
161161
<string>swift</string>
162+
<key>CDDefaultModuleKind</key>
163+
<string>Executable</string>
162164
<key>CFBundleDisplayName</key>
163165
<string>ShapeKit</string>
164166
<key>CFBundleIdentifier</key>
@@ -196,6 +198,7 @@ class DocumentationBundleInfoTests: XCTestCase {
196198
fallbackIdentifier: "swift.org.Identifier",
197199
fallbackVersion: "1.0.0",
198200
fallbackDefaultCodeListingLanguage: "swift",
201+
fallbackDefaultModuleKind: "Executable",
199202
fallbackDefaultAvailability: DefaultAvailability(
200203
with: [
201204
"MyModule": [
@@ -216,6 +219,7 @@ class DocumentationBundleInfoTests: XCTestCase {
216219
identifier: "swift.org.Identifier",
217220
version: Version(arrayLiteral: 1,0,0),
218221
defaultCodeListingLanguage: "swift",
222+
defaultModuleKind: "Executable",
219223
defaultAvailability: DefaultAvailability(
220224
with: [
221225
"MyModule": [
@@ -236,6 +240,7 @@ class DocumentationBundleInfoTests: XCTestCase {
236240
identifier: "swift.org.Identifier",
237241
version: Version(arrayLiteral: 1,0,0),
238242
defaultCodeListingLanguage: "swift",
243+
defaultModuleKind: "Executable",
239244
defaultAvailability: DefaultAvailability(
240245
with: [
241246
"MyModule": [

Tests/SwiftDocCTests/Infrastructure/DocumentationContextTests.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2972,6 +2972,15 @@ let expected = """
29722972
// Verify the solution proposes the expected absolute link replacement.
29732973
XCTAssertEqual(problem.possibleSolutions[0].replacements[0].replacement, "<doc:/documentation/Minimal_docs/A/method(_:)-7mctk>")
29742974
}
2975+
2976+
func testCustomModuleKind() throws {
2977+
let (bundle, context) = try testBundleAndContext(named: "BundleWithExecutableModuleKind")
2978+
XCTAssertEqual(bundle.info.defaultModuleKind, "Executable")
2979+
2980+
let moduleSymbol = try XCTUnwrap(context.symbolIndex["ExampleDocumentedExecutable"]?.symbol)
2981+
XCTAssertEqual(moduleSymbol.kind.identifier.identifier, "module")
2982+
XCTAssertEqual(moduleSymbol.kind.displayName, "Executable")
2983+
}
29752984
}
29762985

29772986
func assertEqualDumps(_ lhs: String, _ rhs: String, file: StaticString = #file, line: UInt = #line) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
{
2+
"metadata": {
3+
"formatVersion": {
4+
"major": 0,
5+
"minor": 5,
6+
"patch": 3
7+
},
8+
"generator": "Apple Swift version 5.5 (swiftlang-1300.0.29.1 clang-1300.0.28.1)"
9+
},
10+
"module": {
11+
"name": "ExampleDocumentedExecutable",
12+
"platform": {
13+
"architecture": "arm64",
14+
"vendor": "apple",
15+
"operatingSystem": {
16+
"name": "macosx",
17+
"minimumVersion": {
18+
"major": 10,
19+
"minor": 10,
20+
"patch": 0
21+
}
22+
}
23+
}
24+
},
25+
"symbols": [
26+
{
27+
"kind": {
28+
"identifier": "swift.struct",
29+
"displayName": "Structure"
30+
},
31+
"identifier": {
32+
"precise": "s:27ExampleDocumentedExecutableAAV",
33+
"interfaceLanguage": "swift"
34+
},
35+
"pathComponents": [
36+
"ExampleDocumentedExecutable"
37+
],
38+
"names": {
39+
"title": "ExampleDocumentedExecutable",
40+
"navigator": [
41+
{
42+
"kind": "identifier",
43+
"spelling": "ExampleDocumentedExecutable"
44+
}
45+
],
46+
"subHeading": [
47+
{
48+
"kind": "keyword",
49+
"spelling": "struct"
50+
},
51+
{
52+
"kind": "text",
53+
"spelling": " "
54+
},
55+
{
56+
"kind": "identifier",
57+
"spelling": "ExampleDocumentedExecutable"
58+
}
59+
]
60+
},
61+
"docComment": {
62+
"lines": [
63+
{
64+
"range": {
65+
"start": {
66+
"line": 0,
67+
"character": 4
68+
},
69+
"end": {
70+
"line": 0,
71+
"character": 55
72+
}
73+
},
74+
"text": "This is a description of what this executable does."
75+
}
76+
]
77+
},
78+
"declarationFragments": [
79+
{
80+
"kind": "attribute",
81+
"spelling": "@main"
82+
},
83+
{
84+
"kind": "text",
85+
"spelling": " "
86+
},
87+
{
88+
"kind": "keyword",
89+
"spelling": "struct"
90+
},
91+
{
92+
"kind": "text",
93+
"spelling": " "
94+
},
95+
{
96+
"kind": "identifier",
97+
"spelling": "ExampleDocumentedExecutable"
98+
}
99+
],
100+
"accessLevel": "internal",
101+
"location": {
102+
"uri": "file:///Users/demo/Downloads/ExampleDocumentedExecutable/Sources/ExampleDocumentedExecutable/ExampleDocumentedExecutable.swift",
103+
"position": {
104+
"line": 2,
105+
"character": 14
106+
}
107+
}
108+
},
109+
{
110+
"kind": {
111+
"identifier": "swift.type.method",
112+
"displayName": "Type Method"
113+
},
114+
"identifier": {
115+
"precise": "s:27ExampleDocumentedExecutableAAV4mainyyFZ",
116+
"interfaceLanguage": "swift"
117+
},
118+
"pathComponents": [
119+
"ExampleDocumentedExecutable",
120+
"main()"
121+
],
122+
"names": {
123+
"title": "main()",
124+
"subHeading": [
125+
{
126+
"kind": "keyword",
127+
"spelling": "static"
128+
},
129+
{
130+
"kind": "text",
131+
"spelling": " "
132+
},
133+
{
134+
"kind": "keyword",
135+
"spelling": "func"
136+
},
137+
{
138+
"kind": "text",
139+
"spelling": " "
140+
},
141+
{
142+
"kind": "identifier",
143+
"spelling": "main"
144+
},
145+
{
146+
"kind": "text",
147+
"spelling": "()"
148+
}
149+
]
150+
},
151+
"functionSignature": {
152+
"returns": [
153+
{
154+
"kind": "text",
155+
"spelling": "()"
156+
}
157+
]
158+
},
159+
"declarationFragments": [
160+
{
161+
"kind": "keyword",
162+
"spelling": "static"
163+
},
164+
{
165+
"kind": "text",
166+
"spelling": " "
167+
},
168+
{
169+
"kind": "keyword",
170+
"spelling": "func"
171+
},
172+
{
173+
"kind": "text",
174+
"spelling": " "
175+
},
176+
{
177+
"kind": "identifier",
178+
"spelling": "main"
179+
},
180+
{
181+
"kind": "text",
182+
"spelling": "()"
183+
}
184+
],
185+
"accessLevel": "internal",
186+
"location": {
187+
"uri": "file:///Users/username/Downloads/ExampleDocumentedExecutable/Sources/ExampleDocumentedExecutable/ExampleDocumentedExecutable.swift",
188+
"position": {
189+
"line": 3,
190+
"character": 23
191+
}
192+
}
193+
}
194+
],
195+
"relationships": [
196+
{
197+
"kind": "memberOf",
198+
"source": "s:27ExampleDocumentedExecutableAAV4mainyyFZ",
199+
"target": "s:27ExampleDocumentedExecutableAAV"
200+
}
201+
]
202+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>CFBundleName</key>
6+
<string>ExampleDocumentedExecutable</string>
7+
<key>CFBundleDisplayName</key>
8+
<string>My Executable</string>
9+
<key>CFBundleIdentifier</key>
10+
<string>org.swift.Executable</string>
11+
<key>CFBundleVersion</key>
12+
<string>0.1.0</string>
13+
<key>CDDefaultModuleKind</key>
14+
<string>Executable</string>
15+
</dict>
16+
</plist>

0 commit comments

Comments
 (0)