diff --git a/Sources/SWBCore/Settings/BuiltinMacros.swift b/Sources/SWBCore/Settings/BuiltinMacros.swift index 48dcf3ce..bef9bc65 100644 --- a/Sources/SWBCore/Settings/BuiltinMacros.swift +++ b/Sources/SWBCore/Settings/BuiltinMacros.swift @@ -1099,6 +1099,7 @@ public final class BuiltinMacros { public static let DOCC_ARCHIVE_PATH = BuiltinMacros.declareStringMacro("DOCC_ARCHIVE_PATH") public static let DOCC_PRETTY_PRINT = BuiltinMacros.declareBooleanMacro("DOCC_PRETTY_PRINT") public static let DOCC_EXTRACT_SPI_DOCUMENTATION = BuiltinMacros.declareBooleanMacro("DOCC_EXTRACT_SPI_DOCUMENTATION") + public static let DOCC_MINIMUM_ACCESS_LEVEL = BuiltinMacros.declareEnumMacro("DOCC_MINIMUM_ACCESS_LEVEL") as EnumMacroDeclaration public static let DOCC_SKIP_SYNTHESIZED_MEMBERS = BuiltinMacros.declareBooleanMacro("DOCC_SKIP_SYNTHESIZED_MEMBERS") public static let DOCC_EXTRACT_EXTENSION_SYMBOLS = BuiltinMacros.declareBooleanMacro("DOCC_EXTRACT_EXTENSION_SYMBOLS") public static let DOCC_EXTRACT_SWIFT_INFO_FOR_OBJC_SYMBOLS = BuiltinMacros.declareBooleanMacro("DOCC_EXTRACT_SWIFT_INFO_FOR_OBJC_SYMBOLS") @@ -1650,6 +1651,7 @@ public final class BuiltinMacros { DOCC_ARCHIVE_PATH, DOCC_PRETTY_PRINT, DOCC_SKIP_SYNTHESIZED_MEMBERS, + DOCC_MINIMUM_ACCESS_LEVEL, DOCC_EXTRACT_SPI_DOCUMENTATION, DOCC_EXTRACT_EXTENSION_SYMBOLS, DOCC_EXTRACT_SWIFT_INFO_FOR_OBJC_SYMBOLS, @@ -2757,6 +2759,18 @@ public enum LinkerDriverChoice: String, Equatable, Hashable, EnumerationMacroTyp case auto } +public enum DoccMinimumAccessLevel: String, Equatable, Hashable, EnumerationMacroType { + public static let defaultValue = DoccMinimumAccessLevel.none + + case none = "" + case `private` = "private" + case `fileprivate` = "fileprivate" + case `internal` = "internal" + case `package` = "package" + case `public` = "public" + case `open` = "open" +} + /// Enumeration macro type for the value of the `INFOPLIST_KEY_LSApplicationCategoryType` build setting. public enum ApplicationCategory: String, Equatable, Hashable, EnumerationMacroType { public static let defaultValue = ApplicationCategory.none diff --git a/Sources/SWBCore/SpecImplementations/PropertyDomainSpec.swift b/Sources/SWBCore/SpecImplementations/PropertyDomainSpec.swift index efbb1618..bfbff393 100644 --- a/Sources/SWBCore/SpecImplementations/PropertyDomainSpec.swift +++ b/Sources/SWBCore/SpecImplementations/PropertyDomainSpec.swift @@ -118,6 +118,8 @@ private final class EnumBuildOptionType : BuildOptionType { return try namespace.declareEnumMacro(name) as EnumMacroDeclaration case "LINKER_FILE_LIST_FORMAT": return try namespace.declareEnumMacro(name) as EnumMacroDeclaration + case "DOCC_MINIMUM_ACCESS_LEVEL": + return try namespace.declareEnumMacro(name) as EnumMacroDeclaration default: return try namespace.declareStringMacro(name) } diff --git a/Sources/SWBCore/SpecImplementations/Tools/DocumentationCompiler.swift b/Sources/SWBCore/SpecImplementations/Tools/DocumentationCompiler.swift index 01ae3a1c..b4b5e5ab 100644 --- a/Sources/SWBCore/SpecImplementations/Tools/DocumentationCompiler.swift +++ b/Sources/SWBCore/SpecImplementations/Tools/DocumentationCompiler.swift @@ -78,7 +78,7 @@ final public class DocumentationCompilerSpec: GenericCompilerSpec, SpecIdentifie var additionalFlags = [String]() // Check if pretty print is requested - if cbc.scope.evaluate(BuiltinMacros.DOCC_PRETTY_PRINT) && swiftCompilerInfo.toolFeatures.has(.emitExtensionBlockSymbols) { + if cbc.scope.evaluate(BuiltinMacros.DOCC_PRETTY_PRINT) { additionalFlags.append("-symbol-graph-pretty-print") } @@ -88,19 +88,34 @@ final public class DocumentationCompilerSpec: GenericCompilerSpec, SpecIdentifie } // Check if synthesized members should be skipped - if cbc.scope.evaluate(BuiltinMacros.DOCC_SKIP_SYNTHESIZED_MEMBERS) && swiftCompilerInfo.toolFeatures.has(.emitExtensionBlockSymbols) { + if cbc.scope.evaluate(BuiltinMacros.DOCC_SKIP_SYNTHESIZED_MEMBERS) { additionalFlags.append("-symbol-graph-skip-synthesized-members") } - switch DocumentationType(from: cbc) { - case .executable: - // When building executable types (like applications and command-line tools), include - // internal symbols in the generated symbol graph. + switch cbc.scope.evaluate(BuiltinMacros.DOCC_MINIMUM_ACCESS_LEVEL) { + case .none: + switch DocumentationType(from: cbc) { + case .executable: + // When building executable types (like applications and command-line tools), include + // internal symbols in the generated symbol graph. + return additionalFlags.appending(contentsOf: ["-symbol-graph-minimum-access-level", "internal"]) + case .framework, .none: + // For frameworks (and non-documentable types), just use the default behavior + // of the symbol graph tool. + return additionalFlags + } + case .private: + return additionalFlags.appending(contentsOf: ["-symbol-graph-minimum-access-level", "private"]) + case .fileprivate: + return additionalFlags.appending(contentsOf: ["-symbol-graph-minimum-access-level", "fileprivate"]) + case .internal: return additionalFlags.appending(contentsOf: ["-symbol-graph-minimum-access-level", "internal"]) - case .framework, .none: - // For frameworks (and non-documentable types), just use the default behavior - // of the symbol graph tool. - return additionalFlags + case .package: + return additionalFlags.appending(contentsOf: ["-symbol-graph-minimum-access-level", "package"]) + case .public: + return additionalFlags.appending(contentsOf: ["-symbol-graph-minimum-access-level", "public"]) + case .open: + return additionalFlags.appending(contentsOf: ["-symbol-graph-minimum-access-level", "open"]) } } diff --git a/Sources/SWBUniversalPlatform/Specs/Documentation.xcspec b/Sources/SWBUniversalPlatform/Specs/Documentation.xcspec index 6a0b8aff..6cafd5e3 100644 --- a/Sources/SWBUniversalPlatform/Specs/Documentation.xcspec +++ b/Sources/SWBUniversalPlatform/Specs/Documentation.xcspec @@ -175,6 +175,22 @@ DefaultValue = NO; }, + // The minimum access level for API for the symbol graph extractor to generate + { + Name = DOCC_MINIMUM_ACCESS_LEVEL; + Type = Enumeration; + Values = ( + none, + private, + fileprivate, + internal, + package, + public, + open, + ); + DefaultValue = none; + }, + { Name = DOCC_EXTRACT_SWIFT_INFO_FOR_OBJC_SYMBOLS; Type = bool; diff --git a/Tests/SWBCoreTests/DocumentationCompilerSpecTests.swift b/Tests/SWBCoreTests/DocumentationCompilerSpecTests.swift index a76ca26a..906e5595 100644 --- a/Tests/SWBCoreTests/DocumentationCompilerSpecTests.swift +++ b/Tests/SWBCoreTests/DocumentationCompilerSpecTests.swift @@ -33,9 +33,57 @@ import SWBMacro swiftCompilerInfo: try mockSwiftCompilerSpec(swiftVersion: "5.6", swiftTag: "swiftlang-5.6.0.0") ) #expect(frameworkArgs == []) + + let publicArgs = await DocumentationCompilerSpec.additionalSymbolGraphGenerationArgs( + try mockApplicationBuildContext(application: false, minimumAccessLevel: .public), + swiftCompilerInfo: try mockSwiftCompilerSpec(swiftVersion: "5.6", swiftTag: "swiftlang-5.6.0.0") + ) + #expect(publicArgs == ["-symbol-graph-minimum-access-level", "public"]) + + let privateArgs = await DocumentationCompilerSpec.additionalSymbolGraphGenerationArgs( + try mockApplicationBuildContext(application: false, minimumAccessLevel: .private), + swiftCompilerInfo: try mockSwiftCompilerSpec(swiftVersion: "5.6", swiftTag: "swiftlang-5.6.0.0") + ) + #expect(privateArgs == ["-symbol-graph-minimum-access-level", "private"]) + + let filePrivateArgs = await DocumentationCompilerSpec.additionalSymbolGraphGenerationArgs( + try mockApplicationBuildContext(application: false, minimumAccessLevel: .fileprivate), + swiftCompilerInfo: try mockSwiftCompilerSpec(swiftVersion: "5.6", swiftTag: "swiftlang-5.6.0.0") + ) + #expect(filePrivateArgs == ["-symbol-graph-minimum-access-level", "fileprivate"]) + + let internalArgs = await DocumentationCompilerSpec.additionalSymbolGraphGenerationArgs( + try mockApplicationBuildContext(application: false, minimumAccessLevel: .internal), + swiftCompilerInfo: try mockSwiftCompilerSpec(swiftVersion: "5.6", swiftTag: "swiftlang-5.6.0.0") + ) + #expect(internalArgs == ["-symbol-graph-minimum-access-level", "internal"]) + + let openArgs = await DocumentationCompilerSpec.additionalSymbolGraphGenerationArgs( + try mockApplicationBuildContext(application: false, minimumAccessLevel: .open), + swiftCompilerInfo: try mockSwiftCompilerSpec(swiftVersion: "5.6", swiftTag: "swiftlang-5.6.0.0") + ) + #expect(openArgs == ["-symbol-graph-minimum-access-level", "open"]) + + let packageArgs = await DocumentationCompilerSpec.additionalSymbolGraphGenerationArgs( + try mockApplicationBuildContext(application: false, minimumAccessLevel: .package), + swiftCompilerInfo: try mockSwiftCompilerSpec(swiftVersion: "5.6", swiftTag: "swiftlang-5.6.0.0") + ) + #expect(packageArgs == ["-symbol-graph-minimum-access-level", "package"]) + + let prettyPrintArgs = await DocumentationCompilerSpec.additionalSymbolGraphGenerationArgs( + try mockApplicationBuildContext(application: false, prettyPrint: true), + swiftCompilerInfo: try mockSwiftCompilerSpec(swiftVersion: "5.6", swiftTag: "swiftlang-5.6.0.0") + ) + #expect(prettyPrintArgs == ["-symbol-graph-pretty-print"]) + + let skipSynthesizedMembers = await DocumentationCompilerSpec.additionalSymbolGraphGenerationArgs( + try mockApplicationBuildContext(application: false, skipSynthesizedMembers: true), + swiftCompilerInfo: try mockSwiftCompilerSpec(swiftVersion: "5.6", swiftTag: "swiftlang-5.6.0.0") + ) + #expect(skipSynthesizedMembers == ["-symbol-graph-skip-synthesized-members"]) } - private func mockApplicationBuildContext(application: Bool) async throws -> CommandBuildContext { + private func mockApplicationBuildContext(application: Bool, minimumAccessLevel: DoccMinimumAccessLevel = .none, prettyPrint: Bool = false, skipSynthesizedMembers: Bool = false) async throws -> CommandBuildContext { let core = try await getCore() let producer = try MockCommandProducer( @@ -49,6 +97,16 @@ import SWBMacro mockTable.push(BuiltinMacros.MACH_O_TYPE, literal: "mh_execute") } + mockTable.push(BuiltinMacros.DOCC_MINIMUM_ACCESS_LEVEL, literal: minimumAccessLevel) + + if prettyPrint { + mockTable.push(BuiltinMacros.DOCC_PRETTY_PRINT, literal: true) + } + + if skipSynthesizedMembers { + mockTable.push(BuiltinMacros.DOCC_SKIP_SYNTHESIZED_MEMBERS, literal: skipSynthesizedMembers) + } + let mockScope = MacroEvaluationScope(table: mockTable) return CommandBuildContext(producer: producer, scope: mockScope, inputs: [])