Skip to content

Commit

Permalink
Improve Destination.sdkPlatformFrameworkPaths()
Browse files Browse the repository at this point in the history
We should not ignore errors or an empty SDK platform path since that means XCTest imports and test execution might silently fail on macOS with no indication to what the problem is. I am speculating that this may be contributing to the recent surge of tests failing by not seeing the XCTest Swift library. I am also re-enabling the disabled tests here to see if this has any practical effect, but regardless it doesn't seem reasonable to silently ignore some failure states here.

rdar://101868275
  • Loading branch information
neonichu committed Nov 4, 2022
1 parent 3c692dc commit 42b8295
Show file tree
Hide file tree
Showing 17 changed files with 76 additions and 77 deletions.
14 changes: 7 additions & 7 deletions Sources/Commands/Utilities/TestingSupport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,13 @@ enum TestingSupport {
),
sanitizers: sanitizers
)
// Add the sdk platform path if we have it. If this is not present, we
// might always end up failing.
if let sdkPlatformFrameworksPath = try Destination.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 this is not present, we might always end up failing.
let sdkPlatformFrameworksPath = try Destination.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)

try TSCBasic.Process.checkNonZeroExit(arguments: args, environment: env)
// Read the temporary file's content.
return try swiftTool.fileSystem.readFileContents(tempFile.path)
Expand Down
6 changes: 3 additions & 3 deletions Sources/PackageLoading/ManifestJSONParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ enum ManifestJSONParser {
do {
path = try AbsolutePath(validating: location)
} catch PathValidationError.invalidAbsolutePath(let path) {
throw ManifestParseError.invalidManifestFormat("'\(path)' is not a valid path for path-based dependencies; use relative or absolute path instead.", diagnosticFile: nil)
throw ManifestParseError.invalidManifestFormat("'\(path)' is not a valid path for path-based dependencies; use relative or absolute path instead.", diagnosticFile: nil, compilerCommandLine: nil)
}
let identity = try identityResolver.resolveIdentity(for: path)
return .fileSystem(identity: identity,
Expand Down Expand Up @@ -224,11 +224,11 @@ enum ManifestJSONParser {
guard hostnameComponent.isEmpty else {
if hostnameComponent == ".." {
throw ManifestParseError.invalidManifestFormat(
"file:// URLs cannot be relative, did you mean to use '.package(path:)'?", diagnosticFile: nil
"file:// URLs cannot be relative, did you mean to use '.package(path:)'?", diagnosticFile: nil, compilerCommandLine: nil
)
}
throw ManifestParseError.invalidManifestFormat(
"file:// URLs with hostnames are not supported, are you missing a '/'?", diagnosticFile: nil
"file:// URLs with hostnames are not supported, are you missing a '/'?", diagnosticFile: nil, compilerCommandLine: nil
)
}
return try AbsolutePath(validating: location).pathString
Expand Down
19 changes: 15 additions & 4 deletions Sources/PackageLoading/ManifestLoader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import enum TSCUtility.Diagnostics

public enum ManifestParseError: Swift.Error, Equatable {
/// The manifest contains invalid format.
case invalidManifestFormat(String, diagnosticFile: AbsolutePath?)
case invalidManifestFormat(String, diagnosticFile: AbsolutePath?, compilerCommandLine: [String]?)

/// The manifest was successfully loaded by swift interpreter but there were runtime issues.
case runtimeManifestErrors([String])
Expand All @@ -28,8 +28,14 @@ public enum ManifestParseError: Swift.Error, Equatable {
extension ManifestParseError: CustomStringConvertible {
public var description: String {
switch self {
case .invalidManifestFormat(let error, _):
return "Invalid manifest\n\(error)"
case .invalidManifestFormat(let error, _, let compilerCommandLine):
let suffix: String
if let compilerCommandLine = compilerCommandLine {
suffix = " (compiled with: `\(compilerCommandLine)`)"
} else {
suffix = ""
}
return "Invalid manifest\(suffix)\n\(error)"
case .runtimeManifestErrors(let errors):
return "Invalid manifest (evaluation failed)\n\(errors.joined(separator: "\n"))"
}
Expand Down Expand Up @@ -279,7 +285,7 @@ public final class ManifestLoader: ManifestLoaderProtocol {
// Throw now if we weren't able to parse the manifest.
guard let manifestJSON = result.manifestJSON, !manifestJSON.isEmpty else {
let errors = result.errorOutput ?? result.compilerOutput ?? "Missing or empty JSON output from manifest compilation for \(packageIdentity)"
throw ManifestParseError.invalidManifestFormat(errors, diagnosticFile: result.diagnosticFile)
throw ManifestParseError.invalidManifestFormat(errors, diagnosticFile: result.diagnosticFile, compilerCommandLine: result.compilerCommandLine)
}

// We should not have any fatal error at this point.
Expand Down Expand Up @@ -653,6 +659,8 @@ public final class ManifestLoader: ManifestLoaderProtocol {
environment["Path"] = "\(windowsPathComponent);\(environment["Path"] ?? "")"
#endif

evaluationResult.compilerCommandLine = cmd

let cleanupAfterRunning = cleanupIfError.delay()
TSCBasic.Process.popen(arguments: cmd, environment: environment, queue: callbackQueue) { result in
dispatchPrecondition(condition: .onQueue(callbackQueue))
Expand Down Expand Up @@ -827,6 +835,9 @@ extension ManifestLoader {
/// The manifest in JSON format.
var manifestJSON: String?

/// The command line used to compile the manifest
var compilerCommandLine: [String]?

/// Any non-compiler error that might have occurred during manifest loading.
///
/// For e.g., we could have failed to spawn the process or create temporary file.
Expand Down
38 changes: 20 additions & 18 deletions Sources/PackageModel/Destination.swift
Original file line number Diff line number Diff line change
Expand Up @@ -167,12 +167,11 @@ public struct Destination: Encodable, Equatable {
var extraCCFlags: [String] = []
var extraSwiftCFlags: [String] = []
#if os(macOS)
if let sdkPaths = try Destination.sdkPlatformFrameworkPaths(environment: environment) {
extraCCFlags += ["-F", sdkPaths.fwk.pathString]
extraSwiftCFlags += ["-F", sdkPaths.fwk.pathString]
extraSwiftCFlags += ["-I", sdkPaths.lib.pathString]
extraSwiftCFlags += ["-L", sdkPaths.lib.pathString]
}
let sdkPaths = try Destination.sdkPlatformFrameworkPaths(environment: environment)
extraCCFlags += ["-F", sdkPaths.fwk.pathString]
extraSwiftCFlags += ["-F", sdkPaths.fwk.pathString]
extraSwiftCFlags += ["-I", sdkPaths.lib.pathString]
extraSwiftCFlags += ["-L", sdkPaths.lib.pathString]
#endif

#if !os(Windows)
Expand All @@ -190,26 +189,29 @@ public struct Destination: Encodable, Equatable {
/// Returns macosx sdk platform framework path.
public static func sdkPlatformFrameworkPaths(
environment: EnvironmentVariables = .process()
) throws -> (fwk: AbsolutePath, lib: AbsolutePath)? {
) throws -> (fwk: AbsolutePath, lib: AbsolutePath) {
if let path = _sdkPlatformFrameworkPath {
return path
}
let platformPath = try? TSCBasic.Process.checkNonZeroExit(
let platformPath = try TSCBasic.Process.checkNonZeroExit(
arguments: ["/usr/bin/xcrun", "--sdk", "macosx", "--show-sdk-platform-path"],
environment: environment).spm_chomp()

if let platformPath = platformPath, !platformPath.isEmpty {
// For XCTest framework.
let fwk = try AbsolutePath(validating: platformPath).appending(
components: "Developer", "Library", "Frameworks")
guard !platformPath.isEmpty else {
throw StringError("could not determine SDK platform path")
}

// For XCTest framework.
let fwk = try AbsolutePath(validating: platformPath).appending(
components: "Developer", "Library", "Frameworks")

// For XCTest Swift library.
let lib = try AbsolutePath(validating: platformPath).appending(
components: "Developer", "usr", "lib")
// For XCTest Swift library.
let lib = try AbsolutePath(validating: platformPath).appending(
components: "Developer", "usr", "lib")

_sdkPlatformFrameworkPath = (fwk, lib)
}
return _sdkPlatformFrameworkPath
let sdkPlatformFrameworkPath = (fwk, lib)
_sdkPlatformFrameworkPath = sdkPlatformFrameworkPath
return sdkPlatformFrameworkPath
}
/// Cache storage for sdk platform path.
private static var _sdkPlatformFrameworkPath: (fwk: AbsolutePath, lib: AbsolutePath)? = nil
Expand Down
12 changes: 0 additions & 12 deletions Sources/SPMTestSupport/Toolchain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,6 @@ private func resolveBinDir() throws -> AbsolutePath {
#endif
}

extension UserToolchain {

#if os(macOS)
public var sdkPlatformFrameworksPath: AbsolutePath {
get throws {
return try Destination.sdkPlatformFrameworkPaths()!.fwk
}
}
#endif

}

extension Destination {
public static var `default`: Self {
get throws {
Expand Down
2 changes: 0 additions & 2 deletions Tests/CommandsTests/BuildToolTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -280,8 +280,6 @@ final class BuildToolTests: CommandsTestCase {
}

func testBuildCompleteMessage() throws {
throw XCTSkip("This test fails to match the 'Compiling' regex; rdar://101815761")

try fixture(name: "DependencyResolution/Internal/Simple") { fixturePath in
do {
let result = try execute([], packagePath: fixturePath)
Expand Down
2 changes: 1 addition & 1 deletion Tests/FunctionalTests/SwiftPMXCTestHelperTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class SwiftPMXCTestHelperTests: XCTestCase {

func XCTAssertXCTestHelper(_ bundlePath: AbsolutePath, testCases: NSDictionary) throws {
#if os(macOS)
let env = ["DYLD_FRAMEWORK_PATH": try UserToolchain.default.sdkPlatformFrameworksPath.pathString]
let env = ["DYLD_FRAMEWORK_PATH": try Destination.sdkPlatformFrameworkPaths().fwk.pathString]
let outputFile = bundlePath.parentDirectory.appending(component: "tests.txt")
let _ = try SwiftPMProduct.XCTestHelper.execute([bundlePath.pathString, outputFile.pathString], env: env)
guard let data = NSData(contentsOfFile: outputFile.pathString) else {
Expand Down
2 changes: 1 addition & 1 deletion Tests/PackageLoadingTests/PD_4_0_LoadingTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ class PackageDescription4_0LoadingTests: PackageDescriptionLoadingTests {

let observability = ObservabilitySystem.makeForTesting()
XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in
if case ManifestParseError.invalidManifestFormat(let error, _) = error {
if case ManifestParseError.invalidManifestFormat(let error, _, _) = error {
XCTAssert(error.contains("error: 'package(url:version:)' is unavailable: use package(url:exact:) instead"), "\(error)")
XCTAssert(error.contains("error: 'package(url:range:)' is unavailable: use package(url:_:) instead"), "\(error)")
} else {
Expand Down
16 changes: 8 additions & 8 deletions Tests/PackageLoadingTests/PD_4_2_LoadingTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests {

let observability = ObservabilitySystem.makeForTesting()
XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in
if case ManifestParseError.invalidManifestFormat(let message, _) = error {
if case ManifestParseError.invalidManifestFormat(let message, _, _) = error {
XCTAssertMatch(
message,
.and(
Expand Down Expand Up @@ -166,7 +166,7 @@ class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests {

let observability = ObservabilitySystem.makeForTesting()
XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in
if case ManifestParseError.invalidManifestFormat(let message, _) = error {
if case ManifestParseError.invalidManifestFormat(let message, _, _) = error {
XCTAssertMatch(message, .contains("is unavailable"))
XCTAssertMatch(message, .contains("was introduced in PackageDescription 5"))
} else {
Expand All @@ -188,7 +188,7 @@ class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests {

let observability = ObservabilitySystem.makeForTesting()
XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in
if case ManifestParseError.invalidManifestFormat(let message, _) = error {
if case ManifestParseError.invalidManifestFormat(let message, _, _) = error {
XCTAssertMatch(message, .contains("is unavailable"))
XCTAssertMatch(message, .contains("was introduced in PackageDescription 5"))
} else {
Expand All @@ -208,7 +208,7 @@ class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests {

let observability = ObservabilitySystem.makeForTesting()
XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in
if case ManifestParseError.invalidManifestFormat(let message, _) = error {
if case ManifestParseError.invalidManifestFormat(let message, _, _) = error {
XCTAssertMatch(message, .contains("is unavailable"))
XCTAssertMatch(message, .contains("was introduced in PackageDescription 5"))
} else {
Expand Down Expand Up @@ -239,7 +239,7 @@ class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests {

let observability = ObservabilitySystem.makeForTesting()
XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in
if case ManifestParseError.invalidManifestFormat(let message, _) = error {
if case ManifestParseError.invalidManifestFormat(let message, _, _) = error {
XCTAssertMatch(message, .contains("is unavailable"))
XCTAssertMatch(message, .contains("was introduced in PackageDescription 5"))
} else {
Expand Down Expand Up @@ -499,7 +499,7 @@ class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests {

let observability = ObservabilitySystem.makeForTesting()
XCTAssertThrowsError(try loadAndValidateManifest(content, observabilityScope: observability.topScope), "expected error") { error in
if case ManifestParseError.invalidManifestFormat(let message, let diagnosticFile) = error {
if case ManifestParseError.invalidManifestFormat(let message, let diagnosticFile, _) = error {
XCTAssertNil(diagnosticFile)
XCTAssertEqual(message, "'https://someurl.com' is not a valid path for path-based dependencies; use relative or absolute path instead.")
} else {
Expand All @@ -519,9 +519,9 @@ class PackageDescription4_2LoadingTests: PackageDescriptionLoadingTests {
case .invalidAbsolutePath:
return nil
case .relativePath:
return .invalidManifestFormat("file:// URLs cannot be relative, did you mean to use '.package(path:)'?", diagnosticFile: nil)
return .invalidManifestFormat("file:// URLs cannot be relative, did you mean to use '.package(path:)'?", diagnosticFile: nil, compilerCommandLine: nil)
case .unsupportedHostname:
return .invalidManifestFormat("file:// URLs with hostnames are not supported, are you missing a '/'?", diagnosticFile: nil)
return .invalidManifestFormat("file:// URLs with hostnames are not supported, are you missing a '/'?", diagnosticFile: nil, compilerCommandLine: nil)
}
}

Expand Down

0 comments on commit 42b8295

Please sign in to comment.