Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

On Darwin, allow XCTest to be missing if we're only building swift-testing tests. #7426

Merged
merged 3 commits into from Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
22 changes: 15 additions & 7 deletions Sources/Commands/SwiftTestCommand.swift
Expand Up @@ -40,7 +40,8 @@ private enum TestError: Swift.Error {
case testProductNotFound(productName: String)
case productIsNotTest(productName: String)
case multipleTestProducts([String])
case xctestNotAvailable
case xctestNotAvailable(reason: String)
case xcodeNotInstalled
}

extension TestError: CustomStringConvertible {
Expand All @@ -57,8 +58,10 @@ extension TestError: CustomStringConvertible {
return "invalid list test JSON structure, produced by \(context)\(underlying)"
case .multipleTestProducts(let products):
return "found multiple test products: \(products.joined(separator: ", ")); use --test-product to select one"
case .xctestNotAvailable:
return "XCTest not available"
case let .xctestNotAvailable(reason):
return "XCTest not available: \(reason)"
case .xcodeNotInstalled:
return "XCTest not available; download and install Xcode to use XCTest on this platform"
}
}
}
Expand Down Expand Up @@ -203,9 +206,14 @@ package struct SwiftTestCommand: AsyncSwiftCommand {
private func xctestRun(_ swiftCommandState: SwiftCommandState) async throws {
// validate XCTest available on darwin based systems
let toolchain = try swiftCommandState.getTargetToolchain()
let isHostTestingAvailable = try swiftCommandState.getHostToolchain().swiftSDK.supportsTesting
if (toolchain.targetTriple.isDarwin() && toolchain.xctestPath == nil) || !isHostTestingAvailable {
throw TestError.xctestNotAvailable
if case let .unsupported(reason) = try swiftCommandState.getHostToolchain().swiftSDK.xctestSupport {
if let reason {
throw TestError.xctestNotAvailable(reason: reason)
} else {
throw TestError.xcodeNotInstalled
}
} else if toolchain.targetTriple.isDarwin() && toolchain.xctestPath == nil {
throw TestError.xcodeNotInstalled
}

let buildParameters = try swiftCommandState.buildParametersForTest(options: self.options, library: .xctest)
Expand Down Expand Up @@ -814,7 +822,7 @@ final class TestRunner {
#if os(macOS)
if library == .xctest {
guard let xctestPath = self.toolchain.xctestPath else {
throw TestError.xctestNotAvailable
throw TestError.xcodeNotInstalled
}
args = [xctestPath.pathString]
args += additionalArguments
Expand Down
11 changes: 6 additions & 5 deletions Sources/Commands/Utilities/TestingSupport.swift
Expand Up @@ -178,11 +178,12 @@ enum TestingSupport {
#endif
return env
#else
// Add the sdk platform path if we have it. If this is not present, we might always end up failing.
let sdkPlatformFrameworksPath = try SwiftSDK.sdkPlatformFrameworkPaths()
// appending since we prefer the user setting (if set) to the one we inject
env.appendPath("DYLD_FRAMEWORK_PATH", value: sdkPlatformFrameworksPath.fwk.pathString)
env.appendPath("DYLD_LIBRARY_PATH", value: sdkPlatformFrameworksPath.lib.pathString)
// Add the sdk platform path if we have it.
if let sdkPlatformFrameworksPath = try? SwiftSDK.sdkPlatformFrameworkPaths() {
// appending since we prefer the user setting (if set) to the one we inject
env.appendPath("DYLD_FRAMEWORK_PATH", value: sdkPlatformFrameworksPath.fwk.pathString)
env.appendPath("DYLD_LIBRARY_PATH", value: sdkPlatformFrameworksPath.lib.pathString)
}

// Fast path when no sanitizers are enabled.
if sanitizers.isEmpty {
Expand Down
64 changes: 55 additions & 9 deletions Sources/PackageModel/SwiftSDKs/SwiftSDK.swift
Expand Up @@ -147,7 +147,29 @@ public struct SwiftSDK: Equatable {
public var architectures: [String]? = nil

/// Whether or not the receiver supports testing.
public let supportsTesting: Bool
@available(*, deprecated, message: "Use `xctestSupport` instead")
public var supportsTesting: Bool {
if case .supported = xctestSupport {
return true
}
return false
}

/// Whether or not the receiver supports testing using XCTest.
package enum XCTestSupport: Sendable, Equatable {
/// XCTest is supported.
case supported

/// XCTest is not supported.
///
/// - Parameters:
/// - reason: A string explaining why XCTest is not supported. If
/// `nil`, no additional information is available.
case unsupported(reason: String?)
}

/// Whether or not the receiver supports using XCTest.
package let xctestSupport: XCTestSupport

/// Root directory path of the SDK used to compile for the target triple.
@available(*, deprecated, message: "use `pathsConfiguration.sdkRootPath` instead")
Expand Down Expand Up @@ -418,18 +440,43 @@ public struct SwiftSDK: Equatable {
}

/// Creates a Swift SDK with the specified properties.
@available(*, deprecated, message: "use `init(hostTriple:targetTriple:toolset:pathsConfiguration:xctestSupport:)` instead")
public init(
hostTriple: Triple? = nil,
targetTriple: Triple? = nil,
toolset: Toolset,
pathsConfiguration: PathsConfiguration,
supportsTesting: Bool = true
supportsTesting: Bool
) {
let xctestSupport: XCTestSupport
if supportsTesting {
xctestSupport = .supported
} else {
xctestSupport = .unsupported(reason: nil)
}

self.init(
hostTriple: hostTriple,
targetTriple: targetTriple,
toolset: toolset,
pathsConfiguration: pathsConfiguration,
xctestSupport: xctestSupport
)
}

/// Creates a Swift SDK with the specified properties.
package init(
hostTriple: Triple? = nil,
targetTriple: Triple? = nil,
toolset: Toolset,
pathsConfiguration: PathsConfiguration,
xctestSupport: XCTestSupport = .supported
) {
self.hostTriple = hostTriple
self.targetTriple = targetTriple
self.toolset = toolset
self.pathsConfiguration = pathsConfiguration
self.supportsTesting = supportsTesting
self.xctestSupport = xctestSupport
}

/// Returns the bin directory for the host.
Expand Down Expand Up @@ -496,7 +543,7 @@ public struct SwiftSDK: Equatable {
#endif

// Compute common arguments for clang and swift.
let supportsTesting: Bool
let xctestSupport: XCTestSupport
var extraCCFlags: [String] = []
var extraSwiftCFlags: [String] = []
#if os(macOS)
Expand All @@ -506,13 +553,12 @@ public struct SwiftSDK: Equatable {
extraSwiftCFlags += ["-F", sdkPaths.fwk.pathString]
extraSwiftCFlags += ["-I", sdkPaths.lib.pathString]
extraSwiftCFlags += ["-L", sdkPaths.lib.pathString]
supportsTesting = true
xctestSupport = .supported
} catch {
supportsTesting = false
observabilityScope?.emit(warning: "could not determine XCTest paths: \(error)")
xctestSupport = .unsupported(reason: String(describing: error))
}
#else
supportsTesting = true
xctestSupport = .supported
#endif

#if !os(Windows)
Expand All @@ -528,7 +574,7 @@ public struct SwiftSDK: Equatable {
rootPaths: [binDir]
),
pathsConfiguration: .init(sdkRootPath: sdkPath),
supportsTesting: supportsTesting
xctestSupport: xctestSupport
)
}

Expand Down