Skip to content

Commit 8f6a53b

Browse files
committed
Refactor Command Execution Logic into Dedicated Executors
This commit introduces a major overhaul of the command execution logic within the SwiftSyntaxDevUtils package. The primary aim is to decouple the command interface from the underlying business logic, thus enhancing code maintainability and enabling the execution of some commands from others. Key Changes: - Introduced the concept of 'executor', which now encapsulates the logic previously contained within specific commands like `Build`, `Format`, etc. - Command structs have been refactored to focus primarily on argument parsing, delegating execution to these new executors. - This refactoring adheres to the Single Responsibility Principle, ensuring a clear, singular focus for each class or struct. No new functionalities have been introduced, but these changes significantly improve the codebase's structure and clarity. This shift lays a solid foundation for more scalable and maintainable development in SwiftSyntaxDevUtils going forward.
1 parent e2d108a commit 8f6a53b

File tree

7 files changed

+290
-110
lines changed

7 files changed

+290
-110
lines changed

SwiftSyntaxDevUtils/Sources/swift-syntax-dev-utils/commands/Build.swift

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import ArgumentParser
1414
import Foundation
1515

16-
struct Build: ParsableCommand, BuildCommand {
16+
struct Build: ParsableCommand {
1717
static let configuration = CommandConfiguration(
1818
abstract: "Build swift-syntax."
1919
)
@@ -22,9 +22,33 @@ struct Build: ParsableCommand, BuildCommand {
2222
var arguments: BuildArguments
2323

2424
func run() throws {
25-
try buildTarget(packageDir: Paths.packageDir, targetName: "SwiftSyntax-all")
26-
try buildTarget(packageDir: Paths.examplesDir, targetName: "Examples-all")
27-
try buildTarget(packageDir: Paths.swiftParserCliDir, targetName: "swift-parser-cli")
25+
let executor = BuildExecutor(
26+
swiftPMBuilder: SwiftPMBuilder(
27+
toolchain: arguments.toolchain,
28+
buildDir: arguments.buildDir,
29+
multirootDataFile: arguments.multirootDataFile,
30+
release: arguments.release,
31+
enableRawSyntaxValidation: arguments.enableRawSyntaxValidation,
32+
enableTestFuzzing: arguments.enableTestFuzzing,
33+
warningsAsErrors: arguments.warningsAsErrors,
34+
verbose: arguments.verbose
35+
)
36+
)
37+
try executor.run()
38+
}
39+
}
40+
41+
struct BuildExecutor {
42+
private let swiftPMBuilder: SwiftPMBuilder
43+
44+
init(swiftPMBuilder: SwiftPMBuilder) {
45+
self.swiftPMBuilder = swiftPMBuilder
46+
}
47+
48+
func run() throws {
49+
try swiftPMBuilder.buildTarget(packageDir: Paths.packageDir, targetName: "SwiftSyntax-all")
50+
try swiftPMBuilder.buildTarget(packageDir: Paths.examplesDir, targetName: "Examples-all")
51+
try swiftPMBuilder.buildTarget(packageDir: Paths.swiftParserCliDir, targetName: "swift-parser-cli")
2852
try buildEditorExtension()
2953
}
3054

@@ -34,4 +58,23 @@ struct Build: ParsableCommand, BuildCommand {
3458
try invokeXcodeBuild(projectPath: Paths.editorExtensionProjectPath, scheme: "SwiftRefactorExtension")
3559
#endif
3660
}
61+
62+
@discardableResult
63+
private func invokeXcodeBuild(projectPath: URL, scheme: String) throws -> ProcessResult {
64+
try withTemporaryDirectory { tempDir in
65+
let processRunner = ProcessRunner(
66+
executableURL: try Paths.xcodebuildExec,
67+
arguments: [
68+
"-project", projectPath.path,
69+
"-scheme", scheme,
70+
"-derivedDataPath", tempDir.path,
71+
],
72+
additionalEnvironment: [:]
73+
)
74+
75+
let result = try processRunner.run(verbose: swiftPMBuilder.verbose)
76+
77+
return result
78+
}
79+
}
3780
}

SwiftSyntaxDevUtils/Sources/swift-syntax-dev-utils/commands/Format.swift

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ struct Format: ParsableCommand {
2727
static let configuration = CommandConfiguration(
2828
abstract: "Format files in SwiftSyntax using swift-format.",
2929
discussion: """
30-
This command automatically builds the '\(Self.swiftFormatBranch)' branch \
30+
This command automatically builds the '\(FormatExecutor.swiftFormatBranch)' branch \
3131
of swift-format in the '\(Paths.swiftFormatBuildDir.lastPathComponent)' \
3232
directory of this repository and uses the build to format the swift-syntax \
3333
sources.
@@ -37,7 +37,12 @@ struct Format: ParsableCommand {
3737
@Flag(help: "Update the sources of swift-format and rebuild swift-format")
3838
var update: Bool = false
3939

40-
@Flag(help: "Instead of formatting in-place, verify that the files are correctly formatted. Exit with 1 if files are not correctly formatted.")
40+
@Flag(
41+
help: """
42+
Instead of formatting in-place, verify that the files are correctly formatted. \
43+
Exit with 1 if files are not correctly formatted.
44+
"""
45+
)
4146
var lint: Bool = false
4247

4348
@Option(
@@ -51,12 +56,18 @@ struct Format: ParsableCommand {
5156
@Flag(help: "Enable verbose logging.")
5257
var verbose: Bool = false
5358

54-
/// The configuration to build swift-format in.
55-
private static let swiftFormatBuildConfiguration: String = "release"
56-
57-
/// The branch of swift-format to build.
58-
private static let swiftFormatBranch: String = "main"
59+
func run() throws {
60+
let executor = FormatExecutor(
61+
update: update,
62+
lint: lint,
63+
swiftFormat: swiftFormat,
64+
verbose: verbose
65+
)
66+
try executor.run()
67+
}
68+
}
5969

70+
struct FormatExecutor {
6071
private enum Error: Swift.Error, CustomStringConvertible {
6172
case swiftFormatNotFound
6273
case lintFailed
@@ -85,6 +96,46 @@ struct Format: ParsableCommand {
8596
}
8697
}
8798

99+
/// Update the sources of swift-format and rebuild swift-format
100+
private let update: Bool
101+
102+
/// Instead of formatting in-place, verify that the files are correctly formatted.
103+
///
104+
/// Exit with 1 if files are not correctly formatted.
105+
private let lint: Bool
106+
107+
/// Instead of building a local swift-format, use this swift-format executable.
108+
///
109+
/// Should primarily be used for CI, which has already built swift-format.
110+
private let swiftFormat: String?
111+
112+
/// Enable verbose logging.
113+
private let verbose: Bool
114+
115+
/// The branch of swift-format to build.
116+
static let swiftFormatBranch: String = "main"
117+
118+
/// The configuration to build swift-format in.
119+
private static let swiftFormatBuildConfiguration: String = "release"
120+
121+
/// Creates an Executor
122+
/// - Parameters:
123+
/// - update: Update the sources of swift-format and rebuild swift-format
124+
/// - lint: Instead of formatting in-place, verify that the files are correctly formatted.
125+
/// - swiftFormat: Instead of building a local swift-format, use this swift-format executable.
126+
/// - verbose: Enable verbose logging.
127+
init(
128+
update: Bool,
129+
lint: Bool = false,
130+
swiftFormat: String? = nil,
131+
verbose: Bool = false
132+
) {
133+
self.update = update
134+
self.lint = lint
135+
self.swiftFormat = swiftFormat
136+
self.verbose = verbose
137+
}
138+
88139
/// Run `git` in the .swift-format-build directory with the provided arguments.
89140
private func runGitCommand(_ arguments: String...) throws {
90141
try ProcessRunner(
@@ -217,12 +268,12 @@ struct Format: ParsableCommand {
217268
print("💡 You are building running the format script with Swift 5.9 or lower. Running it with SwiftPM 5.10 is about 10s faster.")
218269
#endif
219270

220-
try run(updateAndBuild: self.update)
271+
try run(updateAndBuild: update)
221272
}
222273

223274
/// - Parameter updateAndBuild: Whether to update the locally checked out
224275
/// swift-format sources and rebuild swift-format.
225-
func run(updateAndBuild: Bool) throws {
276+
private func run(updateAndBuild: Bool) throws {
226277
if updateAndBuild {
227278
try cloneOrUpdateSwiftFormat()
228279
try buildSwiftFormat()

SwiftSyntaxDevUtils/Sources/swift-syntax-dev-utils/commands/GenerateSourceCode.swift

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import ArgumentParser
1414
import Foundation
1515

16-
struct GenerateSourceCode: ParsableCommand, SourceCodeGeneratorCommand {
16+
struct GenerateSourceCode: ParsableCommand {
1717
static let configuration = CommandConfiguration(
1818
abstract: "Generate swift-syntax sources."
1919
)
@@ -22,6 +22,55 @@ struct GenerateSourceCode: ParsableCommand, SourceCodeGeneratorCommand {
2222
var arguments: SourceCodeGeneratorArguments
2323

2424
func run() throws {
25-
try self.runCodeGeneration(sourceDir: Paths.sourcesDir)
25+
let executor = GenerateSourceCodeExecutor(
26+
toolchain: arguments.toolchain,
27+
verbose: arguments.verbose
28+
)
29+
try executor.run(sourceDir: Paths.sourcesDir)
30+
}
31+
}
32+
33+
struct GenerateSourceCodeExecutor {
34+
/// The path to the toolchain that shall be used to build SwiftSyntax.
35+
private let toolchain: URL
36+
37+
/// Enable verbose logging.
38+
private let verbose: Bool
39+
40+
/// Creates an executor
41+
/// - Parameters:
42+
/// - toolchain: The path to the toolchain that shall be used to build SwiftSyntax.
43+
/// - verbose: Enable verbose logging.
44+
init(toolchain: URL, verbose: Bool = false) {
45+
self.toolchain = toolchain
46+
self.verbose = verbose
47+
}
48+
49+
func run(sourceDir: URL) throws {
50+
logSection("Running code generation")
51+
52+
var args = [
53+
"run",
54+
"--package-path", Paths.codeGenerationDir.relativePath,
55+
"generate-swift-syntax", sourceDir.relativePath,
56+
]
57+
58+
if verbose {
59+
args += ["--verbose"]
60+
}
61+
62+
let additionalEnvironment = [
63+
"SWIFT_BUILD_SCRIPT_ENVIRONMENT": "1",
64+
"SWIFTSYNTAX_ENABLE_RAWSYNTAX_VALIDATION": "1",
65+
"SWIFTCI_USE_LOCAL_DEPS": nil,
66+
]
67+
68+
let process = ProcessRunner(
69+
executableURL: toolchain.appendingPathComponent("bin").appendingPathComponent("swift"),
70+
arguments: args,
71+
additionalEnvironment: additionalEnvironment
72+
)
73+
74+
try process.run(verbose: verbose)
2675
}
2776
}

SwiftSyntaxDevUtils/Sources/swift-syntax-dev-utils/commands/Test.swift

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,38 @@
1313
import ArgumentParser
1414
import Foundation
1515

16-
struct Test: ParsableCommand, BuildCommand {
16+
struct Test: ParsableCommand {
1717
static let configuration = CommandConfiguration(
1818
abstract: "Run swift-syntax tests."
1919
)
2020

2121
@OptionGroup
2222
var arguments: BuildArguments
2323

24+
func run() throws {
25+
let executor = TestExecutor(
26+
swiftPMBuilder: SwiftPMBuilder(
27+
toolchain: arguments.toolchain,
28+
buildDir: arguments.buildDir,
29+
multirootDataFile: arguments.multirootDataFile,
30+
release: arguments.release,
31+
enableRawSyntaxValidation: arguments.enableRawSyntaxValidation,
32+
enableTestFuzzing: arguments.enableTestFuzzing,
33+
warningsAsErrors: arguments.warningsAsErrors,
34+
verbose: arguments.verbose
35+
)
36+
)
37+
try executor.run()
38+
}
39+
}
40+
41+
struct TestExecutor {
42+
private let swiftPMBuilder: SwiftPMBuilder
43+
44+
init(swiftPMBuilder: SwiftPMBuilder) {
45+
self.swiftPMBuilder = swiftPMBuilder
46+
}
47+
2448
func run() throws {
2549
try runTests()
2650
try runCodeGenerationTests()
@@ -32,42 +56,42 @@ struct Test: ParsableCommand, BuildCommand {
3256
private func runTests() throws {
3357
logSection("Running SwiftSyntax Tests")
3458

35-
try invokeSwiftPM(
59+
try swiftPMBuilder.invokeSwiftPM(
3660
action: "test",
3761
packageDir: Paths.packageDir,
3862
additionalArguments: ["--test-product", "swift-syntaxPackageTests"],
39-
additionalEnvironment: swiftPMEnvironmentVariables,
63+
additionalEnvironment: swiftPMBuilder.swiftPMEnvironmentVariables,
4064
captureStdout: false,
4165
captureStderr: false
4266
)
4367
}
4468

4569
private func runCodeGenerationTests() throws {
4670
logSection("Running CodeGeneration Tests")
47-
try invokeSwiftPM(
71+
try swiftPMBuilder.invokeSwiftPM(
4872
action: "test",
4973
packageDir: Paths.codeGenerationDir,
5074
additionalArguments: ["--test-product", "CodeGenerationPackageTests"],
51-
additionalEnvironment: swiftPMEnvironmentVariables,
75+
additionalEnvironment: swiftPMBuilder.swiftPMEnvironmentVariables,
5276
captureStdout: false,
5377
captureStderr: false
5478
)
5579
}
5680

5781
private func runExamplesTests() throws {
5882
logSection("Running Examples Tests")
59-
try invokeSwiftPM(
83+
try swiftPMBuilder.invokeSwiftPM(
6084
action: "test",
6185
packageDir: Paths.examplesDir,
6286
additionalArguments: ["--test-product", "ExamplesPackageTests"],
63-
additionalEnvironment: swiftPMEnvironmentVariables,
87+
additionalEnvironment: swiftPMBuilder.swiftPMEnvironmentVariables,
6488
captureStdout: false,
6589
captureStderr: false
6690
)
6791
}
6892

6993
private func findSwiftpmBinPath(packageDir: URL) throws -> String {
70-
return try invokeSwiftPM(
94+
return try swiftPMBuilder.invokeSwiftPM(
7195
action: "build",
7296
packageDir: packageDir,
7397
additionalArguments: ["--show-bin-path"],

0 commit comments

Comments
 (0)