1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
|
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2019 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
import Basics
import Foundation
import class Basics.AsyncProcess
import struct Basics.AsyncProcessResult
import enum TSCBasic.ProcessEnv
/// Defines the executables used by SwiftPM.
/// Contains path to the currently built executable and
/// helper method to execute them.
public enum SwiftPM {
case Build
case Package
case Registry
case Test
case Run
case experimentalSDK
case sdk
}
extension SwiftPM {
/// Executable name.
private var executableName: String {
switch self {
case .Build:
return "swift-build"
case .Package:
return "swift-package"
case .Registry:
return "swift-package-registry"
case .Test:
return "swift-test"
case .Run:
return "swift-run"
case .experimentalSDK:
return "swift-experimental-sdk"
case .sdk:
return "swift-sdk"
}
}
public var xctestBinaryPath: AbsolutePath {
Self.xctestBinaryPath(for: RelativePath("swift-package-manager"))
}
public static func xctestBinaryPath(for executableName: RelativePath) -> AbsolutePath {
#if canImport(Darwin)
for bundle in Bundle.allBundles where bundle.bundlePath.hasSuffix(".xctest") {
return try! AbsolutePath(AbsolutePath(validating: bundle.bundlePath).parentDirectory, executableName)
}
fatalError()
#else
return try! AbsolutePath(validating: CommandLine.arguments.first!, relativeTo: localFileSystem.currentWorkingDirectory!)
.parentDirectory.appending(executableName)
#endif
}
}
extension SwiftPM {
/// Executes the product with specified arguments.
///
/// - Parameters:
/// - args: The arguments to pass.
/// - env: Additional environment variables to pass. The values here are merged with default env.
/// - packagePath: Adds argument `--package-path <path>` if not nil.
///
/// - Returns: The output of the process.
@discardableResult
public func execute(
_ args: [String] = [],
packagePath: AbsolutePath? = nil,
env: Environment? = nil
) async throws -> (stdout: String, stderr: String) {
let result = try await executeProcess(
args,
packagePath: packagePath,
env: env
)
let stdout = try result.utf8Output()
let stderr = try result.utf8stderrOutput()
if result.exitStatus == .terminated(code: 0) {
return (stdout: stdout, stderr: stderr)
}
throw SwiftPMError.executionFailure(
underlying: AsyncProcessResult.Error.nonZeroExit(result),
stdout: stdout,
stderr: stderr
)
}
private func executeProcess(
_ args: [String],
packagePath: AbsolutePath? = nil,
env: Environment? = nil
) async throws -> AsyncProcessResult {
var environment = Environment.current
#if !os(Windows)
environment["SDKROOT"] = nil
#endif
#if Xcode
// Unset these variables which causes issues when running tests via Xcode.
environment["XCTestConfigurationFilePath"] = nil
environment["XCTestSessionIdentifier"] = nil
environment["XCTestBundlePath"] = nil
environment["NSUnbufferedIO"] = nil
#endif
// FIXME: We use this private environment variable hack to be able to
// create special conditions in swift-build for swiftpm tests.
environment["SWIFTPM_TESTS_MODULECACHE"] = self.xctestBinaryPath.parentDirectory.pathString
// Unset the internal env variable that allows skipping certain tests.
environment["_SWIFTPM_SKIP_TESTS_LIST"] = nil
environment["SWIFTPM_EXEC_NAME"] = self.executableName
for (key, value) in env ?? [:] {
environment[key] = value
}
var completeArgs = [xctestBinaryPath.pathString]
if let packagePath = packagePath {
completeArgs += ["--package-path", packagePath.pathString]
}
completeArgs += args
return try await AsyncProcess.popen(arguments: completeArgs, environment: environment)
}
}
extension SwiftPM {
public static func packagePath(for packageName: String, packageRoot: AbsolutePath) throws -> AbsolutePath {
// FIXME: The directory paths are hard coded right now and should be replaced by --get-package-path
// whenever we design that. https://bugs.swift.org/browse/SR-2753
let packagesPath = packageRoot.appending(components: ".build", "checkouts")
for name in try localFileSystem.getDirectoryContents(packagesPath) {
if name.hasPrefix(packageName) {
return try AbsolutePath(validating: name, relativeTo: packagesPath)
}
}
throw SwiftPMError.packagePathNotFound
}
}
public enum SwiftPMError: Error {
case packagePathNotFound
case executionFailure(underlying: Error, stdout: String, stderr: String)
}
public enum SwiftPMProductError: Swift.Error {
case packagePathNotFound
case executionFailure(error: Swift.Error, output: String, stderr: String)
}
|