Skip to content

Refactor swift-syntax-dev-utils package #2350

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Nov 27, 2023
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import ArgumentParser
@main
struct SwiftSyntaxDevUtils: ParsableCommand {

static var configuration: CommandConfiguration = CommandConfiguration(
static let configuration = CommandConfiguration(
abstract: """
Build and test script for SwiftSyntax.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,42 @@
import ArgumentParser
import Foundation

struct Build: ParsableCommand, BuildCommand {
static var configuration: CommandConfiguration {
return CommandConfiguration(
abstract: "Build swift-syntax."
)
}
struct Build: ParsableCommand {
static let configuration = CommandConfiguration(
abstract: "Build swift-syntax."
)

@OptionGroup
var arguments: BuildArguments

func run() throws {
try buildTarget(packageDir: Paths.packageDir, targetName: "SwiftSyntax-all")
try buildTarget(packageDir: Paths.examplesDir, targetName: "Examples-all")
try buildTarget(packageDir: Paths.swiftParserCliDir, targetName: "swift-parser-cli")
let executor = BuildExecutor(
swiftPMBuilder: SwiftPMBuilder(
toolchain: arguments.toolchain,
buildDir: arguments.buildDir,
multirootDataFile: arguments.multirootDataFile,
release: arguments.release,
enableRawSyntaxValidation: arguments.enableRawSyntaxValidation,
enableTestFuzzing: arguments.enableTestFuzzing,
warningsAsErrors: arguments.warningsAsErrors,
verbose: arguments.verbose
)
)
try executor.run()
}
}

struct BuildExecutor {
private let swiftPMBuilder: SwiftPMBuilder

init(swiftPMBuilder: SwiftPMBuilder) {
self.swiftPMBuilder = swiftPMBuilder
}

func run() throws {
try swiftPMBuilder.buildTarget(packageDir: Paths.packageDir, targetName: "SwiftSyntax-all")
try swiftPMBuilder.buildTarget(packageDir: Paths.examplesDir, targetName: "Examples-all")
try swiftPMBuilder.buildTarget(packageDir: Paths.swiftParserCliDir, targetName: "swift-parser-cli")
try buildEditorExtension()
}

Expand All @@ -36,4 +58,23 @@ struct Build: ParsableCommand, BuildCommand {
try invokeXcodeBuild(projectPath: Paths.editorExtensionProjectPath, scheme: "SwiftRefactorExtension")
#endif
}

@discardableResult
private func invokeXcodeBuild(projectPath: URL, scheme: String) throws -> ProcessResult {
try withTemporaryDirectory { tempDir in
let processRunner = ProcessRunner(
executableURL: try Paths.xcodebuildExec,
arguments: [
"-project", projectPath.path,
"-scheme", scheme,
"-derivedDataPath", tempDir.path,
],
additionalEnvironment: [:]
)

let result = try processRunner.run(verbose: swiftPMBuilder.verbose)

return result
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,25 @@ fileprivate let directoriesToExclude = [
]

struct Format: ParsableCommand {
static var configuration: CommandConfiguration {
return CommandConfiguration(
abstract: "Format files in SwiftSyntax using swift-format.",
discussion: """
This command automatically builds the '\(Self.swiftFormatBranch)' branch \
of swift-format in the '\(Paths.swiftFormatBuildDir.lastPathComponent)' \
directory of this repository and uses the build to format the swift-syntax \
sources.
"""
)
}
static let configuration = CommandConfiguration(
abstract: "Format files in SwiftSyntax using swift-format.",
discussion: """
This command automatically builds the '\(FormatExecutor.swiftFormatBranch)' branch \
of swift-format in the '\(Paths.swiftFormatBuildDir.lastPathComponent)' \
directory of this repository and uses the build to format the swift-syntax \
sources.
"""
)

@Flag(help: "Update the sources of swift-format and rebuild swift-format")
var update: Bool = false

@Flag(help: "Instead of formatting in-place, verify that the files are correctly formatted. Exit with 1 if files are not correctly formatted.")
@Flag(
help: """
Instead of formatting in-place, verify that the files are correctly formatted. \
Exit with 1 if files are not correctly formatted.
"""
)
var lint: Bool = false

@Option(
Expand All @@ -53,12 +56,18 @@ struct Format: ParsableCommand {
@Flag(help: "Enable verbose logging.")
var verbose: Bool = false

/// The configuration to build swift-format in.
private static let swiftFormatBuildConfiguration: String = "release"

/// The branch of swift-format to build.
private static let swiftFormatBranch: String = "main"
func run() throws {
let executor = FormatExecutor(
update: update,
lint: lint,
swiftFormat: swiftFormat,
verbose: verbose
)
try executor.run()
}
}

struct FormatExecutor {
private enum Error: Swift.Error, CustomStringConvertible {
case swiftFormatNotFound
case lintFailed
Expand Down Expand Up @@ -87,6 +96,46 @@ struct Format: ParsableCommand {
}
}

/// Update the sources of swift-format and rebuild swift-format
private let update: Bool

/// Instead of formatting in-place, verify that the files are correctly formatted.
///
/// Exit with 1 if files are not correctly formatted.
private let lint: Bool

/// Instead of building a local swift-format, use this swift-format executable.
///
/// Should primarily be used for CI, which has already built swift-format.
private let swiftFormat: String?

/// Enable verbose logging.
private let verbose: Bool

/// The branch of swift-format to build.
static let swiftFormatBranch: String = "main"

/// The configuration to build swift-format in.
private static let swiftFormatBuildConfiguration: String = "release"

/// Creates an Executor
/// - Parameters:
/// - update: Update the sources of swift-format and rebuild swift-format
/// - lint: Instead of formatting in-place, verify that the files are correctly formatted.
/// - swiftFormat: Instead of building a local swift-format, use this swift-format executable.
/// - verbose: Enable verbose logging.
init(
update: Bool,
lint: Bool = false,
swiftFormat: String? = nil,
verbose: Bool = false
) {
self.update = update
self.lint = lint
self.swiftFormat = swiftFormat
self.verbose = verbose
}

/// Run `git` in the .swift-format-build directory with the provided arguments.
private func runGitCommand(_ arguments: String...) throws {
try ProcessRunner(
Expand Down Expand Up @@ -219,12 +268,12 @@ struct Format: ParsableCommand {
print("💡 You are building running the format script with Swift 5.9 or lower. Running it with SwiftPM 5.10 is about 10s faster.")
#endif

try run(updateAndBuild: self.update)
try run(updateAndBuild: update)
}

/// - Parameter updateAndBuild: Whether to update the locally checked out
/// swift-format sources and rebuild swift-format.
func run(updateAndBuild: Bool) throws {
private func run(updateAndBuild: Bool) throws {
if updateAndBuild {
try cloneOrUpdateSwiftFormat()
try buildSwiftFormat()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,64 @@
import ArgumentParser
import Foundation

struct GenerateSourceCode: ParsableCommand, SourceCodeGeneratorCommand {
static var configuration: CommandConfiguration {
return CommandConfiguration(
abstract: "Generate swift-syntax sources."
)
}
struct GenerateSourceCode: ParsableCommand {
static let configuration = CommandConfiguration(
abstract: "Generate swift-syntax sources."
)

@OptionGroup
var arguments: SourceCodeGeneratorArguments

func run() throws {
try self.runCodeGeneration(sourceDir: Paths.sourcesDir)
let executor = GenerateSourceCodeExecutor(
toolchain: arguments.toolchain,
verbose: arguments.verbose
)
try executor.run(sourceDir: Paths.sourcesDir)
}
}

struct GenerateSourceCodeExecutor {
/// The path to the toolchain that shall be used to build SwiftSyntax.
private let toolchain: URL

/// Enable verbose logging.
private let verbose: Bool

/// Creates an executor
/// - Parameters:
/// - toolchain: The path to the toolchain that shall be used to build SwiftSyntax.
/// - verbose: Enable verbose logging.
init(toolchain: URL, verbose: Bool = false) {
self.toolchain = toolchain
self.verbose = verbose
}

func run(sourceDir: URL) throws {
logSection("Running code generation")

var args = [
"run",
"--package-path", Paths.codeGenerationDir.relativePath,
"generate-swift-syntax", sourceDir.relativePath,
]

if verbose {
args += ["--verbose"]
}

let additionalEnvironment = [
"SWIFT_BUILD_SCRIPT_ENVIRONMENT": "1",
"SWIFTSYNTAX_ENABLE_RAWSYNTAX_VALIDATION": "1",
"SWIFTCI_USE_LOCAL_DEPS": nil,
]

let process = ProcessRunner(
executableURL: toolchain.appendingPathComponent("bin").appendingPathComponent("swift"),
arguments: args,
additionalEnvironment: additionalEnvironment
)

try process.run(verbose: verbose)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,38 @@
import ArgumentParser
import Foundation

struct Test: ParsableCommand, BuildCommand {
static var configuration: CommandConfiguration {
return CommandConfiguration(
abstract: "Run swift-syntax tests."
)
}
struct Test: ParsableCommand {
static let configuration = CommandConfiguration(
abstract: "Run swift-syntax tests."
)

@OptionGroup
var arguments: BuildArguments

func run() throws {
let executor = TestExecutor(
swiftPMBuilder: SwiftPMBuilder(
toolchain: arguments.toolchain,
buildDir: arguments.buildDir,
multirootDataFile: arguments.multirootDataFile,
release: arguments.release,
enableRawSyntaxValidation: arguments.enableRawSyntaxValidation,
enableTestFuzzing: arguments.enableTestFuzzing,
warningsAsErrors: arguments.warningsAsErrors,
verbose: arguments.verbose
)
)
try executor.run()
}
}

struct TestExecutor {
private let swiftPMBuilder: SwiftPMBuilder

init(swiftPMBuilder: SwiftPMBuilder) {
self.swiftPMBuilder = swiftPMBuilder
}

func run() throws {
try runTests()
try runCodeGenerationTests()
Expand All @@ -34,53 +56,46 @@ struct Test: ParsableCommand, BuildCommand {
private func runTests() throws {
logSection("Running SwiftSyntax Tests")

try invokeSwiftPM(
try swiftPMBuilder.invokeSwiftPM(
action: "test",
packageDir: Paths.packageDir,
additionalArguments: ["--test-product", "swift-syntaxPackageTests"],
additionalEnvironment: swiftPMEnvironmentVariables,
additionalEnvironment: swiftPMBuilder.swiftPMEnvironmentVariables,
captureStdout: false,
captureStderr: false
)
}

private func runCodeGenerationTests() throws {
logSection("Running CodeGeneration Tests")
try invokeSwiftPM(
try swiftPMBuilder.invokeSwiftPM(
action: "test",
packageDir: Paths.codeGenerationDir,
additionalArguments: ["--test-product", "CodeGenerationPackageTests"],
additionalEnvironment: swiftPMEnvironmentVariables,
additionalEnvironment: swiftPMBuilder.swiftPMEnvironmentVariables,
captureStdout: false,
captureStderr: false
)
}

private func runExamplesTests() throws {
logSection("Running Examples Tests")
try invokeSwiftPM(
try swiftPMBuilder.invokeSwiftPM(
action: "test",
packageDir: Paths.examplesDir,
additionalArguments: ["--test-product", "ExamplesPackageTests"],
additionalEnvironment: swiftPMEnvironmentVariables,
additionalEnvironment: swiftPMBuilder.swiftPMEnvironmentVariables,
captureStdout: false,
captureStderr: false
)
}

private func findSwiftpmBinPath(packageDir: URL) throws -> String {
return try invokeSwiftPM(
return try swiftPMBuilder.invokeSwiftPM(
action: "build",
packageDir: packageDir,
additionalArguments: ["--show-bin-path"],
additionalEnvironment: [:]
).stdout
}

/// This returns a path to the build examples folder.
/// Example: '<workingDir>/swift-syntax/Examples/.build/arm64-apple-macosx/debug
private func findExamplesBinPath() throws -> URL {
let stdOut = try findSwiftpmBinPath(packageDir: Paths.examplesDir)
return URL(fileURLWithPath: stdOut.trimmingCharacters(in: .whitespacesAndNewlines))
}
}
Loading