mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
269 lines
6.5 KiB
Swift
269 lines
6.5 KiB
Swift
//===--- NinjaParserTests.swift -------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2024 - 2025 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
import Foundation
|
|
import Testing
|
|
|
|
@testable import SwiftXcodeGen
|
|
|
|
fileprivate func expectEqual<T, U: Equatable>(
|
|
_ expected: T,
|
|
_ actual: T,
|
|
_ kp: KeyPath<T, U>,
|
|
sourceLocation: SourceLocation = #_sourceLocation
|
|
) {
|
|
#expect(
|
|
expected[keyPath: kp] == actual[keyPath: kp],
|
|
sourceLocation: sourceLocation
|
|
)
|
|
}
|
|
|
|
fileprivate func assertParse(
|
|
_ str: String,
|
|
bindings: [String: String] = [:],
|
|
rules: [String: NinjaBuildFile.Rule] = [:],
|
|
edges: [NinjaBuildFile.BuildEdge],
|
|
sourceLocation: SourceLocation = #_sourceLocation
|
|
) {
|
|
let filePath: AbsolutePath = "/tmp/build.ninja"
|
|
let files: [AbsolutePath: String] = [
|
|
filePath: str
|
|
]
|
|
assertParse(
|
|
filePath,
|
|
in: files,
|
|
bindings: bindings,
|
|
rules: rules,
|
|
edges: edges,
|
|
sourceLocation: sourceLocation
|
|
)
|
|
}
|
|
|
|
fileprivate func assertParse(
|
|
_ filePath: AbsolutePath,
|
|
in fileSystem: [AbsolutePath: String],
|
|
bindings: [String: String] = [:],
|
|
rules: [String: NinjaBuildFile.Rule] = [:],
|
|
edges: [NinjaBuildFile.BuildEdge],
|
|
sourceLocation: SourceLocation = #_sourceLocation
|
|
) {
|
|
do {
|
|
let buildFile = try NinjaParser.parse(filePath: filePath, fileReader: { Data(fileSystem[$0]!.utf8) })
|
|
guard edges.count == buildFile.buildEdges.count else {
|
|
Issue.record(
|
|
"Expected \(edges.count) edges, got \(buildFile.buildEdges.count)",
|
|
sourceLocation: sourceLocation
|
|
)
|
|
return
|
|
}
|
|
#expect(
|
|
bindings == buildFile.bindings.values,
|
|
sourceLocation: sourceLocation
|
|
)
|
|
#expect(
|
|
rules == buildFile.rules,
|
|
sourceLocation: sourceLocation
|
|
)
|
|
for (expected, actual) in zip(edges, buildFile.buildEdges) {
|
|
expectEqual(expected, actual, \.ruleName, sourceLocation: sourceLocation)
|
|
expectEqual(expected, actual, \.inputs, sourceLocation: sourceLocation)
|
|
expectEqual(expected, actual, \.outputs, sourceLocation: sourceLocation)
|
|
expectEqual(expected, actual, \.dependencies, sourceLocation: sourceLocation)
|
|
expectEqual(expected, actual, \.bindings, sourceLocation: sourceLocation)
|
|
|
|
#expect(expected == actual, sourceLocation: sourceLocation)
|
|
}
|
|
} catch {
|
|
Issue.record("\(error)", sourceLocation: sourceLocation)
|
|
}
|
|
}
|
|
|
|
@Suite
|
|
struct NinjaParserTests {
|
|
@Test
|
|
func buildEdge() throws {
|
|
assertParse(
|
|
"""
|
|
# ignore comment, build foo.o: a.swift | dep || orderdep
|
|
#another build comment
|
|
build foo.o foo.swiftmodule: SWIFTC a.swift | dep || orderdep
|
|
notpartofthebuildrule
|
|
""",
|
|
edges: [
|
|
.init(
|
|
ruleName: "SWIFTC",
|
|
inputs: ["a.swift"],
|
|
outputs: ["foo.o", "foo.swiftmodule"],
|
|
dependencies: ["dep", "orderdep"],
|
|
bindings: [:]
|
|
)
|
|
]
|
|
)
|
|
}
|
|
|
|
@Test
|
|
func rule() throws {
|
|
assertParse(
|
|
"""
|
|
rule SWIFTC
|
|
command = /bin/switfc -wmo -target unknown
|
|
other = whatever
|
|
notpartoftherule
|
|
""",
|
|
rules: [
|
|
"SWIFTC": .init(
|
|
name: "SWIFTC",
|
|
bindings: [
|
|
"command": "/bin/switfc -wmo -target unknown",
|
|
"other": "whatever",
|
|
])
|
|
],
|
|
edges: []
|
|
)
|
|
}
|
|
|
|
@Test
|
|
func include() throws {
|
|
let files: [AbsolutePath: String] = [
|
|
"/tmp/build.ninja": """
|
|
include path/to/sub.ninja
|
|
|
|
build foo.swiftmodule : SWIFTC foo.swift
|
|
""",
|
|
"/tmp/path/to/sub.ninja": """
|
|
rule SWIFTC
|
|
command = /bin/swiftc $in -o $out
|
|
"""
|
|
]
|
|
assertParse(
|
|
"/tmp/build.ninja",
|
|
in: files,
|
|
rules: [
|
|
"SWIFTC": .init(
|
|
name: "SWIFTC",
|
|
bindings: [
|
|
"command": "/bin/swiftc $in -o $out",
|
|
])
|
|
],
|
|
edges: [
|
|
.init(
|
|
ruleName: "SWIFTC",
|
|
inputs: ["foo.swift"],
|
|
outputs: ["foo.swiftmodule"],
|
|
dependencies: [],
|
|
bindings: [:]
|
|
)
|
|
]
|
|
)
|
|
}
|
|
|
|
@Test
|
|
func phonyRule() throws {
|
|
assertParse(
|
|
"""
|
|
build foo.swiftmodule : phony bar.swiftmodule
|
|
""",
|
|
edges: [
|
|
.phony(
|
|
for: ["foo.swiftmodule"],
|
|
inputs: ["bar.swiftmodule"]
|
|
)
|
|
]
|
|
)
|
|
}
|
|
|
|
@Test
|
|
func bindings() throws {
|
|
assertParse(
|
|
"""
|
|
x = y
|
|
|
|
CONFIGURATION = Debug
|
|
|
|
build foo.o: SWIFTC xyz foo.swift | baz.o
|
|
UNKNOWN = foobar
|
|
SWIFT_MODULE_NAME = foobar
|
|
|
|
#ignore trivia between attributes
|
|
|
|
\u{20}
|
|
#ignore trivia between attributes
|
|
|
|
FLAGS = -I /a/b -wmo
|
|
ANOTHER_UNKNOWN = a b c
|
|
|
|
build baz.o: CUSTOM_COMMAND baz.swift
|
|
COMMAND = /bin/swiftc -I /a/b -wmo
|
|
FLAGS = -I /c/d -wmo
|
|
|
|
""",
|
|
bindings: [
|
|
"x": "y",
|
|
|
|
"CONFIGURATION": "Debug",
|
|
|
|
// This is considered top-level since it's not indented.
|
|
"FLAGS": "-I /c/d -wmo"
|
|
],
|
|
edges: [
|
|
.init(
|
|
ruleName: "SWIFTC",
|
|
inputs: ["xyz", "foo.swift"],
|
|
outputs: ["foo.o"],
|
|
dependencies: ["baz.o"],
|
|
bindings: [
|
|
"UNKNOWN": "foobar",
|
|
"SWIFT_MODULE_NAME": "foobar",
|
|
"FLAGS": "-I /a/b -wmo",
|
|
"ANOTHER_UNKNOWN": "a b c",
|
|
]
|
|
),
|
|
.init(
|
|
ruleName: "CUSTOM_COMMAND",
|
|
inputs: ["baz.swift"],
|
|
outputs: ["baz.o"],
|
|
dependencies: [],
|
|
bindings: [
|
|
"COMMAND": "/bin/swiftc -I /a/b -wmo",
|
|
]
|
|
)
|
|
]
|
|
)
|
|
}
|
|
|
|
@Test
|
|
func escape() throws {
|
|
for newline in ["\n", "\r", "\r\n"] {
|
|
assertParse(
|
|
"""
|
|
build foo.o$:: SWIFTC xyz$ foo$$.swift | baz$ bar.o
|
|
FLAGS = -I /a$\(newline)\
|
|
/b -wmo
|
|
COMMAND = swiftc$$
|
|
""",
|
|
edges: [
|
|
.init(
|
|
ruleName: "SWIFTC",
|
|
inputs: ["xyz foo$.swift"],
|
|
outputs: ["foo.o:"],
|
|
dependencies: ["baz bar.o"],
|
|
bindings: [
|
|
"FLAGS": "-I /a/b -wmo",
|
|
"COMMAND": "swiftc$",
|
|
]
|
|
)
|
|
]
|
|
)
|
|
}
|
|
}
|
|
}
|