diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml new file mode 100644 index 00000000..c06cc364 --- /dev/null +++ b/.github/workflows/pull_request.yml @@ -0,0 +1,17 @@ +name: Pull request + +on: + pull_request: + types: [opened, reopened, synchronize] + +jobs: + # tests: + # name: Test + # uses: swiftlang/github-workflows/.github/workflows/swift_package_test.yml@main + # with: + # linux_pre_build_command: apt-get update && apt-get install -y locales locales-all libsqlite3-dev + soundness: + name: Soundness + uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main + with: + api_breakage_check_enabled: false diff --git a/.license_header_template b/.license_header_template new file mode 100644 index 00000000..3f564ac4 --- /dev/null +++ b/.license_header_template @@ -0,0 +1,12 @@ +@@===----------------------------------------------------------------------===@@ +@@ +@@ This source file is part of the Swift open source project +@@ +@@ Copyright (c) YEARS 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 +@@ +@@===----------------------------------------------------------------------===@@ + diff --git a/.licenseignore b/.licenseignore new file mode 100644 index 00000000..1957e720 --- /dev/null +++ b/.licenseignore @@ -0,0 +1,22 @@ +.gitignore +.swiftformat +.swiftformatignore +.editorconfig +.unacceptablelanguageignore +Brewfile +Package.swift +Package.resolved +*.md +*.txt +*.yml +**/.editorconfig +**/*.docc/** +**/*.entitlements +**/*.input +**/*.modulemap +**/*.plist +**/*.xcodeproj/** +**/CODEOWNERS +**/Dockerfile +**/Package.swift +Utilities/git.commit.template diff --git a/.swift-format b/.swift-format new file mode 100644 index 00000000..e345c2e9 --- /dev/null +++ b/.swift-format @@ -0,0 +1,70 @@ +{ + "fileScopedDeclarationPrivacy" : { + "accessLevel" : "private" + }, + "indentConditionalCompilationBlocks" : true, + "indentSwitchCaseLabels" : false, + "indentation" : { + "spaces" : 2 + }, + "lineBreakAroundMultilineExpressionChainComponents" : false, + "lineBreakBeforeControlFlowKeywords" : false, + "lineBreakBeforeEachArgument" : false, + "lineBreakBeforeEachGenericRequirement" : false, + "lineLength" : 100, + "maximumBlankLines" : 1, + "multiElementCollectionTrailingCommas" : true, + "noAssignmentInExpressions" : { + "allowedFunctions" : [ + "XCTAssertNoThrow" + ] + }, + "prioritizeKeepingFunctionOutputTogether" : false, + "respectsExistingLineBreaks" : true, + "rules" : { + "AllPublicDeclarationsHaveDocumentation" : false, + "AlwaysUseLiteralForEmptyCollectionInit" : false, + "AlwaysUseLowerCamelCase" : false, + "AmbiguousTrailingClosureOverload" : true, + "BeginDocumentationCommentWithOneLineSummary" : false, + "DoNotUseSemicolons" : true, + "DontRepeatTypeInStaticProperties" : true, + "FileScopedDeclarationPrivacy" : true, + "FullyIndirectEnum" : true, + "GroupNumericLiterals" : true, + "IdentifiersMustBeASCII" : true, + "NeverForceUnwrap" : false, + "NeverUseForceTry" : false, + "NeverUseImplicitlyUnwrappedOptionals" : false, + "NoAccessLevelOnExtensionDeclaration" : true, + "NoAssignmentInExpressions" : true, + "NoBlockComments" : true, + "NoCasesWithOnlyFallthrough" : true, + "NoEmptyTrailingClosureParentheses" : true, + "NoLabelsInCasePatterns" : true, + "NoLeadingUnderscores" : false, + "NoParensAroundConditions" : true, + "NoPlaygroundLiterals" : true, + "NoVoidReturnOnFunctionSignature" : true, + "OmitExplicitReturns" : false, + "OneCasePerLine" : true, + "OneVariableDeclarationPerLine" : true, + "OnlyOneTrailingClosureArgument" : true, + "OrderedImports" : true, + "ReplaceForEachWithForLoop" : true, + "ReturnVoidInsteadOfEmptyTuple" : true, + "TypeNamesShouldBeCapitalized" : true, + "UseEarlyExits" : false, + "UseExplicitNilCheckInConditions" : true, + "UseLetInEveryBoundCaseVariable" : false, + "UseShorthandTypeNames" : true, + "UseSingleLinePropertyGetter" : true, + "UseSynthesizedInitializer" : true, + "UseTripleSlashForDocumentationComments" : true, + "UseWhereClausesInForLoops" : false, + "ValidateDocumentationComments" : false + }, + "spacesAroundRangeFormationOperators" : false, + "tabWidth" : 8, + "version" : 1 +} diff --git a/.swiftformatignore b/.swiftformatignore new file mode 100644 index 00000000..ebdee05e --- /dev/null +++ b/.swiftformatignore @@ -0,0 +1,2 @@ +Sources/Helpers/Vendor/* +Sources/AsyncProcess/ProcessExecutor.swift diff --git a/.unacceptablelanguageignore b/.unacceptablelanguageignore new file mode 100644 index 00000000..24ab69f8 --- /dev/null +++ b/.unacceptablelanguageignore @@ -0,0 +1,2 @@ +Sources/AsyncProcess/ProcessExecutor.swift +Tests/AsyncProcessTests/IntegrationTests.swift diff --git a/Docker/docker-compose.yaml b/Docker/docker-compose.yaml index 3db9408e..95378ce3 100644 --- a/Docker/docker-compose.yaml +++ b/Docker/docker-compose.yaml @@ -19,10 +19,6 @@ services: - ..:/code:z working_dir: /code - soundness: - <<: *common - command: /bin/bash -xcl "swift -version && uname -a && ./Utilities/soundness.sh" - test: <<: *common environment: diff --git a/Package.swift b/Package.swift index 8094ef8b..832b7429 100644 --- a/Package.swift +++ b/Package.swift @@ -11,7 +11,7 @@ let package = Package( .executable( name: "swift-sdk-generator", targets: ["GeneratorCLI"] - ), + ) ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. @@ -23,7 +23,7 @@ let package = Package( .product(name: "ArgumentParser", package: "swift-argument-parser"), ], swiftSettings: [ - .enableExperimentalFeature("StrictConcurrency=complete"), + .enableExperimentalFeature("StrictConcurrency=complete") ] ), .target( @@ -38,22 +38,22 @@ let package = Package( ], exclude: ["Dockerfiles"], swiftSettings: [ - .enableExperimentalFeature("StrictConcurrency=complete"), + .enableExperimentalFeature("StrictConcurrency=complete") ] ), .testTarget( name: "SwiftSDKGeneratorTests", dependencies: [ - "SwiftSDKGenerator", + "SwiftSDKGenerator" ], swiftSettings: [ - .enableExperimentalFeature("StrictConcurrency=complete"), + .enableExperimentalFeature("StrictConcurrency=complete") ] ), .testTarget( name: "GeneratorEngineTests", dependencies: [ - "Helpers", + "Helpers" ] ), .target( @@ -66,13 +66,13 @@ let package = Package( ], exclude: ["Vendor/README.md"], swiftSettings: [ - .enableExperimentalFeature("StrictConcurrency=complete"), + .enableExperimentalFeature("StrictConcurrency=complete") ] ), .testTarget( name: "HelpersTests", dependencies: [ - "Helpers", + "Helpers" ] ), .systemLibrary(name: "SystemSQLite", pkgConfig: "sqlite3"), diff --git a/Sources/AsyncProcess/ChunkSequence.swift b/Sources/AsyncProcess/ChunkSequence.swift index 8daa1a3d..fe605297 100644 --- a/Sources/AsyncProcess/ChunkSequence.swift +++ b/Sources/AsyncProcess/ChunkSequence.swift @@ -13,9 +13,9 @@ import NIO #if os(Linux) || os(Android) || os(Windows) -@preconcurrency import Foundation + @preconcurrency import Foundation #else -import Foundation + import Foundation #endif public struct IllegalStreamConsumptionError: Error { @@ -53,9 +53,9 @@ public struct ChunkSequence: AsyncSequence & Sendable { } else { throw IllegalStreamConsumptionError( description: """ - Either `.discard`ed, `.inherit`ed or redirected this stream to a `.fileHandle`, - cannot also consume it. To consume, please `.stream` it. - """ + Either `.discard`ed, `.inherit`ed or redirected this stream to a `.fileHandle`, + cannot also consume it. To consume, please `.stream` it. + """ ) } } diff --git a/Sources/AsyncProcess/FileContentStream.swift b/Sources/AsyncProcess/FileContentStream.swift index c704ea99..c0ee126a 100644 --- a/Sources/AsyncProcess/FileContentStream.swift +++ b/Sources/AsyncProcess/FileContentStream.swift @@ -255,7 +255,8 @@ private final class ReadIntoAsyncChannelHandler: ChannelDuplexHandler { extension FileHandle { func fileContentStream(eventLoop: EventLoop) throws -> FileContentStream { - let asyncBytes = try FileContentStream(fileDescriptor: self.fileDescriptor, eventLoop: eventLoop) + let asyncBytes = try FileContentStream( + fileDescriptor: self.fileDescriptor, eventLoop: eventLoop) try self.close() return asyncBytes } @@ -272,8 +273,8 @@ extension FileContentStream { } } -public extension AsyncSequence where Element == ByteBuffer, Self: Sendable { - func splitIntoLines( +extension AsyncSequence where Element == ByteBuffer, Self: Sendable { + public func splitIntoLines( dropTerminator: Bool = true, maximumAllowableBufferSize: Int = 1024 * 1024, dropLastChunkIfNoNewline: Bool = false @@ -286,14 +287,13 @@ public extension AsyncSequence where Element == ByteBuffer, Self: Sendable { ) } - var strings: AsyncMapSequence { + public var strings: AsyncMapSequence { self.map { String(buffer: $0) } } } public struct AsyncByteBufferLineSequence: AsyncSequence & Sendable - where Base: AsyncSequence, Base.Element == ByteBuffer -{ +where Base: AsyncSequence, Base.Element == ByteBuffer { public typealias Element = ByteBuffer private let underlying: Base private let dropTerminator: Bool @@ -329,7 +329,9 @@ public struct AsyncByteBufferLineSequence: AsyncSequence & Senda self.buffer.last?.readableBytesView } - mutating func concatenateEverything(upToLastChunkLengthToConsume lastLength: Int) -> ByteBuffer { + mutating func concatenateEverything(upToLastChunkLengthToConsume lastLength: Int) + -> ByteBuffer + { var output = ByteBuffer() output.reserveCapacity(lastLength + self.byteCountButLast) diff --git a/Sources/AsyncProcess/ProcessExecutor+Convenience.swift b/Sources/AsyncProcess/ProcessExecutor+Convenience.swift index b7815cb4..00c6607b 100644 --- a/Sources/AsyncProcess/ProcessExecutor+Convenience.swift +++ b/Sources/AsyncProcess/ProcessExecutor+Convenience.swift @@ -53,7 +53,7 @@ public struct OutputLoggingSettings: Sendable { } } -public extension ProcessExecutor { +extension ProcessExecutor { /// Run child process, discarding all its output. /// /// - note: The `environment` defaults to the empty environment. @@ -69,7 +69,7 @@ public extension ProcessExecutor { /// don't want to /// provide input. /// - logger: Where to log diagnostic messages to (default to no where) - static func run( + public static func run( group: EventLoopGroup = ProcessExecutor.defaultEventLoopGroup, executable: String, _ arguments: [String], @@ -106,7 +106,7 @@ public extension ProcessExecutor { /// provide input. /// - logger: Where to log diagnostic and output messages to /// - logConfiguration: How to log the output lines - static func runLogOutput( + public static func runLogOutput( group: EventLoopGroup = ProcessExecutor.defaultEventLoopGroup, executable: String, _ arguments: [String], @@ -128,7 +128,9 @@ public extension ProcessExecutor { return try await withThrowingTaskGroup(of: ProcessExitReason?.self) { group in group.addTask { for try await (stream, line) in await merge( - exe.standardOutput.splitIntoLines().strings.map { (ProcessOutputStream.standardOutput, $0) }, + exe.standardOutput.splitIntoLines().strings.map { + (ProcessOutputStream.standardOutput, $0) + }, exe.standardError.splitIntoLines().strings.map { (ProcessOutputStream.standardError, $0) } ) { logger.log( @@ -171,12 +173,12 @@ public extension ProcessExecutor { /// - splitOutputIntoLines: Whether to call the closure with full lines (`true`) or arbitrary chunks of output /// (`false`) /// - logger: Where to log diagnostic and output messages to - static func runProcessingOutput( + public static func runProcessingOutput( group: EventLoopGroup = ProcessExecutor.defaultEventLoopGroup, executable: String, _ arguments: [String], standardInput: StandardInput, - outputProcessor: @escaping @Sendable (ProcessOutputStream, ByteBuffer) async throws -> (), + outputProcessor: @escaping @Sendable (ProcessOutputStream, ByteBuffer) async throws -> Void, splitOutputIntoLines: Bool = false, environment: [String: String] = [:], logger: Logger = ProcessExecutor.disableLogging @@ -225,11 +227,11 @@ public extension ProcessExecutor { } } - struct TooMuchProcessOutputError: Error, Sendable & Hashable { + public struct TooMuchProcessOutputError: Error, Sendable & Hashable { public var stream: ProcessOutputStream } - struct ProcessExitReasonAndOutput: Sendable & Hashable { + public struct ProcessExitReasonAndOutput: Sendable & Hashable { public var exitReason: ProcessExitReason public var standardOutput: ByteBuffer? public var standardError: ByteBuffer? @@ -260,7 +262,7 @@ public extension ProcessExecutor { /// - collectStandardError: If `true`, collect all of the child process' standard error into memory, discard if /// `false` /// - logger: Where to log diagnostic and output messages to - static func runCollectingOutput( + public static func runCollectingOutput( group: EventLoopGroup = ProcessExecutor.defaultEventLoopGroup, executable: String, _ arguments: [String], @@ -287,7 +289,9 @@ public extension ProcessExecutor { if collectStandardOutput { var output: ByteBuffer? = nil for try await chunk in await exe.standardOutput { - guard (output?.readableBytes ?? 0) + chunk.readableBytes <= perStreamCollectionLimitBytes else { + guard + (output?.readableBytes ?? 0) + chunk.readableBytes <= perStreamCollectionLimitBytes + else { throw TooMuchProcessOutputError(stream: .standardOutput) } output.setOrWriteImmutableBuffer(chunk) @@ -302,7 +306,9 @@ public extension ProcessExecutor { if collectStandardError { var output: ByteBuffer? = nil for try await chunk in await exe.standardError { - guard (output?.readableBytes ?? 0) + chunk.readableBytes <= perStreamCollectionLimitBytes else { + guard + (output?.readableBytes ?? 0) + chunk.readableBytes <= perStreamCollectionLimitBytes + else { throw TooMuchProcessOutputError(stream: .standardError) } output.setOrWriteImmutableBuffer(chunk) @@ -317,7 +323,8 @@ public extension ProcessExecutor { try await .exitReason(exe.run()) } - var allInfo = ProcessExitReasonAndOutput(exitReason: .exit(-1), standardOutput: nil, standardError: nil) + var allInfo = ProcessExitReasonAndOutput( + exitReason: .exit(-1), standardOutput: nil, standardError: nil) while let next = try await group.next() { switch next { case let .exitReason(exitReason): @@ -333,7 +340,7 @@ public extension ProcessExecutor { } } -public extension ProcessExecutor { +extension ProcessExecutor { /// Run child process, discarding all its output. /// /// - note: The `environment` defaults to the empty environment. @@ -346,7 +353,7 @@ public extension ProcessExecutor { /// If you want to inherit the calling process' environment into the child, specify /// `ProcessInfo.processInfo.environment` /// - logger: Where to log diagnostic messages to (default to no where) - static func run( + public static func run( group: EventLoopGroup = ProcessExecutor.defaultEventLoopGroup, executable: String, _ arguments: [String], @@ -376,7 +383,7 @@ public extension ProcessExecutor { /// `ProcessInfo.processInfo.environment` /// - logger: Where to log diagnostic and output messages to /// - logConfiguration: How to log the output lines - static func runLogOutput( + public static func runLogOutput( group: EventLoopGroup = ProcessExecutor.defaultEventLoopGroup, executable: String, _ arguments: [String], @@ -410,11 +417,11 @@ public extension ProcessExecutor { /// - splitOutputIntoLines: Whether to call the closure with full lines (`true`) or arbitrary chunks of output /// (`false`) /// - logger: Where to log diagnostic and output messages to - static func runProcessingOutput( + public static func runProcessingOutput( group: EventLoopGroup = ProcessExecutor.defaultEventLoopGroup, executable: String, _ arguments: [String], - outputProcessor: @escaping @Sendable (ProcessOutputStream, ByteBuffer) async throws -> (), + outputProcessor: @escaping @Sendable (ProcessOutputStream, ByteBuffer) async throws -> Void, splitOutputIntoLines: Bool = false, environment: [String: String] = [:], logger: Logger = ProcessExecutor.disableLogging @@ -447,7 +454,7 @@ public extension ProcessExecutor { /// - collectStandardError: If `true`, collect all of the child process' standard error into memory, discard if /// `false` /// - logger: Where to log diagnostic and output messages to - static func runCollectingOutput( + public static func runCollectingOutput( group: EventLoopGroup = ProcessExecutor.defaultEventLoopGroup, executable: String, _ arguments: [String], diff --git a/Sources/AsyncProcess/ProcessExecutor.swift b/Sources/AsyncProcess/ProcessExecutor.swift index 59f1d0a6..80429a6f 100644 --- a/Sources/AsyncProcess/ProcessExecutor.swift +++ b/Sources/AsyncProcess/ProcessExecutor.swift @@ -11,13 +11,12 @@ //===----------------------------------------------------------------------===// import Atomics +import Foundation import Logging import NIO @_exported import struct SystemPackage.FileDescriptor -import Foundation - public struct ProcessOutputStream: Sendable & Hashable & CustomStringConvertible { enum Backing { case standardOutput @@ -159,7 +158,8 @@ public final actor ProcessExecutor { private let standardErrorWriteHandle: FileHandle? private let _standardOutput: ChunkSequence private let _standardError: ChunkSequence - private let processIsRunningApproximation = ManagedAtomic(RunningStateApproximation.neverStarted.rawValue) + private let processIsRunningApproximation = ManagedAtomic( + RunningStateApproximation.neverStarted.rawValue) private let processOutputConsumptionApproximation = ManagedAtomic(UInt8(0)) private let processPid = ManagedAtomic(pid_t(0)) private let ownsStandardOutputWriteHandle: Bool @@ -388,14 +388,14 @@ public final actor ProcessExecutor { { () -> Bool in guard outputConsumptionState.contains([.stdoutConsumed]) - || outputConsumptionState.contains([.stdoutNotStreamed]) + || outputConsumptionState.contains([.stdoutNotStreamed]) else { return false } guard outputConsumptionState.contains([.stderrConsumed]) - || outputConsumptionState.contains([.stderrNotStreamed]) + || outputConsumptionState.contains([.stderrNotStreamed]) else { return false } @@ -459,7 +459,7 @@ public final actor ProcessExecutor { case .processHasExited, .killedTheProcess: break loop case .processStillAlive: - () // gotta continue + () // gotta continue } } else { logger.debug("child process already dead") @@ -482,13 +482,13 @@ public final actor ProcessExecutor { public func run() async throws -> ProcessExitReason { let p = Process() #if canImport(Darwin) - if #available(macOS 13.0, *) { - p.executableURL = URL(filePath: self.executable) - } else { - p.launchPath = self.executable - } + if #available(macOS 13.0, *) { + p.executableURL = URL(filePath: self.executable) + } else { + p.launchPath = self.executable + } #else - p.executableURL = URL(fileURLWithPath: self.executable) + p.executableURL = URL(fileURLWithPath: self.executable) #endif p.arguments = self.arguments p.environment = self.environment @@ -559,15 +559,16 @@ public final actor ProcessExecutor { desired: RunningStateApproximation.finishedExecuting.rawValue, ordering: .relaxed ) - terminationStreamProducer.finish() // The termination handler will never have fired. - assert(worked) // We just set it to running above, shouldn't be able to race (no `await`). - assert(original == RunningStateApproximation.running.rawValue) // We compare-and-exchange it. + terminationStreamProducer.finish() // The termination handler will never have fired. + assert(worked) // We just set it to running above, shouldn't be able to race (no `await`). + assert(original == RunningStateApproximation.running.rawValue) // We compare-and-exchange it. throw error } // At this point, the process is running, we should therefore have a process ID (unless we're already dead). let childPid = p.processIdentifier - _ = self.processPid.compareExchange(expected: 0, desired: childPid, ordering: .sequentiallyConsistent) + _ = self.processPid.compareExchange( + expected: 0, desired: childPid, ordering: .sequentiallyConsistent) assert(childPid != 0 || !p.isRunning) self.logger.debug( "running command", @@ -578,12 +579,12 @@ public final actor ProcessExecutor { ] ) - try! self.standardInputPipe?.fileHandleForReading.close() // Must work. + try! self.standardInputPipe?.fileHandleForReading.close() // Must work. if self.ownsStandardOutputWriteHandle { - try! self.standardOutputWriteHandle?.close() // Must work. + try! self.standardOutputWriteHandle?.close() // Must work. } if self.ownsStandardErrorWriteHandle { - try! self.standardErrorWriteHandle?.close() // Must work. + try! self.standardErrorWriteHandle?.close() // Must work. } @Sendable @@ -611,9 +612,11 @@ public final actor ProcessExecutor { await withTaskGroup(of: Void.self) { triggerTeardownGroup in triggerTeardownGroup.addTask { // wait until cancelled - do { while true { - try await Task.sleep(nanoseconds: 1_000_000_000) - } } catch {} + do { + while true { + try await Task.sleep(nanoseconds: 1_000_000_000) + } + } catch {} let isRunning = self.processIsRunningApproximation.load(ordering: .relaxed) guard isRunning != RunningStateApproximation.finishedExecuting.rawValue else { @@ -639,7 +642,7 @@ public final actor ProcessExecutor { } let result = await waitForChildToExit() - triggerTeardownGroup.cancelAll() // This triggers the teardown + triggerTeardownGroup.cancelAll() // This triggers the teardown return result } } @@ -663,7 +666,7 @@ public final actor ProcessExecutor { exitReason = result } } - return exitReason! // must work because the real task will return a reason (or throw) + return exitReason! // must work because the real task will return a reason (or throw) } } @@ -679,21 +682,21 @@ public final actor ProcessExecutor { } } -public extension ProcessExecutor { +extension ProcessExecutor { /// A globally shared, singleton `EventLoopGroup` that's suitable for ``ProcessExecutor``. /// /// At present this is always `MultiThreadedEventLoopGroup.singleton`. - static var defaultEventLoopGroup: any EventLoopGroup { + public static var defaultEventLoopGroup: any EventLoopGroup { globalDefaultEventLoopGroup } /// The default `Logger` for ``ProcessExecutor`` that's used if you do not override it. It won't log anything. - static var disableLogging: Logger { + public static var disableLogging: Logger { globalDisableLoggingLogger } } -public extension ProcessExecutor { +extension ProcessExecutor { /// Create a ``ProcessExecutor`` to spawn a single child process. /// /// - note: The `environment` defaults to the empty environment. @@ -712,7 +715,7 @@ public extension ProcessExecutor { /// ``ProcessOutput/stream`` /// which requires to consume it via ``ProcessExecutor/standardError``. /// - logger: Where to log diagnostic messages to (default to no where) - init( + public init( group: EventLoopGroup = ProcessExecutor.defaultEventLoopGroup, executable: String, _ arguments: [String], diff --git a/Sources/GeneratorCLI/GeneratorCLI.swift b/Sources/GeneratorCLI/GeneratorCLI.swift index a322f10b..82667afa 100644 --- a/Sources/GeneratorCLI/GeneratorCLI.swift +++ b/Sources/GeneratorCLI/GeneratorCLI.swift @@ -11,8 +11,10 @@ //===----------------------------------------------------------------------===// import ArgumentParser +import Foundation import Logging import SwiftSDKGenerator + import struct SystemPackage.FilePath @main @@ -54,10 +56,10 @@ struct GeneratorCLI: AsyncParsableCommand { } #if canImport(Darwin) - // On Darwin platforms Dispatch's signal source uses kqueue and EVFILT_SIGNAL for - // delivering signals. This exists alongside but with lower precedence than signal and - // sigaction: ignore signal handling here to kqueue can deliver signals. - signal(SIGINT, SIG_IGN) + // On Darwin platforms Dispatch's signal source uses kqueue and EVFILT_SIGNAL for + // delivering signals. This exists alongside but with lower precedence than signal and + // sigaction: ignore signal handling here to kqueue can deliver signals. + signal(SIGINT, SIG_IGN) #endif let signalSource = DispatchSource.makeSignalSource(signal: SIGINT) signalSource.setEventHandler { @@ -67,7 +69,9 @@ struct GeneratorCLI: AsyncParsableCommand { try await generatorTask.value } - logger.info("Generator run finished successfully.", metadata: ["elapsedTime": .string(elapsed.intervalString)]) + logger.info( + "Generator run finished successfully.", + metadata: ["elapsedTime": .string(elapsed.intervalString)]) } } @@ -85,14 +89,15 @@ extension GeneratorCLI { @Option( help: """ - Name of the SDK bundle. Defaults to a string composed of Swift version, Linux distribution, Linux release \ - and target CPU architecture. - """ + Name of the SDK bundle. Defaults to a string composed of Swift version, Linux distribution, Linux release \ + and target CPU architecture. + """ ) var sdkName: String? = nil @Flag( - help: "Experimental: avoid cleaning up toolchain and SDK directories and regenerate the SDK bundle incrementally." + help: + "Experimental: avoid cleaning up toolchain and SDK directories and regenerate the SDK bundle incrementally." ) var incremental: Bool = false @@ -101,9 +106,9 @@ extension GeneratorCLI { @Option( help: """ - Branch of Swift to use when downloading nightly snapshots. Specify `development` for snapshots off the `main` \ - branch of Swift open source project repositories. - """ + Branch of Swift to use when downloading nightly snapshots. Specify `development` for snapshots off the `main` \ + branch of Swift open source project repositories. + """ ) var swiftBranch: String? = nil @@ -112,40 +117,40 @@ extension GeneratorCLI { @Option( help: """ - Path to the Swift toolchain package containing the Swift compiler that runs on the host platform. - """ + Path to the Swift toolchain package containing the Swift compiler that runs on the host platform. + """ ) var hostSwiftPackagePath: String? = nil @Flag( inversion: .prefixedNo, help: """ - Whether or not to include the host toolchain in the Swift SDK. - If the host toolchain is not included, this makes the Swift SDK compatible with any host, \ - but requires exactly the same version of the swift.org toolchain to be installed for it to work. - """ + Whether or not to include the host toolchain in the Swift SDK. + If the host toolchain is not included, this makes the Swift SDK compatible with any host, \ + but requires exactly the same version of the swift.org toolchain to be installed for it to work. + """ ) var hostToolchain: Bool = false @Option( help: """ - Path to the Swift toolchain package containing the Swift standard library that runs on the target platform. - """ + Path to the Swift toolchain package containing the Swift standard library that runs on the target platform. + """ ) var targetSwiftPackagePath: String? = nil @Option( help: """ - The host triple of the bundle. Defaults to a triple of the machine this generator is \ - running on if unspecified. - """ + The host triple of the bundle. Defaults to a triple of the machine this generator is \ + running on if unspecified. + """ ) var host: Triple? = nil @Option( help: """ - The target triple of the bundle. The default depends on a recipe used for SDK generation. Pass `--help` to a specific recipe subcommand for more details. - """ + The target triple of the bundle. The default depends on a recipe used for SDK generation. Pass `--help` to a specific recipe subcommand for more details. + """ ) var target: Triple? = nil @@ -161,7 +166,8 @@ extension GeneratorCLI { let current = try SwiftSDKGenerator.getCurrentTriple(isVerbose: self.verbose) if let arch = hostArch { let target = Triple(arch: arch, vendor: current.vendor!, os: current.os!) - appLogger.warning("deprecated: Please use `--host \(target.triple)` instead of `--host-arch \(arch)`") + appLogger.warning( + "deprecated: Please use `--host \(target.triple)` instead of `--host-arch \(arch)`") return target } return current @@ -173,8 +179,8 @@ extension GeneratorCLI { commandName: "make-linux-sdk", abstract: "Generate a Swift SDK bundle for Linux.", discussion: """ - The default `--target` triple is Linux with the same CPU architecture with host triple - """ + The default `--target` triple is Linux with the same CPU architecture with host triple + """ ) @OptionGroup @@ -191,18 +197,18 @@ extension GeneratorCLI { @Option( help: """ - Linux distribution to use if the target platform is Linux. Available options: `ubuntu`, `rhel`. Default is `ubuntu`. - """, + Linux distribution to use if the target platform is Linux. Available options: `ubuntu`, `rhel`. Default is `ubuntu`. + """, transform: LinuxDistribution.Name.init(nameString:) ) var linuxDistributionName = LinuxDistribution.Name.ubuntu @Option( help: """ - Version of the Linux distribution used as a target platform. - Available options for Ubuntu: `20.04`, `22.04` (default when `--linux-distribution-name` is `ubuntu`), `24.04`. - Available options for RHEL: `ubi9` (default when `--linux-distribution-name` is `rhel`). - """ + Version of the Linux distribution used as a target platform. + Available options for Ubuntu: `20.04`, `22.04` (default when `--linux-distribution-name` is `ubuntu`), `24.04`. + Available options for RHEL: `ubi9` (default when `--linux-distribution-name` is `rhel`). + """ ) var linuxDistributionVersion: String? @@ -212,7 +218,8 @@ extension GeneratorCLI { } if let arch = generatorOptions.targetArch { let target = Triple(arch: arch, vendor: nil, os: .linux, environment: .gnu) - appLogger.warning("deprecated: Please use `--target \(target.triple)` instead of `--target-arch \(arch)`") + appLogger.warning( + "deprecated: Please use `--target \(target.triple)` instead of `--target-arch \(arch)`") } return Triple(arch: hostTriple.arch!, vendor: nil, os: .linux, environment: .gnu) } @@ -230,8 +237,10 @@ extension GeneratorCLI { case .ubuntu: linuxDistributionDefaultVersion = "22.04" } - let linuxDistributionVersion = self.linuxDistributionVersion ?? linuxDistributionDefaultVersion - let linuxDistribution = try LinuxDistribution(name: linuxDistributionName, version: linuxDistributionVersion) + let linuxDistributionVersion = + self.linuxDistributionVersion ?? linuxDistributionDefaultVersion + let linuxDistribution = try LinuxDistribution( + name: linuxDistributionName, version: linuxDistributionVersion) let hostTriple = try self.generatorOptions.deriveHostTriple() let targetTriple = self.deriveTargetTriple(hostTriple: hostTriple) @@ -249,7 +258,8 @@ extension GeneratorCLI { includeHostToolchain: self.generatorOptions.hostToolchain, logger: loggerWithLevel(from: self.generatorOptions) ) - try await GeneratorCLI.run(recipe: recipe, targetTriple: targetTriple, options: self.generatorOptions) + try await GeneratorCLI.run( + recipe: recipe, targetTriple: targetTriple, options: self.generatorOptions) } func isInvokedAsDefaultSubcommand() -> Bool { @@ -272,8 +282,8 @@ extension GeneratorCLI { commandName: "make-wasm-sdk", abstract: "Experimental: Generate a Swift SDK bundle for WebAssembly.", discussion: """ - The default `--target` triple is wasm32-unknown-wasi - """ + The default `--target` triple is wasm32-unknown-wasi + """ ) @OptionGroup @@ -281,8 +291,8 @@ extension GeneratorCLI { @Option( help: """ - Path to the WASI sysroot directory containing the WASI libc headers and libraries. - """ + Path to the WASI sysroot directory containing the WASI libc headers and libraries. + """ ) var wasiSysroot: String @@ -305,32 +315,34 @@ extension GeneratorCLI { logger: loggerWithLevel(from: self.generatorOptions) ) let targetTriple = self.deriveTargetTriple() - try await GeneratorCLI.run(recipe: recipe, targetTriple: targetTriple, options: self.generatorOptions) + try await GeneratorCLI.run( + recipe: recipe, targetTriple: targetTriple, options: self.generatorOptions) } } } -import Foundation - extension Duration { var intervalString: String { let reference = Date() let date = Date(timeInterval: TimeInterval(self.components.seconds), since: reference) - let components = Calendar.current.dateComponents([.hour, .minute, .second], from: reference, to: date) + let components = Calendar.current.dateComponents( + [.hour, .minute, .second], from: reference, to: date) if let hours = components.hour, hours > 0 { #if !canImport(Darwin) && compiler(<6.0) - return String(format: "%02d:%02d:%02d", hours, components.minute ?? 0, components.second ?? 0) + return String( + format: "%02d:%02d:%02d", hours, components.minute ?? 0, components.second ?? 0) #else - return self.formatted() + return self.formatted() #endif } else if let minutes = components.minute, minutes > 0 { #if !canImport(Darwin) && compiler(<6.0) - let seconds = components.second ?? 0 - return "\(minutes) minute\(minutes != 1 ? "s" : "") \(seconds) second\(seconds != 1 ? "s" : "")" + let seconds = components.second ?? 0 + return + "\(minutes) minute\(minutes != 1 ? "s" : "") \(seconds) second\(seconds != 1 ? "s" : "")" #else - return "\(self.formatted(.time(pattern: .minuteSecond))) seconds" + return "\(self.formatted(.time(pattern: .minuteSecond))) seconds" #endif } else { return "\(components.second ?? 0) seconds" diff --git a/Sources/Helpers/ThrowingDefer.swift b/Sources/Helpers/ThrowingDefer.swift index 9fec3203..899c581d 100644 --- a/Sources/Helpers/ThrowingDefer.swift +++ b/Sources/Helpers/ThrowingDefer.swift @@ -22,7 +22,7 @@ /// - SeeAlso: ``withAsyncThrowing(do:defer:)`` public func withThrowing( do work: () throws -> T, - defer deferred: () throws -> () + defer deferred: () throws -> Void ) throws -> T { do { let result = try work() @@ -46,7 +46,7 @@ public func withThrowing( /// - SeeAlso: ``withThrowing(do:defer:)`` public func withAsyncThrowing( do work: @Sendable () async throws -> T, - defer deferred: @Sendable () async throws -> () + defer deferred: @Sendable () async throws -> Void ) async throws -> T { do { let result = try await work() diff --git a/Sources/Helpers/Vendor/Triple.swift b/Sources/Helpers/Vendor/Triple.swift index fd256d46..0fe731e6 100644 --- a/Sources/Helpers/Vendor/Triple.swift +++ b/Sources/Helpers/Vendor/Triple.swift @@ -1,6 +1,6 @@ -//===--------------- Triple.swift - Swift Target Triples ------------------===// +//===----------------------------------------------------------------------===// // -// This source file is part of the Swift.org open source project +// This source file is part of the Swift open source project // // Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception diff --git a/Sources/Helpers/Vendor/_AsyncFileSystem/ConcurrencySupport.swift b/Sources/Helpers/Vendor/_AsyncFileSystem/ConcurrencySupport.swift index b62b190c..50d3c541 100644 --- a/Sources/Helpers/Vendor/_AsyncFileSystem/ConcurrencySupport.swift +++ b/Sources/Helpers/Vendor/_AsyncFileSystem/ConcurrencySupport.swift @@ -1,4 +1,3 @@ - //===----------------------------------------------------------------------===// // // This source file is part of the Swift open source project @@ -6,8 +5,8 @@ // Copyright (c) 2020-2024 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // -// See http://swift.org/LICENSE.txt for license information -// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// diff --git a/Sources/Helpers/Vendor/_AsyncFileSystem/WritableStream.swift b/Sources/Helpers/Vendor/_AsyncFileSystem/WritableStream.swift index 844ba04d..dd5657a8 100644 --- a/Sources/Helpers/Vendor/_AsyncFileSystem/WritableStream.swift +++ b/Sources/Helpers/Vendor/_AsyncFileSystem/WritableStream.swift @@ -1,12 +1,14 @@ -/* - This source file is part of the Swift.org open source project - - Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors - Licensed under Apache License v2.0 with Runtime Library Exception - - See http://swift.org/LICENSE.txt for license information - See http://swift.org/CONTRIBUTORS.txt for Swift project authors -*/ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2014-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 _Concurrency diff --git a/Sources/SwiftSDKGenerator/Artifacts/DownloadableArtifacts.swift b/Sources/SwiftSDKGenerator/Artifacts/DownloadableArtifacts.swift index 88fb7d5f..ec4725e2 100644 --- a/Sources/SwiftSDKGenerator/Artifacts/DownloadableArtifacts.swift +++ b/Sources/SwiftSDKGenerator/Artifacts/DownloadableArtifacts.swift @@ -10,12 +10,13 @@ // //===----------------------------------------------------------------------===// -import struct Foundation.URL import Helpers + +import struct Foundation.URL import struct SystemPackage.FilePath -private extension Triple { - var llvmBinaryURLSuffix: String { +extension Triple { + fileprivate var llvmBinaryURLSuffix: String { switch (self.os, self.arch) { case (.linux, .aarch64): return "aarch64-linux-gnu" case (.linux, .x86_64): return "x86_64-linux-gnu-ubuntu-22.04" @@ -53,7 +54,8 @@ struct DownloadableArtifacts: Sendable { if hostTriple.os == .linux { // Amazon Linux 2 is chosen for its best compatibility with all Swift-supported Linux hosts - let linuxArchSuffix = hostTriple.arch == .aarch64 ? "-\(Triple.Arch.aarch64.linuxConventionName)" : "" + let linuxArchSuffix = + hostTriple.arch == .aarch64 ? "-\(Triple.Arch.aarch64.linuxConventionName)" : "" self.hostSwift = .init( remoteURL: versions.swiftDownloadURL( subdirectory: "amazonlinux2\(linuxArchSuffix)", @@ -80,12 +82,12 @@ struct DownloadableArtifacts: Sendable { self.hostLLVM = .init( remoteURL: URL( string: """ - https://github.com/llvm/llvm-project/releases/download/llvmorg-\( - versions.lldVersion - )/clang+llvm-\( - versions.lldVersion - )-\(hostTriple.llvmBinaryURLSuffix).tar.xz - """ + https://github.com/llvm/llvm-project/releases/download/llvmorg-\( + versions.lldVersion + )/clang+llvm-\( + versions.lldVersion + )-\(hostTriple.llvmBinaryURLSuffix).tar.xz + """ )!, localPath: paths.artifactsCachePath .appending("host_llvm_\(versions.lldVersion)_\(hostTriple.triple).tar.xz"), @@ -104,12 +106,12 @@ struct DownloadableArtifacts: Sendable { self.hostLLVM = .init( remoteURL: URL( string: """ - https://github.com/llvm/llvm-project/releases/download/llvmorg-\( - self.versions.lldVersion - )/llvm-project-\( - self.versions.lldVersion - ).src.tar.xz - """ + https://github.com/llvm/llvm-project/releases/download/llvmorg-\( + self.versions.lldVersion + )/llvm-project-\( + self.versions.lldVersion + ).src.tar.xz + """ )!, localPath: self.paths.artifactsCachePath .appending("llvm_\(self.versions.lldVersion).src.tar.xz"), diff --git a/Sources/SwiftSDKGenerator/Extensions/String+hasAnyPrefix.swift b/Sources/SwiftSDKGenerator/Extensions/String+hasAnyPrefix.swift index 139e3716..0ad671bf 100644 --- a/Sources/SwiftSDKGenerator/Extensions/String+hasAnyPrefix.swift +++ b/Sources/SwiftSDKGenerator/Extensions/String+hasAnyPrefix.swift @@ -1,10 +1,22 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 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 +// +//===----------------------------------------------------------------------===// + extension String { - func hasAnyPrefix(from array: [String]) -> Bool { - for item in array { - if self.hasPrefix(item) { - return true - } - } - return false + func hasAnyPrefix(from array: [String]) -> Bool { + for item in array { + if self.hasPrefix(item) { + return true + } } + return false + } } diff --git a/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator+Copy.swift b/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator+Copy.swift index 5b9afae4..e561cd24 100644 --- a/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator+Copy.swift +++ b/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator+Copy.swift @@ -10,8 +10,8 @@ // //===----------------------------------------------------------------------===// -import SystemPackage import Foundation +import SystemPackage extension SwiftSDKGenerator { func copyTargetSwiftFromDocker( @@ -34,23 +34,23 @@ extension SwiftSDKGenerator { try await generator.runOnDockerContainer( id: containerID, command: #""" - sh -c ' - chmod +w /usr/lib64 - cd /usr/lib64 - for n in *; do - destination=$(readlink $n) - echo $destination | grep "\.\." && \ - rm -f $n && \ - ln -s $(basename $destination) $n - done - rm -rf pm-utils - ' - """# + sh -c ' + chmod +w /usr/lib64 + cd /usr/lib64 + for n in *; do + destination=$(readlink $n) + echo $destination | grep "\.\." && \ + rm -f $n && \ + ln -s $(basename $destination) $n + done + rm -rf pm-utils + ' + """# ) } if case let containerLib64 = FilePath("/usr/lib64"), - try await generator.doesPathExist(containerLib64, inContainer: containerID) + try await generator.doesPathExist(containerLib64, inContainer: containerID) { let sdkUsrLib64Path = sdkUsrPath.appending("lib64") // we already checked that the path exists above, so we don't pass `failIfNotExists: false` here. @@ -93,10 +93,10 @@ extension SwiftSDKGenerator { // Copy the ELF interpreter try await generator.copyFromDockerContainer( - id: containerID, - from: FilePath(targetTriple.interpreterPath), - to: sdkDirPath.appending(targetTriple.interpreterPath), - failIfNotExists: true + id: containerID, + from: FilePath(targetTriple.interpreterPath), + to: sdkDirPath.appending(targetTriple.interpreterPath), + failIfNotExists: true ) // Python artifacts are redundant. @@ -119,7 +119,9 @@ extension SwiftSDKGenerator { let fromPath = distributionPath.appending(pathWithinPackage) if isOptional && !doesFileExist(at: fromPath) { - logger.debug("Optional package path ignored since it does not exist", metadata: ["packagePath": .string(fromPath.string)]) + logger.debug( + "Optional package path ignored since it does not exist", + metadata: ["packagePath": .string(fromPath.string)]) continue } @@ -131,10 +133,10 @@ extension SwiftSDKGenerator { extension Triple { var interpreterPath: String { switch self.archName { - case "x86_64": return "/lib64/ld-linux-x86-64.so.2" - case "aarch64": return "/lib/ld-linux-aarch64.so.1" - case "armv7": return "/lib/ld-linux-armhf.so.3" - default: fatalError("unsupported architecture \(self.archName)") + case "x86_64": return "/lib64/ld-linux-x86-64.so.2" + case "aarch64": return "/lib/ld-linux-aarch64.so.1" + case "armv7": return "/lib/ld-linux-armhf.so.3" + default: fatalError("unsupported architecture \(self.archName)") } } } diff --git a/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator+Download.swift b/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator+Download.swift index 59db653e..ad846802 100644 --- a/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator+Download.swift +++ b/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator+Download.swift @@ -11,14 +11,13 @@ //===----------------------------------------------------------------------===// import AsyncAlgorithms -import Logging import Helpers +import Logging import RegexBuilder import class Foundation.ByteCountFormatter import class Foundation.FileManager import struct Foundation.URL - import struct SystemPackage.FilePath private let ubuntuAMD64Mirror = "http://gb.archive.ubuntu.com/ubuntu" @@ -40,7 +39,9 @@ extension SwiftSDKGenerator { let hostLLVMURL = downloadableArtifacts.hostLLVM.remoteURL // Workaround an issue with github.com returning 400 instead of 404 status to HEAD requests from AHC. - if itemsToDownload(downloadableArtifacts).contains(where: { $0.remoteURL == downloadableArtifacts.hostLLVM.remoteURL } ) { + if itemsToDownload(downloadableArtifacts).contains(where: { + $0.remoteURL == downloadableArtifacts.hostLLVM.remoteURL + }) { let isLLVMBinaryArtifactAvailable = try await type(of: client).with(http1Only: true) { try await $0.head( url: hostLLVMURL.absoluteString, @@ -56,7 +57,8 @@ extension SwiftSDKGenerator { let results = try await withThrowingTaskGroup(of: FileCacheRecord.self) { group in for item in itemsToDownload(downloadableArtifacts) { group.addTask { - try await engine[DownloadArtifactQuery(artifact: item, httpClient: client, logger: self.logger)] + try await engine[ + DownloadArtifactQuery(artifact: item, httpClient: client, logger: self.logger)] } } @@ -68,9 +70,11 @@ extension SwiftSDKGenerator { } logger.info("Using downloaded artifacts from cache") - logger.debug("Using downloaded artifacts in these locations.", metadata: [ - "paths": .array(results.map(\.path.metadataValue)) - ]) + logger.debug( + "Using downloaded artifacts in these locations.", + metadata: [ + "paths": .array(results.map(\.path.metadataValue)) + ]) } func downloadUbuntuPackages( @@ -85,10 +89,11 @@ extension SwiftSDKGenerator { // Find xz path let xzPath = try await which("xz") if xzPath == nil { - logger.warning(""" - The `xz` utility was not found in `PATH`. \ - Consider installing it for more efficient downloading of package lists. - """) + logger.warning( + """ + The `xz` utility was not found in `PATH`. \ + Consider installing it for more efficient downloading of package lists. + """) } async let mainPackages = try await client.parseUbuntuPackagesList( @@ -117,7 +122,8 @@ extension SwiftSDKGenerator { xzPath: xzPath ) - let allPackages = try await mainPackages + let allPackages = + try await mainPackages .merging(updatesPackages, uniquingKeysWith: { $1 }) .merging(universePackages, uniquingKeysWith: { $1 }) @@ -130,7 +136,8 @@ extension SwiftSDKGenerator { ) } - logger.info("Downloading Ubuntu packages...", metadata: ["packageCount": .stringConvertible(urls.count)]) + logger.info( + "Downloading Ubuntu packages...", metadata: ["packageCount": .stringConvertible(urls.count)]) try await inTemporaryDirectory { fs, tmpDir in let downloadedFiles = try await self.downloadFiles(from: urls, to: tmpDir, client, engine) await report(downloadedFiles: downloadedFiles) @@ -151,13 +158,16 @@ extension SwiftSDKGenerator { try await withThrowingTaskGroup(of: (URL, UInt64).self) { for url in urls { $0.addTask { - let downloadedFilePath = try await engine[DownloadFileQuery( - remoteURL: url, localDirectory: directory, httpClient: client - )] + let downloadedFilePath = try await engine[ + DownloadFileQuery( + remoteURL: url, localDirectory: directory, httpClient: client + )] let filePath = downloadedFilePath.path - guard let fileSize = try FileManager.default.attributesOfItem( - atPath: filePath.string - )[.size] as? UInt64 else { + guard + let fileSize = try FileManager.default.attributesOfItem( + atPath: filePath.string + )[.size] as? UInt64 + else { throw GeneratorError.fileDoesNotExist(filePath) } return (url, fileSize) @@ -176,10 +186,12 @@ extension SwiftSDKGenerator { let byteCountFormatter = ByteCountFormatter() for (url, bytes) in downloadedFiles { - logger.debug("Downloaded package", metadata: [ - "url": .string(url.absoluteString), - "size": .string(byteCountFormatter.string(fromByteCount: Int64(bytes))) - ]) + logger.debug( + "Downloaded package", + metadata: [ + "url": .string(url.absoluteString), + "size": .string(byteCountFormatter.string(fromByteCount: Int64(bytes))), + ]) } } } @@ -190,7 +202,8 @@ extension HTTPClientProtocol { unzipWith zipPath: String, isVerbose: Bool ) async throws -> String? { - guard let packages = try await get(url: url).body?.unzip(zipPath: zipPath, isVerbose: isVerbose) else { + guard let packages = try await get(url: url).body?.unzip(zipPath: zipPath, isVerbose: isVerbose) + else { throw FileOperationError.downloadFailed(url) } @@ -221,16 +234,18 @@ extension HTTPClientProtocol { } let packagesListURL = """ - \(mirrorURL)/dists/\(ubuntuRelease)\(releaseSuffix)/\(repository)/binary-\( - targetTriple.arch!.debianConventionName - )/\(packagesFileName(isXzAvailable: xzPath != nil)) - """ - - guard let packages = try await downloadUbuntuPackagesList( - from: packagesListURL, - unzipWith: xzPath ?? "/usr/bin/gzip", // fallback on gzip if xz not available - isVerbose: isVerbose - ) else { + \(mirrorURL)/dists/\(ubuntuRelease)\(releaseSuffix)/\(repository)/binary-\( + targetTriple.arch!.debianConventionName + )/\(packagesFileName(isXzAvailable: xzPath != nil)) + """ + + guard + let packages = try await downloadUbuntuPackagesList( + from: packagesListURL, + unzipWith: xzPath ?? "/usr/bin/gzip", // fallback on gzip if xz not available + isVerbose: isVerbose + ) + else { throw GeneratorError.ubuntuPackagesDecompressionFailure } diff --git a/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator+Entrypoint.swift b/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator+Entrypoint.swift index 8bf18f59..fa95b6b3 100644 --- a/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator+Entrypoint.swift +++ b/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator+Entrypoint.swift @@ -11,17 +11,18 @@ //===----------------------------------------------------------------------===// import AsyncAlgorithms -#if canImport(AsyncHTTPClient) -import AsyncHTTPClient -#endif import Foundation import Helpers import RegexBuilder import SystemPackage -public extension Triple.Arch { +#if canImport(AsyncHTTPClient) + import AsyncHTTPClient +#endif + +extension Triple.Arch { /// Returns the value of `cpu` converted to a convention used in Debian package names - var debianConventionName: String { + public var debianConventionName: String { switch self { case .aarch64: return "arm64" case .x86_64: return "amd64" @@ -32,14 +33,15 @@ public extension Triple.Arch { } } -public extension SwiftSDKGenerator { - func run(recipe: SwiftSDKRecipe) async throws { - try await withQueryEngine(OSFileSystem(), self.logger, cacheLocation: self.engineCachePath) { engine in +extension SwiftSDKGenerator { + public func run(recipe: SwiftSDKRecipe) async throws { + try await withQueryEngine(OSFileSystem(), self.logger, cacheLocation: self.engineCachePath) { + engine in let httpClientType: HTTPClientProtocol.Type #if canImport(AsyncHTTPClient) - httpClientType = HTTPClient.self + httpClientType = HTTPClient.self #else - httpClientType = OfflineHTTPClient.self + httpClientType = OfflineHTTPClient.self #endif try await httpClientType.with { client in if !self.isIncremental { @@ -48,7 +50,8 @@ public extension SwiftSDKGenerator { try await self.createDirectoryIfNeeded(at: pathsConfiguration.artifactsCachePath) - let swiftSDKProduct = try await recipe.makeSwiftSDK(generator: self, engine: engine, httpClient: client) + let swiftSDKProduct = try await recipe.makeSwiftSDK( + generator: self, engine: engine, httpClient: client) let toolsetJSONPath = try await self.generateToolsetJSON(recipe: recipe) diff --git a/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator+Fixup.swift b/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator+Fixup.swift index 94df821a..3161b5e3 100644 --- a/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator+Fixup.swift +++ b/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator+Fixup.swift @@ -57,7 +57,8 @@ extension SwiftSDKGenerator { } func symlinkClangHeaders() throws { - let swiftStaticClangPath = self.pathsConfiguration.toolchainDirPath.appending("usr/lib/swift_static/clang") + let swiftStaticClangPath = self.pathsConfiguration.toolchainDirPath.appending( + "usr/lib/swift_static/clang") if !doesFileExist(at: swiftStaticClangPath) { logger.info("Symlinking clang headers...") try self.createSymlink( diff --git a/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator+Metadata.swift b/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator+Metadata.swift index 8388617a..9a274753 100644 --- a/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator+Metadata.swift +++ b/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator+Metadata.swift @@ -43,7 +43,9 @@ extension SwiftSDKGenerator { return toolsetJSONPath } - func generateDestinationJSON(toolsetPath: FilePath, sdkDirPath: FilePath, recipe: SwiftSDKRecipe) throws { + func generateDestinationJSON(toolsetPath: FilePath, sdkDirPath: FilePath, recipe: SwiftSDKRecipe) + throws + { logger.info("Generating destination JSON file...") let destinationJSONPath = pathsConfiguration.swiftSDKRootPath.appending("swift-sdk.json") @@ -57,10 +59,11 @@ extension SwiftSDKGenerator { relativeSDKDir.removePrefix(pathsConfiguration.swiftSDKRootPath), relativeToolsetPath.removePrefix(pathsConfiguration.swiftSDKRootPath) else { - fatalError(""" - `toolchainBinDirPath`, `sdkDirPath`, and `toolsetPath` are at unexpected locations that prevent computing \ - relative paths - """) + fatalError( + """ + `toolchainBinDirPath`, `sdkDirPath`, and `toolsetPath` are at unexpected locations that prevent computing \ + relative paths + """) } var metadata = SwiftSDKMetadataV4.TripleProperties( @@ -79,7 +82,7 @@ extension SwiftSDKGenerator { encoder.encode( SwiftSDKMetadataV4( targetTriples: [ - self.targetTriple.triple: metadata, + self.targetTriple.triple: metadata ] ) ) @@ -104,9 +107,9 @@ extension SwiftSDKGenerator { .init( path: FilePath(artifactID).appending(self.targetTriple.triple).string, supportedTriples: hostTriples.map { $0.map(\.triple) } - ), + ) ] - ), + ) ] ) ) @@ -121,7 +124,7 @@ extension SwiftSDKGenerator { } /// Generates an `SDKSettings.json` file that looks like this: - /// + /// /// ```json /// { /// "CanonicalName" : "-swift-linux-[gnu|gnueabihf]", diff --git a/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator+Unpack.swift b/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator+Unpack.swift index d0257688..563936f3 100644 --- a/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator+Unpack.swift +++ b/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator+Unpack.swift @@ -51,10 +51,10 @@ extension SwiftSDKGenerator { try self.createDirectoryIfNeeded(at: pathsConfiguration.toolchainDirPath) let excludes = - unusedTargetPlatforms.map { "--exclude usr/lib/swift/\($0)" } + - unusedTargetPlatforms.map { "--exclude usr/lib/swift_static/\($0)" } + - unusedHostBinaries.map { "--exclude usr/bin/\($0)" } + - unusedHostLibraries.map { "--exclude usr/lib/\($0)" } + unusedTargetPlatforms.map { "--exclude usr/lib/swift/\($0)" } + + unusedTargetPlatforms.map { "--exclude usr/lib/swift_static/\($0)" } + + unusedHostBinaries.map { "--exclude usr/bin/\($0)" } + + unusedHostLibraries.map { "--exclude usr/lib/\($0)" } if hostSwiftPackagePath.string.contains("tar.gz") { try await Shell.run( @@ -108,7 +108,9 @@ extension SwiftSDKGenerator { } } - func prepareLLDLinker(_ engine: QueryEngine, llvmArtifact: DownloadableArtifacts.Item) async throws { + func prepareLLDLinker(_ engine: QueryEngine, llvmArtifact: DownloadableArtifacts.Item) + async throws + { logger.info("Unpacking and copying `lld` linker...") let pathsConfiguration = self.pathsConfiguration let targetOS = self.targetTriple.os @@ -120,23 +122,27 @@ extension SwiftSDKGenerator { let unpackedLLDPath: FilePath if llvmArtifact.isPrebuilt { - unpackedLLDPath = try await engine[TarExtractQuery( - file: llvmArtifact.localPath, - into: untarDestination, - outputBinarySubpath: ["bin", "lld"], - stripComponents: 1 - )].path + unpackedLLDPath = try await engine[ + TarExtractQuery( + file: llvmArtifact.localPath, + into: untarDestination, + outputBinarySubpath: ["bin", "lld"], + stripComponents: 1 + ) + ].path } else { try await self.untar( file: llvmArtifact.localPath, into: untarDestination, stripComponents: 1 ) - unpackedLLDPath = try await engine[CMakeBuildQuery( - sourcesDirectory: untarDestination, - outputBinarySubpath: ["bin", "lld"], - options: "-DLLVM_ENABLE_PROJECTS=lld -DLLVM_TARGETS_TO_BUILD=''" - )].path + unpackedLLDPath = try await engine[ + CMakeBuildQuery( + sourcesDirectory: untarDestination, + outputBinarySubpath: ["bin", "lld"], + options: "-DLLVM_ENABLE_PROJECTS=lld -DLLVM_TARGETS_TO_BUILD=''" + ) + ].path } let toolchainLLDPath: FilePath diff --git a/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator.swift b/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator.swift index 0db1d5eb..06a9b277 100644 --- a/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator.swift +++ b/Sources/SwiftSDKGenerator/Generator/SwiftSDKGenerator.swift @@ -58,19 +58,21 @@ public actor SwiftSDKGenerator { } private let fileManager = FileManager.default - private static let dockerCommand = ProcessInfo.processInfo.environment["SWIFT_SDK_GENERATOR_CONTAINER_RUNTIME"] ?? "docker" + private static let dockerCommand = + ProcessInfo.processInfo.environment["SWIFT_SDK_GENERATOR_CONTAINER_RUNTIME"] ?? "docker" public static func getCurrentTriple(isVerbose: Bool) throws -> Triple { let current = UnixName.current! let cpu = current.machine #if os(macOS) - let darwinVersion = current.release - let darwinTriple = Triple("\(cpu)-apple-darwin\(darwinVersion)") - return Triple("\(cpu)-apple-macos\(darwinTriple._macOSVersion?.description ?? "")") + let darwinVersion = current.release + let darwinTriple = Triple("\(cpu)-apple-darwin\(darwinVersion)") + return Triple("\(cpu)-apple-macos\(darwinTriple._macOSVersion?.description ?? "")") #elseif os(Linux) - return Triple("\(cpu)-unknown-linux-gnu") + return Triple("\(cpu)-unknown-linux-gnu") #else - fatalError("Triple detection not implemented for the platform that this generator was built on.") + fatalError( + "Triple detection not implemented for the platform that this generator was built on.") #endif } @@ -132,7 +134,7 @@ public actor SwiftSDKGenerator { func withDockerContainer( fromImage imageName: String, - _ body: @Sendable (String) async throws -> () + _ body: @Sendable (String) async throws -> Void ) async throws { let containerID = try await launchDockerContainer(imageName: imageName) try await withAsyncThrowing { @@ -176,19 +178,24 @@ public actor SwiftSDKGenerator { } func findSymlinks(at directory: FilePath) throws -> [(FilePath, FilePath)] { - guard let enumerator = fileManager.enumerator( - at: URL(fileURLWithPath: directory.string), - includingPropertiesForKeys: [.isSymbolicLinkKey] - ) else { return [] } + guard + let enumerator = fileManager.enumerator( + at: URL(fileURLWithPath: directory.string), + includingPropertiesForKeys: [.isSymbolicLinkKey] + ) + else { return [] } var result = [(FilePath, FilePath)]() for case let url as URL in enumerator { - guard let isSymlink = try url.resourceValues(forKeys: [.isSymbolicLinkKey]) - .isSymbolicLink else { continue } + guard + let isSymlink = try url.resourceValues(forKeys: [.isSymbolicLinkKey]) + .isSymbolicLink + else { continue } if isSymlink { let path = url.path - try result.append((FilePath(path), FilePath(self.fileManager.destinationOfSymbolicLink(atPath: url.path)))) + try result.append( + (FilePath(path), FilePath(self.fileManager.destinationOfSymbolicLink(atPath: url.path)))) } } @@ -227,7 +234,8 @@ public actor SwiftSDKGenerator { } func gunzip(file: FilePath, into directoryPath: FilePath) async throws { - try await Shell.run(#"cd "\#(directoryPath)" && gzip -d "\#(file)""#, shouldLogCommands: self.isVerbose) + try await Shell.run( + #"cd "\#(directoryPath)" && gzip -d "\#(file)""#, shouldLogCommands: self.isVerbose) } func untar( @@ -254,7 +262,9 @@ public actor SwiftSDKGenerator { if isVerbose { let cmd = "ls \(tmp)" let lsOutput = try await Shell.readStdout(cmd) - logger.debug("Files unpacked from deb file", metadata: ["cmd": .string(cmd), "output": .string(lsOutput)]) + logger.debug( + "Files unpacked from deb file", + metadata: ["cmd": .string(cmd), "output": .string(lsOutput)]) } try await Shell.run( diff --git a/Sources/SwiftSDKGenerator/PathsConfiguration.swift b/Sources/SwiftSDKGenerator/PathsConfiguration.swift index cb898d0a..bd0d12b4 100644 --- a/Sources/SwiftSDKGenerator/PathsConfiguration.swift +++ b/Sources/SwiftSDKGenerator/PathsConfiguration.swift @@ -15,7 +15,8 @@ import struct SystemPackage.FilePath public struct PathsConfiguration: Sendable { init(sourceRoot: FilePath, artifactID: String, targetTriple: Triple) { self.sourceRoot = sourceRoot - self.artifactBundlePath = sourceRoot + self.artifactBundlePath = + sourceRoot .appending("Bundles") .appending("\(artifactID).artifactbundle") self.artifactsCachePath = sourceRoot.appending("Artifacts") diff --git a/Sources/SwiftSDKGenerator/PlatformModels/LinuxDistribution.swift b/Sources/SwiftSDKGenerator/PlatformModels/LinuxDistribution.swift index f10b818e..0152478a 100644 --- a/Sources/SwiftSDKGenerator/PlatformModels/LinuxDistribution.swift +++ b/Sources/SwiftSDKGenerator/PlatformModels/LinuxDistribution.swift @@ -34,7 +34,8 @@ public enum LinuxDistribution: Hashable, Sendable { case "24.04": self = .noble default: - throw GeneratorError.unknownLinuxDistribution(name: LinuxDistribution.Name.ubuntu.rawValue, version: version) + throw GeneratorError.unknownLinuxDistribution( + name: LinuxDistribution.Name.ubuntu.rawValue, version: version) } } @@ -48,7 +49,8 @@ public enum LinuxDistribution: Hashable, Sendable { public var requiredPackages: [String] { switch self { - case .focal: return [ + case .focal: + return [ "libc6", "libc6-dev", "libgcc-s1", @@ -61,7 +63,8 @@ public enum LinuxDistribution: Hashable, Sendable { "zlib1g", "zlib1g-dev", ] - case .jammy: return [ + case .jammy: + return [ "libc6", "libc6-dev", "libgcc-s1", @@ -74,7 +77,8 @@ public enum LinuxDistribution: Hashable, Sendable { "zlib1g", "zlib1g-dev", ] - case .noble: return [ + case .noble: + return [ "libc6", "libc6-dev", "libgcc-s1", @@ -129,8 +133,8 @@ public enum LinuxDistribution: Hashable, Sendable { } } -public extension LinuxDistribution.Name { - init(nameString: String) throws { +extension LinuxDistribution.Name { + public init(nameString: String) throws { guard let name = LinuxDistribution.Name(rawValue: nameString) else { throw GeneratorError.unknownLinuxDistribution(name: nameString, version: nil) } diff --git a/Sources/SwiftSDKGenerator/PlatformModels/Triple.swift b/Sources/SwiftSDKGenerator/PlatformModels/Triple.swift index fd7e1c33..55dec55a 100644 --- a/Sources/SwiftSDKGenerator/PlatformModels/Triple.swift +++ b/Sources/SwiftSDKGenerator/PlatformModels/Triple.swift @@ -16,12 +16,12 @@ public typealias Triple = Helpers.Triple extension Triple: @unchecked Sendable {} -public extension Triple { - init(arch: Arch, vendor: Vendor?, os: OS, environment: Environment) { +extension Triple { + public init(arch: Arch, vendor: Vendor?, os: OS, environment: Environment) { self.init("\(arch)-\(vendor?.rawValue ?? "unknown")-\(os)-\(environment)", normalizing: true) } - init(arch: Arch, vendor: Vendor?, os: OS) { + public init(arch: Arch, vendor: Vendor?, os: OS) { self.init("\(arch)-\(vendor?.rawValue ?? "unknown")-\(os)", normalizing: true) } } diff --git a/Sources/SwiftSDKGenerator/PlatformModels/VersionsConfiguration.swift b/Sources/SwiftSDKGenerator/PlatformModels/VersionsConfiguration.swift index 85ca59ae..88467aa1 100644 --- a/Sources/SwiftSDKGenerator/PlatformModels/VersionsConfiguration.swift +++ b/Sources/SwiftSDKGenerator/PlatformModels/VersionsConfiguration.swift @@ -24,7 +24,8 @@ public struct VersionsConfiguration: Sendable { self.swiftBranch = swiftBranch ?? "swift-\(swiftVersion.lowercased())" self.lldVersion = lldVersion self.linuxDistribution = linuxDistribution - self.linuxArchSuffix = targetTriple.arch == .aarch64 ? "-\(Triple.Arch.aarch64.linuxConventionName)" : "" + self.linuxArchSuffix = + targetTriple.arch == .aarch64 ? "-\(Triple.Arch.aarch64.linuxConventionName)" : "" } let swiftVersion: String @@ -54,19 +55,20 @@ public struct VersionsConfiguration: Sendable { let computedSubdirectory: String switch self.linuxDistribution { case let .ubuntu(ubuntu): - computedSubdirectory = "ubuntu\(ubuntu.version.replacingOccurrences(of: ".", with: ""))\(self.linuxArchSuffix)" + computedSubdirectory = + "ubuntu\(ubuntu.version.replacingOccurrences(of: ".", with: ""))\(self.linuxArchSuffix)" case let .rhel(rhel): computedSubdirectory = rhel.rawValue } return URL( string: """ - https://download.swift.org/\( - self.swiftBranch - )/\( - subdirectory ?? computedSubdirectory - )/swift-\(self.swiftVersion)/\(self.swiftDistributionName(platform: platform)).\(fileExtension) - """ + https://download.swift.org/\( + self.swiftBranch + )/\( + subdirectory ?? computedSubdirectory + )/swift-\(self.swiftVersion)/\(self.swiftDistributionName(platform: platform)).\(fileExtension) + """ )! } diff --git a/Sources/SwiftSDKGenerator/Queries/CMakeBuildQuery.swift b/Sources/SwiftSDKGenerator/Queries/CMakeBuildQuery.swift index a5d5e209..d0d8652c 100644 --- a/Sources/SwiftSDKGenerator/Queries/CMakeBuildQuery.swift +++ b/Sources/SwiftSDKGenerator/Queries/CMakeBuildQuery.swift @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// import Helpers + import struct SystemPackage.FilePath struct CMakeBuildQuery: CachingQuery { diff --git a/Sources/SwiftSDKGenerator/Queries/DownloadArtifactQuery.swift b/Sources/SwiftSDKGenerator/Queries/DownloadArtifactQuery.swift index 041737ce..e81d2c36 100644 --- a/Sources/SwiftSDKGenerator/Queries/DownloadArtifactQuery.swift +++ b/Sources/SwiftSDKGenerator/Queries/DownloadArtifactQuery.swift @@ -10,9 +10,10 @@ // //===----------------------------------------------------------------------===// -import class Foundation.ByteCountFormatter -import Logging import Helpers +import Logging + +import class Foundation.ByteCountFormatter import struct SystemPackage.FilePath struct DownloadArtifactQuery: Query { @@ -22,7 +23,9 @@ struct DownloadArtifactQuery: Query { let logger: Logger func run(engine: QueryEngine) async throws -> FilePath { - logger.info("Downloading remote artifact not available in local cache", metadata: ["remoteUrl": .string(self.artifact.remoteURL.absoluteString)]) + logger.info( + "Downloading remote artifact not available in local cache", + metadata: ["remoteUrl": .string(self.artifact.remoteURL.absoluteString)]) let stream = self.httpClient.streamDownloadProgress( from: self.artifact.remoteURL, to: self.artifact.localPath ) @@ -39,15 +42,16 @@ struct DownloadArtifactQuery: Query { let byteCountFormatter = ByteCountFormatter() if let total = progress.totalBytes { - logger.debug(""" - \(artifact.remoteURL.lastPathComponent) \( - byteCountFormatter - .string(fromByteCount: Int64(progress.receivedBytes)) - )/\( - byteCountFormatter - .string(fromByteCount: Int64(total)) - ) - """) + logger.debug( + """ + \(artifact.remoteURL.lastPathComponent) \( + byteCountFormatter + .string(fromByteCount: Int64(progress.receivedBytes)) + )/\( + byteCountFormatter + .string(fromByteCount: Int64(total)) + ) + """) } else { logger.debug( "\(artifact.remoteURL.lastPathComponent) \(byteCountFormatter.string(fromByteCount: Int64(progress.receivedBytes)))" diff --git a/Sources/SwiftSDKGenerator/Queries/DownloadFileQuery.swift b/Sources/SwiftSDKGenerator/Queries/DownloadFileQuery.swift index 41d9a1ab..43e3d4ea 100644 --- a/Sources/SwiftSDKGenerator/Queries/DownloadFileQuery.swift +++ b/Sources/SwiftSDKGenerator/Queries/DownloadFileQuery.swift @@ -10,8 +10,9 @@ // //===----------------------------------------------------------------------===// -import struct Foundation.URL import Helpers + +import struct Foundation.URL import struct SystemPackage.FilePath struct DownloadFileQuery: Query { diff --git a/Sources/SwiftSDKGenerator/Queries/TarExtractQuery.swift b/Sources/SwiftSDKGenerator/Queries/TarExtractQuery.swift index cf5828de..4eeabf5a 100644 --- a/Sources/SwiftSDKGenerator/Queries/TarExtractQuery.swift +++ b/Sources/SwiftSDKGenerator/Queries/TarExtractQuery.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift open source project // -// Copyright (c) 2022-202Apple Inc. and the Swift project authors +// Copyright (c) 2022-2024 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 diff --git a/Sources/SwiftSDKGenerator/SwiftSDKRecipes/LinuxRecipe.swift b/Sources/SwiftSDKGenerator/SwiftSDKRecipes/LinuxRecipe.swift index 66a4dfec..7d58357f 100644 --- a/Sources/SwiftSDKGenerator/SwiftSDKRecipes/LinuxRecipe.swift +++ b/Sources/SwiftSDKGenerator/SwiftSDKRecipes/LinuxRecipe.swift @@ -11,8 +11,9 @@ //===----------------------------------------------------------------------===// import Foundation -import Logging import Helpers +import Logging + import struct SystemPackage.FilePath public struct LinuxRecipe: SwiftSDKRecipe { @@ -177,7 +178,8 @@ public struct LinuxRecipe: SwiftSDKRecipe { var items: [DownloadableArtifacts.Item] = [] if self.hostSwiftSource != .preinstalled && self.mainHostTriple.os != .linux - && !self.versionsConfiguration.swiftVersion.hasPrefix("6.0") { + && !self.versionsConfiguration.swiftVersion.hasPrefix("6.0") + { items.append(artifacts.hostLLVM) } @@ -226,7 +228,7 @@ public struct LinuxRecipe: SwiftSDKRecipe { self.linuxDistribution, targetArchName: self.mainTargetTriple.archName ) } - + let sdkDirPath = self.sdkDirPath(paths: generator.pathsConfiguration) if !generator.isIncremental { try await generator.removeRecursively(at: sdkDirPath) @@ -249,7 +251,8 @@ public struct LinuxRecipe: SwiftSDKRecipe { if !self.shouldUseDocker { guard case let .ubuntu(version) = linuxDistribution else { - throw GeneratorError + throw + GeneratorError .distributionSupportsOnlyDockerGenerator(self.linuxDistribution) } @@ -289,7 +292,9 @@ public struct LinuxRecipe: SwiftSDKRecipe { case .remoteTarball: try await generator.unpackTargetSwiftPackage( targetSwiftPackagePath: downloadableArtifacts.targetSwift.localPath, - relativePathToRoot: [FilePath.Component(self.versionsConfiguration.swiftDistributionName())!], + relativePathToRoot: [ + FilePath.Component(self.versionsConfiguration.swiftDistributionName())! + ], sdkDirPath: sdkDirPath ) } @@ -308,11 +313,14 @@ public struct LinuxRecipe: SwiftSDKRecipe { // Swift 6.1 and later do not throw warnings about the SDKSettings.json file missing, // so they don't need this file. if self.versionsConfiguration.swiftVersion.hasAnyPrefix(from: ["5.9", "5.10", "6.0"]) { - try await generator.generateSDKSettingsFile(sdkDirPath: sdkDirPath, distribution: linuxDistribution) + try await generator.generateSDKSettingsFile( + sdkDirPath: sdkDirPath, distribution: linuxDistribution) } if self.hostSwiftSource != .preinstalled { - if self.mainHostTriple.os != .linux && !self.versionsConfiguration.swiftVersion.hasPrefix("6.0") { + if self.mainHostTriple.os != .linux + && !self.versionsConfiguration.swiftVersion.hasPrefix("6.0") + { try await generator.prepareLLDLinker(engine, llvmArtifact: downloadableArtifacts.hostLLVM) } @@ -320,7 +328,8 @@ public struct LinuxRecipe: SwiftSDKRecipe { try await generator.symlinkClangHeaders() } - let autolinkExtractPath = generator.pathsConfiguration.toolchainBinDirPath.appending("swift-autolink-extract") + let autolinkExtractPath = generator.pathsConfiguration.toolchainBinDirPath.appending( + "swift-autolink-extract") if await !generator.doesFileExist(at: autolinkExtractPath) { logger.info("Fixing `swift-autolink-extract` symlink...") diff --git a/Sources/SwiftSDKGenerator/SwiftSDKRecipes/SwiftSDKRecipe.swift b/Sources/SwiftSDKGenerator/SwiftSDKRecipes/SwiftSDKRecipe.swift index 17908a46..c10f0087 100644 --- a/Sources/SwiftSDKGenerator/SwiftSDKRecipes/SwiftSDKRecipe.swift +++ b/Sources/SwiftSDKGenerator/SwiftSDKRecipes/SwiftSDKRecipe.swift @@ -10,8 +10,9 @@ // //===----------------------------------------------------------------------===// -import Logging import Helpers +import Logging + import struct SystemPackage.FilePath public struct SwiftSDKProduct { @@ -39,13 +40,15 @@ public protocol SwiftSDKRecipe: Sendable { var logger: Logger { get } /// The main entrypoint of the recipe to make a Swift SDK - func makeSwiftSDK(generator: SwiftSDKGenerator, engine: QueryEngine, httpClient: some HTTPClientProtocol) async throws + func makeSwiftSDK( + generator: SwiftSDKGenerator, engine: QueryEngine, httpClient: some HTTPClientProtocol + ) async throws -> SwiftSDKProduct } -public extension SwiftSDKRecipe { - func applyPlatformOptions(toolset: inout Toolset, targetTriple: Triple) {} - func applyPlatformOptions( +extension SwiftSDKRecipe { + public func applyPlatformOptions(toolset: inout Toolset, targetTriple: Triple) {} + public func applyPlatformOptions( metadata: inout SwiftSDKMetadataV4.TripleProperties, paths: PathsConfiguration, targetTriple: Triple diff --git a/Sources/SwiftSDKGenerator/SwiftSDKRecipes/WebAssemblyRecipe.swift b/Sources/SwiftSDKGenerator/SwiftSDKRecipes/WebAssemblyRecipe.swift index 21b24853..dc3cb58c 100644 --- a/Sources/SwiftSDKGenerator/SwiftSDKRecipes/WebAssemblyRecipe.swift +++ b/Sources/SwiftSDKGenerator/SwiftSDKRecipes/WebAssemblyRecipe.swift @@ -10,8 +10,9 @@ // //===----------------------------------------------------------------------===// -import Logging import Helpers +import Logging + import struct SystemPackage.FilePath public struct WebAssemblyRecipe: SwiftSDKRecipe { @@ -59,9 +60,10 @@ public struct WebAssemblyRecipe: SwiftSDKRecipe { "-pthread", "-ftls-model=local-exec", ] // Tell LLVM codegen in swiftc to enable those features via clang options - toolset.swiftCompiler?.extraCLIOptions?.append(contentsOf: ccOptions.flatMap { - ["-Xcc", $0] - }) + toolset.swiftCompiler?.extraCLIOptions?.append( + contentsOf: ccOptions.flatMap { + ["-Xcc", $0] + }) // Tell the C and C++ compilers to enable those features toolset.cCompiler = Toolset.ToolProperties(extraCLIOptions: ccOptions) toolset.cxxCompiler = Toolset.ToolProperties(extraCLIOptions: ccOptions) @@ -86,9 +88,11 @@ public struct WebAssemblyRecipe: SwiftSDKRecipe { ) { var relativeToolchainDir = paths.toolchainDirPath guard relativeToolchainDir.removePrefix(paths.swiftSDKRootPath) else { - fatalError("The toolchain bin directory path must be a subdirectory of the Swift SDK root path.") + fatalError( + "The toolchain bin directory path must be a subdirectory of the Swift SDK root path.") } - metadata.swiftStaticResourcesPath = relativeToolchainDir.appending("usr/lib/swift_static").string + metadata.swiftStaticResourcesPath = + relativeToolchainDir.appending("usr/lib/swift_static").string metadata.swiftResourcesPath = metadata.swiftStaticResourcesPath } @@ -104,7 +108,8 @@ public struct WebAssemblyRecipe: SwiftSDKRecipe { var hostTriples: [Triple]? = nil if let hostSwiftPackage { hostTriples = [hostSwiftPackage.triple] - try await generator.rsync(from: hostSwiftPackage.path.appending("usr"), to: pathsConfiguration.toolchainDirPath) + try await generator.rsync( + from: hostSwiftPackage.path.appending("usr"), to: pathsConfiguration.toolchainDirPath) logger.info("Removing unused toolchain components...") let liblldbNames: [String] = try await { @@ -127,15 +132,19 @@ public struct WebAssemblyRecipe: SwiftSDKRecipe { try await self.mergeTargetSwift(from: targetSwiftLibPath, generator: generator) } else { // Simply copy the target Swift package into the SDK bundle when building host-agnostic SDK. - try await generator.createDirectoryIfNeeded(at: pathsConfiguration.toolchainDirPath.appending("usr")) - try await generator.copy(from: targetSwiftLibPath, to: pathsConfiguration.toolchainDirPath.appending("usr/lib")) + try await generator.createDirectoryIfNeeded( + at: pathsConfiguration.toolchainDirPath.appending("usr")) + try await generator.copy( + from: targetSwiftLibPath, to: pathsConfiguration.toolchainDirPath.appending("usr/lib")) } - let autolinkExtractPath = generator.pathsConfiguration.toolchainBinDirPath.appending("swift-autolink-extract") + let autolinkExtractPath = generator.pathsConfiguration.toolchainBinDirPath.appending( + "swift-autolink-extract") // WebAssembly object file requires `swift-autolink-extract` if await !generator.doesFileExist(at: autolinkExtractPath), - await generator.doesFileExist(at: generator.pathsConfiguration.toolchainBinDirPath.appending("swift")) + await generator.doesFileExist( + at: generator.pathsConfiguration.toolchainBinDirPath.appending("swift")) { logger.info("Fixing `swift-autolink-extract` symlink...") try await generator.createSymlink(at: autolinkExtractPath, pointingTo: "swift") @@ -149,24 +158,40 @@ public struct WebAssemblyRecipe: SwiftSDKRecipe { } /// Merge the target Swift package into the Swift SDK bundle derived from the host Swift package. - func mergeTargetSwift(from distributionPath: FilePath, generator: SwiftSDKGenerator) async throws { + func mergeTargetSwift(from distributionPath: FilePath, generator: SwiftSDKGenerator) async throws + { let pathsConfiguration = generator.pathsConfiguration logger.info("Copying Swift core libraries for the target triple into Swift SDK bundle...") for (pathWithinPackage, pathWithinSwiftSDK, isOptional) in [ ("clang", pathsConfiguration.toolchainDirPath.appending("usr/lib"), false), ("swift/clang", pathsConfiguration.toolchainDirPath.appending("usr/lib/swift"), false), ("swift/wasi", pathsConfiguration.toolchainDirPath.appending("usr/lib/swift"), false), - ("swift_static/clang", pathsConfiguration.toolchainDirPath.appending("usr/lib/swift_static"), false), - ("swift_static/wasi", pathsConfiguration.toolchainDirPath.appending("usr/lib/swift_static"), false), - ("swift_static/shims", pathsConfiguration.toolchainDirPath.appending("usr/lib/swift_static"), false), + ( + "swift_static/clang", pathsConfiguration.toolchainDirPath.appending("usr/lib/swift_static"), + false + ), + ( + "swift_static/wasi", pathsConfiguration.toolchainDirPath.appending("usr/lib/swift_static"), + false + ), + ( + "swift_static/shims", pathsConfiguration.toolchainDirPath.appending("usr/lib/swift_static"), + false + ), // Mark CoreFoundation as optional until we set up build system to build it for WebAssembly - ("swift_static/CoreFoundation", pathsConfiguration.toolchainDirPath.appending("usr/lib/swift_static"), true), + ( + "swift_static/CoreFoundation", + pathsConfiguration.toolchainDirPath.appending("usr/lib/swift_static"), true + ), ] { - if isOptional, await !(generator.doesFileExist(at: distributionPath.appending(pathWithinPackage))) { + if isOptional, + await !(generator.doesFileExist(at: distributionPath.appending(pathWithinPackage))) + { logger.debug("Skipping optional path \(pathWithinPackage)") continue } - try await generator.rsync(from: distributionPath.appending(pathWithinPackage), to: pathWithinSwiftSDK) + try await generator.rsync( + from: distributionPath.appending(pathWithinPackage), to: pathWithinSwiftSDK) } } } diff --git a/Sources/SwiftSDKGenerator/SystemUtils/ByteBuffer+Utils.swift b/Sources/SwiftSDKGenerator/SystemUtils/ByteBuffer+Utils.swift index f16b82a1..7dca8153 100644 --- a/Sources/SwiftSDKGenerator/SystemUtils/ByteBuffer+Utils.swift +++ b/Sources/SwiftSDKGenerator/SystemUtils/ByteBuffer+Utils.swift @@ -14,8 +14,8 @@ import AsyncProcess import Foundation import NIOCore -public extension ByteBuffer { - func unzip(zipPath: String, isVerbose: Bool) async throws -> ByteBuffer? { +extension ByteBuffer { + public func unzip(zipPath: String, isVerbose: Bool) async throws -> ByteBuffer? { let result = try await ProcessExecutor.runCollectingOutput( executable: zipPath, ["-cd"], standardInput: [self].async, diff --git a/Sources/SwiftSDKGenerator/SystemUtils/GeneratorError.swift b/Sources/SwiftSDKGenerator/SystemUtils/GeneratorError.swift index 2ecae653..e7706de3 100644 --- a/Sources/SwiftSDKGenerator/SystemUtils/GeneratorError.swift +++ b/Sources/SwiftSDKGenerator/SystemUtils/GeneratorError.swift @@ -39,7 +39,8 @@ extension GeneratorError: CustomStringConvertible { case let .nonZeroExitCode(exitCode, commandInfo): return "Process launched with \(commandInfo) failed with exit code \(exitCode)" case let .unknownLinuxDistribution(name, version): - return "Linux distribution `\(name)`\(version.map { " with version \($0)" } ?? "")` is not supported by this generator." + return + "Linux distribution `\(name)`\(version.map { " with version \($0)" } ?? "")` is not supported by this generator." case let .unknownMacOSVersion(version): return "macOS version `\(version)` is not supported by this generator." case let .unknownCPUArchitecture(cpu): @@ -48,21 +49,23 @@ extension GeneratorError: CustomStringConvertible { return "LLD version `\(version)` is not supported by this generator." case let .distributionSupportsOnlyDockerGenerator(linuxDistribution): return """ - Target Linux distribution \(linuxDistribution) supports Swift SDK generation only when `--with-docker` flag is \ - passed. - """ + Target Linux distribution \(linuxDistribution) supports Swift SDK generation only when `--with-docker` flag is \ + passed. + """ case let .distributionDoesNotSupportArchitecture(linuxDistribution, targetArchName): return """ - Target Linux distribution \(linuxDistribution) does not support the target architecture: \(targetArchName) - """ + Target Linux distribution \(linuxDistribution) does not support the target architecture: \(targetArchName) + """ case let .fileDoesNotExist(filePath): return "Expected to find a file at path `\(filePath)`." case let .fileDownloadFailed(url, status): - return "File could not be downloaded from a URL `\(url)`, the server returned status `\(status)`." + return + "File could not be downloaded from a URL `\(url)`, the server returned status `\(status)`." case .ubuntuPackagesDecompressionFailure: return "Failed to decompress the list of Ubuntu packages" case let .ubuntuPackagesParsingFailure(expected, actual): - return "Failed to parse Ubuntu packages manifest, expected \(expected), found \(actual) packages." + return + "Failed to parse Ubuntu packages manifest, expected \(expected), found \(actual) packages." } } } diff --git a/Sources/SwiftSDKGenerator/SystemUtils/HTTPClient+Download.swift b/Sources/SwiftSDKGenerator/SystemUtils/HTTPClient+Download.swift index a5abfeb2..b11d02ea 100644 --- a/Sources/SwiftSDKGenerator/SystemUtils/HTTPClient+Download.swift +++ b/Sources/SwiftSDKGenerator/SystemUtils/HTTPClient+Download.swift @@ -53,7 +53,9 @@ public protocol HTTPClientProtocol: Sendable { } extension HTTPClientProtocol { - static func with(_ body: @Sendable (any HTTPClientProtocol) async throws -> Result) async throws + static func with( + _ body: @Sendable (any HTTPClientProtocol) async throws -> Result + ) async throws -> Result { try await self.with(http1Only: false, body) @@ -63,107 +65,112 @@ extension HTTPClientProtocol { extension FilePath: @unchecked Sendable {} #if canImport(AsyncHTTPClient) -import AsyncHTTPClient - -extension FileDownloadDelegate.Progress: @unchecked Sendable {} - -extension HTTPClient: HTTPClientProtocol { - public static func with( - http1Only: Bool, _ body: @Sendable (any HTTPClientProtocol) async throws -> Result - ) async throws -> Result { - var configuration = HTTPClient.Configuration(redirectConfiguration: .follow(max: 5, allowCycles: false)) - if http1Only { - configuration.httpVersion = .http1Only - } - let client = HTTPClient(eventLoopGroupProvider: .singleton, configuration: configuration) - return try await withAsyncThrowing { - try await body(client) - } defer: { - try await client.shutdown() + import AsyncHTTPClient + + extension FileDownloadDelegate.Progress: @unchecked Sendable {} + + extension HTTPClient: HTTPClientProtocol { + public static func with( + http1Only: Bool, _ body: @Sendable (any HTTPClientProtocol) async throws -> Result + ) async throws -> Result { + var configuration = HTTPClient.Configuration( + redirectConfiguration: .follow(max: 5, allowCycles: false)) + if http1Only { + configuration.httpVersion = .http1Only + } + let client = HTTPClient(eventLoopGroupProvider: .singleton, configuration: configuration) + return try await withAsyncThrowing { + try await body(client) + } defer: { + try await client.shutdown() + } } - } - public func get(url: String) async throws -> (status: NIOHTTP1.HTTPResponseStatus, body: NIOCore.ByteBuffer?) { - let response = try await self.get(url: url).get() - return (status: response.status, body: response.body) - } + public func get(url: String) async throws -> ( + status: NIOHTTP1.HTTPResponseStatus, body: NIOCore.ByteBuffer? + ) { + let response = try await self.get(url: url).get() + return (status: response.status, body: response.body) + } - public func head(url: String, headers: NIOHTTP1.HTTPHeaders) async throws -> Bool { - var headRequest = HTTPClientRequest(url: url) - headRequest.method = .HEAD - headRequest.headers = ["Accept": "*/*", "User-Agent": "Swift SDK Generator"] - return try await self.execute(headRequest, deadline: .distantFuture).status == .ok - } + public func head(url: String, headers: NIOHTTP1.HTTPHeaders) async throws -> Bool { + var headRequest = HTTPClientRequest(url: url) + headRequest.method = .HEAD + headRequest.headers = ["Accept": "*/*", "User-Agent": "Swift SDK Generator"] + return try await self.execute(headRequest, deadline: .distantFuture).status == .ok + } - public func downloadFile( - from url: URL, - to path: FilePath - ) async throws { - try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<(), Error>) in - do { - let delegate = try FileDownloadDelegate( - path: path.string, - reportHead: { task, responseHead in - if responseHead.status != .ok { - task.fail(reason: GeneratorError.fileDownloadFailed(url, responseHead.status.description)) + public func downloadFile( + from url: URL, + to path: FilePath + ) async throws { + try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<(), Error>) in + do { + let delegate = try FileDownloadDelegate( + path: path.string, + reportHead: { task, responseHead in + if responseHead.status != .ok { + task.fail( + reason: GeneratorError.fileDownloadFailed(url, responseHead.status.description)) + } + } + ) + let request = try HTTPClient.Request(url: url) + + execute(request: request, delegate: delegate).futureResult.whenComplete { + switch $0 { + case let .failure(error): + continuation.resume(throwing: error) + case .success: + continuation.resume(returning: ()) } } - ) - let request = try HTTPClient.Request(url: url) - - execute(request: request, delegate: delegate).futureResult.whenComplete { - switch $0 { - case let .failure(error): - continuation.resume(throwing: error) - case .success: - continuation.resume(returning: ()) - } + } catch { + continuation.resume(throwing: error) } - } catch { - continuation.resume(throwing: error) } } - } - public func streamDownloadProgress( - from url: URL, - to path: FilePath - ) -> AsyncThrowingStream { - .init { continuation in - do { - let delegate = try FileDownloadDelegate( - path: path.string, - reportHead: { - if $0.status != .ok { - continuation - .finish(throwing: FileOperationError.downloadFailed(url, $0.status.description)) + public func streamDownloadProgress( + from url: URL, + to path: FilePath + ) -> AsyncThrowingStream { + .init { continuation in + do { + let delegate = try FileDownloadDelegate( + path: path.string, + reportHead: { + if $0.status != .ok { + continuation + .finish(throwing: FileOperationError.downloadFailed(url, $0.status.description)) + } + }, + reportProgress: { + continuation.yield( + DownloadProgress(totalBytes: $0.totalBytes, receivedBytes: $0.receivedBytes) + ) + } + ) + let request = try HTTPClient.Request(url: url) + + execute(request: request, delegate: delegate).futureResult.whenComplete { + switch $0 { + case let .failure(error): + continuation.finish(throwing: error) + case let .success(finalProgress): + continuation.yield( + DownloadProgress( + totalBytes: finalProgress.totalBytes, receivedBytes: finalProgress.receivedBytes) + ) + continuation.finish() } - }, - reportProgress: { - continuation.yield( - DownloadProgress(totalBytes: $0.totalBytes, receivedBytes: $0.receivedBytes) - ) - } - ) - let request = try HTTPClient.Request(url: url) - - execute(request: request, delegate: delegate).futureResult.whenComplete { - switch $0 { - case let .failure(error): - continuation.finish(throwing: error) - case let .success(finalProgress): - continuation.yield( - DownloadProgress(totalBytes: finalProgress.totalBytes, receivedBytes: finalProgress.receivedBytes) - ) - continuation.finish() } + } catch { + continuation.finish(throwing: error) } - } catch { - continuation.finish(throwing: error) } } } -} #endif struct OfflineHTTPClient: HTTPClientProtocol { @@ -189,11 +196,15 @@ struct OfflineHTTPClient: HTTPClientProtocol { } } - public func get(url: String) async throws -> (status: NIOHTTP1.HTTPResponseStatus, body: NIOCore.ByteBuffer?) { - throw FileOperationError.downloadFailed(URL(string: url)!, "Cannot fetch file with offline client") + public func get(url: String) async throws -> ( + status: NIOHTTP1.HTTPResponseStatus, body: NIOCore.ByteBuffer? + ) { + throw FileOperationError.downloadFailed( + URL(string: url)!, "Cannot fetch file with offline client") } public func head(url: String, headers: NIOHTTP1.HTTPHeaders) async throws -> Bool { - throw FileOperationError.downloadFailed(URL(string: url)!, "Cannot fetch file with offline client") + throw FileOperationError.downloadFailed( + URL(string: url)!, "Cannot fetch file with offline client") } } diff --git a/Sources/SwiftSDKGenerator/SystemUtils/Shell.swift b/Sources/SwiftSDKGenerator/SystemUtils/Shell.swift index de62dfbb..62bc1ca5 100644 --- a/Sources/SwiftSDKGenerator/SystemUtils/Shell.swift +++ b/Sources/SwiftSDKGenerator/SystemUtils/Shell.swift @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// import AsyncProcess + import class Foundation.ProcessInfo import struct SystemPackage.FilePath @@ -125,7 +126,9 @@ struct Shell { try result.exitReason.throwIfNonZero() - guard let stdOutBuffer = result.standardOutput else { throw GeneratorError.noProcessOutput(command) } + guard let stdOutBuffer = result.standardOutput else { + throw GeneratorError.noProcessOutput(command) + } return String(buffer: stdOutBuffer) } diff --git a/Sources/SwiftSDKGenerator/SystemUtils/UnixName.swift b/Sources/SwiftSDKGenerator/SystemUtils/UnixName.swift index 3d9135c3..9389e6f1 100644 --- a/Sources/SwiftSDKGenerator/SystemUtils/UnixName.swift +++ b/Sources/SwiftSDKGenerator/SystemUtils/UnixName.swift @@ -1,7 +1,19 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift 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 +// +//===----------------------------------------------------------------------===// + #if canImport(Darwin) -import Darwin + import Darwin #elseif canImport(Glibc) -import Glibc + import Glibc #endif /// libc's `uname` wrapper diff --git a/Sources/SwiftSDKGenerator/SystemUtils/which.swift b/Sources/SwiftSDKGenerator/SystemUtils/which.swift index 472c32b2..040a7462 100644 --- a/Sources/SwiftSDKGenerator/SystemUtils/which.swift +++ b/Sources/SwiftSDKGenerator/SystemUtils/which.swift @@ -14,13 +14,14 @@ import AsyncProcess import Foundation /// Look for an executable using the `which` utility. -/// +/// /// - Parameter executableName: The name of the executable to search for. /// - Throws: Any errors thrown by the ProcessExecutor. /// - Returns: The path to the executable if found, otherwise nil. func which(_ executableName: String) async throws -> String? { let result = try await ProcessExecutor.runCollectingOutput( - executable: "/usr/bin/which", [executableName], collectStandardOutput: true, collectStandardError: false, + executable: "/usr/bin/which", [executableName], collectStandardOutput: true, + collectStandardError: false, environment: ProcessInfo.processInfo.environment ) diff --git a/Tests/AsyncProcessTests/AsyncByteBufferLineSequenceTests.swift b/Tests/AsyncProcessTests/AsyncByteBufferLineSequenceTests.swift index 1ce58a59..4f56cf00 100644 --- a/Tests/AsyncProcessTests/AsyncByteBufferLineSequenceTests.swift +++ b/Tests/AsyncProcessTests/AsyncByteBufferLineSequenceTests.swift @@ -43,7 +43,8 @@ final class AsyncByteBufferLineSequenceTests: XCTestCase { func testManyChunksNoNewlineNotDeliveringLastChunk() async throws { for n in 0..<100 { let inputs: [ByteBuffer] = [ByteBuffer(repeating: 0, count: n)] - let lines = try await Array(inputs.async.splitIntoLines(dropLastChunkIfNoNewline: true).strings) + let lines = try await Array( + inputs.async.splitIntoLines(dropLastChunkIfNoNewline: true).strings) XCTAssertEqual([], lines) } } @@ -61,11 +62,11 @@ final class AsyncByteBufferLineSequenceTests: XCTestCase { } func testOverlyLongLineIsSplitByDefault() async throws { - var inputs = [ByteBuffer(repeating: UInt8(0), count: 1024 * 1024 - 2)] // almost at the limit + var inputs = [ByteBuffer(repeating: UInt8(0), count: 1024 * 1024 - 2)] // almost at the limit inputs.append(ByteBuffer(integer: UInt8(ascii: "\0"))) - inputs.append(ByteBuffer(integer: UInt8(ascii: "\0"))) // hitting the limit - inputs.append(ByteBuffer(integer: UInt8(ascii: "\0"))) // over the limit - inputs.append(ByteBuffer(integer: UInt8(ascii: "\n"))) // too late + inputs.append(ByteBuffer(integer: UInt8(ascii: "\0"))) // hitting the limit + inputs.append(ByteBuffer(integer: UInt8(ascii: "\0"))) // over the limit + inputs.append(ByteBuffer(integer: UInt8(ascii: "\n"))) // too late let lines = try await Array( inputs.async.splitIntoLines( dropTerminator: false, diff --git a/Tests/AsyncProcessTests/IntegrationTests.swift b/Tests/AsyncProcessTests/IntegrationTests.swift index 7b5a4ee3..50d3c8a7 100644 --- a/Tests/AsyncProcessTests/IntegrationTests.swift +++ b/Tests/AsyncProcessTests/IntegrationTests.swift @@ -19,9 +19,9 @@ import NIOConcurrencyHelpers import XCTest #if canImport(Darwin) -import Darwin + import Darwin #else -import Glibc + import Glibc #endif final class IntegrationTests: XCTestCase { @@ -69,10 +69,10 @@ final class IntegrationTests: XCTestCase { func testSignalsWork() async throws { #if os(Linux) - // workaround for https://github.com/apple/swift-corelibs-foundation/issues/4772 - let signalsToTest: [CInt] = [SIGKILL] + // workaround for https://github.com/apple/swift-corelibs-foundation/issues/4772 + let signalsToTest: [CInt] = [SIGKILL] #else - let signalsToTest: [CInt] = [SIGKILL, SIGTERM, SIGINT] + let signalsToTest: [CInt] = [SIGKILL, SIGTERM, SIGINT] #endif for signal in signalsToTest { let exe = ProcessExecutor( @@ -99,7 +99,7 @@ final class IntegrationTests: XCTestCase { let input = AsyncStream.justMakeIt(elementType: ByteBuffer.self) let exe = ProcessExecutor( group: self.group, - executable: "/bin/cat", ["-nu"], // sh", ["-c", "while read -r line; do echo $line; done"], + executable: "/bin/cat", ["-nu"], // sh", ["-c", "while read -r line; do echo $line; done"], standardInput: input.consumer, logger: self.logger ) @@ -521,7 +521,7 @@ final class IntegrationTests: XCTestCase { func testLogOutputToMetadata() async throws { let sharedRecorder = LogRecorderHandler() var recordedLogger = Logger(label: "recorder", factory: { _ in sharedRecorder }) - recordedLogger.logLevel = .info // don't give us the normal messages + recordedLogger.logLevel = .info // don't give us the normal messages recordedLogger[metadataKey: "yo"] = "hey" try await ProcessExecutor.runLogOutput( @@ -530,20 +530,22 @@ final class IntegrationTests: XCTestCase { ["-c", "echo 1; echo >&2 2; echo 3; echo >&2 4; echo 5; echo >&2 6; echo 7; echo >&2 8;"], standardInput: EOFSequence(), logger: recordedLogger, - logConfiguration: OutputLoggingSettings(logLevel: .critical, to: .metadata(logMessage: "msg", key: "key")) + logConfiguration: OutputLoggingSettings( + logLevel: .critical, to: .metadata(logMessage: "msg", key: "key")) ).throwIfNonZero() XCTAssert(sharedRecorder.recordedMessages.allSatisfy { $0.level == .critical }) XCTAssert(sharedRecorder.recordedMessages.allSatisfy { $0.message == "msg" }) XCTAssert(sharedRecorder.recordedMessages.allSatisfy { $0.metadata["key"] != nil }) XCTAssert(sharedRecorder.recordedMessages.allSatisfy { $0.metadata["yo"] == "hey" }) - let loggedLines = sharedRecorder.recordedMessages.compactMap { $0.metadata["key"]?.description }.sorted() + let loggedLines = sharedRecorder.recordedMessages.compactMap { $0.metadata["key"]?.description } + .sorted() XCTAssertEqual(["1", "2", "3", "4", "5", "6", "7", "8"], loggedLines) } func testLogOutputToMessage() async throws { let sharedRecorder = LogRecorderHandler() var recordedLogger = Logger(label: "recorder", factory: { _ in sharedRecorder }) - recordedLogger.logLevel = .info // don't give us the normal messages + recordedLogger.logLevel = .info // don't give us the normal messages recordedLogger[metadataKey: "yo"] = "hey" try await ProcessExecutor.runLogOutput( @@ -633,7 +635,8 @@ final class IntegrationTests: XCTestCase { func testCollectJustStandardError() async throws { let allInfo = try await ProcessExecutor.runCollectingOutput( group: self.group, - executable: "/bin/sh", ["-c", "/bin/dd >&2 if=/dev/zero bs=\(1024 * 1024) count=1 status=none"], + executable: "/bin/sh", + ["-c", "/bin/dd >&2 if=/dev/zero bs=\(1024 * 1024) count=1 status=none"], standardInput: EOFSequence(), collectStandardOutput: false, collectStandardError: true, @@ -648,7 +651,8 @@ final class IntegrationTests: XCTestCase { func testCollectNothing() async throws { let allInfo = try await ProcessExecutor.runCollectingOutput( group: self.group, - executable: "/bin/sh", ["-c", "/bin/dd >&2 if=/dev/zero bs=\(1024 * 1024) count=100 status=none"], + executable: "/bin/sh", + ["-c", "/bin/dd >&2 if=/dev/zero bs=\(1024 * 1024) count=100 status=none"], standardInput: EOFSequence(), collectStandardOutput: false, collectStandardError: false, @@ -757,8 +761,8 @@ final class IntegrationTests: XCTestCase { } catch { XCTAssertEqual(NSCocoaErrorDomain, (error as NSError).domain) #if canImport(Darwin) - // https://github.com/apple/swift-corelibs-foundation/issues/4810 - XCTAssertEqual(NSFileNoSuchFileError, (error as NSError).code) + // https://github.com/apple/swift-corelibs-foundation/issues/4810 + XCTAssertEqual(NSFileNoSuchFileError, (error as NSError).code) #endif } } @@ -811,23 +815,23 @@ final class IntegrationTests: XCTestCase { let exeStream = ProcessExecutor(executable: "/bin/sh", ["-c", "true"]) #if compiler(>=5.8) - async let stdout = Array(exeStream.standardOutput) - async let stderr = Array(exeStream.standardError) + async let stdout = Array(exeStream.standardOutput) + async let stderr = Array(exeStream.standardError) #else - async let stdout = { - var chunks: [ByteBuffer] = [] - for try await chunk in await exeStream.standardOutput { - chunks.append(chunk) - } - return chunks - }() - async let stderr = { - var chunks: [ByteBuffer] = [] - for try await chunk in await exeStream.standardError { - chunks.append(chunk) - } - return chunks - }() + async let stdout = { + var chunks: [ByteBuffer] = [] + for try await chunk in await exeStream.standardOutput { + chunks.append(chunk) + } + return chunks + }() + async let stderr = { + var chunks: [ByteBuffer] = [] + for try await chunk in await exeStream.standardError { + chunks.append(chunk) + } + return chunks + }() #endif try await exeStream.run().throwIfNonZero() let out = try await stdout @@ -863,7 +867,9 @@ final class IntegrationTests: XCTestCase { XCTAssertNoThrow(try FileManager.default.removeItem(at: tempDir)) } - for (stdoutMode, stderrMode) in [("shared", "shared"), ("shared", "owned"), ("owned", "shared")] { + for (stdoutMode, stderrMode) in [ + ("shared", "shared"), ("shared", "owned"), ("owned", "shared"), + ] { let filePath = tempDir.appendingPathComponent("file-\(stdoutMode)-\(stderrMode)") let fd = try FileDescriptor.open( .init(filePath.path.removingPercentEncoding!), @@ -892,14 +898,14 @@ final class IntegrationTests: XCTestCase { } #if canImport(Darwin) - let command = - "for o in 1 2; do i=1000; while [ $i -gt 0 ]; do echo $o >&$o; i=$(( $i - 1 )); done & done; wait" + let command = + "for o in 1 2; do i=1000; while [ $i -gt 0 ]; do echo $o >&$o; i=$(( $i - 1 )); done & done; wait" #else - // workaround for - // https://github.com/apple/swift-corelibs-foundation/issues/4772 - // which causes `SIGCHLD` being blocked in the shell so it can't wait for its children :| - let command = - "for o in 1 2; do i=1000; while [ $i -gt 0 ]; do echo $o >&$o; i=$(( $i - 1 )); done & done; sleep 10" + // workaround for + // https://github.com/apple/swift-corelibs-foundation/issues/4772 + // which causes `SIGCHLD` being blocked in the shell so it can't wait for its children :| + let command = + "for o in 1 2; do i=1000; while [ $i -gt 0 ]; do echo $o >&$o; i=$(( $i - 1 )); done & done; sleep 10" #endif let exe = ProcessExecutor( @@ -1020,71 +1026,71 @@ final class IntegrationTests: XCTestCase { } #if os(macOS) - // This test will hang on anything that uses swift-corelibs-foundation because of - // https://github.com/apple/swift-corelibs-foundation/issues/4795 - // Foundation.Process on Linux doesn't correctly detect when child process dies (creating zombie processes) - func testCanDealWithRunawayChildProcesses() async throws { - self.logger = Logger(label: "x") - self.logger.logLevel = .info - let p = ProcessExecutor( - executable: "/bin/bash", - [ - "-c", - """ - set -e - /usr/bin/yes "Runaway process from \(#function), please file a swift-sdk-generator bug." > /dev/null & - child_pid=$! - trap "echo >&2 killing $child_pid; kill -KILL $child_pid" INT - echo "$child_pid" # communicate the child pid to our parent - exec >&- # close stdout - echo "waiting for $child_pid" >&2 - wait - """, - ], - standardError: .discard, - teardownSequence: [ - .sendSignal(SIGINT, allowedTimeToExitNS: 10_000_000_000), - ], - logger: self.logger - ) - - try await withThrowingTaskGroup(of: pid_t?.self) { group in - group.addTask { - let result = try await p.run() - XCTAssertEqual(.exit(128 + SIGINT), result) - return nil - } + // This test will deadlock on anything that uses swift-corelibs-foundation because of + // https://github.com/apple/swift-corelibs-foundation/issues/4795 + // Foundation.Process on Linux doesn't correctly detect when child process dies (creating zombie processes) + func testCanDealWithRunawayChildProcesses() async throws { + self.logger = Logger(label: "x") + self.logger.logLevel = .info + let p = ProcessExecutor( + executable: "/bin/bash", + [ + "-c", + """ + set -e + /usr/bin/yes "Runaway process from \(#function), please file a swift-sdk-generator bug." > /dev/null & + child_pid=$! + trap "echo >&2 killing $child_pid; kill -KILL $child_pid" INT + echo "$child_pid" # communicate the child pid to our parent + exec >&- # close stdout + echo "waiting for $child_pid" >&2 + wait + """, + ], + standardError: .discard, + teardownSequence: [ + .sendSignal(SIGINT, allowedTimeToExitNS: 10_000_000_000) + ], + logger: self.logger + ) - group.addTask { - let pidString = try await String(buffer: p.standardOutput.pullAllOfIt()) - guard let pid = pid_t(pidString.dropLast()) else { - XCTFail("couldn't get pid from \(pidString)") + try await withThrowingTaskGroup(of: pid_t?.self) { group in + group.addTask { + let result = try await p.run() + XCTAssertEqual(.exit(128 + SIGINT), result) return nil } - return pid - } - let maybePid = try await group.next()! - let pid = try XCTUnwrap(maybePid) - group.cancelAll() - try await group.waitForAll() + group.addTask { + let pidString = try await String(buffer: p.standardOutput.pullAllOfIt()) + guard let pid = pid_t(pidString.dropLast()) else { + XCTFail("couldn't get pid from \(pidString)") + return nil + } + return pid + } - // Let's check that the subprocess (/usr/bin/yes) of our subprocess (/bin/bash) is actually dead - // This is a tiny bit racy because the pid isn't immediately invalidated, so let's allow a few failures - for attempt in 0 ..< .max { - let killRet = kill(pid, 0) - let errnoCode = errno - guard killRet == -1 || attempt > 5 else { - logger.error("kill didn't fail on attempt \(attempt), trying again...") - usleep(100_000) - continue + let maybePid = try await group.next()! + let pid = try XCTUnwrap(maybePid) + group.cancelAll() + try await group.waitForAll() + + // Let's check that the subprocess (/usr/bin/yes) of our subprocess (/bin/bash) is actually dead + // This is a tiny bit racy because the pid isn't immediately invalidated, so let's allow a few failures + for attempt in 0 ..< .max { + let killRet = kill(pid, 0) + let errnoCode = errno + guard killRet == -1 || attempt > 5 else { + logger.error("kill didn't fail on attempt \(attempt), trying again...") + usleep(100_000) + continue + } + XCTAssertEqual(-1, killRet) + XCTAssertEqual(ESRCH, errnoCode) + break } - XCTAssertEqual(-1, killRet) - XCTAssertEqual(ESRCH, errnoCode) - break - } + } } - } #endif func testShutdownSequenceWorks() async throws { @@ -1115,8 +1121,8 @@ final class IntegrationTests: XCTestCase { group.addTask { let result = try await p.run() #if os(macOS) - // won't work on SCLF: https://github.com/apple/swift-corelibs-foundation/issues/4772 - XCTAssertEqual(.exit(3), result) + // won't work on SCLF: https://github.com/apple/swift-corelibs-foundation/issues/4772 + XCTAssertEqual(.exit(3), result) #endif } var allLines: [String] = [] @@ -1128,8 +1134,8 @@ final class IntegrationTests: XCTestCase { } try await group.waitForAll() #if os(macOS) - // won't work on SCLF: https://github.com/apple/swift-corelibs-foundation/issues/4772 - XCTAssertEqual(["OK", "saw SIGQUIT", "saw SIGTERM", "saw SIGINT"], allLines) + // won't work on SCLF: https://github.com/apple/swift-corelibs-foundation/issues/4772 + XCTAssertEqual(["OK", "saw SIGQUIT", "saw SIGTERM", "saw SIGINT"], allLines) #endif } } diff --git a/Tests/GeneratorEngineTests/EngineTests.swift b/Tests/GeneratorEngineTests/EngineTests.swift index 2e1a90b2..5a3a95d0 100644 --- a/Tests/GeneratorEngineTests/EngineTests.swift +++ b/Tests/GeneratorEngineTests/EngineTests.swift @@ -10,18 +10,22 @@ // //===----------------------------------------------------------------------===// -import struct Foundation.Data import Crypto -@testable import Helpers +import XCTest + +import struct Foundation.Data import struct Logging.Logger import struct SystemPackage.FilePath -import XCTest + +@testable import Helpers private let encoder = JSONEncoder() private let decoder = JSONDecoder() -private extension AsyncFileSystem { - func read(_ path: FilePath, bufferLimit: Int = 10 * 1024 * 1024, as: V.Type) async throws -> V { +extension AsyncFileSystem { + fileprivate func read( + _ path: FilePath, bufferLimit: Int = 10 * 1024 * 1024, as: V.Type + ) async throws -> V { let data = try await self.withOpenReadableFile(path) { var data = Data() for try await chunk in try await $0.read() { @@ -35,7 +39,7 @@ private extension AsyncFileSystem { return try decoder.decode(V.self, from: data) } - func write(_ path: FilePath, _ value: some Encodable) async throws { + fileprivate func write(_ path: FilePath, _ value: some Encodable) async throws { let data = try encoder.encode(value) try await self.withOpenWritableFile(path) { fileHandle in try await fileHandle.write(data) @@ -116,7 +120,7 @@ final class EngineTests: XCTestCase { let engine = QueryEngine( MockFileSystem(), Logger(label: "engine-tests") -// cacheLocation: .memory + // cacheLocation: .memory ) var resultPath = try await engine[Expression(x: 1, y: 2)].path @@ -172,9 +176,11 @@ final class EngineTests: XCTestCase { func testQueryEncoding() throws { let item = MyItem( remoteURL: URL( - string: "https://download.swift.org/swift-5.9.2-release/ubuntu2204-aarch64/swift-5.9.2-RELEASE/swift-5.9.2-RELEASE-ubuntu22.04-aarch64.tar.gz" + string: + "https://download.swift.org/swift-5.9.2-release/ubuntu2204-aarch64/swift-5.9.2-RELEASE/swift-5.9.2-RELEASE-ubuntu22.04-aarch64.tar.gz" )!, - localPath: "/Users/katei/ghq/github.com/apple/swift-sdk-generator/Artifacts/target_swift_5.9.2-RELEASE_aarch64-unknown-linux-gnu.tar.gz", + localPath: + "/Users/katei/ghq/github.com/apple/swift-sdk-generator/Artifacts/target_swift_5.9.2-RELEASE_aarch64-unknown-linux-gnu.tar.gz", isPrebuilt: true ) func hashValue(of key: some CacheKey) throws -> SHA256Digest { diff --git a/Tests/HelpersTests/ThrowingDeferTests.swift b/Tests/HelpersTests/ThrowingDeferTests.swift index 2dc530e5..03a0d3fd 100644 --- a/Tests/HelpersTests/ThrowingDeferTests.swift +++ b/Tests/HelpersTests/ThrowingDeferTests.swift @@ -56,11 +56,13 @@ final class ThrowingDeferTests: XCTestCase { let workError = EquatableError() var didRunCleanup = false - XCTAssertThrowsError(try withThrowing { - throw workError - } defer: { - didRunCleanup = true - }) { + XCTAssertThrowsError( + try withThrowing { + throw workError + } defer: { + didRunCleanup = true + } + ) { XCTAssertTrue($0 is EquatableError) XCTAssertEqual($0 as? EquatableError, workError) } @@ -71,11 +73,13 @@ final class ThrowingDeferTests: XCTestCase { var didRunWork = false let cleanupError = EquatableError() - XCTAssertThrowsError(try withThrowing { - didRunWork = true - } defer: { - throw cleanupError - }) { + XCTAssertThrowsError( + try withThrowing { + didRunWork = true + } defer: { + throw cleanupError + } + ) { XCTAssertTrue($0 is EquatableError) XCTAssertEqual($0 as? EquatableError, cleanupError) } @@ -87,12 +91,14 @@ final class ThrowingDeferTests: XCTestCase { let workError = EquatableError() let cleanupError = EquatableError() - XCTAssertThrowsError(try withThrowing { - didRunWork = true - throw workError - } defer: { - throw cleanupError - }) { + XCTAssertThrowsError( + try withThrowing { + didRunWork = true + throw workError + } defer: { + throw cleanupError + } + ) { XCTAssertTrue($0 is EquatableError) XCTAssertEqual($0 as? EquatableError, cleanupError) } diff --git a/Tests/MacrosTests/MacrosTests.swift b/Tests/MacrosTests/MacrosTests.swift index f9a0b46b..c8c457f6 100644 --- a/Tests/MacrosTests/MacrosTests.swift +++ b/Tests/MacrosTests/MacrosTests.swift @@ -1,6 +1,6 @@ //===----------------------------------------------------------------------===// // -// This source file is part of the Swift.org open source project +// This source file is part of the Swift open source project // // Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception @@ -16,7 +16,9 @@ import SwiftSyntaxMacrosTestSupport import XCTest final class MacrosTests: XCTestCase { - private let macros: [String: Macro.Type] = ["CacheKey": CacheKeyMacro.self, "Query": QueryMacro.self] + private let macros: [String: Macro.Type] = [ + "CacheKey": CacheKeyMacro.self, "Query": QueryMacro.self, + ] func testCacheKeyDerived() { assertMacroExpansion( @@ -34,34 +36,34 @@ final class MacrosTests: XCTestCase { } """, expandedSource: """ - struct Message { - let text: String - let sender: String - } - struct Q { - let number: Int - let text: String - } + struct Message { + let text: String + let sender: String + } + struct Q { + let number: Int + let text: String + } - extension Message: CacheKeyProtocol { - func hash(with hashFunction: inout some HashFunction) { - String(reflecting: Self.self).hash(with: &hashFunction) - text.hash(with: &hashFunction) - sender.hash(with: &hashFunction) + extension Message: CacheKeyProtocol { + func hash(with hashFunction: inout some HashFunction) { + String(reflecting: Self.self).hash(with: &hashFunction) + text.hash(with: &hashFunction) + sender.hash(with: &hashFunction) + } } - } - extension Q: QueryProtocol { - } + extension Q: QueryProtocol { + } - extension Q: CacheKeyProtocol { - func hash(with hashFunction: inout some HashFunction) { - String(reflecting: Self.self).hash(with: &hashFunction) - number.hash(with: &hashFunction) - text.hash(with: &hashFunction) + extension Q: CacheKeyProtocol { + func hash(with hashFunction: inout some HashFunction) { + String(reflecting: Self.self).hash(with: &hashFunction) + number.hash(with: &hashFunction) + text.hash(with: &hashFunction) + } } - } - """, + """, macros: self.macros, indentationWidth: .spaces(2) ) diff --git a/Tests/SwiftSDKGeneratorTests/ArchitectureMappingTests.swift b/Tests/SwiftSDKGeneratorTests/ArchitectureMappingTests.swift index 9202c2a4..893d8c7a 100644 --- a/Tests/SwiftSDKGeneratorTests/ArchitectureMappingTests.swift +++ b/Tests/SwiftSDKGeneratorTests/ArchitectureMappingTests.swift @@ -11,9 +11,10 @@ //===----------------------------------------------------------------------===// import Logging -@testable import SwiftSDKGenerator import XCTest +@testable import SwiftSDKGenerator + final class ArchitectureMappingTests: XCTestCase { let logger = Logger(label: "ArchitectureMappingTests") @@ -39,12 +40,12 @@ final class ArchitectureMappingTests: XCTestCase { hostTriple: Triple, targetTriple: Triple, - artifactID: String, // Base name of the generated bundle - hostLLVMDownloadURL: String, // URL of the host LLVM package - targetSwiftDownloadURL: String, // URL of the target Swift SDK + artifactID: String, // Base name of the generated bundle + hostLLVMDownloadURL: String, // URL of the host LLVM package + targetSwiftDownloadURL: String, // URL of the target Swift SDK - artifactBundlePathSuffix: String, // Path to the generated bundle - sdkDirPathSuffix: String // Path of the SDK within the bundle + artifactBundlePathSuffix: String, // Path to the generated bundle + sdkDirPathSuffix: String // Path of the SDK within the bundle ) async throws { let recipe = try LinuxRecipe( targetTriple: targetTriple, @@ -130,8 +131,10 @@ final class ArchitectureMappingTests: XCTestCase { hostTriple: Triple("x86_64-apple-macosx13"), targetTriple: Triple("x86_64-unknown-linux-gnu"), artifactID: "5.8-RELEASE_ubuntu_jammy_x86_64", - hostLLVMDownloadURL: "https://github.com/llvm/llvm-project/releases/download/llvmorg-16.0.4/clang+llvm-16.0.4-x86_64-apple-darwin22.0.tar.xz", - targetSwiftDownloadURL: "https://download.swift.org/swift-5.8-release/ubuntu2204/swift-5.8-RELEASE/swift-5.8-RELEASE-ubuntu22.04.tar.gz", + hostLLVMDownloadURL: + "https://github.com/llvm/llvm-project/releases/download/llvmorg-16.0.4/clang+llvm-16.0.4-x86_64-apple-darwin22.0.tar.xz", + targetSwiftDownloadURL: + "https://download.swift.org/swift-5.8-release/ubuntu2204/swift-5.8-RELEASE/swift-5.8-RELEASE-ubuntu22.04.tar.gz", artifactBundlePathSuffix: "/Bundles/5.8-RELEASE_ubuntu_jammy_x86_64.artifactbundle", sdkDirPathSuffix: "/5.8-RELEASE_ubuntu_jammy_x86_64/x86_64-unknown-linux-gnu/ubuntu-jammy.sdk" ) @@ -143,10 +146,13 @@ final class ArchitectureMappingTests: XCTestCase { hostTriple: Triple("x86_64-apple-macosx13"), targetTriple: Triple("aarch64-unknown-linux-gnu"), artifactID: "5.8-RELEASE_ubuntu_jammy_aarch64", - hostLLVMDownloadURL: "https://github.com/llvm/llvm-project/releases/download/llvmorg-16.0.4/clang+llvm-16.0.4-x86_64-apple-darwin22.0.tar.xz", - targetSwiftDownloadURL: "https://download.swift.org/swift-5.8-release/ubuntu2204-aarch64/swift-5.8-RELEASE/swift-5.8-RELEASE-ubuntu22.04-aarch64.tar.gz", + hostLLVMDownloadURL: + "https://github.com/llvm/llvm-project/releases/download/llvmorg-16.0.4/clang+llvm-16.0.4-x86_64-apple-darwin22.0.tar.xz", + targetSwiftDownloadURL: + "https://download.swift.org/swift-5.8-release/ubuntu2204-aarch64/swift-5.8-RELEASE/swift-5.8-RELEASE-ubuntu22.04-aarch64.tar.gz", artifactBundlePathSuffix: "/Bundles/5.8-RELEASE_ubuntu_jammy_aarch64.artifactbundle", - sdkDirPathSuffix: "/5.8-RELEASE_ubuntu_jammy_aarch64/aarch64-unknown-linux-gnu/ubuntu-jammy.sdk" + sdkDirPathSuffix: + "/5.8-RELEASE_ubuntu_jammy_aarch64/aarch64-unknown-linux-gnu/ubuntu-jammy.sdk" ) } @@ -156,10 +162,13 @@ final class ArchitectureMappingTests: XCTestCase { hostTriple: Triple("arm64-apple-macosx13"), targetTriple: Triple("aarch64-unknown-linux-gnu"), artifactID: "5.8-RELEASE_ubuntu_jammy_aarch64", - hostLLVMDownloadURL: "https://github.com/llvm/llvm-project/releases/download/llvmorg-16.0.4/clang+llvm-16.0.4-arm64-apple-darwin22.0.tar.xz", - targetSwiftDownloadURL: "https://download.swift.org/swift-5.8-release/ubuntu2204-aarch64/swift-5.8-RELEASE/swift-5.8-RELEASE-ubuntu22.04-aarch64.tar.gz", + hostLLVMDownloadURL: + "https://github.com/llvm/llvm-project/releases/download/llvmorg-16.0.4/clang+llvm-16.0.4-arm64-apple-darwin22.0.tar.xz", + targetSwiftDownloadURL: + "https://download.swift.org/swift-5.8-release/ubuntu2204-aarch64/swift-5.8-RELEASE/swift-5.8-RELEASE-ubuntu22.04-aarch64.tar.gz", artifactBundlePathSuffix: "/Bundles/5.8-RELEASE_ubuntu_jammy_aarch64.artifactbundle", - sdkDirPathSuffix: "/5.8-RELEASE_ubuntu_jammy_aarch64/aarch64-unknown-linux-gnu/ubuntu-jammy.sdk" + sdkDirPathSuffix: + "/5.8-RELEASE_ubuntu_jammy_aarch64/aarch64-unknown-linux-gnu/ubuntu-jammy.sdk" ) } @@ -169,8 +178,10 @@ final class ArchitectureMappingTests: XCTestCase { hostTriple: Triple("arm64-apple-macosx13"), targetTriple: Triple("x86_64-unknown-linux-gnu"), artifactID: "5.8-RELEASE_ubuntu_jammy_x86_64", - hostLLVMDownloadURL: "https://github.com/llvm/llvm-project/releases/download/llvmorg-16.0.4/clang+llvm-16.0.4-arm64-apple-darwin22.0.tar.xz", - targetSwiftDownloadURL: "https://download.swift.org/swift-5.8-release/ubuntu2204/swift-5.8-RELEASE/swift-5.8-RELEASE-ubuntu22.04.tar.gz", + hostLLVMDownloadURL: + "https://github.com/llvm/llvm-project/releases/download/llvmorg-16.0.4/clang+llvm-16.0.4-arm64-apple-darwin22.0.tar.xz", + targetSwiftDownloadURL: + "https://download.swift.org/swift-5.8-release/ubuntu2204/swift-5.8-RELEASE/swift-5.8-RELEASE-ubuntu22.04.tar.gz", artifactBundlePathSuffix: "/Bundles/5.8-RELEASE_ubuntu_jammy_x86_64.artifactbundle", sdkDirPathSuffix: "/5.8-RELEASE_ubuntu_jammy_x86_64/x86_64-unknown-linux-gnu/ubuntu-jammy.sdk" ) diff --git a/Tests/SwiftSDKGeneratorTests/EndToEndTests.swift b/Tests/SwiftSDKGeneratorTests/EndToEndTests.swift index 15b19c98..e5abd836 100644 --- a/Tests/SwiftSDKGeneratorTests/EndToEndTests.swift +++ b/Tests/SwiftSDKGeneratorTests/EndToEndTests.swift @@ -18,7 +18,9 @@ import XCTest @testable import SwiftSDKGenerator extension FileManager { - func withTemporaryDirectory(logger: Logger, cleanup: Bool = true, body: (URL) async throws -> T) async throws -> T { + func withTemporaryDirectory( + logger: Logger, cleanup: Bool = true, body: (URL) async throws -> T + ) async throws -> T { // Create a temporary directory using a UUID. Throws if the directory already exists. // The docs suggest using FileManager.url(for: .itemReplacementDirectory, ...) to create a temporary directory, // but on Linux the directory name contains spaces, which means we need to be careful to quote it everywhere: @@ -33,15 +35,15 @@ extension FileManager { try createDirectory(at: temporaryDirectory, withIntermediateDirectories: false) defer { - // Best effort cleanup. - do { - if cleanup { - try removeItem(at: temporaryDirectory) - logger.info("Removed temporary directory") - } else { - logger.info("Keeping temporary directory") - } - } catch {} + // Best effort cleanup. + do { + if cleanup { + try removeItem(at: temporaryDirectory) + logger.info("Removed temporary directory") + } else { + logger.info("Keeping temporary directory") + } + } catch {} } logger.info("Created temporary directory") @@ -53,7 +55,9 @@ extension FileManager { // This takes a lock on `.build`, but if the tests are being run by `swift test` the outer Swift Package Manager // instance will already hold this lock, causing the test to deadlock. We can work around this by giving // the `swift run swift-sdk-generator` instance its own scratch directory. -func buildSDK(_ logger: Logger, scratchPath: String, withArguments runArguments: String) async throws -> String { +func buildSDK(_ logger: Logger, scratchPath: String, withArguments runArguments: String) + async throws -> String +{ var logger = logger logger[metadataKey: "runArguments"] = "\"\(runArguments)\"" logger[metadataKey: "scratchPath"] = "\(scratchPath)" @@ -69,9 +73,10 @@ func buildSDK(_ logger: Logger, scratchPath: String, withArguments runArguments: ) logger.info("Finished building SDK") - let installCommand = try XCTUnwrap(generatorOutput.split(separator: "\n").first { - $0.contains("swift experimental-sdk install") - }) + let installCommand = try XCTUnwrap( + generatorOutput.split(separator: "\n").first { + $0.contains("swift experimental-sdk install") + }) let bundleName = try XCTUnwrap( FilePath(String(XCTUnwrap(installCommand.split(separator: " ").last))).components.last @@ -79,7 +84,8 @@ func buildSDK(_ logger: Logger, scratchPath: String, withArguments runArguments: logger[metadataKey: "bundleName"] = "\(bundleName)" logger.info("Checking installed SDKs") - let installedSDKs = try await Shell.readStdout("swift experimental-sdk list").components(separatedBy: "\n") + let installedSDKs = try await Shell.readStdout("swift experimental-sdk list").components( + separatedBy: "\n") // Make sure this bundle hasn't been installed already. if installedSDKs.contains(bundleName) { @@ -116,7 +122,9 @@ final class RepeatedBuildTests: XCTestCase { func testRepeatedSDKBuilds() async throws { if ProcessInfo.processInfo.environment.keys.contains("JENKINS_URL") { - throw XCTSkip("EndToEnd tests cannot currently run in CI: https://github.com/swiftlang/swift-sdk-generator/issues/145") + throw XCTSkip( + "EndToEnd tests cannot currently run in CI: https://github.com/swiftlang/swift-sdk-generator/issues/145" + ) } var logger = logger @@ -127,15 +135,19 @@ final class RepeatedBuildTests: XCTestCase { var possibleArguments = ["--host-toolchain"] do { try await Shell.run("docker ps") - possibleArguments.append("--with-docker --linux-distribution-name rhel --linux-distribution-version ubi9") + possibleArguments.append( + "--with-docker --linux-distribution-name rhel --linux-distribution-version ubi9") } catch { - self.logger.warning("Docker CLI does not seem to be working, skipping tests that involve Docker.") + self.logger.warning( + "Docker CLI does not seem to be working, skipping tests that involve Docker.") } for runArguments in possibleArguments { if runArguments.contains("rhel") { // Temporarily skip the RHEL-based SDK. XCTSkip() is not suitable as it would skipping the entire test case - logger.warning("RHEL-based SDKs currently do not work with Swift 6.0: https://github.com/swiftlang/swift-sdk-generator/issues/138") + logger.warning( + "RHEL-based SDKs currently do not work with Swift 6.0: https://github.com/swiftlang/swift-sdk-generator/issues/138" + ) continue } @@ -155,7 +167,9 @@ struct SDKConfiguration { var architecture: String var withDocker: Bool - var bundleName: String { "\(linuxDistributionName)_\(linuxDistributionVersion)_\(architecture)_\(swiftVersion)-RELEASE\(withDocker ? "_with-docker" : "")" } + var bundleName: String { + "\(linuxDistributionName)_\(linuxDistributionVersion)_\(architecture)_\(swiftVersion)-RELEASE\(withDocker ? "_with-docker" : "")" + } func withDocker(_ enabled: Bool = true) -> SDKConfiguration { var res = self @@ -182,8 +196,8 @@ struct SDKConfiguration { "--swift-version \(swiftVersion)-RELEASE", testLinuxSwiftSDKs ? "--host \(hostArch!)-unknown-linux-gnu" : nil, "--target \(architecture)-unknown-linux-gnu", - "--linux-distribution-name \(linuxDistributionName)" - ].compactMap{ $0 }.joined(separator: " ") + "--linux-distribution-name \(linuxDistributionName)", + ].compactMap { $0 }.joined(separator: " ") } } @@ -199,10 +213,13 @@ var testLinuxSwiftSDKs: Bool { ProcessInfo.processInfo.environment.keys.contains("SWIFT_SDK_GENERATOR_TEST_LINUX_SWIFT_SDKS") } -func buildTestcase(_ logger: Logger, testcase: String, bundleName: String, tempDir: URL) async throws { +func buildTestcase(_ logger: Logger, testcase: String, bundleName: String, tempDir: URL) + async throws +{ let testPackageURL = tempDir.appendingPathComponent("swift-sdk-generator-test") let testPackageDir = FilePath(testPackageURL.path) - try FileManager.default.createDirectory(atPath: testPackageDir.string, withIntermediateDirectories: true) + try FileManager.default.createDirectory( + atPath: testPackageDir.string, withIntermediateDirectories: true) logger.info("Creating test project \(testPackageDir)") try await Shell.run("swift package --package-path \(testPackageDir) init --type executable") @@ -227,7 +244,9 @@ func buildTestcase(_ logger: Logger, testcase: String, bundleName: String, tempD // that contains each Swift-supported Linux distribution. This way we can validate that each // distribution is capable of building using the Linux Swift SDK. if testLinuxSwiftSDKs { - let swiftContainerVersions = ["focal", "jammy", "noble", "fedora39", "rhel-ubi9", "amazonlinux2", "bookworm"] + let swiftContainerVersions = [ + "focal", "jammy", "noble", "fedora39", "rhel-ubi9", "amazonlinux2", "bookworm", + ] for containerVersion in swiftContainerVersions { logger.info("Building test project in 6.0-\(containerVersion) container") buildOutput = try await Shell.readStdout( @@ -241,7 +260,8 @@ func buildTestcase(_ logger: Logger, testcase: String, bundleName: String, tempD XCTAssertTrue(buildOutput.contains("Build complete!")) logger.info("Test project built successfully") - logger.info("Building test project in 6.0-\(containerVersion) container with static-swift-stdlib") + logger.info( + "Building test project in 6.0-\(containerVersion) container with static-swift-stdlib") buildOutput = try await Shell.readStdout( """ docker run --rm -v \(testPackageDir):/src \ @@ -277,7 +297,9 @@ func buildTestcases(config: SDKConfiguration) async throws { logger[metadataKey: "testcase"] = "testPackageInitExecutable" if ProcessInfo.processInfo.environment.keys.contains("JENKINS_URL") { - throw XCTSkip("EndToEnd tests cannot currently run in CI: https://github.com/swiftlang/swift-sdk-generator/issues/145") + throw XCTSkip( + "EndToEnd tests cannot currently run in CI: https://github.com/swiftlang/swift-sdk-generator/issues/145" + ) } if config.withDocker { @@ -289,7 +311,8 @@ func buildTestcases(config: SDKConfiguration) async throws { } let bundleName = try await FileManager.default.withTemporaryDirectory(logger: logger) { tempDir in - try await buildSDK(logger, scratchPath: tempDir.path, withArguments: config.sdkGeneratorArguments) + try await buildSDK( + logger, scratchPath: tempDir.path, withArguments: config.sdkGeneratorArguments) } logger.info("Built SDK") diff --git a/Tests/SwiftSDKGeneratorTests/Generator/SwiftSDKGenerator+MetadataTests.swift b/Tests/SwiftSDKGeneratorTests/Generator/SwiftSDKGenerator+MetadataTests.swift index 9baf7ded..b599075b 100644 --- a/Tests/SwiftSDKGeneratorTests/Generator/SwiftSDKGenerator+MetadataTests.swift +++ b/Tests/SwiftSDKGeneratorTests/Generator/SwiftSDKGenerator+MetadataTests.swift @@ -35,7 +35,7 @@ final class SwiftSDKGeneratorMetadataTests: XCTestCase { bundleVersion: "0.0.3", targetTriple: Triple("armv7-unknown-linux-gnueabihf"), expectedCanonicalName: "armv7-swift-linux-gnueabihf" - ) + ), ] for testCase in testCases { diff --git a/Tests/SwiftSDKGeneratorTests/SwiftSDKRecipes/LinuxRecipeTests.swift b/Tests/SwiftSDKGeneratorTests/SwiftSDKRecipes/LinuxRecipeTests.swift index 3c21f0d9..656600e2 100644 --- a/Tests/SwiftSDKGeneratorTests/SwiftSDKRecipes/LinuxRecipeTests.swift +++ b/Tests/SwiftSDKGeneratorTests/SwiftSDKRecipes/LinuxRecipeTests.swift @@ -27,7 +27,7 @@ final class LinuxRecipeTests: XCTestCase { targetSwiftPackagePath: String? = nil, includeHostToolchain: Bool = true ) throws -> LinuxRecipe { - try LinuxRecipe( + try LinuxRecipe( targetTriple: Triple("aarch64-unknown-linux-gnu"), hostTriple: hostTriple, linuxDistribution: .init(name: .ubuntu, version: "22.04"), @@ -48,7 +48,7 @@ final class LinuxRecipeTests: XCTestCase { targetTriple: Triple("x86_64-unknown-linux-gnu"), expectedSwiftCompilerOptions: [ "-Xlinker", "-R/usr/lib/swift/linux/", - "-Xclang-linker", "--ld-path=ld.lld" + "-Xclang-linker", "--ld-path=ld.lld", ], expectedLinkerPath: nil ), @@ -57,7 +57,7 @@ final class LinuxRecipeTests: XCTestCase { targetTriple: Triple("aarch64-unknown-linux-gnu"), expectedSwiftCompilerOptions: [ "-Xlinker", "-R/usr/lib/swift/linux/", - "-use-ld=lld" + "-use-ld=lld", ], expectedLinkerPath: "ld.lld" ), @@ -67,10 +67,10 @@ final class LinuxRecipeTests: XCTestCase { expectedSwiftCompilerOptions: [ "-Xlinker", "-R/usr/lib/swift/linux/", "-use-ld=lld", - "-latomic" + "-latomic", ], expectedLinkerPath: "ld.lld" - ) + ), ] for testCase in testCases { @@ -93,10 +93,12 @@ final class LinuxRecipeTests: XCTestCase { toolset: &toolset, targetTriple: Triple("x86_64-unknown-linux-gnu") ) XCTAssertEqual(toolset.rootPath, nil) - XCTAssertEqual(toolset.swiftCompiler?.extraCLIOptions, [ - "-Xlinker", "-R/usr/lib/swift/linux/", - "-use-ld=lld" - ]) + XCTAssertEqual( + toolset.swiftCompiler?.extraCLIOptions, + [ + "-Xlinker", "-R/usr/lib/swift/linux/", + "-use-ld=lld", + ]) XCTAssertEqual(toolset.cxxCompiler?.extraCLIOptions, ["-lstdc++"]) XCTAssertEqual(toolset.librarian?.path, "llvm-ar") XCTAssert(toolset.linker == nil) @@ -117,9 +119,15 @@ final class LinuxRecipeTests: XCTestCase { pathsConfiguration ) let itemsToDownload = recipe.itemsToDownload(from: downloadableArtifacts) - let foundHostLLVM = itemsToDownload.contains(where: { $0.remoteURL == downloadableArtifacts.hostLLVM.remoteURL }) - let foundTargetSwift = itemsToDownload.contains(where: { $0.remoteURL == downloadableArtifacts.targetSwift.remoteURL }) - let foundHostSwift = itemsToDownload.contains(where: { $0.remoteURL == downloadableArtifacts.hostSwift.remoteURL }) + let foundHostLLVM = itemsToDownload.contains(where: { + $0.remoteURL == downloadableArtifacts.hostLLVM.remoteURL + }) + let foundTargetSwift = itemsToDownload.contains(where: { + $0.remoteURL == downloadableArtifacts.targetSwift.remoteURL + }) + let foundHostSwift = itemsToDownload.contains(where: { + $0.remoteURL == downloadableArtifacts.hostSwift.remoteURL + }) // If this is a Linux host, we do not download LLVM XCTAssertEqual(foundHostLLVM, includesHostLLVM) @@ -129,53 +137,58 @@ final class LinuxRecipeTests: XCTestCase { func testItemsToDownloadForMacOSHost() throws { let hostTriple = Triple("x86_64-apple-macos") - let testCases: [(recipe: LinuxRecipe, includesHostLLVM: Bool, includesTargetSwift: Bool, includesHostSwift: Bool)] = [ - ( - // Remote tarballs on Swift < 6.0 - recipe: try createRecipe(hostTriple: hostTriple, swiftVersion: "5.10"), - includesHostLLVM: true, - includesTargetSwift: true, - includesHostSwift: true - ), - ( - // Remote tarballs on Swift >= 6.0 - recipe: try createRecipe(hostTriple: hostTriple, swiftVersion: "6.0"), - includesHostLLVM: false, - includesTargetSwift: true, - includesHostSwift: true - ), - ( - // Remote target tarball with preinstalled toolchain - recipe: try createRecipe(hostTriple: hostTriple, swiftVersion: "5.9", includeHostToolchain: false), - includesHostLLVM: false, - includesTargetSwift: true, - includesHostSwift: false - ), - ( - // Local packages with Swift < 6.0 - recipe: try createRecipe( - hostTriple: hostTriple, - swiftVersion: "5.10", - hostSwiftPackagePath: "/path/to/host/swift", - targetSwiftPackagePath: "/path/to/target/swift" + let testCases: + [( + recipe: LinuxRecipe, includesHostLLVM: Bool, includesTargetSwift: Bool, + includesHostSwift: Bool + )] = [ + ( + // Remote tarballs on Swift < 6.0 + recipe: try createRecipe(hostTriple: hostTriple, swiftVersion: "5.10"), + includesHostLLVM: true, + includesTargetSwift: true, + includesHostSwift: true ), - includesHostLLVM: true, - includesTargetSwift: false, - includesHostSwift: false - ), - ( - // Local packages with Swift >= 6.0 - recipe: try createRecipe( - hostTriple: hostTriple, - swiftVersion: "6.0", - hostSwiftPackagePath: "/path/to/host/swift", - targetSwiftPackagePath: "/path/to/target/swift" + ( + // Remote tarballs on Swift >= 6.0 + recipe: try createRecipe(hostTriple: hostTriple, swiftVersion: "6.0"), + includesHostLLVM: false, + includesTargetSwift: true, + includesHostSwift: true ), - includesHostLLVM: false, - includesTargetSwift: false, - includesHostSwift: false - ) - ] + ( + // Remote target tarball with preinstalled toolchain + recipe: try createRecipe( + hostTriple: hostTriple, swiftVersion: "5.9", includeHostToolchain: false), + includesHostLLVM: false, + includesTargetSwift: true, + includesHostSwift: false + ), + ( + // Local packages with Swift < 6.0 + recipe: try createRecipe( + hostTriple: hostTriple, + swiftVersion: "5.10", + hostSwiftPackagePath: "/path/to/host/swift", + targetSwiftPackagePath: "/path/to/target/swift" + ), + includesHostLLVM: true, + includesTargetSwift: false, + includesHostSwift: false + ), + ( + // Local packages with Swift >= 6.0 + recipe: try createRecipe( + hostTriple: hostTriple, + swiftVersion: "6.0", + hostSwiftPackagePath: "/path/to/host/swift", + targetSwiftPackagePath: "/path/to/target/swift" + ), + includesHostLLVM: false, + includesTargetSwift: false, + includesHostSwift: false + ), + ] for testCase in testCases { try runItemsToDownloadTestCase( @@ -204,7 +217,8 @@ final class LinuxRecipeTests: XCTestCase { ), ( // Remote target tarball with preinstalled toolchain - recipe: try createRecipe(hostTriple: hostTriple, swiftVersion: "5.9", includeHostToolchain: false), + recipe: try createRecipe( + hostTriple: hostTriple, swiftVersion: "5.9", includeHostToolchain: false), includesTargetSwift: true, includesHostSwift: false ), @@ -229,7 +243,7 @@ final class LinuxRecipeTests: XCTestCase { ), includesTargetSwift: false, includesHostSwift: false - ) + ), ] for testCase in testCases { @@ -253,7 +267,10 @@ final class LinuxRecipeTests: XCTestCase { (swiftVersion: "5.9", includeHostToolchain: false, expectedHostTriples: allHostTriples), (swiftVersion: "5.10", includeHostToolchain: false, expectedHostTriples: allHostTriples), (swiftVersion: "6.0", includeHostToolchain: false, expectedHostTriples: nil), - (swiftVersion: "6.0", includeHostToolchain: true, expectedHostTriples: [Triple("x86_64-unknown-linux-gnu")]) + ( + swiftVersion: "6.0", includeHostToolchain: true, + expectedHostTriples: [Triple("x86_64-unknown-linux-gnu")] + ), ] for testCase in testCases { diff --git a/Tests/SwiftSDKGeneratorTests/SwiftSDKRecipes/WebAssemblyRecipe.swift b/Tests/SwiftSDKGeneratorTests/SwiftSDKRecipes/WebAssemblyRecipe.swift index 71eff42c..080a13ba 100644 --- a/Tests/SwiftSDKGeneratorTests/SwiftSDKRecipes/WebAssemblyRecipe.swift +++ b/Tests/SwiftSDKGeneratorTests/SwiftSDKRecipes/WebAssemblyRecipe.swift @@ -17,7 +17,7 @@ import XCTest final class WebAssemblyRecipeTests: XCTestCase { let logger = Logger(label: "WebAssemblyRecipeTests") - + func createRecipe() -> WebAssemblyRecipe { WebAssemblyRecipe( hostSwiftPackage: nil, @@ -65,8 +65,10 @@ final class WebAssemblyRecipeTests: XCTestCase { ] XCTAssertEqual(toolset.cCompiler?.extraCLIOptions, ccOptions) XCTAssertEqual(toolset.cxxCompiler?.extraCLIOptions, ccOptions) - XCTAssertEqual(toolset.linker?.extraCLIOptions, [ - "--import-memory", "--export-memory", "--shared-memory", "--max-memory=1073741824", - ]) + XCTAssertEqual( + toolset.linker?.extraCLIOptions, + [ + "--import-memory", "--export-memory", "--shared-memory", "--max-memory=1073741824", + ]) } } diff --git a/Utilities/soundness.sh b/Utilities/soundness.sh deleted file mode 100755 index 3ff517dc..00000000 --- a/Utilities/soundness.sh +++ /dev/null @@ -1,150 +0,0 @@ -#!/bin/bash -##===----------------------------------------------------------------------===## -## -## This source file is part of the Swift open source project -## -## Copyright (c) 2022-2023 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 -## -##===----------------------------------------------------------------------===## - -set -eu -here="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" - -function replace_acceptable_years() { - # this needs to replace all acceptable forms with 'YEARS' - sed -e 's/20[12][0123456789]-20[12][0123456789]/YEARS/' -e 's/20[12][0123456789]/YEARS/' -} - -printf "=> Checking for unacceptable language... " -# This greps for unacceptable terminology. The square bracket[s] are so that -# "git grep" doesn't find the lines that greps :). -unacceptable_terms=( - -e blacklis[t] - -e whitelis[t] - -e slav[e] - -e sanit[y] - -e maste[r] -) - -# We have to exclude the code of conduct as it gives examples of unacceptable language. -if git grep --color=never -i "${unacceptable_terms[@]}" -- . > /dev/null; then - printf "\033[0;31mUnacceptable language found.\033[0m\n" - git grep -i "${unacceptable_terms[@]}" -- . - exit 1 -fi -printf "\033[0;32mokay.\033[0m\n" - -printf "=> Checking format... \n" -git diff --name-only | grep ".swift" | while read changed_file; do - printf " * checking ${changed_file}... " - before=$(cat "${changed_file}") - swiftformat "$changed_file" > /dev/null 2>&1 - after=$(cat "${changed_file}") - - if [[ "$before" != "$after" ]]; then - printf "\033[0;31mformatting issues!\033[0m\n" - git --no-pager diff "${changed_file}" - exit 1 - else - printf "\033[0;32mokay.\033[0m\n" - fi -done - -printf "=> Checking license headers... \n" -tmp=$(mktemp /tmp/.swift-package-manager-soundness_XXXXXX) - -for language in swift-or-c bash python; do - printf " * $language... " - declare -a matching_files - declare -a exceptions - expections=( ) - matching_files=( -name '*' ) - case "$language" in - swift-or-c) - exceptions=( -name "Package.swift" -o -path "./Examples/*" -o -path "./Fixtures/*" \ - -o -path "./IntegrationTests/*" -o -path "./Tests/*" -o -path "./Bundles/*" ) - matching_files=( -name '*.swift' -o -name '*.c' -o -name '*.h' ) - cat > "$tmp" <<"EOF" -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift open source project -// -// Copyright (c) YEARS 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 -// -//===----------------------------------------------------------------------===// -EOF - ;; - bash) - exceptions=( -path "./Examples/*" -o -path "./Fixtures/*" -o -path "./IntegrationTests/*" \ - -o -path "./Bundles/*" ) - matching_files=( -name '*.sh' ) - cat > "$tmp" <<"EOF" -#!/bin/bash -##===----------------------------------------------------------------------===## -## -## This source file is part of the Swift open source project -## -## Copyright (c) YEARS 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 -## -##===----------------------------------------------------------------------===## -EOF - ;; - python) - exceptions=( -path "./Examples/*" -o -path "./Fixtures/*" -o -path "./IntegrationTests/*" \ - -o -path "./Bundles/*" ) - matching_files=( -name '*.py' ) - cat > "$tmp" <<"EOF" -#!/usr/bin/env python3 -##===----------------------------------------------------------------------===## -## -## This source file is part of the Swift open source project -## -## Copyright (c) YEARS 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 -## -##===----------------------------------------------------------------------===## -EOF - ;; - *) - echo >&2 "ERROR: unknown language '$language'" - ;; - esac - - expected_lines=$(cat "$tmp" | wc -l) - expected_sha=$(cat "$tmp" | shasum) - - ( - cd "$here/.." - { - find . \ - \( \! -path './.build/*' -a \ - \( "${matching_files[@]}" \) -a \ - \( \! \( "${exceptions[@]}" \) \) \) - - } | while read line; do - if [[ "$(cat "$line" | replace_acceptable_years | head -n $expected_lines | shasum)" != "$expected_sha" ]]; then - printf "\033[0;31mmissing headers in file '$line'!\033[0m\n" - diff -u <(cat "$line" | replace_acceptable_years | head -n $expected_lines) "$tmp" - exit 1 - fi - done - printf "\033[0;32mokay.\033[0m\n" - ) -done - -rm "$tmp"