Skip to content

Commit 1b6ebb2

Browse files
committed
Migrate build script to Swift
1 parent 7fbb726 commit 1b6ebb2

File tree

13 files changed

+666
-2
lines changed

13 files changed

+666
-2
lines changed

Sources/SwiftBasicFormat/BasicFormat.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,9 @@ open class BasicFormat: SyntaxRewriter {
145145

146146
/// Whether a leading newline on `token` should be added.
147147
open func requiresLeadingNewline(_ token: TokenSyntax) -> Bool {
148-
// We don't want to add newlines inside string interpolation
149-
if isInsideStringInterpolation(token) {
148+
// We don't want to add newlines inside string interpolation.
149+
// Only for multiline quotes `"""`.
150+
if isInsideStringInterpolation(token) && token.tokenKind != .multilineStringQuote {
150151
return false
151152
}
152153

SwiftSyntaxCLI/Package.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// swift-tools-version:5.7
2+
3+
import PackageDescription
4+
import Foundation
5+
6+
let package = Package(
7+
name: "SwiftSyntaxCLI",
8+
platforms: [
9+
.macOS(.v10_15)
10+
],
11+
products: [
12+
.executable(name: "swift-syntax-cli", targets: ["swift-syntax-cli"])
13+
],
14+
dependencies: [
15+
.package(path: "..")
16+
],
17+
targets: [
18+
.executableTarget(
19+
name: "swift-syntax-cli",
20+
dependencies: [
21+
.product(name: "ArgumentParser", package: "swift-argument-parser")
22+
]
23+
)
24+
]
25+
)
26+
27+
if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil {
28+
// Building standalone.
29+
package.dependencies += [
30+
.package(url: "https://github.com/apple/swift-argument-parser.git", .upToNextMinor(from: "1.2.2"))
31+
]
32+
} else {
33+
package.dependencies += [
34+
.package(path: "../../swift-argument-parser")
35+
]
36+
}

SwiftSyntaxCLI/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# SwiftSyntaxCLI
2+
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import ArgumentParser
14+
15+
@main
16+
struct SwiftSyntaxCLI: ParsableCommand {
17+
18+
static var configuration: CommandConfiguration = CommandConfiguration(
19+
abstract: """
20+
Build and test script for SwiftSyntax.
21+
22+
The build script can also drive the test suite included in the SwiftSyntax
23+
repo. This requires a custom build of the compiler project since it accesses
24+
test utilities that are not shipped as part of the toolchains. See the Testing
25+
section for arguments that need to be specified for this.
26+
""",
27+
subcommands: [
28+
Build.self,
29+
GenerateSourceCode.self,
30+
Test.self,
31+
VerifySourceCode.self,
32+
]
33+
)
34+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import ArgumentParser
14+
import Foundation
15+
16+
struct Build: ParsableCommand, Builder {
17+
@OptionGroup
18+
var arguments: BuilderArguments
19+
20+
func run() throws {
21+
// Until rdar://53881101 is implemented, we cannot request a build of multiple
22+
// targets simultaneously. For now, just build one product after the other.
23+
try buildProduct(productName: "SwiftSyntax")
24+
try buildProduct(productName: "SwiftSyntaxBuilder")
25+
26+
// Build examples
27+
try buildExample(exampleName: "AddOneToIntegerLiterals")
28+
try buildExample(exampleName: "CodeGenerationUsingSwiftSyntaxBuilder")
29+
}
30+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import ArgumentParser
14+
import Foundation
15+
16+
struct GenerateSourceCode: ParsableCommand, Generator {
17+
@OptionGroup
18+
var arguments: GeneratorArguments
19+
20+
func run() throws {
21+
try self.runCodeGeneration(sourceDir: Paths.sourcesDir)
22+
}
23+
}
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import ArgumentParser
14+
import Foundation
15+
16+
struct Test: ParsableCommand, Builder {
17+
@OptionGroup
18+
var arguments: BuilderArguments
19+
20+
@Flag(help: "Don't run lit-based tests")
21+
var skipLitTests: Bool = false
22+
23+
@Argument(
24+
help: """
25+
Path to the FileCheck executable that was built as part of the LLVM repository.
26+
If not specified, it will be looked up from PATH.
27+
"""
28+
)
29+
var filecheckExec: String?
30+
31+
func run() throws {
32+
try buildProduct(productName: "lit-test-helper")
33+
try buildExample(exampleName: "ExamplePlugin")
34+
35+
try runTests()
36+
37+
print("** All tests passed **")
38+
}
39+
40+
private func runTests() throws {
41+
print("** Running SwiftSyntax Tests **")
42+
43+
if !skipLitTests {
44+
try runLitTests()
45+
}
46+
47+
try runXctests()
48+
}
49+
50+
private func runLitTests() throws {
51+
print("** Running lit-based tests **")
52+
53+
guard FileManager.default.fileExists(atPath: Paths.litExec.relativePath) else {
54+
fatalError(
55+
"""
56+
Error: Could not find lit.py.
57+
Looking at '\(Paths.litExec.relativePath)'.
58+
59+
Make sure you have the llvm repo checked out next to the swift-syntax repo.
60+
Refer to README.md for more information.
61+
"""
62+
)
63+
}
64+
65+
guard FileManager.default.fileExists(atPath: Paths.incrTransferRoundtripExec.relativePath) else {
66+
fatalError(
67+
"""
68+
Error: Could not find incr_transfer_round_trip.py.
69+
70+
Make sure you have the main swift repo checked out next to the swift-syntax
71+
repo.
72+
Refer to README.md for more information.
73+
"""
74+
)
75+
}
76+
77+
let productBinPath = try findProductBinPath()
78+
let examplesBinPath = try findExamplesBinPath()
79+
80+
let litTestHelperExec = productBinPath + "/lit-test-helper"
81+
var litCall = [
82+
Paths.litExec.relativePath,
83+
Paths.packageDir.relativePath,
84+
"lit_tests",
85+
]
86+
87+
if let filecheckExec {
88+
litCall += ["--param", "FILECHECK=" + filecheckExec]
89+
}
90+
91+
litCall += ["--param", "LIT_TEST_HELPER=" + litTestHelperExec]
92+
litCall += ["--param", "INCR_TRANSFER_ROUND_TRIP.PY=" + Paths.incrTransferRoundtripExec.relativePath]
93+
94+
litCall += ["--param", "EXAMPLES_BIN_PATH=" + examplesBinPath]
95+
litCall += ["--param", "TOOLCHAIN=" + arguments.toolchain]
96+
97+
// Print all failures
98+
litCall += ["--verbose"]
99+
// Don't show all commands if verbose is not enabled
100+
if !arguments.verbose {
101+
litCall += ["--succinct"]
102+
}
103+
104+
try runPython(arguments: litCall)
105+
}
106+
107+
private func runXctests() throws {
108+
print("** Running XCTests **")
109+
var swiftpmCallArguments: [String] = []
110+
111+
if arguments.verbose {
112+
swiftpmCallArguments += ["--verbose"]
113+
}
114+
115+
swiftpmCallArguments += ["--test-product", "SwiftSyntaxPackageTests"]
116+
117+
var env = ProcessInfo.processInfo.environment
118+
env["SWIFT_BUILD_SCRIPT_ENVIRONMENT"] = "1"
119+
120+
if arguments.enableRawsyntaxValidation {
121+
env["SWIFTSYNTAX_ENABLE_RAWSYNTAX_VALIDATION"] = "1"
122+
}
123+
124+
if arguments.enableTestFuzzing {
125+
env["SWIFTPARSER_ENABLE_ALTERNATE_TOKEN_INTROSPECTION"] = "1"
126+
}
127+
128+
// Tell other projects in the unified build to use local dependencies
129+
env["SWIFTCI_USE_LOCAL_DEPS"] = "1"
130+
env["SWIFT_SYNTAX_PARSER_LIB_SEARCH_PATH"] =
131+
URL(fileURLWithPath: arguments.toolchain)
132+
.appendingPathComponent("lib")
133+
.appendingPathComponent("swift")
134+
.appendingPathComponent("macosx")
135+
.relativePath
136+
137+
try swiftpmInvocation(
138+
action: "test",
139+
packageDir: Paths.packageDir,
140+
additionalArguments: swiftpmCallArguments,
141+
env: env
142+
)
143+
}
144+
145+
private func findSwiftpmBinPath(packageDir: URL) throws -> String {
146+
return try swiftpmInvocation(
147+
action: "build",
148+
packageDir: packageDir,
149+
additionalArguments: ["--show-bin-path"],
150+
env: [:]
151+
)
152+
}
153+
154+
private func findProductBinPath() throws -> String {
155+
return try findSwiftpmBinPath(packageDir: Paths.packageDir)
156+
}
157+
158+
private func findExamplesBinPath() throws -> String {
159+
return try findSwiftpmBinPath(packageDir: Paths.examplesDir)
160+
}
161+
162+
private func runPython(arguments: [String]) throws {
163+
let process = Process()
164+
process.executableURL = URL(fileURLWithPath: "/usr/bin/python3")
165+
process.arguments = arguments
166+
try process.run()
167+
process.waitUntilExit()
168+
}
169+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import ArgumentParser
14+
import Foundation
15+
16+
struct VerifySourceCode: ParsableCommand, Generator {
17+
@OptionGroup
18+
var arguments: GeneratorArguments
19+
20+
func run() throws {
21+
let tempDir = FileManager.default.temporaryDirectory
22+
23+
try self.runCodeGeneration(sourceDir: tempDir)
24+
25+
print("** Verifing code generated files **")
26+
27+
for module in Paths.modules {
28+
let selfGeneratedDir = tempDir.appendingPathComponent(module).appendingPathComponent("generated")
29+
let userGeneratedDir = Paths.sourcesDir.appendingPathComponent(module).appendingPathComponent("generated")
30+
try checkGeneratedFilesMatch(selfGeneratedDir: selfGeneratedDir, userGeneratedDir: userGeneratedDir)
31+
}
32+
}
33+
34+
private func checkGeneratedFilesMatch(
35+
selfGeneratedDir: URL,
36+
userGeneratedDir: URL
37+
) throws {
38+
39+
let selfGeneratedFiles = try FileManager.default.contentsOfDirectory(at: selfGeneratedDir, includingPropertiesForKeys: nil)
40+
let userGeneratedFiles = try FileManager.default.contentsOfDirectory(at: userGeneratedDir, includingPropertiesForKeys: nil)
41+
42+
zip(selfGeneratedFiles, userGeneratedFiles)
43+
.forEach {
44+
if arguments.verbose {
45+
print("Comparing \($0.relativePath) with \($1.relativePath) ...")
46+
}
47+
48+
if !FileManager.default.contentsEqual(atPath: $0.relativePath, andPath: $1.relativePath) {
49+
fatalError(
50+
"""
51+
FAIL: code-generated files committed to repository do
52+
not match generated ones. Please re-generate the
53+
code-generated-files using the following command, open a PR to the
54+
SwiftSyntax project and merge it alongside the main PR.
55+
$ swift-syntax/build-script.py generate-source-code
56+
--toolchain /path/to/toolchain.xctoolchain/usr
57+
"""
58+
)
59+
}
60+
}
61+
}
62+
}

0 commit comments

Comments
 (0)