diff --git a/Package.resolved b/Package.resolved index 9606eb451..5a9b6fd9a 100644 --- a/Package.resolved +++ b/Package.resolved @@ -15,7 +15,7 @@ "location" : "https://github.com/swiftlang/swift-llbuild.git", "state" : { "branch" : "main", - "revision" : "02db743e7fd4ba241b78207309ddb3c9c2ec5f3f" + "revision" : "9087bdf8c7d4c95402d6d811745ca5ff3e149741" } }, { @@ -23,8 +23,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/swiftlang/swift-toolchain-sqlite", "state" : { - "revision" : "bb8321a7eea3830af401a1501c7c8cc27ded6793", - "version" : "1.0.2" + "revision" : "4ee66b3ab1c40d20176045e61d8276242e73b01d", + "version" : "1.0.3" } }, { @@ -33,7 +33,7 @@ "location" : "https://github.com/swiftlang/swift-tools-support-core.git", "state" : { "branch" : "main", - "revision" : "4074f4db0971328c441fc1621c673937b9ca3b08" + "revision" : "e8fbc8b05a155f311b862178d92d043afb216fe3" } } ], diff --git a/README.md b/README.md index bdf36e052..d29ae2ab6 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # Swift Compiler Driver -Swift's compiler driver is a program that coordinates the compilation of Swift source code into various compiled results: executables, libraries, object files, Swift modules and interfaces, etc. It is the program one invokes from the command line to build Swift code (i.e., `swift` or `swiftc`) and is often invoked on the developer's behalf by a build system such as the [Swift Package Manager (SwiftPM)](https://github.com/swiftlang/swift-package-manager) or Xcode's build system. +Swift's compiler driver is a program that coordinates the compilation of Swift source code into various compiled results: executables, libraries, object files, Swift modules and interfaces, etc. It is the program one invokes from the command line to build Swift code (i.e., `swift` or `swiftc`) and is often invoked on the developer's behalf by a build system such as the [Swift Package Manager](https://github.com/swiftlang/swift-package-manager) or [Swift Build](https://github.com/swiftlang/swift-build). -The `swift-driver` project is a new implementation of the Swift compiler driver that is intended to replace the [existing driver](https://github.com/apple/swift/tree/main/lib/Driver) with a more extensible, maintainable, and robust code base. The specific goals of this project include: +The `swift-driver` project is the primary implementation of the Swift compiler driver that has replaced the [legacy driver](https://github.com/apple/swift/tree/main/lib/Driver) with a more extensible, maintainable, and robust code base. Core pillars of the project's architecture include: * A maintainable, robust, and flexible Swift code base * Library-based architecture that allows better integration with build tools @@ -11,8 +11,6 @@ The `swift-driver` project is a new implementation of the Swift compiler driver ## Getting Started -**Note:** Currently, swift-driver is only compatible with trunk development snapshots from [swift.org](https://swift.org/download/#snapshots). - The preferred way to build `swift-driver` is to use the Swift package manager. On most platforms you can build using: @@ -27,27 +25,24 @@ However, on Windows, because Swift Package Manager does not differentiate betwee swift build -Xlinker "%SDKROOT%\usr\lib\swift\windows\x86_64\swiftCore.lib" ``` -To use `swift-driver` in place of the existing Swift driver, create a symbolic link from `swift` and `swiftc` to `swift-driver`: +To use a locally-built `swift-driver` in place of the existing Swift driver, create a symbolic link from `swift` and `swiftc` to `swift-driver`: ``` ln -s /path/to/built/swift-driver $SOME_PATH/swift ln -s /path/to/built/swift-driver $SOME_PATH/swiftc ``` -Swift packages can be built with the new Swift driver by overriding `SWIFT_EXEC` to refer to the `swiftc` symbolic link created above and `SWIFT_DRIVER_SWIFT_FRONTEND_EXEC` to refer to the original `swift-frontend`, e.g., +Swift packages can be built with the new Swift driver by overriding `SWIFT_EXEC` to refer to the `swiftc` symbolic link created above and `SWIFT_DRIVER_SWIFT_FRONTEND_EXEC` to refer to the original `swift-frontend`, e.g. ``` SWIFT_EXEC=$SOME_PATH/swiftc SWIFT_DRIVER_SWIFT_FRONTEND_EXEC=$TOOLCHAIN_PATH/bin/swift-frontend swift build ``` -Similarly, one can use the new Swift driver within Xcode by adding a custom build setting (usually at the project level) named `SWIFT_EXEC` that refers to `$SOME_PATH/swiftc` and adding `-driver-use-frontend-path $TOOLCHAIN_DIR/usr/bin/swiftc` to `Other Swift Flags`. +Similarly, one can use the Swift driver within Xcode by adding a custom build setting (usually at the project level) named `SWIFT_EXEC` that refers to `$SOME_PATH/swiftc`. ## Building with CMake -`swift-driver` can also be built with CMake, which is suggested for -environments where the Swift Package Manager is not yet -available. Doing so requires several dependencies to be built first, -all with CMake: +`swift-driver` can also be built with CMake, which is suggested for environments where the Swift Package Manager is not yet available. Doing so requires several dependencies to be built first, all with CMake: * (Non-Apple platforms only) [swift-corelibs-foundation](https://github.com/apple/swift-corelibs-foundation) * [llbuild](https://github.com/apple/swift-llbuild) configure CMake with `-DLLBUILD_SUPPORT_BINDINGS="Swift"` and `-DCMAKE_OSX_ARCHITECTURES=x86_64` (If building on Intel) when building @@ -64,7 +59,7 @@ cmake --build ## Developing `swift-driver` -The new Swift driver is a work in progress, and there are numerous places for anyone with an interest to contribute! This section covers testing, miscellaneous development tips and tricks, and a rough development plan showing what work still needs to be done. +Swift compiler driver is constantly evolving and improving, and there are numerous places for anyone with an interest to contribute! This section covers testing, and miscellaneous development tips and tricks. ### Driver Documentation @@ -78,26 +73,6 @@ Test using command-line SwiftPM or Xcode. $ swift test --parallel ``` -Integration tests are costly to run and are disabled by default. Enable them -using `SWIFT_DRIVER_ENABLE_INTEGRATION_TESTS` environment variable. In Xcode, -you can set this variable in the scheme's test action. - -``` -$ SWIFT_DRIVER_ENABLE_INTEGRATION_TESTS=1 swift test --parallel -``` - -Some integration tests run the lit test suites in a Swift working copy. -To enable these, clone Swift and its dependencies and build them with -build-script, then set both `SWIFT_DRIVER_ENABLE_INTEGRATION_TESTS` -and `SWIFT_DRIVER_LIT_DIR`, either in your Xcode scheme or -on the command line: - -``` -$ SWIFT_DRIVER_ENABLE_INTEGRATION_TESTS=1 \ - SWIFT_DRIVER_LIT_DIR=/path/to/build/Ninja-ReleaseAssert/swift-.../test-... \ - swift test -c release --parallel -``` - #### Testing against `swift` compiler trunk `swift-driver` Continuous Integration runs against the most recent Trunk Development snapshot published at [swift.org/download](https://swift.org/download/). @@ -114,7 +89,7 @@ After the toolchain is installed, Xcode needs to be told to use it. This can mea Building with the toolchain is easy, set the toolchain in Xcode: Menu Bar > Xcode > Toolchains > select your toolchain -Running the driver requires setting the TOOLCHAINS environment variable. This tells xcrun which toolchain to use (on darwin xcrun is used to find tools). This variable is the name of the toolchain and not the path (ex: `Swift Development Snapshot`). Important note: xcrun lookup is lower priority than the SWIFT_EXEC_*_EXEC family of environment variables, the tools directory, and any tools in the same directory as the driver (This includes a driver installed in a toolchain). Even though TOOLCHAINS is not highest priority it's a convenient way to run the xctest suite using a custom toolchain. +Running the driver requires setting the TOOLCHAINS environment variable. This tells xcrun which toolchain to use (on darwin xcrun is used to find tools). This variable is the name of the toolchain and not the path (ex: `Swift Development Snapshot`). Important note: xcrun lookup is lower priority than the `SWIFT_EXEC_*_EXEC` family of environment variables, the tools directory, and any tools in the same directory as the driver (This includes a driver installed in a toolchain). Even though `TOOLCHAINS` is not highest priority it's a convenient way to run the xctest suite using a custom toolchain. #### Preparing a Linux docker for debug @@ -155,51 +130,25 @@ $ .build/path/to/makeOptions > Sources/SwiftOptions/Options.swift ### Development Plan -The goal of the new Swift driver is to provide a drop-in replacement for the existing driver, which means that there is a fixed initial feature set to implement before the existing Swift driver can be deprecated and removed. The development plan below covers that feature set, as well as describing a number of tasks that can improve the Swift driver---from code cleanups, to improving testing, implementing missing features, and integrating with existing systems. +The development plan below covers a number of tasks that can improve the Swift driver---from code cleanups, to improving testing, implementing missing features, and integrating with existing systems. * Code and documentation quality - * [ ] Search for `FIXME:` or `TODO:`: there are lots of little things to improve! - * [ ] Improve documentation of how to incorporate the driver into your own builds - * [ ] Add useful descriptions to any `Error` thrown within the library + * Search for `FIXME:` or `TODO:`: there are lots of little things to improve! + * Improve documentation of how to incorporate the driver into your own builds + * Add useful descriptions to any `Error` thrown within the library * Option parsing - * [ ] Look for complete "coverage" of the options in `Options.swift`. Is every option there checked somewhere in the driver? - * [ ] Find a better way to describe aliases for options. Can they be of some other type `OptionAlias` so we can't make the mistake of (e.g.) asking for an alias option when we're translating options? - * [ ] Diagnose unused options on the command line - * [ ] Typo correction for misspelled option names - * [ ] Find a better way than `makeOptions.cpp` to translate the command-line options from [Swift's repository](https://github.com/apple/swift/tree/main/include/swift/Option) into `Options.swift`. -* Platform support - * [x] Teach the `DarwinToolchain` to also handle iOS, tvOS, watchOS - * [x] Fill out the `GenericUnixToolchain` toolchain to get it working - * [x] Implement a `WindowsToolchain` - * [x] Implement proper tokenization for response files -* Compilation modes - * [x] Batch mode - * [x] Whole-module-optimization mode - * [x] REPL mode - * [x] Immediate mode -* Features - * [x] Precompiled bridging headers - * [x] Support embedding of bitcode - * [x] Incremental compilation - * [x] Parseable output, as used by SwiftPM - * [x] Response files - * [x] Input and primary input file lists - * [x] Complete `OutputFileMap` implementation to handle all file types uniformly -* Testing - * [ ] Build stuff with SwiftPM or Xcode or your favorite build system, using `swift-driver`. Were the results identical? What changed? - * [x] Shim in `swift-driver` so it can run the Swift repository's [driver test suite](https://github.com/apple/swift/tree/main/test/Driver). - * [ ] Investigate differences in the test results for the Swift repository's driver test suite (above) between the existing and new driver. - * [ ] Port interesting tests from the Swift repository's [driver test suite](https://github.com/apple/swift/tree/main/test/Driver) over to XCTest - * [ ] Fuzz the command-line options to try to crash the Swift driver itself -* Integration - * [x] Teach the Swift compiler's [`build-script`](https://github.com/apple/swift/blob/main/utils/build-script) to build `swift-driver`. - * [x] Building on the above, teach the Swift compiler's [`build-toolchain`](https://github.com/apple/swift/blob/main/utils/build-toolchain) to install `swift-driver` as the primary driver so we can test full toolchains with the new driver + * Look for complete "coverage" of the options in `Options.swift`. Is every option there checked somewhere in the driver? + * Find a better way to describe aliases for options. Can they be of some other type `OptionAlias` so we can't make the mistake of (e.g.) asking for an alias option when we're translating options? + * Diagnose unused options on the command line + * Typo correction for misspelled option names + * Find a better way than `makeOptions.cpp` to translate the command-line options from [Swift's repository](https://github.com/apple/swift/tree/main/include/swift/Option) into `Options.swift`. ### Build all Swift interfaces from an SDK Based on libSwiftDriver, `swift-build-sdk-interfaces` is a tool to batch build all Swift textual interfaces (`.swiftinterface`) from an SDK into binary modules (`.swiftmodule`). As an example, the following command finds all Swift textual interface from the MacOSX SDK, builds all of them into binary modules, and outputs module-specific error logs into the given directory. -` -$SDKROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk SWIFT_EXEC=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swiftc swift-build-sdk-interfaces -o /tmp/outputs -v -log-path /tmp/logs` +``` +$ $SDKROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk SWIFT_EXEC=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swiftc swift-build-sdk-interfaces -o /tmp/outputs -v -log-path /tmp/logs` +``` * *SDKROOT*: an env var to specify the SDK to work on * *SWIFT_EXEC*: teach `swift-build-sdk-interfaces` about where to find the Swift compiler to use @@ -209,22 +158,10 @@ $SDKROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/De ## Contributing to swift-driver -Contributions to swift-driver are welcomed and encouraged! Please see the -[Contributing to Swift guide](https://swift.org/contributing/). - -Before submitting the pull request, please make sure you have [tested your - changes](https://github.com/apple/swift/blob/main/docs/ContinuousIntegration.md) - and that they follow the Swift project [guidelines for contributing - code](https://swift.org/contributing/#contributing-code). +Contributions to swift-driver are welcomed and encouraged! Please see the [Contributing to Swift guide](https://swift.org/contributing/). -To be a truly great community, [Swift.org](https://swift.org/) needs to welcome -developers from all walks of life, with different backgrounds, and with a wide -range of experience. A diverse and friendly community will have more great -ideas, more unique perspectives, and produce more great code. We will work -diligently to make the Swift community welcoming to everyone. +Before submitting the pull request, please make sure you have [tested your changes](https://github.com/apple/swift/blob/main/docs/ContinuousIntegration.md) and that they follow the Swift project [guidelines for contributing code](https://swift.org/contributing/#contributing-code). -To give clarity of what is expected of our members, Swift has adopted the -code of conduct defined by the Contributor Covenant. This document is used -across many open source communities, and we think it articulates our values -well. For more, see the [Code of Conduct](https://swift.org/code-of-conduct/). +To be a truly great community, [Swift.org](https://swift.org/) needs to welcome developers from all walks of life, with different backgrounds, and with a wide range of experience. A diverse and friendly community will have more great ideas, more unique perspectives, and produce more great code. We will work diligently to make the Swift community welcoming to everyone. +To give clarity of what is expected of our members, Swift has adopted the code of conduct defined by the Contributor Covenant. This document is used across many open source communities, and we think it articulates our values well. For more, see the [Code of Conduct](https://swift.org/code-of-conduct/). diff --git a/Sources/CSwiftScan/include/swiftscan_header.h b/Sources/CSwiftScan/include/swiftscan_header.h index f6e8e00cd..c59ae3197 100644 --- a/Sources/CSwiftScan/include/swiftscan_header.h +++ b/Sources/CSwiftScan/include/swiftscan_header.h @@ -240,10 +240,6 @@ typedef struct { (*swiftscan_import_set_create)(swiftscan_scanner_t, swiftscan_scan_invocation_t); //=== Scanner Diagnostics -------------------------------------------------===// - swiftscan_diagnostic_set_t* - (*swiftscan_scanner_diagnostics_query)(swiftscan_scanner_t); - void - (*swiftscan_scanner_diagnostics_reset)(swiftscan_scanner_t); swiftscan_string_ref_t (*swiftscan_diagnostic_get_message)(swiftscan_diagnostic_info_t); swiftscan_diagnostic_severity_t diff --git a/Sources/SwiftDriver/CMakeLists.txt b/Sources/SwiftDriver/CMakeLists.txt index d6594845b..890fad822 100644 --- a/Sources/SwiftDriver/CMakeLists.txt +++ b/Sources/SwiftDriver/CMakeLists.txt @@ -33,6 +33,7 @@ add_library(SwiftDriver Execution/DriverExecutor.swift Execution/ParsableOutput.swift Execution/ProcessProtocol.swift + Execution/ProcessSet.swift "IncrementalCompilation/Bitcode/Bitcode.swift" "IncrementalCompilation/Bitcode/BitcodeElement.swift" diff --git a/Sources/SwiftDriver/Driver/Driver.swift b/Sources/SwiftDriver/Driver/Driver.swift index db11ab3cc..602f225a9 100644 --- a/Sources/SwiftDriver/Driver/Driver.swift +++ b/Sources/SwiftDriver/Driver/Driver.swift @@ -27,6 +27,8 @@ import struct TSCBasic.RelativePath import var TSCBasic.localFileSystem import var TSCBasic.stderrStream import var TSCBasic.stdoutStream +import typealias TSCBasic.ProcessEnvironmentBlock +import struct TSCBasic.ProcessEnvironmentKey extension Driver { /// Stub Error for terminating the process. @@ -44,7 +46,6 @@ extension Driver.ErrorDiagnostics: CustomStringConvertible { } } - /// The Swift driver. public struct Driver { public enum Error: Swift.Error, Equatable, DiagnosticData { @@ -150,7 +151,7 @@ public struct Driver { /// The set of environment variables that are visible to the driver and /// processes it launches. This is a hook for testing; in actual use /// it should be identical to the real environment. - public let env: [String: String] + public let env: ProcessEnvironmentBlock /// Whether we are using the driver as the integrated driver via libSwiftDriver public let integratedDriver: Bool @@ -819,11 +820,44 @@ public struct Driver { ) } + @available(*, deprecated, renamed: "init(args:envBlock:diagnosticsOutput:fileSystem:executor:integratedDriver:compilerIntegratedTooling:compilerExecutableDir:externalTargetModuleDetailsMap:interModuleDependencyOracle:)") + @_disfavoredOverload + public init( + args: [String], + env: [String: String] = ProcessEnv.vars, + diagnosticsOutput: DiagnosticsOutput = .engine(DiagnosticsEngine(handlers: [Driver.stderrDiagnosticsHandler])), + fileSystem: FileSystem = localFileSystem, + executor: DriverExecutor, + integratedDriver: Bool = true, + compilerIntegratedTooling: Bool = false, + compilerExecutableDir: AbsolutePath? = nil, + externalTargetModuleDetailsMap: ExternalTargetModuleDetailsMap? = nil, + interModuleDependencyOracle: InterModuleDependencyOracle? = nil + ) throws { + let envBlock = env.reduce([:]) { (partialResult: ProcessEnvironmentBlock, tuple: (key: String, value: String)) in + var result = partialResult + result[ProcessEnvironmentKey(tuple.key)] = tuple.value + return result + } + try self.init( + args: args, + envBlock: envBlock, + diagnosticsOutput: diagnosticsOutput, + fileSystem: fileSystem, + executor: executor, + integratedDriver: integratedDriver, + compilerIntegratedTooling: false, + compilerExecutableDir: compilerExecutableDir, + externalTargetModuleDetailsMap: externalTargetModuleDetailsMap, + interModuleDependencyOracle: interModuleDependencyOracle + ) + } + /// Create the driver with the given arguments. /// /// - Parameter args: The command-line arguments, including the "swift" or "swiftc" /// at the beginning. - /// - Parameter env: The environment variables to use. This is a hook for testing; + /// - Parameter envBlock: The environment variables to use. This is a hook for testing; /// in production, you should use the default argument, which copies the current environment. /// - Parameter diagnosticsOutput: The diagnostics output implementation used by the driver to emit errors /// and warnings. @@ -844,7 +878,7 @@ public struct Driver { /// shared across different module builds by a build system. public init( args: [String], - env: [String: String] = ProcessEnv.vars, + envBlock: ProcessEnvironmentBlock = ProcessEnv.block, diagnosticsOutput: DiagnosticsOutput = .engine(DiagnosticsEngine(handlers: [Driver.stderrDiagnosticsHandler])), fileSystem: FileSystem = localFileSystem, executor: DriverExecutor, @@ -854,7 +888,7 @@ public struct Driver { externalTargetModuleDetailsMap: ExternalTargetModuleDetailsMap? = nil, interModuleDependencyOracle: InterModuleDependencyOracle? = nil ) throws { - self.env = env + self.env = envBlock self.fileSystem = fileSystem self.integratedDriver = integratedDriver self.compilerIntegratedTooling = compilerIntegratedTooling @@ -2550,7 +2584,7 @@ extension Driver { static func determineNumParallelJobs( _ parsedOptions: inout ParsedOptions, diagnosticsEngine: DiagnosticsEngine, - env: [String: String] + env: ProcessEnvironmentBlock ) -> Int? { guard let numJobs = parseIntOption(&parsedOptions, option: .j, diagnosticsEngine: diagnosticsEngine) else { return nil @@ -3043,7 +3077,7 @@ extension Driver { targetTriple: Triple?, fileSystem: FileSystem, diagnosticsEngine: DiagnosticsEngine, - env: [String: String] + env: ProcessEnvironmentBlock ) -> VirtualPath? { var sdkPath: String? @@ -3574,7 +3608,7 @@ extension Driver { _ parsedOptions: inout ParsedOptions, diagnosticsEngine: DiagnosticsEngine, compilerMode: CompilerMode, - env: [String: String], + env: ProcessEnvironmentBlock, executor: DriverExecutor, fileSystem: FileSystem, useStaticResourceDir: Bool, @@ -3605,7 +3639,7 @@ extension Driver { static func computeTargetInfo(_ parsedOptions: inout ParsedOptions, diagnosticsEngine: DiagnosticsEngine, compilerMode: CompilerMode, - env: [String: String], + env: ProcessEnvironmentBlock, executor: DriverExecutor, libSwiftScan: SwiftScan?, toolchain: Toolchain, diff --git a/Sources/SwiftDriver/Execution/DriverExecutor.swift b/Sources/SwiftDriver/Execution/DriverExecutor.swift index 804119003..ad66ecd20 100644 --- a/Sources/SwiftDriver/Execution/DriverExecutor.swift +++ b/Sources/SwiftDriver/Execution/DriverExecutor.swift @@ -15,6 +15,8 @@ import struct TSCBasic.ProcessResult import struct Foundation.Data import class Foundation.JSONDecoder import var Foundation.EXIT_SUCCESS +import typealias TSCBasic.ProcessEnvironmentBlock +import struct TSCBasic.ProcessEnvironmentKey /// A type that is capable of executing compilation jobs on some underlying /// build service. @@ -158,3 +160,14 @@ public protocol JobExecutionDelegate { /// Called when a job is skipped. func jobSkipped(job: Job) } + +@_spi(Testing) public extension ProcessEnvironmentBlock { + var legacyVars: [String: String] { + return self.reduce([:]) { + (partialResult: [String: String], tuple: (key: ProcessEnvironmentKey, value: String)) in + var result = partialResult + result[tuple.key.value] = tuple.value + return result + } + } +} diff --git a/Sources/SwiftDriver/Execution/ProcessProtocol.swift b/Sources/SwiftDriver/Execution/ProcessProtocol.swift index 3eead65ca..86c61f8c0 100644 --- a/Sources/SwiftDriver/Execution/ProcessProtocol.swift +++ b/Sources/SwiftDriver/Execution/ProcessProtocol.swift @@ -12,6 +12,7 @@ import class TSCBasic.Process import struct TSCBasic.ProcessResult +import typealias TSCBasic.ProcessEnvironmentBlock import class Foundation.FileHandle import struct Foundation.Data @@ -32,17 +33,18 @@ public protocol ProcessProtocol { static func launchProcess( arguments: [String], - env: [String: String] + env: ProcessEnvironmentBlock ) throws -> Self static func launchProcessAndWriteInput( arguments: [String], - env: [String: String], + env: ProcessEnvironmentBlock, inputFileHandle: FileHandle ) throws -> Self } extension TSCBasic.Process: ProcessProtocol { + @available(*, deprecated, message: "use launchProcess(arguments:envBlock:) instead") public static func launchProcess( arguments: [String], env: [String: String] @@ -52,12 +54,21 @@ extension TSCBasic.Process: ProcessProtocol { return process } + public static func launchProcess( + arguments: [String], + env: ProcessEnvironmentBlock + ) throws -> TSCBasic.Process { + let process = Process(arguments: arguments, environmentBlock: env) + try process.launch() + return process + } + public static func launchProcessAndWriteInput( arguments: [String], - env: [String: String], + env: ProcessEnvironmentBlock, inputFileHandle: FileHandle ) throws -> TSCBasic.Process { - let process = Process(arguments: arguments, environment: env) + let process = Process(arguments: arguments, environmentBlock: env) let processInputStream = try process.launch() var input: Data // Write out the contents of the input handle and close the input stream diff --git a/Sources/SwiftDriver/Execution/ProcessSet.swift b/Sources/SwiftDriver/Execution/ProcessSet.swift new file mode 100644 index 000000000..d243022f5 --- /dev/null +++ b/Sources/SwiftDriver/Execution/ProcessSet.swift @@ -0,0 +1,131 @@ +//===--------------- ProcessSet.swift - Swift Subprocesses ---------------===// +// +// This source file is part of the Swift.org 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 struct TSCBasic.Condition +import class TSCBasic.Process +import struct TSCBasic.ProcessResult +import class TSCBasic.Thread +import Dispatch +import Foundation + +public enum ProcessSetError: Swift.Error { + /// The process group was cancelled and doesn't allow adding more processes. + case cancelled +} + +/// A process set is a small wrapper for collection of processes. +/// +/// This class is thread safe. +// TODO: Use `TaskGroup` with async `Process` APIs instead +public final class ProcessSet { + + /// Array to hold the processes. + private var processes: Set = [] + + /// Queue to mutate internal states of the process group. + private let serialQueue = DispatchQueue(label: "org.swift.swiftpm.process-set") + + /// If the process group was asked to cancel all active processes. + private var cancelled = false + + /// The timeout (in seconds) after which the processes should be killed if they don't respond to SIGINT. + public let killTimeout: Double + + /// Condition to block kill thread until timeout. + private var killingCondition = Condition() + + /// Boolean predicate for killing condition. + private var shouldKill = false + + /// Create a process set. + public init(killTimeout: Double = 5) { + self.killTimeout = killTimeout + } + + /// Add a process to the process set. This method will throw if the process set is terminated using the terminate() + /// method. + /// + /// Call remove() method to remove the process from set once it has terminated. + /// + /// - Parameters: + /// - process: The process to add. + /// - Throws: ProcessGroupError + public func add(_ process: TSCBasic.Process) throws { + return try serialQueue.sync { + guard !cancelled else { + throw ProcessSetError.cancelled + } + self.processes.insert(process) + } + } + + /// Terminate all the processes. This method blocks until all processes in the set are terminated. + /// + /// A process set cannot be used once it has been asked to terminate. + public func terminate() { + // Mark a process set as cancelled. + serialQueue.sync { + cancelled = true + } + + // Interrupt all processes. + signalAll(SIGINT) + + // Create a thread that will kill all processes after a timeout. + let thread = TSCBasic.Thread { + // Compute the timeout date. + let timeout = Date() + self.killTimeout + // Block until we timeout or notification. + self.killingCondition.whileLocked { + while !self.shouldKill { + // Block until timeout expires. + let timeLimitReached = !self.killingCondition.wait(until: timeout) + // Set should kill to true if time limit was reached. + if timeLimitReached { + self.shouldKill = true + } + } + } + // Send kill signal to all processes. + #if os(Windows) + self.signalAll(SIGTERM) + #else + self.signalAll(SIGKILL) + #endif + } + + thread.start() + + // Wait until all processes terminate and notify the kill thread + // if everyone exited to avoid waiting till timeout. + for process in self.processes { + _ = try? process.waitUntilExit() + } + killingCondition.whileLocked { + shouldKill = true + killingCondition.signal() + } + + // Join the kill thread so we don't exit before everything terminates. + thread.join() + } + + /// Sends signal to all processes in the set. + private func signalAll(_ signal: Int32) { + serialQueue.sync { + // Signal all active processes. + for process in self.processes { + process.signal(signal) + } + } + } +} diff --git a/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyOracle.swift b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyOracle.swift index 06e42cabf..b10e0b057 100644 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyOracle.swift +++ b/Sources/SwiftDriver/ExplicitModuleBuilds/InterModuleDependencies/InterModuleDependencyOracle.swift @@ -17,19 +17,19 @@ import var TSCBasic.localFileSystem import Dispatch -// An inter-module dependency oracle, responsible for responding to queries about -// dependencies of a given module, caching already-discovered dependencies along the way. +// An inter-module dependency oracle, responsible for responding to queries of +// dependencies of a given Swift source module. // -// The oracle is currently implemented as a simple store of ModuleInfo nodes. -// It is the responsibility of the Driver to populate and update -// the store. It does so by invoking individual -scan-dependencies jobs and -// accumulating resulting dependency graphs into the oracle's store. +// The oracle is implemented as a simple wrapper over a 'SwiftScan' instance. +// An oracle instance may be created by 'SwiftDriver' to be used for a given +// build's queries (such as on a standalone 'swiftc' invocation), or provided +// to the driver by a build system (e.g. swift-build) client when the oracle +// is shared across multiple driver invocations. // -// The design of the oracle's public API is meant to abstract that away, -// allowing us to replace the underlying implementation in the future, with -// a persistent-across-targets dependency scanning library. +// The design of the oracle's public API is meant to abstract away the +// underlying implementation of the interface with the libSwiftScan shared +// library, allowing us to replace the underlying implementation in the future. // -/// An abstraction of a cache and query-engine of inter-module dependencies public class InterModuleDependencyOracle { /// Allow external clients to instantiate the oracle /// - Parameter scannerRequiresPlaceholderModules: Configures this driver's/oracle's scanner invocations to @@ -68,12 +68,6 @@ public class InterModuleDependencyOracle { diagnostics: &diagnostics) } - @available(*, deprecated, message: "use verifyOrCreateScannerInstance(swiftScanLibPath:)") - public func verifyOrCreateScannerInstance(fileSystem: FileSystem, - swiftScanLibPath: AbsolutePath) throws { - return try verifyOrCreateScannerInstance(swiftScanLibPath: swiftScanLibPath) - } - /// Given a specified toolchain path, locate and instantiate an instance of the SwiftScan library public func verifyOrCreateScannerInstance(swiftScanLibPath: AbsolutePath?) throws { return try queue.sync { @@ -104,13 +98,6 @@ public class InterModuleDependencyOracle { return swiftScan.hasBinarySwiftModuleHeaderModuleDependencies } - @_spi(Testing) public func supportsScannerDiagnostics() throws -> Bool { - guard let swiftScan = swiftScanLibInstance else { - fatalError("Attempting to query supported scanner API with no scanner instance.") - } - return swiftScan.supportsScannerDiagnostics - } - @_spi(Testing) public func supportsBinaryModuleHeaderDependencies() throws -> Bool { guard let swiftScan = swiftScanLibInstance else { fatalError("Attempting to query supported scanner API with no scanner instance.") @@ -154,18 +141,6 @@ public class InterModuleDependencyOracle { return swiftScan.supportsSeparateImportOnlyDependencise } - @_spi(Testing) public func getScannerDiagnostics() throws -> [ScannerDiagnosticPayload]? { - guard let swiftScan = swiftScanLibInstance else { - fatalError("Attempting to reset scanner cache with no scanner instance.") - } - guard swiftScan.supportsScannerDiagnostics else { - return nil - } - let diags = try swiftScan.queryScannerDiagnostics() - try swiftScan.resetScannerDiagnostics() - return diags.isEmpty ? nil : diags - } - public func getOrCreateCAS(pluginPath: AbsolutePath?, onDiskPath: AbsolutePath?, pluginOptions: [(String, String)]) throws -> SwiftScanCAS { guard let swiftScan = swiftScanLibInstance else { fatalError("Attempting to reset scanner cache with no scanner instance.") diff --git a/Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift b/Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift index d6f488f62..38b276247 100644 --- a/Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift +++ b/Sources/SwiftDriver/ExplicitModuleBuilds/ModuleDependencyScanning.swift @@ -16,6 +16,7 @@ import struct TSCBasic.RelativePath import struct TSCBasic.Diagnostic import var TSCBasic.localFileSystem import var TSCBasic.stdoutStream +import typealias TSCBasic.ProcessEnvironmentBlock import SwiftOptions import struct Foundation.Data @@ -254,11 +255,7 @@ public extension Driver { moduleAliases: moduleOutputInfo.aliases, commandLine: command, diagnostics: &scanDiagnostics) - } catch let DependencyScanningError.dependencyScanFailed(reason) { - try emitGlobalScannerDiagnostics() - throw DependencyScanningError.dependencyScanFailed(reason) } - try emitGlobalScannerDiagnostics() } else { // Fallback to legacy invocation of the dependency scanner with // `swift-frontend -scan-dependencies -import-prescan` @@ -293,17 +290,6 @@ public extension Driver { } } - mutating internal func emitGlobalScannerDiagnostics() throws { - // We only emit global scanner-collected diagnostics as a legacy flow - // when the scanner does not support per-scan diagnostic output - guard try !interModuleDependencyOracle.supportsPerScanDiagnostics() else { - return - } - if let diags = try interModuleDependencyOracle.getScannerDiagnostics() { - try emitScannerDiagnostics(diags) - } - } - mutating func performDependencyScan() throws -> InterModuleDependencyGraph { let scannerJob = try dependencyScanningJob() let forceResponseFiles = parsedOptions.hasArgument(.driverForceResponseFiles) @@ -331,11 +317,7 @@ public extension Driver { commandLine: command, diagnostics: &scanDiagnostics) try emitScannerDiagnostics(scanDiagnostics) - } catch let DependencyScanningError.dependencyScanFailed(reason) { - try emitGlobalScannerDiagnostics() - throw DependencyScanningError.dependencyScanFailed(reason) } - try emitGlobalScannerDiagnostics() } else { // Fallback to legacy invocation of the dependency scanner with // `swift-frontend -scan-dependencies` @@ -384,7 +366,7 @@ public extension Driver { useResponseFiles: useResponseFiles).0.map { $0.spm_shellEscaped() } } - static func getRootPath(of toolchain: Toolchain, env: [String: String]) + static func getRootPath(of toolchain: Toolchain, env: ProcessEnvironmentBlock) throws -> AbsolutePath { return try toolchain.getToolPath(.swiftCompiler) .parentDirectory // bin diff --git a/Sources/SwiftDriver/Jobs/DarwinToolchain+LinkerSupport.swift b/Sources/SwiftDriver/Jobs/DarwinToolchain+LinkerSupport.swift index 27f4c6157..906c29313 100644 --- a/Sources/SwiftDriver/Jobs/DarwinToolchain+LinkerSupport.swift +++ b/Sources/SwiftDriver/Jobs/DarwinToolchain+LinkerSupport.swift @@ -19,7 +19,7 @@ extension DarwinToolchain { internal func findXcodeClangPath() throws -> AbsolutePath? { let result = try executor.checkNonZeroExit( args: "xcrun", "-toolchain", "default", "-f", "clang", - environment: env + environment: env.legacyVars ).trimmingCharacters(in: .whitespacesAndNewlines) return result.isEmpty ? nil : try AbsolutePath(validating: result) diff --git a/Sources/SwiftDriver/Jobs/EmitSupportedFeaturesJob.swift b/Sources/SwiftDriver/Jobs/EmitSupportedFeaturesJob.swift index 06fce203d..568fd725a 100644 --- a/Sources/SwiftDriver/Jobs/EmitSupportedFeaturesJob.swift +++ b/Sources/SwiftDriver/Jobs/EmitSupportedFeaturesJob.swift @@ -18,6 +18,7 @@ import class TSCBasic.DiagnosticsEngine import protocol TSCBasic.FileSystem import struct TSCBasic.RelativePath import var TSCBasic.localFileSystem +import typealias TSCBasic.ProcessEnvironmentBlock /// Describes information about the compiler's supported arguments and features @_spi(Testing) public struct SupportedCompilerFeatures: Codable { @@ -90,7 +91,7 @@ extension Driver { } static func computeSupportedCompilerFeatures(of toolchain: Toolchain, - env: [String: String]) throws -> Set { + env: ProcessEnvironmentBlock) throws -> Set { struct FeatureInfo: Codable { var name: String } diff --git a/Sources/SwiftDriver/Jobs/Job.swift b/Sources/SwiftDriver/Jobs/Job.swift index 2b2f5c370..0686b9de4 100644 --- a/Sources/SwiftDriver/Jobs/Job.swift +++ b/Sources/SwiftDriver/Jobs/Job.swift @@ -12,6 +12,8 @@ import protocol TSCBasic.DiagnosticData import protocol TSCBasic.FileSystem +import typealias TSCBasic.ProcessEnvironmentBlock +import struct TSCBasic.ProcessEnvironmentKey /// A job represents an individual subprocess that should be invoked during compilation. public struct Job: Codable, Equatable, Hashable { @@ -94,8 +96,12 @@ public struct Job: Codable, Equatable, Hashable { public var outputs: [TypedVirtualPath] /// Any extra environment variables which should be set while running the job. + @available(*, deprecated, message: "use extraEnvironmentBlock") public var extraEnvironment: [String: String] + /// Any extra environment variables which should be set while running the job. + public var extraEnvironmentBlock: ProcessEnvironmentBlock + /// Whether or not the job must be executed in place, replacing the current driver process. public var requiresInPlaceExecution: Bool @@ -119,7 +125,7 @@ public struct Job: Codable, Equatable, Hashable { outputs: [TypedVirtualPath], outputCacheKeys: [TypedVirtualPath: String] = [:], inputOutputMap: [TypedVirtualPath : [TypedVirtualPath]] = [:], - extraEnvironment: [String: String] = [:], + extraEnvironment: ProcessEnvironmentBlock = [:], requiresInPlaceExecution: Bool = false ) { self.moduleName = moduleName @@ -132,7 +138,8 @@ public struct Job: Codable, Equatable, Hashable { self.outputs = outputs self.outputCacheKeys = outputCacheKeys self.compileInputOutputMap = inputOutputMap - self.extraEnvironment = extraEnvironment + self.extraEnvironmentBlock = extraEnvironment + self.extraEnvironment = self.extraEnvironmentBlock.legacyVars self.requiresInPlaceExecution = requiresInPlaceExecution self.supportsResponseFiles = tool.supportsResponseFiles } diff --git a/Sources/SwiftDriver/Jobs/Toolchain+InterpreterSupport.swift b/Sources/SwiftDriver/Jobs/Toolchain+InterpreterSupport.swift index 705d28f47..91f6ed7f8 100644 --- a/Sources/SwiftDriver/Jobs/Toolchain+InterpreterSupport.swift +++ b/Sources/SwiftDriver/Jobs/Toolchain+InterpreterSupport.swift @@ -10,13 +10,15 @@ // //===----------------------------------------------------------------------===// +import typealias TSCBasic.ProcessEnvironmentBlock +import struct TSCBasic.ProcessEnvironmentKey import SwiftOptions extension Toolchain { func addPathEnvironmentVariableIfNeeded( - _ environmentVariable: String, - to env: inout [String : String], - currentEnv: [String: String], + _ environmentVariable: ProcessEnvironmentKey, + to env: inout ProcessEnvironmentBlock, + currentEnv: ProcessEnvironmentBlock, option: Option, parsedOptions: inout ParsedOptions, extraPaths: [String] = [] @@ -31,17 +33,17 @@ extension Toolchain { extension DarwinToolchain { public func platformSpecificInterpreterEnvironmentVariables( - env: [String : String], + env: ProcessEnvironmentBlock, parsedOptions: inout ParsedOptions, sdkPath: VirtualPath.Handle?, - targetInfo: FrontendTargetInfo) throws -> [String: String] { - var envVars: [String: String] = [:] + targetInfo: FrontendTargetInfo) throws -> ProcessEnvironmentBlock { + var envVars: ProcessEnvironmentBlock = [:] - addPathEnvironmentVariableIfNeeded("DYLD_LIBRARY_PATH", to: &envVars, + addPathEnvironmentVariableIfNeeded(ProcessEnvironmentKey("DYLD_LIBRARY_PATH"), to: &envVars, currentEnv: env, option: .L, parsedOptions: &parsedOptions) - addPathEnvironmentVariableIfNeeded("DYLD_FRAMEWORK_PATH", to: &envVars, + addPathEnvironmentVariableIfNeeded(ProcessEnvironmentKey("DYLD_FRAMEWORK_PATH"), to: &envVars, currentEnv: env, option: .F, parsedOptions: &parsedOptions, extraPaths: ["/System/Library/Frameworks"]) @@ -52,11 +54,11 @@ extension DarwinToolchain { extension GenericUnixToolchain { public func platformSpecificInterpreterEnvironmentVariables( - env: [String : String], + env: ProcessEnvironmentBlock, parsedOptions: inout ParsedOptions, sdkPath: VirtualPath.Handle?, - targetInfo: FrontendTargetInfo) throws -> [String: String] { - var envVars: [String: String] = [:] + targetInfo: FrontendTargetInfo) throws -> ProcessEnvironmentBlock { + var envVars: ProcessEnvironmentBlock = [:] let runtimePaths = try runtimeLibraryPaths( for: targetInfo, @@ -65,7 +67,7 @@ extension GenericUnixToolchain { isShared: true ).map { $0.name } - addPathEnvironmentVariableIfNeeded("LD_LIBRARY_PATH", to: &envVars, + addPathEnvironmentVariableIfNeeded(ProcessEnvironmentKey("LD_LIBRARY_PATH"), to: &envVars, currentEnv: env, option: .L, parsedOptions: &parsedOptions, extraPaths: runtimePaths) @@ -76,10 +78,10 @@ extension GenericUnixToolchain { extension WindowsToolchain { public func platformSpecificInterpreterEnvironmentVariables( - env: [String : String], + env: ProcessEnvironmentBlock, parsedOptions: inout ParsedOptions, sdkPath: VirtualPath.Handle?, - targetInfo: FrontendTargetInfo) throws -> [String: String] { + targetInfo: FrontendTargetInfo) throws -> ProcessEnvironmentBlock { // TODO(compnerd): setting up `Path` is meaningless currently as the lldb // support required for the interpreter mode fails to load the dependencies. diff --git a/Sources/SwiftDriver/SwiftScan/Loader.swift b/Sources/SwiftDriver/SwiftScan/Loader.swift index decc1d5de..849dfb72c 100644 --- a/Sources/SwiftDriver/SwiftScan/Loader.swift +++ b/Sources/SwiftDriver/SwiftScan/Loader.swift @@ -1,8 +1,8 @@ -//===------------------------ SwiftScan.swift -----------------------------===// +//===------------------------ Loader.swift -------------------------------===// // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// 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 diff --git a/Sources/SwiftDriver/SwiftScan/SwiftScan.swift b/Sources/SwiftDriver/SwiftScan/SwiftScan.swift index 804a1e8d6..4a8b255f8 100644 --- a/Sources/SwiftDriver/SwiftScan/SwiftScan.swift +++ b/Sources/SwiftDriver/SwiftScan/SwiftScan.swift @@ -250,14 +250,6 @@ private extension String { return api.swiftscan_swift_textual_detail_get_swift_source_import_module_dependencies != nil } - @_spi(Testing) public var supportsScannerDiagnostics : Bool { - return api.swiftscan_scanner_diagnostics_query != nil && - api.swiftscan_scanner_diagnostics_reset != nil && - api.swiftscan_diagnostic_get_message != nil && - api.swiftscan_diagnostic_get_severity != nil && - api.swiftscan_diagnostics_set_dispose != nil - } - @_spi(Testing) public var supportsCaching : Bool { #if os(Windows) // Caching is currently not supported on Windows hosts. @@ -348,21 +340,6 @@ private extension String { return result } - @_spi(Testing) public func queryScannerDiagnostics() throws -> [ScannerDiagnosticPayload] { - let diagnosticSetRefOrNull = api.swiftscan_scanner_diagnostics_query(scanner) - guard let diagnosticSetRef = diagnosticSetRefOrNull else { - // Seems heavy-handed to fail here - // throw DependencyScanningError.dependencyScanFailed - return [] - } - defer { api.swiftscan_diagnostics_set_dispose(diagnosticSetRef) } - return try mapToDriverDiagnosticPayload(diagnosticSetRef) - } - - @_spi(Testing) public func resetScannerDiagnostics() throws { - api.swiftscan_scanner_diagnostics_reset(scanner) - } - @_spi(Testing) public func canQuerySupportedArguments() -> Bool { return api.swiftscan_compiler_supported_arguments_query != nil && api.swiftscan_string_set_dispose != nil @@ -483,10 +460,6 @@ private extension swiftscan_functions_t { loadOptional("swiftscan_compiler_target_info_query_v2") // Scanner diagnostic emission query - self.swiftscan_scanner_diagnostics_query = - loadOptional("swiftscan_scanner_diagnostics_query") - self.swiftscan_scanner_diagnostics_reset = - loadOptional("swiftscan_scanner_diagnostics_reset") self.swiftscan_diagnostic_get_message = loadOptional("swiftscan_diagnostic_get_message") self.swiftscan_diagnostic_get_severity = diff --git a/Sources/SwiftDriver/Toolchains/DarwinToolchain.swift b/Sources/SwiftDriver/Toolchains/DarwinToolchain.swift index cfb4c9f05..48773298e 100644 --- a/Sources/SwiftDriver/Toolchains/DarwinToolchain.swift +++ b/Sources/SwiftDriver/Toolchains/DarwinToolchain.swift @@ -21,12 +21,14 @@ import class TSCBasic.DiagnosticsEngine import struct TSCBasic.AbsolutePath import struct TSCBasic.Diagnostic import var TSCBasic.localFileSystem +import typealias TSCBasic.ProcessEnvironmentBlock +import struct TSCBasic.ProcessEnvironmentKey /// Toolchain for Darwin-based platforms, such as macOS and iOS. /// /// FIXME: This class is not thread-safe. public final class DarwinToolchain: Toolchain { - public let env: [String: String] + public let env: ProcessEnvironmentBlock /// Doubles as path cache and point for overriding normal lookup private var toolPaths = [Tool: AbsolutePath]() @@ -45,7 +47,7 @@ public final class DarwinToolchain: Toolchain { public let dummyForTestingObjectFormat = Triple.ObjectFormat.macho - public init(env: [String: String], executor: DriverExecutor, fileSystem: FileSystem = localFileSystem, + public init(env: ProcessEnvironmentBlock, executor: DriverExecutor, fileSystem: FileSystem = localFileSystem, compilerExecutableDir: AbsolutePath? = nil, toolDirectory: AbsolutePath? = nil) { self.env = env self.executor = executor @@ -139,7 +141,7 @@ public final class DarwinToolchain: Toolchain { if target?.isMacOSX == true || hostIsMacOS { let result = try executor.checkNonZeroExit( args: "xcrun", "-sdk", "macosx", "--show-sdk-path", - environment: env + environment: env.legacyVars ).trimmingCharacters(in: .whitespacesAndNewlines) return try AbsolutePath(validating: result) } diff --git a/Sources/SwiftDriver/Toolchains/GenericUnixToolchain.swift b/Sources/SwiftDriver/Toolchains/GenericUnixToolchain.swift index 89fa2bca1..fc241bd96 100644 --- a/Sources/SwiftDriver/Toolchains/GenericUnixToolchain.swift +++ b/Sources/SwiftDriver/Toolchains/GenericUnixToolchain.swift @@ -13,6 +13,7 @@ import protocol TSCBasic.FileSystem import struct TSCBasic.AbsolutePath import var TSCBasic.localFileSystem +import typealias TSCBasic.ProcessEnvironmentBlock internal enum AndroidNDK { internal static func getOSName() -> String? { @@ -28,7 +29,7 @@ internal enum AndroidNDK { #endif } - internal static func getDefaultSysrootPath(in env: [String:String]) -> AbsolutePath? { + internal static func getDefaultSysrootPath(in env: ProcessEnvironmentBlock) -> AbsolutePath? { // The NDK is only available on an x86_64 hosts currently. #if arch(x86_64) guard let ndk = env["ANDROID_NDK_ROOT"], let os = getOSName() else { return nil } @@ -44,7 +45,7 @@ internal enum AndroidNDK { /// Toolchain for Unix-like systems. public final class GenericUnixToolchain: Toolchain { - public let env: [String: String] + public let env: ProcessEnvironmentBlock /// The executor used to run processes used to find tools and retrieve target info. public let executor: DriverExecutor @@ -62,7 +63,7 @@ public final class GenericUnixToolchain: Toolchain { public let dummyForTestingObjectFormat = Triple.ObjectFormat.elf - public init(env: [String: String], executor: DriverExecutor, fileSystem: FileSystem = localFileSystem, compilerExecutableDir: AbsolutePath? = nil, toolDirectory: AbsolutePath? = nil) { + public init(env: ProcessEnvironmentBlock, executor: DriverExecutor, fileSystem: FileSystem = localFileSystem, compilerExecutableDir: AbsolutePath? = nil, toolDirectory: AbsolutePath? = nil) { self.env = env self.executor = executor self.fileSystem = fileSystem diff --git a/Sources/SwiftDriver/Toolchains/Toolchain.swift b/Sources/SwiftDriver/Toolchains/Toolchain.swift index c696ca0be..e366bde6e 100644 --- a/Sources/SwiftDriver/Toolchains/Toolchain.swift +++ b/Sources/SwiftDriver/Toolchains/Toolchain.swift @@ -18,6 +18,8 @@ import func TSCBasic.lookupExecutablePath import class TSCBasic.DiagnosticsEngine import protocol TSCBasic.FileSystem import struct TSCBasic.AbsolutePath +import typealias TSCBasic.ProcessEnvironmentBlock +import struct TSCBasic.ProcessEnvironmentKey public enum Tool: Hashable { case swiftCompiler @@ -76,9 +78,9 @@ public struct ResolvedTool { /// Describes a toolchain, which includes information about compilers, linkers /// and other tools required to build Swift code. public protocol Toolchain { - init(env: [String: String], executor: DriverExecutor, fileSystem: FileSystem, compilerExecutableDir: AbsolutePath?, toolDirectory: AbsolutePath?) + init(env: ProcessEnvironmentBlock, executor: DriverExecutor, fileSystem: FileSystem, compilerExecutableDir: AbsolutePath?, toolDirectory: AbsolutePath?) - var env: [String: String] { get } + var env: ProcessEnvironmentBlock { get } var fileSystem: FileSystem { get } @@ -148,10 +150,10 @@ public protocol Toolchain { ) throws -> String func platformSpecificInterpreterEnvironmentVariables( - env: [String: String], + env: ProcessEnvironmentBlock, parsedOptions: inout ParsedOptions, sdkPath: VirtualPath.Handle?, - targetInfo: FrontendTargetInfo) throws -> [String: String] + targetInfo: FrontendTargetInfo) throws -> ProcessEnvironmentBlock func addPlatformSpecificCommonFrontendOptions( commandLine: inout [Job.ArgTemplate], @@ -199,12 +201,12 @@ extension Toolchain { } /// - Returns: String in the form of: `SWIFT_DRIVER_TOOLNAME_EXEC` - private func envVarName(for toolName: String) -> String { + private func envVarName(for toolName: String) -> ProcessEnvironmentKey { let lookupName = toolName .replacingOccurrences(of: "-", with: "_") .replacingOccurrences(of: "+", with: "X") .uppercased() - return "SWIFT_DRIVER_\(lookupName)_EXEC" + return ProcessEnvironmentKey("SWIFT_DRIVER_\(lookupName)_EXEC") } /// Use this property only for testing purposes, for example, @@ -314,7 +316,7 @@ extension Toolchain { let path = try executor.checkNonZeroExit( args: xcrun, "--find", executable, - environment: env + environment: env.legacyVars ).trimmingCharacters(in: .whitespacesAndNewlines) return try AbsolutePath(validating: path) } diff --git a/Sources/SwiftDriver/Toolchains/WebAssemblyToolchain.swift b/Sources/SwiftDriver/Toolchains/WebAssemblyToolchain.swift index 4e1fa1cd0..fa967d7e1 100644 --- a/Sources/SwiftDriver/Toolchains/WebAssemblyToolchain.swift +++ b/Sources/SwiftDriver/Toolchains/WebAssemblyToolchain.swift @@ -16,6 +16,7 @@ import struct TSCBasic.AbsolutePath import protocol TSCBasic.DiagnosticData import protocol TSCBasic.FileSystem import var TSCBasic.localFileSystem +import typealias TSCBasic.ProcessEnvironmentBlock /// Toolchain for WebAssembly-based systems. public final class WebAssemblyToolchain: Toolchain { @@ -42,7 +43,7 @@ public final class WebAssemblyToolchain: Toolchain { } } - public let env: [String: String] + public let env: ProcessEnvironmentBlock /// The executor used to run processes used to find tools and retrieve target info. public let executor: DriverExecutor @@ -59,7 +60,7 @@ public final class WebAssemblyToolchain: Toolchain { public let dummyForTestingObjectFormat = Triple.ObjectFormat.wasm - public init(env: [String: String], executor: DriverExecutor, fileSystem: FileSystem = localFileSystem, compilerExecutableDir: AbsolutePath? = nil, toolDirectory: AbsolutePath? = nil) { + public init(env: ProcessEnvironmentBlock, executor: DriverExecutor, fileSystem: FileSystem = localFileSystem, compilerExecutableDir: AbsolutePath? = nil, toolDirectory: AbsolutePath? = nil) { self.env = env self.executor = executor self.fileSystem = fileSystem @@ -149,10 +150,10 @@ public final class WebAssemblyToolchain: Toolchain { throw Error.sanitizersUnsupportedForTarget(targetTriple.triple) } - public func platformSpecificInterpreterEnvironmentVariables(env: [String : String], + public func platformSpecificInterpreterEnvironmentVariables(env: ProcessEnvironmentBlock, parsedOptions: inout ParsedOptions, sdkPath: VirtualPath.Handle?, - targetInfo: FrontendTargetInfo) throws -> [String : String] { + targetInfo: FrontendTargetInfo) throws -> ProcessEnvironmentBlock { throw Error.interactiveModeUnsupportedForTarget(targetInfo.target.triple.triple) } } diff --git a/Sources/SwiftDriver/Toolchains/WindowsToolchain.swift b/Sources/SwiftDriver/Toolchains/WindowsToolchain.swift index de4d92572..d9e341c8c 100644 --- a/Sources/SwiftDriver/Toolchains/WindowsToolchain.swift +++ b/Sources/SwiftDriver/Toolchains/WindowsToolchain.swift @@ -17,6 +17,7 @@ import protocol TSCBasic.DiagnosticData import protocol TSCBasic.FileSystem import struct TSCBasic.AbsolutePath import var TSCBasic.localFileSystem +import typealias TSCBasic.ProcessEnvironmentBlock extension WindowsToolchain { public enum ToolchainValidationError: Error, DiagnosticData { @@ -34,7 +35,7 @@ extension WindowsToolchain.ToolchainValidationError { } @_spi(Testing) public final class WindowsToolchain: Toolchain { - public let env: [String:String] + public let env: ProcessEnvironmentBlock public let executor: DriverExecutor public let fileSystem: FileSystem public let compilerExecutableDir: AbsolutePath? @@ -46,7 +47,7 @@ extension WindowsToolchain.ToolchainValidationError { private var toolPaths: [Tool:AbsolutePath] = [:] - public init(env: [String:String], executor: DriverExecutor, + public init(env: ProcessEnvironmentBlock, executor: DriverExecutor, fileSystem: FileSystem = localFileSystem, compilerExecutableDir: AbsolutePath? = nil, toolDirectory: AbsolutePath? = nil) { diff --git a/Sources/SwiftDriver/ToolingInterface/SimpleExecutor.swift b/Sources/SwiftDriver/ToolingInterface/SimpleExecutor.swift index 8196c47d8..33e1ba218 100644 --- a/Sources/SwiftDriver/ToolingInterface/SimpleExecutor.swift +++ b/Sources/SwiftDriver/ToolingInterface/SimpleExecutor.swift @@ -13,6 +13,7 @@ import protocol TSCBasic.FileSystem import struct TSCBasic.ProcessResult import class TSCBasic.Process +import typealias TSCBasic.ProcessEnvironmentBlock /// A simple executor sufficient for managing processes required during /// build planning: e.g. querying frontend target info. @@ -23,9 +24,9 @@ import class TSCBasic.Process @_spi(Testing) public class SimpleExecutor: DriverExecutor { public let resolver: ArgsResolver let fileSystem: FileSystem - let env: [String: String] + let env: ProcessEnvironmentBlock - public init(resolver: ArgsResolver, fileSystem: FileSystem, env: [String: String]) { + public init(resolver: ArgsResolver, fileSystem: FileSystem, env: ProcessEnvironmentBlock) { self.resolver = resolver self.fileSystem = fileSystem self.env = env @@ -37,7 +38,7 @@ import class TSCBasic.Process let arguments: [String] = try resolver.resolveArgumentList(for: job, useResponseFiles: .heuristic) var childEnv = env - childEnv.merge(job.extraEnvironment, uniquingKeysWith: { (_, new) in new }) + childEnv.merge(job.extraEnvironmentBlock, uniquingKeysWith: { (_, new) in new }) let process = try Process.launchProcess(arguments: arguments, env: childEnv) return try process.waitUntilExit() } @@ -48,7 +49,7 @@ import class TSCBasic.Process fatalError("Unsupported operation on current executor") } - public func checkNonZeroExit(args: String..., environment: [String : String]) throws -> String { + public func checkNonZeroExit(args: String..., environment: [String: String]) throws -> String { try Process.checkNonZeroExit(arguments: args, environment: environment) } diff --git a/Sources/SwiftDriver/ToolingInterface/ToolingUtil.swift b/Sources/SwiftDriver/ToolingInterface/ToolingUtil.swift index 751865b14..2a238be6c 100644 --- a/Sources/SwiftDriver/ToolingInterface/ToolingUtil.swift +++ b/Sources/SwiftDriver/ToolingInterface/ToolingUtil.swift @@ -12,11 +12,11 @@ import class TSCBasic.DiagnosticsEngine import struct TSCBasic.Diagnostic -import class TSCBasic.ProcessSet import enum TSCBasic.ProcessEnv -import struct TSCBasic.ProcessEnvironmentBlock +import typealias TSCBasic.ProcessEnvironmentBlock import var TSCBasic.localFileSystem import struct TSCBasic.AbsolutePath +import struct TSCBasic.ProcessEnvironmentKey import SwiftOptions //typedef enum { @@ -88,19 +88,18 @@ public func getSingleFrontendInvocationFromDriverArgumentsV2(driverPath: String, diagnosticCallback: @escaping (CInt, String) -> Void, compilerIntegratedTooling: Bool = false, forceNoOutputs: Bool = false) -> Bool { - let env = ProcessEnv.vars let executor: SimpleExecutor do { let resolver = try ArgsResolver(fileSystem: localFileSystem) executor = SimpleExecutor(resolver: resolver, fileSystem: localFileSystem, - env: env) + env: ProcessEnv.block) } catch { print("Unexpected error: \(error)") return true } - return getSingleFrontendInvocationFromDriverArgumentsV3(driverPath: driverPath, argList: argList, action: action, diagnostics: &diagnostics, diagnosticCallback: diagnosticCallback, env: env, executor: executor, compilerIntegratedTooling: compilerIntegratedTooling, forceNoOutputs: forceNoOutputs) + return getSingleFrontendInvocationFromDriverArgumentsV5(driverPath: driverPath, argList: argList, action: action, diagnostics: &diagnostics, diagnosticCallback: diagnosticCallback, envBlock: ProcessEnv.block, executor: executor, compilerIntegratedTooling: compilerIntegratedTooling, forceNoOutputs: forceNoOutputs) } public func getSingleFrontendInvocationFromDriverArgumentsV3(driverPath: String, @@ -115,6 +114,24 @@ public func getSingleFrontendInvocationFromDriverArgumentsV3(driverPath: String, return getSingleFrontendInvocationFromDriverArgumentsV4(driverPath: driverPath, argList: argList, action: action, diagnostics: &diagnostics, diagnosticCallback: diagnosticCallback, env: env, executor: executor, compilerIntegratedTooling: compilerIntegratedTooling, compilerExecutableDir: nil, forceNoOutputs: forceNoOutputs) } +public func getSingleFrontendInvocationFromDriverArgumentsV4(driverPath: String, + argList: [String], + action: ([String]) -> Bool, + diagnostics: inout [Diagnostic], + diagnosticCallback: @escaping (CInt, String) -> Void, + env: [String: String], + executor: some DriverExecutor, + compilerIntegratedTooling: Bool = false, + compilerExecutableDir: AbsolutePath? = nil, + forceNoOutputs: Bool = false) -> Bool { + let envBlock = env.reduce([:]) { (partialResult: ProcessEnvironmentBlock, tuple: (key: String, value: String)) in + var result = partialResult + result[ProcessEnvironmentKey(tuple.key)] = tuple.value + return result + } + return getSingleFrontendInvocationFromDriverArgumentsV5(driverPath: driverPath, argList: argList, action: action, diagnostics: &diagnostics, diagnosticCallback: diagnosticCallback, envBlock: envBlock, executor: executor, compilerIntegratedTooling: compilerIntegratedTooling, compilerExecutableDir: nil, forceNoOutputs: forceNoOutputs) +} + /// Generates the list of arguments that would be passed to the compiler /// frontend from the given driver arguments, for a single-compiler-invocation /// context. @@ -133,12 +150,12 @@ public func getSingleFrontendInvocationFromDriverArgumentsV3(driverPath: String, /// /// \note This function is not intended to create invocations which are /// suitable for use in REPL or immediate modes. -public func getSingleFrontendInvocationFromDriverArgumentsV4(driverPath: String, +public func getSingleFrontendInvocationFromDriverArgumentsV5(driverPath: String, argList: [String], action: ([String]) -> Bool, diagnostics: inout [Diagnostic], diagnosticCallback: @escaping (CInt, String) -> Void, - env: [String: String], + envBlock: ProcessEnvironmentBlock, executor: some DriverExecutor, compilerIntegratedTooling: Bool = false, compilerExecutableDir: AbsolutePath? = nil, @@ -200,7 +217,7 @@ public func getSingleFrontendInvocationFromDriverArgumentsV4(driverPath: String, // Instantiate the driver, setting up the toolchain in the process, etc. var driver = try Driver(args: parsedOptions.commandLine, - env: env, + envBlock: envBlock, diagnosticsOutput: .engine(diagnosticsEngine), executor: executor, compilerIntegratedTooling: compilerIntegratedTooling, diff --git a/Sources/SwiftDriverExecution/MultiJobExecutor.swift b/Sources/SwiftDriverExecution/MultiJobExecutor.swift index e042ecc62..26f4ec338 100644 --- a/Sources/SwiftDriverExecution/MultiJobExecutor.swift +++ b/Sources/SwiftDriverExecution/MultiJobExecutor.swift @@ -21,11 +21,11 @@ import var Foundation.SIGINT import class TSCBasic.DiagnosticsEngine import class TSCBasic.Process -import class TSCBasic.ProcessSet import protocol TSCBasic.DiagnosticData import protocol TSCBasic.FileSystem import struct TSCBasic.Diagnostic import struct TSCBasic.ProcessResult +import typealias TSCBasic.ProcessEnvironmentBlock import enum TSCUtility.Diagnostics // We either import the llbuildSwift shared library or the llbuild framework. @@ -64,7 +64,7 @@ public final class MultiJobExecutor { let argsResolver: ArgsResolver /// The environment variables. - let env: [String: String] + let env: ProcessEnvironmentBlock /// The file system. let fileSystem: TSCBasic.FileSystem @@ -105,7 +105,7 @@ public final class MultiJobExecutor { init( argsResolver: ArgsResolver, - env: [String: String], + env: ProcessEnvironmentBlock, fileSystem: TSCBasic.FileSystem, workload: DriverExecutorWorkload, executorDelegate: JobExecutionDelegate, @@ -296,7 +296,7 @@ public final class MultiJobExecutor { } /// Execute all jobs. - public func execute(env: [String: String], fileSystem: TSCBasic.FileSystem) throws { + public func execute(env: ProcessEnvironmentBlock, fileSystem: TSCBasic.FileSystem) throws { let context = createContext(env: env, fileSystem: fileSystem) let delegate = JobExecutorBuildDelegate(context) @@ -323,7 +323,7 @@ public final class MultiJobExecutor { } /// Create the context required during the execution. - private func createContext(env: [String: String], fileSystem: TSCBasic.FileSystem) -> Context { + private func createContext(env: ProcessEnvironmentBlock, fileSystem: TSCBasic.FileSystem) -> Context { let jobQueue = OperationQueue() jobQueue.name = "org.swift.driver.job-execution" jobQueue.maxConcurrentOperationCount = numParallelJobs @@ -583,7 +583,7 @@ class ExecuteJobRule: LLBuildRule { let context = self.context let resolver = context.argsResolver let job = myJob - let env = context.env.merging(job.extraEnvironment, uniquingKeysWith: { $1 }) + let env = context.env.merging(job.extraEnvironmentBlock, uniquingKeysWith: { $1 }) let value: DriverBuildValue var pendingFinish = false @@ -660,7 +660,7 @@ class ExecuteJobRule: LLBuildRule { context.delegateQueue.sync { let result = ProcessResult( arguments: [], - environment: env, + environmentBlock: env, exitStatus: .terminated(code: EXIT_FAILURE), output: Result.success([]), stderrOutput: Result.success([]) diff --git a/Sources/SwiftDriverExecution/SwiftDriverExecutor.swift b/Sources/SwiftDriverExecution/SwiftDriverExecutor.swift index 2c7478964..6f824c7cf 100644 --- a/Sources/SwiftDriverExecution/SwiftDriverExecutor.swift +++ b/Sources/SwiftDriverExecution/SwiftDriverExecutor.swift @@ -15,23 +15,23 @@ import class Foundation.FileHandle import class TSCBasic.DiagnosticsEngine import class TSCBasic.Process -import class TSCBasic.ProcessSet import enum TSCBasic.ProcessEnv import func TSCBasic.exec import protocol TSCBasic.FileSystem import struct TSCBasic.ProcessResult +import typealias TSCBasic.ProcessEnvironmentBlock public final class SwiftDriverExecutor: DriverExecutor { let diagnosticsEngine: DiagnosticsEngine let processSet: ProcessSet let fileSystem: FileSystem public let resolver: ArgsResolver - let env: [String: String] + let env: ProcessEnvironmentBlock public init(diagnosticsEngine: DiagnosticsEngine, processSet: ProcessSet, fileSystem: FileSystem, - env: [String: String]) throws { + env: ProcessEnvironmentBlock) throws { self.diagnosticsEngine = diagnosticsEngine self.processSet = processSet self.fileSystem = fileSystem @@ -50,14 +50,14 @@ public final class SwiftDriverExecutor: DriverExecutor { fileSystem: fileSystem) if job.requiresInPlaceExecution { - for (envVar, value) in job.extraEnvironment { - try ProcessEnv.setVar(envVar, value: value) + for (envVar, value) in job.extraEnvironmentBlock { + try ProcessEnv.setVar(envVar.value, value: value) } try exec(path: arguments[0], args: arguments) } else { var childEnv = env - childEnv.merge(job.extraEnvironment, uniquingKeysWith: { (_, new) in new }) + childEnv.merge(job.extraEnvironmentBlock, uniquingKeysWith: { (_, new) in new }) let process : ProcessProtocol if job.inputs.contains(TypedVirtualPath(file: .standardInput, type: .swift)) { process = try Process.launchProcessAndWriteInput( @@ -93,6 +93,11 @@ public final class SwiftDriverExecutor: DriverExecutor { return try Process.checkNonZeroExit(arguments: args, environment: environment) } + @discardableResult + public func checkNonZeroExit(args: String..., environmentBlock: ProcessEnvironmentBlock = ProcessEnv.block) throws -> String { + return try Process.checkNonZeroExit(arguments: args, environmentBlock: environmentBlock) + } + public func description(of job: Job, forceResponseFiles: Bool) throws -> String { let useResponseFiles : ResponseFileHandling = forceResponseFiles ? .forced : .heuristic let (args, usedResponseFile) = try resolver.resolveArgumentList(for: job, useResponseFiles: useResponseFiles) diff --git a/Sources/swift-build-sdk-interfaces/main.swift b/Sources/swift-build-sdk-interfaces/main.swift index 532b9a2cb..72264a385 100644 --- a/Sources/swift-build-sdk-interfaces/main.swift +++ b/Sources/swift-build-sdk-interfaces/main.swift @@ -24,7 +24,6 @@ import Bionic #endif import class TSCBasic.DiagnosticsEngine -import class TSCBasic.ProcessSet import enum TSCBasic.ProcessEnv import func TSCBasic.withTemporaryFile import struct TSCBasic.AbsolutePath @@ -142,7 +141,7 @@ do { let executor = try SwiftDriverExecutor(diagnosticsEngine: diagnosticsEngine, processSet: processSet, fileSystem: localFileSystem, - env: ProcessEnv.vars) + env: ProcessEnv.block) var args = ["swiftc", "-target", collector.targetTriple, tempPath.description, @@ -174,6 +173,7 @@ do { // modules for which a textual interface is discovered, ensuring that modules // always build from interface when one is available. if let supportedFlagsTestDriver = try? Driver(args: ["swiftc", "-v"], + envBlock: ProcessEnv.block, executor: executor, compilerExecutableDir: swiftcPath.parentDirectory), supportedFlagsTestDriver.isFrontendArgSupported(.moduleLoadMode) { @@ -185,6 +185,7 @@ do { let baselineABIDir = try getArgumentAsPath("-baseline-abi-dir") var driver = try Driver(args: args, + envBlock: ProcessEnv.block, diagnosticsOutput: .engine(diagnosticsEngine), executor: executor, compilerExecutableDir: swiftcPath.parentDirectory) diff --git a/Sources/swift-driver/main.swift b/Sources/swift-driver/main.swift index 8c2a75ef0..272a353f0 100644 --- a/Sources/swift-driver/main.swift +++ b/Sources/swift-driver/main.swift @@ -36,7 +36,6 @@ import func TSCBasic.exec import enum TSCBasic.ProcessEnv import class TSCBasic.DiagnosticsEngine import class TSCBasic.Process -import class TSCBasic.ProcessSet import func TSCBasic.resolveSymlinks import protocol TSCBasic.DiagnosticData import var TSCBasic.localFileSystem @@ -105,7 +104,7 @@ do { #endif let path: String = legacyExecutablePath.withUnsafeFileSystemRepresentation { String(cString: $0!) } - if localFileSystem.exists(AbsolutePath(path)) { + if localFileSystem.exists(try AbsolutePath(validating: path)) { let legacyDriverCommand = [path] + CommandLine.arguments[1...] try exec(path: path, args: legacyDriverCommand) } else { @@ -113,10 +112,6 @@ do { } } - if ProcessEnv.block["SWIFT_ENABLE_EXPLICIT_MODULE"] != nil { - CommandLine.arguments.append("-explicit-module-build") - } - let (mode, arguments) = try Driver.invocationRunMode(forArgs: CommandLine.arguments) if case .subcommand(let subcommand) = mode { // We are running as a subcommand, try to find the subcommand adjacent to the executable we are running as. @@ -146,8 +141,9 @@ do { let executor = try SwiftDriverExecutor(diagnosticsEngine: diagnosticsEngine, processSet: processSet, fileSystem: localFileSystem, - env: ProcessEnv.vars) + env: ProcessEnv.block) var driver = try Driver(args: arguments, + envBlock: ProcessEnv.block, diagnosticsOutput: .engine(diagnosticsEngine), executor: executor, integratedDriver: false) diff --git a/Sources/swift-help/main.swift b/Sources/swift-help/main.swift index 0cb4f4c07..17b907fe7 100644 --- a/Sources/swift-help/main.swift +++ b/Sources/swift-help/main.swift @@ -93,7 +93,7 @@ struct SwiftHelp: ParsableCommand { } func printIntro() { - let is256Color = ProcessEnv.vars["TERM"] == "xterm-256color" + let is256Color = ProcessEnv.block["TERM"] == "xterm-256color" let orangeRed = is256Color ? "\u{001b}[1;38;5;196m" : "" let plain = is256Color ? "\u{001b}[0m" : "" let plainBold = is256Color ? "\u{001b}[1m" : "" diff --git a/Tests/SwiftDriverTests/AssertDiagnosticsTests.swift b/Tests/SwiftDriverTests/AssertDiagnosticsTests.swift index f83795419..6aecd893d 100644 --- a/Tests/SwiftDriverTests/AssertDiagnosticsTests.swift +++ b/Tests/SwiftDriverTests/AssertDiagnosticsTests.swift @@ -186,11 +186,7 @@ class FailableTestCase: XCTestCase { } if #available(macOS 10.13, *) { - super.recordFailure( - withDescription: description, - inFile: filePath, atLine: lineNumber, - expected: expected - ) + super.record(XCTIssue(type: .assertionFailure, compactDescription: description, sourceCodeContext: XCTSourceCodeContext(location: XCTSourceCodeLocation(filePath: filePath, lineNumber: lineNumber)))) } else { fatalError(description, line: UInt(lineNumber)) } diff --git a/Tests/SwiftDriverTests/CachingBuildTests.swift b/Tests/SwiftDriverTests/CachingBuildTests.swift index 189cf781c..8fa736b9e 100644 --- a/Tests/SwiftDriverTests/CachingBuildTests.swift +++ b/Tests/SwiftDriverTests/CachingBuildTests.swift @@ -861,18 +861,8 @@ final class CachingBuildTests: XCTestCase { XCTAssertTrue(error is DependencyScanningError) } - let testDiagnostics: [ScannerDiagnosticPayload] - if try dependencyOracle.supportsPerScanDiagnostics(), - !scanDiagnostics.isEmpty { - testDiagnostics = scanDiagnostics - print("Using Per-Scan diagnostics") - } else { - testDiagnostics = try XCTUnwrap(dependencyOracle.getScannerDiagnostics()) - print("Using Scanner-Global diagnostics") - } - - XCTAssertEqual(testDiagnostics.count, 1) - XCTAssertEqual(testDiagnostics[0].severity, .error) + XCTAssertEqual(scanDiagnostics.count, 1) + XCTAssertEqual(scanDiagnostics[0].severity, .error) } } @@ -895,7 +885,7 @@ final class CachingBuildTests: XCTestCase { let casPath = path.appending(component: "cas") let sdkArgumentsForTesting = (try? Driver.sdkArgumentsForTesting()) ?? [] let mockBlocklistDir = try testInputsPath.appending(components: "Dummy.xctoolchain", "usr", "bin") - var env = ProcessEnv.vars + var env = ProcessEnv.block env["_SWIFT_DRIVER_MOCK_BLOCK_LIST_DIR"] = mockBlocklistDir.nativePathString(escaped: true) var driver = try Driver(args: ["swiftc", "-I", cHeadersPath.nativePathString(escaped: true), diff --git a/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift b/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift index b52b3ab00..398966d0a 100644 --- a/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift +++ b/Tests/SwiftDriverTests/ExplicitModuleBuildTests.swift @@ -135,9 +135,9 @@ func getStdlibShimsPaths(_ driver: Driver) throws -> (AbsolutePath, AbsolutePath let executor = try SwiftDriverExecutor(diagnosticsEngine: DiagnosticsEngine(handlers: [Driver.stderrDiagnosticsHandler]), processSet: ProcessSet(), fileSystem: localFileSystem, - env: ProcessEnv.vars) + env: ProcessEnv.block) let sdkPath = try executor.checkNonZeroExit( - args: "xcrun", "-sdk", "macosx", "--show-sdk-path").spm_chomp() + args: "xcrun", "-sdk", "macosx", "--show-sdk-path", environmentBlock: ProcessEnv.block).spm_chomp() let stdLibPath = try AbsolutePath(validating: sdkPath).appending(component: "usr") .appending(component: "lib") .appending(component: "swift") @@ -1822,8 +1822,8 @@ final class ExplicitModuleBuildTests: XCTestCase { let dependencyOracle = InterModuleDependencyOracle() let scanLibPath = try XCTUnwrap(toolchain.lookupSwiftScanLib()) try dependencyOracle.verifyOrCreateScannerInstance(swiftScanLibPath: scanLibPath) - guard try dependencyOracle.supportsScannerDiagnostics() else { - throw XCTSkip("libSwiftScan does not support diagnostics query.") + guard try dependencyOracle.supportsPerScanDiagnostics() else { + throw XCTSkip("libSwiftScan does not support diagnostics queries.") } // Missing Swift Interface dependency @@ -1857,16 +1857,8 @@ final class ExplicitModuleBuildTests: XCTestCase { try dependencyOracle.getDependencies(workingDirectory: path, commandLine: scannerCommand, diagnostics: &scanDiagnostics) - let potentialDiags: [ScannerDiagnosticPayload]? - if try dependencyOracle.supportsPerScanDiagnostics() { - potentialDiags = scanDiagnostics - print("Using Per-Scan diagnostics") - } else { - potentialDiags = try dependencyOracle.getScannerDiagnostics() - print("Using Scanner-Global diagnostics") - } - XCTAssertEqual(potentialDiags?.count, 5) - let diags = try XCTUnwrap(potentialDiags) + XCTAssertEqual(scanDiagnostics.count, 5) + let diags = try XCTUnwrap(scanDiagnostics) let error = diags[0] XCTAssertEqual(error.severity, .error) if try dependencyOracle.supportsDiagnosticSourceLocations() { @@ -1929,16 +1921,8 @@ final class ExplicitModuleBuildTests: XCTestCase { try dependencyOracle.getDependencies(workingDirectory: path, commandLine: scannerCommand, diagnostics: &scanDiagnostics) - let potentialDiags: [ScannerDiagnosticPayload]? - if try dependencyOracle.supportsPerScanDiagnostics() { - potentialDiags = scanDiagnostics - print("Using Per-Scan diagnostics") - } else { - potentialDiags = try dependencyOracle.getScannerDiagnostics() - print("Using Scanner-Global diagnostics") - } - XCTAssertEqual(potentialDiags?.count, 2) - let diags = try XCTUnwrap(potentialDiags) + XCTAssertEqual(scanDiagnostics.count, 2) + let diags = try XCTUnwrap(scanDiagnostics) let error = diags[0] XCTAssertEqual(error.severity, .error) if try dependencyOracle.supportsDiagnosticSourceLocations() { @@ -2106,7 +2090,7 @@ final class ExplicitModuleBuildTests: XCTestCase { let dummyBrokenDylib = path.appending(component: "lib_InternalSwiftScan.dylib") try localFileSystem.writeFileContents(dummyBrokenDylib, bytes: "n/a") - var environment = ProcessEnv.vars + var environment = ProcessEnv.block environment["SWIFT_DRIVER_SWIFTSCAN_LIB"] = dummyBrokenDylib.nativePathString(escaped: true) let cHeadersPath: AbsolutePath = @@ -2214,41 +2198,33 @@ final class ExplicitModuleBuildTests: XCTestCase { } } // Examine the results - if try dependencyOracle.supportsPerScanDiagnostics() { - for scanIndex in 0.. ( args: [String], - env: [String: String] = ProcessEnv.vars, + env: ProcessEnvironmentBlock = ProcessEnv.block, file: StaticString = #file, line: UInt = #line, do body: (inout Driver, DiagnosticVerifier) throws -> Result ) throws -> Result { @@ -34,7 +34,7 @@ func assertDriverDiagnostics ( /// and will emit all diagnostics so marked by the end of the block. func assertDriverDiagnostics( args: String..., - env: [String: String] = ProcessEnv.vars, + env: ProcessEnvironmentBlock = ProcessEnv.block, file: StaticString = #file, line: UInt = #line, do body: (inout Driver, DiagnosticVerifier) throws -> Void ) throws { @@ -48,7 +48,7 @@ func assertDriverDiagnostics( /// Asserts that the `Driver` it instantiates will not emit any warnings or errors. func assertNoDriverDiagnostics( args: String..., - env: [String: String] = ProcessEnv.vars, + env: ProcessEnvironmentBlock = ProcessEnv.block, file: StaticString = #file, line: UInt = #line, do body: (inout Driver) throws -> Void = { _ in } ) throws { diff --git a/Tests/SwiftDriverTests/IncrementalCompilationTests.swift b/Tests/SwiftDriverTests/IncrementalCompilationTests.swift index b9beff87b..48b276494 100644 --- a/Tests/SwiftDriverTests/IncrementalCompilationTests.swift +++ b/Tests/SwiftDriverTests/IncrementalCompilationTests.swift @@ -199,7 +199,7 @@ extension IncrementalCompilationTests { /// autolink job. /// Much of the code below is taking from testLinking(), but uses the output file map code here. func testAutolinkOutputPath() throws { - var env = ProcessEnv.vars + var env = ProcessEnv.block env["SWIFT_DRIVER_TESTS_ENABLE_EXEC_PATH_FALLBACK"] = "1" env["SWIFT_DRIVER_SWIFT_AUTOLINK_EXTRACT_EXEC"] = "//usr/bin/swift-autolink-extract" env["SWIFT_DRIVER_DSYMUTIL_EXEC"] = "//usr/bin/dsymutil" diff --git a/Tests/SwiftDriverTests/Inputs/ExplicitModuleDependencyBuildInputs.swift b/Tests/SwiftDriverTests/Inputs/ExplicitModuleDependencyBuildInputs.swift index 90211ffbc..1998facda 100644 --- a/Tests/SwiftDriverTests/Inputs/ExplicitModuleDependencyBuildInputs.swift +++ b/Tests/SwiftDriverTests/Inputs/ExplicitModuleDependencyBuildInputs.swift @@ -163,9 +163,9 @@ enum ModuleDependenciesInputs { "-Xcc", "-fno-implicit-module-maps", "-candidate-module-file", - "\(AbsolutePath("/dummy/path2/SwiftOnoneSupport.swiftmodule").nativePathString(escaped: true))", + "\(try! AbsolutePath(validating: "/dummy/path2/SwiftOnoneSupport.swiftmodule").nativePathString(escaped: true))", "-candidate-module-file", - "\(AbsolutePath("/dummy/path1/SwiftOnoneSupport.swiftmodule").nativePathString(escaped: true))", + "\(try! AbsolutePath(validating: "/dummy/path1/SwiftOnoneSupport.swiftmodule").nativePathString(escaped: true))", "-target", "x86_64-apple-macosx10.15", "-sdk", diff --git a/Tests/SwiftDriverTests/IntegrationTests.swift b/Tests/SwiftDriverTests/IntegrationTests.swift index 4da732c24..805e7a7df 100644 --- a/Tests/SwiftDriverTests/IntegrationTests.swift +++ b/Tests/SwiftDriverTests/IntegrationTests.swift @@ -25,7 +25,7 @@ private let packageDirectory = try! AbsolutePath(validating: #file).parentDirect // The "default" here means lit.py will be invoked as an executable, while otherwise let's use // python 3 explicitly. -private let pythonExec = ProcessEnv.vars.keys.contains("SWIFT_DRIVER_INTEGRATION_TESTS_USE_PYTHON_DEFAULT") ? "" : "python3" +private let pythonExec = ProcessEnv.block.keys.contains("SWIFT_DRIVER_INTEGRATION_TESTS_USE_PYTHON_DEFAULT") ? "" : "python3" func makeDriverSymlinks( in tempDir: AbsolutePath, @@ -58,7 +58,7 @@ func makeDriverSymlinks( return (swift: swift, swiftc: swiftc) } -func printCommand(args: [String], extraEnv: [String: String]) { +func printCommand(args: [String], extraEnv: ProcessEnvironmentBlock) { print("$", terminator: "") if !extraEnv.isEmpty { print(" env", terminator: "") @@ -86,13 +86,13 @@ final class IntegrationTests: IntegrationTestCase { "swift", "build", "--package-path", packageDirectory.pathString, "--scratch-path", buildPath.pathString ] - let extraEnv = [ "SWIFT_EXEC": compiler.pathString] + let extraEnv = [ProcessEnvironmentKey("SWIFT_EXEC"): compiler.pathString] printCommand(args: args, extraEnv: extraEnv) let result = try TSCBasic.Process.checkNonZeroExit( arguments: args, - environment: ProcessEnv.vars.merging(extraEnv) { $1 } + environmentBlock: ProcessEnv.block.merging(extraEnv) { $1 } ) XCTAssertTrue(localFileSystem.isExecutableFile(try AbsolutePath(validating: "debug/swift-driver", relativeTo: buildPath)), result) @@ -120,28 +120,28 @@ final class IntegrationTests: IntegrationTestCase { // they will fail. func testLitDriverTests() throws { - guard ProcessEnv.vars.keys.contains("SWIFT_DRIVER_ENABLE_FAILING_INTEGRATION_TESTS") else { + guard ProcessEnv.block.keys.contains("SWIFT_DRIVER_ENABLE_FAILING_INTEGRATION_TESTS") else { throw XCTSkip("Not all Driver tests supported") } try runLitTests(suite: "test", "Driver") } func testLitDriverValidationTests() throws { - guard ProcessEnv.vars.keys.contains("SWIFT_DRIVER_ENABLE_FAILING_INTEGRATION_TESTS") else { + guard ProcessEnv.block.keys.contains("SWIFT_DRIVER_ENABLE_FAILING_INTEGRATION_TESTS") else { throw XCTSkip("Not all Driver validation-tests supported") } try runLitTests(suite: "validation-test", "Driver") } func testLitInterpreterTests() throws { - guard ProcessEnv.vars.keys.contains("SWIFT_DRIVER_ENABLE_FAILING_INTEGRATION_TESTS") else { + guard ProcessEnv.block.keys.contains("SWIFT_DRIVER_ENABLE_FAILING_INTEGRATION_TESTS") else { throw XCTSkip("Interpreter tests unsupported") } try self.runLitTests(suite: "test", "Interpreter") } func testLitStdlibTests() throws { - guard ProcessEnv.vars.keys.contains("SWIFT_DRIVER_ENABLE_FAILING_INTEGRATION_TESTS") else { + guard ProcessEnv.block.keys.contains("SWIFT_DRIVER_ENABLE_FAILING_INTEGRATION_TESTS") else { throw XCTSkip("stdlib tests unsupported") } try self.runLitTests(suite: "test", "stdlib") @@ -155,7 +155,7 @@ final class IntegrationTests: IntegrationTestCase { #if os(macOS) try withTemporaryDirectory() { tempDir in guard - let litConfigPathString = ProcessEnv.vars["SWIFT_DRIVER_LIT_DIR"] + let litConfigPathString = ProcessEnv.block["SWIFT_DRIVER_LIT_DIR"] else { print("Skipping lit tests because SWIFT_DRIVER_LIT_DIR is not set") return @@ -217,7 +217,7 @@ final class IntegrationTests: IntegrationTestCase { ] let commandArgs = pythonExec.isEmpty ? args : [pythonExec] + args - let extraEnv = [ + let extraEnv: ProcessEnvironmentBlock = [ "SWIFT": swift.pathString, "SWIFTC": swiftc.pathString, "SWIFT_FORCE_TEST_NEW_DRIVER": "1", @@ -230,7 +230,7 @@ final class IntegrationTests: IntegrationTestCase { let process = TSCBasic.Process( arguments: commandArgs, - environment: ProcessEnv.vars.merging(extraEnv) { $1 }, + environmentBlock: ProcessEnv.block.merging(extraEnv) { $1 }, outputRedirection: .none ) try process.launch() @@ -245,7 +245,7 @@ final class IntegrationTests: IntegrationTestCase { open class IntegrationTestCase: XCTestCase { #if os(macOS) override open class var defaultTestSuite: XCTestSuite { - if ProcessEnv.vars.keys.contains("SWIFT_DRIVER_ENABLE_INTEGRATION_TESTS") { + if ProcessEnv.block.keys.contains("SWIFT_DRIVER_ENABLE_INTEGRATION_TESTS") { return super.defaultTestSuite } return XCTestSuite(name: String(describing: type(of: self))) diff --git a/Tests/SwiftDriverTests/JobExecutorTests.swift b/Tests/SwiftDriverTests/JobExecutorTests.swift index 0f51b5d70..c0519b82f 100644 --- a/Tests/SwiftDriverTests/JobExecutorTests.swift +++ b/Tests/SwiftDriverTests/JobExecutorTests.swift @@ -16,7 +16,7 @@ import TSCBasic import SwiftDriverExecution import TestUtilities -extension Job.ArgTemplate: ExpressibleByStringLiteral { +extension Job.ArgTemplate: @retroactive ExpressibleByStringLiteral { public init(stringLiteral value: String) { self = .flag(value) } @@ -24,11 +24,11 @@ extension Job.ArgTemplate: ExpressibleByStringLiteral { class JobCollectingDelegate: JobExecutionDelegate { struct StubProcess: ProcessProtocol { - static func launchProcess(arguments: [String], env: [String : String]) throws -> StubProcess { + static func launchProcess(arguments: [String], env: ProcessEnvironmentBlock) throws -> StubProcess { return .init() } - static func launchProcessAndWriteInput(arguments: [String], env: [String : String], + static func launchProcessAndWriteInput(arguments: [String], env: ProcessEnvironmentBlock, inputFileHandle: FileHandle) throws -> StubProcess { return .init() } @@ -38,7 +38,7 @@ class JobCollectingDelegate: JobExecutionDelegate { func waitUntilExit() throws -> ProcessResult { return ProcessResult( arguments: [], - environment: [:], + environmentBlock: [:], exitStatus: .terminated(code: EXIT_SUCCESS), output: Result.success(ByteString("test").contents), stderrOutput: Result.success([]) @@ -66,7 +66,7 @@ extension DarwinToolchain { Result { let result = try executor.checkNonZeroExit( args: "xcrun", "-sdk", "macosx", "--show-sdk-path", - environment: env + environment: env.legacyVars ).spm_chomp() return try AbsolutePath(validating: result) } @@ -105,8 +105,8 @@ final class JobExecutorTests: XCTestCase { let executor = try SwiftDriverExecutor(diagnosticsEngine: DiagnosticsEngine(), processSet: ProcessSet(), fileSystem: localFileSystem, - env: ProcessEnv.vars) - let toolchain = DarwinToolchain(env: ProcessEnv.vars, executor: executor) + env: ProcessEnv.block) + let toolchain = DarwinToolchain(env: ProcessEnv.block, executor: executor) try withTemporaryDirectory { path in let foo = path.appending(component: "foo.swift") let main = path.appending(component: "main.swift") @@ -218,8 +218,8 @@ final class JobExecutorTests: XCTestCase { let executor = try SwiftDriverExecutor(diagnosticsEngine: DiagnosticsEngine(), processSet: ProcessSet(), fileSystem: localFileSystem, - env: ProcessEnv.vars) - let toolchain = DarwinToolchain(env: ProcessEnv.vars, executor: executor) + env: ProcessEnv.block) + let toolchain = DarwinToolchain(env: ProcessEnv.block, executor: executor) try withTemporaryDirectory { path in let exec = path.appending(component: "main") let compile = Job( @@ -306,15 +306,15 @@ final class JobExecutorTests: XCTestCase { diagnosticsEngine: DiagnosticsEngine(), processType: JobCollectingDelegate.StubProcess.self ) - try executor.execute(env: ProcessEnv.vars, fileSystem: localFileSystem) + try executor.execute(env: ProcessEnv.block, fileSystem: localFileSystem) XCTAssertEqual(try delegate.finished[0].1.utf8Output(), "test") #endif } func testSwiftDriverExecOverride() throws { - var env = ProcessEnv.vars - let envVarName = "SWIFT_DRIVER_SWIFT_FRONTEND_EXEC" + var env = ProcessEnv.block + let envVarName = ProcessEnvironmentKey("SWIFT_DRIVER_SWIFT_FRONTEND_EXEC") let dummyPath = "/some/garbage/path/fnord" let executor = try SwiftDriverExecutor(diagnosticsEngine: DiagnosticsEngine(), processSet: ProcessSet(), @@ -462,10 +462,10 @@ final class JobExecutorTests: XCTestCase { private func getHostToolchainSdkArg(_ executor: SwiftDriverExecutor) throws -> [String] { #if os(macOS) - let toolchain = DarwinToolchain(env: ProcessEnv.vars, executor: executor) + let toolchain = DarwinToolchain(env: ProcessEnv.block, executor: executor) return try ["-sdk", toolchain.sdk.get().pathString] #elseif os(Windows) - let toolchain = WindowsToolchain(env: ProcessEnv.vars, executor: executor) + let toolchain = WindowsToolchain(env: ProcessEnv.block, executor: executor) if let path = try toolchain.defaultSDKPath(nil) { return ["-sdk", path.nativePathString(escaped: false)] } @@ -485,11 +485,12 @@ final class JobExecutorTests: XCTestCase { let executor = try SwiftDriverExecutor(diagnosticsEngine: diags, processSet: ProcessSet(), fileSystem: localFileSystem, - env: ProcessEnv.vars) + env: ProcessEnv.block) let outputPath = path.appending(component: "finalOutput") var driver = try Driver(args: ["swiftc", main.pathString, "-driver-filelist-threshold", "0", "-o", outputPath.pathString] + getHostToolchainSdkArg(executor), + envBlock: ProcessEnv.block, diagnosticsOutput: .engine(diags), fileSystem: localFileSystem, executor: executor) @@ -519,12 +520,13 @@ final class JobExecutorTests: XCTestCase { let executor = try SwiftDriverExecutor(diagnosticsEngine: diags, processSet: ProcessSet(), fileSystem: localFileSystem, - env: ProcessEnv.vars) + env: ProcessEnv.block) let outputPath = path.appending(component: "finalOutput") var driver = try Driver(args: ["swiftc", main.pathString, "-save-temps", "-driver-filelist-threshold", "0", "-o", outputPath.pathString] + getHostToolchainSdkArg(executor), + envBlock: ProcessEnv.block, diagnosticsOutput: .engine(diags), fileSystem: localFileSystem, executor: executor) @@ -554,12 +556,13 @@ final class JobExecutorTests: XCTestCase { let executor = try SwiftDriverExecutor(diagnosticsEngine: diags, processSet: ProcessSet(), fileSystem: localFileSystem, - env: ProcessEnv.vars) + env: ProcessEnv.block) let outputPath = path.appending(component: "finalOutput") var driver = try Driver(args: ["swiftc", main.pathString, "-driver-filelist-threshold", "0", "-Xfrontend", "-debug-crash-immediately", "-o", outputPath.pathString] + getHostToolchainSdkArg(executor), + envBlock: ProcessEnv.block, diagnosticsOutput: .engine(diags), fileSystem: localFileSystem, executor: executor) diff --git a/Tests/SwiftDriverTests/ParsableMessageTests.swift b/Tests/SwiftDriverTests/ParsableMessageTests.swift index 55156c64d..a2c4538a1 100644 --- a/Tests/SwiftDriverTests/ParsableMessageTests.swift +++ b/Tests/SwiftDriverTests/ParsableMessageTests.swift @@ -271,7 +271,7 @@ final class ParsableMessageTests: XCTestCase { // Now hijack the error stream and emit finished messages let errorOutput = try withHijackedErrorStream { let resultSuccess = ProcessResult(arguments: args!, - environment: ProcessEnv.vars, + environmentBlock: ProcessEnv.block, exitStatus: ProcessResult.ExitStatus.terminated(code: EXIT_SUCCESS), output: Result.success([]), stderrOutput: Result.success([])) @@ -360,7 +360,7 @@ final class ParsableMessageTests: XCTestCase { // Now hijack the error stream and emit finished messages let errorOutput = try withHijackedErrorStream { let resultSignalled = ProcessResult(arguments: args!, - environment: ProcessEnv.vars, + environmentBlock: ProcessEnv.block, exitStatus: status, output: Result.success([]), stderrOutput: Result.success([])) diff --git a/Tests/SwiftDriverTests/SwiftDriverTests.swift b/Tests/SwiftDriverTests/SwiftDriverTests.swift index e3779ee48..07cc1199a 100644 --- a/Tests/SwiftDriverTests/SwiftDriverTests.swift +++ b/Tests/SwiftDriverTests/SwiftDriverTests.swift @@ -76,12 +76,12 @@ final class SwiftDriverTests: XCTestCase { try? localFileSystem.removeFileTree(AbsolutePath(validating: self.ld.dirname)) } - private var envWithFakeSwiftHelp: [String: String] { + private var envWithFakeSwiftHelp: ProcessEnvironmentBlock { // During build-script builds, build products are not installed into the toolchain // until a project's tests pass. However, we're in the middle of those tests, // so there is no swift-help in the toolchain yet. Set the environment variable // as if we had found it for the purposes of testing build planning. - var env = ProcessEnv.vars + var env = ProcessEnv.block env["SWIFT_DRIVER_SWIFT_HELP_EXEC"] = "/tmp/.test-swift-help" return env } @@ -92,14 +92,14 @@ final class SwiftDriverTests: XCTestCase { let executor = try SwiftDriverExecutor(diagnosticsEngine: DiagnosticsEngine(), processSet: ProcessSet(), fileSystem: localFileSystem, - env: ProcessEnv.vars) + env: ProcessEnv.block) let toolchain: Toolchain #if os(macOS) - toolchain = DarwinToolchain(env: ProcessEnv.vars, executor: executor) + toolchain = DarwinToolchain(env: ProcessEnv.block, executor: executor) #elseif os(Windows) - toolchain = WindowsToolchain(env: ProcessEnv.vars, executor: executor) + toolchain = WindowsToolchain(env: ProcessEnv.block, executor: executor) #else - toolchain = GenericUnixToolchain(env: ProcessEnv.vars, executor: executor) + toolchain = GenericUnixToolchain(env: ProcessEnv.block, executor: executor) #endif do { _ = try toolchain.getToolPath(.lldb) @@ -516,7 +516,7 @@ final class SwiftDriverTests: XCTestCase { XCTAssertEqual(try Driver(args: ["swiftc", "-j", "4"]).numParallelJobs, 4) - var env = ProcessEnv.vars + var env = ProcessEnv.block env["SWIFTC_MAXIMUM_DETERMINISM"] = "1" XCTAssertEqual(try Driver(args: ["swiftc", "-j", "4"], env: env).numParallelJobs, 1) } @@ -526,7 +526,7 @@ final class SwiftDriverTests: XCTestCase { $1.expect(.error("invalid value '0' in '-j'")) } - var env = ProcessEnv.vars + var env = ProcessEnv.block env["SWIFTC_MAXIMUM_DETERMINISM"] = "1" try assertDriverDiagnostics(args: "swiftc", "-j", "8", env: env) { $1.expect(.remark("SWIFTC_MAXIMUM_DETERMINISM overriding -j")) @@ -555,7 +555,7 @@ final class SwiftDriverTests: XCTestCase { } do { - var env = ProcessEnv.vars + var env = ProcessEnv.block env["SWIFT_DRIVER_TESTS_ENABLE_EXEC_PATH_FALLBACK"] = "1" env["RC_DEBUG_PREFIX_MAP"] = "old=new" var driver = try Driver(args: ["swiftc", "-c", "-target", "arm64-apple-macos12", "foo.swift"], env: env) @@ -609,7 +609,7 @@ final class SwiftDriverTests: XCTestCase { XCTAssertJobInvocationMatches(jobs[0], .flag("-file-compilation-dir"), .path(VirtualPath.lookup(path))) } - let workingDirectory = AbsolutePath("/tmp") + let workingDirectory = try AbsolutePath(validating: "/tmp") try assertNoDriverDiagnostics(args: "swiftc", "foo.swift", "-g", "-c", "-working-directory", workingDirectory.nativePathString(escaped: false)) { driver in let jobs = try driver.planBuild() let path = try VirtualPath.intern(path: workingDirectory.nativePathString(escaped: false)) @@ -628,7 +628,7 @@ final class SwiftDriverTests: XCTestCase { } func testDwarfVersionSetting() throws { - var environment = ProcessEnv.vars + var environment = ProcessEnv.block environment["SDKROOT"] = nil let driver = try Driver(args: ["swiftc", "foo.swift"]) @@ -1741,7 +1741,7 @@ final class SwiftDriverTests: XCTestCase { let manyArgs = (1...20000).map { "-DTEST_\($0)" } // Needs response file do { - let source = AbsolutePath("/foo.swift") + let source = try AbsolutePath(validating: "/foo.swift") var driver = try Driver(args: ["swift"] + manyArgs + [source.nativePathString(escaped: false)]) let jobs = try driver.planBuild() XCTAssertEqual(jobs.count, 1) @@ -1773,7 +1773,7 @@ final class SwiftDriverTests: XCTestCase { // Forced response file do { - let source = AbsolutePath("/foo.swift") + let source = try AbsolutePath(validating: "/foo.swift") var driver = try Driver(args: ["swift"] + [source.nativePathString(escaped: false)]) let jobs = try driver.planBuild() XCTAssertEqual(jobs.count, 1) @@ -1899,7 +1899,7 @@ final class SwiftDriverTests: XCTestCase { } func testLinking() throws { - var env = ProcessEnv.vars + var env = ProcessEnv.block env["SWIFT_DRIVER_TESTS_ENABLE_EXEC_PATH_FALLBACK"] = "1" env["SWIFT_DRIVER_SWIFT_AUTOLINK_EXTRACT_EXEC"] = "/garbage/swift-autolink-extract" env["SWIFT_DRIVER_DSYMUTIL_EXEC"] = "/garbage/dsymutil" @@ -2496,7 +2496,7 @@ final class SwiftDriverTests: XCTestCase { } func testWebAssemblyUnsupportedFeatures() throws { - var env = ProcessEnv.vars + var env = ProcessEnv.block env["SWIFT_DRIVER_SWIFT_AUTOLINK_EXTRACT_EXEC"] = "/garbage/swift-autolink-extract" do { var driver = try Driver(args: ["swift", "-target", "wasm32-unknown-wasi", "foo.swift"], env: env) @@ -2552,7 +2552,7 @@ final class SwiftDriverTests: XCTestCase { } func testCompatibilityLibs() throws { - var env = ProcessEnv.vars + var env = ProcessEnv.block env["SWIFT_DRIVER_TESTS_ENABLE_EXEC_PATH_FALLBACK"] = "1" try withTemporaryDirectory { path in let path5_0Mac = path.appending(components: "macosx", "libswiftCompatibility50.a") @@ -2956,7 +2956,7 @@ final class SwiftDriverTests: XCTestCase { } func testADDITIONAL_SWIFT_DRIVER_FLAGS() throws { - var env = ProcessEnv.vars + var env = ProcessEnv.block env["ADDITIONAL_SWIFT_DRIVER_FLAGS"] = "-Xfrontend -unknown1 -Xfrontend -unknown2" var driver = try Driver(args: ["swiftc", "foo.swift", "-module-name", "Test"], env: env) let plannedJobs = try driver.planBuild().removingAutolinkExtractJobs() @@ -3012,7 +3012,7 @@ final class SwiftDriverTests: XCTestCase { } func testPackageInterfacePathImplicit() throws { - let envVars = ProcessEnv.vars + let envVars = ProcessEnv.block // A .package.swiftinterface should only be generated if package-name is passed. do { @@ -3055,7 +3055,7 @@ final class SwiftDriverTests: XCTestCase { } func testSingleThreadedWholeModuleOptimizationCompiles() throws { - var envVars = ProcessEnv.vars + var envVars = ProcessEnv.block envVars["SWIFT_DRIVER_LD_EXEC"] = ld.nativePathString(escaped: false) var driver1 = try Driver(args: ["swiftc", "-whole-module-optimization", "foo.swift", "bar.swift", "-emit-library", "-emit-module", "-module-name", "Test", "-emit-module-interface", "-emit-objc-header-path", "Test-Swift.h", "-emit-private-module-interface-path", "Test.private.swiftinterface", "-emit-tbd", "-o", "libTest"], env: envVars) @@ -3095,7 +3095,7 @@ final class SwiftDriverTests: XCTestCase { func testIndexFileEntryInSupplementaryFileOutputMap() throws { - let workingDirectory = AbsolutePath("/tmp") + let workingDirectory = try AbsolutePath(validating: "/tmp") var driver1 = try Driver(args: [ "swiftc", "foo1.swift", "foo2.swift", "foo3.swift", "foo4.swift", "foo5.swift", "-index-file", "-index-file-path", "foo5.swift", "-o", "/tmp/t.o", @@ -3556,7 +3556,7 @@ final class SwiftDriverTests: XCTestCase { } func testEmitModuleSeparately() throws { - var envVars = ProcessEnv.vars + var envVars = ProcessEnv.block envVars["SWIFT_DRIVER_LD_EXEC"] = ld.nativePathString(escaped: false) do { @@ -3644,7 +3644,7 @@ final class SwiftDriverTests: XCTestCase { } func testEmitModuleSeparatelyWMO() throws { - var envVars = ProcessEnv.vars + var envVars = ProcessEnv.block envVars["SWIFT_DRIVER_LD_EXEC"] = ld.nativePathString(escaped: false) let root = localFileSystem.currentWorkingDirectory!.appending(components: "foo", "bar") @@ -3955,7 +3955,7 @@ final class SwiftDriverTests: XCTestCase { XCTAssertEqual(plannedJobs.count, 2) XCTAssertEqual(plannedJobs[0].kind, .compile) XCTAssertEqual(plannedJobs[1].kind, .link) - try XCTAssertJobInvocationMatches(plannedJobs[0], .flag("-default-isolation"), "MainActor") + XCTAssertJobInvocationMatches(plannedJobs[0], .flag("-default-isolation"), "MainActor") } func testImmediateMode() throws { @@ -3994,9 +3994,9 @@ final class SwiftDriverTests: XCTestCase { if !driver.targetTriple.isWindows { #if os(macOS) // On darwin, swift ships in the OS. Immediate mode should use that runtime. - XCTAssertFalse(job.extraEnvironment.keys.contains(envVar)) + XCTAssertFalse(job.extraEnvironmentBlock.keys.contains(ProcessEnvironmentKey(envVar))) #else - XCTAssertTrue(job.extraEnvironment.keys.contains(envVar)) + XCTAssertTrue(job.extraEnvironmentBlock.keys.contains(ProcessEnvironmentKey(envVar))) #endif } } @@ -4029,7 +4029,7 @@ final class SwiftDriverTests: XCTestCase { XCTAssertEqual(job.inputs[0].file, try toPath("foo.swift")) XCTAssertEqual(job.outputs.count, 0) - let envVar: String + let envVar: ProcessEnvironmentKey if driver.targetTriple.isDarwin { envVar = "DYLD_LIBRARY_PATH" } else if driver.targetTriple.isWindows { @@ -4043,9 +4043,9 @@ final class SwiftDriverTests: XCTestCase { // libraries on Windows. There is no way to derive the path from the // command on Windows. if !driver.targetTriple.isWindows { - XCTAssertTrue(job.extraEnvironment[envVar, default: ""].contains("/path/to/lib")) + XCTAssertTrue(job.extraEnvironmentBlock[envVar, default: ""].contains("/path/to/lib")) if driver.targetTriple.isDarwin { - XCTAssertTrue(job.extraEnvironment["DYLD_FRAMEWORK_PATH", default: ""].contains("/path/to/framework")) + XCTAssertTrue(job.extraEnvironmentBlock["DYLD_FRAMEWORK_PATH", default: ""].contains("/path/to/framework")) } } @@ -4081,7 +4081,7 @@ final class SwiftDriverTests: XCTestCase { } func testTargetVariant() throws { - var envVars = ProcessEnv.vars + var envVars = ProcessEnv.block envVars["SWIFT_DRIVER_LD_EXEC"] = ld.nativePathString(escaped: false) do { @@ -4242,7 +4242,7 @@ final class SwiftDriverTests: XCTestCase { #if os(macOS) do { try withTemporaryDirectory { path in - var env = ProcessEnv.vars + var env = ProcessEnv.block env["LD_TRACE_FILE"] = path.appending(component: ".LD_TRACE").nativePathString(escaped: false) var driver = try Driver(args: ["swiftc", "-target", "x86_64-apple-macosx10.14", @@ -4402,7 +4402,7 @@ final class SwiftDriverTests: XCTestCase { func testDisableClangTargetForImplicitModule() throws { #if os(macOS) - var envVars = ProcessEnv.vars + var envVars = ProcessEnv.block envVars["SWIFT_DRIVER_LD_EXEC"] = ld.nativePathString(escaped: false) let sdkRoot = try testInputsPath.appending(component: "SDKChecks").appending(component: "iPhoneOS.sdk") @@ -4418,7 +4418,7 @@ final class SwiftDriverTests: XCTestCase { } func testPCHasCompileInput() throws { - var envVars = ProcessEnv.vars + var envVars = ProcessEnv.block envVars["SWIFT_DRIVER_LD_EXEC"] = ld.nativePathString(escaped: false) var driver = try Driver(args: ["swiftc", "-target", "x86_64-apple-macosx10.14", "-enable-bridging-pch", "-import-objc-header", "TestInputHeader.h", "foo.swift"], @@ -4649,7 +4649,7 @@ final class SwiftDriverTests: XCTestCase { } func testProfileLinkerArgs() throws { - var envVars = ProcessEnv.vars + var envVars = ProcessEnv.block envVars["SWIFT_DRIVER_LD_EXEC"] = ld.nativePathString(escaped: false) do { @@ -4762,7 +4762,7 @@ final class SwiftDriverTests: XCTestCase { $0.send("garbage") } - var env = ProcessEnv.vars + var env = ProcessEnv.block env["SWIFT_DRIVER_SWIFT_AUTOLINK_EXTRACT_EXEC"] = "//bin/swift-autolink-extract" for triple in ["wasm32-unknown-wasi", "wasm32-unknown-wasip1-threads"] { @@ -4896,7 +4896,7 @@ final class SwiftDriverTests: XCTestCase { // Drop SWIFT_DRIVER_CLANG_EXEC from the environment so it doesn't // interfere with tool lookup. - var env = ProcessEnv.vars + var env = ProcessEnv.block env.removeValue(forKey: "SWIFT_DRIVER_CLANG_EXEC") var driver = try Driver(args: ["swiftc", @@ -4910,7 +4910,7 @@ final class SwiftDriverTests: XCTestCase { // WASI toolchain do { - var env = ProcessEnv.vars + var env = ProcessEnv.block env["SWIFT_DRIVER_SWIFT_AUTOLINK_EXTRACT_EXEC"] = "//bin/swift-autolink-extract" try withTemporaryDirectory { resourceDir in @@ -4955,7 +4955,7 @@ final class SwiftDriverTests: XCTestCase { } func testDarwinSDKToolchainName() throws { - var envVars = ProcessEnv.vars + var envVars = ProcessEnv.block envVars["SWIFT_DRIVER_LD_EXEC"] = ld.nativePathString(escaped: false) try withTemporaryDirectory { tmpDir in @@ -4977,7 +4977,7 @@ final class SwiftDriverTests: XCTestCase { // Test cases ported from Driver/macabi-environment.swift func testDarwinSDKVersioning() throws { - var envVars = ProcessEnv.vars + var envVars = ProcessEnv.block envVars["SWIFT_DRIVER_LD_EXEC"] = ld.nativePathString(escaped: false) try withTemporaryDirectory { tmpDir in @@ -5055,7 +5055,7 @@ final class SwiftDriverTests: XCTestCase { } do { - var envVars = ProcessEnv.vars + var envVars = ProcessEnv.block envVars["SWIFT_DRIVER_LD_EXEC"] = ld.nativePathString(escaped: false) var driver = try Driver(args: ["swiftc", @@ -5089,7 +5089,7 @@ final class SwiftDriverTests: XCTestCase { } do { - var envVars = ProcessEnv.vars + var envVars = ProcessEnv.block envVars["SWIFT_DRIVER_LD_EXEC"] = ld.nativePathString(escaped: false) var driver = try Driver(args: ["swiftc", @@ -5164,7 +5164,7 @@ final class SwiftDriverTests: XCTestCase { } func testDarwinLinkerPlatformVersion() throws { - var envVars = ProcessEnv.vars + var envVars = ProcessEnv.block envVars["SWIFT_DRIVER_LD_EXEC"] = ld.nativePathString(escaped: false) do { @@ -5300,7 +5300,7 @@ final class SwiftDriverTests: XCTestCase { } do { - var env = ProcessEnv.vars + var env = ProcessEnv.block // As per Unix conventions, /var/empty is expected to exist and be empty. // This gives us a non-existent path that we can use for libtool which // allows us to run this this on non-Darwin platforms. @@ -5388,7 +5388,7 @@ final class SwiftDriverTests: XCTestCase { XCTAssertTrue(tracedJobs[0].inputs.contains(.init(file: try toPath("bar.swift").intern(), type: .swift))) } do { - var env = ProcessEnv.vars + var env = ProcessEnv.block env["SWIFT_LOADED_MODULE_TRACE_FILE"] = "/some/path/to/the.trace.json" var driver = try Driver(args: ["swiftc", "-typecheck", "-emit-loaded-module-trace", "foo.swift"], @@ -5659,13 +5659,13 @@ final class SwiftDriverTests: XCTestCase { let executor = try SwiftDriverExecutor(diagnosticsEngine: DiagnosticsEngine(), processSet: ProcessSet(), fileSystem: localFileSystem, - env: ProcessEnv.vars) + env: ProcessEnv.block) #if os(macOS) - toolchain = DarwinToolchain(env: ProcessEnv.vars, executor: executor) + toolchain = DarwinToolchain(env: ProcessEnv.block, executor: executor) #elseif os(Windows) - toolchain = WindowsToolchain(env: ProcessEnv.vars, executor: executor) + toolchain = WindowsToolchain(env: ProcessEnv.block, executor: executor) #else - toolchain = GenericUnixToolchain(env: ProcessEnv.vars, executor: executor) + toolchain = GenericUnixToolchain(env: ProcessEnv.block, executor: executor) #endif XCTAssertEqual( @@ -5680,7 +5680,7 @@ final class SwiftDriverTests: XCTestCase { XCTAssertThrowsError(try driver1.toolchain.getToolPath(.dsymutil)) } - var env = ProcessEnv.vars + var env = ProcessEnv.block env["SWIFT_DRIVER_TESTS_ENABLE_EXEC_PATH_FALLBACK"] = "1" let driver2 = try Driver(args: ["swift", "main.swift"], env: env) XCTAssertNoThrow(try driver2.toolchain.getToolPath(.dsymutil)) @@ -5781,7 +5781,7 @@ final class SwiftDriverTests: XCTestCase { let resolver: ArgsResolver func execute(job: Job, forceResponseFiles: Bool, recordedInputModificationDates: [TypedVirtualPath : TimePoint]) throws -> ProcessResult { - return ProcessResult(arguments: [], environment: [:], exitStatus: .terminated(code: 0), output: .success(Array("bad JSON".utf8)), stderrOutput: .success([])) + return ProcessResult(arguments: [], environmentBlock: [:], exitStatus: .terminated(code: 0), output: .success(Array("bad JSON".utf8)), stderrOutput: .success([])) } func execute(workload: DriverExecutorWorkload, delegate: JobExecutionDelegate, @@ -5790,19 +5790,22 @@ final class SwiftDriverTests: XCTestCase { recordedInputModificationDates: [TypedVirtualPath : TimePoint]) throws { fatalError() } - func checkNonZeroExit(args: String..., environment: [String : String]) throws -> String { + func checkNonZeroExit(args: String..., environment: [String: String]) throws -> String { return try Process.checkNonZeroExit(arguments: args, environment: environment) } + func checkNonZeroExit(args: String..., environmentBlock: ProcessEnvironmentBlock) throws -> String { + return try Process.checkNonZeroExit(arguments: args, environmentBlock: environmentBlock) + } func description(of job: Job, forceResponseFiles: Bool) throws -> String { fatalError() } } // Override path to libSwiftScan to force the fallback of using the executor - var hideSwiftScanEnv = ProcessEnv.vars + var hideSwiftScanEnv = ProcessEnv.block hideSwiftScanEnv["SWIFT_DRIVER_SWIFTSCAN_LIB"] = "/bad/path/lib_InternalSwiftScan.dylib" XCTAssertThrowsError(try Driver(args: ["swift", "-print-target-info"], - env: hideSwiftScanEnv, + envBlock: hideSwiftScanEnv, executor: MockExecutor(resolver: ArgsResolver(fileSystem: InMemoryFileSystem())))) { error in if case .decodingError = error as? JobExecutionError {} @@ -5890,7 +5893,7 @@ final class SwiftDriverTests: XCTestCase { // Test the fallback path of computing the supported arguments using a swift-frontend // invocation, by pointing the driver to look for libSwiftScan in a place that does not // exist - var env = ProcessEnv.vars + var env = ProcessEnv.block env["SWIFT_DRIVER_SWIFT_SCAN_TOOLCHAIN_PATH"] = "/some/nonexistent/path" let driver = try Driver(args: ["swift", "-target", "arm64-apple-ios12.0", "-resource-dir", "baz"], @@ -5980,7 +5983,7 @@ final class SwiftDriverTests: XCTestCase { // a separate process to capture its output here let result = try TSCBasic.Process.checkNonZeroExit( arguments: args, - environment: ProcessEnv.vars + environmentBlock: ProcessEnv.block ) // Make sure the interpret job description was printed XCTAssertTrue(result.contains("-frontend -interpret \(input.description)")) @@ -6103,7 +6106,7 @@ final class SwiftDriverTests: XCTestCase { } func testLTOOutputs() throws { - var envVars = ProcessEnv.vars + var envVars = ProcessEnv.block envVars["SWIFT_DRIVER_LD_EXEC"] = ld.nativePathString(escaped: false) let targets = ["x86_64-unknown-linux-gnu", "x86_64-apple-macosx10.9"] @@ -6220,7 +6223,7 @@ final class SwiftDriverTests: XCTestCase { func testVerifyEmittedInterfaceJob() throws { // Evolution enabled - var envVars = ProcessEnv.vars + var envVars = ProcessEnv.block do { var driver = try Driver(args: ["swiftc", "foo.swift", "-emit-module", "-module-name", "foo", "-emit-module-interface", @@ -6502,7 +6505,7 @@ final class SwiftDriverTests: XCTestCase { func testLoadPackageInterface() throws { try withTemporaryDirectory { path in - let envVars = ProcessEnv.vars + let envVars = ProcessEnv.block let main = path.appending(component: "main.swift") try localFileSystem.writeFileContents(main) { $0.send("import Foo;") @@ -6829,7 +6832,7 @@ final class SwiftDriverTests: XCTestCase { } func testEmbeddedSwiftOptions() throws { - var env = ProcessEnv.vars + var env = ProcessEnv.block env["SWIFT_DRIVER_SWIFT_AUTOLINK_EXTRACT_EXEC"] = "/garbage/swift-autolink-extract" do { @@ -6927,9 +6930,9 @@ final class SwiftDriverTests: XCTestCase { // 32-bit iOS jobs under Embedded should be allowed regardless of OS version do { - try Driver(args: ["swiftc", "-c", "-target", "armv7-apple-ios8", "-enable-experimental-feature", "Embedded", "foo.swift"]) - try Driver(args: ["swiftc", "-c", "-target", "armv7-apple-ios12.1", "-enable-experimental-feature", "Embedded", "foo.swift"]) - try Driver(args: ["swiftc", "-c", "-target", "armv7-apple-ios16", "-enable-experimental-feature", "Embedded", "foo.swift"]) + let _ = try Driver(args: ["swiftc", "-c", "-target", "armv7-apple-ios8", "-enable-experimental-feature", "Embedded", "foo.swift"]) + let _ = try Driver(args: ["swiftc", "-c", "-target", "armv7-apple-ios12.1", "-enable-experimental-feature", "Embedded", "foo.swift"]) + let _ = try Driver(args: ["swiftc", "-c", "-target", "armv7-apple-ios16", "-enable-experimental-feature", "Embedded", "foo.swift"]) } do { @@ -6984,7 +6987,7 @@ final class SwiftDriverTests: XCTestCase { func testSwiftHelpOverride() throws { // FIXME: On Linux, we might not have any Clang in the path. We need a // better override. - var env = ProcessEnv.vars + var env = ProcessEnv.block let swiftHelp: AbsolutePath = try AbsolutePath(validating: "/usr/bin/nonexistent-swift-help") env["SWIFT_DRIVER_SWIFT_HELP_EXEC"] = swiftHelp.pathString env["SWIFT_DRIVER_CLANG_EXEC"] = "/usr/bin/clang" @@ -6997,7 +7000,7 @@ final class SwiftDriverTests: XCTestCase { } func testSwiftClangOverride() throws { - var env = ProcessEnv.vars + var env = ProcessEnv.block let swiftClang = try AbsolutePath(validating: "/A/Path/swift-clang") env["SWIFT_DRIVER_CLANG_EXEC"] = swiftClang.pathString @@ -7014,7 +7017,7 @@ final class SwiftDriverTests: XCTestCase { #if canImport(Darwin) throw XCTSkip("Darwin always uses `clang` to link") #else - var env = ProcessEnv.vars + var env = ProcessEnv.block let swiftClang = try AbsolutePath(validating: "/A/Path/swift-clang") let swiftClangxx = try AbsolutePath(validating: "/A/Path/swift-clang++") env["SWIFT_DRIVER_CLANG_EXEC"] = swiftClang.pathString @@ -7168,7 +7171,7 @@ final class SwiftDriverTests: XCTestCase { } func testPrebuiltModuleCacheFlags() throws { - var envVars = ProcessEnv.vars + var envVars = ProcessEnv.block envVars["SWIFT_DRIVER_LD_EXEC"] = ld.nativePathString(escaped: false) let mockSDKPath: String = @@ -7228,7 +7231,7 @@ final class SwiftDriverTests: XCTestCase { } do { - let workingDirectory = AbsolutePath("/foo/bar") + let workingDirectory = try AbsolutePath(validating: "/foo/bar") // Inputs with relative paths with -working-directory flag should prefix all inputs var driver = try Driver(args: ["swiftc", @@ -7240,9 +7243,9 @@ final class SwiftDriverTests: XCTestCase { let plannedJobs = try driver.planBuild() let compileJob = plannedJobs[0] XCTAssertEqual(compileJob.kind, .compile) - try XCTAssertJobInvocationMatches(compileJob, .flag("-primary-file"), .path(.absolute(workingDirectory.appending(component: "foo.swift")))) - try XCTAssertJobInvocationMatches(compileJob, .flag("-resource-dir"), .path(.absolute(workingDirectory.appending(component: "relresourcepath")))) - try XCTAssertJobInvocationMatches(compileJob, .flag("-sdk"), .path(.absolute(workingDirectory.appending(component: "relsdkpath")))) + XCTAssertJobInvocationMatches(compileJob, .flag("-primary-file"), .path(.absolute(workingDirectory.appending(component: "foo.swift")))) + XCTAssertJobInvocationMatches(compileJob, .flag("-resource-dir"), .path(.absolute(workingDirectory.appending(component: "relresourcepath")))) + XCTAssertJobInvocationMatches(compileJob, .flag("-sdk"), .path(.absolute(workingDirectory.appending(component: "relsdkpath")))) } try withTemporaryFile { fileMapFile in @@ -7267,7 +7270,7 @@ final class SwiftDriverTests: XCTestCase { let plannedJobs = try driver.planBuild() let compileJob = plannedJobs[0] XCTAssertEqual(compileJob.kind, .compile) - try XCTAssertJobInvocationMatches(compileJob, .flag("-o"), .path(.absolute(.init("/tmp/foo/.build/x86_64-apple-macosx/debug/foo.build/foo.o")))) + try XCTAssertJobInvocationMatches(compileJob, .flag("-o"), .path(.absolute(.init(validating: "/tmp/foo/.build/x86_64-apple-macosx/debug/foo.build/foo.o")))) } try withTemporaryFile { fileMapFile in @@ -7277,7 +7280,7 @@ final class SwiftDriverTests: XCTestCase { "diagnostics": "/tmp/foo/.build/x86_64-apple-macosx/debug/foo.build/master.dia", "emit-module-diagnostics": "/tmp/foo/.build/x86_64-apple-macosx/debug/foo.build/master.emit-module.dia" }, - "\(AbsolutePath("/some/workingdir/foo.swift").nativePathString(escaped: true))": { + "\(try AbsolutePath(validating: "/some/workingdir/foo.swift").nativePathString(escaped: true))": { "object": "/tmp/foo/.build/x86_64-apple-macosx/debug/foo.build/foo.o" } } @@ -7288,12 +7291,12 @@ final class SwiftDriverTests: XCTestCase { var driver = try Driver(args: ["swiftc", "-target", "arm64-apple-ios13.1", "foo.swift", - "-working-directory", AbsolutePath("/some/workingdir").nativePathString(escaped: false), + "-working-directory", try AbsolutePath(validating: "/some/workingdir").nativePathString(escaped: false), "-output-file-map", fileMapFile.path.description]) let plannedJobs = try driver.planBuild() let compileJob = plannedJobs[0] XCTAssertEqual(compileJob.kind, .compile) - try XCTAssertJobInvocationMatches(compileJob, .flag("-o"), .path(.absolute(.init("/tmp/foo/.build/x86_64-apple-macosx/debug/foo.build/foo.o")))) + try XCTAssertJobInvocationMatches(compileJob, .flag("-o"), .path(.absolute(.init(validating: "/tmp/foo/.build/x86_64-apple-macosx/debug/foo.build/foo.o")))) } } @@ -7301,7 +7304,7 @@ final class SwiftDriverTests: XCTestCase { do { // Reset the environment to avoid 'SDKROOT' influencing the // linux driver paths and taking the priority over the resource directory. - var env = ProcessEnv.vars + var env = ProcessEnv.block env["SDKROOT"] = nil var driver = try Driver(args: ["swiftc", "-target", "x86_64-unknown-linux", "-lto=llvm-thin", @@ -7324,7 +7327,7 @@ final class SwiftDriverTests: XCTestCase { func testSDKDirLinuxPrioritizedOverRelativeResourceDirForLinkingSwiftRT() throws { do { let sdkRoot = try testInputsPath.appending(component: "mock-sdk.sdk") - var env = ProcessEnv.vars + var env = ProcessEnv.block env["SDKROOT"] = sdkRoot.pathString var driver = try Driver(args: ["swiftc", "-target", "x86_64-unknown-linux", "-lto=llvm-thin", @@ -7440,7 +7443,7 @@ final class SwiftDriverTests: XCTestCase { } func testFilelist() throws { - var envVars = ProcessEnv.vars + var envVars = ProcessEnv.block envVars["SWIFT_DRIVER_LD_EXEC"] = ld.nativePathString(escaped: false) do { @@ -7682,10 +7685,10 @@ final class SwiftDriverTests: XCTestCase { let sdkRoot = try testInputsPath.appending( components: ["Platform Checks", "\(platform).platform", "Developer", "SDKs", "\(sdk).sdk"]) - var env = ProcessEnv.vars + var env = ProcessEnv.block env["PLATFORM_DIR"] = "/tmp/PlatformDir/\(platform).platform" - let workingDirectory = AbsolutePath("/tmp") + let workingDirectory = try AbsolutePath(validating: "/tmp") var driver = try Driver( args: ["swiftc", "-typecheck", "foo.swift", "-sdk", VirtualPath.absolute(sdkRoot).name, "-plugin-path", "PluginA", "-external-plugin-path", "Plugin~B#Bexe", "-load-plugin-library", "PluginB2", "-plugin-path", "PluginC", "-working-directory", workingDirectory.nativePathString(escaped: false)], @@ -7824,7 +7827,7 @@ final class SwiftDriverTests: XCTestCase { } do { - var env = ProcessEnv.vars + var env = ProcessEnv.block env["SDKROOT"] = SDKROOT.nativePathString(escaped: false) var driver = try Driver(args: [ @@ -7838,7 +7841,7 @@ final class SwiftDriverTests: XCTestCase { } do { - var env = ProcessEnv.vars + var env = ProcessEnv.block env["SDKROOT"] = SDKROOT.nativePathString(escaped: false) var driver = try Driver(args: [ @@ -7857,7 +7860,7 @@ final class SwiftDriverTests: XCTestCase { // toolchain relative path. #if false do { - var env = ProcessEnv.vars + var env = ProcessEnv.block env["SDKROOT"] = nil var driver = try Driver(args: [ @@ -7889,12 +7892,12 @@ final class SwiftDriverTests: XCTestCase { func testToolSearching() throws { #if os(Windows) - let PATH = "Path" + let PATH = ProcessEnvironmentKey("Path") #else - let PATH = "PATH" + let PATH = ProcessEnvironmentKey("PATH") #endif - let SWIFT_FRONTEND_EXEC = "SWIFT_DRIVER_SWIFT_FRONTEND_EXEC" - let SWIFT_SCANNER_LIB = "SWIFT_DRIVER_SWIFTSCAN_LIB" + let SWIFT_FRONTEND_EXEC = ProcessEnvironmentKey("SWIFT_DRIVER_SWIFT_FRONTEND_EXEC") + let SWIFT_SCANNER_LIB = ProcessEnvironmentKey("SWIFT_DRIVER_SWIFTSCAN_LIB") // Reset the environment to ensure tool resolution is exactly run against PATH. @@ -8021,7 +8024,7 @@ final class SwiftDriverTests: XCTestCase { func testAndroidNDK() throws { try withTemporaryDirectory { path in - var env = ProcessEnv.vars + var env = ProcessEnv.block env["SWIFT_DRIVER_SWIFT_AUTOLINK_EXTRACT_EXEC"] = "/garbage/swift-autolink-extract" do { @@ -8088,7 +8091,7 @@ final class SwiftDriverTests: XCTestCase { } do { - var env = ProcessEnv.vars + var env = ProcessEnv.block env["TAPI_SDKDB_OUTPUT_PATH"] = path.appending(component: "SDKDB").nativePathString(escaped: false) var driver = try Driver(args: ["swiftc", "foo.swift", "bar.swift", "baz.swift", "-emit-module", "-module-name", "Test"], env: env) @@ -8101,7 +8104,7 @@ final class SwiftDriverTests: XCTestCase { } do { - var env = ProcessEnv.vars + var env = ProcessEnv.block env["LD_TRACE_FILE"] = path.appending(component: ".LD_TRACE").nativePathString(escaped: false) var driver = try Driver(args: ["swiftc", "foo.swift", "bar.swift", "baz.swift", "-emit-module", "-module-name", "Test"], env: env) @@ -8132,7 +8135,7 @@ final class SwiftDriverTests: XCTestCase { } do { - var env = ProcessEnv.vars + var env = ProcessEnv.block env["TAPI_SDKDB_OUTPUT_PATH"] = path.appending(component: "SDKDB").nativePathString(escaped: false) var driver = try Driver(args: ["swiftc", "-whole-module-optimization", "-driver-filelist-threshold=0", @@ -8146,7 +8149,7 @@ final class SwiftDriverTests: XCTestCase { } do { - var env = ProcessEnv.vars + var env = ProcessEnv.block env["LD_TRACE_FILE"] = path.appending(component: ".LD_TRACE").nativePathString(escaped: false) var driver = try Driver(args: ["swiftc", "-whole-module-optimization", "-driver-filelist-threshold=0", diff --git a/Tests/SwiftDriverTests/SwiftDriverToolingInterfaceTests.swift b/Tests/SwiftDriverTests/SwiftDriverToolingInterfaceTests.swift index bb1859a73..25c963c8d 100644 --- a/Tests/SwiftDriverTests/SwiftDriverToolingInterfaceTests.swift +++ b/Tests/SwiftDriverTests/SwiftDriverToolingInterfaceTests.swift @@ -98,9 +98,10 @@ final class SwiftDriverToolingInterfaceTests: XCTestCase { let inputFile = path.appending(components: "test.swift") try localFileSystem.writeFileContents(inputFile) { $0.send("public func foo()") } - let env = ProcessEnv.vars + let envBlock = ProcessEnv.block + let env = envBlock.legacyVars let resolver = try ArgsResolver(fileSystem: localFileSystem) - let executor = SimpleExecutor(resolver: resolver, fileSystem: localFileSystem, env: env) + let executor = SimpleExecutor(resolver: resolver, fileSystem: localFileSystem, env: envBlock) // Expected success scenarios: do { @@ -191,9 +192,10 @@ final class SwiftDriverToolingInterfaceTests: XCTestCase { let inputFile = path.appending(components: "test.swift") try localFileSystem.writeFileContents(inputFile) { $0.send("public func foo()") } - let env = ProcessEnv.vars + let envBlock = ProcessEnv.block + let env = envBlock.legacyVars let resolver = try ArgsResolver(fileSystem: localFileSystem) - let executor = SimpleExecutor(resolver: resolver, fileSystem: localFileSystem, env: env) + let executor = SimpleExecutor(resolver: resolver, fileSystem: localFileSystem, env: envBlock) // Expected success scenarios: do { @@ -279,6 +281,99 @@ final class SwiftDriverToolingInterfaceTests: XCTestCase { } } + func testCreateCompilerInvocationV5() throws { + try withTemporaryDirectory { path in + let inputFile = path.appending(components: "test.swift") + try localFileSystem.writeFileContents(inputFile) { $0.send("public func foo()") } + + let env = ProcessEnv.block + let resolver = try ArgsResolver(fileSystem: localFileSystem) + let executor = SimpleExecutor(resolver: resolver, fileSystem: localFileSystem, env: ProcessEnv.block) + + // Expected success scenarios: + do { + let testCommand = inputFile.description + var emittedDiagnostics: [Diagnostic] = [] + XCTAssertFalse(getSingleFrontendInvocationFromDriverArgumentsV5(driverPath: "swiftc", + argList: testCommand.components(separatedBy: " "), + action: { _ in false }, + diagnostics: &emittedDiagnostics, + diagnosticCallback: {_,_ in }, + envBlock: env, + executor: executor)) + } + do { + let testCommand = "-emit-executable " + inputFile.description + " main.swift lib.swift -module-name createCompilerInvocation -emit-module -emit-objc-header -o t.out" + var emittedDiagnostics: [Diagnostic] = [] + XCTAssertFalse(getSingleFrontendInvocationFromDriverArgumentsV5(driverPath: "swiftc", + argList: testCommand.components(separatedBy: " "), + action: { _ in false }, + diagnostics: &emittedDiagnostics, + diagnosticCallback: {_,_ in }, + envBlock: env, + executor: executor)) + } + do { + let testCommand = "-c " + inputFile.description + " main.swift lib.swift -module-name createCompilerInvocation -emit-module -emit-objc-header" + var emittedDiagnostics: [Diagnostic] = [] + XCTAssertFalse(getSingleFrontendInvocationFromDriverArgumentsV5(driverPath: "swiftc", + argList: testCommand.components(separatedBy: " "), + action: { _ in false }, + diagnostics: &emittedDiagnostics, + diagnosticCallback: {_,_ in }, + envBlock: env, + executor: executor)) + } + do { + let testCommand = inputFile.description + " -enable-batch-mode" + var emittedDiagnostics: [Diagnostic] = [] + XCTAssertFalse(getSingleFrontendInvocationFromDriverArgumentsV5(driverPath: "swiftc", + argList: testCommand.components(separatedBy: " "), + action: { _ in false }, + diagnostics: &emittedDiagnostics, + diagnosticCallback: {_,_ in }, + envBlock: env, + executor: executor)) + } + do { // Force no outputs + let testCommand = "-module-name foo -emit-module -emit-module-path /tmp/foo.swiftmodule -emit-objc-header -emit-objc-header-path /tmp/foo.h -enable-library-evolution -emit-module-interface -emit-module-interface-path /tmp/foo.swiftinterface -emit-library -emit-tbd -emit-tbd-path /tmp/foo.tbd -emit-dependencies -serialize-diagnostics " + inputFile.description + var resultingFrontendArgs: [String] = [] + var emittedDiagnostics: [Diagnostic] = [] + XCTAssertFalse(getSingleFrontendInvocationFromDriverArgumentsV5(driverPath: "swiftc", + argList: testCommand.components(separatedBy: " "), + action: { args in + resultingFrontendArgs = args + return false + }, + diagnostics: &emittedDiagnostics, + diagnosticCallback: {_,_ in }, + envBlock: env, + executor: executor, + forceNoOutputs: true)) + XCTAssertFalse(resultingFrontendArgs.contains("-emit-module-interface-path")) + XCTAssertFalse(resultingFrontendArgs.contains("-emit-objc-header")) + XCTAssertFalse(resultingFrontendArgs.contains("-emit-objc-header-path")) + XCTAssertFalse(resultingFrontendArgs.contains("-emit-module-path")) + XCTAssertFalse(resultingFrontendArgs.contains("-emit-tbd-path")) + } + + // Expected failure scenarios: + do { + let testCommand = "-v" // No inputs + var emittedDiagnostics: [Diagnostic] = [] + XCTAssertTrue(getSingleFrontendInvocationFromDriverArgumentsV5(driverPath: "swiftc", + argList: testCommand.components(separatedBy: " "), + action: { _ in false }, + diagnostics: &emittedDiagnostics, + diagnosticCallback: {_,_ in }, + envBlock: env, + executor: executor)) + let errorMessage = try XCTUnwrap(emittedDiagnostics.first?.message.text) + XCTAssertEqual(errorMessage, "unable to handle compilation, expected exactly one frontend job") + } + } + } + func testCreateCompilerInvocationCAPI() throws { try withTemporaryDirectory { path in let inputFile = path.appending(components: "test.swift") diff --git a/Tests/SwiftDriverTests/TripleTests.swift b/Tests/SwiftDriverTests/TripleTests.swift index 311e77fc4..0380cbcaf 100644 --- a/Tests/SwiftDriverTests/TripleTests.swift +++ b/Tests/SwiftDriverTests/TripleTests.swift @@ -1443,7 +1443,7 @@ final class TripleTests: XCTestCase { } } -extension Triple.Version: ExpressibleByStringLiteral { +extension Triple.Version: @retroactive ExpressibleByStringLiteral { public init(stringLiteral value: String) { self.init(parse: value) } diff --git a/Tests/TestUtilities/DriverExtensions.swift b/Tests/TestUtilities/DriverExtensions.swift index 14ea31265..1bb4a1504 100644 --- a/Tests/TestUtilities/DriverExtensions.swift +++ b/Tests/TestUtilities/DriverExtensions.swift @@ -20,7 +20,7 @@ extension Driver { /// Initializer which creates an executor suitable for use in tests. public init( args: [String], - env: [String: String] = ProcessEnv.vars, + env: ProcessEnvironmentBlock = ProcessEnv.block, diagnosticsEngine: DiagnosticsEngine = DiagnosticsEngine(handlers: [Driver.stderrDiagnosticsHandler]), fileSystem: FileSystem = localFileSystem, integratedDriver: Bool = true, @@ -31,7 +31,7 @@ extension Driver { fileSystem: fileSystem, env: env) try self.init(args: args, - env: env, + envBlock: env, diagnosticsOutput: .engine(diagnosticsEngine), fileSystem: fileSystem, executor: executor, @@ -58,7 +58,7 @@ private let cachedSDKPath: Result? = { return nil #elseif os(macOS) return Result { - if let pathFromEnv = ProcessEnv.vars["SDKROOT"] { + if let pathFromEnv = ProcessEnv.block["SDKROOT"] { return pathFromEnv } let process = Process(arguments: ["xcrun", "-sdk", "macosx", "--show-sdk-path"])