Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Generator/Project.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ let target = Target.target(
"Stencil",
"SwiftSyntax",
"SwiftParser",
"SwiftIfConfig",
"ArgumentParser",
"TOMLKit",
"XcodeProj",
Expand Down
3 changes: 2 additions & 1 deletion Generator/Sources/CLI/Generator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,11 @@ final class Generator {
.filter { $0.exists }
.map(TextFile.init(path:))

let buildConfiguration = CuckooGeneratorBuildConfiguration(customConditions: module.buildConditions)
let files: [FileRepresentation] = await inputFiles.concurrentCompactMap { file in
do {
log(.verbose, message: "Processing file: \(file.path)")
let crawler = try Crawler.crawl(url: file.path.url)
let crawler = try Crawler.crawl(url: file.path.url, buildConfiguration: buildConfiguration)
log(.verbose, message: "Successfully processed file: \(file.path)")
return FileRepresentation(file: file, imports: crawler.imports, tokens: crawler.tokens)
} catch {
Expand Down
4 changes: 4 additions & 0 deletions Generator/Sources/CLI/Module.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ final class Module: @unchecked Sendable {
let filenameFormat: String?
let options: Options
let xcodeproj: Xcodeproj?
let buildConditions: Set<String>

init(name: String, output: String?, configurationPath: Path, dto: DTO) throws {
guard Module.overriddenOutput != nil || output != nil || dto.output != nil else {
Expand All @@ -39,6 +40,7 @@ final class Module: @unchecked Sendable {
protocolsOnly: dto.options?.protocolsOnly ?? false,
omitHeaders: dto.options?.omitHeaders ?? false
)
self.buildConditions = Set(dto.buildConditions ?? [])

if let xcodeproj = dto.xcodeproj {
if let target = xcodeproj.target {
Expand Down Expand Up @@ -103,6 +105,7 @@ extension Module {
let filenameFormat: String?
let options: Options?
let xcodeproj: Xcodeproj?
let buildConditions: [String]?

struct Options: Decodable {
let glob: Bool?
Expand Down Expand Up @@ -132,6 +135,7 @@ extension Module: CustomDebugStringConvertible {
filenameFormat.map { "filename format: \($0.bold)" },
"options:\(options.debugDescription.components(separatedBy: "\n").map { "\n\t- \($0)" }.joined())",
xcodeproj.map { "xcodeproj:\($0.debugDescription.components(separatedBy: "\n").map { "\n\t- \($0)" }.joined())" },
buildConditions.isEmpty ? nil : "build conditions:\(buildConditions.sorted().map { "\n\t-\($0.bold)" }.joined())",
]
.compactMap { $0 }
.joined(separator: "\n")
Expand Down
36 changes: 26 additions & 10 deletions Generator/Sources/Internal/Crawlers/Crawler.swift
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import Foundation
import SwiftSyntax
import SwiftParser
import SwiftIfConfig

final class Crawler: SyntaxVisitor {
static func crawl(url: URL) throws -> Crawler {
static func crawl(url: URL, buildConfiguration: CuckooGeneratorBuildConfiguration = .init(customConditions: [])) throws -> Crawler {
let file = try String(contentsOf: url)
let syntaxTree = Parser.parse(source: file)
#if DEBUG
// Comment out the line above and uncomment the one below to test specific strings.
// The `testString` is at the bottom of this file.
// let syntaxTree = Parser.parse(source: testString)
#endif
let crawler = Self(container: nil, url: url)
let crawler = Self(container: nil, url: url, buildConfiguration: buildConfiguration)
crawler.walk(syntaxTree)
return crawler
}
Expand All @@ -22,10 +23,12 @@ final class Crawler: SyntaxVisitor {
private var container: Reference<Token>?

private let url: URL
private let buildConfiguration: CuckooGeneratorBuildConfiguration

private init(container: Reference<Token>?, url: URL) {
private init(container: Reference<Token>?, url: URL, buildConfiguration: CuckooGeneratorBuildConfiguration) {
self.container = container
self.url = url
self.buildConfiguration = buildConfiguration

super.init(viewMode: .sourceAccurate)
}
Expand Down Expand Up @@ -61,7 +64,7 @@ final class Crawler: SyntaxVisitor {

guard token.accessibility.isAccessible else { return .skipChildren }

let crawler = Crawler(container: Reference(token), url: url)
let crawler = Crawler(container: Reference(token), url: url, buildConfiguration: buildConfiguration)
crawler.walk(members: node.memberBlock)
token.members = crawler.tokens
tokens.append(token)
Expand All @@ -85,7 +88,7 @@ final class Crawler: SyntaxVisitor {

guard token.accessibility.isAccessible else { return .skipChildren }

let crawler = Crawler(container: Reference(token), url: url)
let crawler = Crawler(container: Reference(token), url: url, buildConfiguration: buildConfiguration)
crawler.walk(members: node.memberBlock)
token.members = crawler.tokens
tokens.append(token)
Expand All @@ -105,7 +108,7 @@ final class Crawler: SyntaxVisitor {

guard token.accessibility.isAccessible else { return .skipChildren }

let crawler = Crawler(container: Reference(token), url: url)
let crawler = Crawler(container: Reference(token), url: url, buildConfiguration: buildConfiguration)
crawler.walk(members: node.memberBlock)
token.members = crawler.tokens
tokens.append(token)
Expand All @@ -125,7 +128,7 @@ final class Crawler: SyntaxVisitor {

guard token.accessibility.isAccessible else { return .skipChildren }

let crawler = Crawler(container: Reference(token), url: url)
let crawler = Crawler(container: Reference(token), url: url, buildConfiguration: buildConfiguration)
crawler.walk(members: node.memberBlock)
token.members = crawler.tokens
tokens.append(token)
Expand All @@ -145,7 +148,7 @@ final class Crawler: SyntaxVisitor {

guard token.accessibility.isAccessible else { return .skipChildren }

let crawler = Crawler(container: Reference(token), url: url)
let crawler = Crawler(container: Reference(token), url: url, buildConfiguration: buildConfiguration)
crawler.walk(members: node.memberBlock)
token.members = crawler.tokens
tokens.append(token)
Expand Down Expand Up @@ -178,8 +181,21 @@ final class Crawler: SyntaxVisitor {
}

override func visit(_ node: IfConfigDeclSyntax) -> SyntaxVisitorContinueKind {
// TODO: Implement #if functionality.
return .visitChildren
let (activeClause, _) = node.activeClause(in: buildConfiguration)
guard let elements = activeClause?.elements else { return .skipChildren }
switch elements {
case .decls(let memberList):
for member in memberList {
walk(member)
}
case .statements(let stmtList):
for stmt in stmtList {
walk(stmt)
}
default:
break
}
return .skipChildren
}

override func visit(_ node: TypeAliasDeclSyntax) -> SyntaxVisitorContinueKind {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import SwiftIfConfig
import SwiftSyntax

/// A BuildConfiguration implementation for the Cuckoo generator that evaluates `#if` conditions
/// based on user-supplied custom conditions and sensible defaults for known platform/compiler queries.
struct CuckooGeneratorBuildConfiguration: BuildConfiguration {
/// Custom conditions set via `-D` flags (e.g. `-D DEBUG`).
let customConditions: Set<String>

func isCustomConditionSet(name: String) throws -> Bool {
customConditions.contains(name)
}

func hasFeature(name: String) throws -> Bool {
false
}

func hasAttribute(name: String) throws -> Bool {
false
}

func canImport(importPath: [(TokenSyntax, String)], version: CanImportVersion) throws -> Bool {
false
}

func isActiveTargetOS(name: String) throws -> Bool {
false
}

func isActiveTargetArchitecture(name: String) throws -> Bool {
false
}

func isActiveTargetEnvironment(name: String) throws -> Bool {
false
}

func isActiveTargetRuntime(name: String) throws -> Bool {
false
}

func isActiveTargetPointerAuthentication(name: String) throws -> Bool {
false
}

var targetPointerBitWidth: Int { 64 }

var targetAtomicBitWidths: [Int] { [32, 64] }

var endianness: Endianness { .little }

var languageVersion: VersionTuple { VersionTuple(6, 0) }

var compilerVersion: VersionTuple { VersionTuple(6, 0) }
}
1 change: 1 addition & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ let package = Package(
// .product(name: "SwiftFormat", package: "swift-format"),
.product(name: "SwiftSyntax", package: "swift-syntax"),
.product(name: "SwiftParser", package: "swift-syntax"),
.product(name: "SwiftIfConfig", package: "swift-syntax"),
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "TOMLKit", package: "TOMLKit"),
.product(name: "XcodeProj", package: "XcodeProj"),
Expand Down